agno 1.8.1__py3-none-any.whl → 2.0.0__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 +19 -27
- agno/agent/agent.py +3143 -4170
- agno/api/agent.py +11 -67
- agno/api/api.py +5 -46
- agno/api/evals.py +8 -19
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -41
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +5 -21
- agno/api/schemas/evals.py +7 -16
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +5 -21
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +11 -7
- agno/api/settings.py +53 -0
- agno/api/team.py +11 -66
- 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/db/__init__.py +24 -0
- agno/db/base.py +245 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +1743 -0
- agno/db/dynamo/schemas.py +278 -0
- agno/db/dynamo/utils.py +684 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1432 -0
- agno/db/firestore/schemas.py +130 -0
- agno/db/firestore/utils.py +278 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1001 -0
- agno/db/gcs_json/utils.py +194 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +882 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1045 -0
- agno/db/json/utils.py +196 -0
- agno/db/migrations/v1_to_v2.py +162 -0
- agno/db/mongo/__init__.py +3 -0
- agno/db/mongo/mongo.py +1416 -0
- agno/db/mongo/schemas.py +77 -0
- agno/db/mongo/utils.py +204 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +1719 -0
- agno/db/mysql/schemas.py +124 -0
- agno/db/mysql/utils.py +297 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1710 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +280 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1367 -0
- agno/db/redis/schemas.py +109 -0
- agno/db/redis/utils.py +288 -0
- agno/db/schemas/__init__.py +3 -0
- agno/db/schemas/evals.py +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +116 -0
- agno/db/singlestore/singlestore.py +1712 -0
- agno/db/singlestore/utils.py +326 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1676 -0
- agno/db/sqlite/utils.py +268 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +154 -48
- agno/eval/performance.py +88 -23
- agno/eval/reliability.py +73 -20
- agno/eval/utils.py +23 -13
- agno/integrations/discord/__init__.py +3 -0
- agno/{app → integrations}/discord/client.py +15 -11
- agno/knowledge/__init__.py +2 -2
- agno/{document → knowledge}/chunking/agentic.py +2 -2
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +3 -3
- agno/{document → knowledge}/chunking/markdown.py +2 -2
- agno/{document → knowledge}/chunking/recursive.py +2 -2
- agno/{document → knowledge}/chunking/row.py +2 -2
- agno/knowledge/chunking/semantic.py +59 -0
- agno/knowledge/chunking/strategy.py +121 -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/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
- agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
- agno/{embedder → knowledge/embedder}/base.py +6 -0
- agno/{embedder → knowledge/embedder}/cohere.py +72 -1
- agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/{embedder → knowledge/embedder}/google.py +74 -1
- agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
- agno/{embedder → knowledge/embedder}/jina.py +48 -2
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +139 -0
- agno/{embedder → knowledge/embedder}/nebius.py +1 -1
- agno/{embedder → knowledge/embedder}/ollama.py +54 -3
- agno/knowledge/embedder/openai.py +223 -0
- agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
- agno/knowledge/knowledge.py +1551 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
- agno/knowledge/reader/base.py +88 -0
- agno/{document → knowledge}/reader/csv_reader.py +47 -65
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +58 -9
- agno/{document → knowledge}/reader/pdf_reader.py +71 -126
- agno/knowledge/reader/reader_factory.py +268 -0
- agno/knowledge/reader/s3_reader.py +101 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +128 -0
- agno/knowledge/reader/web_search_reader.py +366 -0
- agno/{document → knowledge}/reader/website_reader.py +37 -10
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/{reranker → knowledge/reranker}/infinity.py +2 -2
- agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
- agno/knowledge/types.py +30 -0
- agno/knowledge/utils.py +169 -0
- agno/media.py +269 -268
- agno/memory/__init__.py +2 -10
- agno/memory/manager.py +1003 -148
- agno/models/aimlapi/__init__.py +2 -2
- agno/models/aimlapi/aimlapi.py +6 -6
- agno/models/anthropic/claude.py +131 -131
- agno/models/aws/bedrock.py +110 -182
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +346 -290
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +105 -46
- agno/models/groq/groq.py +97 -35
- agno/models/huggingface/huggingface.py +92 -27
- agno/models/ibm/watsonx.py +72 -13
- agno/models/litellm/chat.py +85 -13
- agno/models/message.py +46 -151
- agno/models/meta/llama.py +85 -49
- agno/models/metrics.py +120 -0
- agno/models/mistral/mistral.py +90 -21
- agno/models/ollama/__init__.py +0 -2
- agno/models/ollama/chat.py +85 -47
- agno/models/openai/chat.py +154 -37
- agno/models/openai/responses.py +178 -105
- agno/models/perplexity/perplexity.py +26 -2
- agno/models/portkey/portkey.py +0 -7
- agno/models/response.py +15 -9
- agno/models/utils.py +20 -0
- agno/models/vercel/__init__.py +2 -2
- agno/models/vercel/v0.py +1 -1
- agno/models/vllm/__init__.py +2 -2
- agno/models/vllm/vllm.py +3 -3
- agno/models/xai/xai.py +10 -10
- agno/os/__init__.py +3 -0
- agno/os/app.py +497 -0
- agno/os/auth.py +47 -0
- agno/os/config.py +103 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +31 -0
- agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
- agno/{app → os/interfaces}/agui/utils.py +77 -33
- agno/os/interfaces/base.py +21 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
- agno/os/interfaces/slack/slack.py +32 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
- agno/os/interfaces/whatsapp/whatsapp.py +29 -0
- agno/os/mcp.py +235 -0
- agno/os/router.py +1400 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +393 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +161 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +850 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +410 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +178 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +536 -0
- agno/os/schema.py +945 -0
- agno/{app/playground → os}/settings.py +7 -15
- agno/os/utils.py +270 -0
- agno/reasoning/azure_ai_foundry.py +4 -4
- agno/reasoning/deepseek.py +4 -4
- agno/reasoning/default.py +6 -11
- agno/reasoning/groq.py +4 -4
- agno/reasoning/helpers.py +4 -6
- agno/reasoning/ollama.py +4 -4
- agno/reasoning/openai.py +4 -4
- agno/run/agent.py +633 -0
- agno/run/base.py +53 -77
- agno/run/cancel.py +81 -0
- agno/run/team.py +243 -96
- agno/run/workflow.py +550 -12
- agno/session/__init__.py +10 -0
- agno/session/agent.py +244 -0
- agno/session/summary.py +225 -0
- agno/session/team.py +262 -0
- agno/{storage/session/v2 → session}/workflow.py +47 -24
- agno/team/__init__.py +15 -16
- agno/team/team.py +3260 -4824
- agno/tools/agentql.py +14 -5
- agno/tools/airflow.py +9 -4
- agno/tools/api.py +7 -3
- agno/tools/apify.py +2 -46
- agno/tools/arxiv.py +8 -3
- agno/tools/aws_lambda.py +7 -5
- agno/tools/aws_ses.py +7 -1
- agno/tools/baidusearch.py +4 -1
- agno/tools/bitbucket.py +4 -4
- agno/tools/brandfetch.py +14 -11
- agno/tools/bravesearch.py +4 -1
- agno/tools/brightdata.py +43 -23
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +20 -17
- agno/tools/{clickup_tool.py → clickup.py} +12 -25
- agno/tools/confluence.py +8 -8
- agno/tools/crawl4ai.py +7 -1
- agno/tools/csv_toolkit.py +9 -8
- agno/tools/dalle.py +22 -12
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +17 -8
- agno/tools/discord.py +11 -8
- agno/tools/docker.py +30 -42
- agno/tools/duckdb.py +34 -53
- agno/tools/duckduckgo.py +8 -7
- agno/tools/e2b.py +62 -62
- agno/tools/eleven_labs.py +36 -29
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +30 -30
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +127 -18
- agno/tools/giphy.py +23 -11
- agno/tools/github.py +48 -126
- agno/tools/gmail.py +45 -61
- agno/tools/google_bigquery.py +7 -6
- agno/tools/google_maps.py +11 -26
- agno/tools/googlesearch.py +7 -2
- agno/tools/googlesheets.py +21 -17
- agno/tools/hackernews.py +9 -5
- agno/tools/jina.py +5 -4
- agno/tools/jira.py +18 -9
- agno/tools/knowledge.py +31 -32
- agno/tools/linear.py +19 -34
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +32 -20
- agno/tools/mcp.py +1 -2
- agno/tools/mem0.py +18 -12
- agno/tools/memori.py +14 -10
- agno/tools/mlx_transcribe.py +3 -2
- agno/tools/models/azure_openai.py +33 -15
- agno/tools/models/gemini.py +59 -32
- agno/tools/models/groq.py +30 -23
- agno/tools/models/nebius.py +28 -12
- agno/tools/models_labs.py +40 -16
- agno/tools/moviepy_video.py +7 -6
- agno/tools/neo4j.py +10 -8
- agno/tools/newspaper.py +7 -2
- agno/tools/newspaper4k.py +8 -3
- agno/tools/openai.py +58 -32
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +63 -47
- agno/tools/openweather.py +14 -12
- agno/tools/pandas.py +11 -3
- agno/tools/postgres.py +4 -12
- agno/tools/pubmed.py +4 -1
- agno/tools/python.py +9 -22
- agno/tools/reasoning.py +35 -27
- agno/tools/reddit.py +11 -26
- agno/tools/replicate.py +55 -42
- agno/tools/resend.py +4 -1
- agno/tools/scrapegraph.py +15 -14
- agno/tools/searxng.py +10 -23
- agno/tools/serpapi.py +6 -3
- agno/tools/serper.py +13 -4
- agno/tools/shell.py +9 -2
- agno/tools/slack.py +12 -11
- agno/tools/sleep.py +3 -2
- agno/tools/spider.py +24 -4
- agno/tools/sql.py +7 -6
- agno/tools/tavily.py +6 -4
- agno/tools/telegram.py +12 -4
- agno/tools/todoist.py +11 -31
- agno/tools/toolkit.py +1 -1
- agno/tools/trafilatura.py +22 -6
- agno/tools/trello.py +9 -22
- agno/tools/twilio.py +10 -3
- agno/tools/user_control_flow.py +6 -1
- agno/tools/valyu.py +34 -5
- agno/tools/visualization.py +19 -28
- agno/tools/webbrowser.py +4 -3
- agno/tools/webex.py +11 -7
- agno/tools/website.py +15 -46
- agno/tools/webtools.py +12 -4
- agno/tools/whatsapp.py +5 -9
- agno/tools/wikipedia.py +20 -13
- agno/tools/x.py +14 -13
- agno/tools/yfinance.py +13 -40
- agno/tools/youtube.py +26 -20
- agno/tools/zendesk.py +7 -2
- agno/tools/zep.py +10 -7
- agno/tools/zoom.py +10 -9
- agno/utils/common.py +1 -19
- agno/utils/events.py +100 -123
- agno/utils/gemini.py +32 -2
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +54 -4
- agno/utils/mcp.py +68 -10
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/aws_claude.py +1 -1
- agno/utils/models/claude.py +47 -4
- agno/utils/models/cohere.py +1 -1
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1669 -0
- agno/utils/print_response/workflow.py +1451 -0
- agno/utils/prompts.py +14 -14
- agno/utils/reasoning.py +87 -0
- agno/utils/response.py +42 -42
- agno/utils/streamlit.py +481 -0
- agno/utils/string.py +8 -22
- agno/utils/team.py +50 -0
- agno/utils/timer.py +2 -2
- agno/vectordb/base.py +33 -21
- agno/vectordb/cassandra/cassandra.py +287 -23
- agno/vectordb/chroma/chromadb.py +482 -59
- agno/vectordb/clickhouse/clickhousedb.py +270 -63
- agno/vectordb/couchbase/couchbase.py +309 -29
- agno/vectordb/lancedb/lance_db.py +360 -21
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +145 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +374 -0
- agno/vectordb/llamaindex/llamaindexdb.py +127 -0
- agno/vectordb/milvus/milvus.py +242 -32
- agno/vectordb/mongodb/mongodb.py +200 -24
- agno/vectordb/pgvector/pgvector.py +319 -37
- agno/vectordb/pineconedb/pineconedb.py +221 -27
- agno/vectordb/qdrant/qdrant.py +334 -14
- agno/vectordb/singlestore/singlestore.py +286 -29
- agno/vectordb/surrealdb/surrealdb.py +187 -7
- agno/vectordb/upstashdb/upstashdb.py +342 -26
- agno/vectordb/weaviate/weaviate.py +227 -165
- agno/workflow/__init__.py +17 -13
- agno/workflow/{v2/condition.py → condition.py} +135 -32
- agno/workflow/{v2/loop.py → loop.py} +115 -28
- agno/workflow/{v2/parallel.py → parallel.py} +138 -108
- agno/workflow/{v2/router.py → router.py} +133 -32
- agno/workflow/{v2/step.py → step.py} +207 -49
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2410 -696
- agno-2.0.0.dist-info/METADATA +494 -0
- agno-2.0.0.dist-info/RECORD +515 -0
- agno-2.0.0.dist-info/licenses/LICENSE +201 -0
- agno/agent/metrics.py +0 -107
- agno/api/app.py +0 -35
- agno/api/playground.py +0 -92
- agno/api/schemas/app.py +0 -12
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -35
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workflows.py +0 -33
- agno/api/workspace.py +0 -175
- agno/app/agui/__init__.py +0 -3
- agno/app/agui/app.py +0 -17
- agno/app/agui/sync_router.py +0 -120
- agno/app/base.py +0 -186
- agno/app/discord/__init__.py +0 -3
- agno/app/fastapi/__init__.py +0 -3
- agno/app/fastapi/app.py +0 -107
- agno/app/fastapi/async_router.py +0 -457
- agno/app/fastapi/sync_router.py +0 -448
- agno/app/playground/app.py +0 -228
- agno/app/playground/async_router.py +0 -1050
- agno/app/playground/deploy.py +0 -249
- agno/app/playground/operator.py +0 -183
- agno/app/playground/schemas.py +0 -220
- agno/app/playground/serve.py +0 -55
- agno/app/playground/sync_router.py +0 -1042
- agno/app/playground/utils.py +0 -46
- agno/app/settings.py +0 -15
- agno/app/slack/__init__.py +0 -3
- agno/app/slack/app.py +0 -19
- agno/app/slack/sync_router.py +0 -92
- agno/app/utils.py +0 -54
- agno/app/whatsapp/__init__.py +0 -3
- agno/app/whatsapp/app.py +0 -15
- agno/app/whatsapp/sync_router.py +0 -197
- agno/cli/auth_server.py +0 -249
- agno/cli/config.py +0 -274
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -357
- agno/cli/settings.py +0 -96
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -5
- agno/document/chunking/semantic.py +0 -45
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -5
- agno/document/reader/base.py +0 -47
- agno/document/reader/docx_reader.py +0 -60
- agno/document/reader/gcs/pdf_reader.py +0 -44
- agno/document/reader/s3/pdf_reader.py +0 -59
- agno/document/reader/s3/text_reader.py +0 -63
- agno/document/reader/url_reader.py +0 -59
- agno/document/reader/youtube_reader.py +0 -58
- agno/embedder/__init__.py +0 -5
- agno/embedder/langdb.py +0 -80
- agno/embedder/mistral.py +0 -82
- agno/embedder/openai.py +0 -78
- agno/file/__init__.py +0 -5
- 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 -702
- agno/knowledge/arxiv.py +0 -33
- agno/knowledge/combined.py +0 -36
- agno/knowledge/csv.py +0 -144
- agno/knowledge/csv_url.py +0 -124
- agno/knowledge/document.py +0 -223
- agno/knowledge/docx.py +0 -137
- agno/knowledge/firecrawl.py +0 -34
- agno/knowledge/gcs/__init__.py +0 -0
- agno/knowledge/gcs/base.py +0 -39
- agno/knowledge/gcs/pdf.py +0 -125
- agno/knowledge/json.py +0 -137
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/light_rag.py +0 -273
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/markdown.py +0 -154
- agno/knowledge/pdf.py +0 -164
- agno/knowledge/pdf_bytes.py +0 -42
- agno/knowledge/pdf_url.py +0 -148
- agno/knowledge/s3/__init__.py +0 -0
- agno/knowledge/s3/base.py +0 -64
- agno/knowledge/s3/pdf.py +0 -33
- agno/knowledge/s3/text.py +0 -34
- agno/knowledge/text.py +0 -141
- agno/knowledge/url.py +0 -46
- agno/knowledge/website.py +0 -179
- agno/knowledge/wikipedia.py +0 -32
- agno/knowledge/youtube.py +0 -35
- agno/memory/agent.py +0 -423
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -5
- 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 -22
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -201
- agno/memory/summary.py +0 -19
- agno/memory/team.py +0 -415
- agno/memory/v2/__init__.py +0 -2
- agno/memory/v2/db/__init__.py +0 -1
- agno/memory/v2/db/base.py +0 -42
- agno/memory/v2/db/firestore.py +0 -339
- agno/memory/v2/db/mongodb.py +0 -196
- agno/memory/v2/db/postgres.py +0 -214
- agno/memory/v2/db/redis.py +0 -187
- agno/memory/v2/db/schema.py +0 -54
- agno/memory/v2/db/sqlite.py +0 -209
- agno/memory/v2/manager.py +0 -437
- agno/memory/v2/memory.py +0 -1097
- agno/memory/v2/schema.py +0 -55
- agno/memory/v2/summarizer.py +0 -215
- agno/memory/workflow.py +0 -38
- agno/models/ollama/tools.py +0 -430
- agno/models/qwen/__init__.py +0 -5
- agno/playground/__init__.py +0 -10
- agno/playground/deploy.py +0 -3
- agno/playground/playground.py +0 -3
- agno/playground/serve.py +0 -3
- agno/playground/settings.py +0 -3
- agno/reranker/__init__.py +0 -0
- agno/run/response.py +0 -467
- agno/run/v2/__init__.py +0 -0
- agno/run/v2/workflow.py +0 -567
- agno/storage/__init__.py +0 -0
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/dynamodb.py +0 -1
- agno/storage/agent/json.py +0 -1
- agno/storage/agent/mongodb.py +0 -1
- agno/storage/agent/postgres.py +0 -1
- agno/storage/agent/singlestore.py +0 -1
- agno/storage/agent/sqlite.py +0 -1
- agno/storage/agent/yaml.py +0 -1
- agno/storage/base.py +0 -60
- agno/storage/dynamodb.py +0 -673
- agno/storage/firestore.py +0 -297
- agno/storage/gcs_json.py +0 -261
- agno/storage/in_memory.py +0 -234
- agno/storage/json.py +0 -237
- agno/storage/mongodb.py +0 -328
- agno/storage/mysql.py +0 -685
- agno/storage/postgres.py +0 -682
- agno/storage/redis.py +0 -336
- agno/storage/session/__init__.py +0 -16
- agno/storage/session/agent.py +0 -64
- agno/storage/session/team.py +0 -63
- agno/storage/session/v2/__init__.py +0 -5
- agno/storage/session/workflow.py +0 -61
- agno/storage/singlestore.py +0 -606
- agno/storage/sqlite.py +0 -646
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/mongodb.py +0 -1
- agno/storage/workflow/postgres.py +0 -1
- agno/storage/workflow/sqlite.py +0 -1
- agno/storage/yaml.py +0 -241
- agno/tools/thinking.py +0 -73
- 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/workflow/v2/__init__.py +0 -21
- agno/workflow/v2/types.py +0 -357
- agno/workflow/v2/workflow.py +0 -3312
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -52
- agno/workspace/operator.py +0 -757
- agno/workspace/settings.py +0 -158
- agno-1.8.1.dist-info/METADATA +0 -982
- agno-1.8.1.dist-info/RECORD +0 -566
- agno-1.8.1.dist-info/entry_points.txt +0 -3
- agno-1.8.1.dist-info/licenses/LICENSE +0 -375
- /agno/{app → db/migrations}/__init__.py +0 -0
- /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{cli → integrations}/__init__.py +0 -0
- /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
- /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
- /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
- /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
- /agno/{app → os/interfaces}/slack/security.py +0 -0
- /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
- /agno/{file/local → utils/print_response}/__init__.py +0 -0
- /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
agno/utils/streamlit.py
ADDED
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
import streamlit as st
|
|
5
|
+
|
|
6
|
+
from agno.agent import Agent
|
|
7
|
+
from agno.db.base import SessionType
|
|
8
|
+
from agno.models.anthropic import Claude
|
|
9
|
+
from agno.models.google import Gemini
|
|
10
|
+
from agno.models.openai import OpenAIChat
|
|
11
|
+
from agno.utils.log import logger
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def add_message(role: str, content: str, tool_calls: Optional[List[Dict[str, Any]]] = None) -> None:
|
|
15
|
+
"""Add a message to the session state."""
|
|
16
|
+
if "messages" not in st.session_state:
|
|
17
|
+
st.session_state["messages"] = []
|
|
18
|
+
|
|
19
|
+
message: Dict[str, Any] = {"role": role, "content": content}
|
|
20
|
+
if tool_calls:
|
|
21
|
+
message["tool_calls"] = tool_calls
|
|
22
|
+
|
|
23
|
+
st.session_state["messages"].append(message)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def display_tool_calls(container, tools: List[Any]):
|
|
27
|
+
"""Display tool calls in expandable sections."""
|
|
28
|
+
if not tools:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
with container.container():
|
|
32
|
+
for tool in tools:
|
|
33
|
+
if hasattr(tool, "tool_name"):
|
|
34
|
+
name = tool.tool_name or "Tool"
|
|
35
|
+
args = tool.tool_args or {}
|
|
36
|
+
result = tool.result or ""
|
|
37
|
+
else:
|
|
38
|
+
name = tool.get("tool_name") or tool.get("name") or "Tool"
|
|
39
|
+
args = tool.get("tool_args") or tool.get("args") or {}
|
|
40
|
+
result = tool.get("result") or tool.get("content") or ""
|
|
41
|
+
|
|
42
|
+
with st.expander(f"🛠️ {name.replace('_', ' ')}", expanded=False):
|
|
43
|
+
if args:
|
|
44
|
+
st.markdown("**Arguments:**")
|
|
45
|
+
st.json(args)
|
|
46
|
+
if result:
|
|
47
|
+
st.markdown("**Result:**")
|
|
48
|
+
st.json(result)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def session_selector_widget(agent: Agent, model_id: str, agent_creation_callback: Callable[[str, str], Agent]) -> None:
|
|
52
|
+
"""Session selector widget"""
|
|
53
|
+
if not agent.db:
|
|
54
|
+
st.sidebar.info("💡 Database not configured. Sessions will not be saved.")
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
sessions = agent.db.get_sessions(
|
|
59
|
+
session_type=SessionType.AGENT,
|
|
60
|
+
deserialize=True,
|
|
61
|
+
sort_by="created_at",
|
|
62
|
+
sort_order="desc",
|
|
63
|
+
)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
logger.error(f"Error fetching sessions: {e}")
|
|
66
|
+
st.sidebar.error("Could not load sessions")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
if not sessions:
|
|
70
|
+
st.sidebar.info("🆕 New Chat - Start your conversation!")
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
# Filter session data
|
|
74
|
+
session_options = []
|
|
75
|
+
session_dict = {}
|
|
76
|
+
|
|
77
|
+
for session in sessions:
|
|
78
|
+
if not hasattr(session, "session_id") or not session.session_id:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
session_id = session.session_id
|
|
82
|
+
session_name = None
|
|
83
|
+
|
|
84
|
+
# Extract session name from session_data
|
|
85
|
+
if hasattr(session, "session_data") and session.session_data:
|
|
86
|
+
session_name = session.session_data.get("session_name")
|
|
87
|
+
|
|
88
|
+
name = session_name or session_id
|
|
89
|
+
session_options.append(name)
|
|
90
|
+
session_dict[name] = session_id
|
|
91
|
+
|
|
92
|
+
current_session_id = st.session_state.get("session_id")
|
|
93
|
+
current_selection = None
|
|
94
|
+
|
|
95
|
+
if current_session_id and current_session_id not in [s_id for s_id in session_dict.values()]:
|
|
96
|
+
logger.info(f"New session: {current_session_id}")
|
|
97
|
+
if agent.get_session_name():
|
|
98
|
+
current_display_name = agent.get_session_name()
|
|
99
|
+
else:
|
|
100
|
+
current_display_name = f"{current_session_id[:8]}..."
|
|
101
|
+
session_options.insert(0, current_display_name)
|
|
102
|
+
session_dict[current_display_name] = current_session_id
|
|
103
|
+
current_selection = current_display_name
|
|
104
|
+
st.session_state["is_new_session"] = True
|
|
105
|
+
|
|
106
|
+
for display_name, session_id in session_dict.items():
|
|
107
|
+
if session_id == current_session_id:
|
|
108
|
+
current_selection = display_name
|
|
109
|
+
break
|
|
110
|
+
|
|
111
|
+
display_options = session_options
|
|
112
|
+
selected_index = (
|
|
113
|
+
session_options.index(current_selection)
|
|
114
|
+
if current_selection and current_selection in session_options
|
|
115
|
+
else 0
|
|
116
|
+
if session_options
|
|
117
|
+
else None
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if not display_options:
|
|
121
|
+
st.sidebar.info("🆕 Start your first conversation!")
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
selected = st.sidebar.selectbox(
|
|
125
|
+
label="Session",
|
|
126
|
+
options=display_options,
|
|
127
|
+
index=selected_index,
|
|
128
|
+
help="Select a session to continue",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if selected and selected in session_dict:
|
|
132
|
+
selected_session_id = session_dict[selected]
|
|
133
|
+
if selected_session_id != current_session_id:
|
|
134
|
+
if not st.session_state.get("is_new_session", False):
|
|
135
|
+
st.session_state["is_loading_session"] = True
|
|
136
|
+
try:
|
|
137
|
+
_load_session(selected_session_id, model_id, agent_creation_callback)
|
|
138
|
+
finally:
|
|
139
|
+
# Always clear the loading flag, even if there's an error
|
|
140
|
+
st.session_state["is_loading_session"] = False
|
|
141
|
+
else:
|
|
142
|
+
# Clear the new session flag since we're done with initialization
|
|
143
|
+
st.session_state["is_new_session"] = False
|
|
144
|
+
|
|
145
|
+
# Rename session
|
|
146
|
+
if agent.session_id:
|
|
147
|
+
if "session_edit_mode" not in st.session_state:
|
|
148
|
+
st.session_state.session_edit_mode = False
|
|
149
|
+
|
|
150
|
+
current_name = agent.get_session_name() or agent.session_id
|
|
151
|
+
|
|
152
|
+
if not st.session_state.session_edit_mode:
|
|
153
|
+
col1, col2 = st.sidebar.columns([3, 1])
|
|
154
|
+
with col1:
|
|
155
|
+
st.write(f"**Session:** {current_name}")
|
|
156
|
+
with col2:
|
|
157
|
+
if st.button("✎", help="Rename session", key="rename_session_button"):
|
|
158
|
+
st.session_state.session_edit_mode = True
|
|
159
|
+
st.rerun()
|
|
160
|
+
else:
|
|
161
|
+
new_name = st.sidebar.text_input("Enter new name:", value=current_name, key="session_name_input")
|
|
162
|
+
|
|
163
|
+
col1, col2 = st.sidebar.columns([1, 1])
|
|
164
|
+
with col1:
|
|
165
|
+
if st.button(
|
|
166
|
+
"💾 Save",
|
|
167
|
+
type="primary",
|
|
168
|
+
use_container_width=True,
|
|
169
|
+
key="save_session_name",
|
|
170
|
+
):
|
|
171
|
+
if new_name and new_name.strip():
|
|
172
|
+
try:
|
|
173
|
+
result = agent.set_session_name(session_name=new_name.strip())
|
|
174
|
+
|
|
175
|
+
if result:
|
|
176
|
+
logger.info(f"Session renamed to: {new_name.strip()}")
|
|
177
|
+
# Clear any cached session data to ensure fresh reload
|
|
178
|
+
if hasattr(agent, "_agent_session") and agent._agent_session:
|
|
179
|
+
agent._agent_session = None
|
|
180
|
+
st.session_state.session_edit_mode = False
|
|
181
|
+
st.sidebar.success("Session renamed!")
|
|
182
|
+
st.rerun()
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error(f"Error renaming session: {e}")
|
|
185
|
+
st.sidebar.error(f"Error: {str(e)}")
|
|
186
|
+
else:
|
|
187
|
+
st.sidebar.error("Please enter a valid name")
|
|
188
|
+
|
|
189
|
+
with col2:
|
|
190
|
+
if st.button("❌ Cancel", use_container_width=True, key="cancel_session_rename"):
|
|
191
|
+
st.session_state.session_edit_mode = False
|
|
192
|
+
st.rerun()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _load_session(session_id: str, model_id: str, agent_creation_callback: Callable[[str, str], Agent]):
|
|
196
|
+
try:
|
|
197
|
+
logger.info(f"Creating agent with session_id: {session_id}")
|
|
198
|
+
new_agent = agent_creation_callback(model_id, session_id)
|
|
199
|
+
|
|
200
|
+
st.session_state["agent"] = new_agent
|
|
201
|
+
st.session_state["session_id"] = session_id
|
|
202
|
+
st.session_state["messages"] = []
|
|
203
|
+
st.session_state["current_model"] = model_id # Keep current_model in sync
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
if new_agent.db:
|
|
207
|
+
selected_session = new_agent.db.get_session(
|
|
208
|
+
session_id=session_id, session_type=SessionType.AGENT, deserialize=True
|
|
209
|
+
)
|
|
210
|
+
else:
|
|
211
|
+
selected_session = None
|
|
212
|
+
|
|
213
|
+
# Recreate the chat history
|
|
214
|
+
if selected_session:
|
|
215
|
+
if hasattr(selected_session, "runs") and selected_session.runs:
|
|
216
|
+
for run_idx, run in enumerate(selected_session.runs):
|
|
217
|
+
messages = getattr(run, "messages", None)
|
|
218
|
+
|
|
219
|
+
if messages:
|
|
220
|
+
user_msg = None
|
|
221
|
+
assistant_msg = None
|
|
222
|
+
tool_calls = []
|
|
223
|
+
|
|
224
|
+
for msg_idx, message in enumerate(messages):
|
|
225
|
+
if not hasattr(message, "role") or not hasattr(message, "content"):
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
role = message.role
|
|
229
|
+
content = str(message.content) if message.content else ""
|
|
230
|
+
|
|
231
|
+
if role == "user":
|
|
232
|
+
if content and content.strip():
|
|
233
|
+
user_msg = content.strip()
|
|
234
|
+
elif role == "assistant":
|
|
235
|
+
if content and content.strip() and content.strip().lower() != "none":
|
|
236
|
+
assistant_msg = content
|
|
237
|
+
|
|
238
|
+
# Display tool calls for this run
|
|
239
|
+
if hasattr(run, "tools") and run.tools:
|
|
240
|
+
tool_calls = run.tools
|
|
241
|
+
|
|
242
|
+
# Add messages to chat history
|
|
243
|
+
if user_msg:
|
|
244
|
+
add_message("user", user_msg)
|
|
245
|
+
if assistant_msg:
|
|
246
|
+
add_message("assistant", assistant_msg, tool_calls)
|
|
247
|
+
|
|
248
|
+
else:
|
|
249
|
+
logger.warning(f"No session found in database for session_id: {session_id}")
|
|
250
|
+
|
|
251
|
+
except Exception as e:
|
|
252
|
+
logger.warning(f"Could not load chat history: {e}")
|
|
253
|
+
|
|
254
|
+
st.rerun()
|
|
255
|
+
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.error(f"Error loading session: {e}")
|
|
258
|
+
st.sidebar.error(f"Error loading session: {str(e)}")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def display_response(agent: Agent, question: str) -> None:
|
|
262
|
+
"""Handle agent response with streaming and tool call display."""
|
|
263
|
+
with st.chat_message("assistant"):
|
|
264
|
+
tool_calls_container = st.empty()
|
|
265
|
+
resp_container = st.empty()
|
|
266
|
+
with st.spinner("🤔 Thinking..."):
|
|
267
|
+
response = ""
|
|
268
|
+
try:
|
|
269
|
+
# Run the agent and stream the response
|
|
270
|
+
run_response = agent.run(question, stream=True)
|
|
271
|
+
for resp_chunk in run_response:
|
|
272
|
+
try:
|
|
273
|
+
# Display tool calls if available
|
|
274
|
+
if hasattr(resp_chunk, "tool") and resp_chunk.tool:
|
|
275
|
+
display_tool_calls(tool_calls_container, [resp_chunk.tool])
|
|
276
|
+
except Exception as tool_error:
|
|
277
|
+
logger.warning(f"Error displaying tool calls: {tool_error}")
|
|
278
|
+
|
|
279
|
+
if resp_chunk.content is not None:
|
|
280
|
+
content = str(resp_chunk.content)
|
|
281
|
+
|
|
282
|
+
if not (
|
|
283
|
+
content.strip().endswith("completed in") or "completed in" in content and "s." in content
|
|
284
|
+
):
|
|
285
|
+
response += content
|
|
286
|
+
resp_container.markdown(response)
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
if hasattr(agent, "run_response") and agent.run_response and hasattr(agent.run_response, "tools"):
|
|
290
|
+
add_message("assistant", response, agent.run_response.tools)
|
|
291
|
+
else:
|
|
292
|
+
add_message("assistant", response)
|
|
293
|
+
except Exception as add_msg_error:
|
|
294
|
+
logger.warning(f"Error adding message with tools: {add_msg_error}")
|
|
295
|
+
add_message("assistant", response)
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
st.error(f"Sorry, I encountered an error: {str(e)}")
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def display_chat_messages() -> None:
|
|
302
|
+
"""Display all chat messages from session state."""
|
|
303
|
+
if "messages" not in st.session_state:
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
for message in st.session_state["messages"]:
|
|
307
|
+
if message["role"] in ["user", "assistant"]:
|
|
308
|
+
content = message["content"]
|
|
309
|
+
with st.chat_message(message["role"]):
|
|
310
|
+
# Display tool calls
|
|
311
|
+
if "tool_calls" in message and message["tool_calls"]:
|
|
312
|
+
display_tool_calls(st.container(), message["tool_calls"])
|
|
313
|
+
|
|
314
|
+
if content is not None and str(content).strip() and str(content).strip().lower() != "none":
|
|
315
|
+
st.markdown(content)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def initialize_agent(model_id: str, agent_creation_callback: Callable[[str, Optional[str]], Agent]) -> Agent:
|
|
319
|
+
"""Initialize or get agent with proper session management."""
|
|
320
|
+
if "agent" not in st.session_state or st.session_state["agent"] is None:
|
|
321
|
+
# First time initialization - get existing session_id if any
|
|
322
|
+
session_id = st.session_state.get("session_id")
|
|
323
|
+
agent = agent_creation_callback(model_id, session_id)
|
|
324
|
+
st.session_state["agent"] = agent
|
|
325
|
+
st.session_state["current_model"] = model_id
|
|
326
|
+
|
|
327
|
+
return agent
|
|
328
|
+
else:
|
|
329
|
+
return st.session_state["agent"]
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def reset_session_state(agent: Agent) -> None:
|
|
333
|
+
"""Update session state."""
|
|
334
|
+
print(f"Resetting session state for agent: {agent.session_id}")
|
|
335
|
+
if agent.session_id is not None:
|
|
336
|
+
st.session_state["session_id"] = agent.session_id
|
|
337
|
+
|
|
338
|
+
if "messages" not in st.session_state:
|
|
339
|
+
st.session_state["messages"] = []
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def knowledge_base_info_widget(agent: Agent) -> None:
|
|
343
|
+
"""Display knowledge base information widget."""
|
|
344
|
+
if not agent.knowledge:
|
|
345
|
+
st.sidebar.info("No knowledge base configured")
|
|
346
|
+
return
|
|
347
|
+
|
|
348
|
+
vector_db = getattr(agent.knowledge, "vector_db", None)
|
|
349
|
+
if not vector_db:
|
|
350
|
+
st.sidebar.info("No vector db configured")
|
|
351
|
+
return
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
doc_count = vector_db.get_count()
|
|
355
|
+
if doc_count == 0:
|
|
356
|
+
st.sidebar.info("💡 Upload documents to populate the knowledge base")
|
|
357
|
+
else:
|
|
358
|
+
st.sidebar.metric("Documents Loaded", doc_count)
|
|
359
|
+
except Exception as e:
|
|
360
|
+
logger.error(f"Error getting knowledge base info: {e}")
|
|
361
|
+
st.sidebar.warning("Could not retrieve knowledge base information")
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def export_chat_history(app_name: str = "Chat") -> str:
|
|
365
|
+
"""Export chat history to markdown."""
|
|
366
|
+
if "messages" not in st.session_state or not st.session_state["messages"]:
|
|
367
|
+
return "# Chat History\n\n*No messages to export*"
|
|
368
|
+
|
|
369
|
+
title = f"{app_name} Chat History"
|
|
370
|
+
for msg in st.session_state["messages"]:
|
|
371
|
+
if msg.get("role") == "user" and msg.get("content"):
|
|
372
|
+
title = msg["content"][:100]
|
|
373
|
+
if len(msg["content"]) > 100:
|
|
374
|
+
title += "..."
|
|
375
|
+
break
|
|
376
|
+
|
|
377
|
+
chat_text = f"# {title}\n\n"
|
|
378
|
+
chat_text += f"**Exported:** {datetime.now().strftime('%B %d, %Y at %I:%M %p')}\n\n"
|
|
379
|
+
chat_text += "---\n\n"
|
|
380
|
+
|
|
381
|
+
for msg in st.session_state["messages"]:
|
|
382
|
+
role = msg.get("role", "")
|
|
383
|
+
content = msg.get("content", "")
|
|
384
|
+
|
|
385
|
+
if not content or str(content).strip().lower() == "none":
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
role_display = "## 🙋 User" if role == "user" else "## 🤖 Assistant"
|
|
389
|
+
chat_text += f"{role_display}\n\n{content}\n\n---\n\n"
|
|
390
|
+
return chat_text
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def get_model_from_id(model_id: str):
|
|
394
|
+
"""Get a model instance from a model ID string."""
|
|
395
|
+
if model_id.startswith("openai:"):
|
|
396
|
+
return OpenAIChat(id=model_id.split("openai:")[1])
|
|
397
|
+
elif model_id.startswith("anthropic:"):
|
|
398
|
+
return Claude(id=model_id.split("anthropic:")[1])
|
|
399
|
+
elif model_id.startswith("google:"):
|
|
400
|
+
return Gemini(id=model_id.split("google:")[1])
|
|
401
|
+
else:
|
|
402
|
+
return OpenAIChat(id="gpt-4o")
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def get_model_with_provider(model_name: str):
|
|
406
|
+
"""Get a model instance by inferring the correct provider from the model name.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
model_name: Model name (e.g., "gpt-4o", "claude-4-sonnet", "gemini-2.5-pro")
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
Model instance with correct provider
|
|
413
|
+
"""
|
|
414
|
+
if ":" in model_name:
|
|
415
|
+
return get_model_from_id(model_name)
|
|
416
|
+
|
|
417
|
+
model_lower = model_name.lower()
|
|
418
|
+
|
|
419
|
+
if any(pattern in model_lower for pattern in ["gpt", "o1", "o3"]):
|
|
420
|
+
return get_model_from_id(f"openai:{model_name}")
|
|
421
|
+
|
|
422
|
+
elif "claude" in model_lower:
|
|
423
|
+
return get_model_from_id(f"anthropic:{model_name}")
|
|
424
|
+
|
|
425
|
+
elif "gemini" in model_lower:
|
|
426
|
+
return get_model_from_id(f"google:{model_name}")
|
|
427
|
+
|
|
428
|
+
else:
|
|
429
|
+
return get_model_from_id(f"openai:{model_name}")
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def about_section(description: str):
|
|
433
|
+
"""About section"""
|
|
434
|
+
st.sidebar.markdown("---")
|
|
435
|
+
st.sidebar.markdown("### ℹ️ About")
|
|
436
|
+
st.sidebar.markdown(f"""
|
|
437
|
+
{description}
|
|
438
|
+
|
|
439
|
+
Built with:
|
|
440
|
+
- 🚀 Agno
|
|
441
|
+
- 💫 Streamlit
|
|
442
|
+
""")
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
MODELS = [
|
|
446
|
+
"gpt-4o",
|
|
447
|
+
"o3-mini",
|
|
448
|
+
"gpt-5",
|
|
449
|
+
"claude-4-sonnet",
|
|
450
|
+
"gemini-2.5-pro",
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
COMMON_CSS = """
|
|
455
|
+
<style>
|
|
456
|
+
.main-title {
|
|
457
|
+
text-align: center;
|
|
458
|
+
background: linear-gradient(45deg, #FF4B2B, #FF416C);
|
|
459
|
+
-webkit-background-clip: text;
|
|
460
|
+
-webkit-text-fill-color: transparent;
|
|
461
|
+
font-size: 3em;
|
|
462
|
+
font-weight: bold;
|
|
463
|
+
padding: 1em 0;
|
|
464
|
+
}
|
|
465
|
+
.subtitle {
|
|
466
|
+
text-align: center;
|
|
467
|
+
color: #666;
|
|
468
|
+
margin-bottom: 2em;
|
|
469
|
+
}
|
|
470
|
+
.stButton button {
|
|
471
|
+
width: 100%;
|
|
472
|
+
border-radius: 20px;
|
|
473
|
+
margin: 0.2em 0;
|
|
474
|
+
transition: all 0.3s ease;
|
|
475
|
+
}
|
|
476
|
+
.stButton button:hover {
|
|
477
|
+
transform: translateY(-2px);
|
|
478
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
|
479
|
+
}
|
|
480
|
+
</style>
|
|
481
|
+
"""
|
agno/utils/string.py
CHANGED
|
@@ -27,20 +27,6 @@ def is_valid_uuid(uuid_str: str) -> bool:
|
|
|
27
27
|
return False
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def safe_content_hash(content: str) -> str:
|
|
31
|
-
"""
|
|
32
|
-
Return an MD5 hash of the input string, replacing null bytes and invalid surrogates for safe hashing.
|
|
33
|
-
"""
|
|
34
|
-
cleaned_content = content.replace("\x00", "\ufffd")
|
|
35
|
-
try:
|
|
36
|
-
content_hash = hashlib.md5(cleaned_content.encode("utf-8")).hexdigest()
|
|
37
|
-
except UnicodeEncodeError:
|
|
38
|
-
cleaned_content = "".join("\ufffd" if "\ud800" <= c <= "\udfff" else c for c in cleaned_content)
|
|
39
|
-
content_hash = hashlib.md5(cleaned_content.encode("utf-8")).hexdigest()
|
|
40
|
-
|
|
41
|
-
return content_hash
|
|
42
|
-
|
|
43
|
-
|
|
44
30
|
def url_safe_string(input_string):
|
|
45
31
|
# Replace spaces with dashes
|
|
46
32
|
safe_string = input_string.replace(" ", "-")
|
|
@@ -130,13 +116,13 @@ def _clean_json_content(content: str) -> str:
|
|
|
130
116
|
return content
|
|
131
117
|
|
|
132
118
|
|
|
133
|
-
def _parse_individual_json(content: str,
|
|
119
|
+
def _parse_individual_json(content: str, output_schema: Type[BaseModel]) -> Optional[BaseModel]:
|
|
134
120
|
"""Parse individual JSON objects from content and merge them based on response model fields."""
|
|
135
121
|
candidate_jsons = _extract_json_objects(content)
|
|
136
122
|
merged_data: dict = {}
|
|
137
123
|
|
|
138
124
|
# Get the expected fields from the response model
|
|
139
|
-
model_fields =
|
|
125
|
+
model_fields = output_schema.model_fields if hasattr(output_schema, "model_fields") else {}
|
|
140
126
|
|
|
141
127
|
for candidate in candidate_jsons:
|
|
142
128
|
try:
|
|
@@ -161,13 +147,13 @@ def _parse_individual_json(content: str, response_model: Type[BaseModel]) -> Opt
|
|
|
161
147
|
return None
|
|
162
148
|
|
|
163
149
|
try:
|
|
164
|
-
return
|
|
150
|
+
return output_schema.model_validate(merged_data)
|
|
165
151
|
except ValidationError as e:
|
|
166
152
|
logger.warning("Validation failed on merged data: %s", e)
|
|
167
153
|
return None
|
|
168
154
|
|
|
169
155
|
|
|
170
|
-
def parse_response_model_str(content: str,
|
|
156
|
+
def parse_response_model_str(content: str, output_schema: Type[BaseModel]) -> Optional[BaseModel]:
|
|
171
157
|
structured_output = None
|
|
172
158
|
|
|
173
159
|
# Clean content first to simplify all parsing attempts
|
|
@@ -175,12 +161,12 @@ def parse_response_model_str(content: str, response_model: Type[BaseModel]) -> O
|
|
|
175
161
|
|
|
176
162
|
try:
|
|
177
163
|
# First attempt: direct JSON validation on cleaned content
|
|
178
|
-
structured_output =
|
|
164
|
+
structured_output = output_schema.model_validate_json(cleaned_content)
|
|
179
165
|
except (ValidationError, json.JSONDecodeError):
|
|
180
166
|
try:
|
|
181
167
|
# Second attempt: Parse as Python dict
|
|
182
168
|
data = json.loads(cleaned_content)
|
|
183
|
-
structured_output =
|
|
169
|
+
structured_output = output_schema.model_validate(data)
|
|
184
170
|
except (ValidationError, json.JSONDecodeError) as e:
|
|
185
171
|
logger.warning(f"Failed to parse cleaned JSON: {e}")
|
|
186
172
|
|
|
@@ -191,13 +177,13 @@ def parse_response_model_str(content: str, response_model: Type[BaseModel]) -> O
|
|
|
191
177
|
# Single JSON object - try to parse it directly
|
|
192
178
|
try:
|
|
193
179
|
data = json.loads(candidate_jsons[0])
|
|
194
|
-
structured_output =
|
|
180
|
+
structured_output = output_schema.model_validate(data)
|
|
195
181
|
except (ValidationError, json.JSONDecodeError):
|
|
196
182
|
pass
|
|
197
183
|
|
|
198
184
|
if structured_output is None:
|
|
199
185
|
# Final attempt: Handle concatenated JSON objects with field merging
|
|
200
|
-
structured_output = _parse_individual_json(cleaned_content,
|
|
186
|
+
structured_output = _parse_individual_json(cleaned_content, output_schema)
|
|
201
187
|
if structured_output is None:
|
|
202
188
|
logger.warning("All parsing attempts failed.")
|
|
203
189
|
|
agno/utils/team.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
2
|
+
|
|
3
|
+
from agno.agent import Agent
|
|
4
|
+
from agno.utils.string import is_valid_uuid, url_safe_string
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from agno.team.team import Team
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def format_member_agent_task(
|
|
11
|
+
task_description: str,
|
|
12
|
+
expected_output: Optional[str] = None,
|
|
13
|
+
team_member_interactions_str: Optional[str] = None,
|
|
14
|
+
) -> str:
|
|
15
|
+
member_agent_task = "You are a member of a team of agents. Your goal is to complete the following task:"
|
|
16
|
+
member_agent_task += f"\n\n<task>\n{task_description}\n</task>"
|
|
17
|
+
|
|
18
|
+
if expected_output is not None:
|
|
19
|
+
member_agent_task += f"\n\n<expected_output>\n{expected_output}\n</expected_output>"
|
|
20
|
+
|
|
21
|
+
if team_member_interactions_str:
|
|
22
|
+
member_agent_task += f"\n\n{team_member_interactions_str}"
|
|
23
|
+
|
|
24
|
+
return member_agent_task
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_member_id(member: Union[Agent, "Team"]) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Get the ID of a member
|
|
30
|
+
|
|
31
|
+
If the member has an agent_id or team_id, use that if it is not a valid UUID.
|
|
32
|
+
Then if the member has a name, convert that to a URL safe string.
|
|
33
|
+
Then if the member has the default UUID ID, use that.
|
|
34
|
+
Otherwise, return None.
|
|
35
|
+
"""
|
|
36
|
+
from agno.team.team import Team
|
|
37
|
+
|
|
38
|
+
if isinstance(member, Agent) and member.id is not None and (not is_valid_uuid(member.id)):
|
|
39
|
+
url_safe_member_id = url_safe_string(member.id)
|
|
40
|
+
elif isinstance(member, Team) and member.id is not None and (not is_valid_uuid(member.id)):
|
|
41
|
+
url_safe_member_id = url_safe_string(member.id)
|
|
42
|
+
elif member.name is not None:
|
|
43
|
+
url_safe_member_id = url_safe_string(member.name)
|
|
44
|
+
elif isinstance(member, Agent) and member.id is not None:
|
|
45
|
+
url_safe_member_id = member.id
|
|
46
|
+
elif isinstance(member, Team) and member.id is not None:
|
|
47
|
+
url_safe_member_id = member.id
|
|
48
|
+
else:
|
|
49
|
+
url_safe_member_id = None
|
|
50
|
+
return url_safe_member_id
|
agno/utils/timer.py
CHANGED
|
@@ -24,11 +24,11 @@ class Timer:
|
|
|
24
24
|
self.elapsed_time = self.end_time - self.start_time
|
|
25
25
|
return self.end_time
|
|
26
26
|
|
|
27
|
-
def __enter__(self):
|
|
27
|
+
def __enter__(self) -> "Timer":
|
|
28
28
|
self.start_time = perf_counter()
|
|
29
29
|
return self
|
|
30
30
|
|
|
31
|
-
def __exit__(self, *args):
|
|
31
|
+
def __exit__(self, *args) -> None:
|
|
32
32
|
self.end_time = perf_counter()
|
|
33
33
|
if self.start_time is not None:
|
|
34
34
|
self.elapsed_time = self.end_time - self.start_time
|