agenthub-python 0.4.13__tar.gz → 0.4.15__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.
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/PKG-INFO +1 -1
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/deepseek_v4/client.py +63 -29
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/gpt6/client.py +63 -29
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/minimax_m3/client.py +51 -24
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_responses/client.py +53 -26
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/types.py +3 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/pyproject.toml +1 -1
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/pyproject.toml.orig +1 -1
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/README.md +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/abort_signal.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/ant_messages/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/ant_messages/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/auto_client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/base_client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/claude5/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/claude5/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/deepseek_v4/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/errors.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/gemini3_8/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/gemini3_8/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/glm5_3/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/glm5_3/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/gpt6/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/integration/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/integration/playground.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/integration/tracer.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/kimi_k3/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/kimi_k3/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/minimax_m3/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_chat/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_chat/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_chat_vllm_adapter/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_chat_vllm_adapter/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_embedding/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_embedding/client.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_responses/__init__.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/registry.py +0 -0
- {agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/utils.py +0 -0
|
@@ -201,16 +201,23 @@ class DeepSeekV4Client(LLMClient):
|
|
|
201
201
|
raise ValueError("tool_call_id is required for tool result.")
|
|
202
202
|
|
|
203
203
|
# NOTE: tool results are input items
|
|
204
|
-
|
|
204
|
+
image_parts = []
|
|
205
205
|
if "images" in item:
|
|
206
206
|
if not supports_image:
|
|
207
207
|
raise ValueError(f"DeepSeek {self._model} does not support images in tool results.")
|
|
208
208
|
|
|
209
209
|
for image_url in item["images"]:
|
|
210
|
-
|
|
210
|
+
image_parts.append({"type": "input_image", "image_url": image_url})
|
|
211
|
+
|
|
212
|
+
# a plain string is the form the Responses API documents for a text result and the
|
|
213
|
+
# one every endpoint that fronts this model accepts; the content-part list is
|
|
214
|
+
# reserved for results carrying images
|
|
215
|
+
output = (
|
|
216
|
+
[{"type": "input_text", "text": item["text"]}, *image_parts] if image_parts else item["text"]
|
|
217
|
+
)
|
|
211
218
|
|
|
212
219
|
input_list.append(
|
|
213
|
-
{"type": "function_call_output", "call_id": item["tool_call_id"], "output":
|
|
220
|
+
{"type": "function_call_output", "call_id": item["tool_call_id"], "output": output}
|
|
214
221
|
)
|
|
215
222
|
else:
|
|
216
223
|
raise ValueError(f"Unknown item: {item}")
|
|
@@ -253,6 +260,7 @@ class DeepSeekV4Client(LLMClient):
|
|
|
253
260
|
"name": model_output.item.name,
|
|
254
261
|
"arguments": "",
|
|
255
262
|
"tool_call_id": model_output.item.call_id,
|
|
263
|
+
"item_id": model_output.item.id,
|
|
256
264
|
}
|
|
257
265
|
)
|
|
258
266
|
else:
|
|
@@ -261,11 +269,27 @@ class DeepSeekV4Client(LLMClient):
|
|
|
261
269
|
elif deepseek_event_type == "response.function_call_arguments.delta":
|
|
262
270
|
event_type = "delta"
|
|
263
271
|
content_items.append(
|
|
264
|
-
{
|
|
272
|
+
{
|
|
273
|
+
"type": "partial_tool_call",
|
|
274
|
+
"name": "",
|
|
275
|
+
"arguments": model_output.delta,
|
|
276
|
+
"tool_call_id": "",
|
|
277
|
+
"item_id": model_output.item_id,
|
|
278
|
+
}
|
|
265
279
|
)
|
|
266
280
|
|
|
267
281
|
elif deepseek_event_type == "response.function_call_arguments.done":
|
|
282
|
+
# a stop naming the item closes that call
|
|
268
283
|
event_type = "stop"
|
|
284
|
+
content_items.append(
|
|
285
|
+
{
|
|
286
|
+
"type": "partial_tool_call",
|
|
287
|
+
"name": "",
|
|
288
|
+
"arguments": "",
|
|
289
|
+
"tool_call_id": "",
|
|
290
|
+
"item_id": model_output.item_id,
|
|
291
|
+
}
|
|
292
|
+
)
|
|
269
293
|
|
|
270
294
|
elif deepseek_event_type in ("response.completed", "response.incomplete"):
|
|
271
295
|
event_type = "stop"
|
|
@@ -327,51 +351,61 @@ class DeepSeekV4Client(LLMClient):
|
|
|
327
351
|
# Use unified message conversion
|
|
328
352
|
input_list = self.transform_uni_message_to_model_input(messages)
|
|
329
353
|
|
|
354
|
+
# Calls still streaming, keyed by the item id their fragments carry (the call id when a
|
|
355
|
+
# server sends none): a gateway may open several before closing any of them.
|
|
356
|
+
open_tool_calls: dict[str, dict] = {}
|
|
357
|
+
last_opened = ""
|
|
358
|
+
|
|
359
|
+
def key_of(item_id: str | None) -> str:
|
|
360
|
+
return item_id if item_id in open_tool_calls else last_opened
|
|
361
|
+
|
|
330
362
|
# Stream generate
|
|
331
|
-
partial_tool_call = {}
|
|
332
363
|
stream = await self._client.responses.create(**deepseek_config, input=input_list, stream=True)
|
|
333
|
-
async for
|
|
334
|
-
event = self.transform_model_output_to_uni_event(
|
|
364
|
+
async for model_event in stream:
|
|
365
|
+
event = self.transform_model_output_to_uni_event(model_event)
|
|
366
|
+
fragments = [item for item in event["content_items"] if item["type"] == "partial_tool_call"]
|
|
335
367
|
if event["event_type"] == "start":
|
|
336
|
-
for item in
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
yield event
|
|
368
|
+
for item in fragments:
|
|
369
|
+
last_opened = item.get("item_id") or item["tool_call_id"]
|
|
370
|
+
open_tool_calls[last_opened] = {
|
|
371
|
+
"name": item["name"],
|
|
372
|
+
"tool_call_id": item["tool_call_id"],
|
|
373
|
+
"arguments": "",
|
|
374
|
+
}
|
|
375
|
+
yield event
|
|
345
376
|
elif event["event_type"] == "delta":
|
|
346
|
-
for item in
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
377
|
+
for item in fragments:
|
|
378
|
+
tool_call = open_tool_calls.get(key_of(item.get("item_id")))
|
|
379
|
+
if tool_call is not None:
|
|
380
|
+
tool_call["arguments"] += item["arguments"]
|
|
351
381
|
yield event
|
|
352
382
|
elif event["event_type"] == "stop":
|
|
353
|
-
|
|
354
|
-
|
|
383
|
+
# a stop that names calls closes them; the end of the response closes whatever
|
|
384
|
+
# a gateway never closed on its own
|
|
385
|
+
closing = [key_of(item.get("item_id")) for item in fragments] or list(open_tool_calls)
|
|
386
|
+
for key in closing:
|
|
387
|
+
tool_call = open_tool_calls.pop(key, None)
|
|
388
|
+
if tool_call is None:
|
|
389
|
+
continue
|
|
355
390
|
yield {
|
|
356
391
|
"role": "assistant",
|
|
357
392
|
"event_type": "delta",
|
|
358
393
|
"content_items": [
|
|
359
394
|
{
|
|
360
395
|
"type": "tool_call",
|
|
361
|
-
"name":
|
|
396
|
+
"name": tool_call["name"],
|
|
362
397
|
"arguments": parse_tool_call_arguments(
|
|
363
|
-
|
|
398
|
+
tool_call["arguments"],
|
|
364
399
|
self.__class__.__name__,
|
|
365
|
-
|
|
366
|
-
|
|
400
|
+
tool_call["name"],
|
|
401
|
+
tool_call["tool_call_id"],
|
|
367
402
|
),
|
|
368
|
-
"tool_call_id":
|
|
403
|
+
"tool_call_id": tool_call["tool_call_id"],
|
|
369
404
|
}
|
|
370
405
|
],
|
|
371
406
|
"usage_metadata": None,
|
|
372
407
|
"finish_reason": None,
|
|
373
408
|
}
|
|
374
|
-
partial_tool_call = {}
|
|
375
409
|
|
|
376
410
|
if event["finish_reason"] or event["usage_metadata"]:
|
|
377
411
|
yield event
|
|
@@ -212,13 +212,20 @@ class GPT6Client(LLMClient):
|
|
|
212
212
|
raise ValueError("tool_call_id is required for tool result.")
|
|
213
213
|
|
|
214
214
|
# NOTE: tool results are input items
|
|
215
|
-
|
|
215
|
+
image_parts = []
|
|
216
216
|
if "images" in item:
|
|
217
217
|
for image_url in item["images"]:
|
|
218
|
-
|
|
218
|
+
image_parts.append(self._convert_image_url(image_url))
|
|
219
|
+
|
|
220
|
+
# a plain string is the form the Responses API documents for a text result and the
|
|
221
|
+
# one every endpoint that fronts this model accepts; the content-part list is
|
|
222
|
+
# reserved for results carrying images
|
|
223
|
+
output = (
|
|
224
|
+
[{"type": "input_text", "text": item["text"]}, *image_parts] if image_parts else item["text"]
|
|
225
|
+
)
|
|
219
226
|
|
|
220
227
|
input_list.append(
|
|
221
|
-
{"type": "function_call_output", "call_id": item["tool_call_id"], "output":
|
|
228
|
+
{"type": "function_call_output", "call_id": item["tool_call_id"], "output": output}
|
|
222
229
|
)
|
|
223
230
|
else:
|
|
224
231
|
raise ValueError(f"Unknown item: {item}")
|
|
@@ -265,6 +272,7 @@ class GPT6Client(LLMClient):
|
|
|
265
272
|
"name": model_output.item.name,
|
|
266
273
|
"arguments": "",
|
|
267
274
|
"tool_call_id": model_output.item.call_id,
|
|
275
|
+
"item_id": model_output.item.id,
|
|
268
276
|
}
|
|
269
277
|
)
|
|
270
278
|
elif model_output.item.type == "message":
|
|
@@ -281,11 +289,27 @@ class GPT6Client(LLMClient):
|
|
|
281
289
|
elif openai_event_type == "response.function_call_arguments.delta":
|
|
282
290
|
event_type = "delta"
|
|
283
291
|
content_items.append(
|
|
284
|
-
{
|
|
292
|
+
{
|
|
293
|
+
"type": "partial_tool_call",
|
|
294
|
+
"name": "",
|
|
295
|
+
"arguments": model_output.delta,
|
|
296
|
+
"tool_call_id": "",
|
|
297
|
+
"item_id": model_output.item_id,
|
|
298
|
+
}
|
|
285
299
|
)
|
|
286
300
|
|
|
287
301
|
elif openai_event_type == "response.function_call_arguments.done":
|
|
302
|
+
# a stop naming the item closes that call
|
|
288
303
|
event_type = "stop"
|
|
304
|
+
content_items.append(
|
|
305
|
+
{
|
|
306
|
+
"type": "partial_tool_call",
|
|
307
|
+
"name": "",
|
|
308
|
+
"arguments": "",
|
|
309
|
+
"tool_call_id": "",
|
|
310
|
+
"item_id": model_output.item_id,
|
|
311
|
+
}
|
|
312
|
+
)
|
|
289
313
|
|
|
290
314
|
elif openai_event_type in ("response.completed", "response.incomplete"):
|
|
291
315
|
event_type = "stop"
|
|
@@ -375,51 +399,61 @@ class GPT6Client(LLMClient):
|
|
|
375
399
|
# Use unified message conversion
|
|
376
400
|
input_list = self.transform_uni_message_to_model_input(messages)
|
|
377
401
|
|
|
402
|
+
# Calls still streaming, keyed by the item id their fragments carry (the call id when a
|
|
403
|
+
# server sends none): a gateway may open several before closing any of them.
|
|
404
|
+
open_tool_calls: dict[str, dict] = {}
|
|
405
|
+
last_opened = ""
|
|
406
|
+
|
|
407
|
+
def key_of(item_id: str | None) -> str:
|
|
408
|
+
return item_id if item_id in open_tool_calls else last_opened
|
|
409
|
+
|
|
378
410
|
# Stream generate
|
|
379
|
-
partial_tool_call = {}
|
|
380
411
|
stream = await self._client.responses.create(**openai_config, input=input_list, stream=True)
|
|
381
|
-
async for
|
|
382
|
-
event = self.transform_model_output_to_uni_event(
|
|
412
|
+
async for model_event in stream:
|
|
413
|
+
event = self.transform_model_output_to_uni_event(model_event)
|
|
414
|
+
fragments = [item for item in event["content_items"] if item["type"] == "partial_tool_call"]
|
|
383
415
|
if event["event_type"] == "start":
|
|
384
|
-
for item in
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
yield event
|
|
416
|
+
for item in fragments:
|
|
417
|
+
last_opened = item.get("item_id") or item["tool_call_id"]
|
|
418
|
+
open_tool_calls[last_opened] = {
|
|
419
|
+
"name": item["name"],
|
|
420
|
+
"tool_call_id": item["tool_call_id"],
|
|
421
|
+
"arguments": "",
|
|
422
|
+
}
|
|
423
|
+
yield event
|
|
393
424
|
elif event["event_type"] == "delta":
|
|
394
|
-
for item in
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
425
|
+
for item in fragments:
|
|
426
|
+
tool_call = open_tool_calls.get(key_of(item.get("item_id")))
|
|
427
|
+
if tool_call is not None:
|
|
428
|
+
tool_call["arguments"] += item["arguments"]
|
|
399
429
|
yield event
|
|
400
430
|
elif event["event_type"] == "stop":
|
|
401
|
-
|
|
402
|
-
|
|
431
|
+
# a stop that names calls closes them; the end of the response closes whatever
|
|
432
|
+
# a gateway never closed on its own
|
|
433
|
+
closing = [key_of(item.get("item_id")) for item in fragments] or list(open_tool_calls)
|
|
434
|
+
for key in closing:
|
|
435
|
+
tool_call = open_tool_calls.pop(key, None)
|
|
436
|
+
if tool_call is None:
|
|
437
|
+
continue
|
|
403
438
|
yield {
|
|
404
439
|
"role": "assistant",
|
|
405
440
|
"event_type": "delta",
|
|
406
441
|
"content_items": [
|
|
407
442
|
{
|
|
408
443
|
"type": "tool_call",
|
|
409
|
-
"name":
|
|
444
|
+
"name": tool_call["name"],
|
|
410
445
|
"arguments": parse_tool_call_arguments(
|
|
411
|
-
|
|
446
|
+
tool_call["arguments"],
|
|
412
447
|
self.__class__.__name__,
|
|
413
|
-
|
|
414
|
-
|
|
448
|
+
tool_call["name"],
|
|
449
|
+
tool_call["tool_call_id"],
|
|
415
450
|
),
|
|
416
|
-
"tool_call_id":
|
|
451
|
+
"tool_call_id": tool_call["tool_call_id"],
|
|
417
452
|
}
|
|
418
453
|
],
|
|
419
454
|
"usage_metadata": None,
|
|
420
455
|
"finish_reason": None,
|
|
421
456
|
}
|
|
422
|
-
partial_tool_call = {}
|
|
423
457
|
|
|
424
458
|
if event["finish_reason"] or event["usage_metadata"]:
|
|
425
459
|
yield event
|
|
@@ -217,17 +217,34 @@ class MiniMaxM3Client(LLMClient):
|
|
|
217
217
|
"name": model_output.item.name,
|
|
218
218
|
"arguments": "",
|
|
219
219
|
"tool_call_id": model_output.item.call_id,
|
|
220
|
+
"item_id": model_output.item.id,
|
|
220
221
|
}
|
|
221
222
|
)
|
|
222
223
|
|
|
223
224
|
elif minimax_event_type == "response.function_call_arguments.delta":
|
|
224
225
|
event_type = "delta"
|
|
225
226
|
content_items.append(
|
|
226
|
-
{
|
|
227
|
+
{
|
|
228
|
+
"type": "partial_tool_call",
|
|
229
|
+
"name": "",
|
|
230
|
+
"arguments": model_output.delta,
|
|
231
|
+
"tool_call_id": "",
|
|
232
|
+
"item_id": model_output.item_id,
|
|
233
|
+
}
|
|
227
234
|
)
|
|
228
235
|
|
|
229
236
|
elif minimax_event_type == "response.function_call_arguments.done":
|
|
237
|
+
# a stop naming the item closes that call
|
|
230
238
|
event_type = "stop"
|
|
239
|
+
content_items.append(
|
|
240
|
+
{
|
|
241
|
+
"type": "partial_tool_call",
|
|
242
|
+
"name": "",
|
|
243
|
+
"arguments": "",
|
|
244
|
+
"tool_call_id": "",
|
|
245
|
+
"item_id": model_output.item_id,
|
|
246
|
+
}
|
|
247
|
+
)
|
|
231
248
|
|
|
232
249
|
elif minimax_event_type in ("response.completed", "response.incomplete"):
|
|
233
250
|
event_type = "stop"
|
|
@@ -279,50 +296,60 @@ class MiniMaxM3Client(LLMClient):
|
|
|
279
296
|
minimax_config = self.transform_uni_config_to_model_config(config)
|
|
280
297
|
input_list = self.transform_uni_message_to_model_input(messages)
|
|
281
298
|
|
|
282
|
-
|
|
299
|
+
# Calls still streaming, keyed by the item id their fragments carry (the call id when a
|
|
300
|
+
# server sends none): a gateway may open several before closing any of them.
|
|
301
|
+
open_tool_calls: dict[str, dict] = {}
|
|
302
|
+
last_opened = ""
|
|
303
|
+
|
|
304
|
+
def key_of(item_id: str | None) -> str:
|
|
305
|
+
return item_id if item_id in open_tool_calls else last_opened
|
|
306
|
+
|
|
283
307
|
stream = await self._client.responses.create(**minimax_config, input=input_list, stream=True)
|
|
284
308
|
async for model_event in stream:
|
|
285
309
|
event = self.transform_model_output_to_uni_event(model_event)
|
|
310
|
+
fragments = [item for item in event["content_items"] if item["type"] == "partial_tool_call"]
|
|
286
311
|
if event["event_type"] == "start":
|
|
287
|
-
for item in
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
yield event
|
|
312
|
+
for item in fragments:
|
|
313
|
+
last_opened = item.get("item_id") or item["tool_call_id"]
|
|
314
|
+
open_tool_calls[last_opened] = {
|
|
315
|
+
"name": item["name"],
|
|
316
|
+
"tool_call_id": item["tool_call_id"],
|
|
317
|
+
"arguments": "",
|
|
318
|
+
}
|
|
319
|
+
yield event
|
|
296
320
|
elif event["event_type"] == "delta":
|
|
297
|
-
for item in
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
321
|
+
for item in fragments:
|
|
322
|
+
tool_call = open_tool_calls.get(key_of(item.get("item_id")))
|
|
323
|
+
if tool_call is not None:
|
|
324
|
+
tool_call["arguments"] += item["arguments"]
|
|
302
325
|
yield event
|
|
303
326
|
elif event["event_type"] == "stop":
|
|
304
|
-
|
|
305
|
-
|
|
327
|
+
# a stop that names calls closes them; the end of the response closes whatever
|
|
328
|
+
# a gateway never closed on its own
|
|
329
|
+
closing = [key_of(item.get("item_id")) for item in fragments] or list(open_tool_calls)
|
|
330
|
+
for key in closing:
|
|
331
|
+
tool_call = open_tool_calls.pop(key, None)
|
|
332
|
+
if tool_call is None:
|
|
333
|
+
continue
|
|
306
334
|
yield {
|
|
307
335
|
"role": "assistant",
|
|
308
336
|
"event_type": "delta",
|
|
309
337
|
"content_items": [
|
|
310
338
|
{
|
|
311
339
|
"type": "tool_call",
|
|
312
|
-
"name":
|
|
340
|
+
"name": tool_call["name"],
|
|
313
341
|
"arguments": parse_tool_call_arguments(
|
|
314
|
-
|
|
342
|
+
tool_call["arguments"],
|
|
315
343
|
self.__class__.__name__,
|
|
316
|
-
|
|
317
|
-
|
|
344
|
+
tool_call["name"],
|
|
345
|
+
tool_call["tool_call_id"],
|
|
318
346
|
),
|
|
319
|
-
"tool_call_id":
|
|
347
|
+
"tool_call_id": tool_call["tool_call_id"],
|
|
320
348
|
}
|
|
321
349
|
],
|
|
322
350
|
"usage_metadata": None,
|
|
323
351
|
"finish_reason": None,
|
|
324
352
|
}
|
|
325
|
-
partial_tool_call = {}
|
|
326
353
|
|
|
327
354
|
if event["finish_reason"] or event["usage_metadata"]:
|
|
328
355
|
yield event
|
|
@@ -268,6 +268,7 @@ class OpenaiResponsesClient(LLMClient):
|
|
|
268
268
|
"name": model_output.item.name,
|
|
269
269
|
"arguments": "",
|
|
270
270
|
"tool_call_id": model_output.item.call_id,
|
|
271
|
+
"item_id": model_output.item.id,
|
|
271
272
|
}
|
|
272
273
|
)
|
|
273
274
|
elif model_output.item.type == "message" and getattr(model_output.item, "phase", None):
|
|
@@ -295,11 +296,27 @@ class OpenaiResponsesClient(LLMClient):
|
|
|
295
296
|
elif openai_event_type == "response.function_call_arguments.delta":
|
|
296
297
|
event_type = "delta"
|
|
297
298
|
content_items.append(
|
|
298
|
-
{
|
|
299
|
+
{
|
|
300
|
+
"type": "partial_tool_call",
|
|
301
|
+
"name": "",
|
|
302
|
+
"arguments": model_output.delta,
|
|
303
|
+
"tool_call_id": "",
|
|
304
|
+
"item_id": model_output.item_id,
|
|
305
|
+
}
|
|
299
306
|
)
|
|
300
307
|
|
|
301
308
|
elif openai_event_type == "response.function_call_arguments.done":
|
|
309
|
+
# a stop naming the item closes that call
|
|
302
310
|
event_type = "stop"
|
|
311
|
+
content_items.append(
|
|
312
|
+
{
|
|
313
|
+
"type": "partial_tool_call",
|
|
314
|
+
"name": "",
|
|
315
|
+
"arguments": "",
|
|
316
|
+
"tool_call_id": "",
|
|
317
|
+
"item_id": model_output.item_id,
|
|
318
|
+
}
|
|
319
|
+
)
|
|
303
320
|
|
|
304
321
|
elif openai_event_type in ("response.completed", "response.incomplete"):
|
|
305
322
|
event_type = "stop"
|
|
@@ -364,51 +381,61 @@ class OpenaiResponsesClient(LLMClient):
|
|
|
364
381
|
# Use unified message conversion
|
|
365
382
|
input_list = self.transform_uni_message_to_model_input(messages)
|
|
366
383
|
|
|
384
|
+
# Calls still streaming, keyed by the item id their fragments carry (the call id when a
|
|
385
|
+
# server sends none): a gateway may open several before closing any of them.
|
|
386
|
+
open_tool_calls: dict[str, dict] = {}
|
|
387
|
+
last_opened = ""
|
|
388
|
+
|
|
389
|
+
def key_of(item_id: str | None) -> str:
|
|
390
|
+
return item_id if item_id in open_tool_calls else last_opened
|
|
391
|
+
|
|
367
392
|
# Stream generate
|
|
368
|
-
partial_tool_call = {}
|
|
369
393
|
stream = await self._client.responses.create(**openai_config, input=input_list, stream=True)
|
|
370
|
-
async for
|
|
371
|
-
event = self.transform_model_output_to_uni_event(
|
|
394
|
+
async for model_event in stream:
|
|
395
|
+
event = self.transform_model_output_to_uni_event(model_event)
|
|
396
|
+
fragments = [item for item in event["content_items"] if item["type"] == "partial_tool_call"]
|
|
372
397
|
if event["event_type"] == "start":
|
|
373
|
-
for item in
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
yield event
|
|
398
|
+
for item in fragments:
|
|
399
|
+
last_opened = item.get("item_id") or item["tool_call_id"]
|
|
400
|
+
open_tool_calls[last_opened] = {
|
|
401
|
+
"name": item["name"],
|
|
402
|
+
"tool_call_id": item["tool_call_id"],
|
|
403
|
+
"arguments": "",
|
|
404
|
+
}
|
|
405
|
+
yield event
|
|
382
406
|
elif event["event_type"] == "delta":
|
|
383
|
-
for item in
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
407
|
+
for item in fragments:
|
|
408
|
+
tool_call = open_tool_calls.get(key_of(item.get("item_id")))
|
|
409
|
+
if tool_call is not None:
|
|
410
|
+
tool_call["arguments"] += item["arguments"]
|
|
388
411
|
yield event
|
|
389
412
|
elif event["event_type"] == "stop":
|
|
390
|
-
|
|
391
|
-
|
|
413
|
+
# a stop that names calls closes them; the end of the response closes whatever
|
|
414
|
+
# a gateway never closed on its own
|
|
415
|
+
closing = [key_of(item.get("item_id")) for item in fragments] or list(open_tool_calls)
|
|
416
|
+
for key in closing:
|
|
417
|
+
tool_call = open_tool_calls.pop(key, None)
|
|
418
|
+
if tool_call is None:
|
|
419
|
+
continue
|
|
392
420
|
yield {
|
|
393
421
|
"role": "assistant",
|
|
394
422
|
"event_type": "delta",
|
|
395
423
|
"content_items": [
|
|
396
424
|
{
|
|
397
425
|
"type": "tool_call",
|
|
398
|
-
"name":
|
|
426
|
+
"name": tool_call["name"],
|
|
399
427
|
"arguments": parse_tool_call_arguments(
|
|
400
|
-
|
|
428
|
+
tool_call["arguments"],
|
|
401
429
|
self.__class__.__name__,
|
|
402
|
-
|
|
403
|
-
|
|
430
|
+
tool_call["name"],
|
|
431
|
+
tool_call["tool_call_id"],
|
|
404
432
|
),
|
|
405
|
-
"tool_call_id":
|
|
433
|
+
"tool_call_id": tool_call["tool_call_id"],
|
|
406
434
|
}
|
|
407
435
|
],
|
|
408
436
|
"usage_metadata": None,
|
|
409
437
|
"finish_reason": None,
|
|
410
438
|
}
|
|
411
|
-
partial_tool_call = {}
|
|
412
439
|
|
|
413
440
|
if event["finish_reason"] or event["usage_metadata"]:
|
|
414
441
|
yield event
|
|
@@ -96,6 +96,9 @@ class PartialToolCallContentItem(TypedDict):
|
|
|
96
96
|
name: str
|
|
97
97
|
arguments: str
|
|
98
98
|
tool_call_id: str
|
|
99
|
+
# id of the streamed item this fragment belongs to, when the provider sends one (the
|
|
100
|
+
# Responses output item id); ties fragments to their call when several calls stream at once
|
|
101
|
+
item_id: NotRequired[str | None]
|
|
99
102
|
fidelity: NotRequired[Fidelity]
|
|
100
103
|
|
|
101
104
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_chat_vllm_adapter/__init__.py
RENAMED
|
File without changes
|
{agenthub_python-0.4.13 → agenthub_python-0.4.15}/agenthub/openai_chat_vllm_adapter/client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|