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
|
@@ -0,0 +1,1914 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Set, Union, get_args
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from agno.filters import FilterExpr
|
|
6
|
+
from agno.media import Audio, File, Image, Video
|
|
7
|
+
from agno.models.message import Message
|
|
8
|
+
from agno.models.response import ToolExecution
|
|
9
|
+
from agno.reasoning.step import ReasoningStep
|
|
10
|
+
from agno.run.agent import RunOutput
|
|
11
|
+
from agno.run.team import TeamRunEvent, TeamRunOutput, TeamRunOutputEvent
|
|
12
|
+
from agno.utils.log import log_warning
|
|
13
|
+
from agno.utils.message import get_text_from_message
|
|
14
|
+
from agno.utils.response import build_reasoning_step_panel, create_panel, escape_markdown_tags, format_tool_calls
|
|
15
|
+
from agno.utils.timer import Timer
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from agno.team.team import Team
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def print_response(
|
|
22
|
+
team: "Team",
|
|
23
|
+
input: Union[List, Dict, str, Message, BaseModel, List[Message]],
|
|
24
|
+
console: Optional[Any] = None,
|
|
25
|
+
show_message: bool = True,
|
|
26
|
+
show_reasoning: bool = True,
|
|
27
|
+
show_full_reasoning: bool = False,
|
|
28
|
+
show_member_responses: Optional[bool] = None,
|
|
29
|
+
tags_to_include_in_markdown: Optional[Set[str]] = None,
|
|
30
|
+
session_id: Optional[str] = None,
|
|
31
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
32
|
+
user_id: Optional[str] = None,
|
|
33
|
+
run_id: Optional[str] = None,
|
|
34
|
+
audio: Optional[Sequence[Audio]] = None,
|
|
35
|
+
images: Optional[Sequence[Image]] = None,
|
|
36
|
+
videos: Optional[Sequence[Video]] = None,
|
|
37
|
+
files: Optional[Sequence[File]] = None,
|
|
38
|
+
markdown: bool = False,
|
|
39
|
+
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
40
|
+
add_history_to_context: Optional[bool] = None,
|
|
41
|
+
dependencies: Optional[Dict[str, Any]] = None,
|
|
42
|
+
add_dependencies_to_context: Optional[bool] = None,
|
|
43
|
+
add_session_state_to_context: Optional[bool] = None,
|
|
44
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
45
|
+
debug_mode: Optional[bool] = None,
|
|
46
|
+
**kwargs: Any,
|
|
47
|
+
) -> None:
|
|
48
|
+
import textwrap
|
|
49
|
+
|
|
50
|
+
from rich.console import Group
|
|
51
|
+
from rich.json import JSON
|
|
52
|
+
from rich.live import Live
|
|
53
|
+
from rich.markdown import Markdown
|
|
54
|
+
from rich.status import Status
|
|
55
|
+
from rich.text import Text
|
|
56
|
+
|
|
57
|
+
from agno.utils.response import format_tool_calls
|
|
58
|
+
|
|
59
|
+
if not tags_to_include_in_markdown:
|
|
60
|
+
tags_to_include_in_markdown = {"think", "thinking"}
|
|
61
|
+
|
|
62
|
+
with Live(console=console) as live_console:
|
|
63
|
+
status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
|
|
64
|
+
live_console.update(status)
|
|
65
|
+
|
|
66
|
+
response_timer = Timer()
|
|
67
|
+
response_timer.start()
|
|
68
|
+
# Panels to be rendered
|
|
69
|
+
panels = [status]
|
|
70
|
+
# First render the message panel if the message is not None
|
|
71
|
+
if input and show_message:
|
|
72
|
+
# Convert message to a panel
|
|
73
|
+
message_content = get_text_from_message(input)
|
|
74
|
+
message_panel = create_panel(
|
|
75
|
+
content=Text(message_content, style="green"),
|
|
76
|
+
title="Message",
|
|
77
|
+
border_style="cyan",
|
|
78
|
+
)
|
|
79
|
+
panels.append(message_panel)
|
|
80
|
+
live_console.update(Group(*panels))
|
|
81
|
+
|
|
82
|
+
# Run the agent
|
|
83
|
+
run_response: TeamRunOutput = team.run( # type: ignore
|
|
84
|
+
input=input,
|
|
85
|
+
run_id=run_id,
|
|
86
|
+
images=images,
|
|
87
|
+
audio=audio,
|
|
88
|
+
videos=videos,
|
|
89
|
+
files=files,
|
|
90
|
+
stream=False,
|
|
91
|
+
stream_events=True,
|
|
92
|
+
session_id=session_id,
|
|
93
|
+
session_state=session_state,
|
|
94
|
+
user_id=user_id,
|
|
95
|
+
knowledge_filters=knowledge_filters,
|
|
96
|
+
add_history_to_context=add_history_to_context,
|
|
97
|
+
dependencies=dependencies,
|
|
98
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
99
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
100
|
+
metadata=metadata,
|
|
101
|
+
debug_mode=debug_mode,
|
|
102
|
+
**kwargs,
|
|
103
|
+
)
|
|
104
|
+
response_timer.stop()
|
|
105
|
+
|
|
106
|
+
if run_response.input is not None and run_response.input.input_content != input:
|
|
107
|
+
# Input was modified during the run
|
|
108
|
+
panels = [status]
|
|
109
|
+
if show_message:
|
|
110
|
+
# Convert message to a panel
|
|
111
|
+
message_content = get_text_from_message(run_response.input.input_content)
|
|
112
|
+
message_panel = create_panel(
|
|
113
|
+
content=Text(message_content, style="green"),
|
|
114
|
+
title="Message",
|
|
115
|
+
border_style="cyan",
|
|
116
|
+
)
|
|
117
|
+
panels.append(message_panel) # type: ignore
|
|
118
|
+
live_console.update(Group(*panels))
|
|
119
|
+
|
|
120
|
+
team_markdown = False
|
|
121
|
+
member_markdown = {}
|
|
122
|
+
if markdown:
|
|
123
|
+
for member in team.members:
|
|
124
|
+
if member.id is not None:
|
|
125
|
+
member_markdown[member.id] = True
|
|
126
|
+
team_markdown = True
|
|
127
|
+
|
|
128
|
+
if team.output_schema is not None:
|
|
129
|
+
team_markdown = False
|
|
130
|
+
|
|
131
|
+
for member in team.members:
|
|
132
|
+
if member.output_schema is not None and member.id is not None:
|
|
133
|
+
member_markdown[member.id] = False # type: ignore
|
|
134
|
+
|
|
135
|
+
# Handle reasoning
|
|
136
|
+
reasoning_steps = []
|
|
137
|
+
if isinstance(run_response, TeamRunOutput) and run_response.reasoning_steps is not None:
|
|
138
|
+
reasoning_steps = run_response.reasoning_steps
|
|
139
|
+
|
|
140
|
+
if len(reasoning_steps) > 0 and show_reasoning:
|
|
141
|
+
# Create panels for reasoning steps
|
|
142
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
143
|
+
reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
|
|
144
|
+
panels.append(reasoning_panel)
|
|
145
|
+
live_console.update(Group(*panels))
|
|
146
|
+
|
|
147
|
+
if isinstance(run_response, TeamRunOutput) and run_response.reasoning_content is not None and show_reasoning:
|
|
148
|
+
# Create panel for thinking
|
|
149
|
+
thinking_panel = create_panel(
|
|
150
|
+
content=Text(run_response.reasoning_content),
|
|
151
|
+
title=f"Thinking ({response_timer.elapsed:.1f}s)",
|
|
152
|
+
border_style="green",
|
|
153
|
+
)
|
|
154
|
+
panels.append(thinking_panel)
|
|
155
|
+
live_console.update(Group(*panels))
|
|
156
|
+
|
|
157
|
+
if isinstance(run_response, TeamRunOutput):
|
|
158
|
+
# Handle member responses
|
|
159
|
+
if show_member_responses:
|
|
160
|
+
for member_response in run_response.member_responses:
|
|
161
|
+
# Handle member reasoning
|
|
162
|
+
reasoning_steps = []
|
|
163
|
+
if isinstance(member_response, RunOutput) and member_response.reasoning_steps is not None:
|
|
164
|
+
reasoning_steps.extend(member_response.reasoning_steps)
|
|
165
|
+
|
|
166
|
+
if len(reasoning_steps) > 0 and show_reasoning:
|
|
167
|
+
# Create panels for reasoning steps
|
|
168
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
169
|
+
member_reasoning_panel = build_reasoning_step_panel(
|
|
170
|
+
i, step, show_full_reasoning, color="magenta"
|
|
171
|
+
)
|
|
172
|
+
panels.append(member_reasoning_panel)
|
|
173
|
+
|
|
174
|
+
# Add tool calls panel for member if available
|
|
175
|
+
if hasattr(member_response, "tools") and member_response.tools:
|
|
176
|
+
member_name = None
|
|
177
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
178
|
+
member_name = team._get_member_name(member_response.agent_id)
|
|
179
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
180
|
+
member_name = team._get_member_name(member_response.team_id)
|
|
181
|
+
|
|
182
|
+
if member_name:
|
|
183
|
+
formatted_calls = format_tool_calls(member_response.tools)
|
|
184
|
+
if formatted_calls:
|
|
185
|
+
console_width = console.width if console else 80
|
|
186
|
+
panel_width = console_width + 30
|
|
187
|
+
|
|
188
|
+
lines = []
|
|
189
|
+
for call in formatted_calls:
|
|
190
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
191
|
+
lines.append(wrapped_call)
|
|
192
|
+
|
|
193
|
+
tool_calls_text = "\n\n".join(lines)
|
|
194
|
+
|
|
195
|
+
member_tool_calls_panel = create_panel(
|
|
196
|
+
content=tool_calls_text,
|
|
197
|
+
title=f"{member_name} Tool Calls",
|
|
198
|
+
border_style="yellow",
|
|
199
|
+
)
|
|
200
|
+
panels.append(member_tool_calls_panel)
|
|
201
|
+
live_console.update(Group(*panels))
|
|
202
|
+
|
|
203
|
+
show_markdown = False
|
|
204
|
+
if member_markdown:
|
|
205
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
206
|
+
show_markdown = member_markdown.get(member_response.agent_id, False)
|
|
207
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
208
|
+
show_markdown = member_markdown.get(member_response.team_id, False)
|
|
209
|
+
|
|
210
|
+
member_response_content: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
|
|
211
|
+
member_response,
|
|
212
|
+
tags_to_include_in_markdown,
|
|
213
|
+
show_markdown=show_markdown,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Create panel for member response
|
|
217
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
218
|
+
member_response_panel = create_panel(
|
|
219
|
+
content=member_response_content,
|
|
220
|
+
title=f"{team._get_member_name(member_response.agent_id)} Response",
|
|
221
|
+
border_style="magenta",
|
|
222
|
+
)
|
|
223
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
224
|
+
member_response_panel = create_panel(
|
|
225
|
+
content=member_response_content,
|
|
226
|
+
title=f"{team._get_member_name(member_response.team_id)} Response",
|
|
227
|
+
border_style="magenta",
|
|
228
|
+
)
|
|
229
|
+
panels.append(member_response_panel)
|
|
230
|
+
|
|
231
|
+
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
232
|
+
md_lines = []
|
|
233
|
+
|
|
234
|
+
# Add search queries if present
|
|
235
|
+
if member_response.citations.search_queries:
|
|
236
|
+
md_lines.append("**Search Queries:**")
|
|
237
|
+
for query in member_response.citations.search_queries:
|
|
238
|
+
md_lines.append(f"- {query}")
|
|
239
|
+
md_lines.append("") # Empty line before URLs
|
|
240
|
+
|
|
241
|
+
# Add URL citations
|
|
242
|
+
md_lines.extend(
|
|
243
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
244
|
+
for i, citation in enumerate(member_response.citations.urls)
|
|
245
|
+
if citation.url # Only include citations with valid URLs
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
md_content = "\n".join(md_lines)
|
|
249
|
+
if md_content: # Only create panel if there are citations
|
|
250
|
+
citations_panel = create_panel(
|
|
251
|
+
content=Markdown(md_content),
|
|
252
|
+
title="Citations",
|
|
253
|
+
border_style="magenta",
|
|
254
|
+
)
|
|
255
|
+
panels.append(citations_panel)
|
|
256
|
+
|
|
257
|
+
live_console.update(Group(*panels))
|
|
258
|
+
|
|
259
|
+
# Add team level tool calls panel if available
|
|
260
|
+
if run_response.tools:
|
|
261
|
+
formatted_calls = format_tool_calls(run_response.tools)
|
|
262
|
+
if formatted_calls:
|
|
263
|
+
console_width = console.width if console else 80
|
|
264
|
+
# Allow for panel borders and padding
|
|
265
|
+
panel_width = console_width + 30
|
|
266
|
+
|
|
267
|
+
lines = []
|
|
268
|
+
for call in formatted_calls:
|
|
269
|
+
wrapped_call = textwrap.fill(
|
|
270
|
+
f"• {call}", width=panel_width, subsequent_indent=" "
|
|
271
|
+
) # Indent continuation lines
|
|
272
|
+
lines.append(wrapped_call)
|
|
273
|
+
|
|
274
|
+
# Join with blank lines between items
|
|
275
|
+
tool_calls_text = "\n\n".join(lines)
|
|
276
|
+
|
|
277
|
+
# Add compression stats at end of tool calls
|
|
278
|
+
if team.compression_manager is not None and team.compression_manager.stats:
|
|
279
|
+
stats = team.compression_manager.stats
|
|
280
|
+
saved = stats.get("original_size", 0) - stats.get("compressed_size", 0)
|
|
281
|
+
orig = stats.get("original_size", 1)
|
|
282
|
+
if stats.get("tool_results_compressed", 0) > 0:
|
|
283
|
+
tool_calls_text += f"\n\ncompressed: {stats.get('tool_results_compressed', 0)} | Saved: {saved:,} chars ({saved / orig * 100:.0f}%)"
|
|
284
|
+
team.compression_manager.stats.clear()
|
|
285
|
+
|
|
286
|
+
team_tool_calls_panel = create_panel(
|
|
287
|
+
content=tool_calls_text,
|
|
288
|
+
title="Team Tool Calls",
|
|
289
|
+
border_style="yellow",
|
|
290
|
+
)
|
|
291
|
+
panels.append(team_tool_calls_panel)
|
|
292
|
+
live_console.update(Group(*panels))
|
|
293
|
+
|
|
294
|
+
response_content_batch: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
|
|
295
|
+
run_response, tags_to_include_in_markdown, show_markdown=team_markdown
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Create panel for response
|
|
299
|
+
response_panel = create_panel(
|
|
300
|
+
content=response_content_batch,
|
|
301
|
+
title=f"Response ({response_timer.elapsed:.1f}s)",
|
|
302
|
+
border_style="blue",
|
|
303
|
+
)
|
|
304
|
+
panels.append(response_panel)
|
|
305
|
+
|
|
306
|
+
# Add citations
|
|
307
|
+
if run_response.citations is not None and run_response.citations.urls is not None:
|
|
308
|
+
md_lines = []
|
|
309
|
+
|
|
310
|
+
# Add search queries if present
|
|
311
|
+
if run_response.citations.search_queries:
|
|
312
|
+
md_lines.append("**Search Queries:**")
|
|
313
|
+
for query in run_response.citations.search_queries:
|
|
314
|
+
md_lines.append(f"- {query}")
|
|
315
|
+
md_lines.append("") # Empty line before URLs
|
|
316
|
+
|
|
317
|
+
# Add URL citations
|
|
318
|
+
md_lines.extend(
|
|
319
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
320
|
+
for i, citation in enumerate(run_response.citations.urls)
|
|
321
|
+
if citation.url # Only include citations with valid URLs
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
md_content = "\n".join(md_lines)
|
|
325
|
+
if md_content: # Only create panel if there are citations
|
|
326
|
+
citations_panel = create_panel(
|
|
327
|
+
content=Markdown(md_content),
|
|
328
|
+
title="Citations",
|
|
329
|
+
border_style="green",
|
|
330
|
+
)
|
|
331
|
+
panels.append(citations_panel)
|
|
332
|
+
|
|
333
|
+
if team.memory_manager is not None:
|
|
334
|
+
if team.memory_manager.memories_updated:
|
|
335
|
+
memory_panel = create_panel(
|
|
336
|
+
content=Text("Memories updated"),
|
|
337
|
+
title="Memories",
|
|
338
|
+
border_style="green",
|
|
339
|
+
)
|
|
340
|
+
panels.append(memory_panel)
|
|
341
|
+
|
|
342
|
+
if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
|
|
343
|
+
summary_panel = create_panel(
|
|
344
|
+
content=Text("Session summary updated"),
|
|
345
|
+
title="Session Summary",
|
|
346
|
+
border_style="green",
|
|
347
|
+
)
|
|
348
|
+
panels.append(summary_panel)
|
|
349
|
+
team.session_summary_manager.summaries_updated = False
|
|
350
|
+
|
|
351
|
+
# Final update to remove the "Thinking..." status
|
|
352
|
+
panels = [p for p in panels if not isinstance(p, Status)]
|
|
353
|
+
live_console.update(Group(*panels))
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def print_response_stream(
|
|
357
|
+
team: "Team",
|
|
358
|
+
input: Union[List, Dict, str, Message, BaseModel, List[Message]],
|
|
359
|
+
console: Optional[Any] = None,
|
|
360
|
+
show_message: bool = True,
|
|
361
|
+
show_reasoning: bool = True,
|
|
362
|
+
show_full_reasoning: bool = False,
|
|
363
|
+
show_member_responses: Optional[bool] = None,
|
|
364
|
+
tags_to_include_in_markdown: Optional[Set[str]] = None,
|
|
365
|
+
session_id: Optional[str] = None,
|
|
366
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
367
|
+
user_id: Optional[str] = None,
|
|
368
|
+
run_id: Optional[str] = None,
|
|
369
|
+
audio: Optional[Sequence[Audio]] = None,
|
|
370
|
+
images: Optional[Sequence[Image]] = None,
|
|
371
|
+
videos: Optional[Sequence[Video]] = None,
|
|
372
|
+
files: Optional[Sequence[File]] = None,
|
|
373
|
+
markdown: bool = False,
|
|
374
|
+
stream_events: bool = False,
|
|
375
|
+
stream_intermediate_steps: bool = False, # type: ignore
|
|
376
|
+
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
377
|
+
add_history_to_context: Optional[bool] = None,
|
|
378
|
+
dependencies: Optional[Dict[str, Any]] = None,
|
|
379
|
+
add_dependencies_to_context: Optional[bool] = None,
|
|
380
|
+
add_session_state_to_context: Optional[bool] = None,
|
|
381
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
382
|
+
debug_mode: Optional[bool] = None,
|
|
383
|
+
**kwargs: Any,
|
|
384
|
+
) -> None:
|
|
385
|
+
import textwrap
|
|
386
|
+
|
|
387
|
+
from rich.console import Group
|
|
388
|
+
from rich.json import JSON
|
|
389
|
+
from rich.live import Live
|
|
390
|
+
from rich.markdown import Markdown
|
|
391
|
+
from rich.status import Status
|
|
392
|
+
from rich.text import Text
|
|
393
|
+
|
|
394
|
+
from agno.utils.response import format_tool_calls
|
|
395
|
+
|
|
396
|
+
if not tags_to_include_in_markdown:
|
|
397
|
+
tags_to_include_in_markdown = {"think", "thinking"}
|
|
398
|
+
|
|
399
|
+
stream_events = True # With streaming print response, we need to stream intermediate steps
|
|
400
|
+
|
|
401
|
+
_response_content: str = ""
|
|
402
|
+
_response_reasoning_content: str = ""
|
|
403
|
+
reasoning_steps: List[ReasoningStep] = []
|
|
404
|
+
|
|
405
|
+
# Track tool calls by member and team
|
|
406
|
+
member_tool_calls = {} # type: ignore
|
|
407
|
+
team_tool_calls = [] # type: ignore
|
|
408
|
+
|
|
409
|
+
# Track processed tool calls to avoid duplicates
|
|
410
|
+
processed_tool_calls = set()
|
|
411
|
+
|
|
412
|
+
with Live(console=console) as live_console:
|
|
413
|
+
status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
|
|
414
|
+
live_console.update(status)
|
|
415
|
+
response_timer = Timer()
|
|
416
|
+
response_timer.start()
|
|
417
|
+
# Flag which indicates if the panels should be rendered
|
|
418
|
+
render = False
|
|
419
|
+
# Panels to be rendered
|
|
420
|
+
panels = [status]
|
|
421
|
+
# First render the message panel if the message is not None
|
|
422
|
+
if input and show_message:
|
|
423
|
+
render = True
|
|
424
|
+
# Convert message to a panel
|
|
425
|
+
message_content = get_text_from_message(input)
|
|
426
|
+
message_panel = create_panel(
|
|
427
|
+
content=Text(message_content, style="green"),
|
|
428
|
+
title="Message",
|
|
429
|
+
border_style="cyan",
|
|
430
|
+
)
|
|
431
|
+
panels.append(message_panel)
|
|
432
|
+
if render:
|
|
433
|
+
live_console.update(Group(*panels))
|
|
434
|
+
|
|
435
|
+
# Get response from the team
|
|
436
|
+
stream_resp = team.run( # type: ignore
|
|
437
|
+
input=input,
|
|
438
|
+
audio=audio,
|
|
439
|
+
images=images,
|
|
440
|
+
videos=videos,
|
|
441
|
+
files=files,
|
|
442
|
+
stream=True,
|
|
443
|
+
stream_events=stream_events,
|
|
444
|
+
session_id=session_id,
|
|
445
|
+
session_state=session_state,
|
|
446
|
+
user_id=user_id,
|
|
447
|
+
run_id=run_id,
|
|
448
|
+
knowledge_filters=knowledge_filters,
|
|
449
|
+
add_history_to_context=add_history_to_context,
|
|
450
|
+
dependencies=dependencies,
|
|
451
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
452
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
453
|
+
metadata=metadata,
|
|
454
|
+
debug_mode=debug_mode,
|
|
455
|
+
yield_run_output=True,
|
|
456
|
+
**kwargs,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
input_content = get_text_from_message(input)
|
|
460
|
+
|
|
461
|
+
team_markdown = None
|
|
462
|
+
member_markdown = {}
|
|
463
|
+
|
|
464
|
+
# Dict to track member response panels by member_id
|
|
465
|
+
member_response_panels = {}
|
|
466
|
+
|
|
467
|
+
final_run_response = None
|
|
468
|
+
for resp in stream_resp:
|
|
469
|
+
if team_markdown is None:
|
|
470
|
+
if markdown:
|
|
471
|
+
team_markdown = True
|
|
472
|
+
else:
|
|
473
|
+
team_markdown = False
|
|
474
|
+
|
|
475
|
+
if team.output_schema is not None:
|
|
476
|
+
team_markdown = False
|
|
477
|
+
|
|
478
|
+
if isinstance(resp, TeamRunOutput):
|
|
479
|
+
final_run_response = resp
|
|
480
|
+
continue
|
|
481
|
+
|
|
482
|
+
if isinstance(resp, tuple(get_args(TeamRunOutputEvent))):
|
|
483
|
+
if resp.event == TeamRunEvent.run_content:
|
|
484
|
+
if isinstance(resp.content, str):
|
|
485
|
+
_response_content += resp.content
|
|
486
|
+
elif team.output_schema is not None and isinstance(resp.content, BaseModel):
|
|
487
|
+
try:
|
|
488
|
+
_response_content = JSON(resp.content.model_dump_json(exclude_none=True), indent=2) # type: ignore
|
|
489
|
+
except Exception as e:
|
|
490
|
+
log_warning(f"Failed to convert response to JSON: {e}")
|
|
491
|
+
if hasattr(resp, "reasoning_content") and resp.reasoning_content is not None: # type: ignore
|
|
492
|
+
_response_reasoning_content += resp.reasoning_content # type: ignore
|
|
493
|
+
if hasattr(resp, "reasoning_steps") and resp.reasoning_steps is not None: # type: ignore
|
|
494
|
+
reasoning_steps = resp.reasoning_steps # type: ignore
|
|
495
|
+
|
|
496
|
+
if resp.event == TeamRunEvent.pre_hook_completed: # type: ignore
|
|
497
|
+
if resp.run_input is not None: # type: ignore
|
|
498
|
+
input_content = get_text_from_message(resp.run_input.input_content) # type: ignore
|
|
499
|
+
|
|
500
|
+
# Collect team tool calls, avoiding duplicates
|
|
501
|
+
if resp.event == TeamRunEvent.tool_call_completed and resp.tool: # type: ignore
|
|
502
|
+
tool = resp.tool # type: ignore
|
|
503
|
+
# Generate a unique ID for this tool call
|
|
504
|
+
if tool.tool_call_id:
|
|
505
|
+
tool_id = tool.tool_call_id
|
|
506
|
+
else:
|
|
507
|
+
tool_id = str(hash(str(tool)))
|
|
508
|
+
if tool_id not in processed_tool_calls:
|
|
509
|
+
processed_tool_calls.add(tool_id)
|
|
510
|
+
team_tool_calls.append(tool)
|
|
511
|
+
|
|
512
|
+
# Collect member tool calls, avoiding duplicates
|
|
513
|
+
if show_member_responses and hasattr(resp, "member_responses") and resp.member_responses:
|
|
514
|
+
for member_response in resp.member_responses:
|
|
515
|
+
member_id = None
|
|
516
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
517
|
+
member_id = member_response.agent_id
|
|
518
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
519
|
+
member_id = member_response.team_id
|
|
520
|
+
|
|
521
|
+
if member_id and hasattr(member_response, "tools") and member_response.tools:
|
|
522
|
+
if member_id not in member_tool_calls:
|
|
523
|
+
member_tool_calls[member_id] = []
|
|
524
|
+
|
|
525
|
+
for tool in member_response.tools:
|
|
526
|
+
# Generate a unique ID for this tool call
|
|
527
|
+
if tool.tool_call_id:
|
|
528
|
+
tool_id = tool.tool_call_id
|
|
529
|
+
else:
|
|
530
|
+
tool_id = str(hash(str(tool)))
|
|
531
|
+
if tool_id not in processed_tool_calls:
|
|
532
|
+
processed_tool_calls.add(tool_id)
|
|
533
|
+
member_tool_calls[member_id].append(tool)
|
|
534
|
+
|
|
535
|
+
response_content_stream: Union[str, Markdown] = _response_content
|
|
536
|
+
# Escape special tags before markdown conversion
|
|
537
|
+
if team_markdown:
|
|
538
|
+
escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
|
|
539
|
+
response_content_stream = Markdown(escaped_content)
|
|
540
|
+
|
|
541
|
+
# Create new panels for each chunk
|
|
542
|
+
panels = []
|
|
543
|
+
|
|
544
|
+
if input_content and show_message:
|
|
545
|
+
render = True
|
|
546
|
+
# Convert message to a panel
|
|
547
|
+
message_panel = create_panel(
|
|
548
|
+
content=Text(input_content, style="green"),
|
|
549
|
+
title="Message",
|
|
550
|
+
border_style="cyan",
|
|
551
|
+
)
|
|
552
|
+
panels.append(message_panel)
|
|
553
|
+
|
|
554
|
+
if len(reasoning_steps) > 0 and show_reasoning:
|
|
555
|
+
render = True
|
|
556
|
+
# Create panels for reasoning steps
|
|
557
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
558
|
+
reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
|
|
559
|
+
panels.append(reasoning_panel)
|
|
560
|
+
|
|
561
|
+
if len(_response_reasoning_content) > 0 and show_reasoning:
|
|
562
|
+
render = True
|
|
563
|
+
# Create panel for thinking
|
|
564
|
+
thinking_panel = create_panel(
|
|
565
|
+
content=Text(_response_reasoning_content),
|
|
566
|
+
title=f"Thinking ({response_timer.elapsed:.1f}s)",
|
|
567
|
+
border_style="green",
|
|
568
|
+
)
|
|
569
|
+
panels.append(thinking_panel)
|
|
570
|
+
elif _response_content == "":
|
|
571
|
+
# Keep showing status if no content yet
|
|
572
|
+
panels.append(status)
|
|
573
|
+
|
|
574
|
+
# Process member responses and their tool calls
|
|
575
|
+
for member_response in (
|
|
576
|
+
resp.member_responses if show_member_responses and hasattr(resp, "member_responses") else []
|
|
577
|
+
):
|
|
578
|
+
member_id = None
|
|
579
|
+
member_name = "Team Member"
|
|
580
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
581
|
+
member_id = member_response.agent_id
|
|
582
|
+
member_name = team._get_member_name(member_id)
|
|
583
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
584
|
+
member_id = member_response.team_id
|
|
585
|
+
|
|
586
|
+
member_name = team._get_member_name(member_id)
|
|
587
|
+
|
|
588
|
+
# If we have tool calls for this member, display them
|
|
589
|
+
if member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
590
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
591
|
+
if formatted_calls:
|
|
592
|
+
console_width = console.width if console else 80
|
|
593
|
+
panel_width = console_width + 30
|
|
594
|
+
|
|
595
|
+
lines = []
|
|
596
|
+
for call in formatted_calls:
|
|
597
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
598
|
+
lines.append(wrapped_call)
|
|
599
|
+
|
|
600
|
+
tool_calls_text = "\n\n".join(lines)
|
|
601
|
+
|
|
602
|
+
member_tool_calls_panel = create_panel(
|
|
603
|
+
content=tool_calls_text,
|
|
604
|
+
title=f"{member_name} Tool Calls",
|
|
605
|
+
border_style="yellow",
|
|
606
|
+
)
|
|
607
|
+
panels.append(member_tool_calls_panel)
|
|
608
|
+
|
|
609
|
+
# Process member response content
|
|
610
|
+
if show_member_responses and member_id is not None:
|
|
611
|
+
show_markdown = False
|
|
612
|
+
if markdown:
|
|
613
|
+
show_markdown = True
|
|
614
|
+
|
|
615
|
+
member_response_content = _parse_response_content(
|
|
616
|
+
member_response,
|
|
617
|
+
tags_to_include_in_markdown,
|
|
618
|
+
show_markdown=show_markdown,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
member_response_panel = create_panel(
|
|
622
|
+
content=member_response_content,
|
|
623
|
+
title=f"{member_name} Response",
|
|
624
|
+
border_style="magenta",
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
panels.append(member_response_panel)
|
|
628
|
+
|
|
629
|
+
# Store for reference
|
|
630
|
+
if member_id is not None:
|
|
631
|
+
member_response_panels[member_id] = member_response_panel
|
|
632
|
+
|
|
633
|
+
# Add team tool calls panel if available (before the team response)
|
|
634
|
+
if team_tool_calls:
|
|
635
|
+
formatted_calls = format_tool_calls(team_tool_calls)
|
|
636
|
+
if formatted_calls:
|
|
637
|
+
console_width = console.width if console else 80
|
|
638
|
+
panel_width = console_width + 30
|
|
639
|
+
|
|
640
|
+
lines = []
|
|
641
|
+
# Create a set to track already added calls by their string representation
|
|
642
|
+
added_calls = set()
|
|
643
|
+
for call in formatted_calls:
|
|
644
|
+
if call not in added_calls:
|
|
645
|
+
added_calls.add(call)
|
|
646
|
+
# Wrap the call text to fit within the panel
|
|
647
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
648
|
+
lines.append(wrapped_call)
|
|
649
|
+
|
|
650
|
+
# Join with blank lines between items
|
|
651
|
+
tool_calls_text = "\n\n".join(lines)
|
|
652
|
+
|
|
653
|
+
# Add compression stats if available (don't clear - will be cleared in final_panels)
|
|
654
|
+
if team.compression_manager is not None and team.compression_manager.stats:
|
|
655
|
+
stats = team.compression_manager.stats
|
|
656
|
+
saved = stats.get("original_size", 0) - stats.get("compressed_size", 0)
|
|
657
|
+
orig = stats.get("original_size", 1)
|
|
658
|
+
if stats.get("tool_results_compressed", 0) > 0:
|
|
659
|
+
tool_calls_text += f"\n\ncompressed: {stats.get('tool_results_compressed', 0)} | Saved: {saved:,} chars ({saved / orig * 100:.0f}%)"
|
|
660
|
+
|
|
661
|
+
team_tool_calls_panel = create_panel(
|
|
662
|
+
content=tool_calls_text,
|
|
663
|
+
title="Team Tool Calls",
|
|
664
|
+
border_style="yellow",
|
|
665
|
+
)
|
|
666
|
+
panels.append(team_tool_calls_panel)
|
|
667
|
+
|
|
668
|
+
# Add the team response panel at the end
|
|
669
|
+
if response_content_stream:
|
|
670
|
+
render = True
|
|
671
|
+
# Create panel for response
|
|
672
|
+
response_panel = create_panel(
|
|
673
|
+
content=response_content_stream,
|
|
674
|
+
title=f"Response ({response_timer.elapsed:.1f}s)",
|
|
675
|
+
border_style="blue",
|
|
676
|
+
)
|
|
677
|
+
panels.append(response_panel)
|
|
678
|
+
|
|
679
|
+
if render or len(panels) > 0:
|
|
680
|
+
live_console.update(Group(*panels))
|
|
681
|
+
|
|
682
|
+
response_timer.stop()
|
|
683
|
+
run_response = final_run_response
|
|
684
|
+
|
|
685
|
+
# Add citations
|
|
686
|
+
if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
|
|
687
|
+
md_lines = []
|
|
688
|
+
|
|
689
|
+
# Add search queries if present
|
|
690
|
+
if resp.citations.search_queries:
|
|
691
|
+
md_lines.append("**Search Queries:**")
|
|
692
|
+
for query in resp.citations.search_queries:
|
|
693
|
+
md_lines.append(f"- {query}")
|
|
694
|
+
md_lines.append("") # Empty line before URLs
|
|
695
|
+
|
|
696
|
+
# Add URL citations
|
|
697
|
+
md_lines.extend(
|
|
698
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
699
|
+
for i, citation in enumerate(resp.citations.urls)
|
|
700
|
+
if citation.url # Only include citations with valid URLs
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
md_content = "\n".join(md_lines)
|
|
704
|
+
if md_content: # Only create panel if there are citations
|
|
705
|
+
citations_panel = create_panel(
|
|
706
|
+
content=Markdown(md_content),
|
|
707
|
+
title="Citations",
|
|
708
|
+
border_style="green",
|
|
709
|
+
)
|
|
710
|
+
panels.append(citations_panel)
|
|
711
|
+
live_console.update(Group(*panels))
|
|
712
|
+
|
|
713
|
+
if team.memory_manager is not None:
|
|
714
|
+
if team.memory_manager.memories_updated:
|
|
715
|
+
memory_panel = create_panel(
|
|
716
|
+
content=Text("Memories updated"),
|
|
717
|
+
title="Memories",
|
|
718
|
+
border_style="green",
|
|
719
|
+
)
|
|
720
|
+
panels.append(memory_panel)
|
|
721
|
+
live_console.update(Group(*panels))
|
|
722
|
+
|
|
723
|
+
if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
|
|
724
|
+
summary_panel = create_panel(
|
|
725
|
+
content=Text("Session summary updated"),
|
|
726
|
+
title="Session Summary",
|
|
727
|
+
border_style="green",
|
|
728
|
+
)
|
|
729
|
+
panels.append(summary_panel)
|
|
730
|
+
live_console.update(Group(*panels))
|
|
731
|
+
team.session_summary_manager.summaries_updated = False
|
|
732
|
+
|
|
733
|
+
# Final update to remove the "Thinking..." status
|
|
734
|
+
panels = [p for p in panels if not isinstance(p, Status)]
|
|
735
|
+
|
|
736
|
+
if markdown:
|
|
737
|
+
for member in team.members:
|
|
738
|
+
if member.id is not None:
|
|
739
|
+
member_markdown[member.id] = True
|
|
740
|
+
|
|
741
|
+
for member in team.members:
|
|
742
|
+
if member.output_schema is not None and member.id is not None:
|
|
743
|
+
member_markdown[member.id] = False # type: ignore
|
|
744
|
+
|
|
745
|
+
# Final panels assembly - we'll recreate the panels from scratch to ensure correct order
|
|
746
|
+
final_panels = []
|
|
747
|
+
|
|
748
|
+
# Start with the message
|
|
749
|
+
if input_content and show_message:
|
|
750
|
+
message_panel = create_panel(
|
|
751
|
+
content=Text(input_content, style="green"),
|
|
752
|
+
title="Message",
|
|
753
|
+
border_style="cyan",
|
|
754
|
+
)
|
|
755
|
+
final_panels.append(message_panel)
|
|
756
|
+
|
|
757
|
+
# Add reasoning steps
|
|
758
|
+
if reasoning_steps and show_reasoning:
|
|
759
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
760
|
+
reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
|
|
761
|
+
final_panels.append(reasoning_panel)
|
|
762
|
+
|
|
763
|
+
# Add thinking panel if available
|
|
764
|
+
if _response_reasoning_content and show_reasoning:
|
|
765
|
+
thinking_panel = create_panel(
|
|
766
|
+
content=Text(_response_reasoning_content),
|
|
767
|
+
title=f"Thinking ({response_timer.elapsed:.1f}s)",
|
|
768
|
+
border_style="green",
|
|
769
|
+
)
|
|
770
|
+
final_panels.append(thinking_panel)
|
|
771
|
+
|
|
772
|
+
# Add member tool calls and responses in correct order
|
|
773
|
+
if show_member_responses and run_response is not None and hasattr(run_response, "member_responses"):
|
|
774
|
+
for i, member_response in enumerate(run_response.member_responses): # type: ignore
|
|
775
|
+
member_id = None
|
|
776
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
777
|
+
member_id = member_response.agent_id
|
|
778
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
779
|
+
member_id = member_response.team_id
|
|
780
|
+
|
|
781
|
+
if member_id:
|
|
782
|
+
# First add tool calls if any
|
|
783
|
+
if member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
784
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
785
|
+
if formatted_calls:
|
|
786
|
+
console_width = console.width if console else 80
|
|
787
|
+
panel_width = console_width + 30
|
|
788
|
+
|
|
789
|
+
lines = []
|
|
790
|
+
for call in formatted_calls:
|
|
791
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
792
|
+
lines.append(wrapped_call)
|
|
793
|
+
|
|
794
|
+
tool_calls_text = "\n\n".join(lines)
|
|
795
|
+
|
|
796
|
+
member_name = team._get_member_name(member_id)
|
|
797
|
+
member_tool_calls_panel = create_panel(
|
|
798
|
+
content=tool_calls_text,
|
|
799
|
+
title=f"{member_name} Tool Calls",
|
|
800
|
+
border_style="yellow",
|
|
801
|
+
)
|
|
802
|
+
final_panels.append(member_tool_calls_panel)
|
|
803
|
+
|
|
804
|
+
# Add reasoning steps if any
|
|
805
|
+
reasoning_steps = []
|
|
806
|
+
if member_response.reasoning_steps is not None:
|
|
807
|
+
reasoning_steps = member_response.reasoning_steps
|
|
808
|
+
if reasoning_steps and show_reasoning:
|
|
809
|
+
for j, step in enumerate(reasoning_steps, 1):
|
|
810
|
+
member_reasoning_panel = build_reasoning_step_panel(
|
|
811
|
+
j, step, show_full_reasoning, color="magenta"
|
|
812
|
+
)
|
|
813
|
+
final_panels.append(member_reasoning_panel)
|
|
814
|
+
|
|
815
|
+
# Then add response
|
|
816
|
+
show_markdown = False
|
|
817
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
818
|
+
show_markdown = member_markdown.get(member_response.agent_id, False)
|
|
819
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
820
|
+
show_markdown = member_markdown.get(member_response.team_id, False)
|
|
821
|
+
|
|
822
|
+
member_response_content = _parse_response_content( # type: ignore
|
|
823
|
+
member_response,
|
|
824
|
+
tags_to_include_in_markdown,
|
|
825
|
+
show_markdown=show_markdown,
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
member_name = "Team Member"
|
|
829
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
830
|
+
member_name = team._get_member_name(member_response.agent_id)
|
|
831
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
832
|
+
member_name = team._get_member_name(member_response.team_id)
|
|
833
|
+
|
|
834
|
+
member_response_panel = create_panel(
|
|
835
|
+
content=member_response_content,
|
|
836
|
+
title=f"{member_name} Response",
|
|
837
|
+
border_style="magenta",
|
|
838
|
+
)
|
|
839
|
+
final_panels.append(member_response_panel)
|
|
840
|
+
|
|
841
|
+
# Add citations if any
|
|
842
|
+
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
843
|
+
md_lines = []
|
|
844
|
+
|
|
845
|
+
# Add search queries if present
|
|
846
|
+
if member_response.citations.search_queries:
|
|
847
|
+
md_lines.append("**Search Queries:**")
|
|
848
|
+
for query in member_response.citations.search_queries:
|
|
849
|
+
md_lines.append(f"- {query}")
|
|
850
|
+
md_lines.append("") # Empty line before URLs
|
|
851
|
+
|
|
852
|
+
# Add URL citations
|
|
853
|
+
md_lines.extend(
|
|
854
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
855
|
+
for i, citation in enumerate(member_response.citations.urls)
|
|
856
|
+
if citation.url # Only include citations with valid URLs
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
md_content = "\n".join(md_lines)
|
|
860
|
+
if md_content: # Only create panel if there are citations
|
|
861
|
+
citations_panel = create_panel(
|
|
862
|
+
content=Markdown(md_content),
|
|
863
|
+
title="Citations",
|
|
864
|
+
border_style="magenta",
|
|
865
|
+
)
|
|
866
|
+
final_panels.append(citations_panel)
|
|
867
|
+
|
|
868
|
+
# Add team tool calls before team response
|
|
869
|
+
if team_tool_calls:
|
|
870
|
+
formatted_calls = format_tool_calls(team_tool_calls)
|
|
871
|
+
if formatted_calls:
|
|
872
|
+
console_width = console.width if console else 80
|
|
873
|
+
panel_width = console_width + 30
|
|
874
|
+
|
|
875
|
+
lines = []
|
|
876
|
+
# Create a set to track already added calls by their string representation
|
|
877
|
+
added_calls = set()
|
|
878
|
+
for call in formatted_calls:
|
|
879
|
+
if call not in added_calls:
|
|
880
|
+
added_calls.add(call)
|
|
881
|
+
# Wrap the call text to fit within the panel
|
|
882
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
883
|
+
lines.append(wrapped_call)
|
|
884
|
+
|
|
885
|
+
tool_calls_text = "\n\n".join(lines)
|
|
886
|
+
|
|
887
|
+
# Add compression stats at end of tool calls
|
|
888
|
+
if team.compression_manager is not None and team.compression_manager.stats:
|
|
889
|
+
stats = team.compression_manager.stats
|
|
890
|
+
saved = stats.get("original_size", 0) - stats.get("compressed_size", 0)
|
|
891
|
+
orig = stats.get("original_size", 1)
|
|
892
|
+
if stats.get("tool_results_compressed", 0) > 0:
|
|
893
|
+
tool_calls_text += f"\n\ncompressed: {stats.get('tool_results_compressed', 0)} | Saved: {saved:,} chars ({saved / orig * 100:.0f}%)"
|
|
894
|
+
team.compression_manager.stats.clear()
|
|
895
|
+
|
|
896
|
+
team_tool_calls_panel = create_panel(
|
|
897
|
+
content=tool_calls_text,
|
|
898
|
+
title="Team Tool Calls",
|
|
899
|
+
border_style="yellow",
|
|
900
|
+
)
|
|
901
|
+
final_panels.append(team_tool_calls_panel)
|
|
902
|
+
|
|
903
|
+
# Add team response
|
|
904
|
+
if _response_content:
|
|
905
|
+
response_content_stream = _response_content
|
|
906
|
+
if team_markdown:
|
|
907
|
+
escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
|
|
908
|
+
response_content_stream = Markdown(escaped_content)
|
|
909
|
+
|
|
910
|
+
response_panel = create_panel(
|
|
911
|
+
content=response_content_stream,
|
|
912
|
+
title=f"Response ({response_timer.elapsed:.1f}s)",
|
|
913
|
+
border_style="blue",
|
|
914
|
+
)
|
|
915
|
+
final_panels.append(response_panel)
|
|
916
|
+
|
|
917
|
+
# Add team citations
|
|
918
|
+
if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
|
|
919
|
+
md_lines = []
|
|
920
|
+
|
|
921
|
+
# Add search queries if present
|
|
922
|
+
if resp.citations.search_queries:
|
|
923
|
+
md_lines.append("**Search Queries:**")
|
|
924
|
+
for query in resp.citations.search_queries:
|
|
925
|
+
md_lines.append(f"- {query}")
|
|
926
|
+
md_lines.append("") # Empty line before URLs
|
|
927
|
+
|
|
928
|
+
# Add URL citations
|
|
929
|
+
md_lines.extend(
|
|
930
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
931
|
+
for i, citation in enumerate(resp.citations.urls)
|
|
932
|
+
if citation.url # Only include citations with valid URLs
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
md_content = "\n".join(md_lines)
|
|
936
|
+
if md_content: # Only create panel if there are citations
|
|
937
|
+
citations_panel = create_panel(
|
|
938
|
+
content=Markdown(md_content),
|
|
939
|
+
title="Citations",
|
|
940
|
+
border_style="green",
|
|
941
|
+
)
|
|
942
|
+
final_panels.append(citations_panel)
|
|
943
|
+
|
|
944
|
+
# Final update with correctly ordered panels
|
|
945
|
+
live_console.update(Group(*final_panels))
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
async def aprint_response(
|
|
949
|
+
team: "Team",
|
|
950
|
+
input: Union[List, Dict, str, Message, BaseModel, List[Message]],
|
|
951
|
+
console: Optional[Any] = None,
|
|
952
|
+
show_message: bool = True,
|
|
953
|
+
show_reasoning: bool = True,
|
|
954
|
+
show_full_reasoning: bool = False,
|
|
955
|
+
show_member_responses: Optional[bool] = None,
|
|
956
|
+
tags_to_include_in_markdown: Optional[Set[str]] = None,
|
|
957
|
+
session_id: Optional[str] = None,
|
|
958
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
959
|
+
user_id: Optional[str] = None,
|
|
960
|
+
run_id: Optional[str] = None,
|
|
961
|
+
audio: Optional[Sequence[Audio]] = None,
|
|
962
|
+
images: Optional[Sequence[Image]] = None,
|
|
963
|
+
videos: Optional[Sequence[Video]] = None,
|
|
964
|
+
files: Optional[Sequence[File]] = None,
|
|
965
|
+
markdown: bool = False,
|
|
966
|
+
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
967
|
+
add_history_to_context: Optional[bool] = None,
|
|
968
|
+
dependencies: Optional[Dict[str, Any]] = None,
|
|
969
|
+
add_dependencies_to_context: Optional[bool] = None,
|
|
970
|
+
add_session_state_to_context: Optional[bool] = None,
|
|
971
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
972
|
+
debug_mode: Optional[bool] = None,
|
|
973
|
+
**kwargs: Any,
|
|
974
|
+
) -> None:
|
|
975
|
+
import textwrap
|
|
976
|
+
|
|
977
|
+
from rich.console import Group
|
|
978
|
+
from rich.json import JSON
|
|
979
|
+
from rich.live import Live
|
|
980
|
+
from rich.markdown import Markdown
|
|
981
|
+
from rich.status import Status
|
|
982
|
+
from rich.text import Text
|
|
983
|
+
|
|
984
|
+
from agno.utils.response import format_tool_calls
|
|
985
|
+
|
|
986
|
+
if not tags_to_include_in_markdown:
|
|
987
|
+
tags_to_include_in_markdown = {"think", "thinking"}
|
|
988
|
+
|
|
989
|
+
with Live(console=console) as live_console:
|
|
990
|
+
status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
|
|
991
|
+
live_console.update(status)
|
|
992
|
+
|
|
993
|
+
response_timer = Timer()
|
|
994
|
+
response_timer.start()
|
|
995
|
+
# Panels to be rendered
|
|
996
|
+
panels = [status]
|
|
997
|
+
# First render the message panel if the message is not None
|
|
998
|
+
if input and show_message:
|
|
999
|
+
# Convert message to a panel
|
|
1000
|
+
message_content = get_text_from_message(input)
|
|
1001
|
+
message_panel = create_panel(
|
|
1002
|
+
content=Text(message_content, style="green"),
|
|
1003
|
+
title="Message",
|
|
1004
|
+
border_style="cyan",
|
|
1005
|
+
)
|
|
1006
|
+
panels.append(message_panel)
|
|
1007
|
+
live_console.update(Group(*panels))
|
|
1008
|
+
|
|
1009
|
+
# Run the agent
|
|
1010
|
+
run_response: TeamRunOutput = await team.arun( # type: ignore
|
|
1011
|
+
input=input,
|
|
1012
|
+
run_id=run_id,
|
|
1013
|
+
images=images,
|
|
1014
|
+
audio=audio,
|
|
1015
|
+
videos=videos,
|
|
1016
|
+
files=files,
|
|
1017
|
+
stream=False,
|
|
1018
|
+
stream_events=True,
|
|
1019
|
+
session_id=session_id,
|
|
1020
|
+
session_state=session_state,
|
|
1021
|
+
user_id=user_id,
|
|
1022
|
+
knowledge_filters=knowledge_filters,
|
|
1023
|
+
add_history_to_context=add_history_to_context,
|
|
1024
|
+
dependencies=dependencies,
|
|
1025
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1026
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1027
|
+
metadata=metadata,
|
|
1028
|
+
debug_mode=debug_mode,
|
|
1029
|
+
**kwargs,
|
|
1030
|
+
)
|
|
1031
|
+
response_timer.stop()
|
|
1032
|
+
|
|
1033
|
+
if run_response.input is not None and run_response.input.input_content != input:
|
|
1034
|
+
# Input was modified during the run
|
|
1035
|
+
panels = [status]
|
|
1036
|
+
if show_message:
|
|
1037
|
+
# Convert message to a panel
|
|
1038
|
+
message_content = get_text_from_message(run_response.input.input_content)
|
|
1039
|
+
message_panel = create_panel(
|
|
1040
|
+
content=Text(message_content, style="green"),
|
|
1041
|
+
title="Message",
|
|
1042
|
+
border_style="cyan",
|
|
1043
|
+
)
|
|
1044
|
+
panels.append(message_panel) # type: ignore
|
|
1045
|
+
live_console.update(Group(*panels))
|
|
1046
|
+
|
|
1047
|
+
team_markdown = False
|
|
1048
|
+
member_markdown = {}
|
|
1049
|
+
if markdown:
|
|
1050
|
+
for member in team.members:
|
|
1051
|
+
if member.id is not None:
|
|
1052
|
+
member_markdown[member.id] = True
|
|
1053
|
+
team_markdown = True
|
|
1054
|
+
|
|
1055
|
+
if team.output_schema is not None:
|
|
1056
|
+
team_markdown = False
|
|
1057
|
+
|
|
1058
|
+
for member in team.members:
|
|
1059
|
+
if member.output_schema is not None and member.id is not None:
|
|
1060
|
+
member_markdown[member.id] = False # type: ignore
|
|
1061
|
+
|
|
1062
|
+
# Handle reasoning
|
|
1063
|
+
reasoning_steps = []
|
|
1064
|
+
if isinstance(run_response, TeamRunOutput) and run_response.reasoning_steps is not None:
|
|
1065
|
+
reasoning_steps = run_response.reasoning_steps
|
|
1066
|
+
|
|
1067
|
+
if len(reasoning_steps) > 0 and show_reasoning:
|
|
1068
|
+
# Create panels for reasoning steps
|
|
1069
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
1070
|
+
reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
|
|
1071
|
+
panels.append(reasoning_panel)
|
|
1072
|
+
live_console.update(Group(*panels))
|
|
1073
|
+
|
|
1074
|
+
if isinstance(run_response, TeamRunOutput) and run_response.reasoning_content is not None and show_reasoning:
|
|
1075
|
+
# Create panel for thinking
|
|
1076
|
+
thinking_panel = create_panel(
|
|
1077
|
+
content=Text(run_response.reasoning_content),
|
|
1078
|
+
title=f"Thinking ({response_timer.elapsed:.1f}s)",
|
|
1079
|
+
border_style="green",
|
|
1080
|
+
)
|
|
1081
|
+
panels.append(thinking_panel)
|
|
1082
|
+
live_console.update(Group(*panels))
|
|
1083
|
+
|
|
1084
|
+
if isinstance(run_response, TeamRunOutput):
|
|
1085
|
+
# Handle member responses
|
|
1086
|
+
if show_member_responses:
|
|
1087
|
+
for member_response in run_response.member_responses:
|
|
1088
|
+
# Handle member reasoning
|
|
1089
|
+
reasoning_steps = []
|
|
1090
|
+
if isinstance(member_response, RunOutput) and member_response.reasoning_steps is not None:
|
|
1091
|
+
reasoning_steps.extend(member_response.reasoning_steps)
|
|
1092
|
+
|
|
1093
|
+
if len(reasoning_steps) > 0 and show_reasoning:
|
|
1094
|
+
# Create panels for reasoning steps
|
|
1095
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
1096
|
+
member_reasoning_panel = build_reasoning_step_panel(
|
|
1097
|
+
i, step, show_full_reasoning, color="magenta"
|
|
1098
|
+
)
|
|
1099
|
+
panels.append(member_reasoning_panel)
|
|
1100
|
+
|
|
1101
|
+
# Add tool calls panel for member if available
|
|
1102
|
+
if hasattr(member_response, "tools") and member_response.tools:
|
|
1103
|
+
member_name = None
|
|
1104
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1105
|
+
member_name = team._get_member_name(member_response.agent_id)
|
|
1106
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1107
|
+
member_name = team._get_member_name(member_response.team_id)
|
|
1108
|
+
|
|
1109
|
+
if member_name:
|
|
1110
|
+
# Format tool calls
|
|
1111
|
+
formatted_calls = format_tool_calls(member_response.tools)
|
|
1112
|
+
if formatted_calls:
|
|
1113
|
+
console_width = console.width if console else 80
|
|
1114
|
+
panel_width = console_width + 30
|
|
1115
|
+
|
|
1116
|
+
lines = []
|
|
1117
|
+
for call in formatted_calls:
|
|
1118
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
1119
|
+
lines.append(wrapped_call)
|
|
1120
|
+
|
|
1121
|
+
tool_calls_text = "\n\n".join(lines)
|
|
1122
|
+
|
|
1123
|
+
member_tool_calls_panel = create_panel(
|
|
1124
|
+
content=tool_calls_text,
|
|
1125
|
+
title=f"{member_name} Tool Calls",
|
|
1126
|
+
border_style="yellow",
|
|
1127
|
+
)
|
|
1128
|
+
panels.append(member_tool_calls_panel)
|
|
1129
|
+
live_console.update(Group(*panels))
|
|
1130
|
+
|
|
1131
|
+
show_markdown = False
|
|
1132
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1133
|
+
show_markdown = member_markdown.get(member_response.agent_id, False)
|
|
1134
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1135
|
+
show_markdown = member_markdown.get(member_response.team_id, False)
|
|
1136
|
+
|
|
1137
|
+
member_response_content: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
|
|
1138
|
+
member_response,
|
|
1139
|
+
tags_to_include_in_markdown,
|
|
1140
|
+
show_markdown=show_markdown,
|
|
1141
|
+
)
|
|
1142
|
+
|
|
1143
|
+
# Create panel for member response
|
|
1144
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1145
|
+
member_response_panel = create_panel(
|
|
1146
|
+
content=member_response_content,
|
|
1147
|
+
title=f"{team._get_member_name(member_response.agent_id)} Response",
|
|
1148
|
+
border_style="magenta",
|
|
1149
|
+
)
|
|
1150
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1151
|
+
member_response_panel = create_panel(
|
|
1152
|
+
content=member_response_content,
|
|
1153
|
+
title=f"{team._get_member_name(member_response.team_id)} Response",
|
|
1154
|
+
border_style="magenta",
|
|
1155
|
+
)
|
|
1156
|
+
panels.append(member_response_panel)
|
|
1157
|
+
|
|
1158
|
+
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
1159
|
+
md_lines = []
|
|
1160
|
+
|
|
1161
|
+
# Add search queries if present
|
|
1162
|
+
if member_response.citations.search_queries:
|
|
1163
|
+
md_lines.append("**Search Queries:**")
|
|
1164
|
+
for query in member_response.citations.search_queries:
|
|
1165
|
+
md_lines.append(f"- {query}")
|
|
1166
|
+
md_lines.append("") # Empty line before URLs
|
|
1167
|
+
|
|
1168
|
+
# Add URL citations
|
|
1169
|
+
md_lines.extend(
|
|
1170
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
1171
|
+
for i, citation in enumerate(member_response.citations.urls)
|
|
1172
|
+
if citation.url # Only include citations with valid URLs
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
md_content = "\n".join(md_lines)
|
|
1176
|
+
if md_content:
|
|
1177
|
+
citations_panel = create_panel(
|
|
1178
|
+
content=Markdown(md_content),
|
|
1179
|
+
title="Citations",
|
|
1180
|
+
border_style="magenta",
|
|
1181
|
+
)
|
|
1182
|
+
panels.append(citations_panel)
|
|
1183
|
+
|
|
1184
|
+
live_console.update(Group(*panels))
|
|
1185
|
+
|
|
1186
|
+
# Add team level tool calls panel if available
|
|
1187
|
+
if run_response.tools:
|
|
1188
|
+
formatted_calls = format_tool_calls(run_response.tools)
|
|
1189
|
+
if formatted_calls:
|
|
1190
|
+
console_width = console.width if console else 80
|
|
1191
|
+
# Allow for panel borders and padding
|
|
1192
|
+
panel_width = console_width + 30
|
|
1193
|
+
|
|
1194
|
+
lines = []
|
|
1195
|
+
for call in formatted_calls:
|
|
1196
|
+
# Wrap the call text to fit within the panel
|
|
1197
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
1198
|
+
lines.append(wrapped_call)
|
|
1199
|
+
|
|
1200
|
+
tool_calls_text = "\n\n".join(lines)
|
|
1201
|
+
|
|
1202
|
+
# Add compression stats at end of tool calls
|
|
1203
|
+
if team.compression_manager is not None and team.compression_manager.stats:
|
|
1204
|
+
stats = team.compression_manager.stats
|
|
1205
|
+
saved = stats.get("original_size", 0) - stats.get("compressed_size", 0)
|
|
1206
|
+
orig = stats.get("original_size", 1)
|
|
1207
|
+
if stats.get("tool_results_compressed", 0) > 0:
|
|
1208
|
+
tool_calls_text += f"\n\ncompressed: {stats.get('tool_results_compressed', 0)} | Saved: {saved:,} chars ({saved / orig * 100:.0f}%)"
|
|
1209
|
+
team.compression_manager.stats.clear()
|
|
1210
|
+
|
|
1211
|
+
team_tool_calls_panel = create_panel(
|
|
1212
|
+
content=tool_calls_text,
|
|
1213
|
+
title="Team Tool Calls",
|
|
1214
|
+
border_style="yellow",
|
|
1215
|
+
)
|
|
1216
|
+
panels.append(team_tool_calls_panel)
|
|
1217
|
+
live_console.update(Group(*panels))
|
|
1218
|
+
|
|
1219
|
+
response_content_batch: Union[str, JSON, Markdown] = _parse_response_content( # type: ignore
|
|
1220
|
+
run_response, tags_to_include_in_markdown, show_markdown=team_markdown
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
# Create panel for response
|
|
1224
|
+
response_panel = create_panel(
|
|
1225
|
+
content=response_content_batch,
|
|
1226
|
+
title=f"Response ({response_timer.elapsed:.1f}s)",
|
|
1227
|
+
border_style="blue",
|
|
1228
|
+
)
|
|
1229
|
+
panels.append(response_panel)
|
|
1230
|
+
|
|
1231
|
+
# Add citations
|
|
1232
|
+
if run_response.citations is not None and run_response.citations.urls is not None:
|
|
1233
|
+
md_lines = []
|
|
1234
|
+
|
|
1235
|
+
# Add search queries if present
|
|
1236
|
+
if run_response.citations.search_queries:
|
|
1237
|
+
md_lines.append("**Search Queries:**")
|
|
1238
|
+
for query in run_response.citations.search_queries:
|
|
1239
|
+
md_lines.append(f"- {query}")
|
|
1240
|
+
md_lines.append("") # Empty line before URLs
|
|
1241
|
+
|
|
1242
|
+
# Add URL citations
|
|
1243
|
+
md_lines.extend(
|
|
1244
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
1245
|
+
for i, citation in enumerate(run_response.citations.urls)
|
|
1246
|
+
if citation.url # Only include citations with valid URLs
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
md_content = "\n".join(md_lines)
|
|
1250
|
+
if md_content: # Only create panel if there are citations
|
|
1251
|
+
citations_panel = create_panel(
|
|
1252
|
+
content=Markdown(md_content),
|
|
1253
|
+
title="Citations",
|
|
1254
|
+
border_style="green",
|
|
1255
|
+
)
|
|
1256
|
+
panels.append(citations_panel)
|
|
1257
|
+
|
|
1258
|
+
if team.memory_manager is not None:
|
|
1259
|
+
if team.memory_manager.memories_updated:
|
|
1260
|
+
memory_panel = create_panel(
|
|
1261
|
+
content=Text("Memories updated"),
|
|
1262
|
+
title="Memories",
|
|
1263
|
+
border_style="green",
|
|
1264
|
+
)
|
|
1265
|
+
panels.append(memory_panel)
|
|
1266
|
+
|
|
1267
|
+
if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
|
|
1268
|
+
summary_panel = create_panel(
|
|
1269
|
+
content=Text("Session summary updated"),
|
|
1270
|
+
title="Session Summary",
|
|
1271
|
+
border_style="green",
|
|
1272
|
+
)
|
|
1273
|
+
panels.append(summary_panel)
|
|
1274
|
+
team.session_summary_manager.summaries_updated = False
|
|
1275
|
+
|
|
1276
|
+
# Final update to remove the "Thinking..." status
|
|
1277
|
+
panels = [p for p in panels if not isinstance(p, Status)]
|
|
1278
|
+
live_console.update(Group(*panels))
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
async def aprint_response_stream(
|
|
1282
|
+
team: "Team",
|
|
1283
|
+
input: Union[List, Dict, str, Message, BaseModel, List[Message]],
|
|
1284
|
+
console: Optional[Any] = None,
|
|
1285
|
+
show_message: bool = True,
|
|
1286
|
+
show_reasoning: bool = True,
|
|
1287
|
+
show_full_reasoning: bool = False,
|
|
1288
|
+
show_member_responses: Optional[bool] = None,
|
|
1289
|
+
tags_to_include_in_markdown: Optional[Set[str]] = None,
|
|
1290
|
+
session_id: Optional[str] = None,
|
|
1291
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
1292
|
+
user_id: Optional[str] = None,
|
|
1293
|
+
run_id: Optional[str] = None,
|
|
1294
|
+
audio: Optional[Sequence[Audio]] = None,
|
|
1295
|
+
images: Optional[Sequence[Image]] = None,
|
|
1296
|
+
videos: Optional[Sequence[Video]] = None,
|
|
1297
|
+
files: Optional[Sequence[File]] = None,
|
|
1298
|
+
markdown: bool = False,
|
|
1299
|
+
stream_events: bool = False,
|
|
1300
|
+
stream_intermediate_steps: bool = False, # type: ignore
|
|
1301
|
+
knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
|
|
1302
|
+
add_history_to_context: Optional[bool] = None,
|
|
1303
|
+
dependencies: Optional[Dict[str, Any]] = None,
|
|
1304
|
+
add_dependencies_to_context: Optional[bool] = None,
|
|
1305
|
+
add_session_state_to_context: Optional[bool] = None,
|
|
1306
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
1307
|
+
debug_mode: Optional[bool] = None,
|
|
1308
|
+
**kwargs: Any,
|
|
1309
|
+
) -> None:
|
|
1310
|
+
import textwrap
|
|
1311
|
+
|
|
1312
|
+
from rich.console import Group
|
|
1313
|
+
from rich.json import JSON
|
|
1314
|
+
from rich.live import Live
|
|
1315
|
+
from rich.markdown import Markdown
|
|
1316
|
+
from rich.status import Status
|
|
1317
|
+
from rich.text import Text
|
|
1318
|
+
|
|
1319
|
+
if not tags_to_include_in_markdown:
|
|
1320
|
+
tags_to_include_in_markdown = {"think", "thinking"}
|
|
1321
|
+
|
|
1322
|
+
stream_events = True # With streaming print response, we need to stream intermediate steps
|
|
1323
|
+
|
|
1324
|
+
_response_content: str = ""
|
|
1325
|
+
_response_reasoning_content: str = ""
|
|
1326
|
+
reasoning_steps: List[ReasoningStep] = []
|
|
1327
|
+
|
|
1328
|
+
# Track tool calls by member and team
|
|
1329
|
+
member_tool_calls = {} # type: ignore
|
|
1330
|
+
team_tool_calls: List[ToolExecution] = []
|
|
1331
|
+
|
|
1332
|
+
# Track processed tool calls to avoid duplicates
|
|
1333
|
+
processed_tool_calls = set()
|
|
1334
|
+
|
|
1335
|
+
# Initialize final_panels here
|
|
1336
|
+
final_panels = [] # type: ignore
|
|
1337
|
+
|
|
1338
|
+
with Live(console=console) as live_console:
|
|
1339
|
+
status = Status("Thinking...", spinner="aesthetic", speed=0.4, refresh_per_second=10)
|
|
1340
|
+
live_console.update(status)
|
|
1341
|
+
response_timer = Timer()
|
|
1342
|
+
response_timer.start()
|
|
1343
|
+
# Flag which indicates if the panels should be rendered
|
|
1344
|
+
render = False
|
|
1345
|
+
# Panels to be rendered
|
|
1346
|
+
panels = [status]
|
|
1347
|
+
# First render the message panel if the message is not None
|
|
1348
|
+
if input and show_message:
|
|
1349
|
+
render = True
|
|
1350
|
+
# Convert message to a panel
|
|
1351
|
+
message_content = get_text_from_message(input)
|
|
1352
|
+
message_panel = create_panel(
|
|
1353
|
+
content=Text(message_content, style="green"),
|
|
1354
|
+
title="Message",
|
|
1355
|
+
border_style="cyan",
|
|
1356
|
+
)
|
|
1357
|
+
panels.append(message_panel)
|
|
1358
|
+
if render:
|
|
1359
|
+
live_console.update(Group(*panels))
|
|
1360
|
+
|
|
1361
|
+
# Get response from the team
|
|
1362
|
+
team_markdown = None
|
|
1363
|
+
member_markdown = {}
|
|
1364
|
+
|
|
1365
|
+
# Dict to track member response panels by member_id
|
|
1366
|
+
member_response_panels = {}
|
|
1367
|
+
|
|
1368
|
+
input_content = get_text_from_message(input)
|
|
1369
|
+
|
|
1370
|
+
final_run_response = None
|
|
1371
|
+
async for resp in team.arun( # type: ignore
|
|
1372
|
+
input=input,
|
|
1373
|
+
audio=audio,
|
|
1374
|
+
images=images,
|
|
1375
|
+
videos=videos,
|
|
1376
|
+
files=files,
|
|
1377
|
+
stream=True,
|
|
1378
|
+
stream_events=stream_events,
|
|
1379
|
+
session_id=session_id,
|
|
1380
|
+
session_state=session_state,
|
|
1381
|
+
user_id=user_id,
|
|
1382
|
+
run_id=run_id,
|
|
1383
|
+
knowledge_filters=knowledge_filters,
|
|
1384
|
+
add_history_to_context=add_history_to_context,
|
|
1385
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1386
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1387
|
+
dependencies=dependencies,
|
|
1388
|
+
metadata=metadata,
|
|
1389
|
+
debug_mode=debug_mode,
|
|
1390
|
+
yield_run_output=True,
|
|
1391
|
+
**kwargs,
|
|
1392
|
+
):
|
|
1393
|
+
if team_markdown is None:
|
|
1394
|
+
if markdown:
|
|
1395
|
+
team_markdown = True
|
|
1396
|
+
else:
|
|
1397
|
+
team_markdown = False
|
|
1398
|
+
|
|
1399
|
+
if team.output_schema is not None:
|
|
1400
|
+
team_markdown = False
|
|
1401
|
+
|
|
1402
|
+
if isinstance(resp, TeamRunOutput):
|
|
1403
|
+
final_run_response = resp
|
|
1404
|
+
continue
|
|
1405
|
+
|
|
1406
|
+
if isinstance(resp, tuple(get_args(TeamRunOutputEvent))):
|
|
1407
|
+
if resp.event == TeamRunEvent.run_content:
|
|
1408
|
+
if isinstance(resp.content, str):
|
|
1409
|
+
_response_content += resp.content
|
|
1410
|
+
elif team.output_schema is not None and isinstance(resp.content, BaseModel):
|
|
1411
|
+
try:
|
|
1412
|
+
_response_content = JSON(resp.content.model_dump_json(exclude_none=True), indent=2) # type: ignore
|
|
1413
|
+
except Exception as e:
|
|
1414
|
+
log_warning(f"Failed to convert response to JSON: {e}")
|
|
1415
|
+
if hasattr(resp, "reasoning_content") and resp.reasoning_content is not None: # type: ignore
|
|
1416
|
+
_response_reasoning_content += resp.reasoning_content # type: ignore
|
|
1417
|
+
if hasattr(resp, "reasoning_steps") and resp.reasoning_steps is not None: # type: ignore
|
|
1418
|
+
reasoning_steps = resp.reasoning_steps # type: ignore
|
|
1419
|
+
|
|
1420
|
+
if resp.event == TeamRunEvent.pre_hook_completed: # type: ignore
|
|
1421
|
+
if resp.run_input is not None: # type: ignore
|
|
1422
|
+
input_content = get_text_from_message(resp.run_input.input_content) # type: ignore
|
|
1423
|
+
|
|
1424
|
+
# Collect team tool calls, avoiding duplicates
|
|
1425
|
+
if resp.event == TeamRunEvent.tool_call_completed and resp.tool: # type: ignore
|
|
1426
|
+
tool = resp.tool # type: ignore
|
|
1427
|
+
# Generate a unique ID for this tool call
|
|
1428
|
+
if tool.tool_call_id is not None:
|
|
1429
|
+
tool_id = tool.tool_call_id
|
|
1430
|
+
else:
|
|
1431
|
+
tool_id = str(hash(str(tool)))
|
|
1432
|
+
if tool_id not in processed_tool_calls:
|
|
1433
|
+
processed_tool_calls.add(tool_id)
|
|
1434
|
+
team_tool_calls.append(tool)
|
|
1435
|
+
|
|
1436
|
+
# Collect member tool calls, avoiding duplicates
|
|
1437
|
+
if show_member_responses and hasattr(resp, "member_responses") and resp.member_responses:
|
|
1438
|
+
for member_response in resp.member_responses:
|
|
1439
|
+
member_id = None
|
|
1440
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1441
|
+
member_id = member_response.agent_id
|
|
1442
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1443
|
+
member_id = member_response.team_id
|
|
1444
|
+
|
|
1445
|
+
if member_id and hasattr(member_response, "tools") and member_response.tools:
|
|
1446
|
+
if member_id not in member_tool_calls:
|
|
1447
|
+
member_tool_calls[member_id] = []
|
|
1448
|
+
|
|
1449
|
+
for tool in member_response.tools:
|
|
1450
|
+
if tool.tool_call_id is not None:
|
|
1451
|
+
tool_id = tool.tool_call_id
|
|
1452
|
+
else:
|
|
1453
|
+
tool_id = str(hash(str(tool)))
|
|
1454
|
+
if tool_id not in processed_tool_calls:
|
|
1455
|
+
processed_tool_calls.add(tool_id)
|
|
1456
|
+
member_tool_calls[member_id].append(tool)
|
|
1457
|
+
|
|
1458
|
+
response_content_stream: Union[str, Markdown] = _response_content
|
|
1459
|
+
# Escape special tags before markdown conversion
|
|
1460
|
+
if team_markdown:
|
|
1461
|
+
escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
|
|
1462
|
+
response_content_stream = Markdown(escaped_content)
|
|
1463
|
+
|
|
1464
|
+
# Create new panels for each chunk
|
|
1465
|
+
panels = []
|
|
1466
|
+
|
|
1467
|
+
if input_content and show_message:
|
|
1468
|
+
render = True
|
|
1469
|
+
# Convert message to a panel
|
|
1470
|
+
message_panel = create_panel(
|
|
1471
|
+
content=Text(input_content, style="green"),
|
|
1472
|
+
title="Message",
|
|
1473
|
+
border_style="cyan",
|
|
1474
|
+
)
|
|
1475
|
+
panels.append(message_panel)
|
|
1476
|
+
|
|
1477
|
+
if len(reasoning_steps) > 0 and show_reasoning:
|
|
1478
|
+
render = True
|
|
1479
|
+
# Create panels for reasoning steps
|
|
1480
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
1481
|
+
reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
|
|
1482
|
+
panels.append(reasoning_panel)
|
|
1483
|
+
|
|
1484
|
+
if len(_response_reasoning_content) > 0 and show_reasoning:
|
|
1485
|
+
render = True
|
|
1486
|
+
# Create panel for thinking
|
|
1487
|
+
thinking_panel = create_panel(
|
|
1488
|
+
content=Text(_response_reasoning_content),
|
|
1489
|
+
title=f"Thinking ({response_timer.elapsed:.1f}s)",
|
|
1490
|
+
border_style="green",
|
|
1491
|
+
)
|
|
1492
|
+
panels.append(thinking_panel)
|
|
1493
|
+
elif _response_content == "":
|
|
1494
|
+
# Keep showing status if no content yet
|
|
1495
|
+
panels.append(status)
|
|
1496
|
+
|
|
1497
|
+
# Process member responses and their tool calls
|
|
1498
|
+
for member_response in (
|
|
1499
|
+
resp.member_responses if show_member_responses and hasattr(resp, "member_responses") else []
|
|
1500
|
+
):
|
|
1501
|
+
member_id = None
|
|
1502
|
+
member_name = "Team Member"
|
|
1503
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1504
|
+
member_id = member_response.agent_id
|
|
1505
|
+
member_name = team._get_member_name(member_id)
|
|
1506
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1507
|
+
member_id = member_response.team_id
|
|
1508
|
+
|
|
1509
|
+
member_name = team._get_member_name(member_id)
|
|
1510
|
+
|
|
1511
|
+
# If we have tool calls for this member, display them
|
|
1512
|
+
if member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
1513
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
1514
|
+
if formatted_calls:
|
|
1515
|
+
console_width = console.width if console else 80
|
|
1516
|
+
panel_width = console_width + 30
|
|
1517
|
+
|
|
1518
|
+
lines = []
|
|
1519
|
+
for call in formatted_calls:
|
|
1520
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
1521
|
+
lines.append(wrapped_call)
|
|
1522
|
+
|
|
1523
|
+
tool_calls_text = "\n\n".join(lines)
|
|
1524
|
+
|
|
1525
|
+
member_tool_calls_panel = create_panel(
|
|
1526
|
+
content=tool_calls_text,
|
|
1527
|
+
title=f"{member_name} Tool Calls",
|
|
1528
|
+
border_style="yellow",
|
|
1529
|
+
)
|
|
1530
|
+
panels.append(member_tool_calls_panel)
|
|
1531
|
+
|
|
1532
|
+
# Process member response content
|
|
1533
|
+
if show_member_responses and member_id is not None:
|
|
1534
|
+
show_markdown = False
|
|
1535
|
+
if markdown:
|
|
1536
|
+
show_markdown = True
|
|
1537
|
+
|
|
1538
|
+
member_response_content = _parse_response_content(
|
|
1539
|
+
member_response,
|
|
1540
|
+
tags_to_include_in_markdown,
|
|
1541
|
+
show_markdown=show_markdown,
|
|
1542
|
+
)
|
|
1543
|
+
|
|
1544
|
+
member_response_panel = create_panel(
|
|
1545
|
+
content=member_response_content,
|
|
1546
|
+
title=f"{member_name} Response",
|
|
1547
|
+
border_style="magenta",
|
|
1548
|
+
)
|
|
1549
|
+
|
|
1550
|
+
panels.append(member_response_panel)
|
|
1551
|
+
|
|
1552
|
+
# Store for reference
|
|
1553
|
+
if member_id is not None:
|
|
1554
|
+
member_response_panels[member_id] = member_response_panel
|
|
1555
|
+
|
|
1556
|
+
# Add team tool calls panel if available (before the team response)
|
|
1557
|
+
if team_tool_calls:
|
|
1558
|
+
formatted_calls = format_tool_calls(team_tool_calls)
|
|
1559
|
+
if formatted_calls:
|
|
1560
|
+
console_width = console.width if console else 80
|
|
1561
|
+
panel_width = console_width + 30
|
|
1562
|
+
|
|
1563
|
+
lines = []
|
|
1564
|
+
# Create a set to track already added calls by their string representation
|
|
1565
|
+
added_calls = set()
|
|
1566
|
+
for call in formatted_calls:
|
|
1567
|
+
if call not in added_calls:
|
|
1568
|
+
added_calls.add(call)
|
|
1569
|
+
# Wrap the call text to fit within the panel
|
|
1570
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
1571
|
+
lines.append(wrapped_call)
|
|
1572
|
+
|
|
1573
|
+
# Join with blank lines between items
|
|
1574
|
+
tool_calls_text = "\n\n".join(lines)
|
|
1575
|
+
|
|
1576
|
+
# Add compression stats if available (don't clear - will be cleared in final_panels)
|
|
1577
|
+
if team.compression_manager is not None and team.compression_manager.stats:
|
|
1578
|
+
stats = team.compression_manager.stats
|
|
1579
|
+
saved = stats.get("original_size", 0) - stats.get("compressed_size", 0)
|
|
1580
|
+
orig = stats.get("original_size", 1)
|
|
1581
|
+
if stats.get("tool_results_compressed", 0) > 0:
|
|
1582
|
+
tool_calls_text += f"\n\ncompressed: {stats.get('tool_results_compressed', 0)} | Saved: {saved:,} chars ({saved / orig * 100:.0f}%)"
|
|
1583
|
+
|
|
1584
|
+
team_tool_calls_panel = create_panel(
|
|
1585
|
+
content=tool_calls_text,
|
|
1586
|
+
title="Team Tool Calls",
|
|
1587
|
+
border_style="yellow",
|
|
1588
|
+
)
|
|
1589
|
+
panels.append(team_tool_calls_panel)
|
|
1590
|
+
|
|
1591
|
+
# Add the team response panel at the end
|
|
1592
|
+
if response_content_stream:
|
|
1593
|
+
render = True
|
|
1594
|
+
# Create panel for response
|
|
1595
|
+
response_panel = create_panel(
|
|
1596
|
+
content=response_content_stream,
|
|
1597
|
+
title=f"Response ({response_timer.elapsed:.1f}s)",
|
|
1598
|
+
border_style="blue",
|
|
1599
|
+
)
|
|
1600
|
+
panels.append(response_panel)
|
|
1601
|
+
|
|
1602
|
+
if render or len(panels) > 0:
|
|
1603
|
+
live_console.update(Group(*panels))
|
|
1604
|
+
|
|
1605
|
+
response_timer.stop()
|
|
1606
|
+
|
|
1607
|
+
run_response = final_run_response
|
|
1608
|
+
|
|
1609
|
+
# Add citations
|
|
1610
|
+
if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
|
|
1611
|
+
md_lines = []
|
|
1612
|
+
|
|
1613
|
+
# Add search queries if present
|
|
1614
|
+
if resp.citations.search_queries:
|
|
1615
|
+
md_lines.append("**Search Queries:**")
|
|
1616
|
+
for query in resp.citations.search_queries:
|
|
1617
|
+
md_lines.append(f"- {query}")
|
|
1618
|
+
md_lines.append("") # Empty line before URLs
|
|
1619
|
+
|
|
1620
|
+
# Add URL citations
|
|
1621
|
+
md_lines.extend(
|
|
1622
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
1623
|
+
for i, citation in enumerate(resp.citations.urls)
|
|
1624
|
+
if citation.url # Only include citations with valid URLs
|
|
1625
|
+
)
|
|
1626
|
+
|
|
1627
|
+
md_content = "\n".join(md_lines)
|
|
1628
|
+
if md_content: # Only create panel if there are citations
|
|
1629
|
+
citations_panel = create_panel(
|
|
1630
|
+
content=Markdown(md_content),
|
|
1631
|
+
title="Citations",
|
|
1632
|
+
border_style="green",
|
|
1633
|
+
)
|
|
1634
|
+
panels.append(citations_panel)
|
|
1635
|
+
live_console.update(Group(*panels))
|
|
1636
|
+
|
|
1637
|
+
if team.memory_manager is not None:
|
|
1638
|
+
if team.memory_manager.memories_updated:
|
|
1639
|
+
memory_panel = create_panel(
|
|
1640
|
+
content=Text("Memories updated"),
|
|
1641
|
+
title="Memories",
|
|
1642
|
+
border_style="green",
|
|
1643
|
+
)
|
|
1644
|
+
panels.append(memory_panel)
|
|
1645
|
+
live_console.update(Group(*panels))
|
|
1646
|
+
|
|
1647
|
+
if team.session_summary_manager is not None and team.session_summary_manager.summaries_updated:
|
|
1648
|
+
summary_panel = create_panel(
|
|
1649
|
+
content=Text("Session summary updated"),
|
|
1650
|
+
title="Session Summary",
|
|
1651
|
+
border_style="green",
|
|
1652
|
+
)
|
|
1653
|
+
panels.append(summary_panel)
|
|
1654
|
+
live_console.update(Group(*panels))
|
|
1655
|
+
team.session_summary_manager.summaries_updated = False
|
|
1656
|
+
|
|
1657
|
+
# Final update to remove the "Thinking..." status
|
|
1658
|
+
panels = [p for p in panels if not isinstance(p, Status)]
|
|
1659
|
+
|
|
1660
|
+
if markdown:
|
|
1661
|
+
for member in team.members:
|
|
1662
|
+
if member.id is not None:
|
|
1663
|
+
member_markdown[member.id] = True # type: ignore
|
|
1664
|
+
|
|
1665
|
+
for member in team.members:
|
|
1666
|
+
if member.output_schema is not None and member.id is not None:
|
|
1667
|
+
member_markdown[member.id] = False # type: ignore
|
|
1668
|
+
|
|
1669
|
+
# Final panels assembly - we'll recreate the panels from scratch to ensure correct order
|
|
1670
|
+
final_panels = []
|
|
1671
|
+
|
|
1672
|
+
# Start with the message
|
|
1673
|
+
if input_content and show_message:
|
|
1674
|
+
message_panel = create_panel(
|
|
1675
|
+
content=Text(input_content, style="green"),
|
|
1676
|
+
title="Message",
|
|
1677
|
+
border_style="cyan",
|
|
1678
|
+
)
|
|
1679
|
+
final_panels.append(message_panel)
|
|
1680
|
+
|
|
1681
|
+
# Add reasoning steps
|
|
1682
|
+
if reasoning_steps and show_reasoning:
|
|
1683
|
+
for i, step in enumerate(reasoning_steps, 1):
|
|
1684
|
+
reasoning_panel = build_reasoning_step_panel(i, step, show_full_reasoning)
|
|
1685
|
+
final_panels.append(reasoning_panel)
|
|
1686
|
+
|
|
1687
|
+
# Add thinking panel if available
|
|
1688
|
+
if _response_reasoning_content and show_reasoning:
|
|
1689
|
+
thinking_panel = create_panel(
|
|
1690
|
+
content=Text(_response_reasoning_content),
|
|
1691
|
+
title=f"Thinking ({response_timer.elapsed:.1f}s)",
|
|
1692
|
+
border_style="green",
|
|
1693
|
+
)
|
|
1694
|
+
final_panels.append(thinking_panel)
|
|
1695
|
+
|
|
1696
|
+
# Add member tool calls and responses in correct order
|
|
1697
|
+
if show_member_responses and run_response is not None and hasattr(run_response, "member_responses"):
|
|
1698
|
+
for i, member_response in enumerate(run_response.member_responses):
|
|
1699
|
+
member_id = None
|
|
1700
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1701
|
+
member_id = member_response.agent_id
|
|
1702
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1703
|
+
member_id = member_response.team_id
|
|
1704
|
+
|
|
1705
|
+
# Print tool calls
|
|
1706
|
+
if member_id:
|
|
1707
|
+
# First add tool calls if any
|
|
1708
|
+
if member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
1709
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
1710
|
+
if formatted_calls:
|
|
1711
|
+
console_width = console.width if console else 80
|
|
1712
|
+
panel_width = console_width + 30
|
|
1713
|
+
|
|
1714
|
+
lines = []
|
|
1715
|
+
# Create a set to track already added calls by their string representation
|
|
1716
|
+
added_calls = set()
|
|
1717
|
+
for call in formatted_calls:
|
|
1718
|
+
if call not in added_calls:
|
|
1719
|
+
added_calls.add(call)
|
|
1720
|
+
# Wrap the call text to fit within the panel
|
|
1721
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
1722
|
+
lines.append(wrapped_call)
|
|
1723
|
+
|
|
1724
|
+
tool_calls_text = "\n\n".join(lines)
|
|
1725
|
+
|
|
1726
|
+
member_name = team._get_member_name(member_id)
|
|
1727
|
+
member_tool_calls_panel = create_panel(
|
|
1728
|
+
content=tool_calls_text,
|
|
1729
|
+
title=f"{member_name} Tool Calls",
|
|
1730
|
+
border_style="yellow",
|
|
1731
|
+
)
|
|
1732
|
+
final_panels.append(member_tool_calls_panel)
|
|
1733
|
+
|
|
1734
|
+
# Add reasoning steps if any
|
|
1735
|
+
reasoning_steps = []
|
|
1736
|
+
if member_response.reasoning_steps is not None:
|
|
1737
|
+
reasoning_steps = member_response.reasoning_steps
|
|
1738
|
+
if reasoning_steps and show_reasoning:
|
|
1739
|
+
for j, step in enumerate(reasoning_steps, 1):
|
|
1740
|
+
member_reasoning_panel = build_reasoning_step_panel(
|
|
1741
|
+
j, step, show_full_reasoning, color="magenta"
|
|
1742
|
+
)
|
|
1743
|
+
final_panels.append(member_reasoning_panel)
|
|
1744
|
+
|
|
1745
|
+
# Add reasoning steps if any
|
|
1746
|
+
reasoning_steps = []
|
|
1747
|
+
if hasattr(member_response, "reasoning_steps") and member_response.reasoning_steps is not None:
|
|
1748
|
+
reasoning_steps = member_response.reasoning_steps
|
|
1749
|
+
if reasoning_steps and show_reasoning:
|
|
1750
|
+
for j, step in enumerate(reasoning_steps, 1):
|
|
1751
|
+
member_reasoning_panel = build_reasoning_step_panel(
|
|
1752
|
+
j, step, show_full_reasoning, color="magenta"
|
|
1753
|
+
)
|
|
1754
|
+
final_panels.append(member_reasoning_panel)
|
|
1755
|
+
|
|
1756
|
+
# Then add response
|
|
1757
|
+
show_markdown = False
|
|
1758
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1759
|
+
show_markdown = member_markdown.get(member_response.agent_id, False)
|
|
1760
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1761
|
+
show_markdown = member_markdown.get(member_response.team_id, False)
|
|
1762
|
+
|
|
1763
|
+
member_response_content = _parse_response_content( # type: ignore
|
|
1764
|
+
member_response,
|
|
1765
|
+
tags_to_include_in_markdown,
|
|
1766
|
+
show_markdown=show_markdown,
|
|
1767
|
+
)
|
|
1768
|
+
|
|
1769
|
+
member_name = "Team Member"
|
|
1770
|
+
if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
|
|
1771
|
+
member_name = team._get_member_name(member_response.agent_id)
|
|
1772
|
+
elif isinstance(member_response, TeamRunOutput) and member_response.team_id is not None:
|
|
1773
|
+
member_name = team._get_member_name(member_response.team_id)
|
|
1774
|
+
|
|
1775
|
+
member_response_panel = create_panel(
|
|
1776
|
+
content=member_response_content,
|
|
1777
|
+
title=f"{member_name} Response",
|
|
1778
|
+
border_style="magenta",
|
|
1779
|
+
)
|
|
1780
|
+
final_panels.append(member_response_panel)
|
|
1781
|
+
|
|
1782
|
+
# Add citations if any
|
|
1783
|
+
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
1784
|
+
md_lines = []
|
|
1785
|
+
|
|
1786
|
+
# Add search queries if present
|
|
1787
|
+
if member_response.citations.search_queries:
|
|
1788
|
+
md_lines.append("**Search Queries:**")
|
|
1789
|
+
for query in member_response.citations.search_queries:
|
|
1790
|
+
md_lines.append(f"- {query}")
|
|
1791
|
+
md_lines.append("") # Empty line before URLs
|
|
1792
|
+
|
|
1793
|
+
# Add URL citations
|
|
1794
|
+
md_lines.extend(
|
|
1795
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
1796
|
+
for i, citation in enumerate(member_response.citations.urls)
|
|
1797
|
+
if citation.url # Only include citations with valid URLs
|
|
1798
|
+
)
|
|
1799
|
+
|
|
1800
|
+
md_content = "\n".join(md_lines)
|
|
1801
|
+
if md_content: # Only create panel if there are citations
|
|
1802
|
+
citations_panel = create_panel(
|
|
1803
|
+
content=Markdown(md_content),
|
|
1804
|
+
title="Citations",
|
|
1805
|
+
border_style="magenta",
|
|
1806
|
+
)
|
|
1807
|
+
final_panels.append(citations_panel)
|
|
1808
|
+
|
|
1809
|
+
# Add team tool calls before team response
|
|
1810
|
+
if team_tool_calls:
|
|
1811
|
+
formatted_calls = format_tool_calls(team_tool_calls)
|
|
1812
|
+
if formatted_calls:
|
|
1813
|
+
console_width = console.width if console else 80
|
|
1814
|
+
panel_width = console_width + 30
|
|
1815
|
+
|
|
1816
|
+
lines = []
|
|
1817
|
+
# Create a set to track already added calls by their string representation
|
|
1818
|
+
added_calls = set()
|
|
1819
|
+
for call in formatted_calls:
|
|
1820
|
+
if call not in added_calls:
|
|
1821
|
+
added_calls.add(call)
|
|
1822
|
+
# Wrap the call text to fit within the panel
|
|
1823
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
1824
|
+
lines.append(wrapped_call)
|
|
1825
|
+
|
|
1826
|
+
tool_calls_text = "\n\n".join(lines)
|
|
1827
|
+
|
|
1828
|
+
# Add compression stats at end of tool calls
|
|
1829
|
+
if team.compression_manager is not None and team.compression_manager.stats:
|
|
1830
|
+
stats = team.compression_manager.stats
|
|
1831
|
+
saved = stats.get("original_size", 0) - stats.get("compressed_size", 0)
|
|
1832
|
+
orig = stats.get("original_size", 1)
|
|
1833
|
+
if stats.get("tool_results_compressed", 0) > 0:
|
|
1834
|
+
tool_calls_text += f"\n\ncompressed: {stats.get('tool_results_compressed', 0)} | Saved: {saved:,} chars ({saved / orig * 100:.0f}%)"
|
|
1835
|
+
team.compression_manager.stats.clear()
|
|
1836
|
+
|
|
1837
|
+
team_tool_calls_panel = create_panel(
|
|
1838
|
+
content=tool_calls_text,
|
|
1839
|
+
title="Team Tool Calls",
|
|
1840
|
+
border_style="yellow",
|
|
1841
|
+
)
|
|
1842
|
+
final_panels.append(team_tool_calls_panel)
|
|
1843
|
+
|
|
1844
|
+
# Add team response
|
|
1845
|
+
if _response_content:
|
|
1846
|
+
response_content_stream = _response_content
|
|
1847
|
+
if team_markdown:
|
|
1848
|
+
escaped_content = escape_markdown_tags(_response_content, tags_to_include_in_markdown)
|
|
1849
|
+
response_content_stream = Markdown(escaped_content)
|
|
1850
|
+
|
|
1851
|
+
response_panel = create_panel(
|
|
1852
|
+
content=response_content_stream,
|
|
1853
|
+
title=f"Response ({response_timer.elapsed:.1f}s)",
|
|
1854
|
+
border_style="blue",
|
|
1855
|
+
)
|
|
1856
|
+
final_panels.append(response_panel)
|
|
1857
|
+
|
|
1858
|
+
# Add team citations
|
|
1859
|
+
if hasattr(resp, "citations") and resp.citations is not None and resp.citations.urls is not None:
|
|
1860
|
+
md_lines = []
|
|
1861
|
+
|
|
1862
|
+
# Add search queries if present
|
|
1863
|
+
if resp.citations.search_queries:
|
|
1864
|
+
md_lines.append("**Search Queries:**")
|
|
1865
|
+
for query in resp.citations.search_queries:
|
|
1866
|
+
md_lines.append(f"- {query}")
|
|
1867
|
+
md_lines.append("") # Empty line before URLs
|
|
1868
|
+
|
|
1869
|
+
# Add URL citations
|
|
1870
|
+
md_lines.extend(
|
|
1871
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
1872
|
+
for i, citation in enumerate(resp.citations.urls)
|
|
1873
|
+
if citation.url # Only include citations with valid URLs
|
|
1874
|
+
)
|
|
1875
|
+
|
|
1876
|
+
md_content = "\n".join(md_lines)
|
|
1877
|
+
if md_content: # Only create panel if there are citations
|
|
1878
|
+
citations_panel = create_panel(
|
|
1879
|
+
content=Markdown(md_content),
|
|
1880
|
+
title="Citations",
|
|
1881
|
+
border_style="green",
|
|
1882
|
+
)
|
|
1883
|
+
final_panels.append(citations_panel)
|
|
1884
|
+
|
|
1885
|
+
# Final update with correctly ordered panels
|
|
1886
|
+
live_console.update(Group(*final_panels))
|
|
1887
|
+
|
|
1888
|
+
|
|
1889
|
+
def _parse_response_content(
|
|
1890
|
+
run_response: Union[TeamRunOutput, RunOutput],
|
|
1891
|
+
tags_to_include_in_markdown: Set[str],
|
|
1892
|
+
show_markdown: bool = True,
|
|
1893
|
+
) -> Any:
|
|
1894
|
+
from rich.json import JSON
|
|
1895
|
+
from rich.markdown import Markdown
|
|
1896
|
+
|
|
1897
|
+
if isinstance(run_response.content, str):
|
|
1898
|
+
if show_markdown:
|
|
1899
|
+
escaped_content = escape_markdown_tags(run_response.content, tags_to_include_in_markdown)
|
|
1900
|
+
return Markdown(escaped_content)
|
|
1901
|
+
else:
|
|
1902
|
+
return run_response.get_content_as_string(indent=4)
|
|
1903
|
+
elif isinstance(run_response.content, BaseModel):
|
|
1904
|
+
try:
|
|
1905
|
+
return JSON(run_response.content.model_dump_json(exclude_none=True), indent=2)
|
|
1906
|
+
except Exception as e:
|
|
1907
|
+
log_warning(f"Failed to convert response to JSON: {e}")
|
|
1908
|
+
else:
|
|
1909
|
+
import json
|
|
1910
|
+
|
|
1911
|
+
try:
|
|
1912
|
+
return JSON(json.dumps(run_response.content), indent=4)
|
|
1913
|
+
except Exception as e:
|
|
1914
|
+
log_warning(f"Failed to convert response to JSON: {e}")
|