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/opencv.py
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Callable, List
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from agno.agent import Agent
|
|
7
|
+
from agno.media import Image, Video
|
|
8
|
+
from agno.tools import Toolkit
|
|
9
|
+
from agno.tools.function import ToolResult
|
|
10
|
+
from agno.utils.log import log_debug, log_error, log_info
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import cv2
|
|
14
|
+
except ImportError:
|
|
15
|
+
raise ImportError("`opencv-python` package not found. Please install it with `pip install opencv-python`")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OpenCVTools(Toolkit):
|
|
19
|
+
"""Tools for capturing images and videos from the webcam using OpenCV"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
show_preview=False,
|
|
24
|
+
enable_capture_image: bool = True,
|
|
25
|
+
enable_capture_video: bool = True,
|
|
26
|
+
all: bool = False,
|
|
27
|
+
**kwargs,
|
|
28
|
+
):
|
|
29
|
+
self.show_preview = show_preview
|
|
30
|
+
|
|
31
|
+
tools: List[Callable] = []
|
|
32
|
+
if all or enable_capture_image:
|
|
33
|
+
tools.append(self.capture_image)
|
|
34
|
+
if all or enable_capture_video:
|
|
35
|
+
tools.append(self.capture_video)
|
|
36
|
+
|
|
37
|
+
super().__init__(
|
|
38
|
+
name="opencv_tools",
|
|
39
|
+
tools=tools,
|
|
40
|
+
**kwargs,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def capture_image(
|
|
44
|
+
self,
|
|
45
|
+
agent: Agent,
|
|
46
|
+
prompt: str = "Webcam capture",
|
|
47
|
+
) -> ToolResult:
|
|
48
|
+
"""Capture an image from the webcam.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
prompt (str): Description of the image capture. Defaults to "Webcam capture".
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
ToolResult: A ToolResult containing the captured image or error message.
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
log_debug("Initializing webcam for image capture...")
|
|
58
|
+
cam = cv2.VideoCapture(0)
|
|
59
|
+
|
|
60
|
+
if not cam.isOpened():
|
|
61
|
+
cam = cv2.VideoCapture(0, cv2.CAP_AVFOUNDATION) # macOS
|
|
62
|
+
if not cam.isOpened():
|
|
63
|
+
cam = cv2.VideoCapture(0, cv2.CAP_DSHOW) # Windows
|
|
64
|
+
if not cam.isOpened():
|
|
65
|
+
cam = cv2.VideoCapture(0, cv2.CAP_V4L2) # Linux
|
|
66
|
+
|
|
67
|
+
if not cam.isOpened():
|
|
68
|
+
error_msg = "Could not open webcam. Please ensure your terminal has camera permissions and the camera is not being used by another application."
|
|
69
|
+
log_error(error_msg)
|
|
70
|
+
return ToolResult(content=error_msg)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
|
|
74
|
+
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
|
|
75
|
+
cam.set(cv2.CAP_PROP_FPS, 30)
|
|
76
|
+
|
|
77
|
+
log_debug("Camera initialized successfully")
|
|
78
|
+
captured_frame = None
|
|
79
|
+
|
|
80
|
+
if self.show_preview:
|
|
81
|
+
log_info("Live preview started. Press 'c' to capture image, 'q' to quit.")
|
|
82
|
+
|
|
83
|
+
while True:
|
|
84
|
+
ret, frame = cam.read()
|
|
85
|
+
if not ret:
|
|
86
|
+
error_msg = "Failed to read frame from webcam"
|
|
87
|
+
log_error(error_msg)
|
|
88
|
+
return ToolResult(content=error_msg)
|
|
89
|
+
|
|
90
|
+
cv2.imshow('Camera Preview - Press "c" to capture, "q" to quit', frame)
|
|
91
|
+
|
|
92
|
+
key = cv2.waitKey(1) & 0xFF
|
|
93
|
+
if key == ord("c"):
|
|
94
|
+
captured_frame = frame.copy()
|
|
95
|
+
log_info("Image captured!")
|
|
96
|
+
break
|
|
97
|
+
elif key == ord("q"):
|
|
98
|
+
log_info("Capture cancelled by user")
|
|
99
|
+
return ToolResult(content="Image capture cancelled by user")
|
|
100
|
+
else:
|
|
101
|
+
ret, captured_frame = cam.read()
|
|
102
|
+
if not ret:
|
|
103
|
+
error_msg = "Failed to capture image from webcam"
|
|
104
|
+
log_error(error_msg)
|
|
105
|
+
return ToolResult(content=error_msg)
|
|
106
|
+
|
|
107
|
+
if captured_frame is None:
|
|
108
|
+
error_msg = "No frame captured"
|
|
109
|
+
log_error(error_msg)
|
|
110
|
+
return ToolResult(content=error_msg)
|
|
111
|
+
|
|
112
|
+
success, encoded_image = cv2.imencode(".png", captured_frame)
|
|
113
|
+
|
|
114
|
+
if not success:
|
|
115
|
+
error_msg = "Failed to encode captured image"
|
|
116
|
+
log_error(error_msg)
|
|
117
|
+
return ToolResult(content=error_msg)
|
|
118
|
+
|
|
119
|
+
image_bytes = encoded_image.tobytes()
|
|
120
|
+
media_id = str(uuid4())
|
|
121
|
+
|
|
122
|
+
# Create ImageArtifact with raw bytes (not base64 encoded)
|
|
123
|
+
image_artifact = Image(
|
|
124
|
+
id=media_id,
|
|
125
|
+
content=image_bytes, # Store as raw bytes
|
|
126
|
+
original_prompt=prompt,
|
|
127
|
+
mime_type="image/png",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
log_debug(f"Successfully captured and attached image {media_id}")
|
|
131
|
+
return ToolResult(
|
|
132
|
+
content="Image captured successfully",
|
|
133
|
+
images=[image_artifact],
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
finally:
|
|
137
|
+
# Release the camera and close windows
|
|
138
|
+
cam.release()
|
|
139
|
+
cv2.destroyAllWindows()
|
|
140
|
+
log_debug("Camera resources released")
|
|
141
|
+
|
|
142
|
+
except Exception as e:
|
|
143
|
+
error_msg = f"Error capturing image: {str(e)}"
|
|
144
|
+
log_error(error_msg)
|
|
145
|
+
return ToolResult(content=error_msg)
|
|
146
|
+
|
|
147
|
+
def capture_video(
|
|
148
|
+
self,
|
|
149
|
+
agent: Agent,
|
|
150
|
+
duration: int = 10,
|
|
151
|
+
prompt: str = "Webcam video capture",
|
|
152
|
+
) -> ToolResult:
|
|
153
|
+
"""Capture a video from the webcam.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
duration (int): Duration in seconds to record video. Defaults to 10 seconds.
|
|
157
|
+
prompt (str): Description of the video capture. Defaults to "Webcam video capture".
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
ToolResult: A ToolResult containing the captured video or error message.
|
|
161
|
+
"""
|
|
162
|
+
try:
|
|
163
|
+
log_debug("Initializing webcam for video capture...")
|
|
164
|
+
cap = cv2.VideoCapture(0)
|
|
165
|
+
|
|
166
|
+
# Try different backends for better compatibility
|
|
167
|
+
if not cap.isOpened():
|
|
168
|
+
cap = cv2.VideoCapture(0, cv2.CAP_AVFOUNDATION) # macOS
|
|
169
|
+
if not cap.isOpened():
|
|
170
|
+
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # Windows
|
|
171
|
+
if not cap.isOpened():
|
|
172
|
+
cap = cv2.VideoCapture(0, cv2.CAP_V4L2) # Linux
|
|
173
|
+
|
|
174
|
+
if not cap.isOpened():
|
|
175
|
+
error_msg = "Could not open webcam. Please ensure your terminal has camera permissions and the camera is not being used by another application."
|
|
176
|
+
log_error(error_msg)
|
|
177
|
+
return ToolResult(content=error_msg)
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
181
|
+
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
182
|
+
actual_fps = cap.get(cv2.CAP_PROP_FPS)
|
|
183
|
+
|
|
184
|
+
# Use actual FPS or default to 30 if detection fails
|
|
185
|
+
if actual_fps <= 0 or actual_fps > 60:
|
|
186
|
+
actual_fps = 30.0
|
|
187
|
+
|
|
188
|
+
log_debug(f"Video properties: {frame_width}x{frame_height} at {actual_fps} FPS")
|
|
189
|
+
|
|
190
|
+
# Try different codecs in order of preference for compatibility
|
|
191
|
+
codecs_to_try = [
|
|
192
|
+
("avc1", "H.264"), # Most compatible
|
|
193
|
+
("mp4v", "MPEG-4"), # Fallback
|
|
194
|
+
("XVID", "Xvid"), # Another fallback
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
import os
|
|
198
|
+
import tempfile
|
|
199
|
+
|
|
200
|
+
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
|
|
201
|
+
temp_filepath = temp_file.name
|
|
202
|
+
|
|
203
|
+
out = None
|
|
204
|
+
successful_codec = None
|
|
205
|
+
|
|
206
|
+
for codec_fourcc, codec_name in codecs_to_try:
|
|
207
|
+
try:
|
|
208
|
+
fourcc = getattr(cv2, "VideoWriter_fourcc")(*codec_fourcc)
|
|
209
|
+
out = cv2.VideoWriter(temp_filepath, fourcc, actual_fps, (frame_width, frame_height))
|
|
210
|
+
|
|
211
|
+
if out.isOpened():
|
|
212
|
+
successful_codec = codec_name
|
|
213
|
+
log_debug(f"Successfully initialized video writer with {codec_name} codec")
|
|
214
|
+
break
|
|
215
|
+
else:
|
|
216
|
+
out.release()
|
|
217
|
+
out = None
|
|
218
|
+
except Exception as e:
|
|
219
|
+
log_debug(f"Failed to initialize {codec_name} codec: {e}")
|
|
220
|
+
if out:
|
|
221
|
+
out.release()
|
|
222
|
+
out = None
|
|
223
|
+
|
|
224
|
+
if not out or not out.isOpened():
|
|
225
|
+
error_msg = "Failed to initialize video writer with any codec"
|
|
226
|
+
log_error(error_msg)
|
|
227
|
+
return ToolResult(content=error_msg)
|
|
228
|
+
|
|
229
|
+
start_time = time.time()
|
|
230
|
+
frame_count = 0
|
|
231
|
+
|
|
232
|
+
if self.show_preview:
|
|
233
|
+
log_info(f"Recording {duration}s video with live preview using {successful_codec} codec...")
|
|
234
|
+
else:
|
|
235
|
+
log_info(f"Recording {duration}s video using {successful_codec} codec...")
|
|
236
|
+
|
|
237
|
+
while True:
|
|
238
|
+
ret, frame = cap.read()
|
|
239
|
+
|
|
240
|
+
if not ret:
|
|
241
|
+
error_msg = "Failed to capture video frame"
|
|
242
|
+
log_error(error_msg)
|
|
243
|
+
return ToolResult(content=error_msg)
|
|
244
|
+
|
|
245
|
+
# Write the frame to the output file
|
|
246
|
+
out.write(frame)
|
|
247
|
+
frame_count += 1
|
|
248
|
+
|
|
249
|
+
# Show live preview if enabled
|
|
250
|
+
if self.show_preview:
|
|
251
|
+
# Add recording indicator
|
|
252
|
+
elapsed = time.time() - start_time
|
|
253
|
+
remaining = max(0, duration - elapsed)
|
|
254
|
+
|
|
255
|
+
# Draw recording info on frame
|
|
256
|
+
display_frame = frame.copy()
|
|
257
|
+
cv2.putText(
|
|
258
|
+
display_frame,
|
|
259
|
+
f"REC {remaining:.1f}s",
|
|
260
|
+
(10, 30),
|
|
261
|
+
cv2.FONT_HERSHEY_SIMPLEX,
|
|
262
|
+
1,
|
|
263
|
+
(0, 0, 255),
|
|
264
|
+
2,
|
|
265
|
+
)
|
|
266
|
+
cv2.circle(display_frame, (30, 60), 10, (0, 0, 255), -1) # Red dot
|
|
267
|
+
|
|
268
|
+
cv2.imshow(f"Recording Video - {remaining:.1f}s remaining", display_frame)
|
|
269
|
+
cv2.waitKey(1)
|
|
270
|
+
|
|
271
|
+
# Check if recording duration is reached
|
|
272
|
+
if time.time() - start_time >= duration:
|
|
273
|
+
break
|
|
274
|
+
|
|
275
|
+
# Release video writer
|
|
276
|
+
out.release()
|
|
277
|
+
|
|
278
|
+
# Verify the file was created and has content
|
|
279
|
+
temp_path = Path(temp_filepath)
|
|
280
|
+
if not temp_path.exists() or temp_path.stat().st_size == 0:
|
|
281
|
+
error_msg = "Video file was not created or is empty"
|
|
282
|
+
log_error(error_msg)
|
|
283
|
+
return ToolResult(content=error_msg)
|
|
284
|
+
|
|
285
|
+
# Read the video file and encode to base64
|
|
286
|
+
with open(temp_filepath, "rb") as video_file:
|
|
287
|
+
video_bytes = video_file.read()
|
|
288
|
+
|
|
289
|
+
# Clean up temporary file
|
|
290
|
+
os.unlink(temp_filepath)
|
|
291
|
+
|
|
292
|
+
media_id = str(uuid4())
|
|
293
|
+
|
|
294
|
+
# Create VideoArtifact with base64 encoded content
|
|
295
|
+
video_artifact = Video(
|
|
296
|
+
id=media_id,
|
|
297
|
+
content=video_bytes,
|
|
298
|
+
original_prompt=prompt,
|
|
299
|
+
mime_type="video/mp4",
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
actual_duration = time.time() - start_time
|
|
303
|
+
log_debug(
|
|
304
|
+
f"Successfully captured and attached video {media_id} ({actual_duration:.1f}s, {frame_count} frames)"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return ToolResult(
|
|
308
|
+
content=f"Video captured successfully and attached as artifact {media_id} ({actual_duration:.1f}s, {frame_count} frames, {successful_codec} codec)",
|
|
309
|
+
videos=[video_artifact],
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
finally:
|
|
313
|
+
if "cap" in locals():
|
|
314
|
+
cap.release()
|
|
315
|
+
cv2.destroyAllWindows()
|
|
316
|
+
log_debug("Video capture resources released")
|
|
317
|
+
|
|
318
|
+
except Exception as e:
|
|
319
|
+
error_msg = f"Error capturing video: {str(e)}"
|
|
320
|
+
log_error(error_msg)
|
|
321
|
+
return ToolResult(content=error_msg)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from os import getenv
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from agno.tools import Toolkit
|
|
6
|
+
from agno.utils.log import log_info, logger
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import requests
|
|
10
|
+
except ImportError:
|
|
11
|
+
raise ImportError("`requests` not installed. Please install using `pip install requests`")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OpenWeatherTools(Toolkit):
|
|
15
|
+
"""
|
|
16
|
+
OpenWeather is a toolkit for accessing weather data from OpenWeatherMap API.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
api_key (Optional[str]): OpenWeatherMap API key. If not provided, will try to get from OPENWEATHER_API_KEY env var.
|
|
20
|
+
units (str): Units of measurement. Options are 'standard', 'metric', and 'imperial'. Default is 'metric'.
|
|
21
|
+
enable_current_weather (bool): Enable current weather function. Default is True.
|
|
22
|
+
enable_forecast (bool): Enable forecast function. Default is True.
|
|
23
|
+
enable_air_pollution (bool): Enable air pollution function. Default is True.
|
|
24
|
+
enable_geocoding (bool): Enable geocoding function. Default is True.
|
|
25
|
+
all (bool): Enable all functions. Default is False.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
api_key: Optional[str] = None,
|
|
31
|
+
units: str = "metric",
|
|
32
|
+
enable_current_weather: bool = True,
|
|
33
|
+
enable_forecast: bool = True,
|
|
34
|
+
enable_air_pollution: bool = True,
|
|
35
|
+
enable_geocoding: bool = True,
|
|
36
|
+
all: bool = False,
|
|
37
|
+
**kwargs,
|
|
38
|
+
):
|
|
39
|
+
self.api_key = api_key or getenv("OPENWEATHER_API_KEY")
|
|
40
|
+
if not self.api_key:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"OpenWeather API key is required. Provide it as an argument or set the OPENWEATHER_API_KEY environment variable."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
self.units = units
|
|
46
|
+
self.base_url = "https://api.openweathermap.org/data/2.5"
|
|
47
|
+
self.geo_url = "https://api.openweathermap.org/geo/1.0"
|
|
48
|
+
|
|
49
|
+
tools: List[Any] = []
|
|
50
|
+
if enable_current_weather or all:
|
|
51
|
+
tools.append(self.get_current_weather)
|
|
52
|
+
if enable_forecast or all:
|
|
53
|
+
tools.append(self.get_forecast)
|
|
54
|
+
if enable_air_pollution or all:
|
|
55
|
+
tools.append(self.get_air_pollution)
|
|
56
|
+
if enable_geocoding or all:
|
|
57
|
+
tools.append(self.geocode_location)
|
|
58
|
+
|
|
59
|
+
super().__init__(name="openweather_tools", tools=tools, **kwargs)
|
|
60
|
+
|
|
61
|
+
def _make_request(self, url: str, params: Dict) -> Dict:
|
|
62
|
+
"""Make a request to the OpenWeatherMap API.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
url (str): The API endpoint URL.
|
|
66
|
+
params (Dict): Query parameters for the request.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Dict: The JSON response from the API.
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
params["appid"] = self.api_key
|
|
73
|
+
response = requests.get(url, params=params)
|
|
74
|
+
response.raise_for_status()
|
|
75
|
+
return response.json()
|
|
76
|
+
except requests.exceptions.RequestException as e:
|
|
77
|
+
logger.error(f"Error making request to {url}: {e}")
|
|
78
|
+
return {"error": str(e)}
|
|
79
|
+
|
|
80
|
+
def geocode_location(self, location: str, limit: int = 1) -> str:
|
|
81
|
+
"""Convert a location name to geographic coordinates.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
location (str): The name of the city, e.g., "London", "Paris", "New York".
|
|
85
|
+
limit (int): Maximum number of location results. Default is 1.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
str: JSON string containing location data with coordinates.
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
log_info(f"Geocoding location: {location}")
|
|
92
|
+
url = f"{self.geo_url}/direct"
|
|
93
|
+
params = {"q": location, "limit": limit}
|
|
94
|
+
|
|
95
|
+
result = self._make_request(url, params)
|
|
96
|
+
|
|
97
|
+
if "error" in result:
|
|
98
|
+
return json.dumps(result)
|
|
99
|
+
|
|
100
|
+
if not result:
|
|
101
|
+
return json.dumps({"error": f"No location found for '{location}'"})
|
|
102
|
+
|
|
103
|
+
return json.dumps(result, indent=2)
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.error(f"Error geocoding location: {e}")
|
|
106
|
+
return json.dumps({"error": str(e)})
|
|
107
|
+
|
|
108
|
+
def get_current_weather(self, location: str) -> str:
|
|
109
|
+
"""Get current weather data for a location.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
location (str): The name of the city, e.g., "London", "Paris", "New York".
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
str: JSON string containing current weather data.
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
log_info(f"Getting current weather for: {location}")
|
|
119
|
+
|
|
120
|
+
# First geocode the location to get coordinates
|
|
121
|
+
geocode_result = json.loads(self.geocode_location(location))
|
|
122
|
+
if "error" in geocode_result:
|
|
123
|
+
return json.dumps(geocode_result)
|
|
124
|
+
|
|
125
|
+
if not geocode_result:
|
|
126
|
+
return json.dumps({"error": f"No location found for '{location}'"})
|
|
127
|
+
|
|
128
|
+
# Get the first location result
|
|
129
|
+
loc_data = geocode_result[0]
|
|
130
|
+
lat, lon = loc_data["lat"], loc_data["lon"]
|
|
131
|
+
|
|
132
|
+
# Get current weather using coordinates
|
|
133
|
+
url = f"{self.base_url}/weather"
|
|
134
|
+
params = {"lat": lat, "lon": lon, "units": self.units}
|
|
135
|
+
|
|
136
|
+
result = self._make_request(url, params)
|
|
137
|
+
|
|
138
|
+
# Add the location name to the result
|
|
139
|
+
if "error" not in result:
|
|
140
|
+
result["location_name"] = loc_data.get("name", location)
|
|
141
|
+
result["country"] = loc_data.get("country", "")
|
|
142
|
+
|
|
143
|
+
return json.dumps(result, indent=2)
|
|
144
|
+
except Exception as e:
|
|
145
|
+
logger.error(f"Error getting current weather: {e}")
|
|
146
|
+
return json.dumps({"error": str(e)})
|
|
147
|
+
|
|
148
|
+
def get_forecast(self, location: str, days: int = 5) -> str:
|
|
149
|
+
"""Get weather forecast for a location.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
location (str): The name of the city, e.g., "London", "Paris", "New York".
|
|
153
|
+
days (int): Number of days for forecast (max 5). Default is 5.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
str: JSON string containing forecast data.
|
|
157
|
+
"""
|
|
158
|
+
try:
|
|
159
|
+
log_info(f"Getting {days}-day forecast for: {location}")
|
|
160
|
+
|
|
161
|
+
# First geocode the location to get coordinates
|
|
162
|
+
geocode_result = json.loads(self.geocode_location(location))
|
|
163
|
+
if "error" in geocode_result:
|
|
164
|
+
return json.dumps(geocode_result)
|
|
165
|
+
|
|
166
|
+
if not geocode_result:
|
|
167
|
+
return json.dumps({"error": f"No location found for '{location}'"})
|
|
168
|
+
|
|
169
|
+
# Get the first location result
|
|
170
|
+
loc_data = geocode_result[0]
|
|
171
|
+
lat, lon = loc_data["lat"], loc_data["lon"]
|
|
172
|
+
|
|
173
|
+
# Get forecast using coordinates
|
|
174
|
+
url = f"{self.base_url}/forecast"
|
|
175
|
+
params = {
|
|
176
|
+
"lat": lat,
|
|
177
|
+
"lon": lon,
|
|
178
|
+
"units": self.units,
|
|
179
|
+
# Each day has 8 3-hour forecasts, max 5 days (40 entries)
|
|
180
|
+
"cnt": min(days * 8, 40),
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
result = self._make_request(url, params)
|
|
184
|
+
|
|
185
|
+
# Add the location name to the result
|
|
186
|
+
if "error" not in result:
|
|
187
|
+
result["location_name"] = loc_data.get("name", location)
|
|
188
|
+
result["country"] = loc_data.get("country", "")
|
|
189
|
+
|
|
190
|
+
return json.dumps(result, indent=2)
|
|
191
|
+
except Exception as e:
|
|
192
|
+
logger.error(f"Error getting forecast: {e}")
|
|
193
|
+
return json.dumps({"error": str(e)})
|
|
194
|
+
|
|
195
|
+
def get_air_pollution(self, location: str) -> str:
|
|
196
|
+
"""Get current air pollution data for a location.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
location (str): The name of the city, e.g., "London", "Paris", "New York".
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
str: JSON string containing air pollution data.
|
|
203
|
+
"""
|
|
204
|
+
try:
|
|
205
|
+
log_info(f"Getting air pollution data for: {location}")
|
|
206
|
+
|
|
207
|
+
# First geocode the location to get coordinates
|
|
208
|
+
geocode_result = json.loads(self.geocode_location(location))
|
|
209
|
+
if "error" in geocode_result:
|
|
210
|
+
return json.dumps(geocode_result)
|
|
211
|
+
|
|
212
|
+
if not geocode_result:
|
|
213
|
+
return json.dumps({"error": f"No location found for '{location}'"})
|
|
214
|
+
|
|
215
|
+
# Get the first location result
|
|
216
|
+
loc_data = geocode_result[0]
|
|
217
|
+
lat, lon = loc_data["lat"], loc_data["lon"]
|
|
218
|
+
|
|
219
|
+
# Get air pollution data using coordinates
|
|
220
|
+
url = f"{self.base_url}/air_pollution"
|
|
221
|
+
params = {"lat": lat, "lon": lon}
|
|
222
|
+
|
|
223
|
+
result = self._make_request(url, params)
|
|
224
|
+
|
|
225
|
+
# Add the location name to the result
|
|
226
|
+
if "error" not in result:
|
|
227
|
+
result["location_name"] = loc_data.get("name", location)
|
|
228
|
+
result["country"] = loc_data.get("country", "")
|
|
229
|
+
|
|
230
|
+
return json.dumps(result, indent=2)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"Error getting air pollution data: {e}")
|
|
233
|
+
return json.dumps({"error": str(e)})
|