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
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()