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
agno/tools/file.py
CHANGED
|
@@ -1,30 +1,55 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Any, List, Optional, Tuple
|
|
4
4
|
|
|
5
5
|
from agno.tools import Toolkit
|
|
6
|
-
from agno.utils.log import
|
|
6
|
+
from agno.utils.log import log_debug, log_error
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class FileTools(Toolkit):
|
|
10
10
|
def __init__(
|
|
11
11
|
self,
|
|
12
12
|
base_dir: Optional[Path] = None,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
enable_save_file: bool = True,
|
|
14
|
+
enable_read_file: bool = True,
|
|
15
|
+
enable_delete_file: bool = False,
|
|
16
|
+
enable_list_files: bool = True,
|
|
17
|
+
enable_search_files: bool = True,
|
|
18
|
+
enable_read_file_chunk: bool = True,
|
|
19
|
+
enable_replace_file_chunk: bool = True,
|
|
20
|
+
expose_base_directory: bool = False,
|
|
21
|
+
max_file_length: int = 10000000,
|
|
22
|
+
max_file_lines: int = 100000,
|
|
23
|
+
line_separator: str = "\n",
|
|
24
|
+
all: bool = False,
|
|
25
|
+
**kwargs,
|
|
16
26
|
):
|
|
17
|
-
super().__init__(name="file_tools")
|
|
18
|
-
|
|
19
27
|
self.base_dir: Path = base_dir or Path.cwd()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
self.base_dir = self.base_dir.resolve()
|
|
29
|
+
|
|
30
|
+
tools: List[Any] = []
|
|
31
|
+
self.max_file_length = max_file_length
|
|
32
|
+
self.max_file_lines = max_file_lines
|
|
33
|
+
self.line_separator = line_separator
|
|
34
|
+
self.expose_base_directory = expose_base_directory
|
|
35
|
+
if all or enable_save_file:
|
|
36
|
+
tools.append(self.save_file)
|
|
37
|
+
if all or enable_read_file:
|
|
38
|
+
tools.append(self.read_file)
|
|
39
|
+
if all or enable_list_files:
|
|
40
|
+
tools.append(self.list_files)
|
|
41
|
+
if all or enable_search_files:
|
|
42
|
+
tools.append(self.search_files)
|
|
43
|
+
if all or enable_delete_file:
|
|
44
|
+
tools.append(self.delete_file)
|
|
45
|
+
if all or enable_read_file_chunk:
|
|
46
|
+
tools.append(self.read_file_chunk)
|
|
47
|
+
if all or enable_replace_file_chunk:
|
|
48
|
+
tools.append(self.replace_file_chunk)
|
|
49
|
+
|
|
50
|
+
super().__init__(name="file_tools", tools=tools, **kwargs)
|
|
51
|
+
|
|
52
|
+
def save_file(self, contents: str, file_name: str, overwrite: bool = True, encoding: str = "utf-8") -> str:
|
|
28
53
|
"""Saves the contents to a file called `file_name` and returns the file name if successful.
|
|
29
54
|
|
|
30
55
|
:param contents: The contents to save.
|
|
@@ -33,42 +58,183 @@ class FileTools(Toolkit):
|
|
|
33
58
|
:return: The file name if successful, otherwise returns an error message.
|
|
34
59
|
"""
|
|
35
60
|
try:
|
|
36
|
-
file_path = self.
|
|
37
|
-
|
|
61
|
+
safe, file_path = self.check_escape(file_name)
|
|
62
|
+
if not (safe):
|
|
63
|
+
log_error(f"Attempted to save file: {file_name}")
|
|
64
|
+
return "Error saving file"
|
|
65
|
+
log_debug(f"Saving contents to {file_path}")
|
|
38
66
|
if not file_path.parent.exists():
|
|
39
67
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
40
68
|
if file_path.exists() and not overwrite:
|
|
41
69
|
return f"File {file_name} already exists"
|
|
42
|
-
file_path.write_text(contents)
|
|
43
|
-
|
|
70
|
+
file_path.write_text(contents, encoding=encoding)
|
|
71
|
+
log_debug(f"Saved: {file_path}")
|
|
44
72
|
return str(file_name)
|
|
45
73
|
except Exception as e:
|
|
46
|
-
|
|
74
|
+
log_error(f"Error saving to file: {e}")
|
|
47
75
|
return f"Error saving to file: {e}"
|
|
48
76
|
|
|
49
|
-
def
|
|
77
|
+
def read_file_chunk(self, file_name: str, start_line: int, end_line: int, encoding: str = "utf-8") -> str:
|
|
78
|
+
"""Reads the contents of the file `file_name` and returns lines from start_line to end_line.
|
|
79
|
+
|
|
80
|
+
:param file_name: The name of the file to read.
|
|
81
|
+
:param start_line: Number of first line in the returned chunk
|
|
82
|
+
:param end_line: Number of the last line in the returned chunk
|
|
83
|
+
:param encoding: Encoding to use, default - utf-8
|
|
84
|
+
|
|
85
|
+
:return: The contents of the selected chunk
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
log_debug(f"Reading file: {file_name}")
|
|
89
|
+
safe, file_path = self.check_escape(file_name)
|
|
90
|
+
if not (safe):
|
|
91
|
+
log_error(f"Attempted to read file: {file_name}")
|
|
92
|
+
return "Error reading file"
|
|
93
|
+
contents = file_path.read_text(encoding=encoding)
|
|
94
|
+
lines = contents.split(self.line_separator)
|
|
95
|
+
return self.line_separator.join(lines[start_line : end_line + 1])
|
|
96
|
+
except Exception as e:
|
|
97
|
+
log_error(f"Error reading file: {e}")
|
|
98
|
+
return f"Error reading file: {e}"
|
|
99
|
+
|
|
100
|
+
def replace_file_chunk(
|
|
101
|
+
self, file_name: str, start_line: int, end_line: int, chunk: str, encoding: str = "utf-8"
|
|
102
|
+
) -> str:
|
|
103
|
+
"""Reads the contents of the file, replaces lines
|
|
104
|
+
between start_line and end_line with chunk and writes the file
|
|
105
|
+
|
|
106
|
+
:param file_name: The name of the file to process.
|
|
107
|
+
:param start_line: Number of first line in the replaced chunk
|
|
108
|
+
:param end_line: Number of the last line in the replaced chunk
|
|
109
|
+
:param chunk: String to be inserted instead of lines from start_line to end_line. Can have multiple lines.
|
|
110
|
+
:param encoding: Encoding to use, default - utf-8
|
|
111
|
+
|
|
112
|
+
:return: file name if successfull, error message otherwise
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
log_debug(f"Patching file: {file_name}")
|
|
116
|
+
safe, file_path = self.check_escape(file_name)
|
|
117
|
+
if not (safe):
|
|
118
|
+
log_error(f"Attempted to read file: {file_name}")
|
|
119
|
+
return "Error reading file"
|
|
120
|
+
contents = file_path.read_text(encoding=encoding)
|
|
121
|
+
lines = contents.split(self.line_separator)
|
|
122
|
+
start = lines[0:start_line]
|
|
123
|
+
end = lines[end_line + 1 :]
|
|
124
|
+
return self.save_file(
|
|
125
|
+
file_name=file_name, contents=self.line_separator.join(start + [chunk] + end), encoding=encoding
|
|
126
|
+
)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
log_error(f"Error patching file: {e}")
|
|
129
|
+
return f"Error patching file: {e}"
|
|
130
|
+
|
|
131
|
+
def read_file(self, file_name: str, encoding: str = "utf-8") -> str:
|
|
50
132
|
"""Reads the contents of the file `file_name` and returns the contents if successful.
|
|
51
133
|
|
|
52
134
|
:param file_name: The name of the file to read.
|
|
135
|
+
:param encoding: Encoding to use, default - utf-8
|
|
53
136
|
:return: The contents of the file if successful, otherwise returns an error message.
|
|
54
137
|
"""
|
|
55
138
|
try:
|
|
56
|
-
|
|
57
|
-
file_path = self.
|
|
58
|
-
|
|
139
|
+
log_debug(f"Reading file: {file_name}")
|
|
140
|
+
safe, file_path = self.check_escape(file_name)
|
|
141
|
+
if not (safe):
|
|
142
|
+
log_error(f"Attempted to read file: {file_name}")
|
|
143
|
+
return "Error reading file"
|
|
144
|
+
contents = file_path.read_text(encoding=encoding)
|
|
145
|
+
if len(contents) > self.max_file_length:
|
|
146
|
+
return "Error reading file: file too long. Use read_file_chunk instead"
|
|
147
|
+
if len(contents.split(self.line_separator)) > self.max_file_lines:
|
|
148
|
+
return "Error reading file: file too long. Use read_file_chunk instead"
|
|
149
|
+
|
|
59
150
|
return str(contents)
|
|
60
151
|
except Exception as e:
|
|
61
|
-
|
|
152
|
+
log_error(f"Error reading file: {e}")
|
|
62
153
|
return f"Error reading file: {e}"
|
|
63
154
|
|
|
64
|
-
def
|
|
65
|
-
"""
|
|
155
|
+
def delete_file(self, file_name: str) -> str:
|
|
156
|
+
"""Deletes a file
|
|
157
|
+
:param file_name: Name of the file to delete
|
|
158
|
+
|
|
159
|
+
:return: Empty string, if operation succeeded, otherwise returns an error message
|
|
160
|
+
"""
|
|
161
|
+
safe, path = self.check_escape(file_name)
|
|
162
|
+
try:
|
|
163
|
+
if safe:
|
|
164
|
+
if path.is_dir():
|
|
165
|
+
path.rmdir()
|
|
166
|
+
return ""
|
|
167
|
+
path.unlink()
|
|
168
|
+
return ""
|
|
169
|
+
else:
|
|
170
|
+
log_error(f"Attempt to delete file outside {self.base_dir}: {file_name}")
|
|
171
|
+
return "Incorrect file_name"
|
|
172
|
+
except Exception as e:
|
|
173
|
+
log_error(f"Error removing {file_name}: {e}")
|
|
174
|
+
return f"Error removing file: {e}"
|
|
175
|
+
|
|
176
|
+
def check_escape(self, relative_path: str) -> Tuple[bool, Path]:
|
|
177
|
+
d = self.base_dir.joinpath(Path(relative_path)).resolve()
|
|
178
|
+
if self.base_dir == d:
|
|
179
|
+
return True, d
|
|
180
|
+
try:
|
|
181
|
+
d.relative_to(self.base_dir)
|
|
182
|
+
except ValueError:
|
|
183
|
+
log_error("Attempted to escape base_dir")
|
|
184
|
+
return False, self.base_dir
|
|
185
|
+
return True, d
|
|
186
|
+
|
|
187
|
+
def list_files(self, **kwargs) -> str:
|
|
188
|
+
"""Returns a list of files in directory
|
|
189
|
+
:param directory: (Optional) name of directory to list.
|
|
66
190
|
|
|
67
191
|
:return: The contents of the file if successful, otherwise returns an error message.
|
|
68
192
|
"""
|
|
193
|
+
directory = kwargs.get("directory", ".")
|
|
69
194
|
try:
|
|
70
|
-
|
|
71
|
-
|
|
195
|
+
log_debug(f"Reading files in : {self.base_dir}/{directory}")
|
|
196
|
+
safe, d = self.check_escape(directory)
|
|
197
|
+
if safe:
|
|
198
|
+
return json.dumps([str(file_path.relative_to(self.base_dir)) for file_path in d.iterdir()], indent=4)
|
|
199
|
+
else:
|
|
200
|
+
return "{}"
|
|
72
201
|
except Exception as e:
|
|
73
|
-
|
|
202
|
+
log_error(f"Error reading files: {e}")
|
|
74
203
|
return f"Error reading files: {e}"
|
|
204
|
+
|
|
205
|
+
def search_files(self, pattern: str) -> str:
|
|
206
|
+
"""Searches for files in the base directory that match the pattern
|
|
207
|
+
|
|
208
|
+
:param pattern: The pattern to search for, e.g. "*.txt", "file*.csv", "**/*.py".
|
|
209
|
+
:return: JSON formatted list of matching file paths, or error message.
|
|
210
|
+
"""
|
|
211
|
+
try:
|
|
212
|
+
if not pattern or not pattern.strip():
|
|
213
|
+
return "Error: Pattern cannot be empty"
|
|
214
|
+
|
|
215
|
+
log_debug(f"Searching files in {self.base_dir} with pattern {pattern}")
|
|
216
|
+
matching_files = list(self.base_dir.glob(pattern))
|
|
217
|
+
result = None
|
|
218
|
+
if self.expose_base_directory:
|
|
219
|
+
file_paths = [str(file_path) for file_path in matching_files]
|
|
220
|
+
result = {
|
|
221
|
+
"pattern": pattern,
|
|
222
|
+
"matches_found": len(file_paths),
|
|
223
|
+
"base_directory": str(self.base_dir),
|
|
224
|
+
"files": file_paths,
|
|
225
|
+
}
|
|
226
|
+
else:
|
|
227
|
+
file_paths = [str(file_path.relative_to(self.base_dir)) for file_path in matching_files]
|
|
228
|
+
|
|
229
|
+
result = {
|
|
230
|
+
"pattern": pattern,
|
|
231
|
+
"matches_found": len(file_paths),
|
|
232
|
+
"files": file_paths,
|
|
233
|
+
}
|
|
234
|
+
log_debug(f"Found {len(file_paths)} files matching pattern {pattern}")
|
|
235
|
+
return json.dumps(result, indent=2)
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
error_msg = f"Error searching files with pattern '{pattern}': {e}"
|
|
239
|
+
log_error(error_msg)
|
|
240
|
+
return error_msg
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import io
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
from agno.media import File
|
|
9
|
+
from agno.tools import Toolkit
|
|
10
|
+
from agno.tools.function import ToolResult
|
|
11
|
+
from agno.utils.log import log_debug, logger
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from reportlab.lib.pagesizes import letter
|
|
15
|
+
from reportlab.lib.styles import getSampleStyleSheet
|
|
16
|
+
from reportlab.lib.units import inch
|
|
17
|
+
from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer
|
|
18
|
+
|
|
19
|
+
PDF_AVAILABLE = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
PDF_AVAILABLE = False
|
|
22
|
+
logger.warning("reportlab not installed. PDF generation will not be available. Install with: pip install reportlab")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FileGenerationTools(Toolkit):
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
enable_json_generation: bool = True,
|
|
29
|
+
enable_csv_generation: bool = True,
|
|
30
|
+
enable_pdf_generation: bool = True,
|
|
31
|
+
enable_txt_generation: bool = True,
|
|
32
|
+
output_directory: Optional[str] = None,
|
|
33
|
+
all: bool = False,
|
|
34
|
+
**kwargs,
|
|
35
|
+
):
|
|
36
|
+
self.enable_json_generation = enable_json_generation
|
|
37
|
+
self.enable_csv_generation = enable_csv_generation
|
|
38
|
+
self.enable_pdf_generation = enable_pdf_generation and PDF_AVAILABLE
|
|
39
|
+
self.enable_txt_generation = enable_txt_generation
|
|
40
|
+
self.output_directory = Path(output_directory) if output_directory else None
|
|
41
|
+
|
|
42
|
+
# Create output directory if specified
|
|
43
|
+
if self.output_directory:
|
|
44
|
+
self.output_directory.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
log_debug(f"Files will be saved to: {self.output_directory}")
|
|
46
|
+
|
|
47
|
+
if enable_pdf_generation and not PDF_AVAILABLE:
|
|
48
|
+
logger.warning("PDF generation requested but reportlab is not installed. Disabling PDF generation.")
|
|
49
|
+
self.enable_pdf_generation = False
|
|
50
|
+
|
|
51
|
+
tools: List[Any] = []
|
|
52
|
+
if all or enable_json_generation:
|
|
53
|
+
tools.append(self.generate_json_file)
|
|
54
|
+
if all or enable_csv_generation:
|
|
55
|
+
tools.append(self.generate_csv_file)
|
|
56
|
+
if all or (enable_pdf_generation and PDF_AVAILABLE):
|
|
57
|
+
tools.append(self.generate_pdf_file)
|
|
58
|
+
if all or enable_txt_generation:
|
|
59
|
+
tools.append(self.generate_text_file)
|
|
60
|
+
|
|
61
|
+
super().__init__(name="file_generation", tools=tools, **kwargs)
|
|
62
|
+
|
|
63
|
+
def _save_file_to_disk(self, content: Union[str, bytes], filename: str) -> Optional[str]:
|
|
64
|
+
"""Save file to disk if output_directory is set. Return file path or None."""
|
|
65
|
+
if not self.output_directory:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
file_path = self.output_directory / filename
|
|
69
|
+
|
|
70
|
+
if isinstance(content, str):
|
|
71
|
+
file_path.write_text(content, encoding="utf-8")
|
|
72
|
+
else:
|
|
73
|
+
file_path.write_bytes(content)
|
|
74
|
+
|
|
75
|
+
log_debug(f"File saved to: {file_path}")
|
|
76
|
+
return str(file_path)
|
|
77
|
+
|
|
78
|
+
def generate_json_file(self, data: Union[Dict, List, str], filename: Optional[str] = None) -> ToolResult:
|
|
79
|
+
"""Generate a JSON file from the provided data.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
data: The data to write to the JSON file. Can be a dictionary, list, or JSON string.
|
|
83
|
+
filename: Optional filename for the generated file. If not provided, a UUID will be used.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
ToolResult: Result containing the generated JSON file as a FileArtifact.
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
log_debug(f"Generating JSON file with data: {type(data)}")
|
|
90
|
+
|
|
91
|
+
# Handle different input types
|
|
92
|
+
if isinstance(data, str):
|
|
93
|
+
try:
|
|
94
|
+
json.loads(data)
|
|
95
|
+
json_content = data # Use the original string if it's valid JSON
|
|
96
|
+
except json.JSONDecodeError:
|
|
97
|
+
# If it's not valid JSON, treat as plain text and wrap it
|
|
98
|
+
json_content = json.dumps({"content": data}, indent=2)
|
|
99
|
+
else:
|
|
100
|
+
json_content = json.dumps(data, indent=2, ensure_ascii=False)
|
|
101
|
+
|
|
102
|
+
# Generate filename if not provided
|
|
103
|
+
if not filename:
|
|
104
|
+
filename = f"generated_file_{str(uuid4())[:8]}.json"
|
|
105
|
+
elif not filename.endswith(".json"):
|
|
106
|
+
filename += ".json"
|
|
107
|
+
|
|
108
|
+
# Save file to disk (if output_directory is set)
|
|
109
|
+
file_path = self._save_file_to_disk(json_content, filename)
|
|
110
|
+
|
|
111
|
+
content_bytes = json_content.encode("utf-8")
|
|
112
|
+
|
|
113
|
+
# Create FileArtifact
|
|
114
|
+
file_artifact = File(
|
|
115
|
+
id=str(uuid4()),
|
|
116
|
+
content=content_bytes,
|
|
117
|
+
mime_type="application/json",
|
|
118
|
+
file_type="json",
|
|
119
|
+
filename=filename,
|
|
120
|
+
size=len(content_bytes),
|
|
121
|
+
filepath=file_path if file_path else None,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
log_debug("JSON file generated successfully")
|
|
125
|
+
success_msg = f"JSON file '{filename}' has been generated successfully with {len(json_content)} characters."
|
|
126
|
+
if file_path:
|
|
127
|
+
success_msg += f" File saved to: {file_path}"
|
|
128
|
+
else:
|
|
129
|
+
success_msg += " File is available in response."
|
|
130
|
+
|
|
131
|
+
return ToolResult(content=success_msg, files=[file_artifact])
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error(f"Failed to generate JSON file: {e}")
|
|
135
|
+
return ToolResult(content=f"Error generating JSON file: {e}")
|
|
136
|
+
|
|
137
|
+
def generate_csv_file(
|
|
138
|
+
self,
|
|
139
|
+
data: Union[List[List], List[Dict], str],
|
|
140
|
+
filename: Optional[str] = None,
|
|
141
|
+
headers: Optional[List[str]] = None,
|
|
142
|
+
) -> ToolResult:
|
|
143
|
+
"""Generate a CSV file from the provided data.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
data: The data to write to the CSV file. Can be a list of lists, list of dictionaries, or CSV string.
|
|
147
|
+
filename: Optional filename for the generated file. If not provided, a UUID will be used.
|
|
148
|
+
headers: Optional headers for the CSV. Used when data is a list of lists.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
ToolResult: Result containing the generated CSV file as a FileArtifact.
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
log_debug(f"Generating CSV file with data: {type(data)}")
|
|
155
|
+
|
|
156
|
+
# Create CSV content
|
|
157
|
+
output = io.StringIO()
|
|
158
|
+
|
|
159
|
+
if isinstance(data, str):
|
|
160
|
+
# If it's already a CSV string, use it directly
|
|
161
|
+
csv_content = data
|
|
162
|
+
elif isinstance(data, list) and len(data) > 0:
|
|
163
|
+
writer = csv.writer(output)
|
|
164
|
+
|
|
165
|
+
if isinstance(data[0], dict):
|
|
166
|
+
# List of dictionaries - use keys as headers
|
|
167
|
+
if data:
|
|
168
|
+
fieldnames = list(data[0].keys())
|
|
169
|
+
writer.writerow(fieldnames)
|
|
170
|
+
for row in data:
|
|
171
|
+
if isinstance(row, dict):
|
|
172
|
+
writer.writerow([row.get(field, "") for field in fieldnames])
|
|
173
|
+
else:
|
|
174
|
+
writer.writerow([str(row)] + [""] * (len(fieldnames) - 1))
|
|
175
|
+
elif isinstance(data[0], list):
|
|
176
|
+
# List of lists
|
|
177
|
+
if headers:
|
|
178
|
+
writer.writerow(headers)
|
|
179
|
+
writer.writerows(data)
|
|
180
|
+
else:
|
|
181
|
+
# List of other types
|
|
182
|
+
if headers:
|
|
183
|
+
writer.writerow(headers)
|
|
184
|
+
for item in data:
|
|
185
|
+
writer.writerow([str(item)])
|
|
186
|
+
|
|
187
|
+
csv_content = output.getvalue()
|
|
188
|
+
else:
|
|
189
|
+
csv_content = ""
|
|
190
|
+
|
|
191
|
+
# Generate filename if not provided
|
|
192
|
+
if not filename:
|
|
193
|
+
filename = f"generated_file_{str(uuid4())[:8]}.csv"
|
|
194
|
+
elif not filename.endswith(".csv"):
|
|
195
|
+
filename += ".csv"
|
|
196
|
+
|
|
197
|
+
# Save file to disk (if output_directory is set)
|
|
198
|
+
file_path = self._save_file_to_disk(csv_content, filename)
|
|
199
|
+
|
|
200
|
+
content_bytes = csv_content.encode("utf-8")
|
|
201
|
+
|
|
202
|
+
# Create FileArtifact
|
|
203
|
+
file_artifact = File(
|
|
204
|
+
id=str(uuid4()),
|
|
205
|
+
content=content_bytes,
|
|
206
|
+
mime_type="text/csv",
|
|
207
|
+
file_type="csv",
|
|
208
|
+
filename=filename,
|
|
209
|
+
size=len(content_bytes),
|
|
210
|
+
filepath=file_path if file_path else None,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
log_debug("CSV file generated successfully")
|
|
214
|
+
success_msg = f"CSV file '{filename}' has been generated successfully with {len(csv_content)} characters."
|
|
215
|
+
if file_path:
|
|
216
|
+
success_msg += f" File saved to: {file_path}"
|
|
217
|
+
else:
|
|
218
|
+
success_msg += " File is available in response."
|
|
219
|
+
|
|
220
|
+
return ToolResult(content=success_msg, files=[file_artifact])
|
|
221
|
+
|
|
222
|
+
except Exception as e:
|
|
223
|
+
logger.error(f"Failed to generate CSV file: {e}")
|
|
224
|
+
return ToolResult(content=f"Error generating CSV file: {e}")
|
|
225
|
+
|
|
226
|
+
def generate_pdf_file(
|
|
227
|
+
self, content: str, filename: Optional[str] = None, title: Optional[str] = None
|
|
228
|
+
) -> ToolResult:
|
|
229
|
+
"""Generate a PDF file from the provided content.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
content: The text content to write to the PDF file.
|
|
233
|
+
filename: Optional filename for the generated file. If not provided, a UUID will be used.
|
|
234
|
+
title: Optional title for the PDF document.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
ToolResult: Result containing the generated PDF file as a FileArtifact.
|
|
238
|
+
"""
|
|
239
|
+
if not PDF_AVAILABLE:
|
|
240
|
+
return ToolResult(
|
|
241
|
+
content="PDF generation is not available. Please install reportlab: pip install reportlab"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
log_debug(f"Generating PDF file with content length: {len(content)}")
|
|
246
|
+
|
|
247
|
+
# Create PDF content in memory
|
|
248
|
+
buffer = io.BytesIO()
|
|
249
|
+
doc = SimpleDocTemplate(buffer, pagesize=letter, topMargin=1 * inch)
|
|
250
|
+
|
|
251
|
+
# Get styles
|
|
252
|
+
styles = getSampleStyleSheet()
|
|
253
|
+
title_style = styles["Title"]
|
|
254
|
+
normal_style = styles["Normal"]
|
|
255
|
+
|
|
256
|
+
# Build story (content elements)
|
|
257
|
+
story = []
|
|
258
|
+
|
|
259
|
+
if title:
|
|
260
|
+
story.append(Paragraph(title, title_style))
|
|
261
|
+
story.append(Spacer(1, 20))
|
|
262
|
+
|
|
263
|
+
# Split content into paragraphs and add to story
|
|
264
|
+
paragraphs = content.split("\n\n")
|
|
265
|
+
for para in paragraphs:
|
|
266
|
+
if para.strip():
|
|
267
|
+
# Clean the paragraph text for PDF
|
|
268
|
+
clean_para = para.strip().replace("<", "<").replace(">", ">")
|
|
269
|
+
story.append(Paragraph(clean_para, normal_style))
|
|
270
|
+
story.append(Spacer(1, 10))
|
|
271
|
+
|
|
272
|
+
# Build PDF
|
|
273
|
+
doc.build(story)
|
|
274
|
+
pdf_content = buffer.getvalue()
|
|
275
|
+
buffer.close()
|
|
276
|
+
|
|
277
|
+
# Generate filename if not provided
|
|
278
|
+
if not filename:
|
|
279
|
+
filename = f"generated_file_{str(uuid4())[:8]}.pdf"
|
|
280
|
+
elif not filename.endswith(".pdf"):
|
|
281
|
+
filename += ".pdf"
|
|
282
|
+
|
|
283
|
+
# Save file to disk (if output_directory is set)
|
|
284
|
+
file_path = self._save_file_to_disk(pdf_content, filename)
|
|
285
|
+
|
|
286
|
+
# Create FileArtifact
|
|
287
|
+
file_artifact = File(
|
|
288
|
+
id=str(uuid4()),
|
|
289
|
+
content=pdf_content,
|
|
290
|
+
mime_type="application/pdf",
|
|
291
|
+
file_type="pdf",
|
|
292
|
+
filename=filename,
|
|
293
|
+
size=len(pdf_content),
|
|
294
|
+
filepath=file_path if file_path else None,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
log_debug("PDF file generated successfully")
|
|
298
|
+
success_msg = f"PDF file '{filename}' has been generated successfully with {len(pdf_content)} bytes."
|
|
299
|
+
if file_path:
|
|
300
|
+
success_msg += f" File saved to: {file_path}"
|
|
301
|
+
else:
|
|
302
|
+
success_msg += " File is available in response."
|
|
303
|
+
|
|
304
|
+
return ToolResult(content=success_msg, files=[file_artifact])
|
|
305
|
+
|
|
306
|
+
except Exception as e:
|
|
307
|
+
logger.error(f"Failed to generate PDF file: {e}")
|
|
308
|
+
return ToolResult(content=f"Error generating PDF file: {e}")
|
|
309
|
+
|
|
310
|
+
def generate_text_file(self, content: str, filename: Optional[str] = None) -> ToolResult:
|
|
311
|
+
"""Generate a text file from the provided content.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
content: The text content to write to the file.
|
|
315
|
+
filename: Optional filename for the generated file. If not provided, a UUID will be used.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
ToolResult: Result containing the generated text file as a FileArtifact.
|
|
319
|
+
"""
|
|
320
|
+
try:
|
|
321
|
+
log_debug(f"Generating text file with content length: {len(content)}")
|
|
322
|
+
|
|
323
|
+
# Generate filename if not provided
|
|
324
|
+
if not filename:
|
|
325
|
+
filename = f"generated_file_{str(uuid4())[:8]}.txt"
|
|
326
|
+
elif not filename.endswith(".txt"):
|
|
327
|
+
filename += ".txt"
|
|
328
|
+
|
|
329
|
+
# Save file to disk (if output_directory is set)
|
|
330
|
+
file_path = self._save_file_to_disk(content, filename)
|
|
331
|
+
|
|
332
|
+
content_bytes = content.encode("utf-8")
|
|
333
|
+
|
|
334
|
+
# Create FileArtifact
|
|
335
|
+
file_artifact = File(
|
|
336
|
+
id=str(uuid4()),
|
|
337
|
+
content=content_bytes,
|
|
338
|
+
mime_type="text/plain",
|
|
339
|
+
file_type="txt",
|
|
340
|
+
filename=filename,
|
|
341
|
+
size=len(content_bytes),
|
|
342
|
+
filepath=file_path if file_path else None,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
log_debug("Text file generated successfully")
|
|
346
|
+
success_msg = f"Text file '{filename}' has been generated successfully with {len(content)} characters."
|
|
347
|
+
if file_path:
|
|
348
|
+
success_msg += f" File saved to: {file_path}"
|
|
349
|
+
else:
|
|
350
|
+
success_msg += " File is available in response."
|
|
351
|
+
|
|
352
|
+
return ToolResult(content=success_msg, files=[file_artifact])
|
|
353
|
+
|
|
354
|
+
except Exception as e:
|
|
355
|
+
logger.error(f"Failed to generate text file: {e}")
|
|
356
|
+
return ToolResult(content=f"Error generating text file: {e}")
|