agno 2.0.1__py3-none-any.whl → 2.3.0__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 +6015 -2823
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +385 -6
- agno/db/dynamo/dynamo.py +388 -81
- agno/db/dynamo/schemas.py +47 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +435 -64
- agno/db/firestore/schemas.py +11 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +384 -42
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +351 -66
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +339 -48
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +510 -37
- 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 +2036 -0
- agno/db/mongo/mongo.py +653 -76
- agno/db/mongo/schemas.py +13 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/mysql.py +687 -25
- agno/db/mysql/schemas.py +61 -37
- agno/db/mysql/utils.py +60 -2
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2001 -0
- agno/db/postgres/postgres.py +676 -57
- agno/db/postgres/schemas.py +43 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +344 -38
- agno/db/redis/schemas.py +18 -0
- agno/db/redis/utils.py +60 -2
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/memory.py +13 -0
- agno/db/singlestore/schemas.py +26 -1
- agno/db/singlestore/singlestore.py +687 -53
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2371 -0
- agno/db/sqlite/schemas.py +24 -0
- agno/db/sqlite/sqlite.py +774 -85
- agno/db/sqlite/utils.py +168 -5
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1361 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +50 -22
- agno/eval/accuracy.py +50 -43
- agno/eval/performance.py +6 -3
- agno/eval/reliability.py +6 -3
- agno/eval/utils.py +33 -16
- agno/exceptions.py +68 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/integrations/discord/client.py +1 -0
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +1 -1
- agno/knowledge/chunking/semantic.py +40 -8
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/aws_bedrock.py +9 -4
- agno/knowledge/embedder/azure_openai.py +54 -0
- agno/knowledge/embedder/base.py +2 -0
- agno/knowledge/embedder/cohere.py +184 -5
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/google.py +79 -1
- agno/knowledge/embedder/huggingface.py +9 -4
- agno/knowledge/embedder/jina.py +63 -0
- agno/knowledge/embedder/mistral.py +78 -11
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +13 -0
- agno/knowledge/embedder/openai.py +37 -65
- agno/knowledge/embedder/sentence_transformer.py +8 -4
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +69 -16
- agno/knowledge/knowledge.py +594 -186
- agno/knowledge/reader/base.py +9 -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 +290 -0
- agno/knowledge/reader/json_reader.py +6 -5
- agno/knowledge/reader/markdown_reader.py +13 -13
- agno/knowledge/reader/pdf_reader.py +43 -68
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +51 -6
- agno/knowledge/reader/s3_reader.py +3 -15
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/reader/text_reader.py +13 -13
- agno/knowledge/reader/web_search_reader.py +2 -43
- agno/knowledge/reader/website_reader.py +43 -25
- agno/knowledge/reranker/__init__.py +2 -8
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +72 -0
- agno/memory/manager.py +336 -82
- agno/models/aimlapi/aimlapi.py +2 -2
- agno/models/anthropic/claude.py +183 -37
- agno/models/aws/bedrock.py +52 -112
- agno/models/aws/claude.py +33 -1
- agno/models/azure/ai_foundry.py +33 -15
- agno/models/azure/openai_chat.py +25 -8
- agno/models/base.py +999 -519
- agno/models/cerebras/cerebras.py +19 -13
- agno/models/cerebras/cerebras_openai.py +8 -5
- agno/models/cohere/chat.py +27 -1
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/dashscope.py +1 -0
- agno/models/deepinfra/deepinfra.py +2 -2
- agno/models/deepseek/deepseek.py +2 -2
- agno/models/fireworks/fireworks.py +2 -2
- agno/models/google/gemini.py +103 -31
- agno/models/groq/groq.py +28 -11
- agno/models/huggingface/huggingface.py +2 -1
- agno/models/internlm/internlm.py +2 -2
- agno/models/langdb/langdb.py +4 -4
- agno/models/litellm/chat.py +18 -1
- agno/models/litellm/litellm_openai.py +2 -2
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/message.py +139 -0
- agno/models/meta/llama.py +27 -10
- agno/models/meta/llama_openai.py +5 -17
- agno/models/nebius/nebius.py +6 -6
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/nvidia.py +2 -2
- agno/models/ollama/chat.py +59 -5
- agno/models/openai/chat.py +69 -29
- agno/models/openai/responses.py +103 -106
- agno/models/openrouter/openrouter.py +41 -3
- agno/models/perplexity/perplexity.py +4 -5
- agno/models/portkey/portkey.py +3 -3
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +77 -1
- agno/models/sambanova/sambanova.py +2 -2
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/together.py +2 -2
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +2 -2
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +96 -0
- agno/models/vllm/vllm.py +1 -0
- agno/models/xai/xai.py +3 -2
- agno/os/app.py +543 -178
- agno/os/auth.py +24 -14
- agno/os/config.py +1 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/agui.py +23 -7
- agno/os/interfaces/agui/router.py +27 -3
- agno/os/interfaces/agui/utils.py +242 -142
- agno/os/interfaces/base.py +6 -2
- agno/os/interfaces/slack/router.py +81 -23
- agno/os/interfaces/slack/slack.py +29 -14
- agno/os/interfaces/whatsapp/router.py +11 -4
- agno/os/interfaces/whatsapp/whatsapp.py +14 -7
- agno/os/mcp.py +111 -54
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +556 -139
- agno/os/routers/evals/evals.py +71 -34
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/evals/utils.py +6 -5
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/knowledge.py +185 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +158 -53
- agno/os/routers/memory/schemas.py +20 -16
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +499 -38
- agno/os/schema.py +308 -198
- agno/os/utils.py +401 -41
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +3 -1
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +2 -2
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +7 -2
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +248 -94
- agno/run/base.py +44 -5
- agno/run/team.py +238 -97
- agno/run/workflow.py +144 -33
- agno/session/agent.py +105 -89
- agno/session/summary.py +65 -25
- agno/session/team.py +176 -96
- agno/session/workflow.py +406 -40
- agno/team/team.py +3854 -1610
- agno/tools/dalle.py +2 -4
- agno/tools/decorator.py +4 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +14 -7
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +250 -30
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +270 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/knowledge.py +3 -3
- 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 +284 -0
- agno/tools/mem0.py +11 -17
- agno/tools/memori.py +1 -53
- agno/tools/memory.py +419 -0
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/scrapegraph.py +58 -31
- agno/tools/searxng.py +2 -2
- agno/tools/serper.py +2 -2
- agno/tools/slack.py +18 -3
- agno/tools/spider.py +2 -2
- agno/tools/tavily.py +146 -0
- agno/tools/whatsapp.py +1 -1
- agno/tools/workflow.py +278 -0
- agno/tools/yfinance.py +12 -11
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +27 -0
- agno/utils/common.py +90 -1
- agno/utils/events.py +217 -2
- agno/utils/gemini.py +180 -22
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +111 -0
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +188 -10
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +60 -0
- agno/utils/models/claude.py +40 -11
- agno/utils/print_response/agent.py +105 -21
- agno/utils/print_response/team.py +103 -38
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/reasoning.py +22 -1
- agno/utils/serialize.py +32 -0
- agno/utils/streamlit.py +16 -10
- agno/utils/string.py +41 -0
- agno/utils/team.py +98 -9
- agno/utils/tools.py +1 -1
- agno/vectordb/base.py +23 -4
- agno/vectordb/cassandra/cassandra.py +65 -9
- agno/vectordb/chroma/chromadb.py +182 -38
- agno/vectordb/clickhouse/clickhousedb.py +64 -11
- agno/vectordb/couchbase/couchbase.py +105 -10
- agno/vectordb/lancedb/lance_db.py +124 -133
- agno/vectordb/langchaindb/langchaindb.py +25 -7
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +46 -7
- agno/vectordb/milvus/milvus.py +126 -9
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +112 -7
- agno/vectordb/pgvector/pgvector.py +142 -21
- agno/vectordb/pineconedb/pineconedb.py +80 -8
- agno/vectordb/qdrant/qdrant.py +125 -39
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/singlestore/singlestore.py +111 -25
- agno/vectordb/surrealdb/surrealdb.py +31 -5
- agno/vectordb/upstashdb/upstashdb.py +76 -8
- agno/vectordb/weaviate/weaviate.py +86 -15
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +112 -18
- agno/workflow/loop.py +69 -10
- agno/workflow/parallel.py +266 -118
- agno/workflow/router.py +110 -17
- agno/workflow/step.py +638 -129
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +61 -23
- agno/workflow/workflow.py +2085 -272
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/METADATA +182 -58
- agno-2.3.0.dist-info/RECORD +577 -0
- agno/knowledge/reader/url_reader.py +0 -128
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -610
- agno/utils/models/aws_claude.py +0 -170
- agno-2.0.1.dist-info/RECORD +0 -515
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/models/groq/groq.py
CHANGED
|
@@ -12,6 +12,7 @@ from agno.models.message import Message
|
|
|
12
12
|
from agno.models.metrics import Metrics
|
|
13
13
|
from agno.models.response import ModelResponse
|
|
14
14
|
from agno.run.agent import RunOutput
|
|
15
|
+
from agno.utils.http import get_default_async_client, get_default_sync_client
|
|
15
16
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
16
17
|
from agno.utils.openai import images_to_message
|
|
17
18
|
|
|
@@ -61,7 +62,7 @@ class Groq(Model):
|
|
|
61
62
|
max_retries: Optional[int] = None
|
|
62
63
|
default_headers: Optional[Any] = None
|
|
63
64
|
default_query: Optional[Any] = None
|
|
64
|
-
http_client: Optional[httpx.Client] = None
|
|
65
|
+
http_client: Optional[Union[httpx.Client, httpx.AsyncClient]] = None
|
|
65
66
|
client_params: Optional[Dict[str, Any]] = None
|
|
66
67
|
|
|
67
68
|
# Groq clients
|
|
@@ -93,7 +94,7 @@ class Groq(Model):
|
|
|
93
94
|
|
|
94
95
|
def get_client(self) -> GroqClient:
|
|
95
96
|
"""
|
|
96
|
-
Returns a Groq client.
|
|
97
|
+
Returns a Groq client. Caches the client to avoid recreating it on every request.
|
|
97
98
|
|
|
98
99
|
Returns:
|
|
99
100
|
GroqClient: An instance of the Groq client.
|
|
@@ -103,30 +104,46 @@ class Groq(Model):
|
|
|
103
104
|
|
|
104
105
|
client_params: Dict[str, Any] = self._get_client_params()
|
|
105
106
|
if self.http_client is not None:
|
|
106
|
-
|
|
107
|
+
if isinstance(self.http_client, httpx.Client):
|
|
108
|
+
client_params["http_client"] = self.http_client
|
|
109
|
+
else:
|
|
110
|
+
log_warning("http_client is not an instance of httpx.Client. Using default global httpx.Client.")
|
|
111
|
+
# Use global sync client when user http_client is invalid
|
|
112
|
+
client_params["http_client"] = get_default_sync_client()
|
|
113
|
+
else:
|
|
114
|
+
# Use global sync client when no custom http_client is provided
|
|
115
|
+
client_params["http_client"] = get_default_sync_client()
|
|
107
116
|
|
|
108
117
|
self.client = GroqClient(**client_params)
|
|
109
118
|
return self.client
|
|
110
119
|
|
|
111
120
|
def get_async_client(self) -> AsyncGroqClient:
|
|
112
121
|
"""
|
|
113
|
-
Returns an asynchronous Groq client.
|
|
122
|
+
Returns an asynchronous Groq client. Caches the client to avoid recreating it on every request.
|
|
114
123
|
|
|
115
124
|
Returns:
|
|
116
125
|
AsyncGroqClient: An instance of the asynchronous Groq client.
|
|
117
126
|
"""
|
|
118
|
-
if self.async_client:
|
|
127
|
+
if self.async_client and not self.async_client.is_closed():
|
|
119
128
|
return self.async_client
|
|
120
129
|
|
|
121
130
|
client_params: Dict[str, Any] = self._get_client_params()
|
|
122
131
|
if self.http_client:
|
|
123
|
-
|
|
132
|
+
if isinstance(self.http_client, httpx.AsyncClient):
|
|
133
|
+
client_params["http_client"] = self.http_client
|
|
134
|
+
else:
|
|
135
|
+
log_warning(
|
|
136
|
+
"http_client is not an instance of httpx.AsyncClient. Using default global httpx.AsyncClient."
|
|
137
|
+
)
|
|
138
|
+
# Use global async client when user http_client is invalid
|
|
139
|
+
client_params["http_client"] = get_default_async_client()
|
|
124
140
|
else:
|
|
125
|
-
#
|
|
126
|
-
client_params["http_client"] =
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
141
|
+
# Use global async client when no custom http_client is provided
|
|
142
|
+
client_params["http_client"] = get_default_async_client()
|
|
143
|
+
|
|
144
|
+
# Create and cache the client
|
|
145
|
+
self.async_client = AsyncGroqClient(**client_params)
|
|
146
|
+
return self.async_client
|
|
130
147
|
|
|
131
148
|
def get_request_params(
|
|
132
149
|
self,
|
|
@@ -382,7 +382,8 @@ class HuggingFace(Model):
|
|
|
382
382
|
List[Dict[str, Any]]: The built tool calls.
|
|
383
383
|
"""
|
|
384
384
|
tool_calls: List[Dict[str, Any]] = []
|
|
385
|
-
for
|
|
385
|
+
for tool_call in tool_calls_data:
|
|
386
|
+
_tool_call = tool_call[0]
|
|
386
387
|
_index = _tool_call.index
|
|
387
388
|
_tool_call_id = _tool_call.id
|
|
388
389
|
_tool_call_type = _tool_call.type
|
agno/models/internlm/internlm.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
@@ -22,5 +22,5 @@ class InternLM(OpenAILike):
|
|
|
22
22
|
name: str = "InternLM"
|
|
23
23
|
provider: str = "InternLM"
|
|
24
24
|
|
|
25
|
-
api_key: Optional[str] = getenv("INTERNLM_API_KEY")
|
|
25
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("INTERNLM_API_KEY"))
|
|
26
26
|
base_url: Optional[str] = "https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions"
|
agno/models/langdb/langdb.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Any, Dict, Optional
|
|
4
4
|
|
|
@@ -22,10 +22,10 @@ class LangDB(OpenAILike):
|
|
|
22
22
|
name: str = "LangDB"
|
|
23
23
|
provider: str = "LangDB"
|
|
24
24
|
|
|
25
|
-
api_key: Optional[str] = getenv("LANGDB_API_KEY")
|
|
26
|
-
project_id: Optional[str] = getenv("LANGDB_PROJECT_ID")
|
|
25
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("LANGDB_API_KEY"))
|
|
26
|
+
project_id: Optional[str] = field(default_factory=lambda: getenv("LANGDB_PROJECT_ID"))
|
|
27
27
|
|
|
28
|
-
base_host_url: str = getenv("LANGDB_API_BASE_URL", "https://api.us-east-1.langdb.ai")
|
|
28
|
+
base_host_url: str = field(default_factory=lambda: getenv("LANGDB_API_BASE_URL", "https://api.us-east-1.langdb.ai"))
|
|
29
29
|
|
|
30
30
|
base_url: Optional[str] = None
|
|
31
31
|
label: Optional[str] = None
|
agno/models/litellm/chat.py
CHANGED
|
@@ -38,6 +38,10 @@ class LiteLLM(Model):
|
|
|
38
38
|
max_tokens: Optional[int] = None
|
|
39
39
|
temperature: float = 0.7
|
|
40
40
|
top_p: float = 1.0
|
|
41
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
42
|
+
extra_headers: Optional[Dict[str, Any]] = None
|
|
43
|
+
extra_query: Optional[Dict[str, Any]] = None
|
|
44
|
+
extra_body: Optional[Dict[str, Any]] = None
|
|
41
45
|
request_params: Optional[Dict[str, Any]] = None
|
|
42
46
|
|
|
43
47
|
client: Optional[Any] = None
|
|
@@ -47,7 +51,7 @@ class LiteLLM(Model):
|
|
|
47
51
|
super().__post_init__()
|
|
48
52
|
|
|
49
53
|
# Set up API key from environment variable if not already set
|
|
50
|
-
if not self.api_key:
|
|
54
|
+
if not self.client and not self.api_key:
|
|
51
55
|
self.api_key = getenv("LITELLM_API_KEY")
|
|
52
56
|
if not self.api_key:
|
|
53
57
|
# Check for other present valid keys, e.g. OPENAI_API_KEY if self.id is an OpenAI model
|
|
@@ -148,10 +152,23 @@ class LiteLLM(Model):
|
|
|
148
152
|
base_params["api_key"] = self.api_key
|
|
149
153
|
if self.api_base:
|
|
150
154
|
base_params["api_base"] = self.api_base
|
|
155
|
+
if self.extra_headers:
|
|
156
|
+
base_params["extra_headers"] = self.extra_headers
|
|
157
|
+
if self.extra_query:
|
|
158
|
+
base_params["extra_query"] = self.extra_query
|
|
151
159
|
if tools:
|
|
152
160
|
base_params["tools"] = tools
|
|
153
161
|
base_params["tool_choice"] = "auto"
|
|
154
162
|
|
|
163
|
+
# Handle metadata via extra_body as per LiteLLM docs
|
|
164
|
+
if self.metadata:
|
|
165
|
+
if self.extra_body:
|
|
166
|
+
base_params["extra_body"] = {**self.extra_body, "metadata": self.metadata}
|
|
167
|
+
else:
|
|
168
|
+
base_params["extra_body"] = {"metadata": self.metadata}
|
|
169
|
+
elif self.extra_body:
|
|
170
|
+
base_params["extra_body"] = self.extra_body
|
|
171
|
+
|
|
155
172
|
# Add additional request params if provided
|
|
156
173
|
request_params: Dict[str, Any] = {k: v for k, v in base_params.items() if v is not None}
|
|
157
174
|
if self.request_params:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
@@ -21,5 +21,5 @@ class LiteLLMOpenAI(OpenAILike):
|
|
|
21
21
|
name: str = "LiteLLM"
|
|
22
22
|
provider: str = "LiteLLM"
|
|
23
23
|
|
|
24
|
-
api_key: Optional[str] = getenv("LITELLM_API_KEY")
|
|
24
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("LITELLM_API_KEY"))
|
|
25
25
|
base_url: str = "http://0.0.0.0:4000"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from agno.models.openai.like import OpenAILike
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class LlamaCpp(OpenAILike):
|
|
8
|
+
"""
|
|
9
|
+
A class for interacting with LLMs using Llama CPP.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
id (str): The id of the Llama CPP model. Default is "ggml-org/gpt-oss-20b-GGUF".
|
|
13
|
+
name (str): The name of this chat model instance. Default is "LlamaCpp".
|
|
14
|
+
provider (str): The provider of the model. Default is "LlamaCpp".
|
|
15
|
+
base_url (str): The base url to which the requests are sent.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
id: str = "ggml-org/gpt-oss-20b-GGUF"
|
|
19
|
+
name: str = "LlamaCpp"
|
|
20
|
+
provider: str = "LlamaCpp"
|
|
21
|
+
|
|
22
|
+
base_url: str = "http://127.0.0.1:8080/v1"
|
agno/models/message.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from time import time
|
|
3
3
|
from typing import Any, Dict, List, Optional, Sequence, Union
|
|
4
|
+
from uuid import uuid4
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel, ConfigDict, Field
|
|
6
7
|
|
|
@@ -51,6 +52,8 @@ class Citations(BaseModel):
|
|
|
51
52
|
class Message(BaseModel):
|
|
52
53
|
"""Message sent to the Model"""
|
|
53
54
|
|
|
55
|
+
id: str = Field(default_factory=lambda: str(uuid4()))
|
|
56
|
+
|
|
54
57
|
# The role of the message author.
|
|
55
58
|
# One of system, user, assistant, or tool.
|
|
56
59
|
role: str
|
|
@@ -74,6 +77,7 @@ class Message(BaseModel):
|
|
|
74
77
|
audio_output: Optional[Audio] = None
|
|
75
78
|
image_output: Optional[Image] = None
|
|
76
79
|
video_output: Optional[Video] = None
|
|
80
|
+
file_output: Optional[File] = None
|
|
77
81
|
|
|
78
82
|
# The thinking content from the model
|
|
79
83
|
redacted_reasoning_content: Optional[str] = None
|
|
@@ -121,11 +125,144 @@ class Message(BaseModel):
|
|
|
121
125
|
|
|
122
126
|
@classmethod
|
|
123
127
|
def from_dict(cls, data: Dict[str, Any]) -> "Message":
|
|
128
|
+
# Handle image reconstruction properly
|
|
129
|
+
if "images" in data and data["images"]:
|
|
130
|
+
reconstructed_images = []
|
|
131
|
+
for i, img_data in enumerate(data["images"]):
|
|
132
|
+
if isinstance(img_data, dict):
|
|
133
|
+
# If content is base64, decode it back to bytes
|
|
134
|
+
if "content" in img_data and isinstance(img_data["content"], str):
|
|
135
|
+
reconstructed_images.append(
|
|
136
|
+
Image.from_base64(
|
|
137
|
+
img_data["content"],
|
|
138
|
+
id=img_data.get("id"),
|
|
139
|
+
mime_type=img_data.get("mime_type"),
|
|
140
|
+
format=img_data.get("format"),
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
# Regular image (filepath/url)
|
|
145
|
+
reconstructed_images.append(Image(**img_data))
|
|
146
|
+
else:
|
|
147
|
+
reconstructed_images.append(img_data)
|
|
148
|
+
data["images"] = reconstructed_images
|
|
149
|
+
|
|
150
|
+
# Handle audio reconstruction properly
|
|
151
|
+
if "audio" in data and data["audio"]:
|
|
152
|
+
reconstructed_audio = []
|
|
153
|
+
for i, aud_data in enumerate(data["audio"]):
|
|
154
|
+
if isinstance(aud_data, dict):
|
|
155
|
+
# If content is base64, decode it back to bytes
|
|
156
|
+
if "content" in aud_data and isinstance(aud_data["content"], str):
|
|
157
|
+
reconstructed_audio.append(
|
|
158
|
+
Audio.from_base64(
|
|
159
|
+
aud_data["content"],
|
|
160
|
+
id=aud_data.get("id"),
|
|
161
|
+
mime_type=aud_data.get("mime_type"),
|
|
162
|
+
transcript=aud_data.get("transcript"),
|
|
163
|
+
expires_at=aud_data.get("expires_at"),
|
|
164
|
+
sample_rate=aud_data.get("sample_rate", 24000),
|
|
165
|
+
channels=aud_data.get("channels", 1),
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
reconstructed_audio.append(Audio(**aud_data))
|
|
170
|
+
else:
|
|
171
|
+
reconstructed_audio.append(aud_data)
|
|
172
|
+
data["audio"] = reconstructed_audio
|
|
173
|
+
|
|
174
|
+
# Handle video reconstruction properly
|
|
175
|
+
if "videos" in data and data["videos"]:
|
|
176
|
+
reconstructed_videos = []
|
|
177
|
+
for i, vid_data in enumerate(data["videos"]):
|
|
178
|
+
if isinstance(vid_data, dict):
|
|
179
|
+
# If content is base64, decode it back to bytes
|
|
180
|
+
if "content" in vid_data and isinstance(vid_data["content"], str):
|
|
181
|
+
reconstructed_videos.append(
|
|
182
|
+
Video.from_base64(
|
|
183
|
+
vid_data["content"],
|
|
184
|
+
id=vid_data.get("id"),
|
|
185
|
+
mime_type=vid_data.get("mime_type"),
|
|
186
|
+
format=vid_data.get("format"),
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
reconstructed_videos.append(Video(**vid_data))
|
|
191
|
+
else:
|
|
192
|
+
reconstructed_videos.append(vid_data)
|
|
193
|
+
data["videos"] = reconstructed_videos
|
|
194
|
+
|
|
195
|
+
# Handle file reconstruction properly
|
|
196
|
+
if "files" in data and data["files"]:
|
|
197
|
+
reconstructed_files = []
|
|
198
|
+
for i, file_data in enumerate(data["files"]):
|
|
199
|
+
if isinstance(file_data, dict):
|
|
200
|
+
# If content is base64, decode it back to bytes
|
|
201
|
+
if "content" in file_data and isinstance(file_data["content"], str):
|
|
202
|
+
reconstructed_files.append(
|
|
203
|
+
File.from_base64(
|
|
204
|
+
file_data["content"],
|
|
205
|
+
id=file_data.get("id"),
|
|
206
|
+
mime_type=file_data.get("mime_type"),
|
|
207
|
+
filename=file_data.get("filename"),
|
|
208
|
+
name=file_data.get("name"),
|
|
209
|
+
format=file_data.get("format"),
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
else:
|
|
213
|
+
reconstructed_files.append(File(**file_data))
|
|
214
|
+
else:
|
|
215
|
+
reconstructed_files.append(file_data)
|
|
216
|
+
data["files"] = reconstructed_files
|
|
217
|
+
|
|
218
|
+
if "audio_output" in data and data["audio_output"]:
|
|
219
|
+
aud_data = data["audio_output"]
|
|
220
|
+
if isinstance(aud_data, dict):
|
|
221
|
+
if "content" in aud_data and isinstance(aud_data["content"], str):
|
|
222
|
+
data["audio_output"] = Audio.from_base64(
|
|
223
|
+
aud_data["content"],
|
|
224
|
+
id=aud_data.get("id"),
|
|
225
|
+
mime_type=aud_data.get("mime_type"),
|
|
226
|
+
transcript=aud_data.get("transcript"),
|
|
227
|
+
expires_at=aud_data.get("expires_at"),
|
|
228
|
+
sample_rate=aud_data.get("sample_rate", 24000),
|
|
229
|
+
channels=aud_data.get("channels", 1),
|
|
230
|
+
)
|
|
231
|
+
else:
|
|
232
|
+
data["audio_output"] = Audio(**aud_data)
|
|
233
|
+
|
|
234
|
+
if "image_output" in data and data["image_output"]:
|
|
235
|
+
img_data = data["image_output"]
|
|
236
|
+
if isinstance(img_data, dict):
|
|
237
|
+
if "content" in img_data and isinstance(img_data["content"], str):
|
|
238
|
+
data["image_output"] = Image.from_base64(
|
|
239
|
+
img_data["content"],
|
|
240
|
+
id=img_data.get("id"),
|
|
241
|
+
mime_type=img_data.get("mime_type"),
|
|
242
|
+
format=img_data.get("format"),
|
|
243
|
+
)
|
|
244
|
+
else:
|
|
245
|
+
data["image_output"] = Image(**img_data)
|
|
246
|
+
|
|
247
|
+
if "video_output" in data and data["video_output"]:
|
|
248
|
+
vid_data = data["video_output"]
|
|
249
|
+
if isinstance(vid_data, dict):
|
|
250
|
+
if "content" in vid_data and isinstance(vid_data["content"], str):
|
|
251
|
+
data["video_output"] = Video.from_base64(
|
|
252
|
+
vid_data["content"],
|
|
253
|
+
id=vid_data.get("id"),
|
|
254
|
+
mime_type=vid_data.get("mime_type"),
|
|
255
|
+
format=vid_data.get("format"),
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
data["video_output"] = Video(**vid_data)
|
|
259
|
+
|
|
124
260
|
return cls(**data)
|
|
125
261
|
|
|
126
262
|
def to_dict(self) -> Dict[str, Any]:
|
|
127
263
|
"""Returns the message as a dictionary."""
|
|
128
264
|
message_dict = {
|
|
265
|
+
"id": self.id,
|
|
129
266
|
"content": self.content,
|
|
130
267
|
"reasoning_content": self.reasoning_content,
|
|
131
268
|
"from_history": self.from_history,
|
|
@@ -152,6 +289,8 @@ class Message(BaseModel):
|
|
|
152
289
|
message_dict["audio"] = [aud.to_dict() for aud in self.audio]
|
|
153
290
|
if self.videos:
|
|
154
291
|
message_dict["videos"] = [vid.to_dict() for vid in self.videos]
|
|
292
|
+
if self.files:
|
|
293
|
+
message_dict["files"] = [file.to_dict() for file in self.files]
|
|
155
294
|
if self.audio_output:
|
|
156
295
|
message_dict["audio_output"] = self.audio_output.to_dict()
|
|
157
296
|
|
agno/models/meta/llama.py
CHANGED
|
@@ -12,6 +12,7 @@ from agno.models.message import Message
|
|
|
12
12
|
from agno.models.metrics import Metrics
|
|
13
13
|
from agno.models.response import ModelResponse
|
|
14
14
|
from agno.run.agent import RunOutput
|
|
15
|
+
from agno.utils.http import get_default_async_client, get_default_sync_client
|
|
15
16
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
16
17
|
from agno.utils.models.llama import format_message
|
|
17
18
|
|
|
@@ -61,7 +62,7 @@ class Llama(Model):
|
|
|
61
62
|
max_retries: Optional[int] = None
|
|
62
63
|
default_headers: Optional[Any] = None
|
|
63
64
|
default_query: Optional[Any] = None
|
|
64
|
-
http_client: Optional[httpx.Client] = None
|
|
65
|
+
http_client: Optional[Union[httpx.Client, httpx.AsyncClient]] = None
|
|
65
66
|
client_params: Optional[Dict[str, Any]] = None
|
|
66
67
|
|
|
67
68
|
# OpenAI clients
|
|
@@ -104,8 +105,16 @@ class Llama(Model):
|
|
|
104
105
|
return self.client
|
|
105
106
|
|
|
106
107
|
client_params: Dict[str, Any] = self._get_client_params()
|
|
107
|
-
if self.http_client
|
|
108
|
-
|
|
108
|
+
if self.http_client:
|
|
109
|
+
if isinstance(self.http_client, httpx.Client):
|
|
110
|
+
client_params["http_client"] = self.http_client
|
|
111
|
+
else:
|
|
112
|
+
log_warning("http_client is not an instance of httpx.Client. Using default global httpx.Client.")
|
|
113
|
+
# Use global sync client when user http_client is invalid
|
|
114
|
+
client_params["http_client"] = get_default_sync_client()
|
|
115
|
+
else:
|
|
116
|
+
# Use global sync client when no custom http_client is provided
|
|
117
|
+
client_params["http_client"] = get_default_sync_client()
|
|
109
118
|
self.client = LlamaAPIClient(**client_params)
|
|
110
119
|
return self.client
|
|
111
120
|
|
|
@@ -116,18 +125,26 @@ class Llama(Model):
|
|
|
116
125
|
Returns:
|
|
117
126
|
AsyncLlamaAPIClient: An instance of the asynchronous Llama client.
|
|
118
127
|
"""
|
|
119
|
-
if self.async_client:
|
|
128
|
+
if self.async_client and not self.async_client.is_closed():
|
|
120
129
|
return self.async_client
|
|
121
130
|
|
|
122
131
|
client_params: Dict[str, Any] = self._get_client_params()
|
|
123
132
|
if self.http_client:
|
|
124
|
-
|
|
133
|
+
if isinstance(self.http_client, httpx.AsyncClient):
|
|
134
|
+
client_params["http_client"] = self.http_client
|
|
135
|
+
else:
|
|
136
|
+
log_warning(
|
|
137
|
+
"http_client is not an instance of httpx.AsyncClient. Using default global httpx.AsyncClient."
|
|
138
|
+
)
|
|
139
|
+
# Use global async client when user http_client is invalid
|
|
140
|
+
client_params["http_client"] = get_default_async_client()
|
|
125
141
|
else:
|
|
126
|
-
#
|
|
127
|
-
client_params["http_client"] =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
# Use global async client when no custom http_client is provided
|
|
143
|
+
client_params["http_client"] = get_default_async_client()
|
|
144
|
+
|
|
145
|
+
# Create and cache the client
|
|
146
|
+
self.async_client = AsyncLlamaAPIClient(**client_params)
|
|
147
|
+
return self.async_client
|
|
131
148
|
|
|
132
149
|
def get_request_params(
|
|
133
150
|
self,
|
agno/models/meta/llama_openai.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Any, Dict, Optional
|
|
4
4
|
|
|
5
|
-
import httpx
|
|
6
|
-
|
|
7
5
|
try:
|
|
8
6
|
from openai import AsyncOpenAI as AsyncOpenAIClient
|
|
9
7
|
except ImportError:
|
|
@@ -31,7 +29,7 @@ class LlamaOpenAI(OpenAILike):
|
|
|
31
29
|
name: str = "LlamaOpenAI"
|
|
32
30
|
provider: str = "LlamaOpenAI"
|
|
33
31
|
|
|
34
|
-
api_key: Optional[str] = getenv("LLAMA_API_KEY")
|
|
32
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("LLAMA_API_KEY"))
|
|
35
33
|
base_url: Optional[str] = "https://api.llama.com/compat/v1/"
|
|
36
34
|
|
|
37
35
|
# Request parameters
|
|
@@ -48,6 +46,9 @@ class LlamaOpenAI(OpenAILike):
|
|
|
48
46
|
supports_native_structured_outputs: bool = False
|
|
49
47
|
supports_json_schema_outputs: bool = True
|
|
50
48
|
|
|
49
|
+
# Cached async client
|
|
50
|
+
openai_async_client: Optional[AsyncOpenAIClient] = None
|
|
51
|
+
|
|
51
52
|
def _format_message(self, message: Message) -> Dict[str, Any]:
|
|
52
53
|
"""
|
|
53
54
|
Format a message into the format expected by Llama API.
|
|
@@ -59,16 +60,3 @@ class LlamaOpenAI(OpenAILike):
|
|
|
59
60
|
Dict[str, Any]: The formatted message.
|
|
60
61
|
"""
|
|
61
62
|
return format_message(message, openai_like=True)
|
|
62
|
-
|
|
63
|
-
def get_async_client(self):
|
|
64
|
-
"""Override to provide custom httpx client that properly handles redirects"""
|
|
65
|
-
client_params = self._get_client_params()
|
|
66
|
-
|
|
67
|
-
# Llama gives a 307 redirect error, so we need to set up a custom client to allow redirects
|
|
68
|
-
client_params["http_client"] = httpx.AsyncClient(
|
|
69
|
-
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=100),
|
|
70
|
-
follow_redirects=True,
|
|
71
|
-
timeout=httpx.Timeout(30.0),
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
return AsyncOpenAIClient(**client_params)
|
agno/models/nebius/nebius.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Any, Dict, Optional
|
|
4
4
|
|
|
@@ -9,22 +9,22 @@ from agno.models.openai.like import OpenAILike
|
|
|
9
9
|
@dataclass
|
|
10
10
|
class Nebius(OpenAILike):
|
|
11
11
|
"""
|
|
12
|
-
A class for interacting with Nebius
|
|
12
|
+
A class for interacting with Nebius Token Factory models.
|
|
13
13
|
|
|
14
14
|
Attributes:
|
|
15
15
|
id (str): The model id. Defaults to "Qwen/Qwen3-235B-A22B"".
|
|
16
16
|
name (str): The model name. Defaults to "Nebius".
|
|
17
17
|
provider (str): The provider name. Defaults to "Nebius".
|
|
18
18
|
api_key (Optional[str]): The API key.
|
|
19
|
-
base_url (str): The base URL. Defaults to "https://api.
|
|
19
|
+
base_url (str): The base URL. Defaults to "https://api.tokenfactory.nebius.com/v1".
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
id: str = "
|
|
22
|
+
id: str = "openai/gpt-oss-20b" # Default model for chat
|
|
23
23
|
name: str = "Nebius"
|
|
24
24
|
provider: str = "Nebius"
|
|
25
25
|
|
|
26
|
-
api_key: Optional[str] = getenv("NEBIUS_API_KEY")
|
|
27
|
-
base_url: str = "https://api.
|
|
26
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("NEBIUS_API_KEY"))
|
|
27
|
+
base_url: str = "https://api.tokenfactory.nebius.com/v1/"
|
|
28
28
|
|
|
29
29
|
def _get_client_params(self) -> Dict[str, Any]:
|
|
30
30
|
if not self.api_key:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from agno.models.openai.like import OpenAILike
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class Nexus(OpenAILike):
|
|
8
|
+
"""
|
|
9
|
+
A class for interacting with LLMs using Nexus.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
id (str): The id of the Nexus model to use. Default is "openai/gpt-4".
|
|
13
|
+
name (str): The name of this chat model instance. Default is "Nexus"
|
|
14
|
+
provider (str): The provider of the model. Default is "Nexus".
|
|
15
|
+
base_url (str): The base url to which the requests are sent.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
id: str = "openai/gpt-4"
|
|
19
|
+
name: str = "Nexus"
|
|
20
|
+
provider: str = "Nexus"
|
|
21
|
+
|
|
22
|
+
base_url: str = "http://localhost:8000/llm/v1/"
|
agno/models/nvidia/nvidia.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from os import getenv
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
@@ -22,7 +22,7 @@ class Nvidia(OpenAILike):
|
|
|
22
22
|
name: str = "Nvidia"
|
|
23
23
|
provider: str = "Nvidia"
|
|
24
24
|
|
|
25
|
-
api_key: Optional[str] = getenv("NVIDIA_API_KEY")
|
|
25
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("NVIDIA_API_KEY"))
|
|
26
26
|
base_url: str = "https://integrate.api.nvidia.com/v1"
|
|
27
27
|
|
|
28
28
|
supports_native_structured_outputs: bool = False
|