smarta2a 0.3.0__py3-none-any.whl → 0.4.0__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 (42) hide show
  1. smarta2a/agent/a2a_agent.py +25 -15
  2. smarta2a/agent/a2a_human.py +56 -0
  3. smarta2a/archive/smart_mcp_client.py +47 -0
  4. smarta2a/archive/subscription_service.py +85 -0
  5. smarta2a/{server → archive}/task_service.py +17 -8
  6. smarta2a/client/a2a_client.py +35 -8
  7. smarta2a/client/mcp_client.py +3 -0
  8. smarta2a/history_update_strategies/rolling_window_strategy.py +16 -0
  9. smarta2a/model_providers/__init__.py +1 -1
  10. smarta2a/model_providers/base_llm_provider.py +3 -3
  11. smarta2a/model_providers/openai_provider.py +126 -89
  12. smarta2a/nats-server.conf +12 -0
  13. smarta2a/server/json_rpc_request_processor.py +130 -0
  14. smarta2a/server/nats_client.py +49 -0
  15. smarta2a/server/request_handler.py +667 -0
  16. smarta2a/server/send_task_handler.py +174 -0
  17. smarta2a/server/server.py +124 -726
  18. smarta2a/server/state_manager.py +173 -19
  19. smarta2a/server/webhook_request_processor.py +112 -0
  20. smarta2a/state_stores/base_state_store.py +3 -3
  21. smarta2a/state_stores/inmemory_state_store.py +21 -7
  22. smarta2a/utils/agent_discovery_manager.py +121 -0
  23. smarta2a/utils/prompt_helpers.py +1 -1
  24. smarta2a/utils/tools_manager.py +108 -0
  25. smarta2a/utils/types.py +18 -3
  26. smarta2a-0.4.0.dist-info/METADATA +402 -0
  27. smarta2a-0.4.0.dist-info/RECORD +41 -0
  28. smarta2a-0.4.0.dist-info/licenses/LICENSE +35 -0
  29. smarta2a/client/tools_manager.py +0 -62
  30. smarta2a/examples/__init__.py +0 -0
  31. smarta2a/examples/echo_server/__init__.py +0 -0
  32. smarta2a/examples/echo_server/curl.txt +0 -1
  33. smarta2a/examples/echo_server/main.py +0 -39
  34. smarta2a/examples/openai_delegator_agent/__init__.py +0 -0
  35. smarta2a/examples/openai_delegator_agent/main.py +0 -41
  36. smarta2a/examples/openai_weather_agent/__init__.py +0 -0
  37. smarta2a/examples/openai_weather_agent/main.py +0 -32
  38. smarta2a/server/subscription_service.py +0 -109
  39. smarta2a-0.3.0.dist-info/METADATA +0 -103
  40. smarta2a-0.3.0.dist-info/RECORD +0 -40
  41. smarta2a-0.3.0.dist-info/licenses/LICENSE +0 -21
  42. {smarta2a-0.3.0.dist-info → smarta2a-0.4.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,174 @@
1
+ # Library imports
2
+ from typing import Optional
3
+ from uuid import uuid4
4
+ import json
5
+ from pydantic import ValidationError
6
+
7
+ # Local imports
8
+ from smarta2a.utils.types import (
9
+ JSONRPCRequest,
10
+ SendTaskRequest,
11
+ SendTaskResponse,
12
+ StateData,
13
+ Task,
14
+ TaskStatus,
15
+ TaskState,
16
+ Message,
17
+ JSONRPCError,
18
+ InvalidRequestError,
19
+ JSONParseError,
20
+ InternalError,
21
+ MethodNotFoundError
22
+ )
23
+
24
+ class SendTaskHandler:
25
+
26
+ @classmethod
27
+ async def _handle_send_task(self, request_data: JSONRPCRequest, state_data: Optional[StateData] = None) -> SendTaskResponse:
28
+ try:
29
+ # Validate request format
30
+ request = SendTaskRequest.model_validate(request_data.model_dump())
31
+ handler = self.registry.get_handler("tasks/send")
32
+
33
+ if not handler:
34
+ return SendTaskResponse(
35
+ id=request.id,
36
+ error=MethodNotFoundError()
37
+ )
38
+
39
+ # Extract parameters from request
40
+ task_id = request.params.id
41
+ session_id = request.params.sessionId or str(uuid4())
42
+ raw = request.params.message
43
+ user_message = Message.model_validate(raw)
44
+ request_metadata = request.params.metadata or {}
45
+
46
+ if state_data:
47
+ task_history = state_data.task.history.copy() or []
48
+ context_history = state_data.context_history.copy() or []
49
+ metadata = state_data.task.metadata or {}
50
+
51
+ # Call handler with state data
52
+ raw_result = await handler(request, state_data)
53
+
54
+ # Handle direct SendTaskResponse returns
55
+ if isinstance(raw_result, SendTaskResponse):
56
+ return raw_result
57
+
58
+ # Build task with updated history (before agent response)
59
+ task = self.task_builder.build(
60
+ content=raw_result,
61
+ task_id=task_id,
62
+ session_id=session_id,
63
+ metadata=metadata,
64
+ history=task_history
65
+ )
66
+
67
+ # Process messages through strategy
68
+ messages = []
69
+ if task.artifacts:
70
+ agent_parts = [p for a in task.artifacts for p in a.parts]
71
+ agent_message = Message(
72
+ role="agent",
73
+ parts=agent_parts,
74
+ metadata=task.metadata
75
+ )
76
+ messages.append(agent_message)
77
+
78
+ # Update Task history with a simple append
79
+ task_history.extend(messages)
80
+
81
+ # Update context history with a strategy - this is the history that will be passed to an LLM call
82
+ history_strategy = self.state_mgr.get_history_strategy()
83
+ context_history = history_strategy.update_history(
84
+ existing_history=context_history,
85
+ new_messages=messages
86
+ )
87
+
88
+ # Update task with final state
89
+ task.history = task_history
90
+
91
+ # State store update (if enabled)
92
+ if self.state_mgr:
93
+ state_store = self.state_mgr.get_store()
94
+ state_store.update_state(
95
+ task_id=task_id,
96
+ state_data=StateData(
97
+ task_id=task_id,
98
+ task=task,
99
+ context_history=context_history,
100
+ metadata=metadata # Use merged metadata
101
+ )
102
+ )
103
+
104
+ else:
105
+ # There is no state manager, so we need to build a task from scratch
106
+ task = Task(
107
+ id=task_id,
108
+ sessionId=session_id,
109
+ status=TaskStatus(state=TaskState.WORKING),
110
+ history=[user_message],
111
+ metadata=request_metadata
112
+ )
113
+ task_history = task.history.copy()
114
+ metadata = request_metadata.copy()
115
+
116
+ # Call handler without state data
117
+ raw_result = await handler(request)
118
+
119
+ # Handle direct SendTaskResponse returns
120
+ if isinstance(raw_result, SendTaskResponse):
121
+ return raw_result
122
+
123
+ # Build task with updated history (before agent response)
124
+ task = self.task_builder.build(
125
+ content=raw_result,
126
+ task_id=task_id,
127
+ session_id=session_id,
128
+ metadata=metadata,
129
+ history=task_history
130
+ )
131
+
132
+ # Process messages through strategy
133
+ messages = []
134
+ if task.artifacts:
135
+ agent_parts = [p for a in task.artifacts for p in a.parts]
136
+ agent_message = Message(
137
+ role="agent",
138
+ parts=agent_parts,
139
+ metadata=task.metadata
140
+ )
141
+ messages.append(agent_message)
142
+
143
+ # Update Task history with a simple append
144
+ task_history.extend(messages)
145
+
146
+ # Update task with final state
147
+ task.history = task_history
148
+
149
+
150
+ return SendTaskResponse(
151
+ id=request.id,
152
+ result=task
153
+ )
154
+ except ValidationError as e:
155
+ return SendTaskResponse(
156
+ id=request_data.id,
157
+ error=InvalidRequestError(data=e.errors())
158
+ )
159
+ except json.JSONDecodeError as e:
160
+ return SendTaskResponse(
161
+ id=request_data.id,
162
+ error=JSONParseError(data=str(e))
163
+ )
164
+ except Exception as e:
165
+ # Handle case where handler returns SendTaskResponse with error
166
+ if isinstance(e, JSONRPCError):
167
+ return SendTaskResponse(
168
+ id=request.id,
169
+ error=e
170
+ )
171
+ return SendTaskResponse(
172
+ id=request.id,
173
+ error=InternalError(data=str(e))
174
+ )