agno 2.2.13__py3-none-any.whl → 2.4.3__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/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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 +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -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 +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -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 +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knowledge Protocol
|
|
3
|
+
==================
|
|
4
|
+
Defines the minimal interface that knowledge implementations must implement.
|
|
5
|
+
|
|
6
|
+
This protocol enables:
|
|
7
|
+
- Custom knowledge bases to be used with agents
|
|
8
|
+
- Each implementation defines its own tools and context
|
|
9
|
+
- Flexible tool naming (not forced to use 'search')
|
|
10
|
+
- Type safety with Protocol typing
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Callable, List, Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from agno.knowledge.document import Document
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@runtime_checkable
|
|
19
|
+
class KnowledgeProtocol(Protocol):
|
|
20
|
+
"""Minimal protocol for knowledge implementations.
|
|
21
|
+
|
|
22
|
+
Enables custom knowledge bases to be used with agents.
|
|
23
|
+
Each implementation defines what tools it exposes and what
|
|
24
|
+
context/instructions it provides to the agent.
|
|
25
|
+
|
|
26
|
+
Required methods:
|
|
27
|
+
- build_context(): Return instructions for the agent's system prompt
|
|
28
|
+
- get_tools(): Return tools to expose to the agent
|
|
29
|
+
- aget_tools(): Async version of get_tools
|
|
30
|
+
|
|
31
|
+
Optional methods:
|
|
32
|
+
- retrieve(): Default retrieval for context injection (add_knowledge_to_context)
|
|
33
|
+
- aretrieve(): Async version of retrieve
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
```python
|
|
37
|
+
from agno.knowledge.protocol import KnowledgeProtocol
|
|
38
|
+
from agno.knowledge.document import Document
|
|
39
|
+
|
|
40
|
+
class MyKnowledge:
|
|
41
|
+
def build_context(self, **kwargs) -> str:
|
|
42
|
+
return "Use search_docs to find information."
|
|
43
|
+
|
|
44
|
+
def get_tools(self, **kwargs) -> List[Callable]:
|
|
45
|
+
return [self.search_docs]
|
|
46
|
+
|
|
47
|
+
async def aget_tools(self, **kwargs) -> List[Callable]:
|
|
48
|
+
return [self.search_docs]
|
|
49
|
+
|
|
50
|
+
def search_docs(self, query: str) -> str:
|
|
51
|
+
# Your search implementation
|
|
52
|
+
return "Results for: " + query
|
|
53
|
+
|
|
54
|
+
# Optional: for add_knowledge_to_context feature
|
|
55
|
+
def retrieve(self, query: str, **kwargs) -> List[Document]:
|
|
56
|
+
results = self._internal_search(query)
|
|
57
|
+
return [Document(content=r) for r in results]
|
|
58
|
+
|
|
59
|
+
# MyKnowledge satisfies KnowledgeProtocol
|
|
60
|
+
agent = Agent(knowledge=MyKnowledge())
|
|
61
|
+
```
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def build_context(self, **kwargs) -> str:
|
|
65
|
+
"""Build context string for the agent's system prompt.
|
|
66
|
+
|
|
67
|
+
Returns instructions about how to use this knowledge,
|
|
68
|
+
what tools are available, and any usage guidelines.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
**kwargs: Context including enable_agentic_filters, etc.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Formatted context string to inject into system prompt.
|
|
75
|
+
"""
|
|
76
|
+
...
|
|
77
|
+
|
|
78
|
+
def get_tools(self, **kwargs) -> List[Callable]:
|
|
79
|
+
"""Get tools to expose to the agent.
|
|
80
|
+
|
|
81
|
+
Returns callable tools that the agent can use to interact
|
|
82
|
+
with this knowledge. Each implementation decides what
|
|
83
|
+
tools make sense (e.g., search, grep, list_files, query_db).
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
**kwargs: Context including run_response, run_context,
|
|
87
|
+
async_mode, enable_agentic_filters, agent, etc.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
List of callable tools.
|
|
91
|
+
"""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
async def aget_tools(self, **kwargs) -> List[Callable]:
|
|
95
|
+
"""Async version of get_tools.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
**kwargs: Same as get_tools.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of callable tools.
|
|
102
|
+
"""
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
# Optional methods - used by add_knowledge_to_context feature
|
|
106
|
+
# Implementations that don't support context injection can omit these
|
|
107
|
+
|
|
108
|
+
def retrieve(self, query: str, **kwargs) -> List[Document]:
|
|
109
|
+
"""Retrieve documents for context injection.
|
|
110
|
+
|
|
111
|
+
Used by the add_knowledge_to_context feature to pre-fetch
|
|
112
|
+
relevant documents into the user message. This is optional;
|
|
113
|
+
if not implemented, add_knowledge_to_context will be skipped.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
query: The query string.
|
|
117
|
+
**kwargs: Additional parameters (max_results, filters, etc.)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
List of Document objects.
|
|
121
|
+
"""
|
|
122
|
+
...
|
|
123
|
+
|
|
124
|
+
async def aretrieve(self, query: str, **kwargs) -> List[Document]:
|
|
125
|
+
"""Async version of retrieve.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
query: The query string.
|
|
129
|
+
**kwargs: Additional parameters.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
List of Document objects.
|
|
133
|
+
"""
|
|
134
|
+
...
|
|
@@ -17,9 +17,10 @@ class ArxivReader(Reader):
|
|
|
17
17
|
sort_by: arxiv.SortCriterion = arxiv.SortCriterion.Relevance
|
|
18
18
|
|
|
19
19
|
@classmethod
|
|
20
|
-
def get_supported_chunking_strategies(
|
|
20
|
+
def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
|
|
21
21
|
"""Get the list of supported chunking strategies for Arxiv readers."""
|
|
22
22
|
return [
|
|
23
|
+
ChunkingStrategyType.CODE_CHUNKER,
|
|
23
24
|
ChunkingStrategyType.FIXED_SIZE_CHUNKER,
|
|
24
25
|
ChunkingStrategyType.AGENTIC_CHUNKER,
|
|
25
26
|
ChunkingStrategyType.DOCUMENT_CHUNKER,
|
|
@@ -28,7 +29,7 @@ class ArxivReader(Reader):
|
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
@classmethod
|
|
31
|
-
def get_supported_content_types(
|
|
32
|
+
def get_supported_content_types(cls) -> List[ContentType]:
|
|
32
33
|
return [ContentType.TOPIC]
|
|
33
34
|
|
|
34
35
|
def __init__(
|
agno/knowledge/reader/base.py
CHANGED
|
@@ -73,11 +73,17 @@ class Reader:
|
|
|
73
73
|
def chunk_document(self, document: Document) -> List[Document]:
|
|
74
74
|
if self.chunking_strategy is None:
|
|
75
75
|
self.chunking_strategy = FixedSizeChunking(chunk_size=self.chunk_size)
|
|
76
|
-
return self.chunking_strategy.chunk(document)
|
|
76
|
+
return self.chunking_strategy.chunk(document)
|
|
77
|
+
|
|
78
|
+
async def achunk_document(self, document: Document) -> List[Document]:
|
|
79
|
+
"""Async version of chunk_document."""
|
|
80
|
+
if self.chunking_strategy is None:
|
|
81
|
+
self.chunking_strategy = FixedSizeChunking(chunk_size=self.chunk_size)
|
|
82
|
+
return await self.chunking_strategy.achunk(document)
|
|
77
83
|
|
|
78
84
|
async def chunk_documents_async(self, documents: List[Document]) -> List[Document]:
|
|
79
85
|
"""
|
|
80
|
-
Asynchronously chunk a list of documents
|
|
86
|
+
Asynchronously chunk a list of documents.
|
|
81
87
|
|
|
82
88
|
Args:
|
|
83
89
|
documents: List of documents to be chunked.
|
|
@@ -85,11 +91,7 @@ class Reader:
|
|
|
85
91
|
Returns:
|
|
86
92
|
A flattened list of chunked documents.
|
|
87
93
|
"""
|
|
88
|
-
|
|
89
|
-
async def _chunk_document_async(doc: Document) -> List[Document]:
|
|
90
|
-
return await asyncio.to_thread(self.chunk_document, doc)
|
|
91
|
-
|
|
92
94
|
# Process chunking in parallel for all documents
|
|
93
|
-
chunked_lists = await asyncio.gather(*[
|
|
95
|
+
chunked_lists = await asyncio.gather(*[self.achunk_document(doc) for doc in documents])
|
|
94
96
|
# Flatten the result
|
|
95
97
|
return [chunk for sublist in chunked_lists for chunk in sublist]
|
|
@@ -14,21 +14,42 @@ from agno.knowledge.chunking.row import RowChunking
|
|
|
14
14
|
from agno.knowledge.chunking.strategy import ChunkingStrategy, ChunkingStrategyType
|
|
15
15
|
from agno.knowledge.document.base import Document
|
|
16
16
|
from agno.knowledge.reader.base import Reader
|
|
17
|
+
from agno.knowledge.reader.utils import stringify_cell_value
|
|
17
18
|
from agno.knowledge.types import ContentType
|
|
18
|
-
from agno.utils.log import
|
|
19
|
+
from agno.utils.log import log_debug, log_error
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class CSVReader(Reader):
|
|
22
|
-
"""Reader for CSV files
|
|
23
|
+
"""Reader for CSV files.
|
|
24
|
+
|
|
25
|
+
Converts CSV files to documents with optional chunking support.
|
|
26
|
+
For Excel files (.xlsx, .xls), use ExcelReader instead.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
chunking_strategy: Strategy for chunking documents. Default is RowChunking.
|
|
30
|
+
**kwargs: Additional arguments passed to base Reader.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
```python
|
|
34
|
+
from agno.knowledge.reader.csv_reader import CSVReader
|
|
35
|
+
|
|
36
|
+
reader = CSVReader()
|
|
37
|
+
docs = reader.read("data.csv")
|
|
38
|
+
|
|
39
|
+
# Custom delimiter
|
|
40
|
+
docs = reader.read("data.tsv", delimiter="\\t")
|
|
41
|
+
```
|
|
42
|
+
"""
|
|
23
43
|
|
|
24
44
|
def __init__(self, chunking_strategy: Optional[ChunkingStrategy] = RowChunking(), **kwargs):
|
|
25
45
|
super().__init__(chunking_strategy=chunking_strategy, **kwargs)
|
|
26
46
|
|
|
27
47
|
@classmethod
|
|
28
|
-
def get_supported_chunking_strategies(
|
|
48
|
+
def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
|
|
29
49
|
"""Get the list of supported chunking strategies for CSV readers."""
|
|
30
50
|
return [
|
|
31
51
|
ChunkingStrategyType.ROW_CHUNKER,
|
|
52
|
+
ChunkingStrategyType.CODE_CHUNKER,
|
|
32
53
|
ChunkingStrategyType.FIXED_SIZE_CHUNKER,
|
|
33
54
|
ChunkingStrategyType.AGENTIC_CHUNKER,
|
|
34
55
|
ChunkingStrategyType.DOCUMENT_CHUNKER,
|
|
@@ -36,39 +57,54 @@ class CSVReader(Reader):
|
|
|
36
57
|
]
|
|
37
58
|
|
|
38
59
|
@classmethod
|
|
39
|
-
def get_supported_content_types(
|
|
40
|
-
|
|
60
|
+
def get_supported_content_types(cls) -> List[ContentType]:
|
|
61
|
+
"""Get the list of supported content types."""
|
|
62
|
+
return [ContentType.CSV]
|
|
41
63
|
|
|
42
64
|
def read(
|
|
43
65
|
self, file: Union[Path, IO[Any]], delimiter: str = ",", quotechar: str = '"', name: Optional[str] = None
|
|
44
66
|
) -> List[Document]:
|
|
67
|
+
"""Read a CSV file and return a list of documents.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
file: Path to CSV file or file-like object.
|
|
71
|
+
delimiter: CSV field delimiter. Default is comma.
|
|
72
|
+
quotechar: CSV quote character. Default is double quote.
|
|
73
|
+
name: Optional name override for the document.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List of Document objects.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
FileNotFoundError: If the file path doesn't exist.
|
|
80
|
+
"""
|
|
45
81
|
try:
|
|
46
82
|
if isinstance(file, Path):
|
|
47
83
|
if not file.exists():
|
|
48
84
|
raise FileNotFoundError(f"Could not find file: {file}")
|
|
49
|
-
|
|
50
|
-
|
|
85
|
+
log_debug(f"Reading: {file}")
|
|
86
|
+
csv_name = name or file.stem
|
|
87
|
+
file_content: Union[io.TextIOWrapper, io.StringIO] = file.open(
|
|
88
|
+
newline="", mode="r", encoding=self.encoding or "utf-8"
|
|
89
|
+
)
|
|
51
90
|
else:
|
|
52
|
-
|
|
91
|
+
log_debug(f"Reading retrieved file: {getattr(file, 'name', 'BytesIO')}")
|
|
92
|
+
csv_name = name or getattr(file, "name", "csv_file").split(".")[0]
|
|
53
93
|
file.seek(0)
|
|
54
|
-
file_content = io.StringIO(file.read().decode("utf-8"))
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Path(file.name).stem
|
|
58
|
-
if isinstance(file, Path)
|
|
59
|
-
else (getattr(file, "name", "csv_file").split(".")[0] if hasattr(file, "name") else "csv_file")
|
|
60
|
-
)
|
|
61
|
-
csv_content = ""
|
|
94
|
+
file_content = io.StringIO(file.read().decode(self.encoding or "utf-8"))
|
|
95
|
+
|
|
96
|
+
csv_lines: List[str] = []
|
|
62
97
|
with file_content as csvfile:
|
|
63
98
|
csv_reader = csv.reader(csvfile, delimiter=delimiter, quotechar=quotechar)
|
|
64
99
|
for row in csv_reader:
|
|
65
|
-
|
|
100
|
+
# Normalize line endings in CSV cells to preserve row integrity
|
|
101
|
+
csv_lines.append(", ".join(stringify_cell_value(cell) for cell in row))
|
|
66
102
|
|
|
67
103
|
documents = [
|
|
68
104
|
Document(
|
|
69
105
|
name=csv_name,
|
|
70
106
|
id=str(uuid4()),
|
|
71
|
-
content=
|
|
107
|
+
content="\n".join(csv_lines),
|
|
72
108
|
)
|
|
73
109
|
]
|
|
74
110
|
if self.chunk:
|
|
@@ -77,8 +113,15 @@ class CSVReader(Reader):
|
|
|
77
113
|
chunked_documents.extend(self.chunk_document(document))
|
|
78
114
|
return chunked_documents
|
|
79
115
|
return documents
|
|
116
|
+
except FileNotFoundError:
|
|
117
|
+
raise
|
|
118
|
+
except UnicodeDecodeError as e:
|
|
119
|
+
file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
|
|
120
|
+
log_error(f"Encoding error reading {file_desc}: {e}. Try specifying a different encoding.")
|
|
121
|
+
return []
|
|
80
122
|
except Exception as e:
|
|
81
|
-
|
|
123
|
+
file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
|
|
124
|
+
log_error(f"Error reading {file_desc}: {e}")
|
|
82
125
|
return []
|
|
83
126
|
|
|
84
127
|
async def async_read(
|
|
@@ -89,36 +132,35 @@ class CSVReader(Reader):
|
|
|
89
132
|
page_size: int = 1000,
|
|
90
133
|
name: Optional[str] = None,
|
|
91
134
|
) -> List[Document]:
|
|
92
|
-
"""
|
|
93
|
-
Read a CSV file asynchronously, processing batches of rows concurrently.
|
|
135
|
+
"""Read a CSV file asynchronously, processing batches of rows concurrently.
|
|
94
136
|
|
|
95
137
|
Args:
|
|
96
|
-
file: Path or file-like object
|
|
97
|
-
delimiter: CSV delimiter
|
|
98
|
-
quotechar: CSV quote character
|
|
99
|
-
page_size: Number of rows per page
|
|
138
|
+
file: Path to CSV file or file-like object.
|
|
139
|
+
delimiter: CSV field delimiter. Default is comma.
|
|
140
|
+
quotechar: CSV quote character. Default is double quote.
|
|
141
|
+
page_size: Number of rows per page for large files.
|
|
142
|
+
name: Optional name override for the document.
|
|
100
143
|
|
|
101
144
|
Returns:
|
|
102
|
-
List of Document objects
|
|
145
|
+
List of Document objects.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
FileNotFoundError: If the file path doesn't exist.
|
|
103
149
|
"""
|
|
104
150
|
try:
|
|
105
151
|
if isinstance(file, Path):
|
|
106
152
|
if not file.exists():
|
|
107
153
|
raise FileNotFoundError(f"Could not find file: {file}")
|
|
108
|
-
|
|
109
|
-
async with aiofiles.open(file, mode="r", encoding="utf-8", newline="") as file_content:
|
|
154
|
+
log_debug(f"Reading async: {file}")
|
|
155
|
+
async with aiofiles.open(file, mode="r", encoding=self.encoding or "utf-8", newline="") as file_content:
|
|
110
156
|
content = await file_content.read()
|
|
111
157
|
file_content_io = io.StringIO(content)
|
|
158
|
+
csv_name = name or file.stem
|
|
112
159
|
else:
|
|
113
|
-
|
|
160
|
+
log_debug(f"Reading retrieved file async: {getattr(file, 'name', 'BytesIO')}")
|
|
114
161
|
file.seek(0)
|
|
115
|
-
file_content_io = io.StringIO(file.read().decode("utf-8"))
|
|
116
|
-
|
|
117
|
-
csv_name = name or (
|
|
118
|
-
Path(file.name).stem
|
|
119
|
-
if isinstance(file, Path)
|
|
120
|
-
else (getattr(file, "name", "csv_file").split(".")[0] if hasattr(file, "name") else "csv_file")
|
|
121
|
-
)
|
|
162
|
+
file_content_io = io.StringIO(file.read().decode(self.encoding or "utf-8"))
|
|
163
|
+
csv_name = name or getattr(file, "name", "csv_file").split(".")[0]
|
|
122
164
|
|
|
123
165
|
file_content_io.seek(0)
|
|
124
166
|
csv_reader = csv.reader(file_content_io, delimiter=delimiter, quotechar=quotechar)
|
|
@@ -126,7 +168,8 @@ class CSVReader(Reader):
|
|
|
126
168
|
total_rows = len(rows)
|
|
127
169
|
|
|
128
170
|
if total_rows <= 10:
|
|
129
|
-
|
|
171
|
+
# Small files: single document
|
|
172
|
+
csv_content = " ".join(", ".join(stringify_cell_value(cell) for cell in row) for row in rows)
|
|
130
173
|
documents = [
|
|
131
174
|
Document(
|
|
132
175
|
name=csv_name,
|
|
@@ -135,14 +178,15 @@ class CSVReader(Reader):
|
|
|
135
178
|
)
|
|
136
179
|
]
|
|
137
180
|
else:
|
|
181
|
+
# Large files: paginate and process in parallel
|
|
138
182
|
pages = []
|
|
139
183
|
for i in range(0, total_rows, page_size):
|
|
140
184
|
pages.append(rows[i : i + page_size])
|
|
141
185
|
|
|
142
186
|
async def _process_page(page_number: int, page_rows: List[List[str]]) -> Document:
|
|
143
|
-
"""Process a page of rows into a document"""
|
|
187
|
+
"""Process a page of rows into a document."""
|
|
144
188
|
start_row = (page_number - 1) * page_size + 1
|
|
145
|
-
page_content = " ".join(", ".join(row) for row in page_rows)
|
|
189
|
+
page_content = " ".join(", ".join(stringify_cell_value(cell) for cell in row) for row in page_rows)
|
|
146
190
|
|
|
147
191
|
return Document(
|
|
148
192
|
name=csv_name,
|
|
@@ -159,8 +203,13 @@ class CSVReader(Reader):
|
|
|
159
203
|
documents = await self.chunk_documents_async(documents)
|
|
160
204
|
|
|
161
205
|
return documents
|
|
206
|
+
except FileNotFoundError:
|
|
207
|
+
raise
|
|
208
|
+
except UnicodeDecodeError as e:
|
|
209
|
+
file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
|
|
210
|
+
log_error(f"Encoding error reading {file_desc}: {e}. Try specifying a different encoding.")
|
|
211
|
+
return []
|
|
162
212
|
except Exception as e:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
213
|
+
file_desc = getattr(file, "name", str(file)) if isinstance(file, IO) else file
|
|
214
|
+
log_error(f"Error reading {file_desc}: {e}")
|
|
166
215
|
return []
|
|
@@ -8,7 +8,7 @@ from agno.knowledge.chunking.strategy import ChunkingStrategy, ChunkingStrategyT
|
|
|
8
8
|
from agno.knowledge.document.base import Document
|
|
9
9
|
from agno.knowledge.reader.base import Reader
|
|
10
10
|
from agno.knowledge.types import ContentType
|
|
11
|
-
from agno.utils.log import
|
|
11
|
+
from agno.utils.log import log_debug, log_error
|
|
12
12
|
|
|
13
13
|
try:
|
|
14
14
|
from docx import Document as DocxDocument # type: ignore
|
|
@@ -23,10 +23,11 @@ class DocxReader(Reader):
|
|
|
23
23
|
super().__init__(chunking_strategy=chunking_strategy, **kwargs)
|
|
24
24
|
|
|
25
25
|
@classmethod
|
|
26
|
-
def get_supported_chunking_strategies(
|
|
26
|
+
def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
|
|
27
27
|
"""Get the list of supported chunking strategies for DOCX readers."""
|
|
28
28
|
return [
|
|
29
29
|
ChunkingStrategyType.DOCUMENT_CHUNKER,
|
|
30
|
+
ChunkingStrategyType.CODE_CHUNKER,
|
|
30
31
|
ChunkingStrategyType.FIXED_SIZE_CHUNKER,
|
|
31
32
|
ChunkingStrategyType.SEMANTIC_CHUNKER,
|
|
32
33
|
ChunkingStrategyType.AGENTIC_CHUNKER,
|
|
@@ -34,7 +35,7 @@ class DocxReader(Reader):
|
|
|
34
35
|
]
|
|
35
36
|
|
|
36
37
|
@classmethod
|
|
37
|
-
def get_supported_content_types(
|
|
38
|
+
def get_supported_content_types(cls) -> List[ContentType]:
|
|
38
39
|
return [ContentType.DOCX, ContentType.DOC]
|
|
39
40
|
|
|
40
41
|
def read(self, file: Union[Path, IO[Any]], name: Optional[str] = None) -> List[Document]:
|
|
@@ -43,15 +44,13 @@ class DocxReader(Reader):
|
|
|
43
44
|
if isinstance(file, Path):
|
|
44
45
|
if not file.exists():
|
|
45
46
|
raise FileNotFoundError(f"Could not find file: {file}")
|
|
46
|
-
|
|
47
|
+
log_debug(f"Reading: {file}")
|
|
47
48
|
docx_document = DocxDocument(str(file))
|
|
48
49
|
doc_name = name or file.stem
|
|
49
50
|
else:
|
|
50
|
-
|
|
51
|
+
log_debug(f"Reading uploaded file: {getattr(file, 'name', 'BytesIO')}")
|
|
51
52
|
docx_document = DocxDocument(file)
|
|
52
|
-
doc_name = name or (
|
|
53
|
-
getattr(file, "name", "docx_file").split(".")[0] if hasattr(file, "name") else "docx_file"
|
|
54
|
-
)
|
|
53
|
+
doc_name = name or getattr(file, "name", "docx_file").split(".")[0]
|
|
55
54
|
|
|
56
55
|
doc_content = "\n\n".join([para.text for para in docx_document.paragraphs])
|
|
57
56
|
|
|
@@ -70,7 +69,7 @@ class DocxReader(Reader):
|
|
|
70
69
|
return documents
|
|
71
70
|
|
|
72
71
|
except Exception as e:
|
|
73
|
-
|
|
72
|
+
log_error(f"Error reading file: {e}")
|
|
74
73
|
return []
|
|
75
74
|
|
|
76
75
|
async def async_read(self, file: Union[Path, IO[Any]], name: Optional[str] = None) -> List[Document]:
|
|
@@ -78,5 +77,5 @@ class DocxReader(Reader):
|
|
|
78
77
|
try:
|
|
79
78
|
return await asyncio.to_thread(self.read, file, name)
|
|
80
79
|
except Exception as e:
|
|
81
|
-
|
|
80
|
+
log_error(f"Error reading file asynchronously: {e}")
|
|
82
81
|
return []
|