File size: 4,255 Bytes
7807849
33c8a2e
7807849
 
f3325f9
 
7807849
 
 
 
 
 
 
 
 
 
f3325f9
7807849
f3325f9
 
 
 
 
 
7807849
33c8a2e
 
f3325f9
 
33c8a2e
 
f3325f9
 
 
 
33c8a2e
 
f3325f9
 
33c8a2e
 
 
 
 
 
 
 
 
f3325f9
 
33c8a2e
 
f3325f9
 
 
33c8a2e
 
 
 
 
 
 
 
 
f3325f9
 
33c8a2e
 
f3325f9
33c8a2e
 
 
 
f3325f9
 
 
33c8a2e
 
 
f3325f9
 
33c8a2e
 
 
 
 
f3325f9
7807849
f3325f9
7807849
d3046db
 
de5c185
 
 
f3325f9
d3046db
 
7807849
 
 
f3325f9
7807849
f3325f9
7807849
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import gradio as gr
from backend.council import stage1_collect_responses, stage2_collect_rankings, stage3_synthesize_final_stream
from backend.config import COUNCIL_MODELS, CHAIRMAN_MODEL


async def ask_council(question: str, progress=gr.Progress()):
    """
    Ask the LLM Council a question.
    
    The council consists of multiple advanced LLMs (currently: {models}) that:
    1. Individually answer the question
    2. Rank each other's answers
    3. Synthesize a final best answer (Chairman: {chairman})
    
    Args:
        question: The user's question to be discussed by the council.
        progress: Gradio progress tracker.
        
    Yields:
        Status updates and finally the synthesized answer.
    """.format(
        models=", ".join([m.split("/")[-1] for m in COUNCIL_MODELS]), chairman=CHAIRMAN_MODEL.split("/")[-1]
    )

    try:
        buffer = ""

        # Stage 1: Collect individual responses
        progress(0.1, desc="Stage 1: Collecting individual responses...")
        buffer += "## 🟑 Stage 1: Collecting individual responses from council members...\n\n"
        yield buffer

        stage1_results = await stage1_collect_responses(question)

        if not stage1_results:
            buffer += "\n❌ The council failed to generate a response."
            yield buffer
            return

        # Format Stage 1 results
        buffer += f"### βœ… Received {len(stage1_results)} responses:\n"
        for res in stage1_results:
            model_name = res["model"].split("/")[-1]
            preview = res["response"][:100].replace("\n", " ") + "..."
            buffer += f"- **{model_name}**: {preview}\n"
        buffer += "\n---\n\n"
        yield buffer

        # Stage 2: Collect rankings
        progress(0.4, desc="Stage 2: Council members are ranking responses...")
        buffer += "## 🟑 Stage 2: Council members are ranking each other's responses...\n\n"
        yield buffer

        stage2_results, _ = await stage2_collect_rankings(question, stage1_results)

        # Format Stage 2 results
        buffer += "### βœ… Rankings Collected:\n"
        for res in stage2_results:
            model_name = res["model"].split("/")[-1]
            # Extract just the ranking part if possible, or just say "Ranked"
            buffer += f"- **{model_name}** has submitted their rankings.\n"
        buffer += "\n---\n\n"
        yield buffer

        # Stage 3: Synthesize final answer
        progress(0.7, desc="Stage 3: Chairman is synthesizing the final answer...")
        buffer += "## 🟑 Stage 3: Chairman is synthesizing the final answer...\n\n"
        yield buffer

        full_response = ""
        async for chunk in stage3_synthesize_final_stream(question, stage1_results, stage2_results):
            full_response += chunk
            yield buffer + full_response

        progress(1.0, desc="Complete!")

        if not full_response:
            buffer += "\n❌ The council failed to generate a final synthesis."
            yield buffer
            return

        # Let's keep the history but mark Stage 3 as done
        final_buffer = buffer.replace(
            "## 🟑 Stage 3: Chairman is synthesizing the final answer...", "## 🟒 Stage 3: Final Answer"
        )
        yield final_buffer + full_response

    except Exception as e:
        yield f"❌ Error consulting the council: {str(e)}"


description = """
An MCP server that consults a council of LLMs to answer questions. [LLM Council](https://github.com/machine-theory/lm-council?tab=readme-ov-file) is a project by Machine Theory
and Andrej Karpathy. This space exposes it as an MCP server so you can use it in your own projchatects.
<img src="https://pbs.twimg.com/media/G6ZZO7ragAAtnCZ?format=jpg" alt="MCP Server" style="width: 300px; height: auto; text-align: center;">
⚠️ We're using 5 models in the council, so it takes a minute to answer.
"""

demo = gr.Interface(
    fn=ask_council,
    inputs=gr.Textbox(lines=2, placeholder="Ask the council..."),
    outputs=gr.Markdown(height=200),
    title="LLM Council MCP Server",
    description=description,
)

if __name__ == "__main__":
    # Launch with mcp_server=True to expose as MCP
    demo.launch(mcp_server=True, show_error=True)