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.
- a2a_server/__init__.py +29 -0
- a2a_server/a2a_agent_card.py +365 -0
- a2a_server/a2a_errors.py +1133 -0
- a2a_server/a2a_executor.py +926 -0
- a2a_server/a2a_router.py +1033 -0
- a2a_server/a2a_types.py +344 -0
- a2a_server/agent_card.py +408 -0
- a2a_server/agents_server.py +271 -0
- a2a_server/auth_api.py +349 -0
- a2a_server/billing_api.py +638 -0
- a2a_server/billing_service.py +712 -0
- a2a_server/billing_webhooks.py +501 -0
- a2a_server/config.py +96 -0
- a2a_server/database.py +2165 -0
- a2a_server/email_inbound.py +398 -0
- a2a_server/email_notifications.py +486 -0
- a2a_server/enhanced_agents.py +919 -0
- a2a_server/enhanced_server.py +160 -0
- a2a_server/hosted_worker.py +1049 -0
- a2a_server/integrated_agents_server.py +347 -0
- a2a_server/keycloak_auth.py +750 -0
- a2a_server/livekit_bridge.py +439 -0
- a2a_server/marketing_tools.py +1364 -0
- a2a_server/mcp_client.py +196 -0
- a2a_server/mcp_http_server.py +2256 -0
- a2a_server/mcp_server.py +191 -0
- a2a_server/message_broker.py +725 -0
- a2a_server/mock_mcp.py +273 -0
- a2a_server/models.py +494 -0
- a2a_server/monitor_api.py +5904 -0
- a2a_server/opencode_bridge.py +1594 -0
- a2a_server/redis_task_manager.py +518 -0
- a2a_server/server.py +726 -0
- a2a_server/task_manager.py +668 -0
- a2a_server/task_queue.py +742 -0
- a2a_server/tenant_api.py +333 -0
- a2a_server/tenant_middleware.py +219 -0
- a2a_server/tenant_service.py +760 -0
- a2a_server/user_auth.py +721 -0
- a2a_server/vault_client.py +576 -0
- a2a_server/worker_sse.py +873 -0
- agent_worker/__init__.py +8 -0
- agent_worker/worker.py +4877 -0
- codetether/__init__.py +10 -0
- codetether/__main__.py +4 -0
- codetether/cli.py +112 -0
- codetether/worker_cli.py +57 -0
- codetether-1.2.2.dist-info/METADATA +570 -0
- codetether-1.2.2.dist-info/RECORD +66 -0
- codetether-1.2.2.dist-info/WHEEL +5 -0
- codetether-1.2.2.dist-info/entry_points.txt +4 -0
- codetether-1.2.2.dist-info/licenses/LICENSE +202 -0
- codetether-1.2.2.dist-info/top_level.txt +5 -0
- codetether_voice_agent/__init__.py +6 -0
- codetether_voice_agent/agent.py +445 -0
- codetether_voice_agent/codetether_mcp.py +345 -0
- codetether_voice_agent/config.py +16 -0
- codetether_voice_agent/functiongemma_caller.py +380 -0
- codetether_voice_agent/session_playback.py +247 -0
- codetether_voice_agent/tools/__init__.py +21 -0
- codetether_voice_agent/tools/definitions.py +135 -0
- codetether_voice_agent/tools/handlers.py +380 -0
- run_server.py +314 -0
- ui/monitor-tailwind.html +1790 -0
- ui/monitor.html +1775 -0
- ui/monitor.js +2662 -0
run_server.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
A2A Server Runner
|
|
4
|
+
|
|
5
|
+
Main entry point for running A2A server instances.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import sys
|
|
11
|
+
import argparse
|
|
12
|
+
import os
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from a2a_server.config import load_config, create_agent_config
|
|
16
|
+
from a2a_server.agent_card import create_agent_card
|
|
17
|
+
from a2a_server.task_manager import PersistentTaskManager
|
|
18
|
+
from a2a_server.redis_task_manager import RedisTaskManager
|
|
19
|
+
from a2a_server.message_broker import MessageBroker, InMemoryMessageBroker
|
|
20
|
+
from a2a_server.server import A2AServer, CustomA2AAgent
|
|
21
|
+
from a2a_server.enhanced_server import EnhancedA2AServer, create_enhanced_agent_card
|
|
22
|
+
from a2a_server.integrated_agents_server import IntegratedAgentsServer, create_integrated_agent_card
|
|
23
|
+
from a2a_server.models import Message, Part
|
|
24
|
+
from a2a_server.mcp_http_server import run_mcp_http_server
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SimpleEchoAgent(CustomA2AAgent):
|
|
28
|
+
"""Simple echo agent implementation."""
|
|
29
|
+
|
|
30
|
+
async def _process_message(self, message: Message, skill_id: Optional[str] = None) -> Message:
|
|
31
|
+
"""Echo messages back with a prefix."""
|
|
32
|
+
response_parts = []
|
|
33
|
+
for part in message.parts:
|
|
34
|
+
if part.type == "text":
|
|
35
|
+
response_parts.append(Part(
|
|
36
|
+
type="text",
|
|
37
|
+
content=f"Echo: {part.content}"
|
|
38
|
+
))
|
|
39
|
+
else:
|
|
40
|
+
response_parts.append(part)
|
|
41
|
+
|
|
42
|
+
return Message(parts=response_parts)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def setup_logging(level: str = "INFO"):
|
|
46
|
+
"""Setup logging configuration."""
|
|
47
|
+
logging.basicConfig(
|
|
48
|
+
level=getattr(logging, level.upper()),
|
|
49
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Get logger after setup
|
|
54
|
+
logger = logging.getLogger(__name__)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def create_server(
|
|
58
|
+
agent_name: str,
|
|
59
|
+
agent_description: str,
|
|
60
|
+
port: int,
|
|
61
|
+
use_redis: bool = False,
|
|
62
|
+
use_enhanced: bool = True,
|
|
63
|
+
use_agents_sdk: bool = False # Disabled by default - requires OPENAI_API_KEY
|
|
64
|
+
) -> A2AServer:
|
|
65
|
+
"""Create an A2A server instance."""
|
|
66
|
+
# Create agent configuration
|
|
67
|
+
agent_config = create_agent_config(
|
|
68
|
+
name=agent_name,
|
|
69
|
+
description=agent_description,
|
|
70
|
+
port=port
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Load config and auto-detect Redis availability
|
|
74
|
+
config = load_config()
|
|
75
|
+
|
|
76
|
+
# Auto-detect Redis: try to connect, fall back to in-memory if unavailable
|
|
77
|
+
redis_available = False
|
|
78
|
+
if config.redis_url:
|
|
79
|
+
try:
|
|
80
|
+
# Test if Redis is available
|
|
81
|
+
from a2a_server.redis_task_manager import RedisTaskManager, REDIS_AVAILABLE
|
|
82
|
+
if REDIS_AVAILABLE:
|
|
83
|
+
test_manager = RedisTaskManager(config.redis_url)
|
|
84
|
+
await test_manager.connect()
|
|
85
|
+
await test_manager.disconnect()
|
|
86
|
+
redis_available = True
|
|
87
|
+
print(f"✓ Redis detected and available at {config.redis_url}")
|
|
88
|
+
except Exception as e:
|
|
89
|
+
print(f"⚠ Redis not available ({e}), using PostgreSQL task storage")
|
|
90
|
+
redis_available = False
|
|
91
|
+
|
|
92
|
+
# Create components with Redis if available
|
|
93
|
+
if redis_available:
|
|
94
|
+
task_manager = RedisTaskManager(config.redis_url)
|
|
95
|
+
await task_manager.connect()
|
|
96
|
+
message_broker = MessageBroker(config.redis_url)
|
|
97
|
+
print("✓ Using Redis for task persistence and message broker")
|
|
98
|
+
else:
|
|
99
|
+
task_manager = PersistentTaskManager(config.database_url)
|
|
100
|
+
message_broker = InMemoryMessageBroker()
|
|
101
|
+
print("✓ Using PostgreSQL task manager and in-memory message broker")
|
|
102
|
+
|
|
103
|
+
# Create authentication callback if needed
|
|
104
|
+
auth_callback = None
|
|
105
|
+
if config.auth_enabled and config.auth_tokens:
|
|
106
|
+
def auth_callback(token: str) -> bool:
|
|
107
|
+
return token in config.auth_tokens.values()
|
|
108
|
+
|
|
109
|
+
# Check if OpenAI API key is available for agents SDK
|
|
110
|
+
if use_agents_sdk:
|
|
111
|
+
import os
|
|
112
|
+
if not os.getenv("OPENAI_API_KEY"):
|
|
113
|
+
print("⚠ OPENAI_API_KEY not set, falling back to enhanced server")
|
|
114
|
+
use_agents_sdk = False
|
|
115
|
+
use_enhanced = True
|
|
116
|
+
|
|
117
|
+
if use_agents_sdk:
|
|
118
|
+
# Use OpenAI Agents SDK integration (requires OPENAI_API_KEY)
|
|
119
|
+
print("✓ Using OpenAI Agents SDK integration")
|
|
120
|
+
agent_card = create_integrated_agent_card()
|
|
121
|
+
agent_card.card.name = agent_config.name
|
|
122
|
+
agent_card.card.description = agent_config.description
|
|
123
|
+
agent_card.card.url = agent_config.base_url or f"http://localhost:{port}"
|
|
124
|
+
agent_card.card.provider.organization = agent_config.organization
|
|
125
|
+
agent_card.card.provider.url = agent_config.organization_url
|
|
126
|
+
|
|
127
|
+
return IntegratedAgentsServer(
|
|
128
|
+
agent_card=agent_card,
|
|
129
|
+
task_manager=task_manager,
|
|
130
|
+
message_broker=message_broker,
|
|
131
|
+
auth_callback=auth_callback
|
|
132
|
+
)
|
|
133
|
+
elif use_enhanced:
|
|
134
|
+
# Create enhanced agent card with MCP tool capabilities
|
|
135
|
+
agent_card = create_enhanced_agent_card()
|
|
136
|
+
agent_card.card.name = agent_config.name
|
|
137
|
+
agent_card.card.description = agent_config.description
|
|
138
|
+
agent_card.card.url = agent_config.base_url or f"http://localhost:{port}"
|
|
139
|
+
agent_card.card.provider.organization = agent_config.organization
|
|
140
|
+
agent_card.card.provider.url = agent_config.organization_url
|
|
141
|
+
|
|
142
|
+
# Create and return enhanced server
|
|
143
|
+
return EnhancedA2AServer(
|
|
144
|
+
agent_card=agent_card,
|
|
145
|
+
task_manager=task_manager,
|
|
146
|
+
message_broker=message_broker,
|
|
147
|
+
auth_callback=auth_callback
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
# Create basic agent card
|
|
151
|
+
base_url = agent_config.base_url or f"http://localhost:{port}"
|
|
152
|
+
agent_card = (create_agent_card(
|
|
153
|
+
name=agent_config.name,
|
|
154
|
+
description=agent_config.description,
|
|
155
|
+
url=base_url,
|
|
156
|
+
organization=agent_config.organization,
|
|
157
|
+
organization_url=agent_config.organization_url
|
|
158
|
+
)
|
|
159
|
+
.with_streaming()
|
|
160
|
+
.with_push_notifications()
|
|
161
|
+
.with_skill(
|
|
162
|
+
skill_id="echo",
|
|
163
|
+
name="Echo Messages",
|
|
164
|
+
description="Echoes back messages with a prefix",
|
|
165
|
+
input_modes=["text"],
|
|
166
|
+
output_modes=["text"],
|
|
167
|
+
examples=[{
|
|
168
|
+
"input": {"type": "text", "content": "Hello!"},
|
|
169
|
+
"output": {"type": "text", "content": "Echo: Hello!"}
|
|
170
|
+
}]
|
|
171
|
+
)
|
|
172
|
+
.build())
|
|
173
|
+
|
|
174
|
+
# Create and return basic server
|
|
175
|
+
return SimpleEchoAgent(
|
|
176
|
+
agent_card=agent_card,
|
|
177
|
+
task_manager=task_manager,
|
|
178
|
+
message_broker=message_broker,
|
|
179
|
+
auth_callback=auth_callback
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async def run_server(args):
|
|
184
|
+
"""Run a single server instance with optional MCP HTTP server."""
|
|
185
|
+
setup_logging(args.log_level)
|
|
186
|
+
|
|
187
|
+
# Get MCP configuration from environment or args
|
|
188
|
+
mcp_enabled = os.environ.get("MCP_HTTP_ENABLED", "true").lower() == "true"
|
|
189
|
+
mcp_host = os.environ.get("MCP_HTTP_HOST", args.host)
|
|
190
|
+
mcp_port = int(os.environ.get("MCP_HTTP_PORT", "9000"))
|
|
191
|
+
|
|
192
|
+
server = await create_server(
|
|
193
|
+
agent_name=args.name,
|
|
194
|
+
agent_description=args.description,
|
|
195
|
+
port=args.port,
|
|
196
|
+
use_redis=args.redis,
|
|
197
|
+
use_enhanced=args.enhanced,
|
|
198
|
+
use_agents_sdk=args.agents_sdk if hasattr(args, 'agents_sdk') else True
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
print(f"Starting A2A server '{args.name}' on port {args.port}")
|
|
202
|
+
print(f"Agent card: http://localhost:{args.port}/.well-known/agent-card.json")
|
|
203
|
+
|
|
204
|
+
tasks = []
|
|
205
|
+
|
|
206
|
+
# Start A2A server
|
|
207
|
+
a2a_task = asyncio.create_task(server.start(host=args.host, port=args.port))
|
|
208
|
+
tasks.append(a2a_task)
|
|
209
|
+
|
|
210
|
+
# Start MCP HTTP server if enabled (with enhanced or agents-sdk mode)
|
|
211
|
+
if mcp_enabled and (args.enhanced or (hasattr(args, 'agents_sdk') and args.agents_sdk)):
|
|
212
|
+
print(f"Starting MCP HTTP server on port {mcp_port}")
|
|
213
|
+
print(f"MCP endpoint: http://localhost:{mcp_port}/mcp/v1/rpc")
|
|
214
|
+
print(f"MCP tools: http://localhost:{mcp_port}/mcp/v1/tools")
|
|
215
|
+
print(f"Monitor UI: http://localhost:{mcp_port}/v1/monitor/")
|
|
216
|
+
mcp_task = asyncio.create_task(run_mcp_http_server(host=mcp_host, port=mcp_port, a2a_server=server))
|
|
217
|
+
tasks.append(mcp_task)
|
|
218
|
+
|
|
219
|
+
print("Press Ctrl+C to stop")
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
await asyncio.gather(*tasks)
|
|
223
|
+
except KeyboardInterrupt:
|
|
224
|
+
print("\nShutting down...")
|
|
225
|
+
await server.stop()
|
|
226
|
+
for task in tasks:
|
|
227
|
+
task.cancel()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
async def run_multiple_servers():
|
|
231
|
+
"""Run multiple example servers concurrently."""
|
|
232
|
+
setup_logging("INFO")
|
|
233
|
+
|
|
234
|
+
servers = [
|
|
235
|
+
await create_server("Enhanced Agent 1", "First enhanced agent with MCP tools", 8001, False, True),
|
|
236
|
+
await create_server("Enhanced Agent 2", "Second enhanced agent with MCP tools", 8002, False, True),
|
|
237
|
+
await create_server("Basic Echo Agent", "Basic echo agent", 8003, False, False),
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
print("Starting multiple A2A servers:")
|
|
241
|
+
for i, server in enumerate(servers, 1):
|
|
242
|
+
port = 8000 + i
|
|
243
|
+
print(f" Agent {i}: http://localhost:{port}/.well-known/agent-card.json")
|
|
244
|
+
|
|
245
|
+
print("Press Ctrl+C to stop all servers")
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
# Start all servers concurrently
|
|
249
|
+
tasks = []
|
|
250
|
+
for i, server in enumerate(servers, 1):
|
|
251
|
+
port = 8000 + i
|
|
252
|
+
task = asyncio.create_task(server.start(host="0.0.0.0", port=port))
|
|
253
|
+
tasks.append(task)
|
|
254
|
+
|
|
255
|
+
await asyncio.gather(*tasks)
|
|
256
|
+
except KeyboardInterrupt:
|
|
257
|
+
print("\nShutting down all servers...")
|
|
258
|
+
for server in servers:
|
|
259
|
+
await server.stop()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def main():
|
|
263
|
+
parser = argparse.ArgumentParser(description="A2A Server Runner")
|
|
264
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
265
|
+
|
|
266
|
+
# Single server command
|
|
267
|
+
single_parser = subparsers.add_parser("run", help="Run a single A2A server")
|
|
268
|
+
single_parser.add_argument("--name", default="A2A Coordination Hub", help="Agent name")
|
|
269
|
+
single_parser.add_argument("--description", default="Agent-to-Agent communication and task coordination server", help="Agent description")
|
|
270
|
+
single_parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
|
|
271
|
+
single_parser.add_argument("--port", type=int, default=8000, help="Port to bind to")
|
|
272
|
+
single_parser.add_argument("--redis", action="store_true", help="Use Redis message broker")
|
|
273
|
+
single_parser.add_argument("--enhanced", action="store_true", default=False, help="Use enhanced MCP-enabled agents (legacy)")
|
|
274
|
+
single_parser.add_argument("--agents-sdk", dest="agents_sdk", action="store_true", default=True, help="Use OpenAI Agents SDK (default, recommended)")
|
|
275
|
+
single_parser.add_argument("--basic", dest="enhanced", action="store_false", help="Use basic echo agent only")
|
|
276
|
+
single_parser.add_argument("--log-level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR"])
|
|
277
|
+
|
|
278
|
+
# Multiple servers command
|
|
279
|
+
multi_parser = subparsers.add_parser("multi", help="Run multiple example servers")
|
|
280
|
+
|
|
281
|
+
# Example configurations
|
|
282
|
+
examples_parser = subparsers.add_parser("examples", help="Show example configurations")
|
|
283
|
+
|
|
284
|
+
args = parser.parse_args()
|
|
285
|
+
|
|
286
|
+
if args.command == "run":
|
|
287
|
+
asyncio.run(run_server(args))
|
|
288
|
+
elif args.command == "multi":
|
|
289
|
+
asyncio.run(run_multiple_servers())
|
|
290
|
+
elif args.command == "examples":
|
|
291
|
+
print("Example configurations:")
|
|
292
|
+
print()
|
|
293
|
+
print("1. Run a simple echo agent:")
|
|
294
|
+
print(" python run_server.py run --name 'My Agent' --port 8000")
|
|
295
|
+
print()
|
|
296
|
+
print("2. Run with Redis message broker:")
|
|
297
|
+
print(" python run_server.py run --redis")
|
|
298
|
+
print()
|
|
299
|
+
print("3. Run multiple agents for testing:")
|
|
300
|
+
print(" python run_server.py multi")
|
|
301
|
+
print()
|
|
302
|
+
print("4. Environment variables:")
|
|
303
|
+
print(" A2A_HOST=0.0.0.0")
|
|
304
|
+
print(" A2A_PORT=8000")
|
|
305
|
+
print(" A2A_REDIS_URL=redis://localhost:6379")
|
|
306
|
+
print(" A2A_AUTH_ENABLED=true")
|
|
307
|
+
print(" A2A_AUTH_TOKENS=agent1:token123,agent2:token456")
|
|
308
|
+
print(" A2A_LOG_LEVEL=INFO")
|
|
309
|
+
else:
|
|
310
|
+
parser.print_help()
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
if __name__ == "__main__":
|
|
314
|
+
main()
|