fusionkit 0.1.1__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.
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.3
2
+ Name: fusionkit
3
+ Version: 0.1.1
4
+ Summary: Command line interface for fusionkit: local response-level model fusion.
5
+ Requires-Dist: fusionkit-core==0.1.1
6
+ Requires-Dist: fusionkit-evals==0.1.1
7
+ Requires-Dist: fusionkit-mlx==0.1.1
8
+ Requires-Dist: fusionkit-server==0.1.1
9
+ Requires-Dist: typer>=0.20.0
10
+ Requires-Python: >=3.11
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "fusionkit"
3
+ version = "0.1.1"
4
+ description = "Command line interface for fusionkit: local response-level model fusion."
5
+ requires-python = ">=3.11"
6
+ dependencies = [
7
+ "fusionkit-core==0.1.1",
8
+ "fusionkit-evals==0.1.1",
9
+ "fusionkit-mlx==0.1.1",
10
+ "fusionkit-server==0.1.1",
11
+ "typer>=0.20.0",
12
+ ]
13
+
14
+ [project.scripts]
15
+ fusionkit = "fusionkit_cli.main:app"
16
+
17
+ [tool.uv.build-backend]
18
+ module-name = "fusionkit_cli"
19
+
20
+ [build-system]
21
+ requires = ["uv_build>=0.11.21,<0.12.0"]
22
+ build-backend = "uv_build"
@@ -0,0 +1,3 @@
1
+ from fusionkit_cli.main import app
2
+
3
+ __all__ = ["app"]
@@ -0,0 +1,237 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import json
5
+ import shlex
6
+ from pathlib import Path
7
+ from typing import Annotated
8
+
9
+ import typer
10
+ import uvicorn
11
+ from fusionkit_core.clients import build_clients
12
+ from fusionkit_core.config import FusionMode, load_config
13
+ from fusionkit_core.fusion import FusionEngine
14
+ from fusionkit_evals.benchmark import BenchmarkRunner, load_jsonl_samples, write_jsonl_results
15
+ from fusionkit_evals.fusion_bench import (
16
+ CommandHandoffKitExecutor,
17
+ FusionBenchReport,
18
+ FusionBenchRunner,
19
+ build_fusion_bench_report,
20
+ load_benchmark_tasks,
21
+ load_fusion_bench_jsonl,
22
+ write_fusion_bench_html_report,
23
+ write_fusion_bench_jsonl,
24
+ write_fusion_bench_markdown_report,
25
+ write_fusion_bench_report_jsonl,
26
+ )
27
+ from fusionkit_evals.pareto import load_points, write_pareto_report
28
+ from fusionkit_evals.tiny import (
29
+ load_tiny_tasks,
30
+ run_tiny_benchmark,
31
+ write_tiny_benchmark_report,
32
+ write_tiny_jsonl,
33
+ )
34
+ from fusionkit_server.app import create_app
35
+ from fusionkit_server.openai_endpoint import build_endpoint, serve_single_endpoint
36
+
37
+ app = typer.Typer(help="Local model fusion toolkit.")
38
+
39
+
40
+ @app.command()
41
+ def serve(
42
+ config: Annotated[Path, typer.Option("--config", "-c")],
43
+ host: str = "127.0.0.1",
44
+ port: int = 8080,
45
+ ) -> None:
46
+ fusion_config = load_config(config)
47
+ api = create_app(fusion_config)
48
+ uvicorn.run(api, host=host, port=port)
49
+
50
+
51
+ @app.command("serve-endpoint")
52
+ def serve_endpoint(
53
+ id: Annotated[str, typer.Option("--id", help="endpoint id exposed via /v1/models")],
54
+ model: Annotated[str, typer.Option("--model", help="provider model name (e.g. gpt-5.5)")],
55
+ port: Annotated[int, typer.Option("--port")],
56
+ provider: Annotated[str, typer.Option("--provider")] = "openai",
57
+ base_url: Annotated[
58
+ str | None, typer.Option("--base-url", help="override the provider base URL")
59
+ ] = None,
60
+ api_key_env: Annotated[
61
+ str | None, typer.Option("--api-key-env", help="env var holding the API key")
62
+ ] = None,
63
+ timeout_s: Annotated[float, typer.Option("--timeout-s")] = 120.0,
64
+ host: Annotated[str, typer.Option("--host")] = "127.0.0.1",
65
+ ) -> None:
66
+ """Front a single provider model as an OpenAI Chat Completions endpoint."""
67
+ endpoint = build_endpoint(
68
+ id=id,
69
+ model=model,
70
+ provider=provider,
71
+ base_url=base_url,
72
+ api_key_env=api_key_env,
73
+ timeout_s=timeout_s,
74
+ )
75
+ serve_single_endpoint(endpoint, host=host, port=port)
76
+
77
+
78
+ @app.command()
79
+ def eval(
80
+ config: Annotated[Path, typer.Option("--config", "-c")],
81
+ samples: Annotated[Path, typer.Option("--samples", "-s")],
82
+ output: Annotated[Path, typer.Option("--output", "-o")],
83
+ mode: FusionMode = "single",
84
+ config_id: str = "local",
85
+ ) -> None:
86
+ fusion_config = load_config(config)
87
+ clients = build_clients(fusion_config)
88
+ engine = FusionEngine(config=fusion_config, clients=clients)
89
+ runner = BenchmarkRunner(engine)
90
+ results = asyncio.run(runner.run_samples(load_jsonl_samples(samples), config_id, mode))
91
+ write_jsonl_results(output, results)
92
+ typer.echo(json.dumps({"results": len(results), "output": str(output)}))
93
+
94
+
95
+ @app.command()
96
+ def pareto(
97
+ points: Annotated[Path, typer.Option("--points", "-p")],
98
+ output: Annotated[Path, typer.Option("--output", "-o")],
99
+ ) -> None:
100
+ write_pareto_report(output, load_points(points))
101
+ typer.echo(json.dumps({"output": str(output)}))
102
+
103
+
104
+ @app.command("tiny-bench")
105
+ def tiny_bench(
106
+ config: Annotated[Path, typer.Option("--config", "-c")],
107
+ output: Annotated[Path, typer.Option("--output", "-o")],
108
+ report: Annotated[Path | None, typer.Option("--report", "-r")] = None,
109
+ mode: FusionMode = "panel",
110
+ config_id: str = "local",
111
+ ) -> None:
112
+ fusion_config = load_config(config)
113
+ clients = build_clients(fusion_config)
114
+ engine = FusionEngine(config=fusion_config, clients=clients)
115
+ results = asyncio.run(
116
+ run_tiny_benchmark(
117
+ engine,
118
+ config_id=config_id,
119
+ mode=mode,
120
+ tasks=load_tiny_tasks(),
121
+ model_versions={endpoint.id: endpoint.model for endpoint in fusion_config.endpoints},
122
+ )
123
+ )
124
+ write_tiny_jsonl(output, results)
125
+ response = {"results": len(results), "output": str(output)}
126
+ if report is not None:
127
+ write_tiny_benchmark_report(report, results)
128
+ response["report"] = str(report)
129
+ typer.echo(json.dumps(response))
130
+
131
+
132
+ @app.command("fusion-bench")
133
+ def fusion_bench(
134
+ config: Annotated[Path, typer.Option("--config", "-c")],
135
+ output: Annotated[Path, typer.Option("--output", "-o")],
136
+ manifest: Annotated[Path | None, typer.Option("--manifest", "-m")] = None,
137
+ run_root: Annotated[Path, typer.Option("--run-root")] = Path(".fusionkit/fusion-bench"),
138
+ report_jsonl: Annotated[Path | None, typer.Option("--report-jsonl")] = None,
139
+ report_markdown: Annotated[
140
+ Path | None,
141
+ typer.Option("--report", "-r", "--report-markdown"),
142
+ ] = None,
143
+ report_html: Annotated[Path | None, typer.Option("--report-html")] = None,
144
+ handoff_command: Annotated[
145
+ str | None,
146
+ typer.Option(
147
+ "--handoff-command",
148
+ help=(
149
+ "Optional HandoffKit-compatible command. It receives task JSON on stdin "
150
+ "and emits model-fusion contract records on stdout."
151
+ ),
152
+ ),
153
+ ] = None,
154
+ handoff_timeout_s: Annotated[
155
+ float,
156
+ typer.Option("--handoff-timeout-s", min=1.0),
157
+ ] = 300.0,
158
+ mode: FusionMode = "panel",
159
+ config_id: str = "local",
160
+ ) -> None:
161
+ fusion_config = load_config(config)
162
+ clients = build_clients(fusion_config)
163
+ engine = FusionEngine(config=fusion_config, clients=clients)
164
+ runner = FusionBenchRunner(
165
+ engine,
166
+ run_root=run_root,
167
+ config_id=config_id,
168
+ mode=mode,
169
+ model_versions={endpoint.id: endpoint.model for endpoint in fusion_config.endpoints},
170
+ handoff_executor=(
171
+ CommandHandoffKitExecutor(
172
+ shlex.split(handoff_command),
173
+ timeout_s=handoff_timeout_s,
174
+ )
175
+ if handoff_command is not None
176
+ else None
177
+ ),
178
+ )
179
+ tasks = load_benchmark_tasks(manifest) if manifest else load_benchmark_tasks()
180
+ rows = asyncio.run(runner.run_tasks(tasks))
181
+ write_fusion_bench_jsonl(output, rows)
182
+ response: dict[str, int | str] = {"rows": len(rows), "output": str(output)}
183
+ response.update(
184
+ _write_fusion_bench_reports(
185
+ build_fusion_bench_report(rows),
186
+ report_jsonl=report_jsonl,
187
+ report_markdown=report_markdown,
188
+ report_html=report_html,
189
+ )
190
+ )
191
+ typer.echo(json.dumps(response))
192
+
193
+
194
+ @app.command("fusion-bench-report")
195
+ def fusion_bench_report(
196
+ input_path: Annotated[Path, typer.Option("--input", "-i")],
197
+ report_jsonl: Annotated[Path | None, typer.Option("--jsonl")] = None,
198
+ report_markdown: Annotated[Path | None, typer.Option("--markdown", "-m")] = None,
199
+ report_html: Annotated[Path | None, typer.Option("--html")] = None,
200
+ ) -> None:
201
+ rows = load_fusion_bench_jsonl(input_path)
202
+ report = build_fusion_bench_report(rows)
203
+ response: dict[str, int | str] = {
204
+ "rows": len(rows),
205
+ "tasks": report.aggregate.total_tasks,
206
+ "skipped": report.aggregate.skipped_tasks,
207
+ "failed": report.aggregate.failed_tasks,
208
+ }
209
+ response.update(
210
+ _write_fusion_bench_reports(
211
+ report,
212
+ report_jsonl=report_jsonl,
213
+ report_markdown=report_markdown,
214
+ report_html=report_html,
215
+ )
216
+ )
217
+ typer.echo(json.dumps(response))
218
+
219
+
220
+ def _write_fusion_bench_reports(
221
+ report: FusionBenchReport,
222
+ *,
223
+ report_jsonl: Path | None,
224
+ report_markdown: Path | None,
225
+ report_html: Path | None,
226
+ ) -> dict[str, str]:
227
+ outputs = {}
228
+ if report_jsonl is not None:
229
+ write_fusion_bench_report_jsonl(report_jsonl, report)
230
+ outputs["report_jsonl"] = str(report_jsonl)
231
+ if report_markdown is not None:
232
+ write_fusion_bench_markdown_report(report_markdown, report)
233
+ outputs["report_markdown"] = str(report_markdown)
234
+ if report_html is not None:
235
+ write_fusion_bench_html_report(report_html, report)
236
+ outputs["report_html"] = str(report_html)
237
+ return outputs