fusefable 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. fusefable-0.1.2/PKG-INFO +221 -0
  2. fusefable-0.1.2/README.md +193 -0
  3. fusefable-0.1.2/fusefable/__init__.py +1 -0
  4. fusefable-0.1.2/fusefable.egg-info/PKG-INFO +221 -0
  5. {fusefable-0.1.1 → fusefable-0.1.2}/pyproject.toml +1 -1
  6. fusefable-0.1.1/PKG-INFO +0 -156
  7. fusefable-0.1.1/README.md +0 -128
  8. fusefable-0.1.1/fusefable/__init__.py +0 -1
  9. fusefable-0.1.1/fusefable.egg-info/PKG-INFO +0 -156
  10. {fusefable-0.1.1 → fusefable-0.1.2}/LICENSE +0 -0
  11. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/cli.py +0 -0
  12. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/client.py +0 -0
  13. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/config.py +0 -0
  14. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/core.py +0 -0
  15. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/cost.py +0 -0
  16. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/fanout.py +0 -0
  17. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/fusion.py +0 -0
  18. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/judge.py +0 -0
  19. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/mcp_server.py +0 -0
  20. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/models.py +0 -0
  21. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/providers/__init__.py +0 -0
  22. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/providers/anthropic.py +0 -0
  23. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/providers/base.py +0 -0
  24. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/providers/factory.py +0 -0
  25. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/providers/google.py +0 -0
  26. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/providers/openai_compat.py +0 -0
  27. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/routing.py +0 -0
  28. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable/wizard.py +0 -0
  29. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable.egg-info/SOURCES.txt +0 -0
  30. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable.egg-info/dependency_links.txt +0 -0
  31. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable.egg-info/entry_points.txt +0 -0
  32. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable.egg-info/requires.txt +0 -0
  33. {fusefable-0.1.1 → fusefable-0.1.2}/fusefable.egg-info/top_level.txt +0 -0
  34. {fusefable-0.1.1 → fusefable-0.1.2}/setup.cfg +0 -0
  35. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_cli.py +0 -0
  36. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_client.py +0 -0
  37. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_config.py +0 -0
  38. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_core.py +0 -0
  39. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_cost.py +0 -0
  40. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_fanout.py +0 -0
  41. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_fusion.py +0 -0
  42. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_judge.py +0 -0
  43. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_mcp_server.py +0 -0
  44. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_models.py +0 -0
  45. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_native_providers.py +0 -0
  46. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_openai_compat.py +0 -0
  47. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_routing.py +0 -0
  48. {fusefable-0.1.1 → fusefable-0.1.2}/tests/test_wizard.py +0 -0
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.4
2
+ Name: fusefable
3
+ Version: 0.1.2
4
+ Summary: Fuse multiple AI models and judge the best answer for coding
5
+ Author: proultrax9
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/proultrax9/fusefable
8
+ Project-URL: Repository, https://github.com/proultrax9/fusefable
9
+ Keywords: llm,ai,openrouter,cli,mcp,fusion,coding
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Environment :: Console
14
+ Classifier: Topic :: Software Development :: Code Generators
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: httpx>=0.27
19
+ Requires-Dist: typer>=0.12
20
+ Requires-Dist: pyyaml>=6.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8.0; extra == "dev"
23
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
24
+ Requires-Dist: respx>=0.21; extra == "dev"
25
+ Provides-Extra: mcp
26
+ Requires-Dist: mcp>=1.2; extra == "mcp"
27
+ Dynamic: license-file
28
+
29
+ # Fuse Fable
30
+
31
+ 🌐 **Languages:** English · [ไทย (Thai)](https://github.com/proultrax9/fusefable/blob/main/README.th.md)
32
+
33
+ Fan out a coding prompt to many AI models **in parallel**, then let a judge pick the
34
+ single best answer — total latency ≈ **x2** (slowest model + one judge pass), not x7–x15.
35
+
36
+ Works three ways: as a **CLI**, as an **MCP server** (connect Cursor / VS Code / Claude),
37
+ and as a **subagent / pipe** (callable by other tools and scripts).
38
+
39
+ ## Install
40
+ ```bash
41
+ pip install fusefable # core
42
+ pip install "fusefable[mcp]" # if you want the MCP server
43
+ ```
44
+ From source:
45
+ ```bash
46
+ git clone https://github.com/proultrax9/fusefable.git
47
+ cd fusefable
48
+ pip install -e ".[mcp]"
49
+ ```
50
+
51
+ ## Setup (first run)
52
+ ```bash
53
+ fusefable config
54
+ ```
55
+ - Choose **AI Gateway** → one key for everything, then it asks "how many models?" and
56
+ prompts for each one.
57
+ - Known gateways (base URL auto-filled): `openrouter`, `groq`, `together`,
58
+ `fireworks`, `deepinfra`, `novita`, `hyperbolic`, `aimlapi`, `portkey`,
59
+ `deepseek`, `openai` — any other works too, just type its base URL.
60
+ - Or **Single providers** → it asks how many, then the **API kind** of each:
61
+ - `openai_compat` — any OpenAI-compatible endpoint (you provide the base URL)
62
+ - `anthropic` — Anthropic native (`/v1/messages`, base URL auto-filled)
63
+ - `google` — Google Gemini native (`generateContent`, base URL auto-filled)
64
+
65
+ Set your API key as an environment variable named as the wizard asks:
66
+ ```bash
67
+ export OPENROUTER_API_KEY=sk-... # macOS/Linux
68
+ setx OPENROUTER_API_KEY "sk-..." # Windows (open a new terminal afterwards)
69
+ ```
70
+
71
+ Config is stored at `~/.fusefable/config.yaml`.
72
+
73
+ ## 1) Use as a CLI
74
+ ```bash
75
+ fusefable ask "Write a quicksort function in Python"
76
+ fusefable ask --show-all "..." # show every answer + judge reason
77
+ fusefable ask --models gpt-5,qwen3-coder "..." # restrict to specific models
78
+ fusefable ask --cheap "..." # use cheap_models from config
79
+ ff ask "..." # short alias
80
+ ```
81
+
82
+ ## 2) Use as a subagent / in a pipe
83
+ ```bash
84
+ fusefable ask --quiet "..." # print only the answer (no headers)
85
+ echo "Explain this code" | fusefable ask --quiet # read the prompt from stdin
86
+ cat bug.py | fusefable ask -q "Find the bug in this code"
87
+ fusefable ask --json "..." # JSON output: answer, chosen_model, reason, cost, candidates
88
+ ```
89
+ `--json` is ideal for scripts/agents that parse the result; `--quiet` is ideal for piping.
90
+
91
+ ## 3) Use as an MCP server (Cursor / VS Code / Claude / other agents)
92
+ Run as an MCP server over stdio:
93
+ ```bash
94
+ fusefable mcp
95
+ ```
96
+ Exposes a tool `fuse_ask(question, models?, cheap?)` for any MCP client.
97
+
98
+ ### Cursor
99
+ `~/.cursor/mcp.json` (or Settings → MCP):
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "fusefable": {
104
+ "command": "fusefable",
105
+ "args": ["mcp"],
106
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### VS Code (Copilot / MCP-compatible extension)
113
+ `.vscode/mcp.json` in your project:
114
+ ```json
115
+ {
116
+ "servers": {
117
+ "fusefable": {
118
+ "command": "fusefable",
119
+ "args": ["mcp"],
120
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Claude Desktop
127
+ `claude_desktop_config.json`:
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "fusefable": {
132
+ "command": "fusefable",
133
+ "args": ["mcp"],
134
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ > Requires `pip install "fusefable[mcp]"` and a completed `fusefable config`.
141
+ > If `fusefable` isn't on the app's PATH, use a full path such as `python -m fusefable.cli`.
142
+
143
+ ## Architecture
144
+
145
+ ```
146
+ ENTRYPOINTS (one shared core)
147
+ ┌─────────────┬──────────────────┬─────────────────────┐
148
+ │ CLI │ pipe / subagent │ MCP server │
149
+ │ fusefable │ stdin · --quiet │ fusefable mcp │
150
+ │ ask "..." │ · --json │ tool: fuse_ask() │
151
+ └──────┬──────┴────────┬─────────┴──────────┬──────────┘
152
+ └───────────────┴────────────────────┘
153
+
154
+
155
+ ┌───────────────────┐ ~/.fusefable/config.yaml
156
+ │ core.fuse() │◀──── (gateway | single providers,
157
+ └─────────┬─────────┘ models, judge, timeout)
158
+
159
+
160
+ ┌───────────────────┐
161
+ │ routing │ build (provider, model) routes
162
+ │ + provider │ via factory → pick adapter by kind
163
+ │ factory │
164
+ └─────────┬─────────┘
165
+
166
+ ┌────────────── FAN-OUT (asyncio.gather) ──────────────┐
167
+ │ ทุกตัวยิงพร้อมกัน · per-model timeout · ตัวพัง = ตัดทิ้ง │
168
+ │ │
169
+ ▼ ▼ ▼ ▼ ▼
170
+ ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐
171
+ │ openai_ │ │ anthropic│ │ google │ │ openai_ │ │ ... │
172
+ │ compat │ │ native │ │ native │ │ compat │ │ │
173
+ │(gateway)│ │/v1/msgs │ │generateCont│ │ │ │ │
174
+ └────┬────┘ └────┬─────┘ └─────┬──────┘ └────┬─────┘ └───┬────┘
175
+ └───────────┴──────────────┴───────────────┴───────────┘
176
+ │ Completion[] (สำเร็จเท่านั้น)
177
+
178
+ ┌───────────────────┐
179
+ │ judge │ ปกปิดชื่อ → Answer A/B/C...
180
+ │ (anonymized) │ ให้ judge model เลือกตัวดีสุด
181
+ └─────────┬─────────┘ (พัง → fallback ตัวแรก)
182
+
183
+ ┌───────────────────┐
184
+ │ FinalAnswer │ text · chosen_model · reason
185
+ │ (+ cost estimate)│ · cost_usd · candidates
186
+ └───────────────────┘
187
+ ```
188
+
189
+ **Request lifecycle**
190
+ 1. **Entrypoint** — CLI, a pipe/subagent call, or the MCP tool `fuse_ask()` — all funnel into one core function `core.fuse()`.
191
+ 2. **Routing** — config (gateway or single providers) is turned into `(provider, model)` routes; a provider **factory** picks the right adapter per `kind` (`openai_compat` / `anthropic` / `google`).
192
+ 3. **Fan-out** — every model is called concurrently via `asyncio.gather` (total time = slowest model). Each call has its own timeout; any model that times out or errors is dropped and never slows the run.
193
+ 4. **Judge** — model names are anonymized (Answer A/B/C...) so the judge picks on quality alone, not by brand; if the judge fails it falls back to the first answer.
194
+ 5. **Result** — returns the best answer with the chosen model, the judge's reason, an estimated cost, and all candidates.
195
+
196
+ **Components** (`fusefable/`)
197
+
198
+ | File | Responsibility |
199
+ |---|---|
200
+ | `cli.py` | Typer CLI (`ask` / `config` / `mcp`), output modes |
201
+ | `mcp_server.py` | MCP server exposing the `fuse_ask` tool |
202
+ | `core.py` | shared `fuse()` entrypoint + model selection |
203
+ | `config.py` | load/save `config.yaml` |
204
+ | `wizard.py` | interactive setup (gateway vs single, API kind) |
205
+ | `routing.py` | config → `(provider, model)` routes |
206
+ | `providers/factory.py` | pick adapter by `kind` |
207
+ | `providers/openai_compat.py` · `anthropic.py` · `google.py` | provider adapters |
208
+ | `fanout.py` | parallel fan-out (drops failures) |
209
+ | `judge.py` | anonymized judging |
210
+ | `fusion.py` | orchestrator: fan-out → judge → `FinalAnswer` |
211
+ | `cost.py` | token-based cost estimate |
212
+ | `models.py` | dataclasses: `Completion`, `FinalAnswer`, `ProviderConfig` |
213
+
214
+ ## Development
215
+ ```bash
216
+ pip install -e ".[dev,mcp]"
217
+ pytest -q
218
+ ```
219
+
220
+ ## License
221
+ MIT
@@ -0,0 +1,193 @@
1
+ # Fuse Fable
2
+
3
+ 🌐 **Languages:** English · [ไทย (Thai)](https://github.com/proultrax9/fusefable/blob/main/README.th.md)
4
+
5
+ Fan out a coding prompt to many AI models **in parallel**, then let a judge pick the
6
+ single best answer — total latency ≈ **x2** (slowest model + one judge pass), not x7–x15.
7
+
8
+ Works three ways: as a **CLI**, as an **MCP server** (connect Cursor / VS Code / Claude),
9
+ and as a **subagent / pipe** (callable by other tools and scripts).
10
+
11
+ ## Install
12
+ ```bash
13
+ pip install fusefable # core
14
+ pip install "fusefable[mcp]" # if you want the MCP server
15
+ ```
16
+ From source:
17
+ ```bash
18
+ git clone https://github.com/proultrax9/fusefable.git
19
+ cd fusefable
20
+ pip install -e ".[mcp]"
21
+ ```
22
+
23
+ ## Setup (first run)
24
+ ```bash
25
+ fusefable config
26
+ ```
27
+ - Choose **AI Gateway** → one key for everything, then it asks "how many models?" and
28
+ prompts for each one.
29
+ - Known gateways (base URL auto-filled): `openrouter`, `groq`, `together`,
30
+ `fireworks`, `deepinfra`, `novita`, `hyperbolic`, `aimlapi`, `portkey`,
31
+ `deepseek`, `openai` — any other works too, just type its base URL.
32
+ - Or **Single providers** → it asks how many, then the **API kind** of each:
33
+ - `openai_compat` — any OpenAI-compatible endpoint (you provide the base URL)
34
+ - `anthropic` — Anthropic native (`/v1/messages`, base URL auto-filled)
35
+ - `google` — Google Gemini native (`generateContent`, base URL auto-filled)
36
+
37
+ Set your API key as an environment variable named as the wizard asks:
38
+ ```bash
39
+ export OPENROUTER_API_KEY=sk-... # macOS/Linux
40
+ setx OPENROUTER_API_KEY "sk-..." # Windows (open a new terminal afterwards)
41
+ ```
42
+
43
+ Config is stored at `~/.fusefable/config.yaml`.
44
+
45
+ ## 1) Use as a CLI
46
+ ```bash
47
+ fusefable ask "Write a quicksort function in Python"
48
+ fusefable ask --show-all "..." # show every answer + judge reason
49
+ fusefable ask --models gpt-5,qwen3-coder "..." # restrict to specific models
50
+ fusefable ask --cheap "..." # use cheap_models from config
51
+ ff ask "..." # short alias
52
+ ```
53
+
54
+ ## 2) Use as a subagent / in a pipe
55
+ ```bash
56
+ fusefable ask --quiet "..." # print only the answer (no headers)
57
+ echo "Explain this code" | fusefable ask --quiet # read the prompt from stdin
58
+ cat bug.py | fusefable ask -q "Find the bug in this code"
59
+ fusefable ask --json "..." # JSON output: answer, chosen_model, reason, cost, candidates
60
+ ```
61
+ `--json` is ideal for scripts/agents that parse the result; `--quiet` is ideal for piping.
62
+
63
+ ## 3) Use as an MCP server (Cursor / VS Code / Claude / other agents)
64
+ Run as an MCP server over stdio:
65
+ ```bash
66
+ fusefable mcp
67
+ ```
68
+ Exposes a tool `fuse_ask(question, models?, cheap?)` for any MCP client.
69
+
70
+ ### Cursor
71
+ `~/.cursor/mcp.json` (or Settings → MCP):
72
+ ```json
73
+ {
74
+ "mcpServers": {
75
+ "fusefable": {
76
+ "command": "fusefable",
77
+ "args": ["mcp"],
78
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### VS Code (Copilot / MCP-compatible extension)
85
+ `.vscode/mcp.json` in your project:
86
+ ```json
87
+ {
88
+ "servers": {
89
+ "fusefable": {
90
+ "command": "fusefable",
91
+ "args": ["mcp"],
92
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### Claude Desktop
99
+ `claude_desktop_config.json`:
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "fusefable": {
104
+ "command": "fusefable",
105
+ "args": ["mcp"],
106
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ > Requires `pip install "fusefable[mcp]"` and a completed `fusefable config`.
113
+ > If `fusefable` isn't on the app's PATH, use a full path such as `python -m fusefable.cli`.
114
+
115
+ ## Architecture
116
+
117
+ ```
118
+ ENTRYPOINTS (one shared core)
119
+ ┌─────────────┬──────────────────┬─────────────────────┐
120
+ │ CLI │ pipe / subagent │ MCP server │
121
+ │ fusefable │ stdin · --quiet │ fusefable mcp │
122
+ │ ask "..." │ · --json │ tool: fuse_ask() │
123
+ └──────┬──────┴────────┬─────────┴──────────┬──────────┘
124
+ └───────────────┴────────────────────┘
125
+
126
+
127
+ ┌───────────────────┐ ~/.fusefable/config.yaml
128
+ │ core.fuse() │◀──── (gateway | single providers,
129
+ └─────────┬─────────┘ models, judge, timeout)
130
+
131
+
132
+ ┌───────────────────┐
133
+ │ routing │ build (provider, model) routes
134
+ │ + provider │ via factory → pick adapter by kind
135
+ │ factory │
136
+ └─────────┬─────────┘
137
+
138
+ ┌────────────── FAN-OUT (asyncio.gather) ──────────────┐
139
+ │ ทุกตัวยิงพร้อมกัน · per-model timeout · ตัวพัง = ตัดทิ้ง │
140
+ │ │
141
+ ▼ ▼ ▼ ▼ ▼
142
+ ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐
143
+ │ openai_ │ │ anthropic│ │ google │ │ openai_ │ │ ... │
144
+ │ compat │ │ native │ │ native │ │ compat │ │ │
145
+ │(gateway)│ │/v1/msgs │ │generateCont│ │ │ │ │
146
+ └────┬────┘ └────┬─────┘ └─────┬──────┘ └────┬─────┘ └───┬────┘
147
+ └───────────┴──────────────┴───────────────┴───────────┘
148
+ │ Completion[] (สำเร็จเท่านั้น)
149
+
150
+ ┌───────────────────┐
151
+ │ judge │ ปกปิดชื่อ → Answer A/B/C...
152
+ │ (anonymized) │ ให้ judge model เลือกตัวดีสุด
153
+ └─────────┬─────────┘ (พัง → fallback ตัวแรก)
154
+
155
+ ┌───────────────────┐
156
+ │ FinalAnswer │ text · chosen_model · reason
157
+ │ (+ cost estimate)│ · cost_usd · candidates
158
+ └───────────────────┘
159
+ ```
160
+
161
+ **Request lifecycle**
162
+ 1. **Entrypoint** — CLI, a pipe/subagent call, or the MCP tool `fuse_ask()` — all funnel into one core function `core.fuse()`.
163
+ 2. **Routing** — config (gateway or single providers) is turned into `(provider, model)` routes; a provider **factory** picks the right adapter per `kind` (`openai_compat` / `anthropic` / `google`).
164
+ 3. **Fan-out** — every model is called concurrently via `asyncio.gather` (total time = slowest model). Each call has its own timeout; any model that times out or errors is dropped and never slows the run.
165
+ 4. **Judge** — model names are anonymized (Answer A/B/C...) so the judge picks on quality alone, not by brand; if the judge fails it falls back to the first answer.
166
+ 5. **Result** — returns the best answer with the chosen model, the judge's reason, an estimated cost, and all candidates.
167
+
168
+ **Components** (`fusefable/`)
169
+
170
+ | File | Responsibility |
171
+ |---|---|
172
+ | `cli.py` | Typer CLI (`ask` / `config` / `mcp`), output modes |
173
+ | `mcp_server.py` | MCP server exposing the `fuse_ask` tool |
174
+ | `core.py` | shared `fuse()` entrypoint + model selection |
175
+ | `config.py` | load/save `config.yaml` |
176
+ | `wizard.py` | interactive setup (gateway vs single, API kind) |
177
+ | `routing.py` | config → `(provider, model)` routes |
178
+ | `providers/factory.py` | pick adapter by `kind` |
179
+ | `providers/openai_compat.py` · `anthropic.py` · `google.py` | provider adapters |
180
+ | `fanout.py` | parallel fan-out (drops failures) |
181
+ | `judge.py` | anonymized judging |
182
+ | `fusion.py` | orchestrator: fan-out → judge → `FinalAnswer` |
183
+ | `cost.py` | token-based cost estimate |
184
+ | `models.py` | dataclasses: `Completion`, `FinalAnswer`, `ProviderConfig` |
185
+
186
+ ## Development
187
+ ```bash
188
+ pip install -e ".[dev,mcp]"
189
+ pytest -q
190
+ ```
191
+
192
+ ## License
193
+ MIT
@@ -0,0 +1 @@
1
+ __version__ = "0.1.2"
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.4
2
+ Name: fusefable
3
+ Version: 0.1.2
4
+ Summary: Fuse multiple AI models and judge the best answer for coding
5
+ Author: proultrax9
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/proultrax9/fusefable
8
+ Project-URL: Repository, https://github.com/proultrax9/fusefable
9
+ Keywords: llm,ai,openrouter,cli,mcp,fusion,coding
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Environment :: Console
14
+ Classifier: Topic :: Software Development :: Code Generators
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: httpx>=0.27
19
+ Requires-Dist: typer>=0.12
20
+ Requires-Dist: pyyaml>=6.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8.0; extra == "dev"
23
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
24
+ Requires-Dist: respx>=0.21; extra == "dev"
25
+ Provides-Extra: mcp
26
+ Requires-Dist: mcp>=1.2; extra == "mcp"
27
+ Dynamic: license-file
28
+
29
+ # Fuse Fable
30
+
31
+ 🌐 **Languages:** English · [ไทย (Thai)](https://github.com/proultrax9/fusefable/blob/main/README.th.md)
32
+
33
+ Fan out a coding prompt to many AI models **in parallel**, then let a judge pick the
34
+ single best answer — total latency ≈ **x2** (slowest model + one judge pass), not x7–x15.
35
+
36
+ Works three ways: as a **CLI**, as an **MCP server** (connect Cursor / VS Code / Claude),
37
+ and as a **subagent / pipe** (callable by other tools and scripts).
38
+
39
+ ## Install
40
+ ```bash
41
+ pip install fusefable # core
42
+ pip install "fusefable[mcp]" # if you want the MCP server
43
+ ```
44
+ From source:
45
+ ```bash
46
+ git clone https://github.com/proultrax9/fusefable.git
47
+ cd fusefable
48
+ pip install -e ".[mcp]"
49
+ ```
50
+
51
+ ## Setup (first run)
52
+ ```bash
53
+ fusefable config
54
+ ```
55
+ - Choose **AI Gateway** → one key for everything, then it asks "how many models?" and
56
+ prompts for each one.
57
+ - Known gateways (base URL auto-filled): `openrouter`, `groq`, `together`,
58
+ `fireworks`, `deepinfra`, `novita`, `hyperbolic`, `aimlapi`, `portkey`,
59
+ `deepseek`, `openai` — any other works too, just type its base URL.
60
+ - Or **Single providers** → it asks how many, then the **API kind** of each:
61
+ - `openai_compat` — any OpenAI-compatible endpoint (you provide the base URL)
62
+ - `anthropic` — Anthropic native (`/v1/messages`, base URL auto-filled)
63
+ - `google` — Google Gemini native (`generateContent`, base URL auto-filled)
64
+
65
+ Set your API key as an environment variable named as the wizard asks:
66
+ ```bash
67
+ export OPENROUTER_API_KEY=sk-... # macOS/Linux
68
+ setx OPENROUTER_API_KEY "sk-..." # Windows (open a new terminal afterwards)
69
+ ```
70
+
71
+ Config is stored at `~/.fusefable/config.yaml`.
72
+
73
+ ## 1) Use as a CLI
74
+ ```bash
75
+ fusefable ask "Write a quicksort function in Python"
76
+ fusefable ask --show-all "..." # show every answer + judge reason
77
+ fusefable ask --models gpt-5,qwen3-coder "..." # restrict to specific models
78
+ fusefable ask --cheap "..." # use cheap_models from config
79
+ ff ask "..." # short alias
80
+ ```
81
+
82
+ ## 2) Use as a subagent / in a pipe
83
+ ```bash
84
+ fusefable ask --quiet "..." # print only the answer (no headers)
85
+ echo "Explain this code" | fusefable ask --quiet # read the prompt from stdin
86
+ cat bug.py | fusefable ask -q "Find the bug in this code"
87
+ fusefable ask --json "..." # JSON output: answer, chosen_model, reason, cost, candidates
88
+ ```
89
+ `--json` is ideal for scripts/agents that parse the result; `--quiet` is ideal for piping.
90
+
91
+ ## 3) Use as an MCP server (Cursor / VS Code / Claude / other agents)
92
+ Run as an MCP server over stdio:
93
+ ```bash
94
+ fusefable mcp
95
+ ```
96
+ Exposes a tool `fuse_ask(question, models?, cheap?)` for any MCP client.
97
+
98
+ ### Cursor
99
+ `~/.cursor/mcp.json` (or Settings → MCP):
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "fusefable": {
104
+ "command": "fusefable",
105
+ "args": ["mcp"],
106
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### VS Code (Copilot / MCP-compatible extension)
113
+ `.vscode/mcp.json` in your project:
114
+ ```json
115
+ {
116
+ "servers": {
117
+ "fusefable": {
118
+ "command": "fusefable",
119
+ "args": ["mcp"],
120
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Claude Desktop
127
+ `claude_desktop_config.json`:
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "fusefable": {
132
+ "command": "fusefable",
133
+ "args": ["mcp"],
134
+ "env": { "OPENROUTER_API_KEY": "sk-..." }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ > Requires `pip install "fusefable[mcp]"` and a completed `fusefable config`.
141
+ > If `fusefable` isn't on the app's PATH, use a full path such as `python -m fusefable.cli`.
142
+
143
+ ## Architecture
144
+
145
+ ```
146
+ ENTRYPOINTS (one shared core)
147
+ ┌─────────────┬──────────────────┬─────────────────────┐
148
+ │ CLI │ pipe / subagent │ MCP server │
149
+ │ fusefable │ stdin · --quiet │ fusefable mcp │
150
+ │ ask "..." │ · --json │ tool: fuse_ask() │
151
+ └──────┬──────┴────────┬─────────┴──────────┬──────────┘
152
+ └───────────────┴────────────────────┘
153
+
154
+
155
+ ┌───────────────────┐ ~/.fusefable/config.yaml
156
+ │ core.fuse() │◀──── (gateway | single providers,
157
+ └─────────┬─────────┘ models, judge, timeout)
158
+
159
+
160
+ ┌───────────────────┐
161
+ │ routing │ build (provider, model) routes
162
+ │ + provider │ via factory → pick adapter by kind
163
+ │ factory │
164
+ └─────────┬─────────┘
165
+
166
+ ┌────────────── FAN-OUT (asyncio.gather) ──────────────┐
167
+ │ ทุกตัวยิงพร้อมกัน · per-model timeout · ตัวพัง = ตัดทิ้ง │
168
+ │ │
169
+ ▼ ▼ ▼ ▼ ▼
170
+ ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌────────┐
171
+ │ openai_ │ │ anthropic│ │ google │ │ openai_ │ │ ... │
172
+ │ compat │ │ native │ │ native │ │ compat │ │ │
173
+ │(gateway)│ │/v1/msgs │ │generateCont│ │ │ │ │
174
+ └────┬────┘ └────┬─────┘ └─────┬──────┘ └────┬─────┘ └───┬────┘
175
+ └───────────┴──────────────┴───────────────┴───────────┘
176
+ │ Completion[] (สำเร็จเท่านั้น)
177
+
178
+ ┌───────────────────┐
179
+ │ judge │ ปกปิดชื่อ → Answer A/B/C...
180
+ │ (anonymized) │ ให้ judge model เลือกตัวดีสุด
181
+ └─────────┬─────────┘ (พัง → fallback ตัวแรก)
182
+
183
+ ┌───────────────────┐
184
+ │ FinalAnswer │ text · chosen_model · reason
185
+ │ (+ cost estimate)│ · cost_usd · candidates
186
+ └───────────────────┘
187
+ ```
188
+
189
+ **Request lifecycle**
190
+ 1. **Entrypoint** — CLI, a pipe/subagent call, or the MCP tool `fuse_ask()` — all funnel into one core function `core.fuse()`.
191
+ 2. **Routing** — config (gateway or single providers) is turned into `(provider, model)` routes; a provider **factory** picks the right adapter per `kind` (`openai_compat` / `anthropic` / `google`).
192
+ 3. **Fan-out** — every model is called concurrently via `asyncio.gather` (total time = slowest model). Each call has its own timeout; any model that times out or errors is dropped and never slows the run.
193
+ 4. **Judge** — model names are anonymized (Answer A/B/C...) so the judge picks on quality alone, not by brand; if the judge fails it falls back to the first answer.
194
+ 5. **Result** — returns the best answer with the chosen model, the judge's reason, an estimated cost, and all candidates.
195
+
196
+ **Components** (`fusefable/`)
197
+
198
+ | File | Responsibility |
199
+ |---|---|
200
+ | `cli.py` | Typer CLI (`ask` / `config` / `mcp`), output modes |
201
+ | `mcp_server.py` | MCP server exposing the `fuse_ask` tool |
202
+ | `core.py` | shared `fuse()` entrypoint + model selection |
203
+ | `config.py` | load/save `config.yaml` |
204
+ | `wizard.py` | interactive setup (gateway vs single, API kind) |
205
+ | `routing.py` | config → `(provider, model)` routes |
206
+ | `providers/factory.py` | pick adapter by `kind` |
207
+ | `providers/openai_compat.py` · `anthropic.py` · `google.py` | provider adapters |
208
+ | `fanout.py` | parallel fan-out (drops failures) |
209
+ | `judge.py` | anonymized judging |
210
+ | `fusion.py` | orchestrator: fan-out → judge → `FinalAnswer` |
211
+ | `cost.py` | token-based cost estimate |
212
+ | `models.py` | dataclasses: `Completion`, `FinalAnswer`, `ProviderConfig` |
213
+
214
+ ## Development
215
+ ```bash
216
+ pip install -e ".[dev,mcp]"
217
+ pytest -q
218
+ ```
219
+
220
+ ## License
221
+ MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fusefable"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "Fuse multiple AI models and judge the best answer for coding"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
fusefable-0.1.1/PKG-INFO DELETED
@@ -1,156 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: fusefable
3
- Version: 0.1.1
4
- Summary: Fuse multiple AI models and judge the best answer for coding
5
- Author: proultrax9
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/proultrax9/fusefable
8
- Project-URL: Repository, https://github.com/proultrax9/fusefable
9
- Keywords: llm,ai,openrouter,cli,mcp,fusion,coding
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Environment :: Console
14
- Classifier: Topic :: Software Development :: Code Generators
15
- Requires-Python: >=3.10
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: httpx>=0.27
19
- Requires-Dist: typer>=0.12
20
- Requires-Dist: pyyaml>=6.0
21
- Provides-Extra: dev
22
- Requires-Dist: pytest>=8.0; extra == "dev"
23
- Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
24
- Requires-Dist: respx>=0.21; extra == "dev"
25
- Provides-Extra: mcp
26
- Requires-Dist: mcp>=1.2; extra == "mcp"
27
- Dynamic: license-file
28
-
29
- # Fuse Fable
30
-
31
- 🌐 **Languages:** English · [ไทย (Thai)](https://github.com/proultrax9/fusefable/blob/main/README.th.md)
32
-
33
- Fan out a coding prompt to many AI models **in parallel**, then let a judge pick the
34
- single best answer — total latency ≈ **x2** (slowest model + one judge pass), not x7–x15.
35
-
36
- Works three ways: as a **CLI**, as an **MCP server** (connect Cursor / VS Code / Claude),
37
- and as a **subagent / pipe** (callable by other tools and scripts).
38
-
39
- ## Install
40
- ```bash
41
- pip install fusefable # core
42
- pip install "fusefable[mcp]" # if you want the MCP server
43
- ```
44
- From source:
45
- ```bash
46
- git clone https://github.com/proultrax9/fusefable.git
47
- cd fusefable
48
- pip install -e ".[mcp]"
49
- ```
50
-
51
- ## Setup (first run)
52
- ```bash
53
- fusefable config
54
- ```
55
- - Choose **AI Gateway** → one key for everything, then it asks "how many models?" and
56
- prompts for each one.
57
- - Known gateways (base URL auto-filled): `openrouter`, `groq`, `together`,
58
- `fireworks`, `deepinfra`, `novita`, `hyperbolic`, `aimlapi`, `portkey`,
59
- `deepseek`, `openai` — any other works too, just type its base URL.
60
- - Or **Single providers** → it asks how many, then the **API kind** of each:
61
- - `openai_compat` — any OpenAI-compatible endpoint (you provide the base URL)
62
- - `anthropic` — Anthropic native (`/v1/messages`, base URL auto-filled)
63
- - `google` — Google Gemini native (`generateContent`, base URL auto-filled)
64
-
65
- Set your API key as an environment variable named as the wizard asks:
66
- ```bash
67
- export OPENROUTER_API_KEY=sk-... # macOS/Linux
68
- setx OPENROUTER_API_KEY "sk-..." # Windows (open a new terminal afterwards)
69
- ```
70
-
71
- Config is stored at `~/.fusefable/config.yaml`.
72
-
73
- ## 1) Use as a CLI
74
- ```bash
75
- fusefable ask "Write a quicksort function in Python"
76
- fusefable ask --show-all "..." # show every answer + judge reason
77
- fusefable ask --models gpt-5,qwen3-coder "..." # restrict to specific models
78
- fusefable ask --cheap "..." # use cheap_models from config
79
- ff ask "..." # short alias
80
- ```
81
-
82
- ## 2) Use as a subagent / in a pipe
83
- ```bash
84
- fusefable ask --quiet "..." # print only the answer (no headers)
85
- echo "Explain this code" | fusefable ask --quiet # read the prompt from stdin
86
- cat bug.py | fusefable ask -q "Find the bug in this code"
87
- fusefable ask --json "..." # JSON output: answer, chosen_model, reason, cost, candidates
88
- ```
89
- `--json` is ideal for scripts/agents that parse the result; `--quiet` is ideal for piping.
90
-
91
- ## 3) Use as an MCP server (Cursor / VS Code / Claude / other agents)
92
- Run as an MCP server over stdio:
93
- ```bash
94
- fusefable mcp
95
- ```
96
- Exposes a tool `fuse_ask(question, models?, cheap?)` for any MCP client.
97
-
98
- ### Cursor
99
- `~/.cursor/mcp.json` (or Settings → MCP):
100
- ```json
101
- {
102
- "mcpServers": {
103
- "fusefable": {
104
- "command": "fusefable",
105
- "args": ["mcp"],
106
- "env": { "OPENROUTER_API_KEY": "sk-..." }
107
- }
108
- }
109
- }
110
- ```
111
-
112
- ### VS Code (Copilot / MCP-compatible extension)
113
- `.vscode/mcp.json` in your project:
114
- ```json
115
- {
116
- "servers": {
117
- "fusefable": {
118
- "command": "fusefable",
119
- "args": ["mcp"],
120
- "env": { "OPENROUTER_API_KEY": "sk-..." }
121
- }
122
- }
123
- }
124
- ```
125
-
126
- ### Claude Desktop
127
- `claude_desktop_config.json`:
128
- ```json
129
- {
130
- "mcpServers": {
131
- "fusefable": {
132
- "command": "fusefable",
133
- "args": ["mcp"],
134
- "env": { "OPENROUTER_API_KEY": "sk-..." }
135
- }
136
- }
137
- }
138
- ```
139
-
140
- > Requires `pip install "fusefable[mcp]"` and a completed `fusefable config`.
141
- > If `fusefable` isn't on the app's PATH, use a full path such as `python -m fusefable.cli`.
142
-
143
- ## How it works
144
- 1. **Fan-out** — every model is called concurrently via `asyncio` (total time = slowest model).
145
- 2. Any model that times out or fails is dropped — it never slows the whole run.
146
- 3. **Judge** — model names are anonymized (Answer A/B/C...) and a judge model picks the best.
147
- 4. Returns the best answer plus an estimated cost.
148
-
149
- ## Development
150
- ```bash
151
- pip install -e ".[dev,mcp]"
152
- pytest -q
153
- ```
154
-
155
- ## License
156
- MIT
fusefable-0.1.1/README.md DELETED
@@ -1,128 +0,0 @@
1
- # Fuse Fable
2
-
3
- 🌐 **Languages:** English · [ไทย (Thai)](https://github.com/proultrax9/fusefable/blob/main/README.th.md)
4
-
5
- Fan out a coding prompt to many AI models **in parallel**, then let a judge pick the
6
- single best answer — total latency ≈ **x2** (slowest model + one judge pass), not x7–x15.
7
-
8
- Works three ways: as a **CLI**, as an **MCP server** (connect Cursor / VS Code / Claude),
9
- and as a **subagent / pipe** (callable by other tools and scripts).
10
-
11
- ## Install
12
- ```bash
13
- pip install fusefable # core
14
- pip install "fusefable[mcp]" # if you want the MCP server
15
- ```
16
- From source:
17
- ```bash
18
- git clone https://github.com/proultrax9/fusefable.git
19
- cd fusefable
20
- pip install -e ".[mcp]"
21
- ```
22
-
23
- ## Setup (first run)
24
- ```bash
25
- fusefable config
26
- ```
27
- - Choose **AI Gateway** → one key for everything, then it asks "how many models?" and
28
- prompts for each one.
29
- - Known gateways (base URL auto-filled): `openrouter`, `groq`, `together`,
30
- `fireworks`, `deepinfra`, `novita`, `hyperbolic`, `aimlapi`, `portkey`,
31
- `deepseek`, `openai` — any other works too, just type its base URL.
32
- - Or **Single providers** → it asks how many, then the **API kind** of each:
33
- - `openai_compat` — any OpenAI-compatible endpoint (you provide the base URL)
34
- - `anthropic` — Anthropic native (`/v1/messages`, base URL auto-filled)
35
- - `google` — Google Gemini native (`generateContent`, base URL auto-filled)
36
-
37
- Set your API key as an environment variable named as the wizard asks:
38
- ```bash
39
- export OPENROUTER_API_KEY=sk-... # macOS/Linux
40
- setx OPENROUTER_API_KEY "sk-..." # Windows (open a new terminal afterwards)
41
- ```
42
-
43
- Config is stored at `~/.fusefable/config.yaml`.
44
-
45
- ## 1) Use as a CLI
46
- ```bash
47
- fusefable ask "Write a quicksort function in Python"
48
- fusefable ask --show-all "..." # show every answer + judge reason
49
- fusefable ask --models gpt-5,qwen3-coder "..." # restrict to specific models
50
- fusefable ask --cheap "..." # use cheap_models from config
51
- ff ask "..." # short alias
52
- ```
53
-
54
- ## 2) Use as a subagent / in a pipe
55
- ```bash
56
- fusefable ask --quiet "..." # print only the answer (no headers)
57
- echo "Explain this code" | fusefable ask --quiet # read the prompt from stdin
58
- cat bug.py | fusefable ask -q "Find the bug in this code"
59
- fusefable ask --json "..." # JSON output: answer, chosen_model, reason, cost, candidates
60
- ```
61
- `--json` is ideal for scripts/agents that parse the result; `--quiet` is ideal for piping.
62
-
63
- ## 3) Use as an MCP server (Cursor / VS Code / Claude / other agents)
64
- Run as an MCP server over stdio:
65
- ```bash
66
- fusefable mcp
67
- ```
68
- Exposes a tool `fuse_ask(question, models?, cheap?)` for any MCP client.
69
-
70
- ### Cursor
71
- `~/.cursor/mcp.json` (or Settings → MCP):
72
- ```json
73
- {
74
- "mcpServers": {
75
- "fusefable": {
76
- "command": "fusefable",
77
- "args": ["mcp"],
78
- "env": { "OPENROUTER_API_KEY": "sk-..." }
79
- }
80
- }
81
- }
82
- ```
83
-
84
- ### VS Code (Copilot / MCP-compatible extension)
85
- `.vscode/mcp.json` in your project:
86
- ```json
87
- {
88
- "servers": {
89
- "fusefable": {
90
- "command": "fusefable",
91
- "args": ["mcp"],
92
- "env": { "OPENROUTER_API_KEY": "sk-..." }
93
- }
94
- }
95
- }
96
- ```
97
-
98
- ### Claude Desktop
99
- `claude_desktop_config.json`:
100
- ```json
101
- {
102
- "mcpServers": {
103
- "fusefable": {
104
- "command": "fusefable",
105
- "args": ["mcp"],
106
- "env": { "OPENROUTER_API_KEY": "sk-..." }
107
- }
108
- }
109
- }
110
- ```
111
-
112
- > Requires `pip install "fusefable[mcp]"` and a completed `fusefable config`.
113
- > If `fusefable` isn't on the app's PATH, use a full path such as `python -m fusefable.cli`.
114
-
115
- ## How it works
116
- 1. **Fan-out** — every model is called concurrently via `asyncio` (total time = slowest model).
117
- 2. Any model that times out or fails is dropped — it never slows the whole run.
118
- 3. **Judge** — model names are anonymized (Answer A/B/C...) and a judge model picks the best.
119
- 4. Returns the best answer plus an estimated cost.
120
-
121
- ## Development
122
- ```bash
123
- pip install -e ".[dev,mcp]"
124
- pytest -q
125
- ```
126
-
127
- ## License
128
- MIT
@@ -1 +0,0 @@
1
- __version__ = "0.1.1"
@@ -1,156 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: fusefable
3
- Version: 0.1.1
4
- Summary: Fuse multiple AI models and judge the best answer for coding
5
- Author: proultrax9
6
- License: MIT
7
- Project-URL: Homepage, https://github.com/proultrax9/fusefable
8
- Project-URL: Repository, https://github.com/proultrax9/fusefable
9
- Keywords: llm,ai,openrouter,cli,mcp,fusion,coding
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Environment :: Console
14
- Classifier: Topic :: Software Development :: Code Generators
15
- Requires-Python: >=3.10
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: httpx>=0.27
19
- Requires-Dist: typer>=0.12
20
- Requires-Dist: pyyaml>=6.0
21
- Provides-Extra: dev
22
- Requires-Dist: pytest>=8.0; extra == "dev"
23
- Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
24
- Requires-Dist: respx>=0.21; extra == "dev"
25
- Provides-Extra: mcp
26
- Requires-Dist: mcp>=1.2; extra == "mcp"
27
- Dynamic: license-file
28
-
29
- # Fuse Fable
30
-
31
- 🌐 **Languages:** English · [ไทย (Thai)](https://github.com/proultrax9/fusefable/blob/main/README.th.md)
32
-
33
- Fan out a coding prompt to many AI models **in parallel**, then let a judge pick the
34
- single best answer — total latency ≈ **x2** (slowest model + one judge pass), not x7–x15.
35
-
36
- Works three ways: as a **CLI**, as an **MCP server** (connect Cursor / VS Code / Claude),
37
- and as a **subagent / pipe** (callable by other tools and scripts).
38
-
39
- ## Install
40
- ```bash
41
- pip install fusefable # core
42
- pip install "fusefable[mcp]" # if you want the MCP server
43
- ```
44
- From source:
45
- ```bash
46
- git clone https://github.com/proultrax9/fusefable.git
47
- cd fusefable
48
- pip install -e ".[mcp]"
49
- ```
50
-
51
- ## Setup (first run)
52
- ```bash
53
- fusefable config
54
- ```
55
- - Choose **AI Gateway** → one key for everything, then it asks "how many models?" and
56
- prompts for each one.
57
- - Known gateways (base URL auto-filled): `openrouter`, `groq`, `together`,
58
- `fireworks`, `deepinfra`, `novita`, `hyperbolic`, `aimlapi`, `portkey`,
59
- `deepseek`, `openai` — any other works too, just type its base URL.
60
- - Or **Single providers** → it asks how many, then the **API kind** of each:
61
- - `openai_compat` — any OpenAI-compatible endpoint (you provide the base URL)
62
- - `anthropic` — Anthropic native (`/v1/messages`, base URL auto-filled)
63
- - `google` — Google Gemini native (`generateContent`, base URL auto-filled)
64
-
65
- Set your API key as an environment variable named as the wizard asks:
66
- ```bash
67
- export OPENROUTER_API_KEY=sk-... # macOS/Linux
68
- setx OPENROUTER_API_KEY "sk-..." # Windows (open a new terminal afterwards)
69
- ```
70
-
71
- Config is stored at `~/.fusefable/config.yaml`.
72
-
73
- ## 1) Use as a CLI
74
- ```bash
75
- fusefable ask "Write a quicksort function in Python"
76
- fusefable ask --show-all "..." # show every answer + judge reason
77
- fusefable ask --models gpt-5,qwen3-coder "..." # restrict to specific models
78
- fusefable ask --cheap "..." # use cheap_models from config
79
- ff ask "..." # short alias
80
- ```
81
-
82
- ## 2) Use as a subagent / in a pipe
83
- ```bash
84
- fusefable ask --quiet "..." # print only the answer (no headers)
85
- echo "Explain this code" | fusefable ask --quiet # read the prompt from stdin
86
- cat bug.py | fusefable ask -q "Find the bug in this code"
87
- fusefable ask --json "..." # JSON output: answer, chosen_model, reason, cost, candidates
88
- ```
89
- `--json` is ideal for scripts/agents that parse the result; `--quiet` is ideal for piping.
90
-
91
- ## 3) Use as an MCP server (Cursor / VS Code / Claude / other agents)
92
- Run as an MCP server over stdio:
93
- ```bash
94
- fusefable mcp
95
- ```
96
- Exposes a tool `fuse_ask(question, models?, cheap?)` for any MCP client.
97
-
98
- ### Cursor
99
- `~/.cursor/mcp.json` (or Settings → MCP):
100
- ```json
101
- {
102
- "mcpServers": {
103
- "fusefable": {
104
- "command": "fusefable",
105
- "args": ["mcp"],
106
- "env": { "OPENROUTER_API_KEY": "sk-..." }
107
- }
108
- }
109
- }
110
- ```
111
-
112
- ### VS Code (Copilot / MCP-compatible extension)
113
- `.vscode/mcp.json` in your project:
114
- ```json
115
- {
116
- "servers": {
117
- "fusefable": {
118
- "command": "fusefable",
119
- "args": ["mcp"],
120
- "env": { "OPENROUTER_API_KEY": "sk-..." }
121
- }
122
- }
123
- }
124
- ```
125
-
126
- ### Claude Desktop
127
- `claude_desktop_config.json`:
128
- ```json
129
- {
130
- "mcpServers": {
131
- "fusefable": {
132
- "command": "fusefable",
133
- "args": ["mcp"],
134
- "env": { "OPENROUTER_API_KEY": "sk-..." }
135
- }
136
- }
137
- }
138
- ```
139
-
140
- > Requires `pip install "fusefable[mcp]"` and a completed `fusefable config`.
141
- > If `fusefable` isn't on the app's PATH, use a full path such as `python -m fusefable.cli`.
142
-
143
- ## How it works
144
- 1. **Fan-out** — every model is called concurrently via `asyncio` (total time = slowest model).
145
- 2. Any model that times out or fails is dropped — it never slows the whole run.
146
- 3. **Judge** — model names are anonymized (Answer A/B/C...) and a judge model picks the best.
147
- 4. Returns the best answer plus an estimated cost.
148
-
149
- ## Development
150
- ```bash
151
- pip install -e ".[dev,mcp]"
152
- pytest -q
153
- ```
154
-
155
- ## License
156
- MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes