├── .python-version ├── backend ├── __init__.py ├── Dockerfile ├── config.py ├── zenmux.py ├── openrouter.py ├── storage.py ├── main.py └── council.py ├── header.jpg ├── main.py ├── frontend ├── postcss.config.js ├── src │ ├── lib │ │ └── utils.js │ ├── App.css │ ├── main.jsx │ ├── components │ │ ├── Stage3.css │ │ ├── PromoBadge.jsx │ │ ├── ui │ │ │ ├── textarea.jsx │ │ │ ├── tooltip.jsx │ │ │ ├── avatar.jsx │ │ │ ├── tabs.jsx │ │ │ ├── card.jsx │ │ │ ├── scroll-area.jsx │ │ │ ├── button.jsx │ │ │ ├── sheet.jsx │ │ │ └── dropdown-menu.jsx │ │ ├── NavigationPanel.jsx │ │ ├── Stage1.css │ │ ├── Sidebar.css │ │ ├── LanguageSwitcher.jsx │ │ ├── Stage3.jsx │ │ ├── PartnerFooter.css │ │ ├── Sidebar.jsx │ │ ├── PromoBadge.css │ │ ├── Stage1.jsx │ │ ├── ShareButton.jsx │ │ ├── Stage2.css │ │ ├── CouncilAvatars.css │ │ ├── ChatInterface.css │ │ ├── CouncilAvatars.jsx │ │ ├── PartnerFooter.jsx │ │ ├── Stage2.jsx │ │ ├── ModelBeads.jsx │ │ └── ChatInterface.jsx │ ├── assets │ │ └── react.svg │ ├── i18n.js │ ├── api.js │ ├── index.css │ └── App.jsx ├── vite.config.js ├── .gitignore ├── components.json ├── index.html ├── eslint.config.js ├── public │ ├── favicon.svg │ ├── vite.svg │ └── logo.svg ├── README.md ├── package.json └── tailwind.config.js ├── .gitignore ├── pyproject.toml ├── .dockerignore ├── start.sh ├── docker-compose.yml ├── deploy.sh ├── nginx.conf ├── README.md └── CLAUDE.md /.python-version: -------------------------------------------------------------------------------- 1 | 3.10 2 | -------------------------------------------------------------------------------- /backend/__init__.py: -------------------------------------------------------------------------------- 1 | """LLM Council backend package.""" 2 | -------------------------------------------------------------------------------- /header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wquguru/llm-council-zenmux/HEAD/header.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | print("Hello from llm-council!") 3 | 4 | 5 | if __name__ == "__main__": 6 | main() 7 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/lib/utils.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # Virtual environments 10 | .venv 11 | 12 | # Keys and secrets 13 | .env 14 | 15 | # Data files 16 | data/ 17 | 18 | # Frontend 19 | frontend/node_modules/ 20 | frontend/dist/ 21 | frontend/.vite/ -------------------------------------------------------------------------------- /frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import path from "path" 2 | import { defineConfig } from 'vite' 3 | import react from '@vitejs/plugin-react' 4 | 5 | // https://vite.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "llm-council" 3 | version = "0.1.0" 4 | description = "Your LLM Council" 5 | readme = "README.md" 6 | requires-python = ">=3.10" 7 | dependencies = [ 8 | "fastapi>=0.115.0", 9 | "uvicorn[standard]>=0.32.0", 10 | "python-dotenv>=1.0.0", 11 | "httpx[socks]>=0.27.0", 12 | "pydantic>=2.9.0", 13 | "slowapi>=0.1.9", 14 | ] 15 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | .app { 6 | display: flex; 7 | height: 100vh; 8 | width: 100vw; 9 | overflow: hidden; 10 | background: #ffffff; 11 | color: #333; 12 | font-family: 13 | -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", 14 | "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 15 | } 16 | -------------------------------------------------------------------------------- /frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": false, 5 | "tsx": false, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import "./index.css"; 5 | import "./i18n"; // Import i18n configuration 6 | import App from "./App.jsx"; 7 | 8 | createRoot(document.getElementById("root")).render( 9 | 10 | 11 | 12 | 13 | , 14 | ); 15 | -------------------------------------------------------------------------------- /frontend/src/components/Stage3.css: -------------------------------------------------------------------------------- 1 | .stage3 { 2 | background: #f0fff0; 3 | border-color: #c8e6c8; 4 | } 5 | 6 | .final-response { 7 | background: #ffffff; 8 | padding: 20px; 9 | border-radius: 6px; 10 | border: 1px solid #c8e6c8; 11 | } 12 | 13 | .chairman-label { 14 | color: #2d8a2d; 15 | font-size: 12px; 16 | font-family: monospace; 17 | margin-bottom: 12px; 18 | font-weight: 600; 19 | } 20 | 21 | .final-text { 22 | color: #333; 23 | line-height: 1.7; 24 | font-size: 15px; 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/components/PromoBadge.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './PromoBadge.css'; 3 | 4 | export default function PromoBadge({ text, type = 'default', mode = 'promo' }) { 5 | // mode: 'promo' for promotional badges, 'product' for product type badges (LLM, VPS) 6 | const className = mode === 'product' 7 | ? `product-badge product-badge-${type}` 8 | : `promo-badge promo-badge-${type}`; 9 | 10 | return ( 11 | 12 | {text} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | LLM Council 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # Git 2 | .git 3 | .gitignore 4 | 5 | # Environment 6 | .env 7 | .env.* 8 | *.env 9 | 10 | # Python 11 | __pycache__ 12 | *.py[cod] 13 | *$py.class 14 | *.so 15 | .Python 16 | env/ 17 | venv/ 18 | ENV/ 19 | .venv 20 | 21 | # Data and logs 22 | data/ 23 | *.log 24 | 25 | # Frontend 26 | frontend/node_modules 27 | frontend/dist 28 | 29 | # IDE 30 | .vscode 31 | .idea 32 | *.swp 33 | *.swo 34 | *~ 35 | 36 | # OS 37 | .DS_Store 38 | Thumbs.db 39 | 40 | # Docker 41 | docker-compose.yml 42 | Dockerfile 43 | .dockerignore 44 | 45 | # Misc 46 | *.md 47 | !README.md 48 | header.jpg 49 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # Backend Dockerfile for LLM Council 2 | FROM python:3.11-slim 3 | 4 | WORKDIR /app 5 | 6 | # Install uv for dependency management 7 | RUN pip install uv 8 | 9 | # Copy dependency files 10 | COPY pyproject.toml uv.lock ./ 11 | 12 | # Install dependencies using uv 13 | RUN uv sync --frozen 14 | 15 | # Copy backend code 16 | COPY backend ./backend 17 | 18 | # Create data directory for conversations 19 | RUN mkdir -p /app/data/conversations 20 | 21 | # Expose backend port 22 | EXPOSE 8008 23 | 24 | # Run the application 25 | CMD ["uv", "run", "python", "-m", "backend.main"] 26 | -------------------------------------------------------------------------------- /backend/config.py: -------------------------------------------------------------------------------- 1 | """Configuration for the LLM Council.""" 2 | 3 | import os 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | # OpenRouter API key 9 | ZENMUX_API_KEY = os.getenv("ZENMUX_API_KEY") 10 | 11 | # Council members - list of ZenMux model identifiers 12 | COUNCIL_MODELS = [ 13 | "qwen/qwen3-14b", 14 | "x-ai/grok-4.1-fast", 15 | "kuaishou/kat-coder-pro-v1", 16 | ] 17 | 18 | # Chairman model - synthesizes final response 19 | CHAIRMAN_MODEL = "deepseek/deepseek-chat-v3.1" 20 | 21 | # ZenMux API endpoint 22 | ZENMUX_API_URL = "https://zenmux.ai/api/v1/chat/completions" 23 | 24 | # Data directory for conversation storage 25 | DATA_DIR = "data/conversations" 26 | -------------------------------------------------------------------------------- /frontend/src/components/ui/textarea.jsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | const Textarea = React.forwardRef(({ className, ...props }, ref) => { 6 | return ( 7 |