agno 2.1.2__py3-none-any.whl → 2.3.13__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.
- agno/agent/agent.py +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/models/openai/responses.py
CHANGED
|
@@ -6,16 +6,18 @@ import httpx
|
|
|
6
6
|
from pydantic import BaseModel
|
|
7
7
|
from typing_extensions import Literal
|
|
8
8
|
|
|
9
|
-
from agno.exceptions import ModelProviderError
|
|
9
|
+
from agno.exceptions import ModelAuthenticationError, ModelProviderError
|
|
10
10
|
from agno.media import File
|
|
11
|
-
from agno.models.base import
|
|
11
|
+
from agno.models.base import Model
|
|
12
12
|
from agno.models.message import Citations, Message, UrlCitation
|
|
13
13
|
from agno.models.metrics import Metrics
|
|
14
14
|
from agno.models.response import ModelResponse
|
|
15
15
|
from agno.run.agent import RunOutput
|
|
16
|
+
from agno.utils.http import get_default_async_client, get_default_sync_client
|
|
16
17
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
17
18
|
from agno.utils.models.openai_responses import images_to_message
|
|
18
19
|
from agno.utils.models.schema_utils import get_response_schema_for_provider
|
|
20
|
+
from agno.utils.tokens import count_schema_tokens
|
|
19
21
|
|
|
20
22
|
try:
|
|
21
23
|
from openai import APIConnectionError, APIStatusError, AsyncOpenAI, OpenAI, RateLimitError
|
|
@@ -53,6 +55,7 @@ class OpenAIResponses(Model):
|
|
|
53
55
|
truncation: Optional[Literal["auto", "disabled"]] = None
|
|
54
56
|
user: Optional[str] = None
|
|
55
57
|
service_tier: Optional[Literal["auto", "default", "flex", "priority"]] = None
|
|
58
|
+
strict_output: bool = True # When True, guarantees schema adherence for structured outputs. When False, attempts to follow schema as a guide but may occasionally deviate
|
|
56
59
|
extra_headers: Optional[Any] = None
|
|
57
60
|
extra_query: Optional[Any] = None
|
|
58
61
|
extra_body: Optional[Any] = None
|
|
@@ -66,7 +69,7 @@ class OpenAIResponses(Model):
|
|
|
66
69
|
max_retries: Optional[int] = None
|
|
67
70
|
default_headers: Optional[Dict[str, str]] = None
|
|
68
71
|
default_query: Optional[Dict[str, str]] = None
|
|
69
|
-
http_client: Optional[httpx.Client] = None
|
|
72
|
+
http_client: Optional[Union[httpx.Client, httpx.AsyncClient]] = None
|
|
70
73
|
client_params: Optional[Dict[str, Any]] = None
|
|
71
74
|
|
|
72
75
|
# Parameters affecting built-in tools
|
|
@@ -115,7 +118,10 @@ class OpenAIResponses(Model):
|
|
|
115
118
|
if not self.api_key:
|
|
116
119
|
self.api_key = getenv("OPENAI_API_KEY")
|
|
117
120
|
if not self.api_key:
|
|
118
|
-
|
|
121
|
+
raise ModelAuthenticationError(
|
|
122
|
+
message="OPENAI_API_KEY not set. Please set the OPENAI_API_KEY environment variable.",
|
|
123
|
+
model_name=self.name,
|
|
124
|
+
)
|
|
119
125
|
|
|
120
126
|
# Define base client params
|
|
121
127
|
base_params = {
|
|
@@ -139,7 +145,7 @@ class OpenAIResponses(Model):
|
|
|
139
145
|
|
|
140
146
|
def get_client(self) -> OpenAI:
|
|
141
147
|
"""
|
|
142
|
-
Returns an OpenAI client.
|
|
148
|
+
Returns an OpenAI client. Caches the client to avoid recreating it on every request.
|
|
143
149
|
|
|
144
150
|
Returns:
|
|
145
151
|
OpenAI: An instance of the OpenAI client.
|
|
@@ -150,28 +156,29 @@ class OpenAIResponses(Model):
|
|
|
150
156
|
client_params: Dict[str, Any] = self._get_client_params()
|
|
151
157
|
if self.http_client is not None:
|
|
152
158
|
client_params["http_client"] = self.http_client
|
|
159
|
+
else:
|
|
160
|
+
# Use global sync client when no custom http_client is provided
|
|
161
|
+
client_params["http_client"] = get_default_sync_client()
|
|
153
162
|
|
|
154
163
|
self.client = OpenAI(**client_params)
|
|
155
164
|
return self.client
|
|
156
165
|
|
|
157
166
|
def get_async_client(self) -> AsyncOpenAI:
|
|
158
167
|
"""
|
|
159
|
-
Returns an asynchronous OpenAI client.
|
|
168
|
+
Returns an asynchronous OpenAI client. Caches the client to avoid recreating it on every request.
|
|
160
169
|
|
|
161
170
|
Returns:
|
|
162
171
|
AsyncOpenAI: An instance of the asynchronous OpenAI client.
|
|
163
172
|
"""
|
|
164
|
-
if self.async_client:
|
|
173
|
+
if self.async_client and not self.async_client.is_closed():
|
|
165
174
|
return self.async_client
|
|
166
175
|
|
|
167
176
|
client_params: Dict[str, Any] = self._get_client_params()
|
|
168
|
-
if self.http_client:
|
|
177
|
+
if self.http_client and isinstance(self.http_client, httpx.AsyncClient):
|
|
169
178
|
client_params["http_client"] = self.http_client
|
|
170
179
|
else:
|
|
171
|
-
#
|
|
172
|
-
client_params["http_client"] =
|
|
173
|
-
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=100)
|
|
174
|
-
)
|
|
180
|
+
# Use global async client when no custom http_client is provided
|
|
181
|
+
client_params["http_client"] = get_default_async_client()
|
|
175
182
|
|
|
176
183
|
self.async_client = AsyncOpenAI(**client_params)
|
|
177
184
|
return self.async_client
|
|
@@ -224,7 +231,7 @@ class OpenAIResponses(Model):
|
|
|
224
231
|
"type": "json_schema",
|
|
225
232
|
"name": response_format.__name__,
|
|
226
233
|
"schema": schema,
|
|
227
|
-
"strict":
|
|
234
|
+
"strict": self.strict_output,
|
|
228
235
|
}
|
|
229
236
|
else:
|
|
230
237
|
# JSON mode
|
|
@@ -301,6 +308,8 @@ class OpenAIResponses(Model):
|
|
|
301
308
|
|
|
302
309
|
def _upload_file(self, file: File) -> Optional[str]:
|
|
303
310
|
"""Upload a file to the OpenAI vector database."""
|
|
311
|
+
from pathlib import Path
|
|
312
|
+
from urllib.parse import urlparse
|
|
304
313
|
|
|
305
314
|
if file.url is not None:
|
|
306
315
|
file_content_tuple = file.file_url_content
|
|
@@ -308,13 +317,12 @@ class OpenAIResponses(Model):
|
|
|
308
317
|
file_content = file_content_tuple[0]
|
|
309
318
|
else:
|
|
310
319
|
return None
|
|
311
|
-
file_name = file.url.
|
|
320
|
+
file_name = Path(urlparse(file.url).path).name or "file"
|
|
312
321
|
file_tuple = (file_name, file_content)
|
|
313
322
|
result = self.get_client().files.create(file=file_tuple, purpose="assistants")
|
|
314
323
|
return result.id
|
|
315
324
|
elif file.filepath is not None:
|
|
316
325
|
import mimetypes
|
|
317
|
-
from pathlib import Path
|
|
318
326
|
|
|
319
327
|
file_path = file.filepath if isinstance(file.filepath, Path) else Path(file.filepath)
|
|
320
328
|
if file_path.exists() and file_path.is_file():
|
|
@@ -392,12 +400,15 @@ class OpenAIResponses(Model):
|
|
|
392
400
|
|
|
393
401
|
return formatted_tools
|
|
394
402
|
|
|
395
|
-
def _format_messages(
|
|
403
|
+
def _format_messages(
|
|
404
|
+
self, messages: List[Message], compress_tool_results: bool = False
|
|
405
|
+
) -> List[Union[Dict[str, Any], ResponseReasoningItem]]:
|
|
396
406
|
"""
|
|
397
407
|
Format a message into the format expected by OpenAI.
|
|
398
408
|
|
|
399
409
|
Args:
|
|
400
410
|
messages (List[Message]): The message to format.
|
|
411
|
+
compress_tool_results: Whether to compress tool results.
|
|
401
412
|
|
|
402
413
|
Returns:
|
|
403
414
|
Dict[str, Any]: The formatted message.
|
|
@@ -442,7 +453,7 @@ class OpenAIResponses(Model):
|
|
|
442
453
|
if message.role in ["user", "system"]:
|
|
443
454
|
message_dict: Dict[str, Any] = {
|
|
444
455
|
"role": self.role_map[message.role],
|
|
445
|
-
"content": message.
|
|
456
|
+
"content": message.get_content(use_compressed_content=compress_tool_results),
|
|
446
457
|
}
|
|
447
458
|
message_dict = {k: v for k, v in message_dict.items() if v is not None}
|
|
448
459
|
|
|
@@ -466,7 +477,9 @@ class OpenAIResponses(Model):
|
|
|
466
477
|
|
|
467
478
|
# Tool call result
|
|
468
479
|
elif message.role == "tool":
|
|
469
|
-
|
|
480
|
+
tool_result = message.get_content(use_compressed_content=compress_tool_results)
|
|
481
|
+
|
|
482
|
+
if message.tool_call_id and tool_result is not None:
|
|
470
483
|
function_call_id = message.tool_call_id
|
|
471
484
|
# Normalize: if a fc_* id was provided, translate to its corresponding call_* id
|
|
472
485
|
if isinstance(function_call_id, str) and function_call_id in fc_id_to_call_id:
|
|
@@ -474,7 +487,7 @@ class OpenAIResponses(Model):
|
|
|
474
487
|
else:
|
|
475
488
|
call_id_value = function_call_id
|
|
476
489
|
formatted_messages.append(
|
|
477
|
-
{"type": "function_call_output", "call_id": call_id_value, "output":
|
|
490
|
+
{"type": "function_call_output", "call_id": call_id_value, "output": tool_result}
|
|
478
491
|
)
|
|
479
492
|
# Tool Calls
|
|
480
493
|
elif message.tool_calls is not None and len(message.tool_calls) > 0:
|
|
@@ -508,6 +521,49 @@ class OpenAIResponses(Model):
|
|
|
508
521
|
formatted_messages.append(reasoning_output)
|
|
509
522
|
return formatted_messages
|
|
510
523
|
|
|
524
|
+
def count_tokens(
|
|
525
|
+
self,
|
|
526
|
+
messages: List[Message],
|
|
527
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
528
|
+
output_schema: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
529
|
+
) -> int:
|
|
530
|
+
try:
|
|
531
|
+
formatted_input = self._format_messages(messages, compress_tool_results=True)
|
|
532
|
+
formatted_tools = self._format_tool_params(messages, tools) if tools else None
|
|
533
|
+
|
|
534
|
+
response = self.get_client().responses.input_tokens.count(
|
|
535
|
+
model=self.id,
|
|
536
|
+
input=formatted_input, # type: ignore
|
|
537
|
+
instructions=self.instructions, # type: ignore
|
|
538
|
+
tools=formatted_tools, # type: ignore
|
|
539
|
+
)
|
|
540
|
+
return response.input_tokens + count_schema_tokens(output_schema, self.id)
|
|
541
|
+
except Exception as e:
|
|
542
|
+
log_warning(f"Failed to count tokens via API: {e}")
|
|
543
|
+
return super().count_tokens(messages, tools, output_schema)
|
|
544
|
+
|
|
545
|
+
async def acount_tokens(
|
|
546
|
+
self,
|
|
547
|
+
messages: List[Message],
|
|
548
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
549
|
+
output_schema: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
550
|
+
) -> int:
|
|
551
|
+
"""Async version of count_tokens using the async client."""
|
|
552
|
+
try:
|
|
553
|
+
formatted_input = self._format_messages(messages, compress_tool_results=True)
|
|
554
|
+
formatted_tools = self._format_tool_params(messages, tools) if tools else None
|
|
555
|
+
|
|
556
|
+
response = await self.get_async_client().responses.input_tokens.count(
|
|
557
|
+
model=self.id,
|
|
558
|
+
input=formatted_input, # type: ignore
|
|
559
|
+
instructions=self.instructions, # type: ignore
|
|
560
|
+
tools=formatted_tools, # type: ignore
|
|
561
|
+
)
|
|
562
|
+
return response.input_tokens + count_schema_tokens(output_schema, self.id)
|
|
563
|
+
except Exception as e:
|
|
564
|
+
log_warning(f"Failed to count tokens via API: {e}")
|
|
565
|
+
return await super().acount_tokens(messages, tools, output_schema)
|
|
566
|
+
|
|
511
567
|
def invoke(
|
|
512
568
|
self,
|
|
513
569
|
messages: List[Message],
|
|
@@ -516,6 +572,7 @@ class OpenAIResponses(Model):
|
|
|
516
572
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
517
573
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
518
574
|
run_response: Optional[RunOutput] = None,
|
|
575
|
+
compress_tool_results: bool = False,
|
|
519
576
|
) -> ModelResponse:
|
|
520
577
|
"""
|
|
521
578
|
Send a request to the OpenAI Responses API.
|
|
@@ -532,7 +589,7 @@ class OpenAIResponses(Model):
|
|
|
532
589
|
|
|
533
590
|
provider_response = self.get_client().responses.create(
|
|
534
591
|
model=self.id,
|
|
535
|
-
input=self._format_messages(messages), # type: ignore
|
|
592
|
+
input=self._format_messages(messages, compress_tool_results), # type: ignore
|
|
536
593
|
**request_params,
|
|
537
594
|
)
|
|
538
595
|
|
|
@@ -573,6 +630,9 @@ class OpenAIResponses(Model):
|
|
|
573
630
|
model_name=self.name,
|
|
574
631
|
model_id=self.id,
|
|
575
632
|
) from exc
|
|
633
|
+
except ModelAuthenticationError as exc:
|
|
634
|
+
log_error(f"Model authentication error from OpenAI API: {exc}")
|
|
635
|
+
raise exc
|
|
576
636
|
except Exception as exc:
|
|
577
637
|
log_error(f"Error from OpenAI API: {exc}")
|
|
578
638
|
raise ModelProviderError(message=str(exc), model_name=self.name, model_id=self.id) from exc
|
|
@@ -585,6 +645,7 @@ class OpenAIResponses(Model):
|
|
|
585
645
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
586
646
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
587
647
|
run_response: Optional[RunOutput] = None,
|
|
648
|
+
compress_tool_results: bool = False,
|
|
588
649
|
) -> ModelResponse:
|
|
589
650
|
"""
|
|
590
651
|
Sends an asynchronous request to the OpenAI Responses API.
|
|
@@ -601,7 +662,7 @@ class OpenAIResponses(Model):
|
|
|
601
662
|
|
|
602
663
|
provider_response = await self.get_async_client().responses.create(
|
|
603
664
|
model=self.id,
|
|
604
|
-
input=self._format_messages(messages), # type: ignore
|
|
665
|
+
input=self._format_messages(messages, compress_tool_results), # type: ignore
|
|
605
666
|
**request_params,
|
|
606
667
|
)
|
|
607
668
|
|
|
@@ -642,6 +703,9 @@ class OpenAIResponses(Model):
|
|
|
642
703
|
model_name=self.name,
|
|
643
704
|
model_id=self.id,
|
|
644
705
|
) from exc
|
|
706
|
+
except ModelAuthenticationError as exc:
|
|
707
|
+
log_error(f"Model authentication error from OpenAI API: {exc}")
|
|
708
|
+
raise exc
|
|
645
709
|
except Exception as exc:
|
|
646
710
|
log_error(f"Error from OpenAI API: {exc}")
|
|
647
711
|
raise ModelProviderError(message=str(exc), model_name=self.name, model_id=self.id) from exc
|
|
@@ -654,6 +718,7 @@ class OpenAIResponses(Model):
|
|
|
654
718
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
655
719
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
656
720
|
run_response: Optional[RunOutput] = None,
|
|
721
|
+
compress_tool_results: bool = False,
|
|
657
722
|
) -> Iterator[ModelResponse]:
|
|
658
723
|
"""
|
|
659
724
|
Send a streaming request to the OpenAI Responses API.
|
|
@@ -671,7 +736,7 @@ class OpenAIResponses(Model):
|
|
|
671
736
|
|
|
672
737
|
for chunk in self.get_client().responses.create(
|
|
673
738
|
model=self.id,
|
|
674
|
-
input=self._format_messages(messages), # type: ignore
|
|
739
|
+
input=self._format_messages(messages, compress_tool_results), # type: ignore
|
|
675
740
|
stream=True,
|
|
676
741
|
**request_params,
|
|
677
742
|
):
|
|
@@ -715,6 +780,9 @@ class OpenAIResponses(Model):
|
|
|
715
780
|
model_name=self.name,
|
|
716
781
|
model_id=self.id,
|
|
717
782
|
) from exc
|
|
783
|
+
except ModelAuthenticationError as exc:
|
|
784
|
+
log_error(f"Model authentication error from OpenAI API: {exc}")
|
|
785
|
+
raise exc
|
|
718
786
|
except Exception as exc:
|
|
719
787
|
log_error(f"Error from OpenAI API: {exc}")
|
|
720
788
|
raise ModelProviderError(message=str(exc), model_name=self.name, model_id=self.id) from exc
|
|
@@ -727,6 +795,7 @@ class OpenAIResponses(Model):
|
|
|
727
795
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
728
796
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
729
797
|
run_response: Optional[RunOutput] = None,
|
|
798
|
+
compress_tool_results: bool = False,
|
|
730
799
|
) -> AsyncIterator[ModelResponse]:
|
|
731
800
|
"""
|
|
732
801
|
Sends an asynchronous streaming request to the OpenAI Responses API.
|
|
@@ -744,7 +813,7 @@ class OpenAIResponses(Model):
|
|
|
744
813
|
|
|
745
814
|
async_stream = await self.get_async_client().responses.create(
|
|
746
815
|
model=self.id,
|
|
747
|
-
input=self._format_messages(messages), # type: ignore
|
|
816
|
+
input=self._format_messages(messages, compress_tool_results), # type: ignore
|
|
748
817
|
stream=True,
|
|
749
818
|
**request_params,
|
|
750
819
|
)
|
|
@@ -785,12 +854,19 @@ class OpenAIResponses(Model):
|
|
|
785
854
|
model_name=self.name,
|
|
786
855
|
model_id=self.id,
|
|
787
856
|
) from exc
|
|
857
|
+
except ModelAuthenticationError as exc:
|
|
858
|
+
log_error(f"Model authentication error from OpenAI API: {exc}")
|
|
859
|
+
raise exc
|
|
788
860
|
except Exception as exc:
|
|
789
861
|
log_error(f"Error from OpenAI API: {exc}")
|
|
790
862
|
raise ModelProviderError(message=str(exc), model_name=self.name, model_id=self.id) from exc
|
|
791
863
|
|
|
792
864
|
def format_function_call_results(
|
|
793
|
-
self,
|
|
865
|
+
self,
|
|
866
|
+
messages: List[Message],
|
|
867
|
+
function_call_results: List[Message],
|
|
868
|
+
tool_call_ids: List[str],
|
|
869
|
+
compress_tool_results: bool = False,
|
|
794
870
|
) -> None:
|
|
795
871
|
"""
|
|
796
872
|
Handle the results of function calls.
|
|
@@ -799,69 +875,13 @@ class OpenAIResponses(Model):
|
|
|
799
875
|
messages (List[Message]): The list of conversation messages.
|
|
800
876
|
function_call_results (List[Message]): The results of the function calls.
|
|
801
877
|
tool_ids (List[str]): The tool ids.
|
|
878
|
+
compress_tool_results (bool): Whether to compress tool results.
|
|
802
879
|
"""
|
|
803
880
|
if len(function_call_results) > 0:
|
|
804
881
|
for _fc_message_index, _fc_message in enumerate(function_call_results):
|
|
805
882
|
_fc_message.tool_call_id = tool_call_ids[_fc_message_index]
|
|
806
883
|
messages.append(_fc_message)
|
|
807
884
|
|
|
808
|
-
def process_response_stream(
|
|
809
|
-
self,
|
|
810
|
-
messages: List[Message],
|
|
811
|
-
assistant_message: Message,
|
|
812
|
-
stream_data: MessageData,
|
|
813
|
-
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
814
|
-
tools: Optional[List[Dict[str, Any]]] = None,
|
|
815
|
-
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
816
|
-
run_response: Optional[RunOutput] = None,
|
|
817
|
-
) -> Iterator[ModelResponse]:
|
|
818
|
-
"""Process the synchronous response stream."""
|
|
819
|
-
for model_response_delta in self.invoke_stream(
|
|
820
|
-
messages=messages,
|
|
821
|
-
assistant_message=assistant_message,
|
|
822
|
-
tools=tools,
|
|
823
|
-
response_format=response_format,
|
|
824
|
-
tool_choice=tool_choice,
|
|
825
|
-
run_response=run_response,
|
|
826
|
-
):
|
|
827
|
-
yield from self._populate_stream_data_and_assistant_message(
|
|
828
|
-
stream_data=stream_data,
|
|
829
|
-
assistant_message=assistant_message,
|
|
830
|
-
model_response_delta=model_response_delta,
|
|
831
|
-
)
|
|
832
|
-
|
|
833
|
-
# Add final metrics to assistant message
|
|
834
|
-
self._populate_assistant_message(assistant_message=assistant_message, provider_response=model_response_delta)
|
|
835
|
-
|
|
836
|
-
async def aprocess_response_stream(
|
|
837
|
-
self,
|
|
838
|
-
messages: List[Message],
|
|
839
|
-
assistant_message: Message,
|
|
840
|
-
stream_data: MessageData,
|
|
841
|
-
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
842
|
-
tools: Optional[List[Dict[str, Any]]] = None,
|
|
843
|
-
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
844
|
-
run_response: Optional[RunOutput] = None,
|
|
845
|
-
) -> AsyncIterator[ModelResponse]:
|
|
846
|
-
"""Process the asynchronous response stream."""
|
|
847
|
-
async for model_response_delta in self.ainvoke_stream(
|
|
848
|
-
messages=messages,
|
|
849
|
-
assistant_message=assistant_message,
|
|
850
|
-
tools=tools,
|
|
851
|
-
response_format=response_format,
|
|
852
|
-
tool_choice=tool_choice,
|
|
853
|
-
run_response=run_response,
|
|
854
|
-
):
|
|
855
|
-
for model_response in self._populate_stream_data_and_assistant_message(
|
|
856
|
-
stream_data=stream_data,
|
|
857
|
-
assistant_message=assistant_message,
|
|
858
|
-
model_response_delta=model_response_delta,
|
|
859
|
-
):
|
|
860
|
-
yield model_response
|
|
861
|
-
|
|
862
|
-
# Add final metrics to assistant message
|
|
863
|
-
self._populate_assistant_message(assistant_message=assistant_message, provider_response=model_response_delta)
|
|
864
|
-
|
|
865
885
|
def _parse_provider_response(self, response: Response, **kwargs) -> ModelResponse:
|
|
866
886
|
"""
|
|
867
887
|
Parse the OpenAI response into a ModelResponse.
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass
|
|
2
2
|
from os import getenv
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Any, Dict, List, Optional, Type, Union
|
|
4
4
|
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from agno.exceptions import ModelAuthenticationError
|
|
5
8
|
from agno.models.openai.like import OpenAILike
|
|
9
|
+
from agno.run.agent import RunOutput
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
@dataclass
|
|
@@ -17,12 +21,65 @@ class OpenRouter(OpenAILike):
|
|
|
17
21
|
api_key (Optional[str]): The API key.
|
|
18
22
|
base_url (str): The base URL. Defaults to "https://openrouter.ai/api/v1".
|
|
19
23
|
max_tokens (int): The maximum number of tokens. Defaults to 1024.
|
|
24
|
+
fallback_models (Optional[List[str]]): List of fallback model IDs to use if the primary model
|
|
25
|
+
fails due to rate limits, timeouts, or unavailability. OpenRouter will automatically try
|
|
26
|
+
these models in order. Example: ["anthropic/claude-sonnet-4", "deepseek/deepseek-r1"]
|
|
20
27
|
"""
|
|
21
28
|
|
|
22
29
|
id: str = "gpt-4o"
|
|
23
30
|
name: str = "OpenRouter"
|
|
24
31
|
provider: str = "OpenRouter"
|
|
25
32
|
|
|
26
|
-
api_key: Optional[str] =
|
|
33
|
+
api_key: Optional[str] = None
|
|
27
34
|
base_url: str = "https://openrouter.ai/api/v1"
|
|
28
35
|
max_tokens: int = 1024
|
|
36
|
+
models: Optional[List[str]] = None # Dynamic model routing https://openrouter.ai/docs/features/model-routing
|
|
37
|
+
|
|
38
|
+
def _get_client_params(self) -> Dict[str, Any]:
|
|
39
|
+
"""
|
|
40
|
+
Returns client parameters for API requests, checking for OPENROUTER_API_KEY.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dict[str, Any]: A dictionary of client parameters for API requests.
|
|
44
|
+
"""
|
|
45
|
+
# Fetch API key from env if not already set
|
|
46
|
+
if not self.api_key:
|
|
47
|
+
self.api_key = getenv("OPENROUTER_API_KEY")
|
|
48
|
+
if not self.api_key:
|
|
49
|
+
raise ModelAuthenticationError(
|
|
50
|
+
message="OPENROUTER_API_KEY not set. Please set the OPENROUTER_API_KEY environment variable.",
|
|
51
|
+
model_name=self.name,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return super()._get_client_params()
|
|
55
|
+
|
|
56
|
+
def get_request_params(
|
|
57
|
+
self,
|
|
58
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
59
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
60
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
61
|
+
run_response: Optional[RunOutput] = None,
|
|
62
|
+
) -> Dict[str, Any]:
|
|
63
|
+
"""
|
|
64
|
+
Returns keyword arguments for API requests, including fallback models configuration.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Dict[str, Any]: A dictionary of keyword arguments for API requests.
|
|
68
|
+
"""
|
|
69
|
+
# Get base request params from parent class
|
|
70
|
+
request_params = super().get_request_params(
|
|
71
|
+
response_format=response_format, tools=tools, tool_choice=tool_choice, run_response=run_response
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Add fallback models to extra_body if specified
|
|
75
|
+
if self.models:
|
|
76
|
+
# Get existing extra_body or create new dict
|
|
77
|
+
extra_body = request_params.get("extra_body") or {}
|
|
78
|
+
|
|
79
|
+
# Merge fallback models into extra_body
|
|
80
|
+
extra_body["models"] = self.models
|
|
81
|
+
|
|
82
|
+
# Update request params
|
|
83
|
+
request_params["extra_body"] = extra_body
|
|
84
|
+
|
|
85
|
+
return request_params
|
|
@@ -4,7 +4,7 @@ from typing import Any, Dict, Optional, Type, Union
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
|
-
from agno.exceptions import ModelProviderError
|
|
7
|
+
from agno.exceptions import ModelAuthenticationError, ModelProviderError
|
|
8
8
|
from agno.models.message import Citations, UrlCitation
|
|
9
9
|
from agno.models.metrics import Metrics
|
|
10
10
|
from agno.models.response import ModelResponse
|
|
@@ -50,6 +50,22 @@ class Perplexity(OpenAILike):
|
|
|
50
50
|
supports_native_structured_outputs: bool = False
|
|
51
51
|
supports_json_schema_outputs: bool = True
|
|
52
52
|
|
|
53
|
+
def _get_client_params(self) -> Dict[str, Any]:
|
|
54
|
+
"""
|
|
55
|
+
Returns client parameters for API requests, checking for PERPLEXITY_API_KEY.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dict[str, Any]: A dictionary of client parameters for API requests.
|
|
59
|
+
"""
|
|
60
|
+
if not self.api_key:
|
|
61
|
+
self.api_key = getenv("PERPLEXITY_API_KEY")
|
|
62
|
+
if not self.api_key:
|
|
63
|
+
raise ModelAuthenticationError(
|
|
64
|
+
message="PERPLEXITY_API_KEY not set. Please set the PERPLEXITY_API_KEY environment variable.",
|
|
65
|
+
model_name=self.name,
|
|
66
|
+
)
|
|
67
|
+
return super()._get_client_params()
|
|
68
|
+
|
|
53
69
|
def get_request_params(
|
|
54
70
|
self,
|
|
55
71
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
agno/models/portkey/portkey.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Any, Dict, Optional, cast
|
|
4
4
|
|
|
5
|
-
from agno.exceptions import
|
|
5
|
+
from agno.exceptions import ModelAuthenticationError
|
|
6
6
|
from agno.models.openai.like import OpenAILike
|
|
7
7
|
|
|
8
8
|
try:
|
|
@@ -30,20 +30,21 @@ class Portkey(OpenAILike):
|
|
|
30
30
|
name: str = "Portkey"
|
|
31
31
|
provider: str = "Portkey"
|
|
32
32
|
|
|
33
|
-
portkey_api_key: Optional[str] =
|
|
34
|
-
virtual_key: Optional[str] =
|
|
33
|
+
portkey_api_key: Optional[str] = None
|
|
34
|
+
virtual_key: Optional[str] = None
|
|
35
35
|
config: Optional[Dict[str, Any]] = None
|
|
36
36
|
base_url: str = PORTKEY_GATEWAY_URL
|
|
37
37
|
|
|
38
38
|
def _get_client_params(self) -> Dict[str, Any]:
|
|
39
39
|
# Check for required keys
|
|
40
40
|
if not self.portkey_api_key:
|
|
41
|
-
raise
|
|
41
|
+
raise ModelAuthenticationError(
|
|
42
42
|
message="PORTKEY_API_KEY not set. Please set the PORTKEY_API_KEY environment variable.",
|
|
43
43
|
model_name=self.name,
|
|
44
|
-
model_id=self.id,
|
|
45
44
|
)
|
|
46
45
|
|
|
46
|
+
self.virtual_key = self.virtual_key or getenv("PORTKEY_VIRTUAL_KEY")
|
|
47
|
+
|
|
47
48
|
# Create headers using Portkey's createHeaders function
|
|
48
49
|
header_params: Dict[str, Any] = {
|
|
49
50
|
"api_key": self.portkey_api_key,
|
agno/models/requesty/requesty.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Any, Dict, List, Optional, Type, Union
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
|
+
from agno.exceptions import ModelAuthenticationError
|
|
7
8
|
from agno.models.openai.like import OpenAILike
|
|
8
9
|
from agno.run.agent import RunOutput
|
|
10
|
+
from agno.run.team import TeamRunOutput
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
@dataclass
|
|
@@ -25,18 +27,36 @@ class Requesty(OpenAILike):
|
|
|
25
27
|
name: str = "Requesty"
|
|
26
28
|
provider: str = "Requesty"
|
|
27
29
|
|
|
28
|
-
api_key: Optional[str] =
|
|
30
|
+
api_key: Optional[str] = None
|
|
29
31
|
base_url: str = "https://router.requesty.ai/v1"
|
|
30
32
|
max_tokens: int = 1024
|
|
31
33
|
|
|
34
|
+
def _get_client_params(self) -> Dict[str, Any]:
|
|
35
|
+
"""
|
|
36
|
+
Returns client parameters for API requests, checking for REQUESTY_API_KEY.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Dict[str, Any]: A dictionary of client parameters for API requests.
|
|
40
|
+
"""
|
|
41
|
+
if not self.api_key:
|
|
42
|
+
self.api_key = getenv("REQUESTY_API_KEY")
|
|
43
|
+
if not self.api_key:
|
|
44
|
+
raise ModelAuthenticationError(
|
|
45
|
+
message="REQUESTY_API_KEY not set. Please set the REQUESTY_API_KEY environment variable.",
|
|
46
|
+
model_name=self.name,
|
|
47
|
+
)
|
|
48
|
+
return super()._get_client_params()
|
|
49
|
+
|
|
32
50
|
def get_request_params(
|
|
33
51
|
self,
|
|
34
52
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
35
53
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
36
54
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
37
|
-
run_response: Optional[RunOutput] = None,
|
|
55
|
+
run_response: Optional[Union[RunOutput, TeamRunOutput]] = None,
|
|
38
56
|
) -> Dict[str, Any]:
|
|
39
|
-
params = super().get_request_params(
|
|
57
|
+
params = super().get_request_params(
|
|
58
|
+
response_format=response_format, tools=tools, tool_choice=tool_choice, run_response=run_response
|
|
59
|
+
)
|
|
40
60
|
|
|
41
61
|
if "extra_body" not in params:
|
|
42
62
|
params["extra_body"] = {}
|