agenthub-python 0.4.11__tar.gz → 0.4.13__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 (39) hide show
  1. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/PKG-INFO +1 -1
  2. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/glm5_3/client.py +8 -7
  3. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/kimi_k3/client.py +8 -9
  4. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_chat/client.py +26 -19
  5. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/pyproject.toml +1 -1
  6. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/pyproject.toml.orig +1 -1
  7. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/README.md +0 -0
  8. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/__init__.py +0 -0
  9. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/abort_signal.py +0 -0
  10. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/ant_messages/__init__.py +0 -0
  11. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/ant_messages/client.py +0 -0
  12. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/auto_client.py +0 -0
  13. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/base_client.py +0 -0
  14. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/claude5/__init__.py +0 -0
  15. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/claude5/client.py +0 -0
  16. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/deepseek_v4/__init__.py +0 -0
  17. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/deepseek_v4/client.py +0 -0
  18. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/errors.py +0 -0
  19. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/gemini3_8/__init__.py +0 -0
  20. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/gemini3_8/client.py +0 -0
  21. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/glm5_3/__init__.py +0 -0
  22. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/gpt6/__init__.py +0 -0
  23. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/gpt6/client.py +0 -0
  24. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/integration/__init__.py +0 -0
  25. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/integration/playground.py +0 -0
  26. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/integration/tracer.py +0 -0
  27. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/kimi_k3/__init__.py +0 -0
  28. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/minimax_m3/__init__.py +0 -0
  29. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/minimax_m3/client.py +0 -0
  30. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_chat/__init__.py +0 -0
  31. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_chat_vllm_adapter/__init__.py +0 -0
  32. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_chat_vllm_adapter/client.py +0 -0
  33. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_embedding/__init__.py +0 -0
  34. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_embedding/client.py +0 -0
  35. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_responses/__init__.py +0 -0
  36. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/openai_responses/client.py +0 -0
  37. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/registry.py +0 -0
  38. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/types.py +0 -0
  39. {agenthub_python-0.4.11 → agenthub_python-0.4.13}/agenthub/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agenthub-python
3
- Version: 0.4.11
3
+ Version: 0.4.13
4
4
  Summary: AgentHub is the LLM API Hub for the Agent era, built for high-precision autonomous agents.
5
5
  Keywords: agent,llm,gemini,claude,gpt
6
6
  Author: PrismShadow
@@ -203,23 +203,24 @@ class GLM5_3Client(LLMClient):
203
203
  if "tool_call_id" not in item:
204
204
  raise ValueError("tool_call_id is required for tool result.")
205
205
 
206
- # a tool result without images stays a plain string, the only content shape
207
- # the Chat Completion schema documents for a tool message
208
- content = item["text"]
206
+ # Chat Completions lets a tool message carry text only, and a server that
207
+ # validates the schema rejects the whole request over an image part in one.
208
+ # The images ride in the user message that follows the turn's tool messages,
209
+ # the one place every OpenAI-compatible server reads them.
209
210
  if "images" in item and item["images"]:
210
211
  if not supports_image:
211
212
  raise ValueError(f"GLM {self._model} does not support images in tool results.")
212
213
 
213
- content = [{"type": "text", "text": item["text"]}]
214
214
  for image_url in item["images"]:
215
- content.append({"type": "image_url", "image_url": {"url": image_url}})
215
+ content_parts.append({"type": "image_url", "image_url": {"url": image_url}})
216
216
 
217
- # Tool results are sent as separate messages
217
+ # the plain string is the only content shape the Chat Completion schema
218
+ # documents for a tool message
218
219
  openai_messages.append(
219
220
  {
220
221
  "role": "tool",
221
222
  "tool_call_id": item["tool_call_id"],
222
- "content": content,
223
+ "content": item["text"],
223
224
  }
224
225
  )
225
226
  else:
@@ -214,23 +214,22 @@ class KimiK3Client(LLMClient):
214
214
  if "tool_call_id" not in item:
215
215
  raise ValueError("tool_call_id is required for tool result.")
216
216
 
217
- content = [{"type": "text", "text": item["text"]}]
218
-
217
+ # Chat Completions lets a tool message carry text only, and a server that
218
+ # validates the schema rejects the whole request over an image part in one.
219
+ # The images ride in the user message that follows the turn's tool messages,
220
+ # the one place every OpenAI-compatible server reads them.
219
221
  if "images" in item and item["images"]:
220
222
  for image_url in item["images"]:
221
223
  base64_image = await self._convert_image_url_to_base64(image_url)
222
- if "siliconflow.cn" in str(self._client.base_url):
223
- # siliconflow does not support image_url in tool result
224
- content_parts.append({"type": "image_url", "image_url": {"url": base64_image}})
225
- else:
226
- content.append({"type": "image_url", "image_url": {"url": base64_image}})
224
+ content_parts.append({"type": "image_url", "image_url": {"url": base64_image}})
227
225
 
228
- # Tool results are sent as separate messages
226
+ # the plain string is the form Moonshot's own tool-call examples send and every
227
+ # OpenAI-compatible server accepts
229
228
  openai_messages.append(
230
229
  {
231
230
  "role": "tool",
232
231
  "tool_call_id": item["tool_call_id"],
233
- "content": content,
232
+ "content": item["text"],
234
233
  }
235
234
  )
236
235
  else:
@@ -143,6 +143,14 @@ class OpenaiChatClient(LLMClient):
143
143
  List of OpenAI Chat Completions message dictionaries
144
144
  """
145
145
  openai_messages = []
146
+ # The reasoning field this upstream has produced so far, if any. A turn the model
147
+ # answered without thinking still has to carry that field, empty, when it made tool
148
+ # calls: DeepSeek stops thinking part-way through a long tool chain, and then rejects
149
+ # the replay of that turn with "the reasoning_content in the thinking mode must be
150
+ # passed back to the API". It waives that only for tool_call ids it issued itself,
151
+ # which a relay that reissues ids takes away. A conversation that never produced a
152
+ # reasoning field never receives one.
153
+ replay_fields: set[str | None] = set()
146
154
 
147
155
  for msg in messages:
148
156
  content_parts = [] # may be empty for tool results
@@ -158,6 +166,8 @@ class OpenaiChatClient(LLMClient):
158
166
  elif item["type"] == "thinking":
159
167
  thinking += item["thinking"]
160
168
  thinking_fields.add((item.get("fidelity") or {}).get("reasoning_field"))
169
+ if item["thinking"]:
170
+ replay_fields.add((item.get("fidelity") or {}).get("reasoning_field"))
161
171
  elif item["type"] == "tool_call":
162
172
  tool_calls.append(
163
173
  {
@@ -173,28 +183,22 @@ class OpenaiChatClient(LLMClient):
173
183
  if "tool_call_id" not in item:
174
184
  raise ValueError("tool_call_id is required for tool result.")
175
185
 
176
- image_parts = []
177
-
186
+ # Chat Completions lets a tool message carry text only, and a server that
187
+ # validates the schema rejects the whole request over an image part in one.
188
+ # The images ride in the user message that follows the turn's tool messages,
189
+ # the one place every OpenAI-compatible server reads them.
178
190
  if "images" in item and item["images"]:
179
191
  for image_url in item["images"]:
180
- part = self._convert_image_url(await self._convert_image_url_to_base64(image_url))
181
- if "siliconflow.cn" in str(self._client.base_url):
182
- # siliconflow does not support image_url in tool result
183
- content_parts.append(part)
184
- else:
185
- image_parts.append(part)
186
-
187
- # a plain string is the form every OpenAI-compatible server accepts for a text
188
- # result; the content-part list is reserved for results carrying images, which
189
- # only servers with multimodal tool messages take
190
- content = [{"type": "text", "text": item["text"]}, *image_parts] if image_parts else item["text"]
191
-
192
- # Tool results are sent as separate messages
192
+ content_parts.append(
193
+ self._convert_image_url(await self._convert_image_url_to_base64(image_url))
194
+ )
195
+
196
+ # the plain string is the form every OpenAI-compatible server accepts
193
197
  openai_messages.append(
194
198
  {
195
199
  "role": "tool",
196
200
  "tool_call_id": item["tool_call_id"],
197
- "content": content,
201
+ "content": item["text"],
198
202
  }
199
203
  )
200
204
  else:
@@ -207,12 +211,15 @@ class OpenaiChatClient(LLMClient):
207
211
  if tool_calls:
208
212
  message["tool_calls"] = tool_calls
209
213
 
210
- if thinking:
214
+ # This turn's own fidelity when it thought; otherwise the field the upstream has
215
+ # produced, with `thinking` still "" — the empty value keeps the turn replayable.
216
+ fields = thinking_fields if thinking else replay_fields
217
+ if thinking or (tool_calls and replay_fields):
211
218
  # send thinking back through the exact field the upstream produced (recorded
212
219
  # in the item fidelity); servers may reject the spelling they did not emit
213
- if thinking_fields == {"reasoning_content"}:
220
+ if fields == {"reasoning_content"}:
214
221
  message["reasoning_content"] = thinking
215
- elif thinking_fields == {"reasoning"}:
222
+ elif fields == {"reasoning"}:
216
223
  message["reasoning"] = thinking
217
224
  else:
218
225
  message["reasoning_content"] = thinking # vLLM & siliconflow compatibility
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agenthub-python"
3
- version = "0.4.11"
3
+ version = "0.4.13"
4
4
  description = "AgentHub is the LLM API Hub for the Agent era, built for high-precision autonomous agents."
5
5
  keywords = [
6
6
  "agent",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agenthub-python"
3
- version = "0.4.11"
3
+ version = "0.4.13"
4
4
  description = "AgentHub is the LLM API Hub for the Agent era, built for high-precision autonomous agents."
5
5
  keywords = ["agent", "llm", "gemini", "claude", "gpt"]
6
6
  readme = "README.md"