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/models/ollama/chat.py
CHANGED
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from dataclasses import
|
|
3
|
-
from
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from os import getenv
|
|
4
|
+
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Type, Union
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel
|
|
6
7
|
|
|
7
|
-
from agno.
|
|
8
|
+
from agno.agent import RunOutput
|
|
9
|
+
from agno.models.base import Model
|
|
8
10
|
from agno.models.message import Message
|
|
9
|
-
from agno.models.
|
|
10
|
-
from agno.
|
|
11
|
+
from agno.models.metrics import Metrics
|
|
12
|
+
from agno.models.response import ModelResponse
|
|
13
|
+
from agno.utils.log import log_debug, log_warning
|
|
14
|
+
from agno.utils.reasoning import extract_thinking_content
|
|
11
15
|
|
|
12
16
|
try:
|
|
13
17
|
from ollama import AsyncClient as AsyncOllamaClient
|
|
14
18
|
from ollama import Client as OllamaClient
|
|
15
|
-
|
|
19
|
+
from ollama._types import ChatResponse
|
|
20
|
+
from ollama._types import Message as OllamaMessage
|
|
21
|
+
except ImportError:
|
|
16
22
|
raise ImportError("`ollama` not installed. Please install using `pip install ollama`")
|
|
17
23
|
|
|
18
24
|
|
|
19
|
-
@dataclass
|
|
20
|
-
class MessageData:
|
|
21
|
-
response_role: Optional[str] = None
|
|
22
|
-
response_message: Optional[Dict[str, Any]] = None
|
|
23
|
-
response_content: Any = ""
|
|
24
|
-
response_content_chunk: str = ""
|
|
25
|
-
tool_calls: List[Dict[str, Any]] = field(default_factory=list)
|
|
26
|
-
tool_call_blocks: Any = field(default_factory=list)
|
|
27
|
-
tool_call_chunk: str = ""
|
|
28
|
-
in_tool_call: bool = False
|
|
29
|
-
response_usage: Optional[Mapping[str, Any]] = None
|
|
30
|
-
|
|
31
|
-
|
|
32
25
|
@dataclass
|
|
33
26
|
class Ollama(Model):
|
|
34
27
|
"""
|
|
@@ -40,7 +33,8 @@ class Ollama(Model):
|
|
|
40
33
|
id: str = "llama3.1"
|
|
41
34
|
name: str = "Ollama"
|
|
42
35
|
provider: str = "Ollama"
|
|
43
|
-
|
|
36
|
+
|
|
37
|
+
supports_native_structured_outputs: bool = True
|
|
44
38
|
|
|
45
39
|
# Request parameters
|
|
46
40
|
format: Optional[Any] = None
|
|
@@ -51,23 +45,35 @@ class Ollama(Model):
|
|
|
51
45
|
# Client parameters
|
|
52
46
|
host: Optional[str] = None
|
|
53
47
|
timeout: Optional[Any] = None
|
|
48
|
+
api_key: Optional[str] = field(default_factory=lambda: getenv("OLLAMA_API_KEY"))
|
|
54
49
|
client_params: Optional[Dict[str, Any]] = None
|
|
55
50
|
|
|
56
51
|
# Ollama clients
|
|
57
52
|
client: Optional[OllamaClient] = None
|
|
58
53
|
async_client: Optional[AsyncOllamaClient] = None
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
55
|
+
def _get_client_params(self) -> Dict[str, Any]:
|
|
56
|
+
host = self.host
|
|
57
|
+
headers = {}
|
|
58
|
+
|
|
59
|
+
if self.api_key:
|
|
60
|
+
if not host:
|
|
61
|
+
host = "https://ollama.com"
|
|
62
|
+
headers["authorization"] = f"Bearer {self.api_key}"
|
|
63
|
+
log_debug(f"Using Ollama cloud endpoint: {host}")
|
|
64
|
+
|
|
65
|
+
base_params = {
|
|
66
|
+
"host": host,
|
|
67
|
+
"timeout": self.timeout,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if headers:
|
|
71
|
+
base_params["headers"] = headers
|
|
72
|
+
|
|
73
|
+
# Create client_params dict with non-None values
|
|
74
|
+
client_params = {k: v for k, v in base_params.items() if v is not None}
|
|
75
|
+
# Add additional client params if provided
|
|
76
|
+
if self.client_params:
|
|
71
77
|
client_params.update(self.client_params)
|
|
72
78
|
return client_params
|
|
73
79
|
|
|
@@ -81,7 +87,8 @@ class Ollama(Model):
|
|
|
81
87
|
if self.client is not None:
|
|
82
88
|
return self.client
|
|
83
89
|
|
|
84
|
-
|
|
90
|
+
self.client = OllamaClient(**self._get_client_params())
|
|
91
|
+
return self.client
|
|
85
92
|
|
|
86
93
|
def get_async_client(self) -> AsyncOllamaClient:
|
|
87
94
|
"""
|
|
@@ -93,32 +100,32 @@ class Ollama(Model):
|
|
|
93
100
|
if self.async_client is not None:
|
|
94
101
|
return self.async_client
|
|
95
102
|
|
|
96
|
-
|
|
103
|
+
self.async_client = AsyncOllamaClient(**self._get_client_params())
|
|
104
|
+
return self.async_client
|
|
97
105
|
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
def get_request_params(
|
|
107
|
+
self,
|
|
108
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
109
|
+
) -> Dict[str, Any]:
|
|
100
110
|
"""
|
|
101
111
|
Returns keyword arguments for API requests.
|
|
102
112
|
|
|
103
113
|
Returns:
|
|
104
114
|
Dict[str, Any]: The API kwargs for the model.
|
|
105
115
|
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
# Ensure types are valid strings
|
|
116
|
-
for tool in request_params["tools"]:
|
|
117
|
-
for prop, obj in tool["function"]["parameters"]["properties"].items():
|
|
118
|
-
if isinstance(obj["type"], list):
|
|
119
|
-
obj["type"] = obj["type"][0]
|
|
120
|
-
if self.request_params is not None:
|
|
116
|
+
base_params = {"format": self.format, "options": self.options, "keep_alive": self.keep_alive}
|
|
117
|
+
# Filter out None values
|
|
118
|
+
request_params = {k: v for k, v in base_params.items() if v is not None}
|
|
119
|
+
# Add tools
|
|
120
|
+
if tools is not None and len(tools) > 0:
|
|
121
|
+
request_params["tools"] = tools
|
|
122
|
+
|
|
123
|
+
# Add additional request params if provided
|
|
124
|
+
if self.request_params:
|
|
121
125
|
request_params.update(self.request_params)
|
|
126
|
+
|
|
127
|
+
if request_params:
|
|
128
|
+
log_debug(f"Calling {self.provider} with request parameters: {request_params}", log_level=2)
|
|
122
129
|
return request_params
|
|
123
130
|
|
|
124
131
|
def to_dict(self) -> Dict[str, Any]:
|
|
@@ -140,226 +147,232 @@ class Ollama(Model):
|
|
|
140
147
|
cleaned_dict = {k: v for k, v in model_dict.items() if v is not None}
|
|
141
148
|
return cleaned_dict
|
|
142
149
|
|
|
143
|
-
def
|
|
150
|
+
def _format_message(self, message: Message, compress_tool_results: bool = False) -> Dict[str, Any]:
|
|
144
151
|
"""
|
|
145
152
|
Format a message into the format expected by Ollama.
|
|
146
153
|
|
|
147
154
|
Args:
|
|
148
155
|
message (Message): The message to format.
|
|
156
|
+
compress_tool_results: Whether to compress tool results.
|
|
149
157
|
|
|
150
158
|
Returns:
|
|
151
159
|
Dict[str, Any]: The formatted message.
|
|
152
160
|
"""
|
|
161
|
+
# Use compressed content for tool messages if compression is active
|
|
162
|
+
if message.role == "tool":
|
|
163
|
+
content = message.get_content(use_compressed_content=compress_tool_results)
|
|
164
|
+
else:
|
|
165
|
+
content = message.content
|
|
166
|
+
|
|
153
167
|
_message: Dict[str, Any] = {
|
|
154
168
|
"role": message.role,
|
|
155
|
-
"content":
|
|
169
|
+
"content": content,
|
|
156
170
|
}
|
|
171
|
+
|
|
172
|
+
if message.role == "assistant" and message.tool_calls is not None:
|
|
173
|
+
# Format tool calls for assistant messages
|
|
174
|
+
formatted_tool_calls = []
|
|
175
|
+
for tool_call in message.tool_calls:
|
|
176
|
+
if "function" in tool_call:
|
|
177
|
+
function_data = tool_call["function"]
|
|
178
|
+
formatted_tool_call = {
|
|
179
|
+
"id": tool_call.get("id"),
|
|
180
|
+
"type": "function",
|
|
181
|
+
"function": {
|
|
182
|
+
"name": function_data["name"],
|
|
183
|
+
"arguments": json.loads(function_data["arguments"])
|
|
184
|
+
if isinstance(function_data["arguments"], str)
|
|
185
|
+
else function_data["arguments"],
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
formatted_tool_calls.append(formatted_tool_call)
|
|
189
|
+
|
|
190
|
+
if formatted_tool_calls:
|
|
191
|
+
_message["tool_calls"] = formatted_tool_calls
|
|
192
|
+
|
|
157
193
|
if message.role == "user":
|
|
158
194
|
if message.images is not None:
|
|
159
195
|
message_images = []
|
|
160
196
|
for image in message.images:
|
|
161
197
|
if image.url is not None:
|
|
162
|
-
message_images.append(image.
|
|
198
|
+
message_images.append(image.get_content_bytes())
|
|
163
199
|
if image.filepath is not None:
|
|
164
200
|
message_images.append(image.filepath) # type: ignore
|
|
165
201
|
if image.content is not None and isinstance(image.content, bytes):
|
|
166
202
|
message_images.append(image.content)
|
|
167
203
|
if message_images:
|
|
168
204
|
_message["images"] = message_images
|
|
205
|
+
|
|
206
|
+
if message.audio is not None and len(message.audio) > 0:
|
|
207
|
+
log_warning("Audio input is currently unsupported.")
|
|
208
|
+
|
|
209
|
+
if message.files is not None and len(message.files) > 0:
|
|
210
|
+
log_warning("File input is currently unsupported.")
|
|
211
|
+
|
|
212
|
+
if message.videos is not None and len(message.videos) > 0:
|
|
213
|
+
log_warning("Video input is currently unsupported.")
|
|
214
|
+
|
|
169
215
|
return _message
|
|
170
216
|
|
|
171
|
-
def _prepare_request_kwargs_for_invoke(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
217
|
+
def _prepare_request_kwargs_for_invoke(
|
|
218
|
+
self,
|
|
219
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
220
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
221
|
+
) -> Dict[str, Any]:
|
|
222
|
+
request_kwargs = self.get_request_params(tools=tools)
|
|
223
|
+
if response_format is not None and isinstance(response_format, type) and issubclass(response_format, BaseModel):
|
|
224
|
+
log_debug("Using structured outputs")
|
|
225
|
+
format_schema = response_format.model_json_schema()
|
|
226
|
+
if "format" not in request_kwargs:
|
|
227
|
+
request_kwargs["format"] = format_schema
|
|
179
228
|
return request_kwargs
|
|
180
229
|
|
|
181
|
-
def invoke(
|
|
230
|
+
def invoke(
|
|
231
|
+
self,
|
|
232
|
+
messages: List[Message],
|
|
233
|
+
assistant_message: Message,
|
|
234
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
235
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
236
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
237
|
+
run_response: Optional[RunOutput] = None,
|
|
238
|
+
compress_tool_results: bool = False,
|
|
239
|
+
) -> ModelResponse:
|
|
182
240
|
"""
|
|
183
241
|
Send a chat request to the Ollama API.
|
|
242
|
+
"""
|
|
243
|
+
request_kwargs = self._prepare_request_kwargs_for_invoke(response_format=response_format, tools=tools)
|
|
184
244
|
|
|
185
|
-
|
|
186
|
-
|
|
245
|
+
if run_response and run_response.metrics:
|
|
246
|
+
run_response.metrics.set_time_to_first_token()
|
|
187
247
|
|
|
188
|
-
|
|
189
|
-
Mapping[str, Any]: The response from the API.
|
|
190
|
-
"""
|
|
191
|
-
request_kwargs = self._prepare_request_kwargs_for_invoke()
|
|
248
|
+
assistant_message.metrics.start_timer()
|
|
192
249
|
|
|
193
|
-
|
|
250
|
+
provider_response = self.get_client().chat(
|
|
194
251
|
model=self.id.strip(),
|
|
195
|
-
messages=[self.
|
|
252
|
+
messages=[self._format_message(m, compress_tool_results) for m in messages], # type: ignore
|
|
196
253
|
**request_kwargs,
|
|
197
254
|
) # type: ignore
|
|
198
255
|
|
|
199
|
-
|
|
256
|
+
assistant_message.metrics.stop_timer()
|
|
257
|
+
|
|
258
|
+
model_response = self._parse_provider_response(provider_response) # type: ignore
|
|
259
|
+
return model_response
|
|
260
|
+
|
|
261
|
+
async def ainvoke(
|
|
262
|
+
self,
|
|
263
|
+
messages: List[Message],
|
|
264
|
+
assistant_message: Message,
|
|
265
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
266
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
267
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
268
|
+
run_response: Optional[RunOutput] = None,
|
|
269
|
+
compress_tool_results: bool = False,
|
|
270
|
+
) -> ModelResponse:
|
|
200
271
|
"""
|
|
201
272
|
Sends an asynchronous chat request to the Ollama API.
|
|
273
|
+
"""
|
|
274
|
+
request_kwargs = self._prepare_request_kwargs_for_invoke(response_format=response_format, tools=tools)
|
|
202
275
|
|
|
203
|
-
|
|
204
|
-
|
|
276
|
+
if run_response and run_response.metrics:
|
|
277
|
+
run_response.metrics.set_time_to_first_token()
|
|
205
278
|
|
|
206
|
-
|
|
207
|
-
Mapping[str, Any]: The response from the API.
|
|
208
|
-
"""
|
|
209
|
-
request_kwargs = self._prepare_request_kwargs_for_invoke()
|
|
279
|
+
assistant_message.metrics.start_timer()
|
|
210
280
|
|
|
211
|
-
|
|
281
|
+
provider_response = await self.get_async_client().chat(
|
|
212
282
|
model=self.id.strip(),
|
|
213
|
-
messages=[self.
|
|
283
|
+
messages=[self._format_message(m, compress_tool_results) for m in messages], # type: ignore
|
|
214
284
|
**request_kwargs,
|
|
215
285
|
) # type: ignore
|
|
216
286
|
|
|
217
|
-
|
|
218
|
-
"""
|
|
219
|
-
Sends a streaming chat request to the Ollama API.
|
|
287
|
+
assistant_message.metrics.stop_timer()
|
|
220
288
|
|
|
221
|
-
|
|
222
|
-
|
|
289
|
+
model_response = self._parse_provider_response(provider_response) # type: ignore
|
|
290
|
+
return model_response
|
|
223
291
|
|
|
224
|
-
|
|
225
|
-
|
|
292
|
+
def invoke_stream(
|
|
293
|
+
self,
|
|
294
|
+
messages: List[Message],
|
|
295
|
+
assistant_message: Message,
|
|
296
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
297
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
298
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
299
|
+
run_response: Optional[RunOutput] = None,
|
|
300
|
+
compress_tool_results: bool = False,
|
|
301
|
+
) -> Iterator[ModelResponse]:
|
|
226
302
|
"""
|
|
227
|
-
|
|
228
|
-
model=self.id,
|
|
229
|
-
messages=[self.format_message(m) for m in messages], # type: ignore
|
|
230
|
-
stream=True,
|
|
231
|
-
**self.request_kwargs,
|
|
232
|
-
) # type: ignore
|
|
233
|
-
|
|
234
|
-
async def ainvoke_stream(self, messages: List[Message]) -> Any:
|
|
303
|
+
Sends a streaming chat request to the Ollama API.
|
|
235
304
|
"""
|
|
236
|
-
|
|
305
|
+
if run_response and run_response.metrics:
|
|
306
|
+
run_response.metrics.set_time_to_first_token()
|
|
237
307
|
|
|
238
|
-
|
|
239
|
-
messages (List[Message]): A list of messages to send to the model.
|
|
308
|
+
assistant_message.metrics.start_timer()
|
|
240
309
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
async_stream = await self.get_async_client().chat(
|
|
245
|
-
model=self.id.strip(),
|
|
246
|
-
messages=[self.format_message(m) for m in messages], # type: ignore
|
|
310
|
+
for chunk in self.get_client().chat(
|
|
311
|
+
model=self.id,
|
|
312
|
+
messages=[self._format_message(m, compress_tool_results) for m in messages], # type: ignore
|
|
247
313
|
stream=True,
|
|
248
|
-
**self.
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
yield chunk
|
|
314
|
+
**self.get_request_params(tools=tools),
|
|
315
|
+
):
|
|
316
|
+
yield self._parse_provider_response_delta(chunk)
|
|
252
317
|
|
|
253
|
-
|
|
318
|
+
assistant_message.metrics.stop_timer()
|
|
319
|
+
|
|
320
|
+
async def ainvoke_stream(
|
|
254
321
|
self,
|
|
255
|
-
assistant_message: Message,
|
|
256
322
|
messages: List[Message],
|
|
257
|
-
|
|
258
|
-
|
|
323
|
+
assistant_message: Message,
|
|
324
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
325
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
326
|
+
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
327
|
+
run_response: Optional[RunOutput] = None,
|
|
328
|
+
compress_tool_results: bool = False,
|
|
329
|
+
) -> AsyncIterator[ModelResponse]:
|
|
259
330
|
"""
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
Args:
|
|
263
|
-
assistant_message (Message): The assistant message.
|
|
264
|
-
messages (List[Message]): The list of messages.
|
|
265
|
-
model_response (ModelResponse): The model response.
|
|
266
|
-
|
|
267
|
-
Returns:
|
|
268
|
-
Optional[ModelResponse]: The model response.
|
|
331
|
+
Sends an asynchronous streaming chat completion request to the Ollama API.
|
|
269
332
|
"""
|
|
270
|
-
if
|
|
271
|
-
|
|
272
|
-
model_response.tool_calls = []
|
|
333
|
+
if run_response and run_response.metrics:
|
|
334
|
+
run_response.metrics.set_time_to_first_token()
|
|
273
335
|
|
|
274
|
-
|
|
275
|
-
model_response.content += "\n\n"
|
|
276
|
-
function_calls_to_run = self._get_function_calls_to_run(assistant_message, messages)
|
|
277
|
-
function_call_results: List[Message] = []
|
|
278
|
-
|
|
279
|
-
if self.show_tool_calls:
|
|
280
|
-
if len(function_calls_to_run) == 1:
|
|
281
|
-
model_response.content += f" - Running: {function_calls_to_run[0].get_call_str()}\n\n"
|
|
282
|
-
elif len(function_calls_to_run) > 1:
|
|
283
|
-
model_response.content += "Running:"
|
|
284
|
-
for _f in function_calls_to_run:
|
|
285
|
-
model_response.content += f"\n - {_f.get_call_str()}"
|
|
286
|
-
model_response.content += "\n\n"
|
|
287
|
-
|
|
288
|
-
for function_call_response in self.run_function_calls(
|
|
289
|
-
function_calls=function_calls_to_run,
|
|
290
|
-
function_call_results=function_call_results,
|
|
291
|
-
):
|
|
292
|
-
if (
|
|
293
|
-
function_call_response.event == ModelResponseEvent.tool_call_completed.value
|
|
294
|
-
and function_call_response.tool_calls is not None
|
|
295
|
-
):
|
|
296
|
-
model_response.tool_calls.extend(function_call_response.tool_calls)
|
|
297
|
-
|
|
298
|
-
self.format_function_call_results(function_call_results, messages)
|
|
299
|
-
|
|
300
|
-
return model_response
|
|
301
|
-
return None
|
|
302
|
-
|
|
303
|
-
def update_usage_metrics(
|
|
304
|
-
self,
|
|
305
|
-
assistant_message: Message,
|
|
306
|
-
metrics: Metrics,
|
|
307
|
-
response: Optional[Mapping[str, Any]] = None,
|
|
308
|
-
) -> None:
|
|
309
|
-
"""
|
|
310
|
-
Update usage metrics for the assistant message.
|
|
336
|
+
assistant_message.metrics.start_timer()
|
|
311
337
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
metrics.input_tokens = response.get("prompt_eval_count", 0)
|
|
320
|
-
metrics.output_tokens = response.get("eval_count", 0)
|
|
321
|
-
metrics.total_tokens = metrics.input_tokens + metrics.output_tokens
|
|
338
|
+
async for chunk in await self.get_async_client().chat(
|
|
339
|
+
model=self.id.strip(),
|
|
340
|
+
messages=[self._format_message(m, compress_tool_results) for m in messages], # type: ignore
|
|
341
|
+
stream=True,
|
|
342
|
+
**self.get_request_params(tools=tools),
|
|
343
|
+
):
|
|
344
|
+
yield self._parse_provider_response_delta(chunk)
|
|
322
345
|
|
|
323
|
-
|
|
324
|
-
self._update_assistant_message_metrics(assistant_message=assistant_message, metrics_for_run=metrics)
|
|
346
|
+
assistant_message.metrics.stop_timer()
|
|
325
347
|
|
|
326
|
-
def
|
|
348
|
+
def _parse_provider_response(self, response: dict) -> ModelResponse:
|
|
327
349
|
"""
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
Args:
|
|
331
|
-
function_call_results (List[Message]): The list of function call results.
|
|
332
|
-
messages (List[Message]): The list of messages.
|
|
350
|
+
Parse the provider response.
|
|
333
351
|
"""
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
352
|
+
model_response = ModelResponse()
|
|
353
|
+
# Get response message
|
|
354
|
+
response_message: OllamaMessage = response.get("message") # type: ignore
|
|
337
355
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
Create an assistant message from the response.
|
|
356
|
+
if response_message.get("role") is not None:
|
|
357
|
+
model_response.role = response_message.get("role")
|
|
341
358
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
metrics: The metrics for this response.
|
|
359
|
+
if response_message.get("content") is not None:
|
|
360
|
+
model_response.content = response_message.get("content")
|
|
345
361
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
message_data = MessageData()
|
|
362
|
+
# Extract thinking content between <think> tags if present
|
|
363
|
+
if model_response.content and model_response.content.find("<think>") != -1:
|
|
364
|
+
reasoning_content, clean_content = extract_thinking_content(model_response.content)
|
|
350
365
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
366
|
+
if reasoning_content:
|
|
367
|
+
# Store extracted thinking content separately
|
|
368
|
+
model_response.reasoning_content = reasoning_content
|
|
369
|
+
# Update main content with clean version
|
|
370
|
+
model_response.content = clean_content
|
|
356
371
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if message_data.tool_call_blocks is not None:
|
|
362
|
-
for block in message_data.tool_call_blocks:
|
|
372
|
+
if response_message.get("tool_calls") is not None:
|
|
373
|
+
if model_response.tool_calls is None:
|
|
374
|
+
model_response.tool_calls = []
|
|
375
|
+
for block in response_message.get("tool_calls", []):
|
|
363
376
|
tool_call = block.get("function")
|
|
364
377
|
tool_name = tool_call.get("name")
|
|
365
378
|
tool_args = tool_call.get("arguments")
|
|
@@ -368,320 +381,72 @@ class Ollama(Model):
|
|
|
368
381
|
"name": tool_name,
|
|
369
382
|
"arguments": (json.dumps(tool_args) if tool_args is not None else None),
|
|
370
383
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if message_data.tool_calls is not None:
|
|
374
|
-
assistant_message.tool_calls = message_data.tool_calls
|
|
375
|
-
|
|
376
|
-
# TODO: Handle Audio
|
|
377
|
-
|
|
378
|
-
# Update metrics
|
|
379
|
-
self.update_usage_metrics(assistant_message=assistant_message, metrics=metrics, response=response)
|
|
380
|
-
return assistant_message
|
|
381
|
-
|
|
382
|
-
def _parse_structured_outputs(self, response: Mapping[str, Any], model_response: ModelResponse) -> None:
|
|
383
|
-
try:
|
|
384
|
-
if (
|
|
385
|
-
self.response_format is not None
|
|
386
|
-
and self.structured_outputs
|
|
387
|
-
and issubclass(self.response_format, BaseModel)
|
|
388
|
-
):
|
|
389
|
-
parsed_object = self.response_format.model_validate_json(response.get("message", {}).get("content", ""))
|
|
390
|
-
if parsed_object is not None:
|
|
391
|
-
model_response.parsed = parsed_object.model_dump_json()
|
|
392
|
-
except Exception as e:
|
|
393
|
-
logger.warning(f"Error parsing structured outputs: {e}")
|
|
394
|
-
|
|
395
|
-
def response(self, messages: List[Message]) -> ModelResponse:
|
|
396
|
-
"""
|
|
397
|
-
Generate a response from Ollama.
|
|
384
|
+
model_response.tool_calls.append({"type": "function", "function": function_def})
|
|
398
385
|
|
|
399
|
-
|
|
400
|
-
|
|
386
|
+
# if response_message.get("images") is not None:
|
|
387
|
+
# model_response.images = response_message.get("images")
|
|
401
388
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
logger.debug("---------- Ollama Response Start ----------")
|
|
406
|
-
self._log_messages(messages)
|
|
407
|
-
model_response = ModelResponse()
|
|
408
|
-
metrics = Metrics()
|
|
389
|
+
# Get response usage
|
|
390
|
+
if response.get("done"):
|
|
391
|
+
model_response.response_usage = self._get_metrics(response)
|
|
409
392
|
|
|
410
|
-
# -*- Generate response
|
|
411
|
-
metrics.start_response_timer()
|
|
412
|
-
response: Mapping[str, Any] = self.invoke(messages=messages)
|
|
413
|
-
metrics.stop_response_timer()
|
|
414
|
-
|
|
415
|
-
# -*- Parse structured outputs
|
|
416
|
-
self._parse_structured_outputs(response=response, model_response=model_response)
|
|
417
|
-
|
|
418
|
-
# -*- Create assistant message
|
|
419
|
-
assistant_message = self.create_assistant_message(response=response, metrics=metrics)
|
|
420
|
-
|
|
421
|
-
# -*- Add assistant message to messages
|
|
422
|
-
messages.append(assistant_message)
|
|
423
|
-
|
|
424
|
-
# -*- Log response and metrics
|
|
425
|
-
assistant_message.log()
|
|
426
|
-
metrics.log()
|
|
427
|
-
|
|
428
|
-
# -*- Update model response with assistant message content and audio
|
|
429
|
-
if assistant_message.content is not None:
|
|
430
|
-
# add the content to the model response
|
|
431
|
-
model_response.content = assistant_message.get_content_string()
|
|
432
|
-
# TODO: Handle audio
|
|
433
|
-
# if assistant_message.audio is not None:
|
|
434
|
-
# # add the audio to the model response
|
|
435
|
-
# model_response.audio = assistant_message.audio
|
|
436
|
-
|
|
437
|
-
# -*- Handle tool calls
|
|
438
|
-
if (
|
|
439
|
-
self.handle_tool_calls(
|
|
440
|
-
assistant_message=assistant_message,
|
|
441
|
-
messages=messages,
|
|
442
|
-
model_response=model_response,
|
|
443
|
-
)
|
|
444
|
-
is not None
|
|
445
|
-
):
|
|
446
|
-
return self.handle_post_tool_call_messages(messages=messages, model_response=model_response)
|
|
447
|
-
|
|
448
|
-
logger.debug("---------- Ollama Response End ----------")
|
|
449
|
-
return model_response
|
|
450
|
-
|
|
451
|
-
async def aresponse(self, messages: List[Message]) -> ModelResponse:
|
|
452
|
-
"""
|
|
453
|
-
Generate an asynchronous response from Ollama.
|
|
454
|
-
|
|
455
|
-
Args:
|
|
456
|
-
messages (List[Message]): A list of messages.
|
|
457
|
-
|
|
458
|
-
Returns:
|
|
459
|
-
ModelResponse: The model response.
|
|
460
|
-
"""
|
|
461
|
-
logger.debug("---------- Ollama Async Response Start ----------")
|
|
462
|
-
self._log_messages(messages)
|
|
463
|
-
model_response = ModelResponse()
|
|
464
|
-
metrics = Metrics()
|
|
465
|
-
|
|
466
|
-
# -*- Generate response
|
|
467
|
-
metrics.start_response_timer()
|
|
468
|
-
response: Mapping[str, Any] = await self.ainvoke(messages=messages)
|
|
469
|
-
metrics.stop_response_timer()
|
|
470
|
-
|
|
471
|
-
# -*- Parse structured outputs
|
|
472
|
-
self._parse_structured_outputs(response=response, model_response=model_response)
|
|
473
|
-
|
|
474
|
-
# -*- Create assistant message
|
|
475
|
-
assistant_message = self.create_assistant_message(response=response, metrics=metrics)
|
|
476
|
-
|
|
477
|
-
# -*- Add assistant message to messages
|
|
478
|
-
messages.append(assistant_message)
|
|
479
|
-
|
|
480
|
-
# -*- Log response and metrics
|
|
481
|
-
assistant_message.log()
|
|
482
|
-
metrics.log()
|
|
483
|
-
|
|
484
|
-
# -*- Update model response with assistant message content and audio
|
|
485
|
-
if assistant_message.content is not None:
|
|
486
|
-
# add the content to the model response
|
|
487
|
-
model_response.content = assistant_message.get_content_string()
|
|
488
|
-
# if assistant_message.audio is not None
|
|
489
|
-
# # add the audio to the model response
|
|
490
|
-
# model_response.audio = assistant_message.audio
|
|
491
|
-
|
|
492
|
-
# -*- Handle tool calls
|
|
493
|
-
if (
|
|
494
|
-
self.handle_tool_calls(
|
|
495
|
-
assistant_message=assistant_message,
|
|
496
|
-
messages=messages,
|
|
497
|
-
model_response=model_response,
|
|
498
|
-
)
|
|
499
|
-
is not None
|
|
500
|
-
):
|
|
501
|
-
return await self.ahandle_post_tool_call_messages(messages=messages, model_response=model_response)
|
|
502
|
-
|
|
503
|
-
logger.debug("---------- Ollama Async Response End ----------")
|
|
504
393
|
return model_response
|
|
505
394
|
|
|
506
|
-
def
|
|
507
|
-
self,
|
|
508
|
-
assistant_message: Message,
|
|
509
|
-
messages: List[Message],
|
|
510
|
-
) -> Iterator[ModelResponse]:
|
|
395
|
+
def _parse_provider_response_delta(self, response: ChatResponse) -> ModelResponse:
|
|
511
396
|
"""
|
|
512
|
-
|
|
397
|
+
Parse the provider response delta.
|
|
513
398
|
|
|
514
399
|
Args:
|
|
515
|
-
|
|
516
|
-
messages (List[Message]): The list of messages.
|
|
400
|
+
response (ChatResponse): The response from the provider.
|
|
517
401
|
|
|
518
402
|
Returns:
|
|
519
403
|
Iterator[ModelResponse]: An iterator of the model response.
|
|
520
404
|
"""
|
|
521
|
-
|
|
522
|
-
yield ModelResponse(content="\n\n")
|
|
523
|
-
function_calls_to_run = self._get_function_calls_to_run(assistant_message, messages)
|
|
524
|
-
function_call_results: List[Message] = []
|
|
525
|
-
|
|
526
|
-
if self.show_tool_calls:
|
|
527
|
-
if len(function_calls_to_run) == 1:
|
|
528
|
-
yield ModelResponse(content=f" - Running: {function_calls_to_run[0].get_call_str()}\n\n")
|
|
529
|
-
elif len(function_calls_to_run) > 1:
|
|
530
|
-
yield ModelResponse(content="Running:")
|
|
531
|
-
for _f in function_calls_to_run:
|
|
532
|
-
yield ModelResponse(content=f"\n - {_f.get_call_str()}")
|
|
533
|
-
yield ModelResponse(content="\n\n")
|
|
534
|
-
|
|
535
|
-
for intermediate_model_response in self.run_function_calls(
|
|
536
|
-
function_calls=function_calls_to_run,
|
|
537
|
-
function_call_results=function_call_results,
|
|
538
|
-
):
|
|
539
|
-
yield intermediate_model_response
|
|
540
|
-
|
|
541
|
-
self.format_function_call_results(function_call_results, messages)
|
|
542
|
-
|
|
543
|
-
def response_stream(self, messages: List[Message]) -> Iterator[ModelResponse]:
|
|
544
|
-
"""
|
|
545
|
-
Generate a streaming response from Ollama.
|
|
405
|
+
model_response = ModelResponse()
|
|
546
406
|
|
|
547
|
-
|
|
548
|
-
messages (List[Message]): A list of messages.
|
|
407
|
+
response_message = response.get("message")
|
|
549
408
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
self._log_messages(messages)
|
|
555
|
-
message_data = MessageData()
|
|
556
|
-
metrics: Metrics = Metrics()
|
|
557
|
-
|
|
558
|
-
# -*- Generate response
|
|
559
|
-
metrics.start_response_timer()
|
|
560
|
-
for response in self.invoke_stream(messages=messages):
|
|
561
|
-
message_data.response_message = response.get("message", {})
|
|
562
|
-
if message_data.response_message:
|
|
563
|
-
metrics.output_tokens += 1
|
|
564
|
-
if metrics.output_tokens == 1:
|
|
565
|
-
metrics.time_to_first_token = metrics.response_timer.elapsed
|
|
566
|
-
|
|
567
|
-
message_data.response_content_chunk = message_data.response_message.get("content", "")
|
|
568
|
-
if message_data.response_content_chunk is not None and message_data.response_content_chunk != "":
|
|
569
|
-
message_data.response_content += message_data.response_content_chunk
|
|
570
|
-
yield ModelResponse(content=message_data.response_content_chunk)
|
|
571
|
-
|
|
572
|
-
message_data.tool_call_blocks = message_data.response_message.get("tool_calls") # type: ignore
|
|
573
|
-
if message_data.tool_call_blocks is not None:
|
|
574
|
-
for block in message_data.tool_call_blocks:
|
|
575
|
-
tool_call = block.get("function")
|
|
576
|
-
tool_name = tool_call.get("name")
|
|
577
|
-
tool_args = tool_call.get("arguments")
|
|
578
|
-
function_def = {
|
|
579
|
-
"name": tool_name,
|
|
580
|
-
"arguments": json.dumps(tool_args) if tool_args is not None else None,
|
|
581
|
-
}
|
|
582
|
-
message_data.tool_calls.append({"type": "function", "function": function_def})
|
|
583
|
-
|
|
584
|
-
if response.get("done"):
|
|
585
|
-
message_data.response_usage = response
|
|
586
|
-
metrics.stop_response_timer()
|
|
587
|
-
|
|
588
|
-
# -*- Create assistant message
|
|
589
|
-
assistant_message = Message(role="assistant", content=message_data.response_content)
|
|
590
|
-
|
|
591
|
-
if len(message_data.tool_calls) > 0:
|
|
592
|
-
assistant_message.tool_calls = message_data.tool_calls
|
|
593
|
-
|
|
594
|
-
# -*- Update usage metrics
|
|
595
|
-
self.update_usage_metrics(
|
|
596
|
-
assistant_message=assistant_message, metrics=metrics, response=message_data.response_usage
|
|
597
|
-
)
|
|
409
|
+
if response_message is not None:
|
|
410
|
+
content_delta = response_message.get("content")
|
|
411
|
+
if content_delta is not None and content_delta != "":
|
|
412
|
+
model_response.content = content_delta
|
|
598
413
|
|
|
599
|
-
|
|
600
|
-
|
|
414
|
+
tool_calls = response_message.get("tool_calls")
|
|
415
|
+
if tool_calls is not None:
|
|
416
|
+
for tool_call in tool_calls:
|
|
417
|
+
tc = tool_call.get("function")
|
|
418
|
+
tool_name = tc.get("name")
|
|
419
|
+
tool_args = tc.get("arguments")
|
|
420
|
+
function_def = {
|
|
421
|
+
"name": tool_name,
|
|
422
|
+
"arguments": json.dumps(tool_args) if tool_args is not None else None,
|
|
423
|
+
}
|
|
424
|
+
model_response.tool_calls.append({"type": "function", "function": function_def})
|
|
601
425
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
metrics.log()
|
|
426
|
+
if response.get("done"):
|
|
427
|
+
model_response.response_usage = self._get_metrics(response)
|
|
605
428
|
|
|
606
|
-
|
|
607
|
-
if assistant_message.tool_calls is not None and len(assistant_message.tool_calls) > 0:
|
|
608
|
-
yield from self.handle_stream_tool_calls(assistant_message, messages)
|
|
609
|
-
yield from self.handle_post_tool_call_messages_stream(messages=messages)
|
|
610
|
-
logger.debug("---------- Ollama Response End ----------")
|
|
429
|
+
return model_response
|
|
611
430
|
|
|
612
|
-
|
|
431
|
+
def _get_metrics(self, response: Union[dict, ChatResponse]) -> Metrics:
|
|
613
432
|
"""
|
|
614
|
-
|
|
433
|
+
Parse the given Ollama usage into an Agno Metrics object.
|
|
615
434
|
|
|
616
435
|
Args:
|
|
617
|
-
|
|
436
|
+
response: The response from the provider.
|
|
618
437
|
|
|
619
438
|
Returns:
|
|
620
|
-
|
|
439
|
+
Metrics: Parsed metrics data
|
|
621
440
|
"""
|
|
622
|
-
|
|
623
|
-
self._log_messages(messages)
|
|
624
|
-
message_data = MessageData()
|
|
625
|
-
metrics: Metrics = Metrics()
|
|
626
|
-
|
|
627
|
-
# -*- Generate response
|
|
628
|
-
metrics.start_response_timer()
|
|
629
|
-
async for response in self.ainvoke_stream(messages=messages):
|
|
630
|
-
message_data.response_message = response.get("message", {})
|
|
631
|
-
if message_data.response_message:
|
|
632
|
-
metrics.output_tokens += 1
|
|
633
|
-
if metrics.output_tokens == 1:
|
|
634
|
-
metrics.time_to_first_token = metrics.response_timer.elapsed
|
|
635
|
-
|
|
636
|
-
message_data.response_content_chunk = message_data.response_message.get("content", "")
|
|
637
|
-
if message_data.response_content_chunk is not None and message_data.response_content_chunk != "":
|
|
638
|
-
message_data.response_content += message_data.response_content_chunk
|
|
639
|
-
yield ModelResponse(content=message_data.response_content_chunk)
|
|
640
|
-
|
|
641
|
-
message_data.tool_call_blocks = message_data.response_message.get("tool_calls")
|
|
642
|
-
if message_data.tool_call_blocks is not None:
|
|
643
|
-
for block in message_data.tool_call_blocks:
|
|
644
|
-
tool_call = block.get("function")
|
|
645
|
-
tool_name = tool_call.get("name")
|
|
646
|
-
tool_args = tool_call.get("arguments")
|
|
647
|
-
function_def = {
|
|
648
|
-
"name": tool_name,
|
|
649
|
-
"arguments": json.dumps(tool_args) if tool_args is not None else None,
|
|
650
|
-
}
|
|
651
|
-
message_data.tool_calls.append({"type": "function", "function": function_def})
|
|
652
|
-
|
|
653
|
-
if response.get("done"):
|
|
654
|
-
message_data.response_usage = response
|
|
655
|
-
metrics.stop_response_timer()
|
|
656
|
-
|
|
657
|
-
# -*- Create assistant message
|
|
658
|
-
assistant_message = Message(role="assistant", content=message_data.response_content)
|
|
659
|
-
|
|
660
|
-
if len(message_data.tool_calls) > 0:
|
|
661
|
-
assistant_message.tool_calls = message_data.tool_calls
|
|
662
|
-
|
|
663
|
-
# -*- Update usage metrics
|
|
664
|
-
self.update_usage_metrics(
|
|
665
|
-
assistant_message=assistant_message, metrics=metrics, response=message_data.response_usage
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
# -*- Add assistant message to messages
|
|
669
|
-
messages.append(assistant_message)
|
|
670
|
-
|
|
671
|
-
# -*- Log response and metrics
|
|
672
|
-
assistant_message.log()
|
|
673
|
-
metrics.log()
|
|
441
|
+
metrics = Metrics()
|
|
674
442
|
|
|
675
|
-
#
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
yield tool_call_response
|
|
679
|
-
async for post_tool_call_response in self.ahandle_post_tool_call_messages_stream(messages=messages):
|
|
680
|
-
yield post_tool_call_response
|
|
681
|
-
logger.debug("---------- Ollama Async Response End ----------")
|
|
443
|
+
# Safely handle None values from Ollama Cloud responses
|
|
444
|
+
input_tokens = response.get("prompt_eval_count")
|
|
445
|
+
output_tokens = response.get("eval_count")
|
|
682
446
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
447
|
+
# Default to 0 if None
|
|
448
|
+
metrics.input_tokens = input_tokens if input_tokens is not None else 0
|
|
449
|
+
metrics.output_tokens = output_tokens if output_tokens is not None else 0
|
|
450
|
+
metrics.total_tokens = metrics.input_tokens + metrics.output_tokens
|
|
686
451
|
|
|
687
|
-
return
|
|
452
|
+
return metrics
|