1
1
import asyncio
2
2
import os
3
- import socket
4
3
import sys
5
- from typing import Optional
6
4
from urllib .parse import urlparse
7
5
8
- import nest_asyncio
9
- import yaml
10
-
11
- from agno import agent , team
6
+ from agno .agent import Agent
12
7
from agno .models .openai import OpenAIChat
13
8
from agno .playground import Playground , serve_playground_app
14
- from agno .tools .mcp import MCPTools , Toolkit
15
- from agno .tools .reasoning import ReasoningTools
9
+ from agno .team import Team
10
+ from agno .tools import Toolkit
11
+ from agno .tools .mcp import MCPTools , SSEClientParams
16
12
from fastapi .middleware .cors import CORSMiddleware
13
+ import nest_asyncio
14
+ import yaml
17
15
18
16
# Allow nested event loops
19
17
nest_asyncio .apply ()
20
18
21
- DOCKER_MODEL_PROVIDER = "docker"
22
-
23
- class Agent (agent .Agent ):
24
- @property
25
- def is_streamable (self ) -> bool :
26
- if self .stream is not None :
27
- return self .stream
28
- return super ().is_streamable
29
-
30
-
31
- class Team (team .Team ):
32
- @property
33
- def is_streamable (self ) -> bool :
34
- stream = getattr (self , "stream" )
35
- if stream is not None :
36
- return stream
37
- return super ().is_streamable
38
19
39
-
40
- def should_stream (model_provider : str , tools : list [Toolkit ]) -> Optional [bool ]:
41
- """Returns whether a model with the given provider and tools can stream"""
42
- if model_provider == DOCKER_MODEL_PROVIDER and len (tools ) > 0 :
43
- # DMR doesn't yet support tools with streaming
44
- return True
45
- # Let the model/options decide
46
- return None
47
-
48
-
49
- def create_model_from_config (entity_data : dict , entity_id : str ) -> tuple [OpenAIChat , str ]:
20
+ def create_model_from_config (entity_data : dict , entity_id : str ) -> OpenAIChat :
50
21
"""Create a model instance from entity configuration data."""
51
- model_name = entity_data .get ("model" )
52
- if not model_name :
53
- model_name = os .getenv ("MODEL_RUNNER_MODEL" )
54
- temperature = entity_data .get ("temperature" , None )
55
- provider = entity_data .get ("model_provider" , "docker" )
56
- model = create_model (model_name , provider , temperature )
57
- return model , provider
22
+ model = entity_data .get ("model" , {})
23
+ name = model .get ("name" )
24
+ if not name :
25
+ raise ValueError (
26
+ f"Model name not specified for { entity_id } . Please set 'model.name' in the configuration."
27
+ )
28
+ provider = model .get ("provider" , "" )
29
+ temperature = entity_data .get ("temperature" )
30
+ return create_model (name , provider , temperature )
58
31
59
32
60
- def create_model (model_name : str , provider : str , temperature : float ) -> OpenAIChat :
33
+ def create_model (
34
+ model_name : str , provider : str , temperature : float | None
35
+ ) -> OpenAIChat :
61
36
"""Create a model instance based on the model name and provider."""
62
- print (f"creating model { model_name } with provider { provider } and temperature { temperature } " )
63
- if provider == DOCKER_MODEL_PROVIDER :
37
+ print (
38
+ f"creating model { model_name } with provider { provider } and temperature { temperature } "
39
+ )
40
+ if provider == "docker" :
64
41
base_url = os .getenv ("MODEL_RUNNER_URL" )
65
42
if base_url is None :
66
- base_url = "http://model-runner.docker.internal/engines/llama.cpp/v1"
43
+ raise ValueError (
44
+ f"MODEL_RUNNER_URL environment variable not set for { model_name } ."
45
+ )
67
46
model = OpenAIChat (id = model_name , base_url = base_url , temperature = temperature )
68
47
model .role_map = {
69
48
"system" : "system" ,
@@ -91,36 +70,16 @@ async def create_mcp_tools(tools_list: list[str], entity_type: str) -> list[Tool
91
70
92
71
tool_names = [name .split (":" , 1 )[1 ] for name in tools_list ]
93
72
94
- # Always use socat, but the endpoint can be different (mock vs real gateway)
95
- endpoint = os .environ ['MCPGATEWAY_ENDPOINT' ]
96
- print (f"DEBUG: { entity_type } connecting to MCP gateway at { endpoint } " )
97
-
98
- # Parse endpoint to extract host and port
99
- try :
100
- # Handle both URL format (http://host:port/path) and host:port format
101
- if endpoint .startswith ('http://' ) or endpoint .startswith ('https://' ):
102
- parsed = urlparse (endpoint )
103
- host = parsed .hostname
104
- port = parsed .port
105
- tcp_endpoint = f"{ host } :{ port } "
106
- else :
107
- # Legacy host:port format
108
- host , port = endpoint .split (':' )
109
- port = int (port )
110
- tcp_endpoint = endpoint
111
-
112
- # Test TCP connection first
113
- sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
114
- sock .settimeout (5 )
115
- sock .connect ((host , port ))
116
- sock .close ()
117
- print (f"DEBUG: TCP connection to { host } :{ port } successful" )
118
- except Exception as e :
119
- print (f"ERROR: TCP connection to { endpoint } failed: { e } " )
120
- raise
73
+ url = os .environ .get ("MCPGATEWAY_URL" )
74
+ if not url :
75
+ raise ValueError (
76
+ f"MCPGATEWAY_URL environment variable not set for { entity_type } tools"
77
+ )
78
+ print (f"DEBUG: { entity_type } connecting to MCP gateway at { url } " )
121
79
122
80
t = MCPTools (
123
- command = f"socat STDIO TCP:{ tcp_endpoint } " ,
81
+ url = url ,
82
+ transport = "sse" ,
124
83
include_tools = tool_names ,
125
84
)
126
85
mcp_tools = await t .__aenter__ ()
@@ -130,9 +89,9 @@ async def create_mcp_tools(tools_list: list[str], entity_type: str) -> list[Tool
130
89
def get_common_config (entity_data : dict ) -> dict :
131
90
"""Extract common configuration options."""
132
91
return {
133
- ' markdown' : entity_data .get ("markdown" , False ),
134
- ' add_datetime_to_instructions' : True ,
135
- ' debug_mode' : True ,
92
+ " markdown" : entity_data .get ("markdown" , False ),
93
+ " add_datetime_to_instructions" : True ,
94
+ " debug_mode" : True ,
136
95
}
137
96
138
97
@@ -145,11 +104,11 @@ async def run_server(config) -> None:
145
104
teams_by_id = {}
146
105
147
106
for agent_id , agent_data in config .get ("agents" , {}).items ():
148
- model , provider = create_model_from_config (agent_data , agent_id )
107
+ model = create_model_from_config (agent_data , agent_id )
149
108
common_config = get_common_config (agent_data )
150
109
151
110
tools : list [Toolkit ] = [
152
- # ReasoningTools(think=True, analyze=True)
111
+ # ReasoningTools(think=True, analyze=True)
153
112
]
154
113
tools_list = agent_data .get ("tools" , [])
155
114
mcp_tools = await create_mcp_tools (tools_list , "Agent" )
@@ -160,10 +119,9 @@ async def run_server(config) -> None:
160
119
role = agent_data .get ("role" , "" ),
161
120
description = agent_data .get ("description" ),
162
121
instructions = agent_data .get ("instructions" ),
163
- tools = tools ,
122
+ tools = tools , # type: ignore
164
123
model = model ,
165
124
show_tool_calls = True ,
166
- stream = should_stream (provider , tools ),
167
125
** common_config ,
168
126
)
169
127
agents_by_id [agent_id ] = agent
@@ -172,7 +130,7 @@ async def run_server(config) -> None:
172
130
agents .append (agent )
173
131
174
132
for team_id , team_data in config .get ("teams" , {}).items ():
175
- model , provider = create_model_from_config (team_data , team_id )
133
+ model = create_model_from_config (team_data , team_id )
176
134
common_config = get_common_config (team_data )
177
135
178
136
team_agents : list [Agent | Team ] = []
@@ -184,7 +142,7 @@ async def run_server(config) -> None:
184
142
team_agents .append (agent )
185
143
186
144
team_tools : list [Toolkit ] = [
187
- # ReasoningTools(think=True, analyze=True)
145
+ # ReasoningTools(think=True, analyze=True)
188
146
]
189
147
tools_list = team_data .get ("tools" , [])
190
148
mcp_tools = await create_mcp_tools (tools_list , "Team" )
@@ -193,21 +151,20 @@ async def run_server(config) -> None:
193
151
team = Team (
194
152
name = team_data .get ("name" , "" ),
195
153
mode = team_data .get ("mode" , "coordinate" ),
196
- members = team_agents , # type: ignore
154
+ members = team_agents ,
197
155
description = team_data .get ("description" ),
198
156
instructions = team_data .get ("instructions" ),
199
- tools = team_tools , # type: ignore,
157
+ tools = team_tools , # type: ignore
200
158
model = model ,
201
159
# show_members_responses=True,
202
160
# show_tool_calls=True,
203
161
** common_config ,
204
162
)
205
- team .stream = should_stream (provider , team_tools )
206
163
teams_by_id [team_id ] = team
207
164
if team_data .get ("chat" , True ):
208
165
teams .append (team )
209
166
210
- playground = Playground (agents = agents , teams = teams ) # type: ignore
167
+ playground = Playground (agents = agents , teams = teams )
211
168
212
169
app = playground .get_app ()
213
170
app .add_middleware (
@@ -225,7 +182,8 @@ async def run_server(config) -> None:
225
182
def main ():
226
183
config_filename = sys .argv [1 ] if len (sys .argv ) > 1 else "/agents.yaml"
227
184
with open (config_filename , "r" ) as f :
228
- config = yaml .safe_load (f )
185
+ expanded = os .path .expandvars (f .read ())
186
+ config = yaml .safe_load (expanded )
229
187
230
188
asyncio .run (run_server (config ))
231
189
0 commit comments