codetether 1.2.2__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.
Files changed (66) hide show
  1. a2a_server/__init__.py +29 -0
  2. a2a_server/a2a_agent_card.py +365 -0
  3. a2a_server/a2a_errors.py +1133 -0
  4. a2a_server/a2a_executor.py +926 -0
  5. a2a_server/a2a_router.py +1033 -0
  6. a2a_server/a2a_types.py +344 -0
  7. a2a_server/agent_card.py +408 -0
  8. a2a_server/agents_server.py +271 -0
  9. a2a_server/auth_api.py +349 -0
  10. a2a_server/billing_api.py +638 -0
  11. a2a_server/billing_service.py +712 -0
  12. a2a_server/billing_webhooks.py +501 -0
  13. a2a_server/config.py +96 -0
  14. a2a_server/database.py +2165 -0
  15. a2a_server/email_inbound.py +398 -0
  16. a2a_server/email_notifications.py +486 -0
  17. a2a_server/enhanced_agents.py +919 -0
  18. a2a_server/enhanced_server.py +160 -0
  19. a2a_server/hosted_worker.py +1049 -0
  20. a2a_server/integrated_agents_server.py +347 -0
  21. a2a_server/keycloak_auth.py +750 -0
  22. a2a_server/livekit_bridge.py +439 -0
  23. a2a_server/marketing_tools.py +1364 -0
  24. a2a_server/mcp_client.py +196 -0
  25. a2a_server/mcp_http_server.py +2256 -0
  26. a2a_server/mcp_server.py +191 -0
  27. a2a_server/message_broker.py +725 -0
  28. a2a_server/mock_mcp.py +273 -0
  29. a2a_server/models.py +494 -0
  30. a2a_server/monitor_api.py +5904 -0
  31. a2a_server/opencode_bridge.py +1594 -0
  32. a2a_server/redis_task_manager.py +518 -0
  33. a2a_server/server.py +726 -0
  34. a2a_server/task_manager.py +668 -0
  35. a2a_server/task_queue.py +742 -0
  36. a2a_server/tenant_api.py +333 -0
  37. a2a_server/tenant_middleware.py +219 -0
  38. a2a_server/tenant_service.py +760 -0
  39. a2a_server/user_auth.py +721 -0
  40. a2a_server/vault_client.py +576 -0
  41. a2a_server/worker_sse.py +873 -0
  42. agent_worker/__init__.py +8 -0
  43. agent_worker/worker.py +4877 -0
  44. codetether/__init__.py +10 -0
  45. codetether/__main__.py +4 -0
  46. codetether/cli.py +112 -0
  47. codetether/worker_cli.py +57 -0
  48. codetether-1.2.2.dist-info/METADATA +570 -0
  49. codetether-1.2.2.dist-info/RECORD +66 -0
  50. codetether-1.2.2.dist-info/WHEEL +5 -0
  51. codetether-1.2.2.dist-info/entry_points.txt +4 -0
  52. codetether-1.2.2.dist-info/licenses/LICENSE +202 -0
  53. codetether-1.2.2.dist-info/top_level.txt +5 -0
  54. codetether_voice_agent/__init__.py +6 -0
  55. codetether_voice_agent/agent.py +445 -0
  56. codetether_voice_agent/codetether_mcp.py +345 -0
  57. codetether_voice_agent/config.py +16 -0
  58. codetether_voice_agent/functiongemma_caller.py +380 -0
  59. codetether_voice_agent/session_playback.py +247 -0
  60. codetether_voice_agent/tools/__init__.py +21 -0
  61. codetether_voice_agent/tools/definitions.py +135 -0
  62. codetether_voice_agent/tools/handlers.py +380 -0
  63. run_server.py +314 -0
  64. ui/monitor-tailwind.html +1790 -0
  65. ui/monitor.html +1775 -0
  66. ui/monitor.js +2662 -0
@@ -0,0 +1,408 @@
1
+ """
2
+ Agent Card implementation for A2A protocol.
3
+
4
+ Provides utilities for creating, managing, and serving agent cards
5
+ for agent discovery and capability advertisement.
6
+ """
7
+
8
+ import json
9
+ from typing import List, Optional, Dict, Any
10
+ from pathlib import Path
11
+
12
+ from .models import (
13
+ AgentCard as AgentCardModel,
14
+ AgentProvider,
15
+ AgentCapabilities,
16
+ AgentSkill,
17
+ AuthenticationScheme,
18
+ AgentExtension,
19
+ AdditionalInterfaces,
20
+ LiveKitInterface
21
+ )
22
+
23
+
24
+ class AgentCard:
25
+ """Helper class for creating and managing A2A Agent Cards."""
26
+
27
+ def __init__(
28
+ self,
29
+ name: str,
30
+ description: str,
31
+ url: str,
32
+ provider: AgentProvider,
33
+ capabilities: Optional[AgentCapabilities] = None,
34
+ authentication: Optional[List[AuthenticationScheme]] = None,
35
+ skills: Optional[List[AgentSkill]] = None,
36
+ additional_interfaces: Optional[AdditionalInterfaces] = None,
37
+ version: str = "1.0"
38
+ ):
39
+ self.card = AgentCardModel(
40
+ name=name,
41
+ description=description,
42
+ url=url,
43
+ provider=provider,
44
+ capabilities=capabilities or AgentCapabilities(),
45
+ authentication=authentication or [],
46
+ skills=skills or [],
47
+ additional_interfaces=additional_interfaces,
48
+ version=version
49
+ )
50
+
51
+ def add_skill(
52
+ self,
53
+ skill_id: str,
54
+ name: str,
55
+ description: str,
56
+ input_modes: Optional[List[str]] = None,
57
+ output_modes: Optional[List[str]] = None,
58
+ examples: Optional[List[Dict[str, Any]]] = None
59
+ ) -> 'AgentCard':
60
+ """Add a skill to the agent card."""
61
+ skill = AgentSkill(
62
+ id=skill_id,
63
+ name=name,
64
+ description=description,
65
+ input_modes=input_modes or ["text"],
66
+ output_modes=output_modes or ["text"],
67
+ examples=examples
68
+ )
69
+ self.card.skills.append(skill)
70
+ return self
71
+
72
+ def add_authentication(
73
+ self,
74
+ scheme: str,
75
+ description: Optional[str] = None
76
+ ) -> 'AgentCard':
77
+ """Add an authentication scheme to the agent card."""
78
+ auth = AuthenticationScheme(
79
+ scheme=scheme,
80
+ description=description
81
+ )
82
+ self.card.authentication.append(auth)
83
+ return self
84
+
85
+ def enable_streaming(self) -> 'AgentCard':
86
+ """Enable streaming capability."""
87
+ if not self.card.capabilities:
88
+ self.card.capabilities = AgentCapabilities()
89
+ self.card.capabilities.streaming = True
90
+ return self
91
+
92
+ def enable_push_notifications(self) -> 'AgentCard':
93
+ """Enable push notifications capability."""
94
+ if not self.card.capabilities:
95
+ self.card.capabilities = AgentCapabilities()
96
+ self.card.capabilities.push_notifications = True
97
+ return self
98
+
99
+ def enable_state_history(self) -> 'AgentCard':
100
+ """Enable state transition history capability."""
101
+ if not self.card.capabilities:
102
+ self.card.capabilities = AgentCapabilities()
103
+ self.card.capabilities.state_transition_history = True
104
+ return self
105
+
106
+ def enable_media(self) -> 'AgentCard':
107
+ """Enable media capability."""
108
+ if not self.card.capabilities:
109
+ self.card.capabilities = AgentCapabilities()
110
+ self.card.capabilities.media = True
111
+ return self
112
+
113
+ def add_livekit_interface(
114
+ self,
115
+ token_endpoint: str,
116
+ join_url_template: Optional[str] = None,
117
+ server_managed: bool = True
118
+ ) -> 'AgentCard':
119
+ """Add LiveKit interface configuration.
120
+
121
+ Args:
122
+ token_endpoint: Endpoint for obtaining LiveKit access tokens
123
+ join_url_template: Template for generating join URLs
124
+ server_managed: Whether the server manages LiveKit resources
125
+ """
126
+ if not self.card.additional_interfaces:
127
+ self.card.additional_interfaces = AdditionalInterfaces()
128
+
129
+ self.card.additional_interfaces.livekit = LiveKitInterface(
130
+ token_endpoint=token_endpoint,
131
+ join_url_template=join_url_template,
132
+ server_managed=server_managed
133
+ )
134
+
135
+ # Also enable media capability
136
+ self.enable_media()
137
+
138
+ return self
139
+
140
+ def add_mcp_interface(
141
+ self,
142
+ endpoint: str,
143
+ protocol: str = "http",
144
+ description: Optional[str] = None
145
+ ) -> 'AgentCard':
146
+ """Add MCP (Model Context Protocol) interface for agent synchronization.
147
+
148
+ Args:
149
+ endpoint: HTTP endpoint for MCP JSON-RPC (e.g., http://localhost:9000/mcp/v1/rpc)
150
+ protocol: Protocol type (http, stdio, sse)
151
+ description: Optional description of available MCP tools
152
+ """
153
+ if not self.card.additional_interfaces:
154
+ self.card.additional_interfaces = AdditionalInterfaces()
155
+
156
+ # Add MCP interface as custom field
157
+ if not hasattr(self.card.additional_interfaces, 'mcp'):
158
+ # Store as extra field in additional_interfaces
159
+ mcp_info = {
160
+ "endpoint": endpoint,
161
+ "protocol": protocol,
162
+ "description": description or "MCP tools for agent synchronization and coordination"
163
+ }
164
+ # Store as an extra field (allowed by model_config)
165
+ if not hasattr(self.card.additional_interfaces, '__pydantic_extra__'):
166
+ self.card.additional_interfaces.__pydantic_extra__ = {}
167
+ self.card.additional_interfaces.__pydantic_extra__["mcp"] = mcp_info
168
+
169
+ return self
170
+
171
+ def add_extension(
172
+ self,
173
+ uri: str,
174
+ description: Optional[str] = None,
175
+ required: bool = False
176
+ ) -> 'AgentCard':
177
+ """Add a protocol extension to the agent card."""
178
+ if not self.card.capabilities:
179
+ self.card.capabilities = AgentCapabilities()
180
+ if not self.card.capabilities.extensions:
181
+ self.card.capabilities.extensions = []
182
+
183
+ extension = AgentExtension(
184
+ uri=uri,
185
+ description=description,
186
+ required=required
187
+ )
188
+ self.card.capabilities.extensions.append(extension)
189
+ return self
190
+
191
+ def to_dict(self) -> Dict[str, Any]:
192
+ """Convert the agent card to a dictionary."""
193
+ return self.card.model_dump(exclude_none=True)
194
+
195
+ def to_json(self, indent: Optional[int] = 2) -> str:
196
+ """Convert the agent card to JSON."""
197
+ return self.card.model_dump_json(exclude_none=True, indent=indent)
198
+
199
+ def save_to_file(self, file_path: str) -> None:
200
+ """Save the agent card to a JSON file."""
201
+ path = Path(file_path)
202
+ path.parent.mkdir(parents=True, exist_ok=True)
203
+
204
+ with open(path, 'w', encoding='utf-8') as f:
205
+ f.write(self.to_json())
206
+
207
+ @classmethod
208
+ def from_dict(cls, data: Dict[str, Any]) -> 'AgentCard':
209
+ """Create an AgentCard from a dictionary."""
210
+ card_model = AgentCardModel.model_validate(data)
211
+ instance = cls.__new__(cls)
212
+ instance.card = card_model
213
+ return instance
214
+
215
+ @classmethod
216
+ def from_json(cls, json_str: str) -> 'AgentCard':
217
+ """Create an AgentCard from a JSON string."""
218
+ data = json.loads(json_str)
219
+ return cls.from_dict(data)
220
+
221
+ @classmethod
222
+ def from_file(cls, file_path: str) -> 'AgentCard':
223
+ """Load an AgentCard from a JSON file."""
224
+ with open(file_path, 'r', encoding='utf-8') as f:
225
+ return cls.from_json(f.read())
226
+
227
+
228
+ class AgentCardBuilder:
229
+ """Fluent builder for creating Agent Cards."""
230
+
231
+ def __init__(self):
232
+ self._name: Optional[str] = None
233
+ self._description: Optional[str] = None
234
+ self._url: Optional[str] = None
235
+ self._provider: Optional[AgentProvider] = None
236
+ self._capabilities: Optional[AgentCapabilities] = None
237
+ self._authentication: List[AuthenticationScheme] = []
238
+ self._skills: List[AgentSkill] = []
239
+ self._additional_interfaces: Optional[AdditionalInterfaces] = None
240
+ self._version: str = "1.0"
241
+
242
+ def name(self, name: str) -> 'AgentCardBuilder':
243
+ """Set the agent name."""
244
+ self._name = name
245
+ return self
246
+
247
+ def description(self, description: str) -> 'AgentCardBuilder':
248
+ """Set the agent description."""
249
+ self._description = description
250
+ return self
251
+
252
+ def url(self, url: str) -> 'AgentCardBuilder':
253
+ """Set the agent URL."""
254
+ self._url = url
255
+ return self
256
+
257
+ def provider(
258
+ self,
259
+ organization: str,
260
+ url: str
261
+ ) -> 'AgentCardBuilder':
262
+ """Set the agent provider information."""
263
+ self._provider = AgentProvider(
264
+ organization=organization,
265
+ url=url
266
+ )
267
+ return self
268
+
269
+ def with_streaming(self) -> 'AgentCardBuilder':
270
+ """Enable streaming capability."""
271
+ if not self._capabilities:
272
+ self._capabilities = AgentCapabilities()
273
+ self._capabilities.streaming = True
274
+ return self
275
+
276
+ def with_push_notifications(self) -> 'AgentCardBuilder':
277
+ """Enable push notifications capability."""
278
+ if not self._capabilities:
279
+ self._capabilities = AgentCapabilities()
280
+ self._capabilities.push_notifications = True
281
+ return self
282
+
283
+ def with_state_history(self) -> 'AgentCardBuilder':
284
+ """Enable state transition history capability."""
285
+ if not self._capabilities:
286
+ self._capabilities = AgentCapabilities()
287
+ self._capabilities.state_transition_history = True
288
+ return self
289
+
290
+ def with_media(self) -> 'AgentCardBuilder':
291
+ """Enable media capability."""
292
+ if not self._capabilities:
293
+ self._capabilities = AgentCapabilities()
294
+ self._capabilities.media = True
295
+ return self
296
+
297
+ def with_livekit_interface(
298
+ self,
299
+ token_endpoint: str,
300
+ join_url_template: Optional[str] = None,
301
+ server_managed: bool = True
302
+ ) -> 'AgentCardBuilder':
303
+ """Add LiveKit interface configuration."""
304
+ if not self._additional_interfaces:
305
+ self._additional_interfaces = AdditionalInterfaces()
306
+
307
+ self._additional_interfaces.livekit = LiveKitInterface(
308
+ token_endpoint=token_endpoint,
309
+ join_url_template=join_url_template,
310
+ server_managed=server_managed
311
+ )
312
+
313
+ # Also enable media capability
314
+ self.with_media()
315
+
316
+ return self
317
+
318
+ def with_extension(
319
+ self,
320
+ uri: str,
321
+ description: Optional[str] = None,
322
+ required: bool = False
323
+ ) -> 'AgentCardBuilder':
324
+ """Add a protocol extension."""
325
+ if not self._capabilities:
326
+ self._capabilities = AgentCapabilities()
327
+ if not self._capabilities.extensions:
328
+ self._capabilities.extensions = []
329
+
330
+ extension = AgentExtension(
331
+ uri=uri,
332
+ description=description,
333
+ required=required
334
+ )
335
+ self._capabilities.extensions.append(extension)
336
+ return self
337
+
338
+ def with_authentication(
339
+ self,
340
+ scheme: str,
341
+ description: Optional[str] = None
342
+ ) -> 'AgentCardBuilder':
343
+ """Add an authentication scheme."""
344
+ auth = AuthenticationScheme(
345
+ scheme=scheme,
346
+ description=description
347
+ )
348
+ self._authentication.append(auth)
349
+ return self
350
+
351
+ def with_skill(
352
+ self,
353
+ skill_id: str,
354
+ name: str,
355
+ description: str,
356
+ input_modes: Optional[List[str]] = None,
357
+ output_modes: Optional[List[str]] = None,
358
+ examples: Optional[List[Dict[str, Any]]] = None
359
+ ) -> 'AgentCardBuilder':
360
+ """Add a skill."""
361
+ skill = AgentSkill(
362
+ id=skill_id,
363
+ name=name,
364
+ description=description,
365
+ input_modes=input_modes or ["text"],
366
+ output_modes=output_modes or ["text"],
367
+ examples=examples
368
+ )
369
+ self._skills.append(skill)
370
+ return self
371
+
372
+ def version(self, version: str) -> 'AgentCardBuilder':
373
+ """Set the agent card version."""
374
+ self._version = version
375
+ return self
376
+
377
+ def build(self) -> AgentCard:
378
+ """Build the agent card."""
379
+ if not all([self._name, self._description, self._url, self._provider]):
380
+ raise ValueError("Name, description, URL, and provider are required")
381
+
382
+ return AgentCard(
383
+ name=self._name,
384
+ description=self._description,
385
+ url=self._url,
386
+ provider=self._provider,
387
+ capabilities=self._capabilities,
388
+ authentication=self._authentication,
389
+ skills=self._skills,
390
+ additional_interfaces=self._additional_interfaces,
391
+ version=self._version
392
+ )
393
+
394
+
395
+ # Convenience function for creating agent cards
396
+ def create_agent_card(
397
+ name: str,
398
+ description: str,
399
+ url: str,
400
+ organization: str,
401
+ organization_url: str
402
+ ) -> AgentCardBuilder:
403
+ """Create a new agent card builder with basic information."""
404
+ return (AgentCardBuilder()
405
+ .name(name)
406
+ .description(description)
407
+ .url(url)
408
+ .provider(organization, organization_url))
@@ -0,0 +1,271 @@
1
+ """
2
+ A2A Server using OpenAI Agents SDK
3
+ This replaces the custom MCP implementation with the official OpenAI Agents SDK.
4
+ """
5
+ import asyncio
6
+ import logging
7
+ from typing import Any, Dict, Optional
8
+ from datetime import datetime
9
+
10
+ from fastapi import FastAPI, HTTPException
11
+ from fastapi.responses import StreamingResponse
12
+ from pydantic import BaseModel
13
+
14
+ from agents import Agent, Runner, function_tool
15
+ from agents.memory import SQLiteSession
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class TaskRequest(BaseModel):
21
+ """Request to create a task."""
22
+ input: str
23
+ session_id: Optional[str] = None
24
+ agent_name: Optional[str] = "Assistant"
25
+
26
+
27
+ class TaskResponse(BaseModel):
28
+ """Response from task execution."""
29
+ output: str
30
+ session_id: Optional[str] = None
31
+ timestamp: str
32
+
33
+
34
+ # Define agent tools using the @function_tool decorator
35
+ @function_tool
36
+ def calculator(operation: str, a: float, b: float = 0.0) -> str:
37
+ """
38
+ Perform mathematical calculations.
39
+
40
+ Args:
41
+ operation: The operation to perform (add, subtract, multiply, divide, square, sqrt)
42
+ a: First number
43
+ b: Second number (optional for unary operations)
44
+
45
+ Returns:
46
+ The result of the calculation as a string
47
+ """
48
+ try:
49
+ if operation == "add":
50
+ result = a + b
51
+ elif operation == "subtract":
52
+ result = a - b
53
+ elif operation == "multiply":
54
+ result = a * b
55
+ elif operation == "divide":
56
+ if b == 0:
57
+ return "Error: Division by zero"
58
+ result = a / b
59
+ elif operation == "square":
60
+ result = a ** 2
61
+ elif operation == "sqrt":
62
+ if a < 0:
63
+ return "Error: Cannot take square root of negative number"
64
+ result = a ** 0.5
65
+ else:
66
+ return f"Error: Unknown operation '{operation}'"
67
+
68
+ return f"Result: {result}"
69
+ except Exception as e:
70
+ return f"Error: {str(e)}"
71
+
72
+
73
+ @function_tool
74
+ def get_weather(city: str) -> str:
75
+ """
76
+ Get weather information for a city.
77
+
78
+ Args:
79
+ city: The name of the city
80
+
81
+ Returns:
82
+ Weather information as a string
83
+ """
84
+ # Mock weather data
85
+ return f"The weather in {city} is sunny with a temperature of 72°F (22°C)."
86
+
87
+
88
+ @function_tool
89
+ def echo(message: str) -> str:
90
+ """
91
+ Echo back a message.
92
+
93
+ Args:
94
+ message: The message to echo
95
+
96
+ Returns:
97
+ The same message
98
+ """
99
+ return message
100
+
101
+
102
+ class AgentsServer:
103
+ """FastAPI server using OpenAI Agents SDK."""
104
+
105
+ def __init__(self, port: int = 9000):
106
+ self.app = FastAPI(title="A2A Agents Server", version="0.1.0")
107
+ self.port = port
108
+ self.sessions_db = "a2a_sessions.db"
109
+
110
+ # Create default agents
111
+ self.agents = {
112
+ "Assistant": Agent(
113
+ name="Assistant",
114
+ instructions="You are a helpful assistant with access to various tools.",
115
+ tools=[calculator, get_weather, echo]
116
+ ),
117
+ "Calculator": Agent(
118
+ name="Calculator Agent",
119
+ instructions="You specialize in mathematical calculations. Use the calculator tool for all math operations.",
120
+ tools=[calculator]
121
+ ),
122
+ "Weather": Agent(
123
+ name="Weather Agent",
124
+ instructions="You specialize in providing weather information. Use the weather tool to get current conditions.",
125
+ tools=[get_weather]
126
+ )
127
+ }
128
+
129
+ # Setup routes
130
+ self._setup_routes()
131
+
132
+ def _setup_routes(self):
133
+ """Setup FastAPI routes."""
134
+
135
+ @self.app.get("/")
136
+ async def root():
137
+ """Root endpoint with server info."""
138
+ return {
139
+ "name": "A2A Agents Server",
140
+ "version": "0.1.0",
141
+ "framework": "OpenAI Agents SDK",
142
+ "agents": list(self.agents.keys()),
143
+ "endpoints": {
144
+ "chat": "/chat",
145
+ "agents": "/agents",
146
+ "health": "/health"
147
+ }
148
+ }
149
+
150
+ @self.app.get("/health")
151
+ async def health():
152
+ """Health check endpoint."""
153
+ return {"status": "healthy", "timestamp": datetime.now().isoformat()}
154
+
155
+ @self.app.get("/agents")
156
+ async def list_agents():
157
+ """List available agents."""
158
+ return {
159
+ "agents": [
160
+ {
161
+ "name": name,
162
+ "instructions": agent.instructions,
163
+ "tools": [tool.__name__ if hasattr(tool, '__name__') else str(tool) for tool in agent.tools]
164
+ }
165
+ for name, agent in self.agents.items()
166
+ ]
167
+ }
168
+
169
+ @self.app.post("/chat", response_model=TaskResponse)
170
+ async def chat(request: TaskRequest):
171
+ """
172
+ Run an agent with the given input.
173
+
174
+ Supports session memory if session_id is provided.
175
+ """
176
+ try:
177
+ # Get the agent
178
+ agent = self.agents.get(request.agent_name)
179
+ if not agent:
180
+ raise HTTPException(
181
+ status_code=404,
182
+ detail=f"Agent '{request.agent_name}' not found"
183
+ )
184
+
185
+ # Create session if session_id provided
186
+ session = None
187
+ if request.session_id:
188
+ session = SQLiteSession(request.session_id, self.sessions_db)
189
+
190
+ # Run the agent
191
+ result = await Runner.run(
192
+ agent,
193
+ input=request.input,
194
+ session=session
195
+ )
196
+
197
+ return TaskResponse(
198
+ output=result.final_output,
199
+ session_id=request.session_id,
200
+ timestamp=datetime.now().isoformat()
201
+ )
202
+
203
+ except Exception as e:
204
+ logger.error(f"Error running agent: {e}", exc_info=True)
205
+ raise HTTPException(status_code=500, detail=str(e))
206
+
207
+ @self.app.get("/chat/stream")
208
+ async def chat_stream(input: str, agent_name: str = "Assistant", session_id: Optional[str] = None):
209
+ """
210
+ Stream agent responses using Server-Sent Events.
211
+ """
212
+ async def event_generator():
213
+ try:
214
+ # Get the agent
215
+ agent = self.agents.get(agent_name)
216
+ if not agent:
217
+ yield f"data: {{'error': 'Agent not found'}}\n\n"
218
+ return
219
+
220
+ # Create session if needed
221
+ session = None
222
+ if session_id:
223
+ session = SQLiteSession(session_id, self.sessions_db)
224
+
225
+ # Stream the response
226
+ result = Runner.run_streamed(agent, input=input, session=session)
227
+
228
+ async for event in result.stream_events():
229
+ if event.type == "run_item_stream_event":
230
+ yield f"data: {{'type': 'chunk', 'content': '{event.item}'}}\n\n"
231
+
232
+ # Send final output
233
+ yield f"data: {{'type': 'final', 'output': '{result.final_output}'}}\n\n"
234
+
235
+ except Exception as e:
236
+ logger.error(f"Error in stream: {e}", exc_info=True)
237
+ yield f"data: {{'error': '{str(e)}'}}\n\n"
238
+
239
+ return StreamingResponse(
240
+ event_generator(),
241
+ media_type="text/event-stream",
242
+ headers={
243
+ "Cache-Control": "no-cache",
244
+ "Connection": "keep-alive",
245
+ "X-Accel-Buffering": "no"
246
+ }
247
+ )
248
+
249
+ async def start(self):
250
+ """Start the server."""
251
+ import uvicorn
252
+ config = uvicorn.Config(
253
+ self.app,
254
+ host="0.0.0.0",
255
+ port=self.port,
256
+ log_level="info"
257
+ )
258
+ server = uvicorn.Server(config)
259
+ await server.serve()
260
+
261
+
262
+ async def main():
263
+ """Main entry point."""
264
+ server = AgentsServer(port=9000)
265
+ logger.info(f"Starting A2A Agents Server on port {server.port}")
266
+ await server.start()
267
+
268
+
269
+ if __name__ == "__main__":
270
+ logging.basicConfig(level=logging.INFO)
271
+ asyncio.run(main())