agno 0.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/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- 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/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- 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 +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -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/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- 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/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -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 +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- 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/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- 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 +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- 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 +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- 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/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import csv
|
|
3
|
+
import io
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import IO, Any, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import aiofiles
|
|
9
|
+
except ImportError:
|
|
10
|
+
raise ImportError("`aiofiles` not installed. Please install it with `pip install aiofiles`")
|
|
11
|
+
|
|
12
|
+
from agno.knowledge.chunking.strategy import ChunkingStrategyType
|
|
13
|
+
from agno.knowledge.document.base import Document
|
|
14
|
+
from agno.knowledge.reader.base import Reader
|
|
15
|
+
from agno.knowledge.types import ContentType
|
|
16
|
+
from agno.utils.log import log_debug, log_error, log_warning
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FieldLabeledCSVReader(Reader):
|
|
20
|
+
"""Reader for CSV files that converts each row to a field-labeled document."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
chunk_title: Optional[Union[str, List[str]]] = None,
|
|
25
|
+
field_names: Optional[List[str]] = None,
|
|
26
|
+
format_headers: bool = True,
|
|
27
|
+
skip_empty_fields: bool = True,
|
|
28
|
+
**kwargs,
|
|
29
|
+
):
|
|
30
|
+
super().__init__(chunk=False, chunking_strategy=None, **kwargs)
|
|
31
|
+
self.chunk_title = chunk_title
|
|
32
|
+
self.field_names = field_names or []
|
|
33
|
+
self.format_headers = format_headers
|
|
34
|
+
self.skip_empty_fields = skip_empty_fields
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def get_supported_chunking_strategies(cls) -> List[ChunkingStrategyType]:
|
|
38
|
+
"""Chunking is not supported - each row is already a logical document unit."""
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def get_supported_content_types(cls) -> List[ContentType]:
|
|
43
|
+
"""Get the list of supported content types."""
|
|
44
|
+
return [ContentType.CSV, ContentType.XLSX, ContentType.XLS]
|
|
45
|
+
|
|
46
|
+
def _format_field_name(self, field_name: str) -> str:
|
|
47
|
+
"""Format field name to be more readable."""
|
|
48
|
+
if not self.format_headers:
|
|
49
|
+
return field_name.strip()
|
|
50
|
+
|
|
51
|
+
# Replace underscores with spaces and title case
|
|
52
|
+
formatted = field_name.replace("_", " ").strip().title()
|
|
53
|
+
return formatted
|
|
54
|
+
|
|
55
|
+
def _get_title_for_entry(self, entry_index: int) -> Optional[str]:
|
|
56
|
+
"""Get title for a specific entry."""
|
|
57
|
+
if self.chunk_title is None:
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
if isinstance(self.chunk_title, str):
|
|
61
|
+
return self.chunk_title
|
|
62
|
+
|
|
63
|
+
if isinstance(self.chunk_title, list) and self.chunk_title:
|
|
64
|
+
return self.chunk_title[entry_index % len(self.chunk_title)]
|
|
65
|
+
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
def _convert_row_to_labeled_text(self, headers: List[str], row: List[str], entry_index: int) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Convert a CSV row to field-labeled text format.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
headers: Column headers
|
|
74
|
+
row: Data row values
|
|
75
|
+
entry_index: Index of this entry (for title rotation)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Formatted text with field labels
|
|
79
|
+
"""
|
|
80
|
+
lines = []
|
|
81
|
+
|
|
82
|
+
title = self._get_title_for_entry(entry_index)
|
|
83
|
+
if title:
|
|
84
|
+
lines.append(title)
|
|
85
|
+
|
|
86
|
+
for i, (header, value) in enumerate(zip(headers, row)):
|
|
87
|
+
clean_value = value.strip() if value else ""
|
|
88
|
+
|
|
89
|
+
if self.skip_empty_fields and not clean_value:
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
if self.field_names and i < len(self.field_names):
|
|
93
|
+
field_name = self.field_names[i]
|
|
94
|
+
else:
|
|
95
|
+
field_name = self._format_field_name(header)
|
|
96
|
+
|
|
97
|
+
lines.append(f"{field_name}: {clean_value}")
|
|
98
|
+
|
|
99
|
+
return "\n".join(lines)
|
|
100
|
+
|
|
101
|
+
def read(
|
|
102
|
+
self, file: Union[Path, IO[Any]], delimiter: str = ",", quotechar: str = '"', name: Optional[str] = None
|
|
103
|
+
) -> List[Document]:
|
|
104
|
+
try:
|
|
105
|
+
if isinstance(file, Path):
|
|
106
|
+
if not file.exists():
|
|
107
|
+
raise FileNotFoundError(f"Could not find file: {file}")
|
|
108
|
+
log_debug(f"Reading: {file}")
|
|
109
|
+
file_content = file.open(newline="", mode="r", encoding=self.encoding or "utf-8")
|
|
110
|
+
else:
|
|
111
|
+
log_debug(f"Reading retrieved file: {name or file.name}")
|
|
112
|
+
file.seek(0)
|
|
113
|
+
file_content = io.StringIO(file.read().decode("utf-8")) # type: ignore
|
|
114
|
+
|
|
115
|
+
csv_name = name or (
|
|
116
|
+
Path(file.name).stem
|
|
117
|
+
if isinstance(file, Path)
|
|
118
|
+
else (getattr(file, "name", "csv_file").split(".")[0] if hasattr(file, "name") else "csv_file")
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
documents = []
|
|
122
|
+
|
|
123
|
+
with file_content as csvfile:
|
|
124
|
+
csv_reader = csv.reader(csvfile, delimiter=delimiter, quotechar=quotechar)
|
|
125
|
+
|
|
126
|
+
# Read all rows
|
|
127
|
+
rows = list(csv_reader)
|
|
128
|
+
|
|
129
|
+
if not rows:
|
|
130
|
+
log_warning("CSV file is empty")
|
|
131
|
+
return []
|
|
132
|
+
|
|
133
|
+
# First row is headers
|
|
134
|
+
headers = [header.strip() for header in rows[0]]
|
|
135
|
+
log_debug(f"Found {len(headers)} headers: {headers}")
|
|
136
|
+
|
|
137
|
+
data_rows = rows[1:] if len(rows) > 1 else []
|
|
138
|
+
log_debug(f"Processing {len(data_rows)} data rows")
|
|
139
|
+
|
|
140
|
+
for row_index, row in enumerate(data_rows):
|
|
141
|
+
# Ensure row has same length as headers (pad or truncate)
|
|
142
|
+
normalized_row = row[: len(headers)] # Truncate if too long
|
|
143
|
+
while len(normalized_row) < len(headers): # Pad if too short
|
|
144
|
+
normalized_row.append("")
|
|
145
|
+
|
|
146
|
+
# Convert row to labeled text
|
|
147
|
+
labeled_text = self._convert_row_to_labeled_text(headers, normalized_row, row_index)
|
|
148
|
+
|
|
149
|
+
if labeled_text.strip():
|
|
150
|
+
# Create document for this row
|
|
151
|
+
doc_id = f"{csv_name}_row_{row_index + 1}"
|
|
152
|
+
|
|
153
|
+
document = Document(
|
|
154
|
+
id=doc_id,
|
|
155
|
+
name=csv_name,
|
|
156
|
+
meta_data={
|
|
157
|
+
"row_index": row_index,
|
|
158
|
+
"headers": headers,
|
|
159
|
+
"total_rows": len(data_rows),
|
|
160
|
+
"source": "field_labeled_csv_reader",
|
|
161
|
+
},
|
|
162
|
+
content=labeled_text,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
documents.append(document)
|
|
166
|
+
log_debug(f"Created document for row {row_index + 1}: {len(labeled_text)} chars")
|
|
167
|
+
|
|
168
|
+
log_debug(f"Successfully created {len(documents)} labeled documents from CSV")
|
|
169
|
+
return documents
|
|
170
|
+
|
|
171
|
+
except Exception as e:
|
|
172
|
+
log_error(f"Error reading: {getattr(file, 'name', str(file)) if isinstance(file, IO) else file}: {e}")
|
|
173
|
+
return []
|
|
174
|
+
|
|
175
|
+
async def async_read(
|
|
176
|
+
self,
|
|
177
|
+
file: Union[Path, IO[Any]],
|
|
178
|
+
delimiter: str = ",",
|
|
179
|
+
quotechar: str = '"',
|
|
180
|
+
page_size: int = 1000,
|
|
181
|
+
name: Optional[str] = None,
|
|
182
|
+
) -> List[Document]:
|
|
183
|
+
try:
|
|
184
|
+
# Handle file input
|
|
185
|
+
if isinstance(file, Path):
|
|
186
|
+
if not file.exists():
|
|
187
|
+
raise FileNotFoundError(f"Could not find file: {file}")
|
|
188
|
+
log_debug(f"Reading async: {file}")
|
|
189
|
+
async with aiofiles.open(file, mode="r", encoding=self.encoding or "utf-8", newline="") as file_content:
|
|
190
|
+
content = await file_content.read()
|
|
191
|
+
file_content_io = io.StringIO(content)
|
|
192
|
+
else:
|
|
193
|
+
log_debug(f"Reading retrieved file async: {name or file.name}")
|
|
194
|
+
file.seek(0)
|
|
195
|
+
file_content_io = io.StringIO(file.read().decode("utf-8")) # type: ignore
|
|
196
|
+
|
|
197
|
+
csv_name = name or (
|
|
198
|
+
Path(file.name).stem
|
|
199
|
+
if isinstance(file, Path)
|
|
200
|
+
else (getattr(file, "name", "csv_file").split(".")[0] if hasattr(file, "name") else "csv_file")
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
file_content_io.seek(0)
|
|
204
|
+
csv_reader = csv.reader(file_content_io, delimiter=delimiter, quotechar=quotechar)
|
|
205
|
+
rows = list(csv_reader)
|
|
206
|
+
|
|
207
|
+
if not rows:
|
|
208
|
+
log_warning("CSV file is empty")
|
|
209
|
+
return []
|
|
210
|
+
|
|
211
|
+
# First row is headers
|
|
212
|
+
headers = [header.strip() for header in rows[0]]
|
|
213
|
+
log_debug(f"Found {len(headers)} headers: {headers}")
|
|
214
|
+
|
|
215
|
+
# Process data rows
|
|
216
|
+
data_rows = rows[1:] if len(rows) > 1 else []
|
|
217
|
+
total_rows = len(data_rows)
|
|
218
|
+
log_debug(f"Processing {total_rows} data rows")
|
|
219
|
+
|
|
220
|
+
# For small files, process all at once
|
|
221
|
+
if total_rows <= 10:
|
|
222
|
+
documents = []
|
|
223
|
+
for row_index, row in enumerate(data_rows):
|
|
224
|
+
normalized_row = row[: len(headers)]
|
|
225
|
+
while len(normalized_row) < len(headers):
|
|
226
|
+
normalized_row.append("")
|
|
227
|
+
|
|
228
|
+
labeled_text = self._convert_row_to_labeled_text(headers, normalized_row, row_index)
|
|
229
|
+
|
|
230
|
+
if labeled_text.strip():
|
|
231
|
+
document = Document(
|
|
232
|
+
id=f"{csv_name}_row_{row_index + 1}",
|
|
233
|
+
name=csv_name,
|
|
234
|
+
meta_data={
|
|
235
|
+
"row_index": row_index,
|
|
236
|
+
"headers": headers,
|
|
237
|
+
"total_rows": total_rows,
|
|
238
|
+
"source": "field_labeled_csv_reader",
|
|
239
|
+
},
|
|
240
|
+
content=labeled_text,
|
|
241
|
+
)
|
|
242
|
+
documents.append(document)
|
|
243
|
+
else:
|
|
244
|
+
pages = []
|
|
245
|
+
for i in range(0, total_rows, page_size):
|
|
246
|
+
pages.append(data_rows[i : i + page_size])
|
|
247
|
+
|
|
248
|
+
async def _process_page(page_number: int, page_rows: List[List[str]]) -> List[Document]:
|
|
249
|
+
"""Process a page of rows into documents"""
|
|
250
|
+
page_documents = []
|
|
251
|
+
start_row_index = (page_number - 1) * page_size
|
|
252
|
+
|
|
253
|
+
for i, row in enumerate(page_rows):
|
|
254
|
+
row_index = start_row_index + i
|
|
255
|
+
|
|
256
|
+
normalized_row = row[: len(headers)]
|
|
257
|
+
while len(normalized_row) < len(headers):
|
|
258
|
+
normalized_row.append("")
|
|
259
|
+
|
|
260
|
+
labeled_text = self._convert_row_to_labeled_text(headers, normalized_row, row_index)
|
|
261
|
+
|
|
262
|
+
if labeled_text.strip():
|
|
263
|
+
document = Document(
|
|
264
|
+
id=f"{csv_name}_row_{row_index + 1}",
|
|
265
|
+
name=csv_name,
|
|
266
|
+
meta_data={
|
|
267
|
+
"row_index": row_index,
|
|
268
|
+
"headers": headers,
|
|
269
|
+
"total_rows": total_rows,
|
|
270
|
+
"page": page_number,
|
|
271
|
+
"source": "field_labeled_csv_reader",
|
|
272
|
+
},
|
|
273
|
+
content=labeled_text,
|
|
274
|
+
)
|
|
275
|
+
page_documents.append(document)
|
|
276
|
+
|
|
277
|
+
return page_documents
|
|
278
|
+
|
|
279
|
+
page_results = await asyncio.gather(
|
|
280
|
+
*[_process_page(page_number, page) for page_number, page in enumerate(pages, start=1)]
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
documents = [doc for page_docs in page_results for doc in page_docs]
|
|
284
|
+
|
|
285
|
+
log_debug(f"Successfully created {len(documents)} labeled documents from CSV")
|
|
286
|
+
return documents
|
|
287
|
+
|
|
288
|
+
except Exception as e:
|
|
289
|
+
log_error(f"Error reading async: {getattr(file, 'name', str(file)) if isinstance(file, IO) else file}: {e}")
|
|
290
|
+
return []
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Dict, List, Literal, Optional
|
|
4
|
+
|
|
5
|
+
from agno.knowledge.chunking.semantic import SemanticChunking
|
|
6
|
+
from agno.knowledge.chunking.strategy import ChunkingStrategy, ChunkingStrategyType
|
|
7
|
+
from agno.knowledge.document.base import Document
|
|
8
|
+
from agno.knowledge.reader.base import Reader
|
|
9
|
+
from agno.knowledge.types import ContentType
|
|
10
|
+
from agno.utils.log import log_debug, logger
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from firecrawl import FirecrawlApp # type: ignore[attr-defined]
|
|
14
|
+
except ImportError:
|
|
15
|
+
raise ImportError("The `firecrawl` package is not installed. Please install it via `pip install firecrawl-py`.")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class FirecrawlReader(Reader):
|
|
20
|
+
api_key: Optional[str] = None
|
|
21
|
+
params: Optional[Dict] = None
|
|
22
|
+
mode: Literal["scrape", "crawl"] = "scrape"
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
api_key: Optional[str] = None,
|
|
27
|
+
params: Optional[Dict] = None,
|
|
28
|
+
mode: Literal["scrape", "crawl"] = "scrape",
|
|
29
|
+
chunk: bool = True,
|
|
30
|
+
chunk_size: int = 5000,
|
|
31
|
+
chunking_strategy: Optional[ChunkingStrategy] = SemanticChunking(),
|
|
32
|
+
name: Optional[str] = None,
|
|
33
|
+
description: Optional[str] = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
# Initialise base Reader (handles chunk_size / strategy)
|
|
36
|
+
super().__init__(
|
|
37
|
+
chunk=chunk, chunk_size=chunk_size, chunking_strategy=chunking_strategy, name=name, description=description
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Firecrawl-specific attributes
|
|
41
|
+
self.api_key = api_key
|
|
42
|
+
self.params = params
|
|
43
|
+
self.mode = mode
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def get_supported_chunking_strategies(self) -> List[ChunkingStrategyType]:
|
|
47
|
+
"""Get the list of supported chunking strategies for Firecrawl readers."""
|
|
48
|
+
return [
|
|
49
|
+
ChunkingStrategyType.SEMANTIC_CHUNKER,
|
|
50
|
+
ChunkingStrategyType.FIXED_SIZE_CHUNKER,
|
|
51
|
+
ChunkingStrategyType.AGENTIC_CHUNKER,
|
|
52
|
+
ChunkingStrategyType.DOCUMENT_CHUNKER,
|
|
53
|
+
ChunkingStrategyType.RECURSIVE_CHUNKER,
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def get_supported_content_types(self) -> List[ContentType]:
|
|
58
|
+
return [ContentType.URL]
|
|
59
|
+
|
|
60
|
+
def scrape(self, url: str, name: Optional[str] = None) -> List[Document]:
|
|
61
|
+
"""
|
|
62
|
+
Scrapes a website and returns a list of documents.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
url: The URL of the website to scrape
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
A list of documents
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
log_debug(f"Scraping: {url}")
|
|
72
|
+
|
|
73
|
+
app = FirecrawlApp(api_key=self.api_key)
|
|
74
|
+
|
|
75
|
+
if self.params:
|
|
76
|
+
scraped_data = app.scrape_url(url, **self.params)
|
|
77
|
+
else:
|
|
78
|
+
scraped_data = app.scrape_url(url)
|
|
79
|
+
if isinstance(scraped_data, dict):
|
|
80
|
+
content = scraped_data.get("markdown", "")
|
|
81
|
+
else:
|
|
82
|
+
content = getattr(scraped_data, "markdown", "")
|
|
83
|
+
|
|
84
|
+
# Debug logging
|
|
85
|
+
log_debug(f"Received content type: {type(content)}")
|
|
86
|
+
log_debug(f"Content empty: {not bool(content)}")
|
|
87
|
+
|
|
88
|
+
# Ensure content is a string
|
|
89
|
+
if content is None:
|
|
90
|
+
content = "" # or you could use metadata to create a meaningful message
|
|
91
|
+
logger.warning(f"No content received for URL: {url}")
|
|
92
|
+
|
|
93
|
+
documents = []
|
|
94
|
+
if self.chunk and content: # Only chunk if there's content
|
|
95
|
+
documents.extend(self.chunk_document(Document(name=name or url, id=url, content=content)))
|
|
96
|
+
else:
|
|
97
|
+
documents.append(Document(name=name or url, id=url, content=content))
|
|
98
|
+
return documents
|
|
99
|
+
|
|
100
|
+
async def async_scrape(self, url: str, name: Optional[str] = None) -> List[Document]:
|
|
101
|
+
"""
|
|
102
|
+
Asynchronously scrapes a website and returns a list of documents.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
url: The URL of the website to scrape
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
A list of documents
|
|
109
|
+
"""
|
|
110
|
+
log_debug(f"Async scraping: {url}")
|
|
111
|
+
|
|
112
|
+
# Use asyncio.to_thread to run the synchronous scrape in a thread
|
|
113
|
+
return await asyncio.to_thread(self.scrape, url, name)
|
|
114
|
+
|
|
115
|
+
def crawl(self, url: str, name: Optional[str] = None) -> List[Document]:
|
|
116
|
+
"""
|
|
117
|
+
Crawls a website and returns a list of documents.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
url: The URL of the website to crawl
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
A list of documents
|
|
124
|
+
"""
|
|
125
|
+
log_debug(f"Crawling: {url}")
|
|
126
|
+
|
|
127
|
+
app = FirecrawlApp(api_key=self.api_key)
|
|
128
|
+
|
|
129
|
+
if self.params:
|
|
130
|
+
crawl_result = app.crawl_url(url, **self.params)
|
|
131
|
+
else:
|
|
132
|
+
crawl_result = app.crawl_url(url)
|
|
133
|
+
documents = []
|
|
134
|
+
|
|
135
|
+
if isinstance(crawl_result, dict):
|
|
136
|
+
results_data = crawl_result.get("data", [])
|
|
137
|
+
else:
|
|
138
|
+
results_data = getattr(crawl_result, "data", [])
|
|
139
|
+
for result in results_data:
|
|
140
|
+
# Get markdown content, default to empty string if not found
|
|
141
|
+
if isinstance(result, dict):
|
|
142
|
+
content = result.get("markdown", "")
|
|
143
|
+
else:
|
|
144
|
+
content = getattr(result, "markdown", "")
|
|
145
|
+
|
|
146
|
+
if content: # Only create document if content exists
|
|
147
|
+
if self.chunk:
|
|
148
|
+
documents.extend(self.chunk_document(Document(name=name or url, id=url, content=content)))
|
|
149
|
+
else:
|
|
150
|
+
documents.append(Document(name=name or url, id=url, content=content))
|
|
151
|
+
|
|
152
|
+
return documents
|
|
153
|
+
|
|
154
|
+
async def async_crawl(self, url: str, name: Optional[str] = None) -> List[Document]:
|
|
155
|
+
"""
|
|
156
|
+
Asynchronously crawls a website and returns a list of documents.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
url: The URL of the website to crawl
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
A list of documents
|
|
163
|
+
"""
|
|
164
|
+
log_debug(f"Async crawling: {url}")
|
|
165
|
+
|
|
166
|
+
# Use asyncio.to_thread to run the synchronous crawl in a thread
|
|
167
|
+
return await asyncio.to_thread(self.crawl, url, name)
|
|
168
|
+
|
|
169
|
+
def read(self, url: str, name: Optional[str] = None) -> List[Document]:
|
|
170
|
+
"""
|
|
171
|
+
Reads from a URL based on the mode setting.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
url: The URL of the website to process
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
A list of documents
|
|
178
|
+
"""
|
|
179
|
+
if self.mode == "scrape":
|
|
180
|
+
return self.scrape(url, name)
|
|
181
|
+
elif self.mode == "crawl":
|
|
182
|
+
return self.crawl(url, name)
|
|
183
|
+
else:
|
|
184
|
+
raise NotImplementedError(f"Mode {self.mode} not implemented")
|
|
185
|
+
|
|
186
|
+
async def async_read(self, url: str, name: Optional[str] = None) -> List[Document]:
|
|
187
|
+
"""
|
|
188
|
+
Asynchronously reads from a URL based on the mode setting.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
url: The URL of the website to process
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
A list of documents
|
|
195
|
+
"""
|
|
196
|
+
if self.mode == "scrape":
|
|
197
|
+
return await self.async_scrape(url, name)
|
|
198
|
+
elif self.mode == "crawl":
|
|
199
|
+
return await self.async_crawl(url, name)
|
|
200
|
+
else:
|
|
201
|
+
raise NotImplementedError(f"Mode {self.mode} not implemented")
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import IO, Any, List, Optional, Union
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
from agno.knowledge.chunking.fixed import FixedSizeChunking
|
|
9
|
+
from agno.knowledge.chunking.strategy import ChunkingStrategy, ChunkingStrategyType
|
|
10
|
+
from agno.knowledge.document.base import Document
|
|
11
|
+
from agno.knowledge.reader.base import Reader
|
|
12
|
+
from agno.knowledge.types import ContentType
|
|
13
|
+
from agno.utils.log import log_debug, log_error
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JSONReader(Reader):
|
|
17
|
+
"""Reader for JSON files"""
|
|
18
|
+
|
|
19
|
+
chunk: bool = False
|
|
20
|
+
|
|
21
|
+
def __init__(self, chunking_strategy: Optional[ChunkingStrategy] = FixedSizeChunking(), **kwargs):
|
|
22
|
+
super().__init__(chunking_strategy=chunking_strategy, **kwargs)
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def get_supported_chunking_strategies(self) -> List[ChunkingStrategyType]:
|
|
26
|
+
"""Get the list of supported chunking strategies for JSON readers."""
|
|
27
|
+
return [
|
|
28
|
+
ChunkingStrategyType.FIXED_SIZE_CHUNKER,
|
|
29
|
+
ChunkingStrategyType.AGENTIC_CHUNKER,
|
|
30
|
+
ChunkingStrategyType.DOCUMENT_CHUNKER,
|
|
31
|
+
ChunkingStrategyType.RECURSIVE_CHUNKER,
|
|
32
|
+
ChunkingStrategyType.SEMANTIC_CHUNKER,
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def get_supported_content_types(self) -> List[ContentType]:
|
|
37
|
+
return [ContentType.JSON]
|
|
38
|
+
|
|
39
|
+
def read(self, path: Union[Path, IO[Any]], name: Optional[str] = None) -> List[Document]:
|
|
40
|
+
try:
|
|
41
|
+
if isinstance(path, Path):
|
|
42
|
+
if not path.exists():
|
|
43
|
+
raise FileNotFoundError(f"Could not find file: {path}")
|
|
44
|
+
log_debug(f"Reading: {path}")
|
|
45
|
+
json_name = name or path.name.split(".")[0]
|
|
46
|
+
json_contents = json.loads(path.read_text(self.encoding or "utf-8"))
|
|
47
|
+
|
|
48
|
+
elif isinstance(path, BytesIO):
|
|
49
|
+
json_name = name or path.name.split(".")[0]
|
|
50
|
+
log_debug(f"Reading uploaded file: {json_name}")
|
|
51
|
+
path.seek(0)
|
|
52
|
+
json_contents = json.load(path)
|
|
53
|
+
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError("Unsupported file type. Must be Path or BytesIO.")
|
|
56
|
+
|
|
57
|
+
if isinstance(json_contents, dict):
|
|
58
|
+
json_contents = [json_contents]
|
|
59
|
+
|
|
60
|
+
documents = [
|
|
61
|
+
Document(
|
|
62
|
+
name=json_name,
|
|
63
|
+
id=str(uuid4()),
|
|
64
|
+
meta_data={"page": page_number},
|
|
65
|
+
content=json.dumps(content),
|
|
66
|
+
)
|
|
67
|
+
for page_number, content in enumerate(json_contents, start=1)
|
|
68
|
+
]
|
|
69
|
+
if self.chunk:
|
|
70
|
+
chunked_documents = []
|
|
71
|
+
for document in documents:
|
|
72
|
+
chunked_documents.extend(self.chunk_document(document))
|
|
73
|
+
return chunked_documents
|
|
74
|
+
return documents
|
|
75
|
+
except Exception as e:
|
|
76
|
+
log_error(f"Error reading: {path}: {e}")
|
|
77
|
+
raise
|
|
78
|
+
|
|
79
|
+
async def async_read(self, path: Union[Path, IO[Any]], name: Optional[str] = None) -> List[Document]:
|
|
80
|
+
"""Asynchronously read JSON files.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
path (Union[Path, IO[Any]]): Path to a JSON file or a file-like object
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List[Document]: List of documents from the JSON file
|
|
87
|
+
"""
|
|
88
|
+
return await asyncio.to_thread(self.read, path, name)
|