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/workflow/loop.py
ADDED
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import warnings
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from agno.run.agent import RunOutputEvent
|
|
8
|
+
from agno.run.base import RunContext
|
|
9
|
+
from agno.run.team import TeamRunOutputEvent
|
|
10
|
+
from agno.run.workflow import (
|
|
11
|
+
LoopExecutionCompletedEvent,
|
|
12
|
+
LoopExecutionStartedEvent,
|
|
13
|
+
LoopIterationCompletedEvent,
|
|
14
|
+
LoopIterationStartedEvent,
|
|
15
|
+
WorkflowRunOutput,
|
|
16
|
+
WorkflowRunOutputEvent,
|
|
17
|
+
)
|
|
18
|
+
from agno.session.workflow import WorkflowSession
|
|
19
|
+
from agno.utils.log import log_debug, logger
|
|
20
|
+
from agno.workflow.step import Step
|
|
21
|
+
from agno.workflow.types import StepInput, StepOutput, StepType
|
|
22
|
+
|
|
23
|
+
WorkflowSteps = List[
|
|
24
|
+
Union[
|
|
25
|
+
Callable[
|
|
26
|
+
[StepInput], Union[StepOutput, Awaitable[StepOutput], Iterator[StepOutput], AsyncIterator[StepOutput]]
|
|
27
|
+
],
|
|
28
|
+
Step,
|
|
29
|
+
"Steps", # type: ignore # noqa: F821
|
|
30
|
+
"Loop", # type: ignore # noqa: F821
|
|
31
|
+
"Parallel", # type: ignore # noqa: F821
|
|
32
|
+
"Condition", # type: ignore # noqa: F821
|
|
33
|
+
"Router", # type: ignore # noqa: F821
|
|
34
|
+
]
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class Loop:
|
|
40
|
+
"""A loop of steps that execute in order"""
|
|
41
|
+
|
|
42
|
+
steps: WorkflowSteps
|
|
43
|
+
|
|
44
|
+
name: Optional[str] = None
|
|
45
|
+
description: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
max_iterations: int = 3 # Default to 3
|
|
48
|
+
end_condition: Optional[Callable[[List[StepOutput]], bool]] = None
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
steps: WorkflowSteps,
|
|
53
|
+
name: Optional[str] = None,
|
|
54
|
+
description: Optional[str] = None,
|
|
55
|
+
max_iterations: int = 3,
|
|
56
|
+
end_condition: Optional[Callable[[List[StepOutput]], bool]] = None,
|
|
57
|
+
):
|
|
58
|
+
self.steps = steps
|
|
59
|
+
self.name = name
|
|
60
|
+
self.description = description
|
|
61
|
+
self.max_iterations = max_iterations
|
|
62
|
+
self.end_condition = end_condition
|
|
63
|
+
|
|
64
|
+
def _prepare_steps(self):
|
|
65
|
+
"""Prepare the steps for execution - mirrors workflow logic"""
|
|
66
|
+
from agno.agent.agent import Agent
|
|
67
|
+
from agno.team.team import Team
|
|
68
|
+
from agno.workflow.condition import Condition
|
|
69
|
+
from agno.workflow.parallel import Parallel
|
|
70
|
+
from agno.workflow.router import Router
|
|
71
|
+
from agno.workflow.step import Step
|
|
72
|
+
from agno.workflow.steps import Steps
|
|
73
|
+
|
|
74
|
+
prepared_steps: WorkflowSteps = []
|
|
75
|
+
for step in self.steps:
|
|
76
|
+
if callable(step) and hasattr(step, "__name__"):
|
|
77
|
+
prepared_steps.append(Step(name=step.__name__, description="User-defined callable step", executor=step))
|
|
78
|
+
elif isinstance(step, Agent):
|
|
79
|
+
prepared_steps.append(Step(name=step.name, description=step.description, agent=step))
|
|
80
|
+
elif isinstance(step, Team):
|
|
81
|
+
prepared_steps.append(Step(name=step.name, description=step.description, team=step))
|
|
82
|
+
elif isinstance(step, (Step, Steps, Loop, Parallel, Condition, Router)):
|
|
83
|
+
prepared_steps.append(step)
|
|
84
|
+
else:
|
|
85
|
+
raise ValueError(f"Invalid step type: {type(step).__name__}")
|
|
86
|
+
|
|
87
|
+
self.steps = prepared_steps
|
|
88
|
+
|
|
89
|
+
def _update_step_input_from_outputs(
|
|
90
|
+
self,
|
|
91
|
+
step_input: StepInput,
|
|
92
|
+
step_outputs: Union[StepOutput, List[StepOutput]],
|
|
93
|
+
loop_step_outputs: Optional[Dict[str, StepOutput]] = None,
|
|
94
|
+
) -> StepInput:
|
|
95
|
+
"""Helper to update step input from step outputs (handles both single and multiple outputs)"""
|
|
96
|
+
current_images = step_input.images or []
|
|
97
|
+
current_videos = step_input.videos or []
|
|
98
|
+
current_audio = step_input.audio or []
|
|
99
|
+
|
|
100
|
+
if isinstance(step_outputs, list):
|
|
101
|
+
all_images = sum([out.images or [] for out in step_outputs], [])
|
|
102
|
+
all_videos = sum([out.videos or [] for out in step_outputs], [])
|
|
103
|
+
all_audio = sum([out.audio or [] for out in step_outputs], [])
|
|
104
|
+
|
|
105
|
+
# Use the last output's content for chaining
|
|
106
|
+
previous_step_content = step_outputs[-1].content if step_outputs else None
|
|
107
|
+
else:
|
|
108
|
+
# Single output
|
|
109
|
+
all_images = step_outputs.images or []
|
|
110
|
+
all_videos = step_outputs.videos or []
|
|
111
|
+
all_audio = step_outputs.audio or []
|
|
112
|
+
previous_step_content = step_outputs.content
|
|
113
|
+
|
|
114
|
+
updated_previous_step_outputs = {}
|
|
115
|
+
if step_input.previous_step_outputs:
|
|
116
|
+
updated_previous_step_outputs.update(step_input.previous_step_outputs)
|
|
117
|
+
if loop_step_outputs:
|
|
118
|
+
updated_previous_step_outputs.update(loop_step_outputs)
|
|
119
|
+
|
|
120
|
+
return StepInput(
|
|
121
|
+
input=step_input.input,
|
|
122
|
+
previous_step_content=previous_step_content,
|
|
123
|
+
previous_step_outputs=updated_previous_step_outputs,
|
|
124
|
+
additional_data=step_input.additional_data,
|
|
125
|
+
images=current_images + all_images,
|
|
126
|
+
videos=current_videos + all_videos,
|
|
127
|
+
audio=current_audio + all_audio,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def execute(
|
|
131
|
+
self,
|
|
132
|
+
step_input: StepInput,
|
|
133
|
+
session_id: Optional[str] = None,
|
|
134
|
+
user_id: Optional[str] = None,
|
|
135
|
+
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
136
|
+
store_executor_outputs: bool = True,
|
|
137
|
+
run_context: Optional[RunContext] = None,
|
|
138
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
139
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
140
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
141
|
+
num_history_runs: int = 3,
|
|
142
|
+
background_tasks: Optional[Any] = None,
|
|
143
|
+
) -> StepOutput:
|
|
144
|
+
"""Execute loop steps with iteration control - mirrors workflow execution logic"""
|
|
145
|
+
# Use workflow logger for loop orchestration
|
|
146
|
+
log_debug(f"Loop Start: {self.name}", center=True, symbol="=")
|
|
147
|
+
|
|
148
|
+
# Prepare steps first
|
|
149
|
+
self._prepare_steps()
|
|
150
|
+
|
|
151
|
+
all_results = []
|
|
152
|
+
iteration = 0
|
|
153
|
+
|
|
154
|
+
while iteration < self.max_iterations:
|
|
155
|
+
# Execute all steps in this iteration - mirroring workflow logic
|
|
156
|
+
iteration_results: List[StepOutput] = []
|
|
157
|
+
current_step_input = step_input
|
|
158
|
+
loop_step_outputs = {} # Track outputs within this loop iteration
|
|
159
|
+
|
|
160
|
+
for i, step in enumerate(self.steps):
|
|
161
|
+
step_output = step.execute( # type: ignore[union-attr]
|
|
162
|
+
current_step_input,
|
|
163
|
+
session_id=session_id,
|
|
164
|
+
user_id=user_id,
|
|
165
|
+
workflow_run_response=workflow_run_response,
|
|
166
|
+
store_executor_outputs=store_executor_outputs,
|
|
167
|
+
run_context=run_context,
|
|
168
|
+
session_state=session_state,
|
|
169
|
+
workflow_session=workflow_session,
|
|
170
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
171
|
+
num_history_runs=num_history_runs,
|
|
172
|
+
background_tasks=background_tasks,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Handle both single StepOutput and List[StepOutput] (from Loop/Condition steps)
|
|
176
|
+
if isinstance(step_output, list):
|
|
177
|
+
# This is a step that returns multiple outputs (Loop, Condition etc.)
|
|
178
|
+
iteration_results.extend(step_output)
|
|
179
|
+
if step_output: # Add last output to loop tracking
|
|
180
|
+
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
181
|
+
loop_step_outputs[step_name] = step_output[-1]
|
|
182
|
+
|
|
183
|
+
if any(output.stop for output in step_output):
|
|
184
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
185
|
+
break
|
|
186
|
+
else:
|
|
187
|
+
# Single StepOutput
|
|
188
|
+
iteration_results.append(step_output)
|
|
189
|
+
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
190
|
+
loop_step_outputs[step_name] = step_output
|
|
191
|
+
|
|
192
|
+
if step_output.stop:
|
|
193
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
# Update step input for next step
|
|
197
|
+
current_step_input = self._update_step_input_from_outputs(
|
|
198
|
+
current_step_input, step_output, loop_step_outputs
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
all_results.append(iteration_results)
|
|
202
|
+
iteration += 1
|
|
203
|
+
|
|
204
|
+
# Check end condition
|
|
205
|
+
if self.end_condition and callable(self.end_condition):
|
|
206
|
+
try:
|
|
207
|
+
should_break = self.end_condition(iteration_results)
|
|
208
|
+
if should_break:
|
|
209
|
+
break
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.warning(f"End condition evaluation failed: {e}")
|
|
212
|
+
# Continue with loop if end condition fails
|
|
213
|
+
|
|
214
|
+
log_debug(f"Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
|
215
|
+
|
|
216
|
+
# Return flattened results from all iterations
|
|
217
|
+
flattened_results = []
|
|
218
|
+
for iteration_results in all_results:
|
|
219
|
+
flattened_results.extend(iteration_results)
|
|
220
|
+
|
|
221
|
+
return StepOutput(
|
|
222
|
+
step_name=self.name,
|
|
223
|
+
step_id=str(uuid4()),
|
|
224
|
+
step_type=StepType.LOOP,
|
|
225
|
+
content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
|
|
226
|
+
success=all(result.success for result in flattened_results) if flattened_results else True,
|
|
227
|
+
steps=flattened_results,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
def execute_stream(
|
|
231
|
+
self,
|
|
232
|
+
step_input: StepInput,
|
|
233
|
+
session_id: Optional[str] = None,
|
|
234
|
+
user_id: Optional[str] = None,
|
|
235
|
+
stream_events: bool = False,
|
|
236
|
+
stream_intermediate_steps: bool = False,
|
|
237
|
+
stream_executor_events: bool = True,
|
|
238
|
+
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
239
|
+
step_index: Optional[Union[int, tuple]] = None,
|
|
240
|
+
store_executor_outputs: bool = True,
|
|
241
|
+
run_context: Optional[RunContext] = None,
|
|
242
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
243
|
+
parent_step_id: Optional[str] = None,
|
|
244
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
245
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
246
|
+
num_history_runs: int = 3,
|
|
247
|
+
background_tasks: Optional[Any] = None,
|
|
248
|
+
) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
|
|
249
|
+
"""Execute loop steps with streaming support - mirrors workflow execution logic"""
|
|
250
|
+
log_debug(f"Loop Start: {self.name}", center=True, symbol="=")
|
|
251
|
+
|
|
252
|
+
# Prepare steps first
|
|
253
|
+
self._prepare_steps()
|
|
254
|
+
|
|
255
|
+
loop_step_id = str(uuid4())
|
|
256
|
+
|
|
257
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
258
|
+
if stream_intermediate_steps is not None:
|
|
259
|
+
warnings.warn(
|
|
260
|
+
"The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
|
|
261
|
+
DeprecationWarning,
|
|
262
|
+
stacklevel=2,
|
|
263
|
+
)
|
|
264
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
265
|
+
|
|
266
|
+
if stream_events and workflow_run_response:
|
|
267
|
+
# Yield loop started event
|
|
268
|
+
yield LoopExecutionStartedEvent(
|
|
269
|
+
run_id=workflow_run_response.run_id or "",
|
|
270
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
271
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
272
|
+
session_id=workflow_run_response.session_id or "",
|
|
273
|
+
step_name=self.name,
|
|
274
|
+
step_index=step_index,
|
|
275
|
+
max_iterations=self.max_iterations,
|
|
276
|
+
step_id=loop_step_id,
|
|
277
|
+
parent_step_id=parent_step_id,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
all_results = []
|
|
281
|
+
iteration = 0
|
|
282
|
+
early_termination = False
|
|
283
|
+
|
|
284
|
+
while iteration < self.max_iterations:
|
|
285
|
+
log_debug(f"Loop iteration {iteration + 1}/{self.max_iterations}")
|
|
286
|
+
|
|
287
|
+
if stream_events and workflow_run_response:
|
|
288
|
+
# Yield iteration started event
|
|
289
|
+
yield LoopIterationStartedEvent(
|
|
290
|
+
run_id=workflow_run_response.run_id or "",
|
|
291
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
292
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
293
|
+
session_id=workflow_run_response.session_id or "",
|
|
294
|
+
step_name=self.name,
|
|
295
|
+
step_index=step_index,
|
|
296
|
+
iteration=iteration + 1,
|
|
297
|
+
max_iterations=self.max_iterations,
|
|
298
|
+
step_id=loop_step_id,
|
|
299
|
+
parent_step_id=parent_step_id,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Execute all steps in this iteration - mirroring workflow logic
|
|
303
|
+
iteration_results = []
|
|
304
|
+
current_step_input = step_input
|
|
305
|
+
loop_step_outputs = {}
|
|
306
|
+
|
|
307
|
+
for i, step in enumerate(self.steps):
|
|
308
|
+
step_outputs_for_iteration = []
|
|
309
|
+
|
|
310
|
+
# Loop children always get sequential sub-indices: parent_index.1, parent_index.2, etc.
|
|
311
|
+
if step_index is None or isinstance(step_index, int):
|
|
312
|
+
# Loop is a main step
|
|
313
|
+
composite_step_index = (step_index if step_index is not None else 0, i)
|
|
314
|
+
else:
|
|
315
|
+
# Loop is a nested step - extend the tuple
|
|
316
|
+
composite_step_index = step_index + (i,)
|
|
317
|
+
|
|
318
|
+
# Stream step execution
|
|
319
|
+
for event in step.execute_stream( # type: ignore[union-attr]
|
|
320
|
+
current_step_input,
|
|
321
|
+
session_id=session_id,
|
|
322
|
+
user_id=user_id,
|
|
323
|
+
stream_events=stream_events,
|
|
324
|
+
stream_executor_events=stream_executor_events,
|
|
325
|
+
workflow_run_response=workflow_run_response,
|
|
326
|
+
step_index=composite_step_index,
|
|
327
|
+
store_executor_outputs=store_executor_outputs,
|
|
328
|
+
run_context=run_context,
|
|
329
|
+
session_state=session_state,
|
|
330
|
+
parent_step_id=loop_step_id,
|
|
331
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
332
|
+
workflow_session=workflow_session,
|
|
333
|
+
num_history_runs=num_history_runs,
|
|
334
|
+
background_tasks=background_tasks,
|
|
335
|
+
):
|
|
336
|
+
if isinstance(event, StepOutput):
|
|
337
|
+
step_outputs_for_iteration.append(event)
|
|
338
|
+
iteration_results.append(event)
|
|
339
|
+
else:
|
|
340
|
+
# Yield other events (streaming content, step events, etc.)
|
|
341
|
+
yield event
|
|
342
|
+
|
|
343
|
+
# Update loop_step_outputs with this step's output
|
|
344
|
+
if step_outputs_for_iteration:
|
|
345
|
+
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
346
|
+
if len(step_outputs_for_iteration) == 1:
|
|
347
|
+
loop_step_outputs[step_name] = step_outputs_for_iteration[0]
|
|
348
|
+
|
|
349
|
+
if step_outputs_for_iteration[0].stop:
|
|
350
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
351
|
+
early_termination = True
|
|
352
|
+
break # Break out of step loop
|
|
353
|
+
|
|
354
|
+
current_step_input = self._update_step_input_from_outputs(
|
|
355
|
+
current_step_input, step_outputs_for_iteration[0], loop_step_outputs
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
# Use last output
|
|
359
|
+
loop_step_outputs[step_name] = step_outputs_for_iteration[-1]
|
|
360
|
+
|
|
361
|
+
if any(output.stop for output in step_outputs_for_iteration):
|
|
362
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
363
|
+
early_termination = True
|
|
364
|
+
break # Break out of step loop
|
|
365
|
+
|
|
366
|
+
current_step_input = self._update_step_input_from_outputs(
|
|
367
|
+
current_step_input, step_outputs_for_iteration, loop_step_outputs
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
all_results.append(iteration_results)
|
|
371
|
+
|
|
372
|
+
# Check end condition
|
|
373
|
+
should_continue = True
|
|
374
|
+
if self.end_condition and callable(self.end_condition):
|
|
375
|
+
try:
|
|
376
|
+
should_break = self.end_condition(iteration_results)
|
|
377
|
+
should_continue = not should_break
|
|
378
|
+
log_debug(f"End condition returned: {should_break}, should_continue: {should_continue}")
|
|
379
|
+
except Exception as e:
|
|
380
|
+
logger.warning(f"End condition evaluation failed: {e}")
|
|
381
|
+
|
|
382
|
+
if early_termination:
|
|
383
|
+
should_continue = False
|
|
384
|
+
log_debug(f"Loop ending early due to step termination request at iteration {iteration}")
|
|
385
|
+
|
|
386
|
+
if stream_events and workflow_run_response:
|
|
387
|
+
# Yield iteration completed event
|
|
388
|
+
yield LoopIterationCompletedEvent(
|
|
389
|
+
run_id=workflow_run_response.run_id or "",
|
|
390
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
391
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
392
|
+
session_id=workflow_run_response.session_id or "",
|
|
393
|
+
step_name=self.name,
|
|
394
|
+
step_index=step_index,
|
|
395
|
+
iteration=iteration + 1,
|
|
396
|
+
max_iterations=self.max_iterations,
|
|
397
|
+
iteration_results=iteration_results,
|
|
398
|
+
should_continue=should_continue,
|
|
399
|
+
step_id=loop_step_id,
|
|
400
|
+
parent_step_id=parent_step_id,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
iteration += 1
|
|
404
|
+
|
|
405
|
+
if not should_continue:
|
|
406
|
+
log_debug(f"Loop ending early due to end_condition at iteration {iteration}")
|
|
407
|
+
break
|
|
408
|
+
|
|
409
|
+
log_debug(f"Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
|
410
|
+
|
|
411
|
+
if stream_events and workflow_run_response:
|
|
412
|
+
# Yield loop completed event
|
|
413
|
+
yield LoopExecutionCompletedEvent(
|
|
414
|
+
run_id=workflow_run_response.run_id or "",
|
|
415
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
416
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
417
|
+
session_id=workflow_run_response.session_id or "",
|
|
418
|
+
step_name=self.name,
|
|
419
|
+
step_index=step_index,
|
|
420
|
+
total_iterations=iteration,
|
|
421
|
+
max_iterations=self.max_iterations,
|
|
422
|
+
all_results=all_results,
|
|
423
|
+
step_id=loop_step_id,
|
|
424
|
+
parent_step_id=parent_step_id,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
flattened_results = []
|
|
428
|
+
for iteration_results in all_results:
|
|
429
|
+
flattened_results.extend(iteration_results)
|
|
430
|
+
|
|
431
|
+
yield StepOutput(
|
|
432
|
+
step_name=self.name,
|
|
433
|
+
step_id=loop_step_id,
|
|
434
|
+
step_type=StepType.LOOP,
|
|
435
|
+
content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
|
|
436
|
+
success=all(result.success for result in flattened_results) if flattened_results else True,
|
|
437
|
+
steps=flattened_results,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
async def aexecute(
|
|
441
|
+
self,
|
|
442
|
+
step_input: StepInput,
|
|
443
|
+
session_id: Optional[str] = None,
|
|
444
|
+
user_id: Optional[str] = None,
|
|
445
|
+
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
446
|
+
store_executor_outputs: bool = True,
|
|
447
|
+
run_context: Optional[RunContext] = None,
|
|
448
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
449
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
450
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
451
|
+
num_history_runs: int = 3,
|
|
452
|
+
background_tasks: Optional[Any] = None,
|
|
453
|
+
) -> StepOutput:
|
|
454
|
+
"""Execute loop steps asynchronously with iteration control - mirrors workflow execution logic"""
|
|
455
|
+
# Use workflow logger for async loop orchestration
|
|
456
|
+
log_debug(f"Loop Start: {self.name}", center=True, symbol="=")
|
|
457
|
+
|
|
458
|
+
loop_step_id = str(uuid4())
|
|
459
|
+
|
|
460
|
+
# Prepare steps first
|
|
461
|
+
self._prepare_steps()
|
|
462
|
+
|
|
463
|
+
all_results = []
|
|
464
|
+
iteration = 0
|
|
465
|
+
|
|
466
|
+
while iteration < self.max_iterations:
|
|
467
|
+
# Execute all steps in this iteration - mirroring workflow logic
|
|
468
|
+
iteration_results: List[StepOutput] = []
|
|
469
|
+
current_step_input = step_input
|
|
470
|
+
loop_step_outputs = {} # Track outputs within this loop iteration
|
|
471
|
+
|
|
472
|
+
for i, step in enumerate(self.steps):
|
|
473
|
+
step_output = await step.aexecute( # type: ignore[union-attr]
|
|
474
|
+
current_step_input,
|
|
475
|
+
session_id=session_id,
|
|
476
|
+
user_id=user_id,
|
|
477
|
+
workflow_run_response=workflow_run_response,
|
|
478
|
+
store_executor_outputs=store_executor_outputs,
|
|
479
|
+
run_context=run_context,
|
|
480
|
+
session_state=session_state,
|
|
481
|
+
workflow_session=workflow_session,
|
|
482
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
483
|
+
num_history_runs=num_history_runs,
|
|
484
|
+
background_tasks=background_tasks,
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# Handle both single StepOutput and List[StepOutput] (from Loop/Condition steps)
|
|
488
|
+
if isinstance(step_output, list):
|
|
489
|
+
# This is a step that returns multiple outputs (Loop, Condition etc.)
|
|
490
|
+
iteration_results.extend(step_output)
|
|
491
|
+
if step_output: # Add last output to loop tracking
|
|
492
|
+
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
493
|
+
loop_step_outputs[step_name] = step_output[-1]
|
|
494
|
+
|
|
495
|
+
if any(output.stop for output in step_output):
|
|
496
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
497
|
+
break
|
|
498
|
+
else:
|
|
499
|
+
# Single StepOutput
|
|
500
|
+
iteration_results.append(step_output)
|
|
501
|
+
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
502
|
+
loop_step_outputs[step_name] = step_output
|
|
503
|
+
|
|
504
|
+
if step_output.stop:
|
|
505
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
506
|
+
break
|
|
507
|
+
|
|
508
|
+
# Update step input for next step
|
|
509
|
+
current_step_input = self._update_step_input_from_outputs(
|
|
510
|
+
current_step_input, step_output, loop_step_outputs
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
all_results.append(iteration_results)
|
|
514
|
+
iteration += 1
|
|
515
|
+
|
|
516
|
+
# Check end condition
|
|
517
|
+
if self.end_condition and callable(self.end_condition):
|
|
518
|
+
try:
|
|
519
|
+
if inspect.iscoroutinefunction(self.end_condition):
|
|
520
|
+
should_break = await self.end_condition(iteration_results)
|
|
521
|
+
else:
|
|
522
|
+
should_break = self.end_condition(iteration_results)
|
|
523
|
+
if should_break:
|
|
524
|
+
break
|
|
525
|
+
except Exception as e:
|
|
526
|
+
logger.warning(f"End condition evaluation failed: {e}")
|
|
527
|
+
|
|
528
|
+
# Use workflow logger for async loop completion
|
|
529
|
+
log_debug(f"Async Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
|
530
|
+
|
|
531
|
+
# Return flattened results from all iterations
|
|
532
|
+
flattened_results = []
|
|
533
|
+
for iteration_results in all_results:
|
|
534
|
+
flattened_results.extend(iteration_results)
|
|
535
|
+
|
|
536
|
+
return StepOutput(
|
|
537
|
+
step_name=self.name,
|
|
538
|
+
step_id=loop_step_id,
|
|
539
|
+
step_type=StepType.LOOP,
|
|
540
|
+
content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
|
|
541
|
+
success=all(result.success for result in flattened_results) if flattened_results else True,
|
|
542
|
+
steps=flattened_results,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
async def aexecute_stream(
|
|
546
|
+
self,
|
|
547
|
+
step_input: StepInput,
|
|
548
|
+
session_id: Optional[str] = None,
|
|
549
|
+
user_id: Optional[str] = None,
|
|
550
|
+
stream_events: bool = False,
|
|
551
|
+
stream_intermediate_steps: bool = False,
|
|
552
|
+
stream_executor_events: bool = True,
|
|
553
|
+
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
554
|
+
step_index: Optional[Union[int, tuple]] = None,
|
|
555
|
+
store_executor_outputs: bool = True,
|
|
556
|
+
run_context: Optional[RunContext] = None,
|
|
557
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
558
|
+
parent_step_id: Optional[str] = None,
|
|
559
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
560
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
561
|
+
num_history_runs: int = 3,
|
|
562
|
+
background_tasks: Optional[Any] = None,
|
|
563
|
+
) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
|
|
564
|
+
"""Execute loop steps with async streaming support - mirrors workflow execution logic"""
|
|
565
|
+
log_debug(f"Loop Start: {self.name}", center=True, symbol="=")
|
|
566
|
+
|
|
567
|
+
loop_step_id = str(uuid4())
|
|
568
|
+
|
|
569
|
+
# Prepare steps first
|
|
570
|
+
self._prepare_steps()
|
|
571
|
+
|
|
572
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
573
|
+
if stream_intermediate_steps is not None:
|
|
574
|
+
warnings.warn(
|
|
575
|
+
"The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
|
|
576
|
+
DeprecationWarning,
|
|
577
|
+
stacklevel=2,
|
|
578
|
+
)
|
|
579
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
580
|
+
|
|
581
|
+
if stream_events and workflow_run_response:
|
|
582
|
+
# Yield loop started event
|
|
583
|
+
yield LoopExecutionStartedEvent(
|
|
584
|
+
run_id=workflow_run_response.run_id or "",
|
|
585
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
586
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
587
|
+
session_id=workflow_run_response.session_id or "",
|
|
588
|
+
step_name=self.name,
|
|
589
|
+
step_index=step_index,
|
|
590
|
+
max_iterations=self.max_iterations,
|
|
591
|
+
step_id=loop_step_id,
|
|
592
|
+
parent_step_id=parent_step_id,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
all_results = []
|
|
596
|
+
iteration = 0
|
|
597
|
+
early_termination = False
|
|
598
|
+
|
|
599
|
+
while iteration < self.max_iterations:
|
|
600
|
+
log_debug(f"Async loop iteration {iteration + 1}/{self.max_iterations}")
|
|
601
|
+
|
|
602
|
+
if stream_events and workflow_run_response:
|
|
603
|
+
# Yield iteration started event
|
|
604
|
+
yield LoopIterationStartedEvent(
|
|
605
|
+
run_id=workflow_run_response.run_id or "",
|
|
606
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
607
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
608
|
+
session_id=workflow_run_response.session_id or "",
|
|
609
|
+
step_name=self.name,
|
|
610
|
+
step_index=step_index,
|
|
611
|
+
iteration=iteration + 1,
|
|
612
|
+
max_iterations=self.max_iterations,
|
|
613
|
+
step_id=loop_step_id,
|
|
614
|
+
parent_step_id=parent_step_id,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
# Execute all steps in this iteration - mirroring workflow logic
|
|
618
|
+
iteration_results = []
|
|
619
|
+
current_step_input = step_input
|
|
620
|
+
loop_step_outputs = {} # Track outputs within this loop iteration
|
|
621
|
+
|
|
622
|
+
for i, step in enumerate(self.steps):
|
|
623
|
+
step_outputs_for_iteration = []
|
|
624
|
+
|
|
625
|
+
# Loop children always get sequential sub-indices: parent_index.1, parent_index.2, etc.
|
|
626
|
+
if step_index is None or isinstance(step_index, int):
|
|
627
|
+
# Loop is a main step
|
|
628
|
+
composite_step_index = (step_index if step_index is not None else 0, i)
|
|
629
|
+
else:
|
|
630
|
+
# Loop is a nested step - extend the tuple
|
|
631
|
+
composite_step_index = step_index + (i,)
|
|
632
|
+
|
|
633
|
+
# Stream step execution - mirroring workflow logic
|
|
634
|
+
async for event in step.aexecute_stream( # type: ignore[union-attr]
|
|
635
|
+
current_step_input,
|
|
636
|
+
session_id=session_id,
|
|
637
|
+
user_id=user_id,
|
|
638
|
+
stream_events=stream_events,
|
|
639
|
+
stream_executor_events=stream_executor_events,
|
|
640
|
+
workflow_run_response=workflow_run_response,
|
|
641
|
+
step_index=composite_step_index,
|
|
642
|
+
store_executor_outputs=store_executor_outputs,
|
|
643
|
+
run_context=run_context,
|
|
644
|
+
session_state=session_state,
|
|
645
|
+
parent_step_id=loop_step_id,
|
|
646
|
+
workflow_session=workflow_session,
|
|
647
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
648
|
+
num_history_runs=num_history_runs,
|
|
649
|
+
background_tasks=background_tasks,
|
|
650
|
+
):
|
|
651
|
+
if isinstance(event, StepOutput):
|
|
652
|
+
step_outputs_for_iteration.append(event)
|
|
653
|
+
iteration_results.append(event)
|
|
654
|
+
else:
|
|
655
|
+
# Yield other events (streaming content, step events, etc.)
|
|
656
|
+
yield event
|
|
657
|
+
|
|
658
|
+
# Update loop_step_outputs with this step's output
|
|
659
|
+
if step_outputs_for_iteration:
|
|
660
|
+
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
661
|
+
if len(step_outputs_for_iteration) == 1:
|
|
662
|
+
loop_step_outputs[step_name] = step_outputs_for_iteration[0]
|
|
663
|
+
|
|
664
|
+
if step_outputs_for_iteration[0].stop:
|
|
665
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
666
|
+
early_termination = True
|
|
667
|
+
break # Break out of step loop
|
|
668
|
+
|
|
669
|
+
current_step_input = self._update_step_input_from_outputs(
|
|
670
|
+
current_step_input, step_outputs_for_iteration[0], loop_step_outputs
|
|
671
|
+
)
|
|
672
|
+
else:
|
|
673
|
+
# Use last output
|
|
674
|
+
loop_step_outputs[step_name] = step_outputs_for_iteration[-1]
|
|
675
|
+
|
|
676
|
+
if any(output.stop for output in step_outputs_for_iteration):
|
|
677
|
+
logger.info(f"Early termination requested by step {step_name}")
|
|
678
|
+
early_termination = True
|
|
679
|
+
break # Break out of step loop
|
|
680
|
+
|
|
681
|
+
current_step_input = self._update_step_input_from_outputs(
|
|
682
|
+
current_step_input, step_outputs_for_iteration, loop_step_outputs
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
all_results.append(iteration_results)
|
|
686
|
+
|
|
687
|
+
# Check end condition
|
|
688
|
+
should_continue = True
|
|
689
|
+
if self.end_condition and callable(self.end_condition):
|
|
690
|
+
try:
|
|
691
|
+
if inspect.iscoroutinefunction(self.end_condition):
|
|
692
|
+
should_break = await self.end_condition(iteration_results)
|
|
693
|
+
else:
|
|
694
|
+
should_break = self.end_condition(iteration_results)
|
|
695
|
+
should_continue = not should_break
|
|
696
|
+
log_debug(f"End condition returned: {should_break}, should_continue: {should_continue}")
|
|
697
|
+
except Exception as e:
|
|
698
|
+
logger.warning(f"End condition evaluation failed: {e}")
|
|
699
|
+
|
|
700
|
+
if early_termination:
|
|
701
|
+
should_continue = False
|
|
702
|
+
log_debug(f"Loop ending early due to step termination request at iteration {iteration}")
|
|
703
|
+
|
|
704
|
+
if stream_events and workflow_run_response:
|
|
705
|
+
# Yield iteration completed event
|
|
706
|
+
yield LoopIterationCompletedEvent(
|
|
707
|
+
run_id=workflow_run_response.run_id or "",
|
|
708
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
709
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
710
|
+
session_id=workflow_run_response.session_id or "",
|
|
711
|
+
step_name=self.name,
|
|
712
|
+
step_index=step_index,
|
|
713
|
+
iteration=iteration + 1,
|
|
714
|
+
max_iterations=self.max_iterations,
|
|
715
|
+
iteration_results=iteration_results,
|
|
716
|
+
should_continue=should_continue,
|
|
717
|
+
step_id=loop_step_id,
|
|
718
|
+
parent_step_id=parent_step_id,
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
iteration += 1
|
|
722
|
+
|
|
723
|
+
if not should_continue:
|
|
724
|
+
log_debug(f"Loop ending early due to end_condition at iteration {iteration}")
|
|
725
|
+
break
|
|
726
|
+
|
|
727
|
+
log_debug(f"Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
|
728
|
+
|
|
729
|
+
if stream_events and workflow_run_response:
|
|
730
|
+
# Yield loop completed event
|
|
731
|
+
yield LoopExecutionCompletedEvent(
|
|
732
|
+
run_id=workflow_run_response.run_id or "",
|
|
733
|
+
workflow_name=workflow_run_response.workflow_name or "",
|
|
734
|
+
workflow_id=workflow_run_response.workflow_id or "",
|
|
735
|
+
session_id=workflow_run_response.session_id or "",
|
|
736
|
+
step_name=self.name,
|
|
737
|
+
step_index=step_index,
|
|
738
|
+
total_iterations=iteration,
|
|
739
|
+
max_iterations=self.max_iterations,
|
|
740
|
+
all_results=all_results,
|
|
741
|
+
step_id=loop_step_id,
|
|
742
|
+
parent_step_id=parent_step_id,
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
flattened_results = []
|
|
746
|
+
for iteration_results in all_results:
|
|
747
|
+
flattened_results.extend(iteration_results)
|
|
748
|
+
|
|
749
|
+
yield StepOutput(
|
|
750
|
+
step_name=self.name,
|
|
751
|
+
step_id=loop_step_id,
|
|
752
|
+
step_type=StepType.LOOP,
|
|
753
|
+
content=f"Loop {self.name} completed {iteration} iterations with {len(flattened_results)} total steps",
|
|
754
|
+
success=all(result.success for result in flattened_results) if flattened_results else True,
|
|
755
|
+
steps=flattened_results,
|
|
756
|
+
)
|