agenthub-python 0.4.4__tar.gz → 0.4.6__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.4 → agenthub_python-0.4.6}/PKG-INFO +2 -1
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/__init__.py +8 -1
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/ant_messages/client.py +26 -4
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/auto_client.py +69 -18
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/base_client.py +29 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/claude5/client.py +34 -5
- agenthub_python-0.4.6/agenthub/deepseek_v4/client.py +371 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/errors.py +14 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/gemini3_7/client.py +62 -32
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/glm5_3/client.py +17 -2
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/gpt5_6/client.py +34 -3
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/integration/playground.py +441 -70
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/kimi_k3/client.py +17 -2
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/minimax_m3/client.py +39 -20
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/openai_chat/client.py +17 -2
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/openai_embedding/client.py +17 -2
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/openai_responses/client.py +38 -17
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/registry.py +63 -25
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/types.py +3 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/utils.py +16 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/pyproject.toml +2 -1
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/pyproject.toml.orig +2 -2
- agenthub_python-0.4.4/agenthub/deepseek_v4/client.py +0 -376
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/README.md +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/abort_signal.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/ant_messages/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/claude5/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/deepseek_v4/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/gemini3_7/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/glm5_3/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/gpt5_6/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/integration/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/integration/tracer.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/kimi_k3/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/minimax_m3/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/openai_chat/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/openai_embedding/__init__.py +0 -0
- {agenthub_python-0.4.4 → agenthub_python-0.4.6}/agenthub/openai_responses/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agenthub-python
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.6
|
|
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
|
|
@@ -14,6 +14,7 @@ Requires-Dist: httpx[socks] ; extra == 'dev'
|
|
|
14
14
|
Requires-Dist: pytest>=8.4.2 ; extra == 'dev'
|
|
15
15
|
Requires-Dist: pytest-asyncio>=0.23.0 ; extra == 'dev'
|
|
16
16
|
Requires-Dist: pytest-xdist>=3.6.0 ; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest-rerunfailures>=15.0 ; extra == 'dev'
|
|
17
18
|
Requires-Dist: ruff>=0.14.3 ; extra == 'dev'
|
|
18
19
|
Requires-Dist: pillow>=10.0.0 ; extra == 'dev'
|
|
19
20
|
Requires-Python: >=3.11
|
|
@@ -13,7 +13,13 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
from .auto_client import AutoLLMClient
|
|
16
|
-
from .errors import
|
|
16
|
+
from .errors import (
|
|
17
|
+
AgentHubError,
|
|
18
|
+
EmptyResponseError,
|
|
19
|
+
ToolCallArgumentParseError,
|
|
20
|
+
UnsupportedOperationError,
|
|
21
|
+
UnsupportedParameterError,
|
|
22
|
+
)
|
|
17
23
|
from .registry import Currency, Modality, ModelPricing, SupportedModel, list_supported_models
|
|
18
24
|
from .types import PromptCaching, ThinkingLevel
|
|
19
25
|
|
|
@@ -29,6 +35,7 @@ __all__ = [
|
|
|
29
35
|
"SupportedModel",
|
|
30
36
|
"ThinkingLevel",
|
|
31
37
|
"ToolCallArgumentParseError",
|
|
38
|
+
"UnsupportedOperationError",
|
|
32
39
|
"UnsupportedParameterError",
|
|
33
40
|
"list_supported_models",
|
|
34
41
|
]
|
|
@@ -33,7 +33,7 @@ from ..types import (
|
|
|
33
33
|
UniMessage,
|
|
34
34
|
UsageMetadata,
|
|
35
35
|
)
|
|
36
|
-
from ..utils import fix_openrouter_usage_metadata
|
|
36
|
+
from ..utils import fix_openrouter_usage_metadata, is_debug_enabled
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
REDACTED_THINKING = "_REDACTED_THINKING"
|
|
@@ -42,14 +42,22 @@ REDACTED_THINKING = "_REDACTED_THINKING"
|
|
|
42
42
|
class AntMessagesClient(LLMClient):
|
|
43
43
|
"""Anthropic Messages-compatible client implementation."""
|
|
44
44
|
|
|
45
|
-
def __init__(
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
model: str,
|
|
48
|
+
api_key: str | None = None,
|
|
49
|
+
base_url: str | None = None,
|
|
50
|
+
default_headers: dict[str, str] | None = None,
|
|
51
|
+
):
|
|
46
52
|
"""Initialize Anthropic Messages-compatible client with model, API key, and base URL."""
|
|
47
53
|
self._model = model
|
|
48
54
|
api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
|
|
49
55
|
base_url = base_url or os.getenv("ANTHROPIC_BASE_URL")
|
|
50
56
|
# send the credential through both header conventions: Anthropic and DeepSeek read
|
|
51
57
|
# x-api-key while gateways such as OpenRouter and Z.AI read Authorization: Bearer
|
|
52
|
-
self._client = AsyncAnthropic(
|
|
58
|
+
self._client = AsyncAnthropic(
|
|
59
|
+
api_key=api_key, auth_token=api_key, base_url=base_url, default_headers=default_headers
|
|
60
|
+
)
|
|
53
61
|
self._history: list[UniMessage] = []
|
|
54
62
|
|
|
55
63
|
def _convert_image_url_to_source(self, url: str) -> dict[str, Any]:
|
|
@@ -306,9 +314,14 @@ class AntMessagesClient(LLMClient):
|
|
|
306
314
|
# gateways that relabel it onto another event
|
|
307
315
|
event_type = "unused"
|
|
308
316
|
|
|
309
|
-
|
|
317
|
+
elif is_debug_enabled():
|
|
310
318
|
raise ValueError(f"Unknown output: {model_output}")
|
|
311
319
|
|
|
320
|
+
else:
|
|
321
|
+
# a gateway injects its own events (heartbeats, cost tickers) into the stream, and
|
|
322
|
+
# killing a long generation over one costs more than dropping it
|
|
323
|
+
event_type = "unused"
|
|
324
|
+
|
|
312
325
|
return {
|
|
313
326
|
"role": "assistant",
|
|
314
327
|
"event_type": event_type,
|
|
@@ -412,3 +425,12 @@ class AntMessagesClient(LLMClient):
|
|
|
412
425
|
"finish_reason": event["finish_reason"],
|
|
413
426
|
}
|
|
414
427
|
partial_usage = {}
|
|
428
|
+
|
|
429
|
+
async def list_models(self) -> list[str]:
|
|
430
|
+
"""
|
|
431
|
+
List the model ids the configured endpoint serves.
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
list[str]: The model ids, in the order the endpoint returned them.
|
|
435
|
+
"""
|
|
436
|
+
return [model.id async for model in self._client.models.list()]
|
|
@@ -20,6 +20,10 @@ from .base_client import LLMClient
|
|
|
20
20
|
from .types import UniConfig, UniEvent, UniMessage
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
# The generic protocol clients are named explicitly rather than deduced from a model id.
|
|
24
|
+
_PROTOCOL_CLIENT_TYPES = ("openai-chat", "openai-responses", "ant-messages", "openai-embedding")
|
|
25
|
+
|
|
26
|
+
|
|
23
27
|
class AutoLLMClient(LLMClient):
|
|
24
28
|
"""
|
|
25
29
|
Auto-routing LLM client that dispatches to appropriate model-specific client.
|
|
@@ -29,7 +33,12 @@ class AutoLLMClient(LLMClient):
|
|
|
29
33
|
"""
|
|
30
34
|
|
|
31
35
|
def __init__(
|
|
32
|
-
self,
|
|
36
|
+
self,
|
|
37
|
+
model: str,
|
|
38
|
+
api_key: str | None = None,
|
|
39
|
+
base_url: str | None = None,
|
|
40
|
+
client_type: str | None = None,
|
|
41
|
+
default_headers: dict[str, str] | None = None,
|
|
33
42
|
):
|
|
34
43
|
"""
|
|
35
44
|
Initialize AutoLLMClient with a specific model.
|
|
@@ -39,14 +48,22 @@ class AutoLLMClient(LLMClient):
|
|
|
39
48
|
api_key: Optional API key
|
|
40
49
|
base_url: Optional base URL for API requests
|
|
41
50
|
client_type: Optional client type override
|
|
51
|
+
default_headers: Optional headers sent with every request, for endpoints that demand their own
|
|
42
52
|
"""
|
|
43
|
-
self.
|
|
53
|
+
self._client_type = (client_type or os.getenv("CLIENT_TYPE") or model).lower()
|
|
54
|
+
self._client = self._create_client_for_model(model, api_key, base_url, self._client_type, default_headers)
|
|
44
55
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
@staticmethod
|
|
57
|
+
def _client_class_for_model(client_type: str) -> type[LLMClient] | None:
|
|
58
|
+
"""
|
|
59
|
+
Resolve which client a resolved (lowercased) client type routes to.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
client_type: The resolved client type, which is the model id when none was given.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
type[LLMClient] | None: The client class, or None when no client claims the type.
|
|
66
|
+
"""
|
|
50
67
|
# every Gemini generation shares the unified client ("gemini-3" also matches the
|
|
51
68
|
# gemini-3.7/gemini-3.6/gemini-3.5-flash-lite client types)
|
|
52
69
|
if any(
|
|
@@ -54,51 +71,64 @@ class AutoLLMClient(LLMClient):
|
|
|
54
71
|
): # e.g., gemini-3.7-flash, gemini-3-flash-preview, gemini-embedding-2
|
|
55
72
|
from .gemini3_7 import Gemini3_7Client
|
|
56
73
|
|
|
57
|
-
return Gemini3_7Client
|
|
74
|
+
return Gemini3_7Client
|
|
58
75
|
elif "claude" in client_type and (
|
|
59
76
|
"4-6" in client_type or "4-7" in client_type or "4-8" in client_type or "-5" in client_type
|
|
60
77
|
): # the whole Claude 4.6+ series shares the unified client, e.g., claude-sonnet-4-6
|
|
61
78
|
from .claude5 import Claude5Client
|
|
62
79
|
|
|
63
|
-
return Claude5Client
|
|
80
|
+
return Claude5Client
|
|
64
81
|
elif "gpt-5.4" in client_type or "gpt-5.5" in client_type or "gpt-5.6" in client_type: # e.g., gpt-5.6
|
|
65
82
|
from .gpt5_6 import GPT5_6Client
|
|
66
83
|
|
|
67
|
-
return GPT5_6Client
|
|
84
|
+
return GPT5_6Client
|
|
68
85
|
elif "glm-5" in client_type: # the whole GLM series shares the unified client
|
|
69
86
|
from .glm5_3 import GLM5_3Client
|
|
70
87
|
|
|
71
|
-
return GLM5_3Client
|
|
88
|
+
return GLM5_3Client
|
|
72
89
|
elif "kimi-k3" in client_type or "kimi-k2.5" in client_type or "kimi-k2.6" in client_type:
|
|
73
90
|
# the whole Kimi K2.5+ series shares the unified client
|
|
74
91
|
from .kimi_k3 import KimiK3Client
|
|
75
92
|
|
|
76
|
-
return KimiK3Client
|
|
93
|
+
return KimiK3Client
|
|
77
94
|
elif client_type == "minimax-m3":
|
|
78
95
|
from .minimax_m3 import MiniMaxM3Client
|
|
79
96
|
|
|
80
|
-
return MiniMaxM3Client
|
|
97
|
+
return MiniMaxM3Client
|
|
81
98
|
elif "deepseek-v4" in client_type:
|
|
82
99
|
from .deepseek_v4 import DeepSeekV4Client
|
|
83
100
|
|
|
84
|
-
return DeepSeekV4Client
|
|
101
|
+
return DeepSeekV4Client
|
|
85
102
|
elif "ant-messages" in client_type:
|
|
86
103
|
from .ant_messages import AntMessagesClient
|
|
87
104
|
|
|
88
|
-
return AntMessagesClient
|
|
105
|
+
return AntMessagesClient
|
|
89
106
|
elif "openai-responses" in client_type:
|
|
90
107
|
from .openai_responses import OpenaiResponsesClient
|
|
91
108
|
|
|
92
|
-
return OpenaiResponsesClient
|
|
109
|
+
return OpenaiResponsesClient
|
|
93
110
|
elif "openai" in client_type and "embedding" in client_type:
|
|
94
111
|
from .openai_embedding import OpenaiEmbeddingClient
|
|
95
112
|
|
|
96
|
-
return OpenaiEmbeddingClient
|
|
113
|
+
return OpenaiEmbeddingClient
|
|
97
114
|
elif "openai" in client_type and "embedding" not in client_type: # openai-chat, plus bare "openai" as alias
|
|
98
115
|
from .openai_chat import OpenaiChatClient
|
|
99
116
|
|
|
100
|
-
return OpenaiChatClient
|
|
117
|
+
return OpenaiChatClient
|
|
101
118
|
else:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
def _create_client_for_model(
|
|
122
|
+
self,
|
|
123
|
+
model: str,
|
|
124
|
+
api_key: str | None = None,
|
|
125
|
+
base_url: str | None = None,
|
|
126
|
+
client_type: str | None = None,
|
|
127
|
+
default_headers: dict[str, str] | None = None,
|
|
128
|
+
) -> LLMClient:
|
|
129
|
+
"""Create the appropriate client for the given model."""
|
|
130
|
+
client_class = self._client_class_for_model(client_type or model.lower())
|
|
131
|
+
if client_class is None:
|
|
102
132
|
raise ValueError(
|
|
103
133
|
f"{client_type} is not supported. "
|
|
104
134
|
"Supported client types: minimax-m3, gemini-3.7, gemini-3.6, gemini-3, "
|
|
@@ -107,6 +137,8 @@ class AutoLLMClient(LLMClient):
|
|
|
107
137
|
"openai-embedding, ant-messages, openai-responses, openai-chat."
|
|
108
138
|
)
|
|
109
139
|
|
|
140
|
+
return client_class(model=model, api_key=api_key, base_url=base_url, default_headers=default_headers)
|
|
141
|
+
|
|
110
142
|
def transform_uni_config_to_model_config(self, config: UniConfig) -> Any:
|
|
111
143
|
"""Delegate to underlying client's transform_uni_config_to_model_config."""
|
|
112
144
|
return self._client.transform_uni_config_to_model_config(config)
|
|
@@ -165,3 +197,22 @@ class AutoLLMClient(LLMClient):
|
|
|
165
197
|
def set_history(self, history: list[UniMessage]) -> None:
|
|
166
198
|
"""Set history in the underlying client."""
|
|
167
199
|
self._client.set_history(history)
|
|
200
|
+
|
|
201
|
+
async def list_models(self) -> list[str]:
|
|
202
|
+
"""
|
|
203
|
+
List the model ids the endpoint serves that the routed client can be used for.
|
|
204
|
+
|
|
205
|
+
A protocol client is chosen explicitly and speaks for whatever the endpoint serves, so its
|
|
206
|
+
listing is returned whole. A client deduced from a model id serves only the ids that deduce
|
|
207
|
+
back to it, so a gateway fronting many vendors is filtered down to that client's own models.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
list[str]: The model ids, in the order the endpoint returned them.
|
|
211
|
+
"""
|
|
212
|
+
model_ids = await self._client.list_models()
|
|
213
|
+
protocol_classes = {self._client_class_for_model(name) for name in _PROTOCOL_CLIENT_TYPES}
|
|
214
|
+
if type(self._client) in protocol_classes:
|
|
215
|
+
return model_ids
|
|
216
|
+
|
|
217
|
+
client_class = type(self._client)
|
|
218
|
+
return [model_id for model_id in model_ids if self._client_class_for_model(model_id.lower()) is client_class]
|
|
@@ -28,6 +28,7 @@ from .types import (
|
|
|
28
28
|
UniMessage,
|
|
29
29
|
UsageMetadata,
|
|
30
30
|
)
|
|
31
|
+
from .utils import is_debug_enabled
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class LLMClient(ABC):
|
|
@@ -139,6 +140,17 @@ class LLMClient(ABC):
|
|
|
139
140
|
elif item["type"] == "partial_tool_call":
|
|
140
141
|
# Skip partial_tool_call items - they should already be converted to tool_call
|
|
141
142
|
pass
|
|
143
|
+
elif item["type"] == "inline_data" and (item.get("mime_type") or "").startswith("audio/"):
|
|
144
|
+
# a spoken response streams as many small audio chunks; the message keeps the
|
|
145
|
+
# whole utterance as one playable item
|
|
146
|
+
if (
|
|
147
|
+
content_items
|
|
148
|
+
and content_items[-1]["type"] == "inline_data"
|
|
149
|
+
and content_items[-1].get("mime_type") == item["mime_type"]
|
|
150
|
+
):
|
|
151
|
+
content_items[-1]["data"] += item["data"]
|
|
152
|
+
else:
|
|
153
|
+
content_items.append(item.copy())
|
|
142
154
|
else:
|
|
143
155
|
content_items.append(item.copy())
|
|
144
156
|
|
|
@@ -175,6 +187,15 @@ class LLMClient(ABC):
|
|
|
175
187
|
"""
|
|
176
188
|
pass
|
|
177
189
|
|
|
190
|
+
@abstractmethod
|
|
191
|
+
async def list_models(self) -> list[str]:
|
|
192
|
+
"""
|
|
193
|
+
List the model ids the configured endpoint serves.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
list[str]: The model ids, in the order the endpoint returned them.
|
|
197
|
+
"""
|
|
198
|
+
|
|
178
199
|
async def streaming_response(
|
|
179
200
|
self,
|
|
180
201
|
messages: list[UniMessage],
|
|
@@ -245,6 +266,14 @@ class LLMClient(ABC):
|
|
|
245
266
|
finally:
|
|
246
267
|
waiting_for_stream = False
|
|
247
268
|
|
|
269
|
+
if event["event_type"] == "unused":
|
|
270
|
+
# a client marks a wire event it has nothing to emit for as "unused"; that is
|
|
271
|
+
# its own bookkeeping and must not reach a caller
|
|
272
|
+
if is_debug_enabled():
|
|
273
|
+
raise ValueError(f"{self.__class__.__name__} yielded an internal unused event: {event}")
|
|
274
|
+
|
|
275
|
+
continue
|
|
276
|
+
|
|
248
277
|
event["created_at"] = int(time.time() * 1000)
|
|
249
278
|
last_event = event
|
|
250
279
|
events.append(event)
|
|
@@ -23,7 +23,7 @@ from anthropic import AsyncAnthropic, AsyncAnthropicBedrock
|
|
|
23
23
|
from anthropic.types.beta import BetaMessageParam, BetaRawMessageStreamEvent
|
|
24
24
|
|
|
25
25
|
from ..base_client import LLMClient
|
|
26
|
-
from ..errors import UnsupportedParameterError, parse_tool_call_arguments
|
|
26
|
+
from ..errors import UnsupportedOperationError, UnsupportedParameterError, parse_tool_call_arguments
|
|
27
27
|
from ..types import (
|
|
28
28
|
EventType,
|
|
29
29
|
FinishReason,
|
|
@@ -36,6 +36,7 @@ from ..types import (
|
|
|
36
36
|
UniMessage,
|
|
37
37
|
UsageMetadata,
|
|
38
38
|
)
|
|
39
|
+
from ..utils import is_debug_enabled
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
REDACTED_THINKING = "_REDACTED_THINKING"
|
|
@@ -44,7 +45,13 @@ REDACTED_THINKING = "_REDACTED_THINKING"
|
|
|
44
45
|
class Claude5Client(LLMClient):
|
|
45
46
|
"""Claude 5-specific LLM client implementation (also serves Claude 4.6 through 4.8)."""
|
|
46
47
|
|
|
47
|
-
def __init__(
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
model: str,
|
|
51
|
+
api_key: str | None = None,
|
|
52
|
+
base_url: str | None = None,
|
|
53
|
+
default_headers: dict[str, str] | None = None,
|
|
54
|
+
):
|
|
48
55
|
"""Initialize Claude 5 client with model and API key."""
|
|
49
56
|
self._model = model
|
|
50
57
|
api_key = api_key or os.getenv("ANTHROPIC_API_KEY")
|
|
@@ -53,11 +60,14 @@ class Claude5Client(LLMClient):
|
|
|
53
60
|
region = base_url.replace("bedrock://", "")
|
|
54
61
|
access_key, secret_key = api_key.split(",")
|
|
55
62
|
self._client = AsyncAnthropicBedrock(
|
|
56
|
-
aws_secret_key=secret_key,
|
|
63
|
+
aws_secret_key=secret_key,
|
|
64
|
+
aws_access_key=access_key,
|
|
65
|
+
aws_region=region,
|
|
66
|
+
default_headers=default_headers,
|
|
57
67
|
)
|
|
58
68
|
self._use_bedrock = True
|
|
59
69
|
else:
|
|
60
|
-
self._client = AsyncAnthropic(api_key=api_key, base_url=base_url)
|
|
70
|
+
self._client = AsyncAnthropic(api_key=api_key, base_url=base_url, default_headers=default_headers)
|
|
61
71
|
self._use_bedrock = False
|
|
62
72
|
|
|
63
73
|
self._history: list[UniMessage] = []
|
|
@@ -356,9 +366,14 @@ class Claude5Client(LLMClient):
|
|
|
356
366
|
# gateways that relabel it onto another event
|
|
357
367
|
event_type = "unused"
|
|
358
368
|
|
|
359
|
-
|
|
369
|
+
elif is_debug_enabled():
|
|
360
370
|
raise ValueError(f"Unknown output: {model_output}")
|
|
361
371
|
|
|
372
|
+
else:
|
|
373
|
+
# a gateway injects its own events (heartbeats, cost tickers) into the stream, and
|
|
374
|
+
# killing a long generation over one costs more than dropping it
|
|
375
|
+
event_type = "unused"
|
|
376
|
+
|
|
362
377
|
return {
|
|
363
378
|
"role": "assistant",
|
|
364
379
|
"event_type": event_type,
|
|
@@ -467,3 +482,17 @@ class Claude5Client(LLMClient):
|
|
|
467
482
|
"finish_reason": event["finish_reason"],
|
|
468
483
|
}
|
|
469
484
|
partial_usage = {}
|
|
485
|
+
|
|
486
|
+
async def list_models(self) -> list[str]:
|
|
487
|
+
"""
|
|
488
|
+
List the model ids the configured endpoint serves.
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
list[str]: The model ids, in the order the endpoint returned them.
|
|
492
|
+
"""
|
|
493
|
+
if self._use_bedrock:
|
|
494
|
+
raise UnsupportedOperationError(
|
|
495
|
+
self.__class__.__name__, "list_models", "Bedrock does not support listing models."
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
return [model.id async for model in self._client.models.list()]
|