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.
fusionkit-0.1.1/PKG-INFO
ADDED
|
@@ -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,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
|