smarta2a 0.4.26__py3-none-any.whl → 0.4.29__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.
- smarta2a/agent/a2a_human.py +1 -1
- smarta2a/model_providers/openai_provider.py +3 -96
- smarta2a/server/request_handler.py +0 -7
- smarta2a/server/server.py +1 -4
- smarta2a/server/state_manager.py +5 -3
- smarta2a/server/webhook_request_processor.py +44 -116
- {smarta2a-0.4.26.dist-info → smarta2a-0.4.29.dist-info}/METADATA +2 -12
- {smarta2a-0.4.26.dist-info → smarta2a-0.4.29.dist-info}/RECORD +11 -12
- smarta2a/server/send_task_handler.py +0 -174
- /smarta2a/{server → client}/nats_client.py +0 -0
- {smarta2a-0.4.26.dist-info → smarta2a-0.4.29.dist-info}/WHEEL +0 -0
- {smarta2a-0.4.26.dist-info → smarta2a-0.4.29.dist-info}/licenses/LICENSE +0 -0
smarta2a/agent/a2a_human.py
CHANGED
@@ -37,7 +37,7 @@ class A2AHuman:
|
|
37
37
|
|
38
38
|
@self.app.on_send_task(forward_to_webhook=True)
|
39
39
|
async def on_send_task(request: SendTaskRequest, state: StateData):
|
40
|
-
return "I am human and
|
40
|
+
return "I am human and take some time to respond, but I will definitely respond to your request"
|
41
41
|
|
42
42
|
@self.app.webhook()
|
43
43
|
async def on_webhook(request: WebhookRequest, state: StateData):
|
@@ -228,104 +228,11 @@ class OpenAIProvider(BaseLLMProvider):
|
|
228
228
|
raise RuntimeError("Max tool iteration depth reached in generate().")
|
229
229
|
|
230
230
|
|
231
|
-
async def
|
231
|
+
async def stream(self, state: StateData, **kwargs) -> AsyncGenerator[str, None]:
|
232
232
|
"""
|
233
|
-
Stream response
|
233
|
+
Stream a response, invoking tools as needed.
|
234
234
|
"""
|
235
|
-
|
236
|
-
# Normalize incoming messages to your Message model
|
237
|
-
msgs = [
|
238
|
-
msg if isinstance(msg, Message) else Message(**msg)
|
239
|
-
for msg in context_history
|
240
|
-
]
|
241
|
-
# Convert to OpenAI schema, including any prior tool results
|
242
|
-
converted_messages = self._convert_messages(msgs)
|
243
|
-
max_iterations = 30
|
244
|
-
|
245
|
-
for _ in range(max_iterations):
|
246
|
-
# Kick off the streaming completion
|
247
|
-
stream = await self.client.chat.completions.create(
|
248
|
-
model=self.model,
|
249
|
-
messages=converted_messages,
|
250
|
-
tools=self._format_openai_tools(),
|
251
|
-
tool_choice="auto",
|
252
|
-
stream=True,
|
253
|
-
**kwargs
|
254
|
-
)
|
255
|
-
|
256
|
-
full_content = ""
|
257
|
-
tool_calls: List[Dict[str, Any]] = []
|
258
|
-
|
259
|
-
# As chunks arrive, yield them and collect any tool_call deltas
|
260
|
-
async for chunk in stream:
|
261
|
-
delta = chunk.choices[0].delta
|
262
|
-
|
263
|
-
# 1) Stream content immediately
|
264
|
-
if hasattr(delta, "content") and delta.content:
|
265
|
-
yield delta.content
|
266
|
-
full_content += delta.content
|
267
|
-
|
268
|
-
# 2) Buffer up any function/tool calls for after the stream
|
269
|
-
if hasattr(delta, "tool_calls") and delta.tool_calls:
|
270
|
-
for d in delta.tool_calls:
|
271
|
-
idx = d.index
|
272
|
-
# Ensure list is long enough
|
273
|
-
while len(tool_calls) <= idx:
|
274
|
-
tool_calls.append({
|
275
|
-
"id": "",
|
276
|
-
"function": {"name": "", "arguments": ""}
|
277
|
-
})
|
278
|
-
if d.id:
|
279
|
-
tool_calls[idx]["id"] = d.id
|
280
|
-
if d.function.name:
|
281
|
-
tool_calls[idx]["function"]["name"] = d.function.name
|
282
|
-
if d.function.arguments:
|
283
|
-
tool_calls[idx]["function"]["arguments"] += d.function.arguments
|
284
|
-
|
285
|
-
# If the assistant didn't invoke any tools, we're done
|
286
|
-
if not tool_calls:
|
287
|
-
return
|
288
|
-
|
289
|
-
# Otherwise, append the assistant's outgoing call and loop for tool execution
|
290
|
-
converted_messages.append({
|
291
|
-
"role": "assistant",
|
292
|
-
"content": full_content,
|
293
|
-
"tool_calls": [
|
294
|
-
{
|
295
|
-
"id": tc["id"],
|
296
|
-
"type": "function",
|
297
|
-
"function": {
|
298
|
-
"name": tc["function"]["name"],
|
299
|
-
"arguments": tc["function"]["arguments"]
|
300
|
-
}
|
301
|
-
}
|
302
|
-
for tc in tool_calls
|
303
|
-
]
|
304
|
-
})
|
305
|
-
|
306
|
-
# Execute each tool in turn and append its result
|
307
|
-
for tc in tool_calls:
|
308
|
-
name = tc["function"]["name"]
|
309
|
-
try:
|
310
|
-
args = json.loads(tc["function"]["arguments"] or "{}")
|
311
|
-
except json.JSONDecodeError:
|
312
|
-
args = {}
|
313
|
-
try:
|
314
|
-
tool_res = await self.tools_manager.call_tool(name, args)
|
315
|
-
result_content = getattr(tool_res, "content", None) or (
|
316
|
-
tool_res.get("content") if isinstance(tool_res, dict) else str(tool_res)
|
317
|
-
)
|
318
|
-
except Exception as e:
|
319
|
-
result_content = f"Error executing {name}: {e}"
|
320
|
-
|
321
|
-
converted_messages.append({
|
322
|
-
"role": "tool",
|
323
|
-
"content": result_content,
|
324
|
-
"tool_call_id": tc["id"]
|
325
|
-
})
|
326
|
-
|
327
|
-
raise RuntimeError("Max tool iteration depth reached in generate_stream().")
|
328
|
-
|
235
|
+
pass
|
329
236
|
|
330
237
|
|
331
238
|
|
@@ -153,13 +153,6 @@ class RequestHandler:
|
|
153
153
|
push_notification_config=push_notification_config if push_notification_config else state_data.push_notification_config,
|
154
154
|
)
|
155
155
|
)
|
156
|
-
|
157
|
-
# If push_notification_config is set send the task to the push notification url
|
158
|
-
if push_notification_config and forward_to_webhook:
|
159
|
-
try:
|
160
|
-
await self.a2a_aclient.send_to_webhook(webhook_url=push_notification_config.url,id=task_id,task=task.model_dump())
|
161
|
-
except Exception as e:
|
162
|
-
pass
|
163
156
|
|
164
157
|
|
165
158
|
# Send the task back to the client
|
smarta2a/server/server.py
CHANGED
@@ -108,7 +108,7 @@ class SmartA2A:
|
|
108
108
|
# <-- Accept both SSE‐style responses:
|
109
109
|
if isinstance(response, (EventSourceResponse, StreamingResponse)):
|
110
110
|
return response
|
111
|
-
|
111
|
+
print(response)
|
112
112
|
# <-- Everything else is a normal pydantic JSONRPCResponse
|
113
113
|
return response.model_dump()
|
114
114
|
|
@@ -124,9 +124,6 @@ class SmartA2A:
|
|
124
124
|
async def handle_webhook(request: Request):
|
125
125
|
try:
|
126
126
|
data = await request.json()
|
127
|
-
print("--- In handle_webhook in server.py ---")
|
128
|
-
print(data)
|
129
|
-
print("--- end of handle_webhook in server.py ---")
|
130
127
|
req = WebhookRequest.model_validate(data)
|
131
128
|
except Exception as e:
|
132
129
|
return WebhookResponse(accepted=False, error=str(e)).model_dump()
|
smarta2a/server/state_manager.py
CHANGED
@@ -7,7 +7,7 @@ from smarta2a.state_stores.base_state_store import BaseStateStore
|
|
7
7
|
from smarta2a.file_stores.base_file_store import BaseFileStore
|
8
8
|
from smarta2a.history_update_strategies.history_update_strategy import HistoryUpdateStrategy
|
9
9
|
from smarta2a.utils.types import Message, StateData, Task, TaskStatus, TaskState, PushNotificationConfig, Part, FilePart
|
10
|
-
from smarta2a.
|
10
|
+
from smarta2a.client.nats_client import NATSClient
|
11
11
|
|
12
12
|
class StateManager:
|
13
13
|
def __init__(self, state_store: BaseStateStore, file_store: BaseFileStore, history_strategy: HistoryUpdateStrategy, nats_server_url: Optional[str] = "nats://localhost:4222", push_notification_config: Optional[PushNotificationConfig] = None):
|
@@ -112,7 +112,7 @@ class StateManager:
|
|
112
112
|
for artifact in result.artifacts or []:
|
113
113
|
if artifact.parts:
|
114
114
|
artifact_message = Message(
|
115
|
-
role="
|
115
|
+
role="tool",
|
116
116
|
parts=artifact.parts,
|
117
117
|
metadata=artifact.metadata
|
118
118
|
)
|
@@ -132,7 +132,9 @@ class StateManager:
|
|
132
132
|
|
133
133
|
# Merge artifacts
|
134
134
|
if result.artifacts:
|
135
|
-
updated_state.task.artifacts
|
135
|
+
if updated_state.task.artifacts is None:
|
136
|
+
updated_state.task.artifacts = []
|
137
|
+
updated_state.task.artifacts.extend(result.artifacts)
|
136
138
|
|
137
139
|
# Merge metadata
|
138
140
|
if result.metadata:
|
@@ -1,145 +1,73 @@
|
|
1
1
|
# Library imports
|
2
2
|
from typing import Callable, Any, Optional
|
3
|
-
import copy
|
4
|
-
from uuid import uuid4
|
5
3
|
|
6
4
|
# Local imports
|
7
5
|
from smarta2a.server.state_manager import StateManager
|
8
|
-
from smarta2a.utils.types import WebhookRequest, WebhookResponse, StateData, Message
|
9
|
-
from smarta2a.utils.task_builder import TaskBuilder
|
6
|
+
from smarta2a.utils.types import WebhookRequest, WebhookResponse, StateData, Message
|
10
7
|
from smarta2a.client.a2a_client import A2AClient
|
11
8
|
|
12
9
|
class WebhookRequestProcessor:
|
13
10
|
def __init__(self, webhook_fn: Callable[[WebhookRequest], Any], state_manager: Optional[StateManager] = None):
|
14
11
|
self.webhook_fn = webhook_fn
|
15
12
|
self.state_manager = state_manager
|
16
|
-
self.task_builder = TaskBuilder(default_status=TaskState.COMPLETED)
|
17
13
|
self.a2a_aclient = A2AClient()
|
18
14
|
|
19
15
|
async def process_request(self, request: WebhookRequest) -> WebhookResponse:
|
20
16
|
if self.state_manager:
|
21
|
-
state_data =
|
17
|
+
state_data = self.state_manager.get_and_update_state_from_webhook(request.id, request.result)
|
22
18
|
return await self._webhook_handler(request, state_data)
|
23
19
|
else:
|
24
20
|
return await self._webhook_handler(request)
|
25
|
-
|
21
|
+
|
26
22
|
|
27
23
|
async def _webhook_handler(self, request: WebhookRequest, state_data: Optional[StateData] = None) -> WebhookResponse:
|
28
|
-
print("--- _webhook_handler ---")
|
29
|
-
print(request)
|
30
|
-
print("--- end of _webhook_handler ---")
|
31
|
-
print("--- state_data ---")
|
32
|
-
print(state_data)
|
33
|
-
print("--- end of state_data ---")
|
34
24
|
try:
|
35
|
-
#
|
36
|
-
|
37
|
-
|
25
|
+
# --- Step 1: Process Incoming Task ---
|
26
|
+
if request.result:
|
27
|
+
incoming_task = request.result
|
28
|
+
|
29
|
+
# Initialize state_data if missing
|
30
|
+
if not state_data:
|
31
|
+
state_data = StateData(
|
32
|
+
task_id=incoming_task.id,
|
33
|
+
task=incoming_task.copy(update={"artifacts": incoming_task.artifacts}),
|
34
|
+
context_history=[],
|
35
|
+
push_notification_config=None
|
36
|
+
)
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
task_history = task.history if task and task.history is not None else state_data.task.history.copy() if state_data.task.history else []
|
42
|
-
context_history = state_data.context_history.copy()
|
43
|
-
metadata = task.metadata if task and task.metadata is not None else state_data.task.metadata.copy() if state_data.task.metadata else {}
|
44
|
-
# Deep copy of push_notification_config
|
45
|
-
push_notification_config = copy.deepcopy(state_data.push_notification_config) if state_data.push_notification_config else None
|
46
|
-
else:
|
47
|
-
# No state_data so just assign based on task from the request
|
48
|
-
session_id = task.sessionId if task and task.sessionId else str(uuid4())
|
49
|
-
task_history = task.history if task and task.history else []
|
50
|
-
context_history = []
|
51
|
-
metadata = task.metadata if task and task.metadata else {}
|
52
|
-
push_notification_config = None
|
53
|
-
|
54
|
-
# Call webhook handler
|
55
|
-
if state_data:
|
56
|
-
# Call webhook_fn with state_data
|
57
|
-
raw_result = await self.webhook_fn(request, state_data)
|
58
|
-
else:
|
59
|
-
# Call webhook_fn with request
|
60
|
-
raw_result = await self.webhook_fn(request)
|
61
|
-
|
62
|
-
print("--- raw_result ---")
|
63
|
-
print(raw_result)
|
64
|
-
print("--- end of raw_result ---")
|
65
|
-
|
66
|
-
# Handle direct WebhookResponse returns
|
67
|
-
if isinstance(raw_result, WebhookResponse):
|
68
|
-
return raw_result
|
69
|
-
|
70
|
-
# Process webhook_response in a way that is similar to handle_send_task
|
71
|
-
# Build task with updated history
|
72
|
-
updated_task = self.task_builder.build(
|
73
|
-
content=raw_result,
|
74
|
-
task_id=task_id,
|
75
|
-
session_id=session_id,
|
76
|
-
metadata=metadata,
|
77
|
-
history=task_history
|
78
|
-
)
|
79
|
-
|
80
|
-
# Process messages through strategy (similar to handle_send_task)
|
81
|
-
messages = []
|
82
|
-
if updated_task.artifacts:
|
83
|
-
agent_parts = [p for a in updated_task.artifacts for p in a.parts]
|
84
|
-
agent_message = Message(
|
85
|
-
role="agent",
|
86
|
-
parts=agent_parts,
|
87
|
-
metadata=updated_task.metadata
|
88
|
-
)
|
89
|
-
messages.append(agent_message)
|
38
|
+
# --- Step 2: Call Webhook Function ---
|
39
|
+
webhook_response = await self.webhook_fn(request, state_data) if state_data else await self.webhook_fn(request)
|
90
40
|
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
)
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
if state_data:
|
113
|
-
await self.state_manager.update_state(
|
114
|
-
state_data=StateData(
|
115
|
-
task_id=task_id,
|
116
|
-
task=updated_task,
|
117
|
-
context_history=context_history,
|
118
|
-
push_notification_config=push_notification_config,
|
41
|
+
# --- Step 3: Process Webhook Response ---
|
42
|
+
if webhook_response.result:
|
43
|
+
updated_task = webhook_response.result
|
44
|
+
existing_task = state_data.task
|
45
|
+
|
46
|
+
# Overwrite artifacts from response
|
47
|
+
existing_task.artifacts = updated_task.artifacts.copy() if updated_task.artifacts else []
|
48
|
+
|
49
|
+
# Merge metadata
|
50
|
+
existing_task.metadata = {**(existing_task.metadata or {}), **(updated_task.metadata or {})}
|
51
|
+
|
52
|
+
# Build messages from updated artifacts
|
53
|
+
updated_parts = [part for artifact in updated_task.artifacts for part in artifact.parts] if updated_task.artifacts else []
|
54
|
+
updated_messages = [Message(role="agent", parts=updated_parts, metadata=updated_task.metadata)]
|
55
|
+
|
56
|
+
# Update context history again
|
57
|
+
if self.state_manager:
|
58
|
+
history_strategy = self.state_manager.get_history_strategy()
|
59
|
+
state_data.context_history = history_strategy.update_history(
|
60
|
+
existing_history=state_data.context_history,
|
61
|
+
new_messages=updated_messages
|
119
62
|
)
|
120
|
-
|
121
|
-
print("--- push_notification_config ---")
|
122
|
-
print(push_notification_config)
|
123
|
-
print("--- end of push_notification_config ---")
|
124
|
-
# If push_notification_config is set send the task to the push notification url
|
125
|
-
if push_notification_config:
|
126
|
-
try:
|
127
|
-
await self.a2a_aclient.send_to_webhook(webhook_url=push_notification_config.url,id=task_id,task=updated_task.model_dump())
|
128
|
-
except Exception as e:
|
129
|
-
pass
|
63
|
+
await self.state_manager.update_state(state_data)
|
130
64
|
|
131
|
-
|
65
|
+
|
66
|
+
# --- Step 5: Return Final Response ---
|
132
67
|
return WebhookResponse(
|
133
68
|
id=request.id,
|
134
|
-
result=
|
69
|
+
result=state_data.task if state_data else None
|
135
70
|
)
|
136
|
-
|
71
|
+
|
137
72
|
except Exception as e:
|
138
|
-
|
139
|
-
print(e)
|
140
|
-
print("--- END OF EXCEPTION CAUGHT ---")
|
141
|
-
# Handle exceptions
|
142
|
-
return WebhookResponse(
|
143
|
-
id=request.id,
|
144
|
-
error=str(e)
|
145
|
-
)
|
73
|
+
return WebhookResponse(id=request.id, error=f"Internal error: {str(e)}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: smarta2a
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.29
|
4
4
|
Summary: a Python framework that helps you build servers and AI agents that communicate using the A2A protocol
|
5
5
|
Project-URL: Homepage, https://github.com/siddharthsma/smarta2a
|
6
6
|
Project-URL: Bug Tracker, https://github.com/siddharthsma/smarta2a/issues
|
@@ -64,7 +64,7 @@ SmartA2A makes it easy to build interoperable, communication-ready AI systems—
|
|
64
64
|
## Installation
|
65
65
|
|
66
66
|
```bash
|
67
|
-
pip install -U smarta2a
|
67
|
+
pip install -U smarta2a==0.3.1
|
68
68
|
```
|
69
69
|
|
70
70
|
## Examples
|
@@ -389,16 +389,6 @@ This example demonstrates:
|
|
389
389
|
|
390
390
|
---
|
391
391
|
|
392
|
-
## Roadmap
|
393
|
-
|
394
|
-
There's still a lot more to come ...
|
395
|
-
|
396
|
-
- Documentation site with tutorials, explanation of concepts, blog and a contribution guide
|
397
|
-
- Support for more LLM providers
|
398
|
-
- Support for more memory types
|
399
|
-
- Authorization and observability framework built-in
|
400
|
-
- Many more !
|
401
|
-
|
402
392
|
## License
|
403
393
|
|
404
394
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
smarta2a/__init__.py,sha256=T_EECYqWrxshix0FbgUv22zlKRX22HFU-HKXcYTOb3w,175
|
2
2
|
smarta2a/agent/a2a_agent.py,sha256=EurcxpV14e3OPWCMutYL0EXMHb5ZKQqAHEGZZF6pNgg,1892
|
3
|
-
smarta2a/agent/a2a_human.py,sha256=
|
3
|
+
smarta2a/agent/a2a_human.py,sha256=yAW9naxyqStQhWwDamXRZIlw-mT_7RW2wcHyhy3rR6Y,1671
|
4
4
|
smarta2a/agent/a2a_mcp_server.py,sha256=X_mxkgYgCA_dSNtCvs0rSlOoWYc-8d3Qyxv0e-a7NKY,1015
|
5
5
|
smarta2a/archive/smart_mcp_client.py,sha256=0s2OWFKWSv-_UF7rb9fOrsh1OIYsYOsGukkXXp_E1cU,4158
|
6
6
|
smarta2a/archive/subscription_service.py,sha256=vftmZD94HbdjPFa_1UBvsBm-WkW-s3ZCVq60fF7OCgA,4109
|
@@ -8,6 +8,7 @@ smarta2a/archive/task_service.py,sha256=ptf-oMHy98Rw4XSxyK1-lpqc1JtkCkEEHTmwAaun
|
|
8
8
|
smarta2a/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
smarta2a/client/a2a_client.py,sha256=apDkKFtq61T79LpkbkzVTKWA0mSjR_eTNdGPUYozyvk,12100
|
10
10
|
smarta2a/client/mcp_client.py,sha256=JeXhBqxM9TYAArpExLRtEr3lZeQZMcnTmGFl6XKsdu8,3797
|
11
|
+
smarta2a/client/nats_client.py,sha256=K97e8awvAxsqgs3BxsTZU_mB3JPPZ0yjLX1LnZZjfXE,1450
|
11
12
|
smarta2a/file_stores/base_file_store.py,sha256=fcwFIOoFjLQiIKb8lIRVujnV6udyuI9Dq8cEc2ldmIQ,591
|
12
13
|
smarta2a/file_stores/local_file_store.py,sha256=4GLDrsKxSoLWn2Oha4OD-P2r5vBpfV-8ePvZ5bhP1e8,2616
|
13
14
|
smarta2a/history_update_strategies/__init__.py,sha256=x5WtiE9rG5ze8d8hA6E6wJOciBhWHa_ZgGgoIAZcXEQ,213
|
@@ -16,16 +17,14 @@ smarta2a/history_update_strategies/history_update_strategy.py,sha256=n2sfIGu8ztK
|
|
16
17
|
smarta2a/history_update_strategies/rolling_window_strategy.py,sha256=7Ch042JWt4TM_r1-sFKlSIxHj8VX1P3ZoqjCvIdeSqA,540
|
17
18
|
smarta2a/model_providers/__init__.py,sha256=hJj0w00JjqTiBgJmHmOWwL6MU_hwmro9xTiX3XYf6ts,148
|
18
19
|
smarta2a/model_providers/base_llm_provider.py,sha256=iQUqjnypl0f2M929iU0WhHoxAE4ek-NUFJPbEnNQ8-4,412
|
19
|
-
smarta2a/model_providers/openai_provider.py,sha256=
|
20
|
+
smarta2a/model_providers/openai_provider.py,sha256=iL4qgRhQQs1hFT2KblAYoS-4UUQL8z2z5YLGzfaRDfw,8348
|
20
21
|
smarta2a/server/__init__.py,sha256=f2X454Ll4vJc02V4JLJHTN-h8u0TBm4d_FkiO4t686U,53
|
21
22
|
smarta2a/server/handler_registry.py,sha256=OVRG5dTvxB7qUNXgsqWxVNxIyRljUShSYxb1gtbi5XM,820
|
22
23
|
smarta2a/server/json_rpc_request_processor.py,sha256=qRB3sfj_n9ImkIOCdaUKMsDmKcO7CiMhaZ4VdQS7Mb4,6993
|
23
|
-
smarta2a/server/
|
24
|
-
smarta2a/server/
|
25
|
-
smarta2a/server/
|
26
|
-
smarta2a/server/
|
27
|
-
smarta2a/server/state_manager.py,sha256=SKXK02g0o5-r3lYOJdCQaciYUnFTMGtB5MFdfgTc_ug,7962
|
28
|
-
smarta2a/server/webhook_request_processor.py,sha256=EVIU6J6gd5eCl-QJER-dDxd8dSczFfXN7o_qA-fD5sc,6387
|
24
|
+
smarta2a/server/request_handler.py,sha256=zw3nRHZCX0xBx0x0sK5t8GjJRwOtUil-uVYhD3J0R7E,25807
|
25
|
+
smarta2a/server/server.py,sha256=0qpIQ1KXTUtFygcl-2SPIVTRVooobW_EmdXq83wvAPw,6637
|
26
|
+
smarta2a/server/state_manager.py,sha256=fdMOypRpaHaaexlMo0lk_MQX_FGYNzOLqVB0a4efiug,8063
|
27
|
+
smarta2a/server/webhook_request_processor.py,sha256=XQUsfOhXwDiKOdpRa7BmN2C5tgBbgtXATgzA4sM38dU,3431
|
29
28
|
smarta2a/state_stores/__init__.py,sha256=vafxAqpwvag_cYFH2XKGk3DPmJIWJr4Ioey30yLFkVQ,220
|
30
29
|
smarta2a/state_stores/base_state_store.py,sha256=_3LInM-qepKwwdypJTDNs9-DozBNrKVycwPwUm7bYdU,512
|
31
30
|
smarta2a/state_stores/inmemory_state_store.py,sha256=nEBBUiiqhEluP2MYJjFUImcjIwLJEvL8BWwMbLCb8Fw,1268
|
@@ -36,7 +35,7 @@ smarta2a/utils/task_builder.py,sha256=wqSyfVHNTaXuGESu09dhlaDi7D007gcN3-8tH-nPQ4
|
|
36
35
|
smarta2a/utils/task_request_builder.py,sha256=6cOGOqj2Rg43xWM03GRJQzlIZHBptsMCJRp7oD-TDAQ,3362
|
37
36
|
smarta2a/utils/tools_manager.py,sha256=oR5cbwzPZ36hQAsWAgb-c6wFv5BthmCPraD7DSv-Bv8,4332
|
38
37
|
smarta2a/utils/types.py,sha256=kzA6Vv5xXfu1sJuxhEXrglI9e9S6eZVIljMnsrQVyN0,13650
|
39
|
-
smarta2a-0.4.
|
40
|
-
smarta2a-0.4.
|
41
|
-
smarta2a-0.4.
|
42
|
-
smarta2a-0.4.
|
38
|
+
smarta2a-0.4.29.dist-info/METADATA,sha256=dTQkVsjbmETIm4CE45NwU0TtdD7-9UCh7QX3UPDb7RI,12783
|
39
|
+
smarta2a-0.4.29.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
40
|
+
smarta2a-0.4.29.dist-info/licenses/LICENSE,sha256=lDbqrxVnzDMY5KJ8JS1WhvkWE8TJaw-O-CHDy-ecsJA,2095
|
41
|
+
smarta2a-0.4.29.dist-info/RECORD,,
|
@@ -1,174 +0,0 @@
|
|
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
|
-
)
|
File without changes
|
File without changes
|
File without changes
|