vanna 0.7.9__py3-none-any.whl → 2.0.0rc1__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.
- vanna/__init__.py +167 -395
- vanna/agents/__init__.py +7 -0
- vanna/capabilities/__init__.py +17 -0
- vanna/capabilities/agent_memory/__init__.py +21 -0
- vanna/capabilities/agent_memory/base.py +103 -0
- vanna/capabilities/agent_memory/models.py +53 -0
- vanna/capabilities/file_system/__init__.py +14 -0
- vanna/capabilities/file_system/base.py +71 -0
- vanna/capabilities/file_system/models.py +25 -0
- vanna/capabilities/sql_runner/__init__.py +13 -0
- vanna/capabilities/sql_runner/base.py +37 -0
- vanna/capabilities/sql_runner/models.py +13 -0
- vanna/components/__init__.py +92 -0
- vanna/components/base.py +11 -0
- vanna/components/rich/__init__.py +83 -0
- vanna/components/rich/containers/__init__.py +7 -0
- vanna/components/rich/containers/card.py +20 -0
- vanna/components/rich/data/__init__.py +9 -0
- vanna/components/rich/data/chart.py +17 -0
- vanna/components/rich/data/dataframe.py +93 -0
- vanna/components/rich/feedback/__init__.py +21 -0
- vanna/components/rich/feedback/badge.py +16 -0
- vanna/components/rich/feedback/icon_text.py +14 -0
- vanna/components/rich/feedback/log_viewer.py +41 -0
- vanna/components/rich/feedback/notification.py +19 -0
- vanna/components/rich/feedback/progress.py +37 -0
- vanna/components/rich/feedback/status_card.py +28 -0
- vanna/components/rich/feedback/status_indicator.py +14 -0
- vanna/components/rich/interactive/__init__.py +21 -0
- vanna/components/rich/interactive/button.py +95 -0
- vanna/components/rich/interactive/task_list.py +58 -0
- vanna/components/rich/interactive/ui_state.py +93 -0
- vanna/components/rich/specialized/__init__.py +7 -0
- vanna/components/rich/specialized/artifact.py +20 -0
- vanna/components/rich/text.py +16 -0
- vanna/components/simple/__init__.py +15 -0
- vanna/components/simple/image.py +15 -0
- vanna/components/simple/link.py +15 -0
- vanna/components/simple/text.py +11 -0
- vanna/core/__init__.py +193 -0
- vanna/core/_compat.py +19 -0
- vanna/core/agent/__init__.py +10 -0
- vanna/core/agent/agent.py +1407 -0
- vanna/core/agent/config.py +123 -0
- vanna/core/audit/__init__.py +28 -0
- vanna/core/audit/base.py +299 -0
- vanna/core/audit/models.py +131 -0
- vanna/core/component_manager.py +329 -0
- vanna/core/components.py +53 -0
- vanna/core/enhancer/__init__.py +11 -0
- vanna/core/enhancer/base.py +94 -0
- vanna/core/enhancer/default.py +118 -0
- vanna/core/enricher/__init__.py +10 -0
- vanna/core/enricher/base.py +59 -0
- vanna/core/errors.py +47 -0
- vanna/core/evaluation/__init__.py +81 -0
- vanna/core/evaluation/base.py +186 -0
- vanna/core/evaluation/dataset.py +254 -0
- vanna/core/evaluation/evaluators.py +376 -0
- vanna/core/evaluation/report.py +289 -0
- vanna/core/evaluation/runner.py +313 -0
- vanna/core/filter/__init__.py +10 -0
- vanna/core/filter/base.py +67 -0
- vanna/core/lifecycle/__init__.py +10 -0
- vanna/core/lifecycle/base.py +83 -0
- vanna/core/llm/__init__.py +16 -0
- vanna/core/llm/base.py +40 -0
- vanna/core/llm/models.py +61 -0
- vanna/core/middleware/__init__.py +10 -0
- vanna/core/middleware/base.py +69 -0
- vanna/core/observability/__init__.py +11 -0
- vanna/core/observability/base.py +88 -0
- vanna/core/observability/models.py +47 -0
- vanna/core/recovery/__init__.py +11 -0
- vanna/core/recovery/base.py +84 -0
- vanna/core/recovery/models.py +32 -0
- vanna/core/registry.py +278 -0
- vanna/core/rich_component.py +156 -0
- vanna/core/simple_component.py +27 -0
- vanna/core/storage/__init__.py +14 -0
- vanna/core/storage/base.py +46 -0
- vanna/core/storage/models.py +46 -0
- vanna/core/system_prompt/__init__.py +13 -0
- vanna/core/system_prompt/base.py +36 -0
- vanna/core/system_prompt/default.py +157 -0
- vanna/core/tool/__init__.py +18 -0
- vanna/core/tool/base.py +70 -0
- vanna/core/tool/models.py +84 -0
- vanna/core/user/__init__.py +17 -0
- vanna/core/user/base.py +29 -0
- vanna/core/user/models.py +25 -0
- vanna/core/user/request_context.py +70 -0
- vanna/core/user/resolver.py +42 -0
- vanna/core/validation.py +164 -0
- vanna/core/workflow/__init__.py +12 -0
- vanna/core/workflow/base.py +254 -0
- vanna/core/workflow/default.py +789 -0
- vanna/examples/__init__.py +1 -0
- vanna/examples/__main__.py +44 -0
- vanna/examples/anthropic_quickstart.py +80 -0
- vanna/examples/artifact_example.py +293 -0
- vanna/examples/claude_sqlite_example.py +236 -0
- vanna/examples/coding_agent_example.py +300 -0
- vanna/examples/custom_system_prompt_example.py +174 -0
- vanna/examples/default_workflow_handler_example.py +208 -0
- vanna/examples/email_auth_example.py +340 -0
- vanna/examples/evaluation_example.py +269 -0
- vanna/examples/extensibility_example.py +262 -0
- vanna/examples/minimal_example.py +67 -0
- vanna/examples/mock_auth_example.py +227 -0
- vanna/examples/mock_custom_tool.py +311 -0
- vanna/examples/mock_quickstart.py +79 -0
- vanna/examples/mock_quota_example.py +145 -0
- vanna/examples/mock_rich_components_demo.py +396 -0
- vanna/examples/mock_sqlite_example.py +223 -0
- vanna/examples/openai_quickstart.py +83 -0
- vanna/examples/primitive_components_demo.py +305 -0
- vanna/examples/quota_lifecycle_example.py +139 -0
- vanna/examples/visualization_example.py +251 -0
- vanna/integrations/__init__.py +17 -0
- vanna/integrations/anthropic/__init__.py +9 -0
- vanna/integrations/anthropic/llm.py +270 -0
- vanna/integrations/azureopenai/__init__.py +9 -0
- vanna/integrations/azureopenai/llm.py +329 -0
- vanna/integrations/azuresearch/__init__.py +7 -0
- vanna/integrations/azuresearch/agent_memory.py +413 -0
- vanna/integrations/bigquery/__init__.py +5 -0
- vanna/integrations/bigquery/sql_runner.py +81 -0
- vanna/integrations/chromadb/__init__.py +104 -0
- vanna/integrations/chromadb/agent_memory.py +416 -0
- vanna/integrations/clickhouse/__init__.py +5 -0
- vanna/integrations/clickhouse/sql_runner.py +82 -0
- vanna/integrations/duckdb/__init__.py +5 -0
- vanna/integrations/duckdb/sql_runner.py +65 -0
- vanna/integrations/faiss/__init__.py +7 -0
- vanna/integrations/faiss/agent_memory.py +431 -0
- vanna/integrations/google/__init__.py +9 -0
- vanna/integrations/google/gemini.py +370 -0
- vanna/integrations/hive/__init__.py +5 -0
- vanna/integrations/hive/sql_runner.py +87 -0
- vanna/integrations/local/__init__.py +17 -0
- vanna/integrations/local/agent_memory/__init__.py +7 -0
- vanna/integrations/local/agent_memory/in_memory.py +285 -0
- vanna/integrations/local/audit.py +59 -0
- vanna/integrations/local/file_system.py +242 -0
- vanna/integrations/local/file_system_conversation_store.py +255 -0
- vanna/integrations/local/storage.py +62 -0
- vanna/integrations/marqo/__init__.py +7 -0
- vanna/integrations/marqo/agent_memory.py +354 -0
- vanna/integrations/milvus/__init__.py +7 -0
- vanna/integrations/milvus/agent_memory.py +458 -0
- vanna/integrations/mock/__init__.py +9 -0
- vanna/integrations/mock/llm.py +65 -0
- vanna/integrations/mssql/__init__.py +5 -0
- vanna/integrations/mssql/sql_runner.py +66 -0
- vanna/integrations/mysql/__init__.py +5 -0
- vanna/integrations/mysql/sql_runner.py +92 -0
- vanna/integrations/ollama/__init__.py +7 -0
- vanna/integrations/ollama/llm.py +252 -0
- vanna/integrations/openai/__init__.py +10 -0
- vanna/integrations/openai/llm.py +267 -0
- vanna/integrations/openai/responses.py +163 -0
- vanna/integrations/opensearch/__init__.py +7 -0
- vanna/integrations/opensearch/agent_memory.py +411 -0
- vanna/integrations/oracle/__init__.py +5 -0
- vanna/integrations/oracle/sql_runner.py +75 -0
- vanna/integrations/pinecone/__init__.py +7 -0
- vanna/integrations/pinecone/agent_memory.py +329 -0
- vanna/integrations/plotly/__init__.py +5 -0
- vanna/integrations/plotly/chart_generator.py +313 -0
- vanna/integrations/postgres/__init__.py +9 -0
- vanna/integrations/postgres/sql_runner.py +112 -0
- vanna/integrations/premium/agent_memory/__init__.py +7 -0
- vanna/integrations/premium/agent_memory/premium.py +186 -0
- vanna/integrations/presto/__init__.py +5 -0
- vanna/integrations/presto/sql_runner.py +107 -0
- vanna/integrations/qdrant/__init__.py +7 -0
- vanna/integrations/qdrant/agent_memory.py +439 -0
- vanna/integrations/snowflake/__init__.py +5 -0
- vanna/integrations/snowflake/sql_runner.py +147 -0
- vanna/integrations/sqlite/__init__.py +9 -0
- vanna/integrations/sqlite/sql_runner.py +65 -0
- vanna/integrations/weaviate/__init__.py +7 -0
- vanna/integrations/weaviate/agent_memory.py +428 -0
- vanna/{ZhipuAI → legacy/ZhipuAI}/ZhipuAI_embeddings.py +11 -11
- vanna/legacy/__init__.py +403 -0
- vanna/legacy/adapter.py +463 -0
- vanna/{advanced → legacy/advanced}/__init__.py +3 -1
- vanna/{anthropic → legacy/anthropic}/anthropic_chat.py +9 -7
- vanna/{azuresearch → legacy/azuresearch}/azuresearch_vector.py +79 -41
- vanna/{base → legacy/base}/base.py +224 -217
- vanna/legacy/bedrock/__init__.py +1 -0
- vanna/{bedrock → legacy/bedrock}/bedrock_converse.py +13 -12
- vanna/{chromadb → legacy/chromadb}/chromadb_vector.py +3 -1
- vanna/legacy/cohere/__init__.py +2 -0
- vanna/{cohere → legacy/cohere}/cohere_chat.py +19 -14
- vanna/{cohere → legacy/cohere}/cohere_embeddings.py +25 -19
- vanna/{deepseek → legacy/deepseek}/deepseek_chat.py +5 -6
- vanna/legacy/faiss/__init__.py +1 -0
- vanna/{faiss → legacy/faiss}/faiss.py +113 -59
- vanna/{flask → legacy/flask}/__init__.py +84 -43
- vanna/{flask → legacy/flask}/assets.py +5 -5
- vanna/{flask → legacy/flask}/auth.py +5 -4
- vanna/{google → legacy/google}/bigquery_vector.py +75 -42
- vanna/{google → legacy/google}/gemini_chat.py +7 -3
- vanna/{hf → legacy/hf}/hf.py +0 -1
- vanna/{milvus → legacy/milvus}/milvus_vector.py +58 -35
- vanna/{mock → legacy/mock}/llm.py +0 -1
- vanna/legacy/mock/vectordb.py +67 -0
- vanna/legacy/ollama/ollama.py +110 -0
- vanna/{openai → legacy/openai}/openai_chat.py +2 -6
- vanna/legacy/opensearch/opensearch_vector.py +369 -0
- vanna/legacy/opensearch/opensearch_vector_semantic.py +200 -0
- vanna/legacy/oracle/oracle_vector.py +584 -0
- vanna/{pgvector → legacy/pgvector}/pgvector.py +42 -13
- vanna/{qdrant → legacy/qdrant}/qdrant.py +2 -6
- vanna/legacy/qianfan/Qianfan_Chat.py +170 -0
- vanna/legacy/qianfan/Qianfan_embeddings.py +36 -0
- vanna/legacy/qianwen/QianwenAI_chat.py +132 -0
- vanna/{remote.py → legacy/remote.py} +28 -26
- vanna/{utils.py → legacy/utils.py} +6 -11
- vanna/{vannadb → legacy/vannadb}/vannadb_vector.py +115 -46
- vanna/{vllm → legacy/vllm}/vllm.py +5 -6
- vanna/{weaviate → legacy/weaviate}/weaviate_vector.py +59 -40
- vanna/{xinference → legacy/xinference}/xinference.py +6 -6
- vanna/py.typed +0 -0
- vanna/servers/__init__.py +16 -0
- vanna/servers/__main__.py +8 -0
- vanna/servers/base/__init__.py +18 -0
- vanna/servers/base/chat_handler.py +65 -0
- vanna/servers/base/models.py +111 -0
- vanna/servers/base/rich_chat_handler.py +141 -0
- vanna/servers/base/templates.py +331 -0
- vanna/servers/cli/__init__.py +7 -0
- vanna/servers/cli/server_runner.py +204 -0
- vanna/servers/fastapi/__init__.py +7 -0
- vanna/servers/fastapi/app.py +163 -0
- vanna/servers/fastapi/routes.py +183 -0
- vanna/servers/flask/__init__.py +7 -0
- vanna/servers/flask/app.py +132 -0
- vanna/servers/flask/routes.py +137 -0
- vanna/tools/__init__.py +41 -0
- vanna/tools/agent_memory.py +322 -0
- vanna/tools/file_system.py +879 -0
- vanna/tools/python.py +222 -0
- vanna/tools/run_sql.py +165 -0
- vanna/tools/visualize_data.py +195 -0
- vanna/utils/__init__.py +0 -0
- vanna/web_components/__init__.py +44 -0
- vanna-2.0.0rc1.dist-info/METADATA +868 -0
- vanna-2.0.0rc1.dist-info/RECORD +289 -0
- vanna-2.0.0rc1.dist-info/entry_points.txt +3 -0
- vanna/bedrock/__init__.py +0 -1
- vanna/cohere/__init__.py +0 -2
- vanna/faiss/__init__.py +0 -1
- vanna/mock/vectordb.py +0 -55
- vanna/ollama/ollama.py +0 -103
- vanna/opensearch/opensearch_vector.py +0 -392
- vanna/opensearch/opensearch_vector_semantic.py +0 -175
- vanna/oracle/oracle_vector.py +0 -585
- vanna/qianfan/Qianfan_Chat.py +0 -165
- vanna/qianfan/Qianfan_embeddings.py +0 -36
- vanna/qianwen/QianwenAI_chat.py +0 -133
- vanna-0.7.9.dist-info/METADATA +0 -408
- vanna-0.7.9.dist-info/RECORD +0 -79
- /vanna/{ZhipuAI → legacy/ZhipuAI}/ZhipuAI_Chat.py +0 -0
- /vanna/{ZhipuAI → legacy/ZhipuAI}/__init__.py +0 -0
- /vanna/{anthropic → legacy/anthropic}/__init__.py +0 -0
- /vanna/{azuresearch → legacy/azuresearch}/__init__.py +0 -0
- /vanna/{base → legacy/base}/__init__.py +0 -0
- /vanna/{chromadb → legacy/chromadb}/__init__.py +0 -0
- /vanna/{deepseek → legacy/deepseek}/__init__.py +0 -0
- /vanna/{exceptions → legacy/exceptions}/__init__.py +0 -0
- /vanna/{google → legacy/google}/__init__.py +0 -0
- /vanna/{hf → legacy/hf}/__init__.py +0 -0
- /vanna/{local.py → legacy/local.py} +0 -0
- /vanna/{marqo → legacy/marqo}/__init__.py +0 -0
- /vanna/{marqo → legacy/marqo}/marqo.py +0 -0
- /vanna/{milvus → legacy/milvus}/__init__.py +0 -0
- /vanna/{mistral → legacy/mistral}/__init__.py +0 -0
- /vanna/{mistral → legacy/mistral}/mistral.py +0 -0
- /vanna/{mock → legacy/mock}/__init__.py +0 -0
- /vanna/{mock → legacy/mock}/embedding.py +0 -0
- /vanna/{ollama → legacy/ollama}/__init__.py +0 -0
- /vanna/{openai → legacy/openai}/__init__.py +0 -0
- /vanna/{openai → legacy/openai}/openai_embeddings.py +0 -0
- /vanna/{opensearch → legacy/opensearch}/__init__.py +0 -0
- /vanna/{oracle → legacy/oracle}/__init__.py +0 -0
- /vanna/{pgvector → legacy/pgvector}/__init__.py +0 -0
- /vanna/{pinecone → legacy/pinecone}/__init__.py +0 -0
- /vanna/{pinecone → legacy/pinecone}/pinecone_vector.py +0 -0
- /vanna/{qdrant → legacy/qdrant}/__init__.py +0 -0
- /vanna/{qianfan → legacy/qianfan}/__init__.py +0 -0
- /vanna/{qianwen → legacy/qianwen}/QianwenAI_embeddings.py +0 -0
- /vanna/{qianwen → legacy/qianwen}/__init__.py +0 -0
- /vanna/{types → legacy/types}/__init__.py +0 -0
- /vanna/{vannadb → legacy/vannadb}/__init__.py +0 -0
- /vanna/{vllm → legacy/vllm}/__init__.py +0 -0
- /vanna/{weaviate → legacy/weaviate}/__init__.py +0 -0
- /vanna/{xinference → legacy/xinference}/__init__.py +0 -0
- {vanna-0.7.9.dist-info → vanna-2.0.0rc1.dist-info}/WHEEL +0 -0
- {vanna-0.7.9.dist-info → vanna-2.0.0rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Demonstration of the new primitive component system.
|
|
3
|
+
|
|
4
|
+
This example shows how tools compose UI from primitive, domain-agnostic
|
|
5
|
+
components like StatusCardComponent, ProgressDisplayComponent, etc.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
PYTHONPATH=. python vanna/examples/primitive_components_demo.py
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import uuid
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import AsyncGenerator, Optional
|
|
15
|
+
|
|
16
|
+
from vanna import (
|
|
17
|
+
AgentConfig,
|
|
18
|
+
Agent,
|
|
19
|
+
MemoryConversationStore,
|
|
20
|
+
MockLlmService,
|
|
21
|
+
User,
|
|
22
|
+
)
|
|
23
|
+
from vanna.core.components import UiComponent
|
|
24
|
+
from vanna.core.rich_components import (
|
|
25
|
+
StatusCardComponent,
|
|
26
|
+
ProgressDisplayComponent,
|
|
27
|
+
LogViewerComponent,
|
|
28
|
+
BadgeComponent,
|
|
29
|
+
IconTextComponent,
|
|
30
|
+
RichTextComponent,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PrimitiveComponentsAgent(Agent):
|
|
35
|
+
"""Agent that demonstrates the new primitive component system."""
|
|
36
|
+
|
|
37
|
+
async def send_message(
|
|
38
|
+
self,
|
|
39
|
+
user: User,
|
|
40
|
+
message: str,
|
|
41
|
+
*,
|
|
42
|
+
conversation_id: Optional[str] = None,
|
|
43
|
+
) -> AsyncGenerator[UiComponent, None]:
|
|
44
|
+
"""Send message and demonstrate primitive component composition."""
|
|
45
|
+
|
|
46
|
+
session_id = str(uuid.uuid4())[:8]
|
|
47
|
+
|
|
48
|
+
# Demo 1: Tool execution using primitive components
|
|
49
|
+
yield UiComponent(
|
|
50
|
+
rich_component=RichTextComponent(
|
|
51
|
+
content="## Primitive Components Demo\n\nShowing how tools now compose UI from primitive components:",
|
|
52
|
+
markdown=True,
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Status card for overall operation
|
|
57
|
+
operation_status = StatusCardComponent(
|
|
58
|
+
id=f"operation-{session_id}",
|
|
59
|
+
title="Data Analysis Pipeline",
|
|
60
|
+
status="running",
|
|
61
|
+
description="Processing user data through multiple analysis stages",
|
|
62
|
+
icon="⚙️",
|
|
63
|
+
)
|
|
64
|
+
yield UiComponent(rich_component=operation_status)
|
|
65
|
+
|
|
66
|
+
# Progress display for overall progress
|
|
67
|
+
overall_progress = ProgressDisplayComponent(
|
|
68
|
+
id=f"progress-{session_id}",
|
|
69
|
+
label="Overall Progress",
|
|
70
|
+
value=0.0,
|
|
71
|
+
description="Starting analysis...",
|
|
72
|
+
animated=True,
|
|
73
|
+
)
|
|
74
|
+
yield UiComponent(rich_component=overall_progress)
|
|
75
|
+
|
|
76
|
+
# Log viewer for detailed output
|
|
77
|
+
log_viewer = LogViewerComponent(
|
|
78
|
+
id=f"logs-{session_id}",
|
|
79
|
+
title="Analysis Log",
|
|
80
|
+
entries=[],
|
|
81
|
+
show_timestamps=True,
|
|
82
|
+
auto_scroll=True,
|
|
83
|
+
)
|
|
84
|
+
yield UiComponent(rich_component=log_viewer)
|
|
85
|
+
|
|
86
|
+
# Simulate analysis stages
|
|
87
|
+
stages = [
|
|
88
|
+
("Data Loading", "📊", 0.2),
|
|
89
|
+
("Data Validation", "✅", 0.4),
|
|
90
|
+
("Statistical Analysis", "🧮", 0.6),
|
|
91
|
+
("Report Generation", "📄", 0.8),
|
|
92
|
+
("Finalization", "🎯", 1.0),
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
for i, (stage_name, stage_icon, progress_value) in enumerate(stages):
|
|
96
|
+
await asyncio.sleep(0.8)
|
|
97
|
+
|
|
98
|
+
# Update overall status
|
|
99
|
+
status = "success" if progress_value == 1.0 else "running"
|
|
100
|
+
yield UiComponent(
|
|
101
|
+
rich_component=operation_status.set_status(
|
|
102
|
+
status, f"Executing: {stage_name}"
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Update progress
|
|
107
|
+
yield UiComponent(
|
|
108
|
+
rich_component=overall_progress.update_progress(
|
|
109
|
+
progress_value, f"Executing {stage_name}..."
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Add log entry
|
|
114
|
+
yield UiComponent(
|
|
115
|
+
rich_component=log_viewer.add_entry(f"Starting {stage_name}", "info")
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Create a status card for this specific stage
|
|
119
|
+
stage_status = StatusCardComponent(
|
|
120
|
+
id=f"stage-{i}-{session_id}",
|
|
121
|
+
title=stage_name,
|
|
122
|
+
status="running" if progress_value < 1.0 else "success",
|
|
123
|
+
description=f"Processing stage {i + 1} of {len(stages)}",
|
|
124
|
+
icon=stage_icon,
|
|
125
|
+
)
|
|
126
|
+
yield UiComponent(rich_component=stage_status)
|
|
127
|
+
|
|
128
|
+
await asyncio.sleep(0.5)
|
|
129
|
+
|
|
130
|
+
# Complete the stage
|
|
131
|
+
final_stage_status = "success" if progress_value < 1.0 else "completed"
|
|
132
|
+
yield UiComponent(
|
|
133
|
+
rich_component=stage_status.set_status(
|
|
134
|
+
final_stage_status, f"{stage_name} completed successfully"
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
yield UiComponent(
|
|
138
|
+
rich_component=log_viewer.add_entry(f"Completed {stage_name}", "info")
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Demo 2: Badge and IconText primitives
|
|
142
|
+
yield UiComponent(
|
|
143
|
+
rich_component=RichTextComponent(
|
|
144
|
+
content="\n### Primitive Component Examples\n\nShowing individual primitive components:",
|
|
145
|
+
markdown=True,
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Various badge examples
|
|
150
|
+
badges = [
|
|
151
|
+
BadgeComponent(text="Processing", variant="primary", size="small"),
|
|
152
|
+
BadgeComponent(text="Complete", variant="success", size="medium"),
|
|
153
|
+
BadgeComponent(text="Warning", variant="warning", size="large", icon="⚠️"),
|
|
154
|
+
BadgeComponent(text="Error", variant="error", size="medium", icon="❌"),
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
for badge in badges:
|
|
158
|
+
yield UiComponent(rich_component=badge)
|
|
159
|
+
|
|
160
|
+
# IconText examples
|
|
161
|
+
icon_texts = [
|
|
162
|
+
IconTextComponent(
|
|
163
|
+
icon="📊",
|
|
164
|
+
text="Data Analysis Complete",
|
|
165
|
+
variant="primary",
|
|
166
|
+
size="large",
|
|
167
|
+
),
|
|
168
|
+
IconTextComponent(
|
|
169
|
+
icon="✅", text="All tests passed", variant="default", size="medium"
|
|
170
|
+
),
|
|
171
|
+
IconTextComponent(
|
|
172
|
+
icon="⏱️",
|
|
173
|
+
text="Processing time: 2.3s",
|
|
174
|
+
variant="secondary",
|
|
175
|
+
size="small",
|
|
176
|
+
),
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
for icon_text in icon_texts:
|
|
180
|
+
yield UiComponent(rich_component=icon_text)
|
|
181
|
+
|
|
182
|
+
# Demo 3: Comparison with old approach
|
|
183
|
+
yield UiComponent(
|
|
184
|
+
rich_component=RichTextComponent(
|
|
185
|
+
content=f"""
|
|
186
|
+
## Key Benefits of Primitive Components
|
|
187
|
+
|
|
188
|
+
**Primitive Component Approach:**
|
|
189
|
+
```python
|
|
190
|
+
# Tool composes UI from primitives
|
|
191
|
+
status_card = StatusCardComponent(
|
|
192
|
+
title="Data Analysis",
|
|
193
|
+
status="running", # Pure UI state
|
|
194
|
+
icon="📊"
|
|
195
|
+
)
|
|
196
|
+
progress = ProgressDisplayComponent(
|
|
197
|
+
label="Analysis Progress",
|
|
198
|
+
value=0.5
|
|
199
|
+
)
|
|
200
|
+
logs = LogViewerComponent(
|
|
201
|
+
title="Analysis Log",
|
|
202
|
+
entries=log_entries
|
|
203
|
+
)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Benefits:
|
|
207
|
+
- **Separation of Concerns**: UI components are purely presentational
|
|
208
|
+
- **Reusability**: Status cards work for any process, not just tools
|
|
209
|
+
- **Composability**: Tools build exactly the UI they need
|
|
210
|
+
- **Maintainability**: Changes to business logic don't affect UI components
|
|
211
|
+
- **Extensibility**: New tools don't require new component types
|
|
212
|
+
|
|
213
|
+
Your message was: "{message}"
|
|
214
|
+
""",
|
|
215
|
+
markdown=True,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def create_primitive_demo_agent() -> PrimitiveComponentsAgent:
|
|
221
|
+
"""Create a primitive components demo agent.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Configured PrimitiveComponentsAgent instance
|
|
225
|
+
"""
|
|
226
|
+
llm_service = MockLlmService(response_content="Primitive components demo response")
|
|
227
|
+
|
|
228
|
+
return PrimitiveComponentsAgent(
|
|
229
|
+
llm_service=llm_service,
|
|
230
|
+
config=AgentConfig(
|
|
231
|
+
stream_responses=True,
|
|
232
|
+
include_thinking_indicators=False,
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
async def main() -> None:
|
|
238
|
+
"""Run the primitive components demo."""
|
|
239
|
+
|
|
240
|
+
# Create agent
|
|
241
|
+
agent = create_primitive_demo_agent()
|
|
242
|
+
|
|
243
|
+
# Create a test user
|
|
244
|
+
user = User(
|
|
245
|
+
id="user123", username="demo_user", email="demo@example.com", permissions=[]
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Start a conversation
|
|
249
|
+
conversation_id = "primitive_demo_123"
|
|
250
|
+
user_message = "Show me how the new primitive component system works!"
|
|
251
|
+
|
|
252
|
+
print(f"User: {user_message}")
|
|
253
|
+
print("Agent response (primitive components):")
|
|
254
|
+
print("=" * 60)
|
|
255
|
+
|
|
256
|
+
# Send message and display components
|
|
257
|
+
component_count = 0
|
|
258
|
+
async for component in agent.send_message(
|
|
259
|
+
user=user, message=user_message, conversation_id=conversation_id
|
|
260
|
+
):
|
|
261
|
+
component_count += 1
|
|
262
|
+
component_type = getattr(component, "type", component.__class__.__name__)
|
|
263
|
+
component_id = getattr(component, "id", "N/A")
|
|
264
|
+
|
|
265
|
+
print(
|
|
266
|
+
f"[{component_count:2d}] {component_type.value if hasattr(component_type, 'value') else component_type} (id: {component_id[:12] if len(str(component_id)) > 12 else component_id})"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
rich_comp = component.rich_component
|
|
270
|
+
|
|
271
|
+
# Show component details
|
|
272
|
+
if hasattr(rich_comp, "title"):
|
|
273
|
+
print(f" Title: {rich_comp.title}")
|
|
274
|
+
if hasattr(rich_comp, "status"):
|
|
275
|
+
print(f" Status: {rich_comp.status}")
|
|
276
|
+
if hasattr(rich_comp, "description") and rich_comp.description:
|
|
277
|
+
desc = (
|
|
278
|
+
rich_comp.description[:60] + "..."
|
|
279
|
+
if len(rich_comp.description) > 60
|
|
280
|
+
else rich_comp.description
|
|
281
|
+
)
|
|
282
|
+
print(f" Description: {desc}")
|
|
283
|
+
if (
|
|
284
|
+
hasattr(rich_comp, "value")
|
|
285
|
+
and hasattr(rich_comp.type, "value")
|
|
286
|
+
and rich_comp.type.value == "progress_display"
|
|
287
|
+
):
|
|
288
|
+
print(f" Progress: {rich_comp.value:.1%}")
|
|
289
|
+
|
|
290
|
+
print()
|
|
291
|
+
|
|
292
|
+
print("=" * 60)
|
|
293
|
+
print(f"Total components emitted: {component_count}")
|
|
294
|
+
print("\nThis demonstrates how tools can now compose rich UIs")
|
|
295
|
+
print("from primitive, reusable components without semantic coupling!")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def run_interactive() -> None:
|
|
299
|
+
"""Entry point for interactive usage."""
|
|
300
|
+
print("Starting Primitive Components Demo...")
|
|
301
|
+
asyncio.run(main())
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if __name__ == "__main__":
|
|
305
|
+
run_interactive()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating lifecycle hooks for user quota management.
|
|
3
|
+
|
|
4
|
+
This example shows how to use lifecycle hooks to add custom functionality
|
|
5
|
+
like quota management without creating custom agent runner subclasses.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
from vanna.core import Agent, LifecycleHook, User
|
|
10
|
+
from vanna.core.errors import AgentError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class QuotaExceededError(AgentError):
|
|
14
|
+
"""Raised when a user exceeds their message quota."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class QuotaCheckHook(LifecycleHook):
|
|
20
|
+
"""Lifecycle hook that enforces user-based message quotas."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, default_quota: int = 10) -> None:
|
|
23
|
+
"""Initialize quota hook.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
default_quota: Default quota per user if not specifically set
|
|
27
|
+
"""
|
|
28
|
+
self._user_quotas: Dict[str, int] = {}
|
|
29
|
+
self._user_usage: Dict[str, int] = {}
|
|
30
|
+
self._default_quota = default_quota
|
|
31
|
+
|
|
32
|
+
def set_user_quota(self, user_id: str, quota: int) -> None:
|
|
33
|
+
"""Set a specific quota for a user."""
|
|
34
|
+
self._user_quotas[user_id] = quota
|
|
35
|
+
|
|
36
|
+
def get_user_quota(self, user_id: str) -> int:
|
|
37
|
+
"""Get the quota for a user."""
|
|
38
|
+
return self._user_quotas.get(user_id, self._default_quota)
|
|
39
|
+
|
|
40
|
+
def get_user_usage(self, user_id: str) -> int:
|
|
41
|
+
"""Get current usage count for a user."""
|
|
42
|
+
return self._user_usage.get(user_id, 0)
|
|
43
|
+
|
|
44
|
+
def get_user_remaining(self, user_id: str) -> int:
|
|
45
|
+
"""Get remaining messages for a user."""
|
|
46
|
+
return self.get_user_quota(user_id) - self.get_user_usage(user_id)
|
|
47
|
+
|
|
48
|
+
def reset_user_usage(self, user_id: str) -> None:
|
|
49
|
+
"""Reset usage count for a user."""
|
|
50
|
+
self._user_usage[user_id] = 0
|
|
51
|
+
|
|
52
|
+
async def before_message(self, user: User, message: str) -> Optional[str]:
|
|
53
|
+
"""Check quota before processing message.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
QuotaExceededError: If user has exceeded their quota
|
|
57
|
+
"""
|
|
58
|
+
usage = self.get_user_usage(user.id)
|
|
59
|
+
quota = self.get_user_quota(user.id)
|
|
60
|
+
|
|
61
|
+
if usage >= quota:
|
|
62
|
+
raise QuotaExceededError(
|
|
63
|
+
f"User {user.username} has exceeded their quota of {quota} messages. "
|
|
64
|
+
f"Current usage: {usage}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Increment usage count
|
|
68
|
+
current_usage = self._user_usage.get(user.id, 0)
|
|
69
|
+
self._user_usage[user.id] = current_usage + 1
|
|
70
|
+
|
|
71
|
+
# Don't modify the message
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LoggingHook(LifecycleHook):
|
|
76
|
+
"""Example logging hook for demonstration."""
|
|
77
|
+
|
|
78
|
+
async def before_message(self, user: User, message: str) -> Optional[str]:
|
|
79
|
+
"""Log incoming messages."""
|
|
80
|
+
print(f"[LOG] User {user.username} ({user.id}) sent message: {message[:50]}...")
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
async def after_message(self, result: Any) -> None:
|
|
84
|
+
"""Log message completion."""
|
|
85
|
+
print(f"[LOG] Message processing completed")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def run_example() -> None:
|
|
89
|
+
"""
|
|
90
|
+
Example showing how to use lifecycle hooks with Agent.
|
|
91
|
+
|
|
92
|
+
Instead of creating a custom subclass, we compose
|
|
93
|
+
the behavior using lifecycle hooks.
|
|
94
|
+
"""
|
|
95
|
+
from vanna.core.registry import ToolRegistry
|
|
96
|
+
from vanna.integrations.anthropic import AnthropicLlmService
|
|
97
|
+
from vanna.integrations.local import MemoryConversationStore
|
|
98
|
+
|
|
99
|
+
# Create quota hook
|
|
100
|
+
quota_hook = QuotaCheckHook(default_quota=10)
|
|
101
|
+
quota_hook.set_user_quota("user123", 5) # Set custom quota for specific user
|
|
102
|
+
|
|
103
|
+
# Create logging hook
|
|
104
|
+
logging_hook = LoggingHook()
|
|
105
|
+
|
|
106
|
+
# Create agent with multiple hooks
|
|
107
|
+
agent = Agent(
|
|
108
|
+
llm_service=AnthropicLlmService(api_key="your-api-key"),
|
|
109
|
+
tool_registry=ToolRegistry(),
|
|
110
|
+
conversation_store=MemoryConversationStore(),
|
|
111
|
+
lifecycle_hooks=[
|
|
112
|
+
logging_hook, # Logs will happen first
|
|
113
|
+
quota_hook, # Then quota check
|
|
114
|
+
],
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Create a test user
|
|
118
|
+
user = User(
|
|
119
|
+
id="user123", username="test_user", email="test@example.com", permissions=[]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Send messages - will track quota
|
|
123
|
+
try:
|
|
124
|
+
async for component in agent.send_message(user=user, message="Hello, agent!"):
|
|
125
|
+
# Process UI components
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
# Check remaining quota
|
|
129
|
+
remaining = quota_hook.get_user_remaining(user.id)
|
|
130
|
+
print(f"Remaining messages: {remaining}/{quota_hook.get_user_quota(user.id)}")
|
|
131
|
+
|
|
132
|
+
except QuotaExceededError as e:
|
|
133
|
+
print(f"Quota exceeded: {e}")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
import asyncio
|
|
138
|
+
|
|
139
|
+
asyncio.run(run_example())
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating SQL query execution with automatic visualization.
|
|
3
|
+
|
|
4
|
+
This example shows the integration of RunSqlTool and VisualizeDataTool,
|
|
5
|
+
demonstrating how SQL results are saved to CSV files and can be visualized
|
|
6
|
+
using the visualization tool with dependency injection.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
PYTHONPATH=. python vanna/examples/visualization_example.py
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import uuid
|
|
16
|
+
from typing import AsyncGenerator, List, Optional
|
|
17
|
+
|
|
18
|
+
from vanna import (
|
|
19
|
+
AgentConfig,
|
|
20
|
+
Agent,
|
|
21
|
+
ToolRegistry,
|
|
22
|
+
User,
|
|
23
|
+
)
|
|
24
|
+
from vanna.core import LlmService
|
|
25
|
+
from vanna.core import (
|
|
26
|
+
LlmRequest,
|
|
27
|
+
LlmResponse,
|
|
28
|
+
LlmStreamChunk,
|
|
29
|
+
ToolCall,
|
|
30
|
+
ToolSchema,
|
|
31
|
+
)
|
|
32
|
+
from vanna.integrations.sqlite import SqliteRunner
|
|
33
|
+
from vanna.tools import (
|
|
34
|
+
RunSqlTool,
|
|
35
|
+
VisualizeDataTool,
|
|
36
|
+
LocalFileSystem,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class VisualizationDemoLlmService(LlmService):
|
|
41
|
+
"""Mock LLM that demonstrates SQL query and visualization workflow."""
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
self.step = 0
|
|
45
|
+
self.csv_filename: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
async def send_request(self, request: LlmRequest) -> LlmResponse:
|
|
48
|
+
"""Handle non-streaming requests."""
|
|
49
|
+
await asyncio.sleep(0.1)
|
|
50
|
+
return self._build_response(request)
|
|
51
|
+
|
|
52
|
+
async def stream_request(
|
|
53
|
+
self, request: LlmRequest
|
|
54
|
+
) -> AsyncGenerator[LlmStreamChunk, None]:
|
|
55
|
+
"""Handle streaming requests."""
|
|
56
|
+
await asyncio.sleep(0.1)
|
|
57
|
+
response = self._build_response(request)
|
|
58
|
+
|
|
59
|
+
if response.tool_calls:
|
|
60
|
+
yield LlmStreamChunk(tool_calls=response.tool_calls)
|
|
61
|
+
if response.content:
|
|
62
|
+
yield LlmStreamChunk(
|
|
63
|
+
content=response.content, finish_reason=response.finish_reason
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
yield LlmStreamChunk(finish_reason=response.finish_reason)
|
|
67
|
+
|
|
68
|
+
async def validate_tools(self, tools: List[ToolSchema]) -> List[str]:
|
|
69
|
+
"""Validate tools - no errors."""
|
|
70
|
+
return []
|
|
71
|
+
|
|
72
|
+
def _build_response(self, request: LlmRequest) -> LlmResponse:
|
|
73
|
+
"""Build response based on conversation state."""
|
|
74
|
+
last_message = request.messages[-1] if request.messages else None
|
|
75
|
+
|
|
76
|
+
# If we got a tool result, process it
|
|
77
|
+
if last_message and last_message.role == "tool":
|
|
78
|
+
tool_result = last_message.content or ""
|
|
79
|
+
|
|
80
|
+
# Check if this was a SQL query result with a CSV file
|
|
81
|
+
if "Results saved to" in tool_result and ".csv" in tool_result:
|
|
82
|
+
# Extract filename from result
|
|
83
|
+
import re
|
|
84
|
+
|
|
85
|
+
match = re.search(r"'([^']*\.csv)'", tool_result)
|
|
86
|
+
if match:
|
|
87
|
+
self.csv_filename = match.group(1)
|
|
88
|
+
# Now visualize the data
|
|
89
|
+
return LlmResponse(
|
|
90
|
+
content=f"Great! I've saved the query results. Now let me create a visualization of the data.",
|
|
91
|
+
tool_calls=[
|
|
92
|
+
ToolCall(
|
|
93
|
+
id=f"call_{uuid.uuid4().hex[:8]}",
|
|
94
|
+
name="visualize_data",
|
|
95
|
+
arguments={"filename": self.csv_filename},
|
|
96
|
+
)
|
|
97
|
+
],
|
|
98
|
+
finish_reason="tool_calls",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# If this was a visualization result, acknowledge it
|
|
102
|
+
if "Created visualization" in tool_result:
|
|
103
|
+
return LlmResponse(
|
|
104
|
+
content=f"Perfect! I've created a visualization of the data. {tool_result}",
|
|
105
|
+
finish_reason="stop",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Default acknowledgment
|
|
109
|
+
return LlmResponse(
|
|
110
|
+
content=f"I've completed the operation. {tool_result}",
|
|
111
|
+
finish_reason="stop",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Initial request - run SQL query
|
|
115
|
+
if self.step == 0:
|
|
116
|
+
self.step += 1
|
|
117
|
+
return LlmResponse(
|
|
118
|
+
content="I'll query the database for you and then create a visualization.",
|
|
119
|
+
tool_calls=[
|
|
120
|
+
ToolCall(
|
|
121
|
+
id=f"call_{uuid.uuid4().hex[:8]}",
|
|
122
|
+
name="run_sql",
|
|
123
|
+
arguments={
|
|
124
|
+
"sql": "SELECT Name, Milliseconds, Bytes FROM Track LIMIT 20"
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
],
|
|
128
|
+
finish_reason="tool_calls",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Default response
|
|
132
|
+
return LlmResponse(
|
|
133
|
+
content="I can help you query databases and visualize the results.",
|
|
134
|
+
finish_reason="stop",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def create_demo_agent() -> Agent:
|
|
139
|
+
"""
|
|
140
|
+
Create a demo agent with SQL and visualization tools.
|
|
141
|
+
|
|
142
|
+
This function is called by the vanna server framework.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Configured Agent with SQL and visualization tools
|
|
146
|
+
"""
|
|
147
|
+
# Check for Chinook database
|
|
148
|
+
database_path = os.path.join(
|
|
149
|
+
os.path.dirname(__file__), "..", "..", "Chinook.sqlite"
|
|
150
|
+
)
|
|
151
|
+
database_path = os.path.abspath(database_path)
|
|
152
|
+
|
|
153
|
+
if not os.path.exists(database_path):
|
|
154
|
+
raise FileNotFoundError(
|
|
155
|
+
f"Chinook database not found at {database_path}. "
|
|
156
|
+
"Please download it from https://vanna.ai/Chinook.sqlite"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Create shared FileSystem for both tools
|
|
160
|
+
file_system = LocalFileSystem(working_directory="./data_storage")
|
|
161
|
+
|
|
162
|
+
# Create SQL tool with FileSystem
|
|
163
|
+
sqlite_runner = SqliteRunner(database_path=database_path)
|
|
164
|
+
sql_tool = RunSqlTool(sql_runner=sqlite_runner, file_system=file_system)
|
|
165
|
+
|
|
166
|
+
# Create visualization tool with same FileSystem
|
|
167
|
+
viz_tool = VisualizeDataTool(file_system=file_system)
|
|
168
|
+
|
|
169
|
+
# Create tool registry
|
|
170
|
+
tool_registry = ToolRegistry()
|
|
171
|
+
tool_registry.register(sql_tool)
|
|
172
|
+
tool_registry.register(viz_tool)
|
|
173
|
+
|
|
174
|
+
# Create LLM service
|
|
175
|
+
llm_service = VisualizationDemoLlmService()
|
|
176
|
+
|
|
177
|
+
# Create agent with streaming enabled for web interface
|
|
178
|
+
return Agent(
|
|
179
|
+
llm_service=llm_service,
|
|
180
|
+
tool_registry=tool_registry,
|
|
181
|
+
config=AgentConfig(
|
|
182
|
+
stream_responses=True,
|
|
183
|
+
include_thinking_indicators=False,
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
async def main() -> None:
|
|
189
|
+
"""Demonstrate SQL query execution with automatic visualization."""
|
|
190
|
+
print("🎨 SQL + Visualization Demo")
|
|
191
|
+
print("=" * 60)
|
|
192
|
+
print("This example demonstrates:")
|
|
193
|
+
print("1. Running SQL queries that save results to CSV files")
|
|
194
|
+
print("2. Automatically visualizing the CSV data")
|
|
195
|
+
print("3. User isolation for file storage")
|
|
196
|
+
print("=" * 60)
|
|
197
|
+
print()
|
|
198
|
+
|
|
199
|
+
# Create agent using factory function
|
|
200
|
+
agent = create_demo_agent()
|
|
201
|
+
|
|
202
|
+
# Create test user
|
|
203
|
+
user = User(id="demo-user", username="demo")
|
|
204
|
+
|
|
205
|
+
# Show available tools
|
|
206
|
+
tools = await agent.get_available_tools(user)
|
|
207
|
+
print(f"Available tools: {[tool.name for tool in tools]}")
|
|
208
|
+
print()
|
|
209
|
+
|
|
210
|
+
# Run conversation
|
|
211
|
+
conversation_id = "viz-demo"
|
|
212
|
+
|
|
213
|
+
print("User: Show me some track data and visualize it")
|
|
214
|
+
print()
|
|
215
|
+
|
|
216
|
+
async for component in agent.send_message(
|
|
217
|
+
user=user,
|
|
218
|
+
message="Show me some track data and visualize it",
|
|
219
|
+
conversation_id=conversation_id,
|
|
220
|
+
):
|
|
221
|
+
if (
|
|
222
|
+
component.simple_component
|
|
223
|
+
and hasattr(component.simple_component, "text")
|
|
224
|
+
and component.simple_component.text
|
|
225
|
+
):
|
|
226
|
+
print(f"Agent: {component.simple_component.text}")
|
|
227
|
+
elif component.simple_component and hasattr(component.simple_component, "text"):
|
|
228
|
+
print(f"Agent: {component.simple_component.text}")
|
|
229
|
+
elif hasattr(component.rich_component, "content"):
|
|
230
|
+
if isinstance(component.rich_component.content, dict):
|
|
231
|
+
# This is the chart
|
|
232
|
+
print(
|
|
233
|
+
f"Agent: [Chart Generated - Plotly figure with {len(str(component.rich_component.content))} chars]"
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
print(f"Agent: {component.rich_component.content}")
|
|
237
|
+
|
|
238
|
+
print()
|
|
239
|
+
print("=" * 60)
|
|
240
|
+
print("Demo complete!")
|
|
241
|
+
print()
|
|
242
|
+
print("Key features demonstrated:")
|
|
243
|
+
print("✅ SQL queries save results to user-isolated CSV files")
|
|
244
|
+
print("✅ Visualization tool reads CSV files using FileSystem")
|
|
245
|
+
print("✅ Automatic chart type selection based on data shape")
|
|
246
|
+
print("✅ Dependency injection allows customization")
|
|
247
|
+
print()
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
if __name__ == "__main__":
|
|
251
|
+
asyncio.run(main())
|