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
agno/tools/cartesia.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from agno.agent import Agent
|
|
7
|
+
from agno.media import Audio
|
|
8
|
+
from agno.team.team import Team
|
|
9
|
+
from agno.tools import Toolkit
|
|
10
|
+
from agno.tools.function import ToolResult
|
|
11
|
+
from agno.utils.log import log_debug, log_error, log_info
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import cartesia # type: ignore
|
|
15
|
+
except ImportError:
|
|
16
|
+
raise ImportError("`cartesia` not installed. Please install using `pip install cartesia`")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CartesiaTools(Toolkit):
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
api_key: Optional[str] = None,
|
|
23
|
+
model_id: str = "sonic-2",
|
|
24
|
+
default_voice_id: str = "78ab82d5-25be-4f7d-82b3-7ad64e5b85b2",
|
|
25
|
+
enable_text_to_speech: bool = True,
|
|
26
|
+
enable_list_voices: bool = True,
|
|
27
|
+
enable_localize_voice: bool = False,
|
|
28
|
+
all: bool = False,
|
|
29
|
+
**kwargs,
|
|
30
|
+
):
|
|
31
|
+
self.api_key = api_key or getenv("CARTESIA_API_KEY")
|
|
32
|
+
|
|
33
|
+
if not self.api_key:
|
|
34
|
+
raise ValueError("CARTESIA_API_KEY not set. Please set the CARTESIA_API_KEY environment variable.")
|
|
35
|
+
|
|
36
|
+
self.client = cartesia.Cartesia(api_key=self.api_key)
|
|
37
|
+
self.model_id = model_id
|
|
38
|
+
self.default_voice_id = default_voice_id
|
|
39
|
+
|
|
40
|
+
tools: List[Any] = []
|
|
41
|
+
if all or enable_localize_voice:
|
|
42
|
+
tools.append(self.localize_voice)
|
|
43
|
+
if all or enable_text_to_speech:
|
|
44
|
+
tools.append(self.text_to_speech)
|
|
45
|
+
if all or enable_list_voices:
|
|
46
|
+
tools.append(self.list_voices)
|
|
47
|
+
|
|
48
|
+
super().__init__(name="cartesia_tools", tools=tools, **kwargs)
|
|
49
|
+
|
|
50
|
+
def list_voices(self) -> str:
|
|
51
|
+
"""List available voices from Cartesia (first page).
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
str: JSON string containing a list of voices, each with id, name, description, and language.
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
voices = self.client.voices.list()
|
|
58
|
+
|
|
59
|
+
voice_objects = voices.items if voices else None
|
|
60
|
+
|
|
61
|
+
filtered_result = []
|
|
62
|
+
if voice_objects:
|
|
63
|
+
for voice in voice_objects:
|
|
64
|
+
try:
|
|
65
|
+
# Extract desired attributes from the Voice object
|
|
66
|
+
voice_data = {
|
|
67
|
+
"id": voice.id if hasattr(voice, "id") else None,
|
|
68
|
+
"name": voice.name if hasattr(voice, "name") else None,
|
|
69
|
+
"description": voice.description if hasattr(voice, "description") else None,
|
|
70
|
+
"language": voice.language if hasattr(voice, "language") else None,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if voice_data["id"]: # Only add if we could get an ID
|
|
74
|
+
filtered_result.append(voice_data)
|
|
75
|
+
else:
|
|
76
|
+
log_info(f"Could not extract 'id' from voice object: {voice}")
|
|
77
|
+
except AttributeError as ae:
|
|
78
|
+
log_error(f"AttributeError accessing voice data: {ae}. Voice data: {voice}")
|
|
79
|
+
continue
|
|
80
|
+
except Exception as inner_e:
|
|
81
|
+
log_error(f"Unexpected error processing voice: {inner_e}. Voice data: {voice}")
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
return json.dumps(filtered_result, indent=4)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
log_error(f"Error listing voices from Cartesia: {e}", exc_info=True)
|
|
87
|
+
return json.dumps({"error": str(e), "detail": "Error occurred in list_voices function."})
|
|
88
|
+
|
|
89
|
+
def localize_voice(
|
|
90
|
+
self,
|
|
91
|
+
name: str,
|
|
92
|
+
description: str,
|
|
93
|
+
language: str,
|
|
94
|
+
original_speaker_gender: str,
|
|
95
|
+
voice_id: Optional[str] = None,
|
|
96
|
+
) -> str:
|
|
97
|
+
"""Create a new voice localized to a different language.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
name (str): The desired name for the new localized voice.
|
|
101
|
+
description (str): The description for the new localized voice.
|
|
102
|
+
language (str): The target language code (e.g., 'fr', 'es').
|
|
103
|
+
original_speaker_gender (str): The gender of the original speaker ("male" or "female").
|
|
104
|
+
voice_id (optional): The ID of an existing voice to use as the base. If None, uses the default voice ID configured in the tool. Defaults to None.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
str: JSON string containing the information of the newly created localized voice, including its 'id'.
|
|
108
|
+
"""
|
|
109
|
+
localize_voice_id = voice_id or self.default_voice_id
|
|
110
|
+
log_debug(f"Using voice_id '{localize_voice_id}' for localization.")
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
result = self.client.voices.localize(
|
|
114
|
+
voice_id=localize_voice_id,
|
|
115
|
+
name=name,
|
|
116
|
+
description=description,
|
|
117
|
+
language=language,
|
|
118
|
+
original_speaker_gender=original_speaker_gender,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if isinstance(result, dict):
|
|
122
|
+
return json.dumps(result, indent=4)
|
|
123
|
+
else:
|
|
124
|
+
return result.model_dump_json(indent=4)
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
log_error(f"Error localizing voice with Cartesia: {e}", exc_info=True)
|
|
128
|
+
return json.dumps({"error": str(e), "type": type(e).__name__})
|
|
129
|
+
|
|
130
|
+
def text_to_speech(
|
|
131
|
+
self,
|
|
132
|
+
agent: Union[Agent, Team],
|
|
133
|
+
transcript: str,
|
|
134
|
+
voice_id: Optional[str] = None,
|
|
135
|
+
) -> ToolResult:
|
|
136
|
+
"""
|
|
137
|
+
Convert text to speech.
|
|
138
|
+
Args:
|
|
139
|
+
transcript: The text to convert to speech
|
|
140
|
+
voice_id (optional): The ID of the voice to use for the text-to-speech. If None, uses the default voice ID configured in the tool. Defaults to None.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
ToolResult: A ToolResult containing the generated audio or error message.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
effective_voice_id = voice_id or self.default_voice_id
|
|
148
|
+
|
|
149
|
+
log_info(f"Using voice_id: {effective_voice_id} for text_to_speech.")
|
|
150
|
+
log_info(f"Using model_id: {self.model_id} for text_to_speech.")
|
|
151
|
+
|
|
152
|
+
output_format_sample_rate = 44100
|
|
153
|
+
requested_bit_rate = 128000
|
|
154
|
+
mime_type = "audio/mpeg"
|
|
155
|
+
|
|
156
|
+
output_format = {
|
|
157
|
+
"container": "mp3",
|
|
158
|
+
"sample_rate": output_format_sample_rate,
|
|
159
|
+
"bit_rate": requested_bit_rate,
|
|
160
|
+
"encoding": "mp3",
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
params: Dict[str, Any] = {
|
|
164
|
+
"model_id": self.model_id,
|
|
165
|
+
"transcript": transcript,
|
|
166
|
+
"voice": {"mode": "id", "id": effective_voice_id},
|
|
167
|
+
"output_format": output_format,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
audio_iterator = self.client.tts.bytes(**params)
|
|
171
|
+
audio_data = b"".join(chunk for chunk in audio_iterator)
|
|
172
|
+
|
|
173
|
+
# Create AudioArtifact
|
|
174
|
+
audio_artifact = Audio(
|
|
175
|
+
id=str(uuid4()),
|
|
176
|
+
content=audio_data,
|
|
177
|
+
mime_type=mime_type, # Hardcoded to audio/mpeg
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return ToolResult(
|
|
181
|
+
content="Audio generated and attached successfully.",
|
|
182
|
+
audios=[audio_artifact],
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
log_error(f"Error generating speech with Cartesia: {e}", exc_info=True)
|
|
187
|
+
return ToolResult(content=f"Error generating speech: {e}")
|
agno/tools/clickup.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from os import getenv
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from agno.tools import Toolkit
|
|
7
|
+
from agno.utils.log import log_debug, logger
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import requests
|
|
11
|
+
except ImportError:
|
|
12
|
+
raise ImportError("`requests` not installed. Please install using `pip install requests`")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ClickUpTools(Toolkit):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
api_key: Optional[str] = None,
|
|
19
|
+
master_space_id: Optional[str] = None,
|
|
20
|
+
**kwargs,
|
|
21
|
+
):
|
|
22
|
+
self.api_key = api_key or getenv("CLICKUP_API_KEY")
|
|
23
|
+
self.master_space_id = master_space_id or getenv("MASTER_SPACE_ID")
|
|
24
|
+
self.base_url = "https://api.clickup.com/api/v2"
|
|
25
|
+
self.headers = {"Authorization": self.api_key}
|
|
26
|
+
|
|
27
|
+
if not self.api_key:
|
|
28
|
+
raise ValueError("CLICKUP_API_KEY not set. Please set the CLICKUP_API_KEY environment variable.")
|
|
29
|
+
if not self.master_space_id:
|
|
30
|
+
raise ValueError("MASTER_SPACE_ID not set. Please set the MASTER_SPACE_ID environment variable.")
|
|
31
|
+
|
|
32
|
+
tools: List[Any] = [
|
|
33
|
+
self.list_tasks,
|
|
34
|
+
self.create_task,
|
|
35
|
+
self.get_task,
|
|
36
|
+
self.update_task,
|
|
37
|
+
self.delete_task,
|
|
38
|
+
self.list_spaces,
|
|
39
|
+
self.list_lists,
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
super().__init__(name="clickup", tools=tools, **kwargs)
|
|
43
|
+
|
|
44
|
+
def _make_request(
|
|
45
|
+
self, method: str, endpoint: str, params: Optional[Dict] = None, data: Optional[Dict] = None
|
|
46
|
+
) -> Dict[str, Any]:
|
|
47
|
+
"""Make a request to the ClickUp API."""
|
|
48
|
+
url = f"{self.base_url}/{endpoint}"
|
|
49
|
+
try:
|
|
50
|
+
response = requests.request(method=method, url=url, headers=self.headers, params=params, json=data)
|
|
51
|
+
response.raise_for_status()
|
|
52
|
+
return response.json()
|
|
53
|
+
except requests.exceptions.RequestException as e:
|
|
54
|
+
logger.error(f"Error making request to {url}: {e}")
|
|
55
|
+
return {"error": str(e)}
|
|
56
|
+
|
|
57
|
+
def _find_by_name(self, items: List[Dict[str, Any]], name: str) -> Optional[Dict[str, Any]]:
|
|
58
|
+
"""Find an item in a list by name using exact match or regex pattern.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
items: List of items to search through
|
|
62
|
+
name: Name to search for
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Matching item or None if not found
|
|
66
|
+
"""
|
|
67
|
+
if not name:
|
|
68
|
+
return items[0] if items else None
|
|
69
|
+
|
|
70
|
+
pattern = re.compile(name, re.IGNORECASE)
|
|
71
|
+
for item in items:
|
|
72
|
+
# Try exact match first (case-insensitive)
|
|
73
|
+
if item["name"].lower() == name.lower():
|
|
74
|
+
return item
|
|
75
|
+
# Then try regex pattern match
|
|
76
|
+
if pattern.search(item["name"]):
|
|
77
|
+
return item
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
def _get_space(self, space_name: str) -> Dict[str, Any]:
|
|
81
|
+
"""Get space information by name."""
|
|
82
|
+
spaces = self._make_request("GET", f"team/{self.master_space_id}/space")
|
|
83
|
+
if "error" in spaces:
|
|
84
|
+
return spaces
|
|
85
|
+
|
|
86
|
+
spaces_list = spaces.get("spaces", [])
|
|
87
|
+
if not spaces_list:
|
|
88
|
+
return {"error": "No spaces found"}
|
|
89
|
+
|
|
90
|
+
space = self._find_by_name(spaces_list, space_name)
|
|
91
|
+
if not space:
|
|
92
|
+
return {"error": f"Space '{space_name}' not found"}
|
|
93
|
+
return space
|
|
94
|
+
|
|
95
|
+
def _get_list(self, space_id: str, list_name: str) -> Dict[str, Any]:
|
|
96
|
+
"""Get list information by name."""
|
|
97
|
+
lists = self._make_request("GET", f"space/{space_id}/list")
|
|
98
|
+
if "error" in lists:
|
|
99
|
+
return lists
|
|
100
|
+
|
|
101
|
+
lists_data = lists.get("lists", [])
|
|
102
|
+
if not lists_data:
|
|
103
|
+
return {"error": "No lists found in space"}
|
|
104
|
+
|
|
105
|
+
list_item = self._find_by_name(lists_data, list_name)
|
|
106
|
+
if not list_item:
|
|
107
|
+
return {"error": f"List '{list_name}' not found"}
|
|
108
|
+
return list_item
|
|
109
|
+
|
|
110
|
+
def _get_tasks(self, list_id: str) -> List[Dict[str, Any]]:
|
|
111
|
+
"""Get tasks in a list, optionally filtered by name."""
|
|
112
|
+
tasks = self._make_request("GET", f"list/{list_id}/task")
|
|
113
|
+
if "error" in tasks:
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
tasks_data = tasks.get("tasks", [])
|
|
117
|
+
return tasks_data
|
|
118
|
+
|
|
119
|
+
def list_tasks(self, space_name: str) -> str:
|
|
120
|
+
"""List all tasks in a space.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
space_name (str): Name of the space to list tasks from
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
str: JSON string containing tasks
|
|
127
|
+
"""
|
|
128
|
+
# Get space
|
|
129
|
+
space = self._get_space(space_name)
|
|
130
|
+
if "error" in space:
|
|
131
|
+
return json.dumps(space, indent=2)
|
|
132
|
+
|
|
133
|
+
# Get lists
|
|
134
|
+
lists = self._make_request("GET", f"space/{space['id']}/list")
|
|
135
|
+
lists_data = lists.get("lists", [])
|
|
136
|
+
if not lists_data:
|
|
137
|
+
return json.dumps({"error": f"No lists found in space '{space_name}'"}, indent=2)
|
|
138
|
+
|
|
139
|
+
# Get tasks from all lists
|
|
140
|
+
all_tasks = []
|
|
141
|
+
for list_info in lists_data:
|
|
142
|
+
tasks = self._get_tasks(list_info["id"])
|
|
143
|
+
for task in tasks:
|
|
144
|
+
task["list_name"] = list_info["name"] # Add list name for context
|
|
145
|
+
all_tasks.extend(tasks)
|
|
146
|
+
|
|
147
|
+
return json.dumps({"tasks": all_tasks}, indent=2)
|
|
148
|
+
|
|
149
|
+
def create_task(self, space_name: str, task_name: str, task_description: str) -> str:
|
|
150
|
+
"""Create a new task in a space.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
space_name (str): Name of the space to create task in
|
|
154
|
+
task_name (str): Name of the task
|
|
155
|
+
task_description (str): Description of the task
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
str: JSON string containing created task details
|
|
159
|
+
"""
|
|
160
|
+
# Get space
|
|
161
|
+
space = self._get_space(space_name)
|
|
162
|
+
if "error" in space:
|
|
163
|
+
return json.dumps(space, indent=2)
|
|
164
|
+
|
|
165
|
+
# Get first list in space
|
|
166
|
+
response = self._make_request("GET", f"space/{space['id']}/list")
|
|
167
|
+
log_debug(f"Lists: {response}")
|
|
168
|
+
lists_data = response.get("lists", [])
|
|
169
|
+
if not lists_data:
|
|
170
|
+
return json.dumps({"error": f"No lists found in space '{space_name}'"}, indent=2)
|
|
171
|
+
|
|
172
|
+
list_info = lists_data[0] # Use first list
|
|
173
|
+
|
|
174
|
+
# Create task
|
|
175
|
+
data = {"name": task_name, "description": task_description}
|
|
176
|
+
|
|
177
|
+
task = self._make_request("POST", f"list/{list_info['id']}/task", data=data)
|
|
178
|
+
return json.dumps(task, indent=2)
|
|
179
|
+
|
|
180
|
+
def list_spaces(self) -> str:
|
|
181
|
+
"""List all spaces in the workspace.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
str: JSON string containing list of spaces
|
|
185
|
+
"""
|
|
186
|
+
spaces = self._make_request("GET", f"team/{self.master_space_id}/space")
|
|
187
|
+
return json.dumps(spaces, indent=2)
|
|
188
|
+
|
|
189
|
+
def list_lists(self, space_name: str) -> str:
|
|
190
|
+
"""List all lists in a space.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
space_name (str): Name of the space to list lists from
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
str: JSON string containing list of lists
|
|
197
|
+
"""
|
|
198
|
+
# Get space
|
|
199
|
+
space = self._get_space(space_name)
|
|
200
|
+
if "error" in space:
|
|
201
|
+
return json.dumps(space, indent=2)
|
|
202
|
+
|
|
203
|
+
# Get lists
|
|
204
|
+
lists = self._make_request("GET", f"space/{space['id']}/list")
|
|
205
|
+
return json.dumps(lists, indent=2)
|
|
206
|
+
|
|
207
|
+
def get_task(self, task_id: str) -> str:
|
|
208
|
+
"""Get details of a specific task.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
task_id (str): The ID of the task
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
str: JSON string containing task details
|
|
215
|
+
"""
|
|
216
|
+
task = self._make_request("GET", f"task/{task_id}")
|
|
217
|
+
return json.dumps(task, indent=2)
|
|
218
|
+
|
|
219
|
+
def update_task(self, task_id: str, **kwargs) -> str:
|
|
220
|
+
"""Update a specific task.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
task_id (str): The ID of the task
|
|
224
|
+
**kwargs: Task fields to update (name, description, status, priority, etc.)
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
str: JSON string containing updated task details
|
|
228
|
+
"""
|
|
229
|
+
task = self._make_request("PUT", f"task/{task_id}", data=kwargs)
|
|
230
|
+
return json.dumps(task, indent=2)
|
|
231
|
+
|
|
232
|
+
def delete_task(self, task_id: str) -> str:
|
|
233
|
+
"""Delete a specific task.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
task_id (str): The ID of the task
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
str: JSON string containing deletion status
|
|
240
|
+
"""
|
|
241
|
+
result = self._make_request("DELETE", f"task/{task_id}")
|
|
242
|
+
if "error" not in result:
|
|
243
|
+
result = {"success": True, "message": f"Task {task_id} deleted successfully"}
|
|
244
|
+
return json.dumps(result, indent=2)
|
agno/tools/confluence.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, List, Optional
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from agno.tools import Toolkit
|
|
8
|
+
from agno.utils.log import log_info, logger
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from atlassian import Confluence
|
|
12
|
+
except (ModuleNotFoundError, ImportError):
|
|
13
|
+
raise ImportError("atlassian-python-api not install . Please install using `pip install atlassian-python-api`")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConfluenceTools(Toolkit):
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
username: Optional[str] = None,
|
|
20
|
+
password: Optional[str] = None,
|
|
21
|
+
url: Optional[str] = None,
|
|
22
|
+
api_key: Optional[str] = None,
|
|
23
|
+
verify_ssl: bool = True,
|
|
24
|
+
**kwargs,
|
|
25
|
+
):
|
|
26
|
+
"""Initialize Confluence Tools with authentication credentials.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
username (str, optional): Confluence username. Defaults to None.
|
|
30
|
+
password (str, optional): Confluence password. Defaults to None.
|
|
31
|
+
url (str, optional): Confluence instance URL. Defaults to None.
|
|
32
|
+
api_key (str, optional): Confluence API key. Defaults to None.
|
|
33
|
+
verify_ssl (bool, optional): Whether to verify SSL certificates. Defaults to True.
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
Credentials can be provided either through method arguments or environment variables:
|
|
37
|
+
- CONFLUENCE_URL
|
|
38
|
+
- CONFLUENCE_USERNAME
|
|
39
|
+
- CONFLUENCE_API_KEY
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
self.url = url or getenv("CONFLUENCE_URL")
|
|
43
|
+
self.username = username or getenv("CONFLUENCE_USERNAME")
|
|
44
|
+
self.password = api_key or getenv("CONFLUENCE_API_KEY") or password or getenv("CONFLUENCE_PASSWORD")
|
|
45
|
+
|
|
46
|
+
if not self.url:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"Confluence URL not provided. Pass it in the constructor or set CONFLUENCE_URL in environment variable"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if not self.username:
|
|
52
|
+
raise ValueError(
|
|
53
|
+
"Confluence username not provided. Pass it in the constructor or set CONFLUENCE_USERNAME in environment variable"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if not self.password:
|
|
57
|
+
raise ValueError("Confluence API KEY or password not provided")
|
|
58
|
+
|
|
59
|
+
session = requests.Session()
|
|
60
|
+
session.verify = verify_ssl
|
|
61
|
+
|
|
62
|
+
if not verify_ssl:
|
|
63
|
+
import urllib3
|
|
64
|
+
|
|
65
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
66
|
+
|
|
67
|
+
self.confluence = Confluence(
|
|
68
|
+
url=self.url,
|
|
69
|
+
username=self.username,
|
|
70
|
+
password=self.password,
|
|
71
|
+
verify_ssl=verify_ssl,
|
|
72
|
+
session=session,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
tools: List[Any] = [
|
|
76
|
+
self.get_page_content,
|
|
77
|
+
self.get_space_key,
|
|
78
|
+
self.create_page,
|
|
79
|
+
self.update_page,
|
|
80
|
+
self.get_all_space_detail,
|
|
81
|
+
self.get_all_page_from_space,
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
super().__init__(name="confluence_tools", tools=tools, **kwargs)
|
|
85
|
+
|
|
86
|
+
def get_page_content(self, space_name: str, page_title: str, expand: Optional[str] = "body.storage"):
|
|
87
|
+
"""Retrieve the content of a specific page in a Confluence space.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
space_name (str): Name of the Confluence space.
|
|
91
|
+
page_title (str): Title of the page to retrieve.
|
|
92
|
+
expand (str, optional): Fields to expand in the page response. Defaults to "body.storage".
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
str: JSON-encoded page content or error message.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
log_info(f"Retrieving page content from space '{space_name}'")
|
|
99
|
+
key = self.get_space_key(space_name=space_name)
|
|
100
|
+
if key == "No space found":
|
|
101
|
+
return json.dumps({"error": f"Space '{space_name}' not found"})
|
|
102
|
+
|
|
103
|
+
page = self.confluence.get_page_by_title(key, page_title, expand=expand)
|
|
104
|
+
if page:
|
|
105
|
+
log_info(f"Successfully retrieved page '{page_title}' from space '{space_name}'")
|
|
106
|
+
return json.dumps(page)
|
|
107
|
+
|
|
108
|
+
logger.warning(f"Page '{page_title}' not found in space '{space_name}'")
|
|
109
|
+
return json.dumps({"error": f"Page '{page_title}' not found in space '{space_name}'"})
|
|
110
|
+
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Error retrieving page '{page_title}': {e}")
|
|
113
|
+
return json.dumps({"error": str(e)})
|
|
114
|
+
|
|
115
|
+
def get_all_space_detail(self):
|
|
116
|
+
"""Retrieve details about all Confluence spaces.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
str: List of space details as a string.
|
|
120
|
+
"""
|
|
121
|
+
log_info("Retrieving details for all Confluence spaces")
|
|
122
|
+
results = []
|
|
123
|
+
start = 0
|
|
124
|
+
limit = 50
|
|
125
|
+
|
|
126
|
+
while True:
|
|
127
|
+
spaces_data = self.confluence.get_all_spaces(start=start, limit=limit)
|
|
128
|
+
if not spaces_data.get("results"):
|
|
129
|
+
break
|
|
130
|
+
results.extend(spaces_data["results"])
|
|
131
|
+
|
|
132
|
+
if len(spaces_data["results"]) < limit:
|
|
133
|
+
break
|
|
134
|
+
start += limit
|
|
135
|
+
|
|
136
|
+
return str(results)
|
|
137
|
+
|
|
138
|
+
def get_space_key(self, space_name: str):
|
|
139
|
+
"""Get the space key for a particular Confluence space.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
space_name (str): Name of the space whose key is required.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
str: Space key or "No space found" if space doesn't exist.
|
|
146
|
+
"""
|
|
147
|
+
start = 0
|
|
148
|
+
limit = 50
|
|
149
|
+
|
|
150
|
+
while True:
|
|
151
|
+
result = self.confluence.get_all_spaces(start=start, limit=limit)
|
|
152
|
+
if not result.get("results"):
|
|
153
|
+
break
|
|
154
|
+
|
|
155
|
+
spaces = result["results"]
|
|
156
|
+
|
|
157
|
+
for space in spaces:
|
|
158
|
+
if space["name"].lower() == space_name.lower():
|
|
159
|
+
log_info(f"Found space key for '{space_name}': {space['key']}")
|
|
160
|
+
return space["key"]
|
|
161
|
+
|
|
162
|
+
for space in spaces:
|
|
163
|
+
if space["key"] == space_name:
|
|
164
|
+
log_info(f"'{space_name}' is already a space key")
|
|
165
|
+
return space_name
|
|
166
|
+
|
|
167
|
+
if len(spaces) < limit:
|
|
168
|
+
break
|
|
169
|
+
start += limit
|
|
170
|
+
|
|
171
|
+
logger.warning(f"No space named {space_name} found")
|
|
172
|
+
return "No space found"
|
|
173
|
+
|
|
174
|
+
def get_all_page_from_space(self, space_name: str):
|
|
175
|
+
"""Retrieve all pages from a specific Confluence space.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
space_name (str): Name of the Confluence space.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
list: Details of pages in the specified space.
|
|
182
|
+
"""
|
|
183
|
+
log_info(f"Retrieving all pages from space '{space_name}'")
|
|
184
|
+
space_key = self.get_space_key(space_name)
|
|
185
|
+
|
|
186
|
+
if space_key == "No space found":
|
|
187
|
+
return json.dumps({"error": f"Space '{space_name}' not found"})
|
|
188
|
+
|
|
189
|
+
page_details = self.confluence.get_all_pages_from_space(
|
|
190
|
+
space_key, status=None, expand=None, content_type="page"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if not page_details:
|
|
194
|
+
return json.dumps({"error": f"No pages found in space '{space_name}'"})
|
|
195
|
+
|
|
196
|
+
page_details = str([{"id": page["id"], "title": page["title"]} for page in page_details])
|
|
197
|
+
return page_details
|
|
198
|
+
|
|
199
|
+
def create_page(self, space_name: str, title: str, body: str, parent_id: Optional[str] = None) -> str:
|
|
200
|
+
"""Create a new page in Confluence.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
space_name (str): Name of the Confluence space.
|
|
204
|
+
title (str): Title of the new page.
|
|
205
|
+
body (str): Content of the new page.
|
|
206
|
+
parent_id (str, optional): ID of the parent page if creating a child page. Defaults to None.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
str: JSON-encoded page ID and title or error message.
|
|
210
|
+
"""
|
|
211
|
+
try:
|
|
212
|
+
space_key = self.get_space_key(space_name=space_name)
|
|
213
|
+
if space_key == "No space found":
|
|
214
|
+
return json.dumps({"error": f"Space '{space_name}' not found"})
|
|
215
|
+
|
|
216
|
+
page = self.confluence.create_page(space_key, title, body, parent_id=parent_id)
|
|
217
|
+
log_info(f"Page created: {title} with ID {page['id']}")
|
|
218
|
+
return json.dumps({"id": page["id"], "title": title})
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.error(f"Error creating page '{title}': {e}")
|
|
221
|
+
return json.dumps({"error": str(e)})
|
|
222
|
+
|
|
223
|
+
def update_page(self, page_id: str, title: str, body: str) -> str:
|
|
224
|
+
"""Update an existing Confluence page.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
page_id (str): ID of the page to update.
|
|
228
|
+
title (str): New title for the page.
|
|
229
|
+
body (str): Updated content for the page.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
str: JSON-encoded status and ID of the updated page or error message.
|
|
233
|
+
"""
|
|
234
|
+
try:
|
|
235
|
+
updated_page = self.confluence.update_page(page_id, title, body)
|
|
236
|
+
log_info(f"Page updated: {title} with ID {updated_page['id']}")
|
|
237
|
+
return json.dumps({"status": "success", "id": updated_page["id"]})
|
|
238
|
+
except Exception as e:
|
|
239
|
+
logger.error(f"Error updating page '{title}': {e}")
|
|
240
|
+
return json.dumps({"error": str(e)})
|