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,347 @@
1
+ """
2
+ Integrated A2A + OpenAI Agents SDK Server
3
+ Combines A2A protocol with OpenAI Agents SDK for better tool handling
4
+ """
5
+ import asyncio
6
+ import logging
7
+ import os
8
+ from typing import Optional, List, Dict, Any
9
+ from datetime import datetime
10
+
11
+ from agents import Agent, Runner, function_tool
12
+ from agents.memory import SQLiteSession
13
+
14
+ from .server import A2AServer
15
+ from .models import Message, Part, AgentCard as A2AAgentCard
16
+ from .message_broker import MessageBroker
17
+ from .task_manager import TaskManager
18
+ from .agent_card import AgentCard
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ # Define tools using OpenAI Agents SDK decorators
24
+ @function_tool
25
+ def calculator(operation: str, a: float, b: float = 0.0) -> str:
26
+ """
27
+ Perform mathematical calculations.
28
+
29
+ Args:
30
+ operation: The operation to perform (add, subtract, multiply, divide, square, sqrt)
31
+ a: First number
32
+ b: Second number (optional for unary operations)
33
+
34
+ Returns:
35
+ The result of the calculation
36
+ """
37
+ try:
38
+ if operation == "add":
39
+ result = a + b
40
+ elif operation == "subtract":
41
+ result = a - b
42
+ elif operation == "multiply":
43
+ result = a * b
44
+ elif operation == "divide":
45
+ if b == 0:
46
+ return "Error: Division by zero"
47
+ result = a / b
48
+ elif operation == "square":
49
+ result = a ** 2
50
+ elif operation == "sqrt":
51
+ if a < 0:
52
+ return "Error: Cannot take square root of negative number"
53
+ result = a ** 0.5
54
+ else:
55
+ return f"Error: Unknown operation '{operation}'"
56
+
57
+ return f"Result: {result}"
58
+ except Exception as e:
59
+ return f"Error: {str(e)}"
60
+
61
+
62
+ @function_tool
63
+ def get_weather(city: str) -> str:
64
+ """
65
+ Get weather information for a city.
66
+
67
+ Args:
68
+ city: The name of the city
69
+
70
+ Returns:
71
+ Weather information
72
+ """
73
+ return f"The weather in {city} is sunny with a temperature of 72°F (22°C)."
74
+
75
+
76
+ @function_tool
77
+ def analyze_text(text: str, analysis_type: str = "sentiment") -> str:
78
+ """
79
+ Analyze text for various properties.
80
+
81
+ Args:
82
+ text: The text to analyze
83
+ analysis_type: Type of analysis (sentiment, keywords, summary)
84
+
85
+ Returns:
86
+ Analysis results
87
+ """
88
+ if analysis_type == "sentiment":
89
+ return f"Text sentiment: Positive (based on analysis of: '{text[:50]}...')"
90
+ elif analysis_type == "keywords":
91
+ words = text.split()[:5]
92
+ return f"Key terms: {', '.join(words)}"
93
+ elif analysis_type == "summary":
94
+ return f"Summary: {text[:100]}..."
95
+ return f"Analysis type '{analysis_type}' not supported"
96
+
97
+
98
+ @function_tool
99
+ def remember_fact(key: str, value: str) -> str:
100
+ """
101
+ Store a fact in memory.
102
+
103
+ Args:
104
+ key: The key to store the fact under
105
+ value: The value to remember
106
+
107
+ Returns:
108
+ Confirmation message
109
+ """
110
+ # This will be handled by session memory automatically
111
+ return f"Remembered: {key} = {value}"
112
+
113
+
114
+ class IntegratedAgentsServer(A2AServer):
115
+ """
116
+ Enhanced A2A Server using OpenAI Agents SDK.
117
+ Provides A2A protocol compatibility with advanced agent capabilities.
118
+ """
119
+
120
+ def __init__(
121
+ self,
122
+ agent_card: AgentCard,
123
+ task_manager: TaskManager,
124
+ message_broker: MessageBroker,
125
+ auth_callback=None,
126
+ sessions_db: str = None
127
+ ):
128
+ super().__init__(agent_card, task_manager, message_broker, auth_callback)
129
+
130
+ # Use SESSIONS_DB_PATH from env, fallback to /tmp for ephemeral storage
131
+ if sessions_db is None:
132
+ sessions_db = os.environ.get('SESSIONS_DB_PATH')
133
+ if not sessions_db:
134
+ import tempfile
135
+ sessions_db = os.path.join(tempfile.gettempdir(), "a2a_sessions.db")
136
+
137
+ self.sessions_db = sessions_db
138
+ self._agents_initialized = False
139
+
140
+ # Ensure the database directory exists and is writable
141
+ db_dir = os.path.dirname(self.sessions_db)
142
+ if db_dir and not os.path.exists(db_dir):
143
+ os.makedirs(db_dir, exist_ok=True)
144
+
145
+ # Create OpenAI Agents SDK agents
146
+ self.agents = {
147
+ "assistant": Agent(
148
+ name="Assistant",
149
+ instructions="You are a helpful assistant with access to various tools. Be concise and helpful.",
150
+ tools=[calculator, get_weather, analyze_text, remember_fact]
151
+ ),
152
+ "calculator": Agent(
153
+ name="Calculator Agent",
154
+ instructions="You specialize in mathematical calculations. Always use the calculator tool for math operations.",
155
+ tools=[calculator]
156
+ ),
157
+ "analyst": Agent(
158
+ name="Analysis Agent",
159
+ instructions="You specialize in text analysis. Use the analyze_text tool to provide insights.",
160
+ tools=[analyze_text]
161
+ ),
162
+ "memory": Agent(
163
+ name="Memory Agent",
164
+ instructions="You help users remember and recall information. Use the remember_fact tool.",
165
+ tools=[remember_fact]
166
+ )
167
+ }
168
+
169
+ # Session cache for conversation history
170
+ self.session_cache: Dict[str, SQLiteSession] = {}
171
+
172
+ logger.info("Initialized IntegratedAgentsServer with OpenAI Agents SDK")
173
+
174
+ def _get_session(self, conversation_id: str) -> SQLiteSession:
175
+ """Get or create a session for conversation history."""
176
+ if conversation_id not in self.session_cache:
177
+ self.session_cache[conversation_id] = SQLiteSession(
178
+ conversation_id,
179
+ self.sessions_db
180
+ )
181
+ return self.session_cache[conversation_id]
182
+
183
+ def _select_agent(self, message_text: str) -> Agent:
184
+ """Select the most appropriate agent based on message content."""
185
+ text_lower = message_text.lower()
186
+
187
+ # Simple keyword-based routing
188
+ if any(word in text_lower for word in ['calculate', 'math', 'add', 'subtract', 'multiply', 'divide']):
189
+ return self.agents["calculator"]
190
+ elif any(word in text_lower for word in ['analyze', 'sentiment', 'keywords', 'summary']):
191
+ return self.agents["analyst"]
192
+ elif any(word in text_lower for word in ['remember', 'recall', 'store', 'memory']):
193
+ return self.agents["memory"]
194
+ else:
195
+ return self.agents["assistant"]
196
+
197
+ async def _process_message(self, message: Message, skill_id: Optional[str] = None) -> Message:
198
+ """
199
+ Process message using OpenAI Agents SDK.
200
+ Maintains A2A protocol compatibility while using advanced agent features.
201
+ """
202
+ try:
203
+ # Extract text from message parts
204
+ text_parts = [part.content for part in message.parts if part.type == "text"]
205
+ input_text = " ".join(text_parts)
206
+
207
+ # Get or create session for this conversation
208
+ # Use message ID or generate one
209
+ conversation_id = getattr(message, 'conversation_id', 'default')
210
+ session = self._get_session(conversation_id)
211
+
212
+ # Select appropriate agent
213
+ agent = self._select_agent(input_text)
214
+
215
+ logger.info(f"Processing message with {agent.name}: {input_text[:50]}...")
216
+
217
+ # Publish to message broker for UI monitoring
218
+ await self.message_broker.publish(
219
+ "agent.message.received",
220
+ {
221
+ "agent": agent.name,
222
+ "message": input_text[:100],
223
+ "timestamp": datetime.now().isoformat(),
224
+ "conversation_id": conversation_id
225
+ }
226
+ )
227
+
228
+ # Run the agent with session memory
229
+ result = await Runner.run(
230
+ agent,
231
+ input=input_text,
232
+ session=session
233
+ )
234
+
235
+ # Publish response to message broker for UI monitoring
236
+ await self.message_broker.publish(
237
+ "agent.message.sent",
238
+ {
239
+ "agent": agent.name,
240
+ "response": result.final_output[:100],
241
+ "timestamp": datetime.now().isoformat(),
242
+ "conversation_id": conversation_id
243
+ }
244
+ )
245
+
246
+ logger.info(f"Agent {agent.name} response: {result.final_output[:50]}...")
247
+
248
+ # Convert back to A2A Message format
249
+ response_parts = [
250
+ Part(
251
+ type="text",
252
+ content=result.final_output
253
+ )
254
+ ]
255
+
256
+ return Message(parts=response_parts)
257
+
258
+ except Exception as e:
259
+ logger.error(f"Error processing message with Agents SDK: {e}", exc_info=True)
260
+
261
+ # Fallback response
262
+ return Message(parts=[
263
+ Part(
264
+ type="text",
265
+ content=f"I encountered an error processing your message: {str(e)}"
266
+ )
267
+ ])
268
+
269
+ async def start(self, host: str = "0.0.0.0", port: int = 8000) -> None:
270
+ """Start the integrated A2A + Agents SDK server."""
271
+ # Start message broker
272
+ await self.message_broker.start()
273
+ logger.info("Message broker started for agent communication")
274
+
275
+ self._agents_initialized = True
276
+
277
+ # Call parent start method to run A2A server
278
+ await super().start(host=host, port=port)
279
+
280
+ async def cleanup(self):
281
+ """Clean up server resources."""
282
+ if self._agents_initialized:
283
+ # Clear session cache
284
+ self.session_cache.clear()
285
+
286
+ if self.message_broker:
287
+ await self.message_broker.stop()
288
+
289
+ self._agents_initialized = False
290
+
291
+ logger.info("Integrated Agents server cleanup completed")
292
+
293
+
294
+ def create_integrated_agent_card() -> AgentCard:
295
+ """Create an agent card for the integrated server."""
296
+ from .models import AgentProvider
297
+
298
+ provider = AgentProvider(
299
+ organization="A2A Protocol Server",
300
+ url="https://github.com/rileyseaburg/codetether"
301
+ )
302
+
303
+ card = AgentCard(
304
+ name="A2A Coordination Server",
305
+ description="Agent-to-Agent communication hub enabling distributed task delegation and inter-agent collaboration",
306
+ url="http://localhost:8000",
307
+ provider=provider
308
+ )
309
+
310
+ # Core A2A capabilities
311
+ card.add_skill(
312
+ skill_id="task_delegation",
313
+ name="Task Delegation",
314
+ description="Create and delegate tasks to other agents in the network",
315
+ input_modes=["text", "structured"],
316
+ output_modes=["text", "structured"]
317
+ )
318
+
319
+ card.add_skill(
320
+ skill_id="agent_discovery",
321
+ name="Agent Discovery",
322
+ description="Discover and query available agents and their capabilities",
323
+ input_modes=["text"],
324
+ output_modes=["structured"]
325
+ )
326
+
327
+ card.add_skill(
328
+ skill_id="message_routing",
329
+ name="Message Routing",
330
+ description="Route messages between agents for asynchronous communication",
331
+ input_modes=["text", "structured"],
332
+ output_modes=["text", "structured"]
333
+ )
334
+
335
+ card.add_skill(
336
+ skill_id="task_monitoring",
337
+ name="Task Monitoring",
338
+ description="Monitor task status and receive updates from executing agents",
339
+ input_modes=["structured"],
340
+ output_modes=["structured"]
341
+ )
342
+
343
+ # Enable A2A capabilities
344
+ card.enable_streaming()
345
+ card.enable_push_notifications()
346
+
347
+ return card