fast-agent-mcp 0.2.50__py3-none-any.whl → 0.2.51__py3-none-any.whl
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.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- {fast_agent_mcp-0.2.50.dist-info → fast_agent_mcp-0.2.51.dist-info}/METADATA +1 -1
- {fast_agent_mcp-0.2.50.dist-info → fast_agent_mcp-0.2.51.dist-info}/RECORD +29 -12
- mcp_agent/cli/commands/quickstart.py +107 -0
- mcp_agent/llm/model_database.py +5 -1
- mcp_agent/llm/model_factory.py +5 -3
- mcp_agent/llm/providers/augmented_llm_openai.py +6 -1
- mcp_agent/llm/providers/augmented_llm_tensorzero_openai.py +127 -0
- mcp_agent/llm/providers/google_converter.py +4 -0
- mcp_agent/resources/examples/tensorzero/.env.sample +2 -0
- mcp_agent/resources/examples/tensorzero/Makefile +31 -0
- mcp_agent/resources/examples/tensorzero/README.md +55 -0
- mcp_agent/resources/examples/tensorzero/agent.py +35 -0
- mcp_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
- mcp_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
- mcp_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
- mcp_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
- mcp_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
- mcp_agent/resources/examples/tensorzero/image_demo.py +67 -0
- mcp_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
- mcp_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
- mcp_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
- mcp_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
- mcp_agent/resources/examples/tensorzero/simple_agent.py +25 -0
- mcp_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
- mcp_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
- mcp_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
- mcp_agent/llm/providers/augmented_llm_tensorzero.py +0 -441
- mcp_agent/llm/providers/multipart_converter_tensorzero.py +0 -201
- {fast_agent_mcp-0.2.50.dist-info → fast_agent_mcp-0.2.51.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.50.dist-info → fast_agent_mcp-0.2.51.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.50.dist-info → fast_agent_mcp-0.2.51.dist-info}/licenses/LICENSE +0 -0
|
@@ -27,7 +27,7 @@ mcp_agent/cli/main.py,sha256=Oo13X7LB0Cf7JrkilQXz8Eqi_48cE0Rr2qqDUOQifEQ,3175
|
|
|
27
27
|
mcp_agent/cli/terminal.py,sha256=GRwD-RGW7saIz2IOWZn5vD6JjiArscELBThm1GTFkuI,1065
|
|
28
28
|
mcp_agent/cli/commands/check_config.py,sha256=15YK0mtDQbVopnMm3HBjOeY2-00FUHj6tt8RvaemKmI,21081
|
|
29
29
|
mcp_agent/cli/commands/go.py,sha256=SHFJlO3SeZzAWvx_Gz72zXKA8n7XfaZ49W6UNS5mbV4,14668
|
|
30
|
-
mcp_agent/cli/commands/quickstart.py,sha256=
|
|
30
|
+
mcp_agent/cli/commands/quickstart.py,sha256=hwIr1F9zGRSQGf7kwvmirMIK7Qke2s6W95inZ-a2SMQ,21171
|
|
31
31
|
mcp_agent/cli/commands/server_helpers.py,sha256=x5tD_qhf1W4D2li09sfOyfRWCOCa6lmpumYAPsEfIQs,3649
|
|
32
32
|
mcp_agent/cli/commands/setup.py,sha256=eOEd4TL-b0DaDeSJMGOfNOsTEItoZ67W88eTP4aP-bo,6482
|
|
33
33
|
mcp_agent/cli/commands/url_parser.py,sha256=5VdtcHRHzi67YignStVbz7u-rcvNNErw9oJLAUFOtEY,5855
|
|
@@ -67,8 +67,8 @@ mcp_agent/llm/augmented_llm_playback.py,sha256=rLzgai496e2RlxqQp_Bp0U-Y1FF1SGsWl
|
|
|
67
67
|
mcp_agent/llm/augmented_llm_silent.py,sha256=IUnK_1Byy4D9TG0Pj46LFeNezgSTQ8d6MQIHWAImBwE,1846
|
|
68
68
|
mcp_agent/llm/augmented_llm_slow.py,sha256=DDSD8bL2flmQrVHZm-UDs7sR8aHRWkDOcOW-mX_GPok,2067
|
|
69
69
|
mcp_agent/llm/memory.py,sha256=pTOaTDV3EA3X68yKwEtUAu7s0xGIQQ_cKBhfYUnfR0w,8614
|
|
70
|
-
mcp_agent/llm/model_database.py,sha256=
|
|
71
|
-
mcp_agent/llm/model_factory.py,sha256=
|
|
70
|
+
mcp_agent/llm/model_database.py,sha256=ehNiw_d5y4f1t_ySJSigxN1NxikXEXFXbkO5E0gLX9M,10943
|
|
71
|
+
mcp_agent/llm/model_factory.py,sha256=dIVs0RBhVy31-8nAjXdnWSIUJmW5UKnQym_oeQNuNiU,11906
|
|
72
72
|
mcp_agent/llm/prompt_utils.py,sha256=yWQHykoK13QRF7evHUKxVF0SpVLN-Bsft0Yixzvn0g0,4825
|
|
73
73
|
mcp_agent/llm/provider_key_manager.py,sha256=LSWIgcXlrUS4sfBvQBCya82qC6NcXQPYLtDHwHNOXR4,3394
|
|
74
74
|
mcp_agent/llm/provider_types.py,sha256=LfuVuFjcM_lMED0lkrNfKK8s8Fs1vbxugQsrcBE2CIY,1119
|
|
@@ -86,14 +86,13 @@ mcp_agent/llm/providers/augmented_llm_generic.py,sha256=5Uq8ZBhcFuQTt7koP_5ykolR
|
|
|
86
86
|
mcp_agent/llm/providers/augmented_llm_google_native.py,sha256=c6zczfs-Iw70j3OYELHJ4S7CRwAddkeXinex_yLMhmU,22194
|
|
87
87
|
mcp_agent/llm/providers/augmented_llm_google_oai.py,sha256=g_g46h-YuxqbRZiO_dVo5zO2OkX1yx7nb6xDaQbOvWs,1137
|
|
88
88
|
mcp_agent/llm/providers/augmented_llm_groq.py,sha256=5pqWgOoEJpvL230rJekBNlmBzUegbgwYitArlXgAmY0,4424
|
|
89
|
-
mcp_agent/llm/providers/augmented_llm_openai.py,sha256=
|
|
89
|
+
mcp_agent/llm/providers/augmented_llm_openai.py,sha256=ncFlItw4DKKWFQ1czePye2Hpo_qzL22Yanc3aaOUuHA,25343
|
|
90
90
|
mcp_agent/llm/providers/augmented_llm_openrouter.py,sha256=m3wS83fabBOmaZJH9gQ9sFw_2TB4xTb44WCOPB-2NJ4,2001
|
|
91
|
-
mcp_agent/llm/providers/
|
|
91
|
+
mcp_agent/llm/providers/augmented_llm_tensorzero_openai.py,sha256=D53Fry2AfBLOB5z9hM19U6_HMJeVNTpiBCAJb11ulNg,5503
|
|
92
92
|
mcp_agent/llm/providers/augmented_llm_xai.py,sha256=MhlX91IUNynQ_NDknx4EQJLwg-NbR8lcHS1P4JuLOnA,1433
|
|
93
|
-
mcp_agent/llm/providers/google_converter.py,sha256=
|
|
93
|
+
mcp_agent/llm/providers/google_converter.py,sha256=YmSfkkZR2YzR5DZduGaOW0v7BtjjindCIiuS8zOJ1VA,16941
|
|
94
94
|
mcp_agent/llm/providers/multipart_converter_anthropic.py,sha256=QsgIwWJ14SNZsun3fKDQaUx5AYdk8X6UYeBgYlZgZN0,16607
|
|
95
95
|
mcp_agent/llm/providers/multipart_converter_openai.py,sha256=8vBwX7PB62mV6pe4VvAj9YxzX1Dp1RJMZK1NWOMnioY,17998
|
|
96
|
-
mcp_agent/llm/providers/multipart_converter_tensorzero.py,sha256=Tr3DgogthxqTw04ajblwE8aB3Ft42X7aSXsMlm5FP4k,8619
|
|
97
96
|
mcp_agent/llm/providers/openai_multipart.py,sha256=qKBn7d3jSabnJmVgWweVzqh8q9mBqr09fsPmP92niAQ,6899
|
|
98
97
|
mcp_agent/llm/providers/openai_utils.py,sha256=T4bTCL9f7DsoS_zoKgQKv_FUv_4n98vgbvaUpdWZJr8,1875
|
|
99
98
|
mcp_agent/llm/providers/sampling_converter_anthropic.py,sha256=35WzBWkPklnuMlu5S6XsQIq0YL58NOy8Ja6A_l4m6eM,1612
|
|
@@ -155,6 +154,24 @@ mcp_agent/resources/examples/researcher/fastagent.config.yaml,sha256=TbVMHQCKcyt
|
|
|
155
154
|
mcp_agent/resources/examples/researcher/researcher-eval.py,sha256=CR9m4lyoXijS1whvsBDuk6IA-RmNc6iOYbtloETkITY,1833
|
|
156
155
|
mcp_agent/resources/examples/researcher/researcher-imp.py,sha256=oJxSVnLbZfIn71QbQR1E6j_m_UBrOOGP4SVljXErHLQ,7879
|
|
157
156
|
mcp_agent/resources/examples/researcher/researcher.py,sha256=SZfExi-FfwYETzGt2O3caS3L5E6EemV3IUrJHyzZqHI,1333
|
|
157
|
+
mcp_agent/resources/examples/tensorzero/.env.sample,sha256=khV_apbP4XprpNuIaeVicnHaVHEkwIdWGyZCvW1OLDc,35
|
|
158
|
+
mcp_agent/resources/examples/tensorzero/Makefile,sha256=BOvcJvPBAJN2MDh8brLsy2leHGwuT_bBjPzakOsUSCU,427
|
|
159
|
+
mcp_agent/resources/examples/tensorzero/README.md,sha256=xsA1qWjg2mI240jlbeFYWjYm_pQLsGeQiPiSlEtayeQ,2126
|
|
160
|
+
mcp_agent/resources/examples/tensorzero/agent.py,sha256=8x_W-_LHbwC_onXWZfDIbVQe2PX6qEZ5JRjwIWGOEr4,1272
|
|
161
|
+
mcp_agent/resources/examples/tensorzero/docker-compose.yml,sha256=YgmWgoHHDDTGeqRlU8Z21mLdEqo48IoKZsnlXaDZkrc,2851
|
|
162
|
+
mcp_agent/resources/examples/tensorzero/fastagent.config.yaml,sha256=3uJJ9p-0evDG48zr2QneektO4fe_h4uRPh1dyWfe7-k,355
|
|
163
|
+
mcp_agent/resources/examples/tensorzero/image_demo.py,sha256=BVdCMg1o8r9A9cgKEPmEO84CBeBOxz9zNF9yBxyYCp4,2122
|
|
164
|
+
mcp_agent/resources/examples/tensorzero/simple_agent.py,sha256=BjbD6k9C5zNJl_vAzKKIVSyghYuOsF-LOzqxMTnEOJ4,858
|
|
165
|
+
mcp_agent/resources/examples/tensorzero/demo_images/clam.jpg,sha256=K1NWrhz5QMfgjZfDMMIVctWwbwTyJSsPHLDaUMbcn18,22231
|
|
166
|
+
mcp_agent/resources/examples/tensorzero/demo_images/crab.png,sha256=W7R3bSKKDmZCjxJEmFk0pBXVhXXhfPGM1gIiVuv_eu4,58413
|
|
167
|
+
mcp_agent/resources/examples/tensorzero/demo_images/shrimp.png,sha256=2r3c6yHE25MpVRDTTwxjAGs1ShJ2UI-qxJqbxa-9ew4,7806
|
|
168
|
+
mcp_agent/resources/examples/tensorzero/mcp_server/Dockerfile,sha256=pujKW5xDY5q0PvIYsn8Wsh9Tz2F3sVuKTJc3o8lgt4M,681
|
|
169
|
+
mcp_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh,sha256=yLUXZKd4mF2k_ZFAd6125ZstLmrtksePpfgCNdewSyU,1123
|
|
170
|
+
mcp_agent/resources/examples/tensorzero/mcp_server/mcp_server.py,sha256=8Wr6VzNFihri3mv9wLo37i5Ly1VWL4s4jSnfiRG7Ez0,933
|
|
171
|
+
mcp_agent/resources/examples/tensorzero/mcp_server/pyproject.toml,sha256=3AppZ_HbIEmRGg_phGuEOq-Q63CY_IIsrIBr658ka9U,228
|
|
172
|
+
mcp_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json,sha256=q81vtb8eyX1gU0qOhT_BFNo7nI2Lg2o-D_Bvp8watEI,626
|
|
173
|
+
mcp_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja,sha256=_Ekz9YuE76pkmwtcMNJeDlih2U5vPRgFB5wFojAVde8,501
|
|
174
|
+
mcp_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml,sha256=wYDKzHyX0A2x42d1vF5a72ot304iTP8_i5Y1rzAcCEA,890
|
|
158
175
|
mcp_agent/resources/examples/workflows/chaining.py,sha256=tY0kA0U8s2rceAO4ogZFtpQEkiUWcrYnYDgHu_-4G50,889
|
|
159
176
|
mcp_agent/resources/examples/workflows/evaluator.py,sha256=XJXrk5r1hrJzfZAMtQ7WIggy6qPttMJG1yqxYELO7C4,3101
|
|
160
177
|
mcp_agent/resources/examples/workflows/fastagent.config.yaml,sha256=qaxk-p7Pl7JepdL3a7BTl0CIp4LHCXies7pFdVWS9xk,783
|
|
@@ -168,8 +185,8 @@ mcp_agent/resources/examples/workflows/short_story.txt,sha256=X3y_1AyhLFN2AKzCKv
|
|
|
168
185
|
mcp_agent/tools/tool_definition.py,sha256=L3Pxl-uLEXqlVoo-bYuFTFALeI-2pIU44YgFhsTKEtM,398
|
|
169
186
|
mcp_agent/ui/console_display.py,sha256=XXrHr950wSBSedEKUaaGkXjOzuFpQYzUKKiyaZ58Mps,28280
|
|
170
187
|
mcp_agent/ui/console_display_legacy.py,sha256=sm2v61-IPVafbF7uUaOyhO2tW_zgFWOjNS83IEWqGgI,14931
|
|
171
|
-
fast_agent_mcp-0.2.
|
|
172
|
-
fast_agent_mcp-0.2.
|
|
173
|
-
fast_agent_mcp-0.2.
|
|
174
|
-
fast_agent_mcp-0.2.
|
|
175
|
-
fast_agent_mcp-0.2.
|
|
188
|
+
fast_agent_mcp-0.2.51.dist-info/METADATA,sha256=6gg7Cc2qW7R5rVOUtSyhlrbBtZyTEOisFsdU00tohUk,31048
|
|
189
|
+
fast_agent_mcp-0.2.51.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
190
|
+
fast_agent_mcp-0.2.51.dist-info/entry_points.txt,sha256=QaX5kLdI0VdMPRdPUF1nkG_WdLUTNjp_icW6e3EhNYU,232
|
|
191
|
+
fast_agent_mcp-0.2.51.dist-info/licenses/LICENSE,sha256=Gx1L3axA4PnuK4FxsbX87jQ1opoOkSFfHHSytW6wLUU,10935
|
|
192
|
+
fast_agent_mcp-0.2.51.dist-info/RECORD,,
|
|
@@ -76,6 +76,25 @@ EXAMPLE_TYPES = {
|
|
|
76
76
|
],
|
|
77
77
|
"create_subdir": True,
|
|
78
78
|
},
|
|
79
|
+
"tensorzero": {
|
|
80
|
+
"description": "A complete example showcasing the TensorZero integration.\n"
|
|
81
|
+
"Includes the T0 Gateway, an MCP server, an interactive agent, and \n"
|
|
82
|
+
"multi-modal functionality.",
|
|
83
|
+
"files": [
|
|
84
|
+
".env.sample",
|
|
85
|
+
"Makefile",
|
|
86
|
+
"README.md",
|
|
87
|
+
"agent.py",
|
|
88
|
+
"docker-compose.yml",
|
|
89
|
+
"fastagent.config.yaml",
|
|
90
|
+
"image_demo.py",
|
|
91
|
+
"simple_agent.py",
|
|
92
|
+
"mcp_server/",
|
|
93
|
+
"demo_images/",
|
|
94
|
+
"tensorzero_config/"
|
|
95
|
+
],
|
|
96
|
+
"create_subdir": True,
|
|
97
|
+
},
|
|
79
98
|
}
|
|
80
99
|
|
|
81
100
|
|
|
@@ -225,6 +244,27 @@ def copy_example_files(example_type: str, target_dir: Path, force: bool = False)
|
|
|
225
244
|
return created
|
|
226
245
|
|
|
227
246
|
|
|
247
|
+
def copy_project_template(source_dir: Path, dest_dir: Path, console: Console, force: bool = False):
|
|
248
|
+
"""
|
|
249
|
+
Recursively copies a project template directory.
|
|
250
|
+
This is a helper to handle project-based quickstarts like TensorZero.
|
|
251
|
+
"""
|
|
252
|
+
if dest_dir.exists():
|
|
253
|
+
if force:
|
|
254
|
+
console.print(f"[yellow]--force specified. Removing existing directory: {dest_dir}[/yellow]")
|
|
255
|
+
shutil.rmtree(dest_dir)
|
|
256
|
+
else:
|
|
257
|
+
console.print(f"[bold yellow]Directory '{dest_dir.name}' already exists.[/bold yellow] Use --force to overwrite.")
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
shutil.copytree(source_dir, dest_dir)
|
|
262
|
+
return True
|
|
263
|
+
except Exception as e:
|
|
264
|
+
console.print(f"[red]Error copying project template: {e}[/red]")
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
|
|
228
268
|
def show_overview() -> None:
|
|
229
269
|
"""Display an overview of available examples in a nicely formatted table."""
|
|
230
270
|
console.print("\n[bold cyan]fast-agent quickstarts[/bold cyan]")
|
|
@@ -397,6 +437,73 @@ def _show_completion_message(example_type: str, created: list[str]) -> None:
|
|
|
397
437
|
console.print("\n[yellow]No files were created.[/yellow]")
|
|
398
438
|
|
|
399
439
|
|
|
440
|
+
@app.command(name="tensorzero", help="Create the TensorZero integration example project.")
|
|
441
|
+
def tensorzero(
|
|
442
|
+
directory: Path = typer.Argument(
|
|
443
|
+
Path("."),
|
|
444
|
+
help="Directory where the 'tensorzero' project folder will be created.",
|
|
445
|
+
),
|
|
446
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite if project directory exists"),
|
|
447
|
+
):
|
|
448
|
+
"""Create the TensorZero project example."""
|
|
449
|
+
console.print("[bold green]Setting up the TensorZero quickstart example...[/bold green]")
|
|
450
|
+
|
|
451
|
+
dest_project_dir = directory.resolve() / "tensorzero"
|
|
452
|
+
|
|
453
|
+
# --- Find Source Directory ---
|
|
454
|
+
from importlib.resources import files
|
|
455
|
+
try:
|
|
456
|
+
# This path MUST match the "to" path from hatch_build.py
|
|
457
|
+
source_dir = files("mcp_agent").joinpath("resources").joinpath("examples").joinpath("tensorzero")
|
|
458
|
+
if not source_dir.is_dir():
|
|
459
|
+
raise FileNotFoundError # Fallback to dev mode if resource isn't a dir
|
|
460
|
+
except (ImportError, ModuleNotFoundError, FileNotFoundError):
|
|
461
|
+
console.print("[yellow]Package resources not found. Falling back to development mode.[/yellow]")
|
|
462
|
+
# This path is relative to the project root in a development environment
|
|
463
|
+
source_dir = Path(__file__).parent.parent.parent.parent / "examples" / "tensorzero"
|
|
464
|
+
|
|
465
|
+
if not source_dir.exists() or not source_dir.is_dir():
|
|
466
|
+
console.print(f"[red]Error: Source project directory not found at '{source_dir}'[/red]")
|
|
467
|
+
raise typer.Exit(1)
|
|
468
|
+
|
|
469
|
+
console.print(f"Source directory: [dim]{source_dir}[/dim]")
|
|
470
|
+
console.print(f"Destination: [dim]{dest_project_dir}[/dim]")
|
|
471
|
+
|
|
472
|
+
# --- Copy Project and Show Message ---
|
|
473
|
+
if copy_project_template(source_dir, dest_project_dir, console, force):
|
|
474
|
+
console.print(
|
|
475
|
+
f"\n[bold green]✅ Success![/bold green] Your TensorZero project has been created in: [cyan]{dest_project_dir}[/cyan]"
|
|
476
|
+
)
|
|
477
|
+
console.print("\n[bold yellow]Next Steps:[/bold yellow]")
|
|
478
|
+
console.print("\n1. [bold]Navigate to your new project directory:[/bold]")
|
|
479
|
+
console.print(f" [cyan]cd {dest_project_dir.relative_to(Path.cwd())}[/cyan]")
|
|
480
|
+
|
|
481
|
+
console.print("\n2. [bold]Set up your API keys:[/bold]")
|
|
482
|
+
console.print(" [cyan]cp .env.sample .env[/cyan]")
|
|
483
|
+
console.print(
|
|
484
|
+
" [dim]Then, open the new '.env' file and add your OpenAI or Anthropic API key.[/dim]"
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
console.print("\n3. [bold]Start the required services (TensorZero Gateway & MCP Server):[/bold]")
|
|
488
|
+
console.print(" [cyan]docker compose up --build -d[/cyan]")
|
|
489
|
+
console.print(
|
|
490
|
+
" [dim](This builds and starts the necessary containers in the background)[/dim]"
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
console.print("\n4. [bold]Run the interactive agent:[/bold]")
|
|
494
|
+
console.print(" [cyan]make agent[/cyan] (or `uv run agent.py`)")
|
|
495
|
+
console.print("\nEnjoy exploring the TensorZero integration with fast-agent! ✨")
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
@app.command(name="t0", help="Alias for the TensorZero quickstart.", hidden=True)
|
|
499
|
+
def t0_alias(
|
|
500
|
+
directory: Path = typer.Argument(Path("."), help="Directory for the 'tensorzero' project folder."),
|
|
501
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite"),
|
|
502
|
+
):
|
|
503
|
+
"""Alias for the `tensorzero` command."""
|
|
504
|
+
tensorzero(directory, force)
|
|
505
|
+
|
|
506
|
+
|
|
400
507
|
@app.callback(invoke_without_command=True)
|
|
401
508
|
def main(ctx: typer.Context) -> None:
|
|
402
509
|
"""Quickstart applications for fast-agent."""
|
mcp_agent/llm/model_database.py
CHANGED
|
@@ -120,7 +120,9 @@ class ModelDatabase:
|
|
|
120
120
|
OPENAI_O3_MINI_SERIES = ModelParameters(
|
|
121
121
|
context_window=200000, max_output_tokens=100000, tokenizes=TEXT_ONLY
|
|
122
122
|
)
|
|
123
|
-
|
|
123
|
+
OPENAI_GPT_OSS_SERIES = ModelParameters(
|
|
124
|
+
context_window=131072, max_output_tokens=32766, tokenizes=TEXT_ONLY, json_mode="object"
|
|
125
|
+
)
|
|
124
126
|
# TODO update to 32000
|
|
125
127
|
ANTHROPIC_OPUS_4_VERSIONED = ModelParameters(
|
|
126
128
|
context_window=200000, max_output_tokens=32000, tokenizes=ANTHROPIC_MULTIMODAL
|
|
@@ -236,6 +238,8 @@ class ModelDatabase:
|
|
|
236
238
|
"moonshotai/kimi-k2-instruct": KIMI_MOONSHOT,
|
|
237
239
|
"qwen/qwen3-32b": QWEN3_REASONER,
|
|
238
240
|
"deepseek-r1-distill-llama-70b": DEEPSEEK_DISTILL,
|
|
241
|
+
"openai/gpt-oss-120b": OPENAI_GPT_OSS_SERIES,
|
|
242
|
+
"openai/gpt-oss-20b": OPENAI_GPT_OSS_SERIES,
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
@classmethod
|
mcp_agent/llm/model_factory.py
CHANGED
|
@@ -22,7 +22,7 @@ from mcp_agent.llm.providers.augmented_llm_google_oai import GoogleOaiAugmentedL
|
|
|
22
22
|
from mcp_agent.llm.providers.augmented_llm_groq import GroqAugmentedLLM
|
|
23
23
|
from mcp_agent.llm.providers.augmented_llm_openai import OpenAIAugmentedLLM
|
|
24
24
|
from mcp_agent.llm.providers.augmented_llm_openrouter import OpenRouterAugmentedLLM
|
|
25
|
-
from mcp_agent.llm.providers.
|
|
25
|
+
from mcp_agent.llm.providers.augmented_llm_tensorzero_openai import TensorZeroOpenAIAugmentedLLM
|
|
26
26
|
from mcp_agent.llm.providers.augmented_llm_xai import XAIAugmentedLLM
|
|
27
27
|
from mcp_agent.mcp.interfaces import AugmentedLLMProtocol
|
|
28
28
|
|
|
@@ -39,7 +39,7 @@ LLMClass = Union[
|
|
|
39
39
|
Type[SlowLLM],
|
|
40
40
|
Type[DeepSeekAugmentedLLM],
|
|
41
41
|
Type[OpenRouterAugmentedLLM],
|
|
42
|
-
Type[
|
|
42
|
+
Type[TensorZeroOpenAIAugmentedLLM],
|
|
43
43
|
Type[GoogleNativeAugmentedLLM],
|
|
44
44
|
Type[GenericAugmentedLLM],
|
|
45
45
|
Type[AzureOpenAIAugmentedLLM],
|
|
@@ -144,6 +144,8 @@ class ModelFactory:
|
|
|
144
144
|
"gemini25": "gemini-2.5-flash-preview-05-20",
|
|
145
145
|
"gemini25pro": "gemini-2.5-pro-preview-05-06",
|
|
146
146
|
"kimi": "groq.moonshotai/kimi-k2-instruct",
|
|
147
|
+
"gpt-oss": "groq.openai/gpt-oss-120b",
|
|
148
|
+
"gpt-oss-20b": "groq.openai/gpt-oss-20b",
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
# Mapping of providers to their LLM classes
|
|
@@ -157,7 +159,7 @@ class ModelFactory:
|
|
|
157
159
|
Provider.GOOGLE: GoogleNativeAugmentedLLM,
|
|
158
160
|
Provider.XAI: XAIAugmentedLLM,
|
|
159
161
|
Provider.OPENROUTER: OpenRouterAugmentedLLM,
|
|
160
|
-
Provider.TENSORZERO:
|
|
162
|
+
Provider.TENSORZERO: TensorZeroOpenAIAugmentedLLM,
|
|
161
163
|
Provider.AZURE: AzureOpenAIAugmentedLLM,
|
|
162
164
|
Provider.ALIYUN: AliyunAugmentedLLM,
|
|
163
165
|
Provider.BEDROCK: BedrockAugmentedLLM,
|
|
@@ -391,8 +391,13 @@ class OpenAIAugmentedLLM(AugmentedLLM[ChatCompletionMessageParam, ChatCompletion
|
|
|
391
391
|
# Convert to dict and remove None values
|
|
392
392
|
message_dict = message.model_dump()
|
|
393
393
|
message_dict = {k: v for k, v in message_dict.items() if v is not None}
|
|
394
|
-
if model_name
|
|
394
|
+
if model_name in (
|
|
395
|
+
"deepseek-r1-distill-llama-70b",
|
|
396
|
+
"openai/gpt-oss-120b",
|
|
397
|
+
"openai/gpt-oss-20b",
|
|
398
|
+
):
|
|
395
399
|
message_dict.pop("reasoning", None)
|
|
400
|
+
message_dict.pop("channel", None)
|
|
396
401
|
|
|
397
402
|
messages.append(message_dict)
|
|
398
403
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionSystemMessageParam
|
|
4
|
+
|
|
5
|
+
from mcp_agent.core.request_params import RequestParams
|
|
6
|
+
from mcp_agent.llm.provider_types import Provider
|
|
7
|
+
from mcp_agent.llm.providers.augmented_llm_openai import OpenAIAugmentedLLM
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TensorZeroOpenAIAugmentedLLM(OpenAIAugmentedLLM):
|
|
11
|
+
"""
|
|
12
|
+
An LLM augmentation that interacts with TensorZero's OpenAI-compatible inference endpoint.
|
|
13
|
+
This class extends the base OpenAIAugmentedLLM to handle TensorZero-specific
|
|
14
|
+
features, such as system template variables and custom parameters.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Initializes the TensorZeroOpenAIAugmentedLLM.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
*args: Variable length argument list.
|
|
23
|
+
**kwargs: Arbitrary keyword arguments.
|
|
24
|
+
"""
|
|
25
|
+
self._t0_episode_id = kwargs.pop("episode_id", None)
|
|
26
|
+
self._t0_function_name = kwargs.get("model", "")
|
|
27
|
+
|
|
28
|
+
super().__init__(*args, provider=Provider.TENSORZERO, **kwargs)
|
|
29
|
+
self.logger.info("TensorZeroOpenAIAugmentedLLM initialized.")
|
|
30
|
+
|
|
31
|
+
def _initialize_default_params(self, kwargs: dict) -> RequestParams:
|
|
32
|
+
"""
|
|
33
|
+
Initializes TensorZero-specific default parameters. Ensures the model name
|
|
34
|
+
is correctly prefixed for the TensorZero API.
|
|
35
|
+
"""
|
|
36
|
+
model = kwargs.get("model", "")
|
|
37
|
+
if not model.startswith("tensorzero::"):
|
|
38
|
+
model = f"tensorzero::function_name::{model}"
|
|
39
|
+
|
|
40
|
+
self.logger.debug(f"Initializing with TensorZero model: {model}")
|
|
41
|
+
|
|
42
|
+
return RequestParams(
|
|
43
|
+
model=model,
|
|
44
|
+
systemPrompt=self.instruction,
|
|
45
|
+
parallel_tool_calls=True,
|
|
46
|
+
max_iterations=10,
|
|
47
|
+
use_history=True,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _base_url(self) -> str:
|
|
51
|
+
"""
|
|
52
|
+
Constructs the TensorZero OpenAI-compatible endpoint URL.
|
|
53
|
+
"""
|
|
54
|
+
default_url = "http://localhost:3000/openai/v1"
|
|
55
|
+
if self.context and self.context.config and hasattr(self.context.config, "tensorzero"):
|
|
56
|
+
base_url = getattr(self.context.config.tensorzero, "base_url", default_url)
|
|
57
|
+
# Ensure the path is correctly appended
|
|
58
|
+
if not base_url.endswith('/openai/v1'):
|
|
59
|
+
base_url = f"{base_url.rstrip('/')}/openai/v1"
|
|
60
|
+
self.logger.debug(f"Using TensorZero base URL from config: {base_url}")
|
|
61
|
+
return base_url
|
|
62
|
+
self.logger.debug(f"Using default TensorZero base URL: {default_url}")
|
|
63
|
+
return default_url
|
|
64
|
+
|
|
65
|
+
def _prepare_api_request(
|
|
66
|
+
self,
|
|
67
|
+
messages: List[ChatCompletionMessageParam],
|
|
68
|
+
tools: Optional[List[Any]],
|
|
69
|
+
request_params: RequestParams
|
|
70
|
+
) -> Dict[str, Any]:
|
|
71
|
+
"""
|
|
72
|
+
Prepares the API request for the TensorZero OpenAI-compatible endpoint.
|
|
73
|
+
This method injects system template variables and other TensorZero-specific
|
|
74
|
+
parameters into the request. It also handles multimodal inputs.
|
|
75
|
+
"""
|
|
76
|
+
self.logger.debug("Preparing API request for TensorZero OpenAI endpoint.")
|
|
77
|
+
|
|
78
|
+
# Start with the base arguments from the parent class
|
|
79
|
+
arguments = super()._prepare_api_request(messages, tools, request_params)
|
|
80
|
+
|
|
81
|
+
# Handle system template variables
|
|
82
|
+
if request_params.template_vars:
|
|
83
|
+
self.logger.debug(f"Injecting template variables: {request_params.template_vars}")
|
|
84
|
+
system_message_found = False
|
|
85
|
+
for i, msg in enumerate(messages):
|
|
86
|
+
if msg.get("role") == "system":
|
|
87
|
+
# If content is a string, convert it to the TensorZero format
|
|
88
|
+
if isinstance(msg.get("content"), str):
|
|
89
|
+
messages[i] = ChatCompletionSystemMessageParam(
|
|
90
|
+
role="system",
|
|
91
|
+
content=[request_params.template_vars]
|
|
92
|
+
)
|
|
93
|
+
elif isinstance(msg.get("content"), list):
|
|
94
|
+
# If content is already a list, merge the template vars
|
|
95
|
+
msg["content"][0].update(request_params.template_vars)
|
|
96
|
+
system_message_found = True
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
if not system_message_found:
|
|
100
|
+
# If no system message exists, create one
|
|
101
|
+
messages.insert(0, ChatCompletionSystemMessageParam(
|
|
102
|
+
role="system",
|
|
103
|
+
content=[request_params.template_vars]
|
|
104
|
+
))
|
|
105
|
+
|
|
106
|
+
# Add TensorZero-specific extra body parameters
|
|
107
|
+
extra_body = arguments.get("extra_body", {})
|
|
108
|
+
|
|
109
|
+
if self._t0_episode_id:
|
|
110
|
+
extra_body["tensorzero::episode_id"] = str(self._t0_episode_id)
|
|
111
|
+
self.logger.debug(f"Added tensorzero::episode_id: {self._t0_episode_id}")
|
|
112
|
+
|
|
113
|
+
# Merge metadata arguments
|
|
114
|
+
if request_params.metadata and isinstance(request_params.metadata, dict):
|
|
115
|
+
t0_args = request_params.metadata.get("tensorzero_arguments")
|
|
116
|
+
if t0_args:
|
|
117
|
+
self.logger.debug(f"Merging tensorzero_arguments from metadata: {t0_args}")
|
|
118
|
+
for msg in messages:
|
|
119
|
+
if msg.get("role") == "system" and isinstance(msg.get("content"), list):
|
|
120
|
+
msg["content"][0].update(t0_args)
|
|
121
|
+
break
|
|
122
|
+
|
|
123
|
+
if extra_body:
|
|
124
|
+
arguments["extra_body"] = extra_body
|
|
125
|
+
|
|
126
|
+
self.logger.debug(f"Final API request arguments: {arguments}")
|
|
127
|
+
return arguments
|
|
@@ -336,6 +336,10 @@ class GoogleConverter:
|
|
|
336
336
|
"""
|
|
337
337
|
Converts a single google.genai types.Content to a fast-agent PromptMessageMultipart.
|
|
338
338
|
"""
|
|
339
|
+
# Official fix for GitHub issue #207: Handle None content or content.parts
|
|
340
|
+
if content is None or not hasattr(content, "parts") or content.parts is None:
|
|
341
|
+
return PromptMessageMultipart(role="assistant", content=[])
|
|
342
|
+
|
|
339
343
|
if content.role == "model" and any(part.function_call for part in content.parts):
|
|
340
344
|
return PromptMessageMultipart(role="assistant", content=[])
|
|
341
345
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
.PHONY: all
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
docker compose build
|
|
5
|
+
|
|
6
|
+
up:
|
|
7
|
+
docker compose up -d
|
|
8
|
+
|
|
9
|
+
logs:
|
|
10
|
+
docker compose logs -f
|
|
11
|
+
|
|
12
|
+
tensorzero-logs:
|
|
13
|
+
docker compose logs -f gateway
|
|
14
|
+
|
|
15
|
+
mcp-logs:
|
|
16
|
+
docker compose logs -f mcp-server
|
|
17
|
+
|
|
18
|
+
minio-logs:
|
|
19
|
+
docker compose logs -f minio
|
|
20
|
+
|
|
21
|
+
stop:
|
|
22
|
+
docker compose stop
|
|
23
|
+
|
|
24
|
+
agent:
|
|
25
|
+
uv run agent.py --model=tensorzero.test_chat
|
|
26
|
+
|
|
27
|
+
simple-agent:
|
|
28
|
+
uv run simple_agent.py --model=tensorzero.simple_chat
|
|
29
|
+
|
|
30
|
+
image-test:
|
|
31
|
+
uv run image_demo.py
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# About the tensorzero / fast-agent integration
|
|
2
|
+
|
|
3
|
+
[TensorZero](https://www.tensorzero.com/) is an open source project designed to help LLM application developers rapidly improve their inference calls. Its core features include:
|
|
4
|
+
|
|
5
|
+
- A uniform inference interface to all leading LLM platforms.
|
|
6
|
+
- The ability to dynamic route to different platforms and program failovers.
|
|
7
|
+
- Automated parameter tuning and training
|
|
8
|
+
- Advance templating features for your system prompts
|
|
9
|
+
- Organization of LLM inference data into a Clickhouse DB allowing for sophisticated downstream analytics
|
|
10
|
+
- A bunch of other good stuff is always in development
|
|
11
|
+
|
|
12
|
+
`tensorzero` is powerful heavy, so we provide here a quickstart example that combines the basic components of `fast-agent`, an MCP server, `tensorzero`, and other supporting services into a cohesive whole.
|
|
13
|
+
|
|
14
|
+
## Quickstart guide
|
|
15
|
+
|
|
16
|
+
- Build and activate the `uv` `fast-agent` environment
|
|
17
|
+
- Ensure that ports `3000`, `4000`, `8000`, `9000`, and `9001` are unallocated before running this demo.
|
|
18
|
+
- Run `cp .env.sample .env` and then drop in at least one of `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`. Make sure the accounts are funded.
|
|
19
|
+
- `make up`
|
|
20
|
+
- `make agent`
|
|
21
|
+
|
|
22
|
+
The demo test's our implementation's ability to:
|
|
23
|
+
|
|
24
|
+
- Implement the T0 model gateway as an inference backend
|
|
25
|
+
- Implement T0's dynamic templating feature
|
|
26
|
+
- Have in-conversation memory
|
|
27
|
+
- Describe and execute tool calls
|
|
28
|
+
- Remember previous tool calls
|
|
29
|
+
|
|
30
|
+
A version of a conversation to test all of this could be:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Hi.
|
|
34
|
+
|
|
35
|
+
Tell me a poem.
|
|
36
|
+
|
|
37
|
+
Do you have any tools that you can use?
|
|
38
|
+
|
|
39
|
+
Please demonstrate the use of that tool on your last response.
|
|
40
|
+
|
|
41
|
+
Please summarize the conversation so far.
|
|
42
|
+
|
|
43
|
+
What tool calls have you executed in this session, and what were their results?
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Multimodal support
|
|
47
|
+
|
|
48
|
+
Run `make image-test` to test the gateway's ability to handle base64-encoded image data
|
|
49
|
+
|
|
50
|
+
## Development notes:
|
|
51
|
+
|
|
52
|
+
- `make stop` will stop the MCP server and the tensorzero server
|
|
53
|
+
- `make tenzorzero-logs` will tail the tensorzero server logs
|
|
54
|
+
- `make mcp-logs` will tail the MCP server logs
|
|
55
|
+
- Generic `make logs` dumps all log output from all services to terminal
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
4
|
+
from mcp_agent.core.request_params import RequestParams
|
|
5
|
+
|
|
6
|
+
# Explicitly provide the path to the config file in the current directory
|
|
7
|
+
CONFIG_FILE = "fastagent.config.yaml"
|
|
8
|
+
fast = FastAgent("fast-agent example", config_path=CONFIG_FILE, ignore_unknown_args=True)
|
|
9
|
+
|
|
10
|
+
# Define T0 system variables here
|
|
11
|
+
my_t0_system_vars = {
|
|
12
|
+
"TEST_VARIABLE_1": "Roses are red",
|
|
13
|
+
"TEST_VARIABLE_2": "Violets are blue",
|
|
14
|
+
"TEST_VARIABLE_3": "Sugar is sweet",
|
|
15
|
+
"TEST_VARIABLE_4": "Vibe code responsibly 👍",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@fast.agent(
|
|
20
|
+
name="default",
|
|
21
|
+
instruction="""
|
|
22
|
+
You are an agent dedicated to helping developers understand the relationship between TensoZero and fast-agent. If the user makes a request
|
|
23
|
+
that requires you to invoke the test tools, please do so. When you use the tool, describe your rationale for doing so.
|
|
24
|
+
""",
|
|
25
|
+
servers=["tester"],
|
|
26
|
+
request_params=RequestParams(template_vars=my_t0_system_vars),
|
|
27
|
+
)
|
|
28
|
+
async def main():
|
|
29
|
+
async with fast.run() as agent_app: # Get the AgentApp wrapper
|
|
30
|
+
print("\nStarting interactive session with template_vars set via decorator...")
|
|
31
|
+
await agent_app.interactive()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
asyncio.run(main()) # type: ignore
|
|
Binary file
|
|
Binary file
|
|
Binary file
|