agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/filters.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Search filter expressions for filtering knowledge base documents and search results.
|
|
2
|
+
|
|
3
|
+
This module provides a set of filter operators for constructing complex search queries
|
|
4
|
+
that can be applied to knowledge bases, vector databases, and other searchable content.
|
|
5
|
+
|
|
6
|
+
Filter Types:
|
|
7
|
+
- Comparison: EQ (equals), GT (greater than), LT (less than)
|
|
8
|
+
- Inclusion: IN (value in list)
|
|
9
|
+
- Logical: AND, OR, NOT
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> from agno.filters import EQ, GT, IN, AND, OR, NOT
|
|
13
|
+
>>>
|
|
14
|
+
>>> # Simple equality filter
|
|
15
|
+
>>> filter = EQ("category", "technology")
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Complex filter with multiple conditions
|
|
18
|
+
>>> filter = AND(
|
|
19
|
+
... EQ("status", "published"),
|
|
20
|
+
... GT("views", 1000),
|
|
21
|
+
... IN("category", ["tech", "science"])
|
|
22
|
+
... )
|
|
23
|
+
>>>
|
|
24
|
+
>>> # Using OR logic
|
|
25
|
+
>>> filter = OR(EQ("priority", "high"), EQ("urgent", True))
|
|
26
|
+
>>>
|
|
27
|
+
>>> # Negating conditions
|
|
28
|
+
>>> filter = NOT(EQ("status", "archived"))
|
|
29
|
+
>>>
|
|
30
|
+
>>> # Complex nested logic
|
|
31
|
+
>>> filter = OR(
|
|
32
|
+
... AND(EQ("type", "article"), GT("word_count", 500)),
|
|
33
|
+
... AND(EQ("type", "tutorial"), NOT(EQ("difficulty", "beginner")))
|
|
34
|
+
... )
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
from typing import Any, List
|
|
40
|
+
|
|
41
|
+
# ============================================================
|
|
42
|
+
# Base Expression
|
|
43
|
+
# ============================================================
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class FilterExpr:
|
|
47
|
+
"""Base class for all filter expressions.
|
|
48
|
+
|
|
49
|
+
Filters can be combined using AND, OR, and NOT classes:
|
|
50
|
+
- AND: Combine filters where both expressions must be true
|
|
51
|
+
- OR: Combine filters where either expression can be true
|
|
52
|
+
- NOT: Negate a filter expression
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
>>> # Create complex filters using AND, OR, NOT
|
|
56
|
+
>>> filter = OR(AND(EQ("status", "active"), GT("age", 18)), EQ("role", "admin"))
|
|
57
|
+
>>> # Equivalent to: (status == "active" AND age > 18) OR role == "admin"
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# Logical operator overloads
|
|
61
|
+
def __or__(self, other: FilterExpr) -> OR:
|
|
62
|
+
"""Combine two filters with OR logic."""
|
|
63
|
+
return OR(self, other)
|
|
64
|
+
|
|
65
|
+
def __and__(self, other: FilterExpr) -> AND:
|
|
66
|
+
"""Combine two filters with AND logic."""
|
|
67
|
+
return AND(self, other)
|
|
68
|
+
|
|
69
|
+
def __invert__(self) -> NOT:
|
|
70
|
+
"""Negate a filter."""
|
|
71
|
+
return NOT(self)
|
|
72
|
+
|
|
73
|
+
def to_dict(self) -> dict:
|
|
74
|
+
"""Convert the filter expression to a dictionary representation."""
|
|
75
|
+
raise NotImplementedError("Subclasses must implement to_dict()")
|
|
76
|
+
|
|
77
|
+
def __repr__(self) -> str:
|
|
78
|
+
return f"{self.__class__.__name__}({self.__dict__})"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ============================================================
|
|
82
|
+
# Comparison & Inclusion Filters
|
|
83
|
+
# ============================================================
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class EQ(FilterExpr):
|
|
87
|
+
"""Equality filter - matches documents where a field equals a specific value.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
key: The field name to compare
|
|
91
|
+
value: The value to match against
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
>>> # Match documents where status is "published"
|
|
95
|
+
>>> filter = EQ("status", "published")
|
|
96
|
+
>>>
|
|
97
|
+
>>> # Match documents where author_id is 123
|
|
98
|
+
>>> filter = EQ("author_id", 123)
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(self, key: str, value: Any):
|
|
102
|
+
self.key = key
|
|
103
|
+
self.value = value
|
|
104
|
+
|
|
105
|
+
def to_dict(self) -> dict:
|
|
106
|
+
return {"op": "EQ", "key": self.key, "value": self.value}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class IN(FilterExpr):
|
|
110
|
+
"""Inclusion filter - matches documents where a field's value is in a list of values.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
key: The field name to check
|
|
114
|
+
values: List of acceptable values
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
>>> # Match documents where category is either "tech", "science", or "engineering"
|
|
118
|
+
>>> filter = IN("category", ["tech", "science", "engineering"])
|
|
119
|
+
>>>
|
|
120
|
+
>>> # Match documents where status is either "draft" or "published"
|
|
121
|
+
>>> filter = IN("status", ["draft", "published"])
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def __init__(self, key: str, values: List[Any]):
|
|
125
|
+
self.key = key
|
|
126
|
+
self.values = values
|
|
127
|
+
|
|
128
|
+
def to_dict(self) -> dict:
|
|
129
|
+
return {"op": "IN", "key": self.key, "values": self.values}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class GT(FilterExpr):
|
|
133
|
+
"""Greater than filter - matches documents where a field's value is greater than a threshold.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
key: The field name to compare
|
|
137
|
+
value: The threshold value
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
>>> # Match documents where age is greater than 18
|
|
141
|
+
>>> filter = GT("age", 18)
|
|
142
|
+
>>>
|
|
143
|
+
>>> # Match documents where price is greater than 100.0
|
|
144
|
+
>>> filter = GT("price", 100.0)
|
|
145
|
+
>>>
|
|
146
|
+
>>> # Match documents created after a certain timestamp
|
|
147
|
+
>>> filter = GT("created_at", 1234567890)
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, key: str, value: Any):
|
|
151
|
+
self.key = key
|
|
152
|
+
self.value = value
|
|
153
|
+
|
|
154
|
+
def to_dict(self) -> dict:
|
|
155
|
+
return {"op": "GT", "key": self.key, "value": self.value}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class LT(FilterExpr):
|
|
159
|
+
"""Less than filter - matches documents where a field's value is less than a threshold.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
key: The field name to compare
|
|
163
|
+
value: The threshold value
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
>>> # Match documents where age is less than 65
|
|
167
|
+
>>> filter = LT("age", 65)
|
|
168
|
+
>>>
|
|
169
|
+
>>> # Match documents where price is less than 50.0
|
|
170
|
+
>>> filter = LT("price", 50.0)
|
|
171
|
+
>>>
|
|
172
|
+
>>> # Match documents created before a certain timestamp
|
|
173
|
+
>>> filter = LT("created_at", 1234567890)
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def __init__(self, key: str, value: Any):
|
|
177
|
+
self.key = key
|
|
178
|
+
self.value = value
|
|
179
|
+
|
|
180
|
+
def to_dict(self) -> dict:
|
|
181
|
+
return {"op": "LT", "key": self.key, "value": self.value}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ============================================================
|
|
185
|
+
# Logical Operators
|
|
186
|
+
# ============================================================
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class AND(FilterExpr):
|
|
190
|
+
"""Logical AND operator - matches documents where ALL expressions are true.
|
|
191
|
+
|
|
192
|
+
Combines multiple filter expressions where every expression must be satisfied
|
|
193
|
+
for a document to match.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
*expressions: Variable number of FilterExpr expressions to combine with AND logic
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
>>> # Match documents where status is "published" AND age > 18
|
|
200
|
+
>>> filter = AND(EQ("status", "published"), GT("age", 18))
|
|
201
|
+
>>>
|
|
202
|
+
>>> # Multiple expressions
|
|
203
|
+
>>> filter = AND(
|
|
204
|
+
... EQ("status", "active"),
|
|
205
|
+
... GT("score", 80),
|
|
206
|
+
... IN("category", ["tech", "science"])
|
|
207
|
+
... )
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(self, *expressions: FilterExpr):
|
|
211
|
+
self.expressions = list(expressions)
|
|
212
|
+
|
|
213
|
+
def to_dict(self) -> dict:
|
|
214
|
+
return {"op": "AND", "conditions": [e.to_dict() for e in self.expressions]}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class OR(FilterExpr):
|
|
218
|
+
"""Logical OR operator - matches documents where ANY expression is true.
|
|
219
|
+
|
|
220
|
+
Combines multiple filter expressions where at least one expression must be satisfied
|
|
221
|
+
for a document to match.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
*expressions: Variable number of FilterExpr expressions to combine with OR logic
|
|
225
|
+
|
|
226
|
+
Example:
|
|
227
|
+
>>> # Match documents where status is "published" OR status is "archived"
|
|
228
|
+
>>> filter = OR(EQ("status", "published"), EQ("status", "archived"))
|
|
229
|
+
>>>
|
|
230
|
+
>>> # Complex: Match VIP users OR users with high score
|
|
231
|
+
>>> filter = OR(
|
|
232
|
+
... EQ("membership", "VIP"),
|
|
233
|
+
... GT("score", 1000)
|
|
234
|
+
... )
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
def __init__(self, *expressions: FilterExpr):
|
|
238
|
+
self.expressions = list(expressions)
|
|
239
|
+
|
|
240
|
+
def to_dict(self) -> dict:
|
|
241
|
+
return {"op": "OR", "conditions": [e.to_dict() for e in self.expressions]}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class NOT(FilterExpr):
|
|
245
|
+
"""Logical NOT operator - matches documents where the expression is NOT true.
|
|
246
|
+
|
|
247
|
+
Negates a filter expression, matching documents that don't satisfy the expression.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
expression: The FilterExpr expression to negate
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
>>> # Match documents where status is NOT "draft"
|
|
254
|
+
>>> filter = NOT(EQ("status", "draft"))
|
|
255
|
+
>>>
|
|
256
|
+
>>> # Exclude inactive users with low scores
|
|
257
|
+
>>> filter = NOT(AND(EQ("status", "inactive"), LT("score", 10)))
|
|
258
|
+
>>>
|
|
259
|
+
>>> # Match users who are NOT in the blocked list
|
|
260
|
+
>>> filter = NOT(IN("user_id", [101, 102, 103]))
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
def __init__(self, expression: FilterExpr):
|
|
264
|
+
self.expression = expression
|
|
265
|
+
|
|
266
|
+
def to_dict(self) -> dict:
|
|
267
|
+
return {"op": "NOT", "condition": self.expression.to_dict()}
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# ============================================================
|
|
271
|
+
# Deserialization
|
|
272
|
+
# ============================================================
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def from_dict(filter_dict: dict) -> FilterExpr:
|
|
276
|
+
"""Reconstruct a FilterExpr object from its dictionary representation.
|
|
277
|
+
|
|
278
|
+
This function deserializes filter expressions that were serialized using the
|
|
279
|
+
to_dict() method, enabling filters to be passed through JSON APIs and reconstructed
|
|
280
|
+
on the server side.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
filter_dict: Dictionary representation of a filter expression with an "op" key
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
FilterExpr: The reconstructed filter expression object
|
|
287
|
+
|
|
288
|
+
Raises:
|
|
289
|
+
ValueError: If the filter dictionary has an invalid structure or unknown operator
|
|
290
|
+
|
|
291
|
+
Example:
|
|
292
|
+
>>> # Serialize and deserialize a simple filter
|
|
293
|
+
>>> original = EQ("status", "published")
|
|
294
|
+
>>> serialized = original.to_dict()
|
|
295
|
+
>>> # {"op": "EQ", "key": "status", "value": "published"}
|
|
296
|
+
>>> reconstructed = from_dict(serialized)
|
|
297
|
+
>>>
|
|
298
|
+
>>> # Complex filter with nested expressions
|
|
299
|
+
>>> complex_filter = OR(AND(EQ("type", "article"), GT("views", 1000)), IN("priority", ["high", "urgent"]))
|
|
300
|
+
>>> serialized = complex_filter.to_dict()
|
|
301
|
+
>>> reconstructed = from_dict(serialized)
|
|
302
|
+
>>>
|
|
303
|
+
>>> # From JSON API
|
|
304
|
+
>>> import json
|
|
305
|
+
>>> json_str = '{"op": "AND", "conditions": [{"op": "EQ", "key": "status", "value": "active"}, {"op": "GT", "key": "age", "value": 18}]}'
|
|
306
|
+
>>> filter_dict = json.loads(json_str)
|
|
307
|
+
>>> filter_expr = from_dict(filter_dict)
|
|
308
|
+
"""
|
|
309
|
+
if not isinstance(filter_dict, dict) or "op" not in filter_dict:
|
|
310
|
+
raise ValueError(f"Invalid filter dictionary: must contain 'op' key. Got: {filter_dict}")
|
|
311
|
+
|
|
312
|
+
op = filter_dict["op"]
|
|
313
|
+
|
|
314
|
+
# Comparison and inclusion operators
|
|
315
|
+
if op == "EQ":
|
|
316
|
+
if "key" not in filter_dict or "value" not in filter_dict:
|
|
317
|
+
raise ValueError(f"EQ filter requires 'key' and 'value' fields. Got: {filter_dict}")
|
|
318
|
+
return EQ(filter_dict["key"], filter_dict["value"])
|
|
319
|
+
|
|
320
|
+
elif op == "IN":
|
|
321
|
+
if "key" not in filter_dict or "values" not in filter_dict:
|
|
322
|
+
raise ValueError(f"IN filter requires 'key' and 'values' fields. Got: {filter_dict}")
|
|
323
|
+
return IN(filter_dict["key"], filter_dict["values"])
|
|
324
|
+
|
|
325
|
+
elif op == "GT":
|
|
326
|
+
if "key" not in filter_dict or "value" not in filter_dict:
|
|
327
|
+
raise ValueError(f"GT filter requires 'key' and 'value' fields. Got: {filter_dict}")
|
|
328
|
+
return GT(filter_dict["key"], filter_dict["value"])
|
|
329
|
+
|
|
330
|
+
elif op == "LT":
|
|
331
|
+
if "key" not in filter_dict or "value" not in filter_dict:
|
|
332
|
+
raise ValueError(f"LT filter requires 'key' and 'value' fields. Got: {filter_dict}")
|
|
333
|
+
return LT(filter_dict["key"], filter_dict["value"])
|
|
334
|
+
|
|
335
|
+
# Logical operators
|
|
336
|
+
elif op == "AND":
|
|
337
|
+
if "conditions" not in filter_dict:
|
|
338
|
+
raise ValueError(f"AND filter requires 'conditions' field. Got: {filter_dict}")
|
|
339
|
+
conditions = [from_dict(cond) for cond in filter_dict["conditions"]]
|
|
340
|
+
return AND(*conditions)
|
|
341
|
+
|
|
342
|
+
elif op == "OR":
|
|
343
|
+
if "conditions" not in filter_dict:
|
|
344
|
+
raise ValueError(f"OR filter requires 'conditions' field. Got: {filter_dict}")
|
|
345
|
+
conditions = [from_dict(cond) for cond in filter_dict["conditions"]]
|
|
346
|
+
return OR(*conditions)
|
|
347
|
+
|
|
348
|
+
elif op == "NOT":
|
|
349
|
+
if "condition" not in filter_dict:
|
|
350
|
+
raise ValueError(f"NOT filter requires 'condition' field. Got: {filter_dict}")
|
|
351
|
+
return NOT(from_dict(filter_dict["condition"]))
|
|
352
|
+
|
|
353
|
+
else:
|
|
354
|
+
raise ValueError(f"Unknown filter operator: {op}")
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from agno.guardrails.base import BaseGuardrail
|
|
2
|
+
from agno.guardrails.openai import OpenAIModerationGuardrail
|
|
3
|
+
from agno.guardrails.pii import PIIDetectionGuardrail
|
|
4
|
+
from agno.guardrails.prompt_injection import PromptInjectionGuardrail
|
|
5
|
+
|
|
6
|
+
__all__ = ["BaseGuardrail", "OpenAIModerationGuardrail", "PIIDetectionGuardrail", "PromptInjectionGuardrail"]
|
agno/guardrails/base.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from agno.run.agent import RunInput
|
|
5
|
+
from agno.run.team import TeamRunInput
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseGuardrail(ABC):
|
|
9
|
+
"""Abstract base class for all guardrail implementations."""
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
13
|
+
"""Perform synchronous guardrail check."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def async_check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
18
|
+
"""Perform asynchronous guardrail check."""
|
|
19
|
+
pass
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from os import getenv
|
|
2
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
3
|
+
|
|
4
|
+
from agno.exceptions import CheckTrigger, InputCheckError
|
|
5
|
+
from agno.guardrails.base import BaseGuardrail
|
|
6
|
+
from agno.run.agent import RunInput
|
|
7
|
+
from agno.run.team import TeamRunInput
|
|
8
|
+
from agno.utils.log import log_debug
|
|
9
|
+
from agno.utils.openai import images_to_message
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OpenAIModerationGuardrail(BaseGuardrail):
|
|
13
|
+
"""Guardrail for detecting content that violates OpenAI's content policy.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
moderation_model (str): The model to use for moderation. Defaults to "omni-moderation-latest".
|
|
17
|
+
raise_for_categories (List[str]): The categories to raise for.
|
|
18
|
+
Options are: "sexual", "sexual/minors", "harassment",
|
|
19
|
+
"harassment/threatening", "hate", "hate/threatening",
|
|
20
|
+
"illicit", "illicit/violent", "self-harm", "self-harm/intent",
|
|
21
|
+
"self-harm/instructions", "violence", "violence/graphic".
|
|
22
|
+
Defaults to include all categories.
|
|
23
|
+
api_key (str): The API key to use for moderation. Defaults to the OPENAI_API_KEY environment variable.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
moderation_model: str = "omni-moderation-latest",
|
|
29
|
+
raise_for_categories: Optional[
|
|
30
|
+
List[
|
|
31
|
+
Literal[
|
|
32
|
+
"sexual",
|
|
33
|
+
"sexual/minors",
|
|
34
|
+
"harassment",
|
|
35
|
+
"harassment/threatening",
|
|
36
|
+
"hate",
|
|
37
|
+
"hate/threatening",
|
|
38
|
+
"illicit",
|
|
39
|
+
"illicit/violent",
|
|
40
|
+
"self-harm",
|
|
41
|
+
"self-harm/intent",
|
|
42
|
+
"self-harm/instructions",
|
|
43
|
+
"violence",
|
|
44
|
+
"violence/graphic",
|
|
45
|
+
]
|
|
46
|
+
]
|
|
47
|
+
] = None,
|
|
48
|
+
api_key: Optional[str] = None,
|
|
49
|
+
):
|
|
50
|
+
self.moderation_model = moderation_model
|
|
51
|
+
self.api_key = api_key or getenv("OPENAI_API_KEY")
|
|
52
|
+
self.raise_for_categories = raise_for_categories
|
|
53
|
+
|
|
54
|
+
def check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
55
|
+
"""Check for content that violates OpenAI's content policy."""
|
|
56
|
+
try:
|
|
57
|
+
from openai import OpenAI as OpenAIClient
|
|
58
|
+
except ImportError:
|
|
59
|
+
raise ImportError("`openai` not installed. Please install using `pip install openai`")
|
|
60
|
+
|
|
61
|
+
content = run_input.input_content_string()
|
|
62
|
+
images = run_input.images
|
|
63
|
+
|
|
64
|
+
log_debug(f"Moderating content using {self.moderation_model}")
|
|
65
|
+
client = OpenAIClient(api_key=self.api_key)
|
|
66
|
+
|
|
67
|
+
model_input: Union[str, List[Dict[str, Any]]] = content
|
|
68
|
+
|
|
69
|
+
if images is not None:
|
|
70
|
+
model_input = [{"type": "text", "text": content}, *images_to_message(images=images)]
|
|
71
|
+
|
|
72
|
+
# Prepare input based on content type
|
|
73
|
+
response = client.moderations.create(model=self.moderation_model, input=model_input) # type: ignore
|
|
74
|
+
|
|
75
|
+
result = response.results[0]
|
|
76
|
+
|
|
77
|
+
if result.flagged:
|
|
78
|
+
moderation_result = {
|
|
79
|
+
"categories": result.categories.model_dump(),
|
|
80
|
+
"category_scores": result.category_scores.model_dump(),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
trigger_validation = False
|
|
84
|
+
|
|
85
|
+
if self.raise_for_categories is not None:
|
|
86
|
+
for category in self.raise_for_categories:
|
|
87
|
+
if moderation_result["categories"][category]:
|
|
88
|
+
trigger_validation = True
|
|
89
|
+
else:
|
|
90
|
+
# Since at least one category is flagged, we need to raise the check
|
|
91
|
+
trigger_validation = True
|
|
92
|
+
|
|
93
|
+
if trigger_validation:
|
|
94
|
+
raise InputCheckError(
|
|
95
|
+
"OpenAI moderation violation detected.",
|
|
96
|
+
additional_data=moderation_result,
|
|
97
|
+
check_trigger=CheckTrigger.INPUT_NOT_ALLOWED,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
async def async_check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
101
|
+
"""Check for content that violates OpenAI's content policy."""
|
|
102
|
+
try:
|
|
103
|
+
from openai import AsyncOpenAI as OpenAIClient
|
|
104
|
+
except ImportError:
|
|
105
|
+
raise ImportError("`openai` not installed. Please install using `pip install openai`")
|
|
106
|
+
|
|
107
|
+
content = run_input.input_content_string()
|
|
108
|
+
images = run_input.images
|
|
109
|
+
|
|
110
|
+
log_debug(f"Moderating content using {self.moderation_model}")
|
|
111
|
+
client = OpenAIClient(api_key=self.api_key)
|
|
112
|
+
|
|
113
|
+
model_input: Union[str, List[Dict[str, Any]]] = content
|
|
114
|
+
|
|
115
|
+
if images is not None:
|
|
116
|
+
model_input = [{"type": "text", "text": content}, *images_to_message(images=images)]
|
|
117
|
+
|
|
118
|
+
# Prepare input based on content type
|
|
119
|
+
response = await client.moderations.create(model=self.moderation_model, input=model_input) # type: ignore
|
|
120
|
+
|
|
121
|
+
result = response.results[0]
|
|
122
|
+
|
|
123
|
+
if result.flagged:
|
|
124
|
+
moderation_result = {
|
|
125
|
+
"categories": result.categories.model_dump(),
|
|
126
|
+
"category_scores": result.category_scores.model_dump(),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
trigger_validation = False
|
|
130
|
+
|
|
131
|
+
if self.raise_for_categories is not None:
|
|
132
|
+
for category in self.raise_for_categories:
|
|
133
|
+
if moderation_result["categories"][category]:
|
|
134
|
+
trigger_validation = True
|
|
135
|
+
else:
|
|
136
|
+
# Since at least one category is flagged, we need to raise the check
|
|
137
|
+
trigger_validation = True
|
|
138
|
+
|
|
139
|
+
if trigger_validation:
|
|
140
|
+
raise InputCheckError(
|
|
141
|
+
"OpenAI moderation violation detected.",
|
|
142
|
+
additional_data=moderation_result,
|
|
143
|
+
check_trigger=CheckTrigger.INPUT_NOT_ALLOWED,
|
|
144
|
+
)
|
agno/guardrails/pii.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from re import Pattern
|
|
2
|
+
from typing import Dict, Optional, Union
|
|
3
|
+
|
|
4
|
+
from agno.exceptions import CheckTrigger, InputCheckError
|
|
5
|
+
from agno.guardrails.base import BaseGuardrail
|
|
6
|
+
from agno.run.agent import RunInput
|
|
7
|
+
from agno.run.team import TeamRunInput
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PIIDetectionGuardrail(BaseGuardrail):
|
|
11
|
+
"""Guardrail for detecting Personally Identifiable Information (PII).
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
mask_pii: Whether to mask the PII in the input, rather than raising an error.
|
|
15
|
+
enable_ssn_check: Whether to check for Social Security Numbers. True by default.
|
|
16
|
+
enable_credit_card_check: Whether to check for credit cards. True by default.
|
|
17
|
+
enable_email_check: Whether to check for emails. True by default.
|
|
18
|
+
enable_phone_check: Whether to check for phone numbers. True by default.
|
|
19
|
+
custom_patterns: A dictionary of custom PII patterns to detect. This is added to the default patterns.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
mask_pii: bool = False,
|
|
25
|
+
enable_ssn_check: bool = True,
|
|
26
|
+
enable_credit_card_check: bool = True,
|
|
27
|
+
enable_email_check: bool = True,
|
|
28
|
+
enable_phone_check: bool = True,
|
|
29
|
+
custom_patterns: Optional[Dict[str, Pattern[str]]] = None,
|
|
30
|
+
):
|
|
31
|
+
import re
|
|
32
|
+
|
|
33
|
+
self.mask_pii = mask_pii
|
|
34
|
+
self.pii_patterns = {}
|
|
35
|
+
|
|
36
|
+
if enable_ssn_check:
|
|
37
|
+
self.pii_patterns["SSN"] = re.compile(r"\b\d{3}-\d{2}-\d{4}\b")
|
|
38
|
+
if enable_credit_card_check:
|
|
39
|
+
self.pii_patterns["Credit Card"] = re.compile(r"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b")
|
|
40
|
+
if enable_email_check:
|
|
41
|
+
self.pii_patterns["Email"] = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
|
|
42
|
+
if enable_phone_check:
|
|
43
|
+
self.pii_patterns["Phone"] = re.compile(r"\b\d{3}[\s.-]?\d{3}[\s.-]?\d{4}\b")
|
|
44
|
+
|
|
45
|
+
if custom_patterns:
|
|
46
|
+
self.pii_patterns.update(custom_patterns)
|
|
47
|
+
|
|
48
|
+
def check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
49
|
+
"""Check for PII patterns in the input."""
|
|
50
|
+
content = run_input.input_content_string()
|
|
51
|
+
detected_pii = []
|
|
52
|
+
for pii_type, pattern in self.pii_patterns.items():
|
|
53
|
+
if pattern.search(content):
|
|
54
|
+
detected_pii.append(pii_type)
|
|
55
|
+
if detected_pii:
|
|
56
|
+
if self.mask_pii:
|
|
57
|
+
for pii_type in detected_pii:
|
|
58
|
+
|
|
59
|
+
def mask_match(match):
|
|
60
|
+
return "*" * len(match.group(0))
|
|
61
|
+
|
|
62
|
+
content = self.pii_patterns[pii_type].sub(mask_match, content)
|
|
63
|
+
run_input.input_content = content
|
|
64
|
+
return
|
|
65
|
+
else:
|
|
66
|
+
raise InputCheckError(
|
|
67
|
+
"Potential PII detected in input",
|
|
68
|
+
additional_data={"detected_pii": detected_pii},
|
|
69
|
+
check_trigger=CheckTrigger.PII_DETECTED,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
async def async_check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
73
|
+
"""Asynchronously check for PII patterns in the input."""
|
|
74
|
+
content = run_input.input_content_string()
|
|
75
|
+
detected_pii = []
|
|
76
|
+
for pii_type, pattern in self.pii_patterns.items():
|
|
77
|
+
if pattern.search(content):
|
|
78
|
+
detected_pii.append(pii_type)
|
|
79
|
+
if detected_pii:
|
|
80
|
+
if self.mask_pii:
|
|
81
|
+
for pii_type in detected_pii:
|
|
82
|
+
|
|
83
|
+
def mask_match(match):
|
|
84
|
+
return "*" * len(match.group(0))
|
|
85
|
+
|
|
86
|
+
content = self.pii_patterns[pii_type].sub(mask_match, content)
|
|
87
|
+
run_input.input_content = content
|
|
88
|
+
return
|
|
89
|
+
else:
|
|
90
|
+
raise InputCheckError(
|
|
91
|
+
"Potential PII detected in input",
|
|
92
|
+
additional_data={"detected_pii": detected_pii},
|
|
93
|
+
check_trigger=CheckTrigger.PII_DETECTED,
|
|
94
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from agno.exceptions import CheckTrigger, InputCheckError
|
|
4
|
+
from agno.guardrails.base import BaseGuardrail
|
|
5
|
+
from agno.run.agent import RunInput
|
|
6
|
+
from agno.run.team import TeamRunInput
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PromptInjectionGuardrail(BaseGuardrail):
|
|
10
|
+
"""Guardrail for detecting prompt injection attempts.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
injection_patterns (Optional[List[str]]): A list of patterns to check for. Defaults to a list of common prompt injection patterns.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, injection_patterns: Optional[List[str]] = None):
|
|
17
|
+
self.injection_patterns = injection_patterns or [
|
|
18
|
+
"ignore previous instructions",
|
|
19
|
+
"ignore your instructions",
|
|
20
|
+
"you are now a",
|
|
21
|
+
"forget everything above",
|
|
22
|
+
"developer mode",
|
|
23
|
+
"override safety",
|
|
24
|
+
"disregard guidelines",
|
|
25
|
+
"system prompt",
|
|
26
|
+
"jailbreak",
|
|
27
|
+
"act as if",
|
|
28
|
+
"pretend you are",
|
|
29
|
+
"roleplay as",
|
|
30
|
+
"simulate being",
|
|
31
|
+
"bypass restrictions",
|
|
32
|
+
"ignore safeguards",
|
|
33
|
+
"admin override",
|
|
34
|
+
"root access",
|
|
35
|
+
"forget everything",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
39
|
+
"""Check for prompt injection patterns in the input."""
|
|
40
|
+
if any(keyword in run_input.input_content_string().lower() for keyword in self.injection_patterns):
|
|
41
|
+
raise InputCheckError(
|
|
42
|
+
"Potential jailbreaking or prompt injection detected.",
|
|
43
|
+
check_trigger=CheckTrigger.PROMPT_INJECTION,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
async def async_check(self, run_input: Union[RunInput, TeamRunInput]) -> None:
|
|
47
|
+
"""Asynchronously check for prompt injection patterns in the input."""
|
|
48
|
+
if any(keyword in run_input.input_content_string().lower() for keyword in self.injection_patterns):
|
|
49
|
+
raise InputCheckError(
|
|
50
|
+
"Potential jailbreaking or prompt injection detected.",
|
|
51
|
+
check_trigger=CheckTrigger.PROMPT_INJECTION,
|
|
52
|
+
)
|
agno/hooks/__init__.py
ADDED