agent-api-server 2.2.1a3__tar.gz → 2.2.1a4__tar.gz

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 (52) hide show
  1. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/PKG-INFO +1 -1
  2. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/adapters/openclaw_adapter/openclaw_adapter.py +26 -11
  3. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/api/thread.py +29 -7
  4. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/pyproject.toml +1 -1
  5. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/sdk/client.py +12 -6
  6. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/README.md +0 -0
  7. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/adapters/__init__.py +0 -0
  8. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/adapters/langgraph_adapter/__init__.py +0 -0
  9. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/adapters/langgraph_adapter/formatter.py +0 -0
  10. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/adapters/langgraph_adapter/langgraph_adapter.py +0 -0
  11. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/adapters/openclaw_adapter/__init__.py +0 -0
  12. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/agent_api_server.py +0 -0
  13. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/api/__init__.py +0 -0
  14. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/api/config.py +0 -0
  15. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/api/graph.py +0 -0
  16. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/api/router.py +0 -0
  17. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/api/schema.py +0 -0
  18. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/client/css/styles.css +0 -0
  19. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/client/favicon.ico +0 -0
  20. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/client/index.html +0 -0
  21. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/client/js/app.js +0 -0
  22. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/client/js/index.umd.js +0 -0
  23. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/__init__.py +0 -0
  24. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/config.py +0 -0
  25. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/crypto.py +0 -0
  26. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/formatting.py +0 -0
  27. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/logging.py +0 -0
  28. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/nats.py +0 -0
  29. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/postgres.py +0 -0
  30. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/common/redis.py +0 -0
  31. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/__init__.py +0 -0
  32. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/loader.py +0 -0
  33. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/__init__.py +0 -0
  34. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/agent_models.py +0 -0
  35. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/base_model.py +0 -0
  36. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/detect_message.py +0 -0
  37. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/dynamic_llm.py +0 -0
  38. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/input_normalization.py +0 -0
  39. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/middleware.py +0 -0
  40. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/model_info.py +0 -0
  41. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/schema_utils.py +0 -0
  42. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/model/streaming.py +0 -0
  43. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/runtime/__init__.py +0 -0
  44. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/runtime/base.py +0 -0
  45. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/core/runtime/manager.py +0 -0
  46. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/demo.py +0 -0
  47. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/integration/__init__.py +0 -0
  48. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/integration/listener.py +0 -0
  49. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/integration/registry.py +0 -0
  50. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/logging.json +0 -0
  51. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/sdk/__init__.py +0 -0
  52. {agent_api_server-2.2.1a3 → agent_api_server-2.2.1a4}/service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-api-server
3
- Version: 2.2.1a3
3
+ Version: 2.2.1a4
4
4
  Summary: A Langgraph agent API server that implements Langgraph agent's web capabilities and can interact with chatbot
5
5
  Keywords: fastapi,langgraph,agent,api-server
6
6
  Requires-Python: >=3.11,<3.14
@@ -694,7 +694,7 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
694
694
  content_parts: list[str] = []
695
695
  tool_calls: list[dict[str, Any]] = []
696
696
  last_message: ChatMessage | None = None
697
- last_tool_message: ChatMessage | None = None
697
+ last_real_tool_message: ChatMessage | None = None
698
698
  event_iterator = agent.execute_stream_typed(query, options=options).__aiter__()
699
699
 
700
700
  while True:
@@ -708,12 +708,12 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
708
708
  break
709
709
  except asyncio.TimeoutError:
710
710
  logger.warning(
711
- "OpenClaw run stream idle timeout without DoneEvent; agent=%s session=%s timeout_s=%s last_message_type=%s last_tool_message=%s",
711
+ "OpenClaw run stream idle timeout without DoneEvent; agent=%s session=%s timeout_s=%s last_message_type=%s last_real_tool_message=%s",
712
712
  self.agent_id,
713
713
  session_name,
714
714
  timeout,
715
715
  last_message.type if last_message is not None else None,
716
- last_tool_message is not None,
716
+ last_real_tool_message is not None,
717
717
  )
718
718
  break
719
719
 
@@ -750,8 +750,8 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
750
750
  session_name=session_name,
751
751
  )
752
752
  last_message = tool_message
753
- if self._is_semantic_tool_message(tool_message):
754
- last_tool_message = tool_message
753
+ if self._is_real_tool_message(tool_message):
754
+ last_real_tool_message = tool_message
755
755
  continue
756
756
 
757
757
  if isinstance(event, DoneEvent):
@@ -773,7 +773,7 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
773
773
  session_name=session_name,
774
774
  tool_calls=tool_calls,
775
775
  last_message=last_message,
776
- last_tool_message=last_tool_message,
776
+ last_real_tool_message=last_real_tool_message,
777
777
  extra_metadata={"token_usage": serialize_data(event.token_usage), "stop_reason": event.stop_reason},
778
778
  )
779
779
 
@@ -785,7 +785,7 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
785
785
  session_name=session_name,
786
786
  tool_calls=tool_calls,
787
787
  last_message=last_message,
788
- last_tool_message=last_tool_message,
788
+ last_real_tool_message=last_real_tool_message,
789
789
  )
790
790
 
791
791
  def _build_ai_message(
@@ -817,12 +817,12 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
817
817
  session_name: str,
818
818
  tool_calls: list[dict[str, Any]],
819
819
  last_message: ChatMessage | None,
820
- last_tool_message: ChatMessage | None,
820
+ last_real_tool_message: ChatMessage | None,
821
821
  extra_metadata: dict[str, Any] | None = None,
822
822
  ) -> ChatMessage:
823
- if last_tool_message is not None:
824
- return last_tool_message.model_copy(
825
- update={"response_metadata": {**last_tool_message.response_metadata, **(extra_metadata or {})}}
823
+ if last_real_tool_message is not None:
824
+ return last_real_tool_message.model_copy(
825
+ update={"response_metadata": {**last_real_tool_message.response_metadata, **(extra_metadata or {})}}
826
826
  )
827
827
 
828
828
  if last_message is not None and last_message.type == "ai":
@@ -854,6 +854,21 @@ class OpenClawAgentAdapter(BaseAgentAdapter):
854
854
  )
855
855
  return not any(re.match(pattern, content) for pattern in low_signal_patterns)
856
856
 
857
+ @staticmethod
858
+ def _is_real_tool_message(message: ChatMessage) -> bool:
859
+ content = message.content.strip()
860
+ if not OpenClawAgentAdapter._is_semantic_tool_message(message):
861
+ return False
862
+ if message.content_type != "text":
863
+ return True
864
+
865
+ low_value_status_patterns = (
866
+ r"^(done|ok|success|completed)\.?$",
867
+ r"^(analysis|task|job) (done|completed|finished)\.?$",
868
+ r"^(saved|written) successfully\.?$",
869
+ )
870
+ return not any(re.match(pattern, content, re.IGNORECASE) for pattern in low_value_status_patterns)
871
+
857
872
  @staticmethod
858
873
  def _resolve_run_completion_idle_timeout_seconds(
859
874
  settings: dict[str, Any],
@@ -57,15 +57,34 @@ def _ensure_thread_ready(state: ThreadState, thread_id: str) -> None:
57
57
  )
58
58
 
59
59
 
60
+ def _thread_agent_conflict(thread_id: str, existing_graph_name: str, requested_graph_name: str) -> HTTPException:
61
+ return HTTPException(
62
+ status_code=status.HTTP_409_CONFLICT,
63
+ detail={
64
+ "error": "thread_agent_conflict",
65
+ "message": "Thread is already bound to a different agent",
66
+ "thread_id": thread_id,
67
+ "existing_graph_name": existing_graph_name,
68
+ "requested_graph_name": requested_graph_name,
69
+ },
70
+ )
71
+
72
+
73
+ def _ensure_thread_matches_graph(
74
+ state: ThreadState,
75
+ *,
76
+ thread_id: str,
77
+ requested_graph_name: str | None = None,
78
+ ) -> None:
79
+ if requested_graph_name and state.graph_name != requested_graph_name:
80
+ raise _thread_agent_conflict(thread_id, state.graph_name, requested_graph_name)
81
+
82
+
60
83
  async def _resolve_thread_for_execution(thread_id: str, request: Request) -> ThreadState:
61
84
  graph_name = request.headers.get("X-Agent-Name", "")
62
85
  state = await _get_or_create_thread(thread_id, graph_name)
63
86
  _ensure_thread_ready(state, thread_id)
64
-
65
- if graph_name:
66
- logger.info("Graph name overridden by header: %s", graph_name)
67
- state.graph_name = graph_name
68
-
87
+ _ensure_thread_matches_graph(state, thread_id=thread_id, requested_graph_name=graph_name or None)
69
88
  return state
70
89
 
71
90
 
@@ -114,6 +133,7 @@ async def _get_or_create_thread(thread_id: str, graph_name: Optional[str] = "")
114
133
  storage = AsyncRedisThreadStorage.get_worker_instance()
115
134
  thread_data = await storage.get_thread(thread_id)
116
135
  if thread_data:
136
+ _ensure_thread_matches_graph(thread_data, thread_id=thread_id, requested_graph_name=graph_name or None)
117
137
  return thread_data
118
138
 
119
139
  if not graph_name:
@@ -248,10 +268,12 @@ async def create_thread(graph_name: str, thread_id: Optional[str] = None) -> Thr
248
268
  if thread_id:
249
269
  try:
250
270
  state = await _get_existing_thread_or_404(thread_id)
271
+ _ensure_thread_matches_graph(state, thread_id=thread_id, requested_graph_name=graph_name)
251
272
  logger.info("%s already exists, returning existing thread", thread_id)
252
273
  return _thread_info(state)
253
- except HTTPException:
254
- pass
274
+ except HTTPException as exc:
275
+ if exc.status_code != status.HTTP_404_NOT_FOUND:
276
+ raise
255
277
 
256
278
  storage = None
257
279
  thread_state = None
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-api-server"
3
- version = "2.2.1-alpha3"
3
+ version = "2.2.1-alpha4"
4
4
  description = "A Langgraph agent API server that implements Langgraph agent's web capabilities and can interact with chatbot"
5
5
  authors = []
6
6
  readme = "README.md"
@@ -1,4 +1,5 @@
1
1
  from typing import Any, AsyncIterator, Iterator
2
+ from uuid import uuid4
2
3
 
3
4
  from core import AgentDefinition, AgentExecutionContext, AgentFramework, AgentManager, get_agent_manager
4
5
  from core import BaseAgentAdapter
@@ -177,6 +178,10 @@ class AgentSDK:
177
178
  await self.manager.get_definition(resolved_agent_name)
178
179
  return {}
179
180
 
181
+ @staticmethod
182
+ def _resolve_thread_id(thread_id: str | None) -> str:
183
+ return thread_id or f"sdk-{uuid4().hex}"
184
+
180
185
  @staticmethod
181
186
  def _build_context(
182
187
  agent_name: str,
@@ -201,7 +206,7 @@ class AgentSDK:
201
206
  self,
202
207
  agent_name: str,
203
208
  query: str | dict[str, Any],
204
- thread_id: str,
209
+ thread_id: str | None,
205
210
  ts_tenant: str | None,
206
211
  ei_token: str | None,
207
212
  attachments: list[dict[str, Any]] | None,
@@ -209,9 +214,10 @@ class AgentSDK:
209
214
  use_system_llm: bool | str | None,
210
215
  ) -> tuple[dict[str, Any], AgentExecutionContext]:
211
216
  inputs, normalized_attachments = normalize_agent_query(query, attachments)
217
+ resolved_thread_id = self._resolve_thread_id(thread_id)
212
218
  context = self._build_context(
213
219
  agent_name=agent_name,
214
- thread_id=thread_id,
220
+ thread_id=resolved_thread_id,
215
221
  ts_tenant=ts_tenant,
216
222
  ei_token=ei_token,
217
223
  attachments=normalized_attachments,
@@ -225,7 +231,7 @@ class AgentSDK:
225
231
  agent_name: str | dict[str, Any] | None = None,
226
232
  query: str | dict[str, Any] | None = None,
227
233
  *,
228
- thread_id: str = "sdk",
234
+ thread_id: str | None = None,
229
235
  ts_tenant: str | None = None,
230
236
  ei_token: str | None = None,
231
237
  attachments: list[dict[str, Any]] | None = None,
@@ -251,7 +257,7 @@ class AgentSDK:
251
257
  agent_name: str | dict[str, Any] | None = None,
252
258
  query: str | dict[str, Any] | None = None,
253
259
  *,
254
- thread_id: str = "sdk",
260
+ thread_id: str | None = None,
255
261
  ts_tenant: str | None = None,
256
262
  ei_token: str | None = None,
257
263
  attachments: list[dict[str, Any]] | None = None,
@@ -277,7 +283,7 @@ class AgentSDK:
277
283
  agent_name: str | dict[str, Any] | None = None,
278
284
  query: str | dict[str, Any] | None = None,
279
285
  *,
280
- thread_id: str = "sdk",
286
+ thread_id: str | None = None,
281
287
  ts_tenant: str | None = None,
282
288
  ei_token: str | None = None,
283
289
  attachments: list[dict[str, Any]] | None = None,
@@ -304,7 +310,7 @@ class AgentSDK:
304
310
  agent_name: str | dict[str, Any] | None = None,
305
311
  query: str | dict[str, Any] | None = None,
306
312
  *,
307
- thread_id: str = "sdk",
313
+ thread_id: str | None = None,
308
314
  ts_tenant: str | None = None,
309
315
  ei_token: str | None = None,
310
316
  attachments: list[dict[str, Any]] | None = None,