QYBER LLM Council is a full‑stack AI assistant that combines multiple LLMs into a 3‑stage council (Stage 1/2/3), with:
- real‑time chat interface
- document upload (PDF, DOCX, PPT/PPTX)
- conversation management
- export of the final answer to DOCX
- dark UI inspired by ChatGPT
The project was developed in pair‑programming with an AI assistant.
- Create and select conversations in the sidebar.
- The LLM remembers context: previous messages are included in the prompt, with a character limit to stay within model context size.
- You can delete conversations from the sidebar.
- Conversation titles are generated automatically from the first user message.
For each user message, the backend runs a 3‑stage pipeline:
-
Stage 1 – Individual Responses
Several models (COUNCIL_MODELS) answer the question independently.
The frontend shows these answers in a tab view (one tab per model). -
Stage 2 – Peer Rankings
Each model sees anonymized responses from all models (Response A, B, C, …) and:- analyzes them,
- produces a ranking.
The frontend displays:
- a tab per evaluating model,
- an “Aggregate Rankings (Street Cred)” section that combines all votes.
-
Stage 3 – Final Council Answer
A “Chairman” model (CHAIRMAN_MODEL) synthesizes everything into a single final answer, shown in a dedicated dark‑themed block.
Models are configured in backend/config.py, for example:
openai/gpt-5.1google/gemini-3-pro-previewanthropic/claude-sonnet-4.5x-ai/grok-4perplexity/sonar-pro-search
The Chairman is one of these models (by default anthropic/claude-sonnet-4.5).
- Upload PDF, DOCX, PPT/PPTX via:
- the
+button in the input bar, or - drag & drop directly onto the input bar.
- the
- Text extraction on the backend using
pypdf,python-docx,python-pptx(see backend/file_extractor.py). - Extracted text is added to the conversation as a special user message:
- stored in the conversation JSON files,
- included in the context sent to the models.
- On the frontend:
- uploaded documents appear as chips / mini cards in the input area,
- in history they appear as small “📎 filename” pills instead of dumping all the text.
- A backend endpoint generates a
.docxfile from the Stage 3 final answer. - On the frontend, the last assistant message that has
stage3shows an “Export DOCX” button:- click → calls the export API,
- triggers download of
conversation-XXXX-final.docx.
- Dark theme:
- Sidebar:
#181818 - Main chat area:
#212121 - Input bar:
#303030(pill shape) - Send button: round blue button.
- Sidebar:
- Sidebar:
- centered QYBER logo
+ New Conversationbutton- “Projects” section (static list)
- “Your conversations” section (actual conversations).
- Chat:
- empty state with “Start a conversation / Ask a question to consult QYBER”
- user messages in dark bubbles
- Stage 1/2/3 blocks using the same dark theme.
- Framework: FastAPI (Python 3.10+)
- Main modules:
backend/main.py– HTTP endpoints:/api/conversations(list/create)/api/conversations/{id}(get/delete)/api/conversations/{id}/message(non‑streaming council run)/api/conversations/{id}/message/stream(SSE, streaming Stage 1/2/3)/api/conversations/{id}/upload(upload document + text extraction)/api/conversations/{id}/export-docx(generate and download DOCX)
- backend/council.py – 3‑stage council logic, aggregate rankings, title generation
backend/openrouter.py– wrapper around the OpenRouter Chat Completions APIbackend/storage.py– conversation storage as JSON underdata/conversations/- backend/file_extractor.py – PDF/DOCX/PPTX text extraction
- backend/config.py – configuration (API key, models, data directory)
- Context management:
- conversation history is concatenated into a text history string,
- truncated to
MAX_HISTORY_CHARSto avoid exceeding model context.
- Framework: React + Vite
- Libraries:
react-markdownfor rendering markdown answers- native
EventSourcefor SSE streaming from the backend.
- Key components:
App.jsx– top‑level layout, selected conversation, loading state- Sidebar.jsx / Sidebar.css – logo, “Projects”, “Your conversations”, new conversation and delete
ChatInterface.jsx/ ChatInterface.css – messages, Stages, new input bar (pill), drag‑and‑drop, attachment chips, Export DOCX buttonFileUpload.jsx– document upload logic and integration with the upload API- Stage1.jsx / Stage1.css – individual responses view
- Stage2.jsx / Stage2.css – peer rankings + aggregate ranking
- Stage3.jsx / Stage3.css – final council answer view
api.js– small API client (conversations, messages, streaming, upload, export DOCX).
- Python 3.10+
uvfor Python dependency management- Node.js + npm
- An OpenRouter account + API key
Create a .env file at the project root:
OPENROUTER_API_KEY=your_openrouter_api_key