agno 2.2.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 +51 -0
- agno/agent/agent.py +10405 -0
- agno/api/__init__.py +0 -0
- agno/api/agent.py +28 -0
- agno/api/api.py +40 -0
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +13 -0
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +16 -0
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/response.py +6 -0
- agno/api/schemas/team.py +16 -0
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +30 -0
- 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/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 +598 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2042 -0
- agno/db/dynamo/schemas.py +314 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1795 -0
- agno/db/firestore/schemas.py +140 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1335 -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 +1160 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1328 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/__init__.py +0 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2026 -0
- agno/db/mongo/mongo.py +1982 -0
- agno/db/mongo/schemas.py +87 -0
- agno/db/mongo/utils.py +259 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +2308 -0
- agno/db/mysql/schemas.py +138 -0
- agno/db/mysql/utils.py +355 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +1927 -0
- agno/db/postgres/postgres.py +2260 -0
- agno/db/postgres/schemas.py +139 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1660 -0
- agno/db/redis/schemas.py +123 -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 +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/schemas/metrics.py +0 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +130 -0
- agno/db/singlestore/singlestore.py +2272 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2293 -0
- agno/db/sqlite/schemas.py +133 -0
- agno/db/sqlite/sqlite.py +2288 -0
- agno/db/sqlite/utils.py +431 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1353 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +116 -0
- agno/debug.py +18 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +834 -0
- agno/eval/performance.py +773 -0
- agno/eval/reliability.py +306 -0
- agno/eval/utils.py +119 -0
- agno/exceptions.py +161 -0
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/integrations/__init__.py +0 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -0
- agno/knowledge/chunking/__init__.py +0 -0
- agno/knowledge/chunking/agentic.py +79 -0
- agno/knowledge/chunking/document.py +91 -0
- agno/knowledge/chunking/fixed.py +57 -0
- agno/knowledge/chunking/markdown.py +151 -0
- agno/knowledge/chunking/recursive.py +63 -0
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +86 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/knowledge/document/base.py +58 -0
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/knowledge/embedder/base.py +23 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/knowledge/embedder/fireworks.py +13 -0
- 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/knowledge/embedder/together.py +13 -0
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +1988 -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 +166 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +87 -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 +194 -0
- agno/knowledge/reader/text_reader.py +115 -0
- agno/knowledge/reader/web_search_reader.py +372 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/__init__.py +0 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/knowledge/reranker/base.py +14 -0
- agno/knowledge/reranker/cohere.py +64 -0
- 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 +189 -0
- agno/media.py +462 -0
- agno/memory/__init__.py +3 -0
- agno/memory/manager.py +1327 -0
- agno/models/__init__.py +0 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +45 -0
- agno/models/anthropic/__init__.py +5 -0
- agno/models/anthropic/claude.py +757 -0
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +701 -0
- agno/models/aws/claude.py +378 -0
- agno/models/azure/__init__.py +18 -0
- agno/models/azure/ai_foundry.py +485 -0
- agno/models/azure/openai_chat.py +131 -0
- agno/models/base.py +2175 -0
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +501 -0
- agno/models/cerebras/cerebras_openai.py +112 -0
- agno/models/cohere/__init__.py +5 -0
- agno/models/cohere/chat.py +389 -0
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +91 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +28 -0
- agno/models/deepseek/__init__.py +5 -0
- agno/models/deepseek/deepseek.py +61 -0
- agno/models/defaults.py +1 -0
- agno/models/fireworks/__init__.py +5 -0
- agno/models/fireworks/fireworks.py +26 -0
- agno/models/google/__init__.py +5 -0
- agno/models/google/gemini.py +1085 -0
- agno/models/groq/__init__.py +5 -0
- agno/models/groq/groq.py +556 -0
- agno/models/huggingface/__init__.py +5 -0
- agno/models/huggingface/huggingface.py +491 -0
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +422 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +26 -0
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +48 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +468 -0
- agno/models/litellm/litellm_openai.py +25 -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 +434 -0
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +475 -0
- agno/models/meta/llama_openai.py +78 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +5 -0
- agno/models/mistral/mistral.py +432 -0
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +54 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +5 -0
- agno/models/nvidia/nvidia.py +28 -0
- agno/models/ollama/__init__.py +5 -0
- agno/models/ollama/chat.py +441 -0
- agno/models/openai/__init__.py +9 -0
- agno/models/openai/chat.py +883 -0
- agno/models/openai/like.py +27 -0
- agno/models/openai/responses.py +1050 -0
- agno/models/openrouter/__init__.py +5 -0
- agno/models/openrouter/openrouter.py +66 -0
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +187 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +81 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +199 -0
- agno/models/sambanova/__init__.py +5 -0
- agno/models/sambanova/sambanova.py +28 -0
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/__init__.py +5 -0
- agno/models/together/together.py +25 -0
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +26 -0
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +70 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +78 -0
- agno/models/xai/__init__.py +3 -0
- agno/models/xai/xai.py +113 -0
- agno/os/__init__.py +3 -0
- agno/os/app.py +876 -0
- agno/os/auth.py +57 -0
- agno/os/config.py +104 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +144 -0
- agno/os/interfaces/agui/utils.py +534 -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 +211 -0
- agno/os/interfaces/whatsapp/security.py +53 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +292 -0
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +1763 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +430 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +162 -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 +997 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +515 -0
- agno/os/routers/memory/schemas.py +62 -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/schema.py +1055 -0
- agno/os/settings.py +43 -0
- agno/os/utils.py +630 -0
- agno/py.typed +0 -0
- agno/reasoning/__init__.py +0 -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 +63 -0
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +31 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +787 -0
- agno/run/base.py +229 -0
- agno/run/cancel.py +81 -0
- agno/run/messages.py +32 -0
- agno/run/team.py +753 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +295 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +392 -0
- agno/session/workflow.py +205 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +8793 -0
- agno/tools/__init__.py +10 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +69 -0
- agno/tools/api.py +122 -0
- agno/tools/apify.py +314 -0
- agno/tools/arxiv.py +127 -0
- agno/tools/aws_lambda.py +53 -0
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +89 -0
- 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 +255 -0
- agno/tools/calculator.py +151 -0
- agno/tools/cartesia.py +187 -0
- agno/tools/clickup.py +244 -0
- agno/tools/confluence.py +240 -0
- agno/tools/crawl4ai.py +158 -0
- agno/tools/csv_toolkit.py +185 -0
- agno/tools/dalle.py +110 -0
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +262 -0
- agno/tools/desi_vocal.py +108 -0
- agno/tools/discord.py +161 -0
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +379 -0
- agno/tools/duckduckgo.py +91 -0
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +196 -0
- agno/tools/email.py +67 -0
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +396 -0
- agno/tools/fal.py +127 -0
- agno/tools/file.py +240 -0
- agno/tools/file_generation.py +350 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +143 -0
- agno/tools/function.py +1187 -0
- agno/tools/giphy.py +93 -0
- agno/tools/github.py +1760 -0
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +270 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +674 -0
- agno/tools/googlesearch.py +98 -0
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +77 -0
- agno/tools/jina.py +101 -0
- agno/tools/jira.py +170 -0
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +426 -0
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +90 -0
- agno/tools/lumalab.py +183 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memori.py +339 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +139 -0
- agno/tools/models/__init__.py +0 -0
- 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 +195 -0
- agno/tools/moviepy_video.py +349 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +46 -0
- agno/tools/newspaper4k.py +93 -0
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +202 -0
- agno/tools/openbb.py +160 -0
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +102 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +257 -0
- agno/tools/pubmed.py +188 -0
- agno/tools/python.py +205 -0
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +467 -0
- agno/tools/replicate.py +117 -0
- agno/tools/resend.py +62 -0
- agno/tools/scrapegraph.py +222 -0
- agno/tools/searxng.py +152 -0
- agno/tools/serpapi.py +116 -0
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +53 -0
- agno/tools/slack.py +136 -0
- agno/tools/sleep.py +20 -0
- agno/tools/spider.py +116 -0
- agno/tools/sql.py +154 -0
- agno/tools/streamlit/__init__.py +0 -0
- agno/tools/streamlit/components.py +113 -0
- agno/tools/tavily.py +254 -0
- agno/tools/telegram.py +48 -0
- agno/tools/todoist.py +218 -0
- agno/tools/tool_registry.py +1 -0
- agno/tools/toolkit.py +146 -0
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +274 -0
- agno/tools/twilio.py +186 -0
- 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 +54 -0
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +63 -0
- agno/tools/workflow.py +278 -0
- agno/tools/x.py +335 -0
- agno/tools/yfinance.py +257 -0
- agno/tools/youtube.py +184 -0
- agno/tools/zendesk.py +82 -0
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +382 -0
- agno/utils/__init__.py +0 -0
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +49 -0
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +132 -0
- agno/utils/dttm.py +13 -0
- agno/utils/enum.py +22 -0
- agno/utils/env.py +11 -0
- agno/utils/events.py +696 -0
- agno/utils/format_str.py +16 -0
- agno/utils/functions.py +166 -0
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +74 -0
- agno/utils/json_schema.py +234 -0
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +255 -0
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +352 -0
- agno/utils/merge_dict.py +41 -0
- agno/utils/message.py +118 -0
- agno/utils/models/__init__.py +0 -0
- agno/utils/models/ai_foundry.py +43 -0
- agno/utils/models/claude.py +358 -0
- agno/utils/models/cohere.py +87 -0
- agno/utils/models/llama.py +78 -0
- agno/utils/models/mistral.py +98 -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 +32 -0
- agno/utils/pprint.py +178 -0
- agno/utils/print_response/__init__.py +0 -0
- agno/utils/print_response/agent.py +842 -0
- agno/utils/print_response/team.py +1724 -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/response_iterator.py +17 -0
- agno/utils/safe_formatter.py +24 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +22 -0
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +231 -0
- agno/utils/team.py +139 -0
- agno/utils/timer.py +41 -0
- agno/utils/tools.py +102 -0
- agno/utils/web.py +23 -0
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +25 -0
- agno/vectordb/__init__.py +3 -0
- agno/vectordb/base.py +127 -0
- agno/vectordb/cassandra/__init__.py +5 -0
- agno/vectordb/cassandra/cassandra.py +501 -0
- agno/vectordb/cassandra/extra_param_mixin.py +11 -0
- agno/vectordb/cassandra/index.py +13 -0
- agno/vectordb/chroma/__init__.py +5 -0
- agno/vectordb/chroma/chromadb.py +929 -0
- agno/vectordb/clickhouse/__init__.py +9 -0
- agno/vectordb/clickhouse/clickhousedb.py +835 -0
- agno/vectordb/clickhouse/index.py +9 -0
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1442 -0
- agno/vectordb/distance.py +7 -0
- agno/vectordb/lancedb/__init__.py +6 -0
- agno/vectordb/lancedb/lance_db.py +995 -0
- 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 +4 -0
- agno/vectordb/milvus/milvus.py +1182 -0
- agno/vectordb/mongodb/__init__.py +9 -0
- agno/vectordb/mongodb/mongodb.py +1417 -0
- agno/vectordb/pgvector/__init__.py +12 -0
- agno/vectordb/pgvector/index.py +23 -0
- agno/vectordb/pgvector/pgvector.py +1462 -0
- agno/vectordb/pineconedb/__init__.py +5 -0
- agno/vectordb/pineconedb/pineconedb.py +747 -0
- agno/vectordb/qdrant/__init__.py +5 -0
- agno/vectordb/qdrant/qdrant.py +1134 -0
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/search.py +7 -0
- agno/vectordb/singlestore/__init__.py +10 -0
- agno/vectordb/singlestore/index.py +41 -0
- agno/vectordb/singlestore/singlestore.py +763 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +699 -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 +1005 -0
- agno/workflow/__init__.py +23 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +738 -0
- agno/workflow/loop.py +735 -0
- agno/workflow/parallel.py +824 -0
- agno/workflow/router.py +702 -0
- agno/workflow/step.py +1432 -0
- agno/workflow/steps.py +592 -0
- agno/workflow/types.py +520 -0
- agno/workflow/workflow.py +4321 -0
- agno-2.2.13.dist-info/METADATA +614 -0
- agno-2.2.13.dist-info/RECORD +575 -0
- agno-2.2.13.dist-info/WHEEL +5 -0
- agno-2.2.13.dist-info/licenses/LICENSE +201 -0
- agno-2.2.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from agno.tools import Toolkit
|
|
4
|
+
from agno.utils.log import log_debug, log_info, logger
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from moviepy import ColorClip, CompositeVideoClip, TextClip, VideoFileClip # type: ignore
|
|
8
|
+
except ImportError:
|
|
9
|
+
raise ImportError("`moviepy` not installed. Please install using `pip install moviepy ffmpeg`")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MoviePyVideoTools(Toolkit):
|
|
13
|
+
"""Tool for processing video files, extracting audio, transcribing and adding captions"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
enable_process_video: bool = True,
|
|
18
|
+
enable_generate_captions: bool = True,
|
|
19
|
+
enable_embed_captions: bool = True,
|
|
20
|
+
all: bool = False,
|
|
21
|
+
**kwargs,
|
|
22
|
+
):
|
|
23
|
+
tools: List[Any] = []
|
|
24
|
+
if enable_process_video or all:
|
|
25
|
+
tools.append(self.extract_audio)
|
|
26
|
+
if enable_generate_captions or all:
|
|
27
|
+
tools.append(self.create_srt)
|
|
28
|
+
if enable_embed_captions or all:
|
|
29
|
+
tools.append(self.embed_captions)
|
|
30
|
+
|
|
31
|
+
super().__init__(name="video_tools", tools=tools, **kwargs)
|
|
32
|
+
|
|
33
|
+
def split_text_into_lines(self, words: List[Dict]) -> List[Dict]:
|
|
34
|
+
"""Split transcribed words into lines based on duration and length constraints
|
|
35
|
+
Args:
|
|
36
|
+
words: List of dictionaries containing word data with 'word', 'start', and 'end' keys
|
|
37
|
+
Returns:
|
|
38
|
+
List[Dict]: List of subtitle lines, each containing word, start time, end time, and text contents
|
|
39
|
+
"""
|
|
40
|
+
MAX_CHARS = 30
|
|
41
|
+
MAX_DURATION = 2.5
|
|
42
|
+
MAX_GAP = 1.5
|
|
43
|
+
|
|
44
|
+
subtitles = []
|
|
45
|
+
line = []
|
|
46
|
+
line_duration = 0
|
|
47
|
+
|
|
48
|
+
for idx, word_data in enumerate(words):
|
|
49
|
+
line.append(word_data)
|
|
50
|
+
line_duration += word_data["end"] - word_data["start"]
|
|
51
|
+
|
|
52
|
+
temp = " ".join(item["word"] for item in line)
|
|
53
|
+
|
|
54
|
+
duration_exceeded = line_duration > MAX_DURATION
|
|
55
|
+
chars_exceeded = len(temp) > MAX_CHARS
|
|
56
|
+
maxgap_exceeded = idx > 0 and word_data["start"] - words[idx - 1]["end"] > MAX_GAP
|
|
57
|
+
|
|
58
|
+
if duration_exceeded or chars_exceeded or maxgap_exceeded:
|
|
59
|
+
if line:
|
|
60
|
+
subtitle_line = {
|
|
61
|
+
"word": " ".join(item["word"] for item in line),
|
|
62
|
+
"start": line[0]["start"],
|
|
63
|
+
"end": line[-1]["end"],
|
|
64
|
+
"textcontents": line,
|
|
65
|
+
}
|
|
66
|
+
subtitles.append(subtitle_line)
|
|
67
|
+
line = []
|
|
68
|
+
line_duration = 0
|
|
69
|
+
|
|
70
|
+
if line:
|
|
71
|
+
subtitle_line = {
|
|
72
|
+
"word": " ".join(item["word"] for item in line),
|
|
73
|
+
"start": line[0]["start"],
|
|
74
|
+
"end": line[-1]["end"],
|
|
75
|
+
"textcontents": line,
|
|
76
|
+
}
|
|
77
|
+
subtitles.append(subtitle_line)
|
|
78
|
+
|
|
79
|
+
return subtitles
|
|
80
|
+
|
|
81
|
+
def create_caption_clips(
|
|
82
|
+
self,
|
|
83
|
+
text_json: Dict,
|
|
84
|
+
frame_size: tuple,
|
|
85
|
+
font="Arial",
|
|
86
|
+
color="white",
|
|
87
|
+
highlight_color="yellow",
|
|
88
|
+
stroke_color="black",
|
|
89
|
+
stroke_width=1.5,
|
|
90
|
+
) -> List[TextClip]:
|
|
91
|
+
"""Create word-level caption clips with highlighting effects
|
|
92
|
+
Args:
|
|
93
|
+
text_json: Dictionary containing text and timing information
|
|
94
|
+
frame_size: Tuple of (width, height) for the video frame
|
|
95
|
+
font: Font family to use for captions
|
|
96
|
+
color: Base text color
|
|
97
|
+
highlight_color: Color for highlighted words
|
|
98
|
+
stroke_color: Color for text outline
|
|
99
|
+
stroke_width: Width of text outline
|
|
100
|
+
Returns:
|
|
101
|
+
List[TextClip]: List of MoviePy TextClip objects for each word and highlight
|
|
102
|
+
"""
|
|
103
|
+
word_clips = []
|
|
104
|
+
x_pos = 0
|
|
105
|
+
y_pos = 0
|
|
106
|
+
line_width = 0
|
|
107
|
+
|
|
108
|
+
frame_width, frame_height = frame_size
|
|
109
|
+
x_buffer = frame_width * 0.1
|
|
110
|
+
max_line_width = frame_width - (2 * x_buffer)
|
|
111
|
+
fontsize = int(frame_height * 0.30)
|
|
112
|
+
|
|
113
|
+
full_duration = text_json["end"] - text_json["start"]
|
|
114
|
+
|
|
115
|
+
for word_data in text_json["textcontents"]:
|
|
116
|
+
duration = word_data["end"] - word_data["start"]
|
|
117
|
+
|
|
118
|
+
# Create base word clip using official TextClip parameters
|
|
119
|
+
word_clip = (
|
|
120
|
+
TextClip(
|
|
121
|
+
text=word_data["word"],
|
|
122
|
+
font=font,
|
|
123
|
+
font_size=int(fontsize),
|
|
124
|
+
color=color,
|
|
125
|
+
stroke_color=stroke_color,
|
|
126
|
+
stroke_width=int(stroke_width),
|
|
127
|
+
method="label",
|
|
128
|
+
)
|
|
129
|
+
.with_start(text_json["start"])
|
|
130
|
+
.with_duration(full_duration)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Create space clip
|
|
134
|
+
space_clip = (
|
|
135
|
+
TextClip(text=" ", font=font, font_size=int(fontsize), color=color, method="label")
|
|
136
|
+
.with_start(text_json["start"])
|
|
137
|
+
.with_duration(full_duration)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
word_width, word_height = word_clip.size
|
|
141
|
+
space_width = space_clip.size[0]
|
|
142
|
+
|
|
143
|
+
# Handle line wrapping
|
|
144
|
+
if line_width + word_width + space_width <= max_line_width:
|
|
145
|
+
word_clip = word_clip.with_position((x_pos + x_buffer, y_pos))
|
|
146
|
+
space_clip = space_clip.with_position((x_pos + word_width + x_buffer, y_pos))
|
|
147
|
+
x_pos += word_width + space_width
|
|
148
|
+
line_width += word_width + space_width
|
|
149
|
+
else:
|
|
150
|
+
x_pos = 0
|
|
151
|
+
y_pos += word_height + 10
|
|
152
|
+
line_width = word_width + space_width
|
|
153
|
+
word_clip = word_clip.with_position((x_buffer, y_pos))
|
|
154
|
+
space_clip = space_clip.with_position((word_width + x_buffer, y_pos))
|
|
155
|
+
|
|
156
|
+
word_clips.append(word_clip)
|
|
157
|
+
word_clips.append(space_clip)
|
|
158
|
+
|
|
159
|
+
# Create highlighted version
|
|
160
|
+
highlight_clip = (
|
|
161
|
+
TextClip(
|
|
162
|
+
text=word_data["word"],
|
|
163
|
+
font=font,
|
|
164
|
+
font_size=int(fontsize),
|
|
165
|
+
color=highlight_color,
|
|
166
|
+
stroke_color=stroke_color,
|
|
167
|
+
stroke_width=int(stroke_width),
|
|
168
|
+
method="label",
|
|
169
|
+
)
|
|
170
|
+
.with_start(word_data["start"])
|
|
171
|
+
.with_duration(duration)
|
|
172
|
+
.with_position(word_clip.pos)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
word_clips.append(highlight_clip)
|
|
176
|
+
|
|
177
|
+
return word_clips
|
|
178
|
+
|
|
179
|
+
def parse_srt(self, srt_content: str) -> List[Dict]:
|
|
180
|
+
"""Convert SRT formatted content into word-level timing data
|
|
181
|
+
Args:
|
|
182
|
+
srt_content: String containing SRT formatted subtitles
|
|
183
|
+
Returns:
|
|
184
|
+
List[Dict]: List of words with their timing information
|
|
185
|
+
"""
|
|
186
|
+
words = []
|
|
187
|
+
lines = srt_content.strip().split("\n\n")
|
|
188
|
+
|
|
189
|
+
for block in lines:
|
|
190
|
+
if not block.strip():
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
parts = block.split("\n")
|
|
194
|
+
if len(parts) < 3:
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
# Parse timestamp line
|
|
198
|
+
timestamp = parts[1]
|
|
199
|
+
start_time, end_time = timestamp.split(" --> ")
|
|
200
|
+
|
|
201
|
+
# Convert timestamp to seconds
|
|
202
|
+
def time_to_seconds(time_str):
|
|
203
|
+
h, m, s = time_str.replace(",", ".").split(":")
|
|
204
|
+
return float(h) * 3600 + float(m) * 60 + float(s)
|
|
205
|
+
|
|
206
|
+
start = time_to_seconds(start_time)
|
|
207
|
+
end = time_to_seconds(end_time)
|
|
208
|
+
|
|
209
|
+
# Get text content (could be multiple lines)
|
|
210
|
+
text = " ".join(parts[2:])
|
|
211
|
+
|
|
212
|
+
# Split text into words and distribute timing
|
|
213
|
+
text_words = text.split()
|
|
214
|
+
if text_words:
|
|
215
|
+
time_per_word = (end - start) / len(text_words)
|
|
216
|
+
|
|
217
|
+
for i, word in enumerate(text_words):
|
|
218
|
+
word_start = start + (i * time_per_word)
|
|
219
|
+
word_end = word_start + time_per_word
|
|
220
|
+
words.append({"word": word, "start": word_start, "end": word_end})
|
|
221
|
+
|
|
222
|
+
return words
|
|
223
|
+
|
|
224
|
+
def extract_audio(self, video_path: str, output_path: str) -> str:
|
|
225
|
+
"""Converts video to audio using MoviePy
|
|
226
|
+
Args:
|
|
227
|
+
video_path: Path to the video file
|
|
228
|
+
output_path: Path where the audio will be saved
|
|
229
|
+
Returns:
|
|
230
|
+
str: Path to the extracted audio file
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
log_debug(f"Extracting audio from {video_path}")
|
|
234
|
+
video = VideoFileClip(video_path)
|
|
235
|
+
video.audio.write_audiofile(output_path)
|
|
236
|
+
log_info(f"Audio extracted to {output_path}")
|
|
237
|
+
return output_path
|
|
238
|
+
except Exception as e:
|
|
239
|
+
logger.error(f"Failed to extract audio: {str(e)}")
|
|
240
|
+
return f"Failed to extract audio: {str(e)}"
|
|
241
|
+
|
|
242
|
+
def create_srt(self, transcription: str, output_path: str) -> str:
|
|
243
|
+
"""Save transcription text to SRT formatted file
|
|
244
|
+
Args:
|
|
245
|
+
transcription: Text transcription in SRT format
|
|
246
|
+
output_path: Path where the SRT file will be saved
|
|
247
|
+
Returns:
|
|
248
|
+
str: Path to the created SRT file, or error message if failed
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
log_debug(f"Creating SRT file at {output_path}")
|
|
252
|
+
# Since we're getting SRT format from Whisper API now,
|
|
253
|
+
# we can just write it directly to file
|
|
254
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
255
|
+
f.write(transcription)
|
|
256
|
+
return output_path
|
|
257
|
+
except Exception as e:
|
|
258
|
+
logger.error(f"Failed to create SRT file: {str(e)}")
|
|
259
|
+
return f"Failed to create SRT file: {str(e)}"
|
|
260
|
+
|
|
261
|
+
def embed_captions(
|
|
262
|
+
self,
|
|
263
|
+
video_path: str,
|
|
264
|
+
srt_path: str,
|
|
265
|
+
output_path: Optional[str] = None,
|
|
266
|
+
font_size: int = 24,
|
|
267
|
+
font_color: str = "white",
|
|
268
|
+
stroke_color: str = "black",
|
|
269
|
+
stroke_width: int = 1,
|
|
270
|
+
) -> str:
|
|
271
|
+
"""Create a new video with embedded scrolling captions and word-level highlighting
|
|
272
|
+
Args:
|
|
273
|
+
video_path: Path to the input video file
|
|
274
|
+
srt_path: Path to the SRT caption file
|
|
275
|
+
output_path: Path for the output video (optional)
|
|
276
|
+
font_size: Size of caption text
|
|
277
|
+
font_color: Color of caption text
|
|
278
|
+
stroke_color: Color of text outline
|
|
279
|
+
stroke_width: Width of text outline
|
|
280
|
+
Returns:
|
|
281
|
+
str: Path to the captioned video file, or error message if failed
|
|
282
|
+
"""
|
|
283
|
+
try:
|
|
284
|
+
# If no output path provided, create one based on input video
|
|
285
|
+
if output_path is None:
|
|
286
|
+
output_path = video_path.rsplit(".", 1)[0] + "_captioned.mp4"
|
|
287
|
+
|
|
288
|
+
# Load video
|
|
289
|
+
video = VideoFileClip(video_path)
|
|
290
|
+
|
|
291
|
+
# Read caption file and parse SRT
|
|
292
|
+
with open(srt_path, "r", encoding="utf-8") as f:
|
|
293
|
+
srt_content = f.read()
|
|
294
|
+
|
|
295
|
+
# Parse SRT and get word timing
|
|
296
|
+
words = self.parse_srt(srt_content)
|
|
297
|
+
|
|
298
|
+
# Split into lines
|
|
299
|
+
subtitle_lines = self.split_text_into_lines(words)
|
|
300
|
+
|
|
301
|
+
all_caption_clips = []
|
|
302
|
+
|
|
303
|
+
# Create caption clips for each line
|
|
304
|
+
for line in subtitle_lines:
|
|
305
|
+
# Increase background height to accommodate larger text
|
|
306
|
+
bg_height = int(video.h * 0.15)
|
|
307
|
+
bg_clip = ColorClip(
|
|
308
|
+
size=(video.w, bg_height), color=(0, 0, 0), duration=line["end"] - line["start"]
|
|
309
|
+
).with_opacity(0.6)
|
|
310
|
+
|
|
311
|
+
# Position background even closer to bottom (90% instead of 85%)
|
|
312
|
+
bg_position = ("center", int(video.h * 0.90))
|
|
313
|
+
bg_clip = bg_clip.with_start(line["start"]).with_position(bg_position)
|
|
314
|
+
|
|
315
|
+
# Create word clips
|
|
316
|
+
word_clips = self.create_caption_clips(line, (video.w, bg_height))
|
|
317
|
+
|
|
318
|
+
# Combine background and words
|
|
319
|
+
caption_composite = CompositeVideoClip([bg_clip] + word_clips, size=bg_clip.size).with_position(
|
|
320
|
+
bg_position
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
all_caption_clips.append(caption_composite)
|
|
324
|
+
|
|
325
|
+
# Combine video with all captions
|
|
326
|
+
final_video = CompositeVideoClip([video] + all_caption_clips, size=video.size)
|
|
327
|
+
|
|
328
|
+
# Write output with optimized settings
|
|
329
|
+
final_video.write_videofile(
|
|
330
|
+
output_path,
|
|
331
|
+
codec="libx264",
|
|
332
|
+
audio_codec="aac",
|
|
333
|
+
fps=video.fps,
|
|
334
|
+
preset="medium",
|
|
335
|
+
threads=4,
|
|
336
|
+
# Disable default progress bar
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
# Cleanup
|
|
340
|
+
video.close()
|
|
341
|
+
final_video.close()
|
|
342
|
+
for clip in all_caption_clips:
|
|
343
|
+
clip.close()
|
|
344
|
+
|
|
345
|
+
return output_path
|
|
346
|
+
|
|
347
|
+
except Exception as e:
|
|
348
|
+
logger.error(f"Failed to embed captions: {str(e)}")
|
|
349
|
+
return f"Failed to embed captions: {str(e)}"
|
agno/tools/neo4j.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, List, Optional
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from neo4j import GraphDatabase
|
|
6
|
+
except ImportError:
|
|
7
|
+
raise ImportError("`neo4j` not installed. Please install using `pip install neo4j`")
|
|
8
|
+
|
|
9
|
+
from agno.tools import Toolkit
|
|
10
|
+
from agno.utils.log import log_debug, logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Neo4jTools(Toolkit):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
uri: Optional[str] = None,
|
|
17
|
+
user: Optional[str] = None,
|
|
18
|
+
password: Optional[str] = None,
|
|
19
|
+
database: Optional[str] = None,
|
|
20
|
+
# Enable flags for <6 functions
|
|
21
|
+
enable_list_labels: bool = True,
|
|
22
|
+
enable_list_relationships: bool = True,
|
|
23
|
+
enable_get_schema: bool = True,
|
|
24
|
+
enable_run_cypher: bool = True,
|
|
25
|
+
all: bool = False,
|
|
26
|
+
**kwargs,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize the Neo4jTools toolkit.
|
|
30
|
+
Connection parameters (uri/user/password or host/port) can be provided.
|
|
31
|
+
If not provided, falls back to NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD env vars.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
uri (Optional[str]): The Neo4j URI.
|
|
35
|
+
user (Optional[str]): The Neo4j username.
|
|
36
|
+
password (Optional[str]): The Neo4j password.
|
|
37
|
+
host (Optional[str]): The Neo4j host.
|
|
38
|
+
port (Optional[int]): The Neo4j port.
|
|
39
|
+
database (Optional[str]): The Neo4j database.
|
|
40
|
+
list_labels (bool): Whether to list node labels.
|
|
41
|
+
list_relationships (bool): Whether to list relationship types.
|
|
42
|
+
get_schema (bool): Whether to get the schema.
|
|
43
|
+
run_cypher (bool): Whether to run Cypher queries.
|
|
44
|
+
**kwargs: Additional keyword arguments.
|
|
45
|
+
"""
|
|
46
|
+
# Determine the connection URI and credentials
|
|
47
|
+
uri = uri or os.getenv("NEO4J_URI", "bolt://localhost:7687")
|
|
48
|
+
user = user or os.getenv("NEO4J_USERNAME")
|
|
49
|
+
password = password or os.getenv("NEO4J_PASSWORD")
|
|
50
|
+
|
|
51
|
+
if user is None or password is None:
|
|
52
|
+
raise ValueError("Username or password for Neo4j not provided")
|
|
53
|
+
|
|
54
|
+
# Create the Neo4j driver
|
|
55
|
+
try:
|
|
56
|
+
self.driver = GraphDatabase.driver(uri, auth=(user, password)) # type: ignore
|
|
57
|
+
self.driver.verify_connectivity()
|
|
58
|
+
log_debug("Connected to Neo4j database")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.error(f"Failed to connect to Neo4j: {e}")
|
|
61
|
+
raise
|
|
62
|
+
|
|
63
|
+
self.database = database or "neo4j"
|
|
64
|
+
|
|
65
|
+
# Register toolkit methods as tools
|
|
66
|
+
tools: List[Any] = []
|
|
67
|
+
if all or enable_list_labels:
|
|
68
|
+
tools.append(self.list_labels)
|
|
69
|
+
if all or enable_list_relationships:
|
|
70
|
+
tools.append(self.list_relationship_types)
|
|
71
|
+
if all or enable_get_schema:
|
|
72
|
+
tools.append(self.get_schema)
|
|
73
|
+
if all or enable_run_cypher:
|
|
74
|
+
tools.append(self.run_cypher_query)
|
|
75
|
+
super().__init__(name="neo4j_tools", tools=tools, **kwargs)
|
|
76
|
+
|
|
77
|
+
def list_labels(self) -> list:
|
|
78
|
+
"""
|
|
79
|
+
Retrieve all node labels present in the connected Neo4j database.
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
log_debug("Listing node labels in Neo4j database")
|
|
83
|
+
with self.driver.session(database=self.database) as session:
|
|
84
|
+
result = session.run("CALL db.labels()")
|
|
85
|
+
labels = [record["label"] for record in result]
|
|
86
|
+
return labels
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error(f"Error listing labels: {e}")
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
def list_relationship_types(self) -> list:
|
|
92
|
+
"""
|
|
93
|
+
Retrieve all relationship types present in the connected Neo4j database.
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
log_debug("Listing relationship types in Neo4j database")
|
|
97
|
+
with self.driver.session(database=self.database) as session:
|
|
98
|
+
result = session.run("CALL db.relationshipTypes()")
|
|
99
|
+
types = [record["relationshipType"] for record in result]
|
|
100
|
+
return types
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error(f"Error listing relationship types: {e}")
|
|
103
|
+
return []
|
|
104
|
+
|
|
105
|
+
def get_schema(self) -> list:
|
|
106
|
+
"""
|
|
107
|
+
Retrieve a visualization of the database schema, including nodes and relationships.
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
log_debug("Retrieving Neo4j schema visualization")
|
|
111
|
+
with self.driver.session(database=self.database) as session:
|
|
112
|
+
result = session.run("CALL db.schema.visualization()")
|
|
113
|
+
schema_data = result.data()
|
|
114
|
+
return schema_data
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Error getting Neo4j schema: {e}")
|
|
117
|
+
return []
|
|
118
|
+
|
|
119
|
+
def run_cypher_query(self, query: str) -> list:
|
|
120
|
+
"""
|
|
121
|
+
Execute an arbitrary Cypher query against the connected Neo4j database.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
query (str): The Cypher query string to execute.
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
log_debug(f"Running Cypher query: {query}")
|
|
128
|
+
with self.driver.session(database=self.database) as session:
|
|
129
|
+
result = session.run(query) # type: ignore[arg-type]
|
|
130
|
+
data = result.data()
|
|
131
|
+
return data
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"Error running Cypher query: {e}")
|
|
134
|
+
return []
|
agno/tools/newspaper.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from agno.tools import Toolkit
|
|
2
|
+
from agno.utils.log import log_debug
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from newspaper import Article
|
|
6
|
+
except ImportError:
|
|
7
|
+
raise ImportError("`newspaper3k` not installed. Please run `pip install newspaper3k lxml_html_clean`.")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NewspaperTools(Toolkit):
|
|
11
|
+
"""
|
|
12
|
+
Newspaper is a tool for getting the text of an article from a URL.
|
|
13
|
+
Args:
|
|
14
|
+
get_article_text (bool): Whether to get the text of an article from a URL.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
enable_get_article_text: bool = True,
|
|
20
|
+
all: bool = False,
|
|
21
|
+
**kwargs,
|
|
22
|
+
):
|
|
23
|
+
tools = []
|
|
24
|
+
if all or enable_get_article_text:
|
|
25
|
+
tools.append(self.get_article_text)
|
|
26
|
+
|
|
27
|
+
super().__init__(name="newspaper_toolkit", tools=tools, **kwargs)
|
|
28
|
+
|
|
29
|
+
def get_article_text(self, url: str) -> str:
|
|
30
|
+
"""Get the text of an article from a URL.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
url (str): The URL of the article.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
str: The text of the article.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
log_debug(f"Reading news: {url}")
|
|
41
|
+
article = Article(url)
|
|
42
|
+
article.download()
|
|
43
|
+
article.parse()
|
|
44
|
+
return article.text
|
|
45
|
+
except Exception as e:
|
|
46
|
+
return f"Error getting article text from {url}: {e}"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
from agno.tools import Toolkit
|
|
5
|
+
from agno.utils.log import log_debug, logger
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import newspaper
|
|
9
|
+
except ImportError:
|
|
10
|
+
raise ImportError("`newspaper4k` not installed. Please run `pip install newspaper4k lxml_html_clean`.")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Newspaper4kTools(Toolkit):
|
|
14
|
+
"""
|
|
15
|
+
Newspaper4kTools is a toolkit for getting the text of an article from a URL.
|
|
16
|
+
Args:
|
|
17
|
+
enable_read_article (bool): Whether to read an article from a URL.
|
|
18
|
+
include_summary (bool): Whether to include the summary of an article.
|
|
19
|
+
article_length (Optional[int]): The length of the article to read.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
include_summary: bool = False,
|
|
25
|
+
article_length: Optional[int] = None,
|
|
26
|
+
enable_read_article: bool = True,
|
|
27
|
+
all: bool = False,
|
|
28
|
+
**kwargs,
|
|
29
|
+
):
|
|
30
|
+
self.include_summary: bool = include_summary
|
|
31
|
+
self.article_length: Optional[int] = article_length
|
|
32
|
+
|
|
33
|
+
tools = []
|
|
34
|
+
if all or enable_read_article:
|
|
35
|
+
tools.append(self.read_article)
|
|
36
|
+
|
|
37
|
+
super().__init__(name="newspaper4k_tools", tools=tools, **kwargs)
|
|
38
|
+
|
|
39
|
+
def get_article_data(self, url: str) -> Optional[Dict[str, Any]]:
|
|
40
|
+
"""Read and get article data from a URL.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
url (str): The URL of the article.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Dict[str, Any]: The article data.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
article = newspaper.article(url)
|
|
51
|
+
article_data = {}
|
|
52
|
+
if article.title:
|
|
53
|
+
article_data["title"] = article.title
|
|
54
|
+
if article.authors:
|
|
55
|
+
article_data["authors"] = article.authors
|
|
56
|
+
if article.text:
|
|
57
|
+
article_data["text"] = article.text
|
|
58
|
+
if self.include_summary and article.summary:
|
|
59
|
+
article_data["summary"] = article.summary
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
if article.publish_date:
|
|
63
|
+
article_data["publish_date"] = article.publish_date.isoformat() if article.publish_date else None
|
|
64
|
+
except Exception:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
return article_data
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.warning(f"Error reading article from {url}: {e}")
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
def read_article(self, url: str) -> str:
|
|
73
|
+
"""Use this function to read an article from a URL.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
url (str): The URL of the article.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
str: JSON containing the article author, publish date, and text.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
log_debug(f"Reading news: {url}")
|
|
84
|
+
article_data = self.get_article_data(url)
|
|
85
|
+
if not article_data:
|
|
86
|
+
return f"Error reading article from {url}: No data found."
|
|
87
|
+
|
|
88
|
+
if self.article_length and "text" in article_data:
|
|
89
|
+
article_data["text"] = article_data["text"][: self.article_length]
|
|
90
|
+
|
|
91
|
+
return json.dumps(article_data, indent=2)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
return f"Error reading article from {url}: {e}"
|