vanna 0.7.9__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.
- 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 +461 -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.0.dist-info/METADATA +485 -0
- vanna-2.0.0.dist-info/RECORD +289 -0
- vanna-2.0.0.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.0.dist-info}/WHEEL +0 -0
- {vanna-0.7.9.dist-info → vanna-2.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User resolver interface for web request authentication.
|
|
3
|
+
|
|
4
|
+
This module provides the abstract base class for resolving web requests
|
|
5
|
+
to authenticated User objects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
|
|
10
|
+
from .models import User
|
|
11
|
+
from .request_context import RequestContext
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UserResolver(ABC):
|
|
15
|
+
"""Resolves web requests to authenticated users.
|
|
16
|
+
|
|
17
|
+
Implementations of this interface handle the specifics of extracting
|
|
18
|
+
user identity from request context (cookies, headers, tokens, etc.)
|
|
19
|
+
and creating authenticated User objects.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
class JwtUserResolver(UserResolver):
|
|
23
|
+
async def resolve_user(self, request_context: RequestContext) -> User:
|
|
24
|
+
token = request_context.get_header('Authorization')
|
|
25
|
+
# ... validate JWT and extract user info
|
|
26
|
+
return User(id=user_id, username=username, email=email)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
async def resolve_user(self, request_context: RequestContext) -> User:
|
|
31
|
+
"""Resolve user from request context.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
request_context: Structured request context with cookies, headers, etc.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Authenticated User object
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
Can raise exceptions for authentication failures
|
|
41
|
+
"""
|
|
42
|
+
pass
|
vanna/core/validation.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Development utilities for validating Pydantic models.
|
|
3
|
+
|
|
4
|
+
This module provides utilities that can be used during development
|
|
5
|
+
and testing to catch forward reference issues early.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Tuple, Type
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
import importlib
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def validate_pydantic_models_in_package(package_name: str) -> Dict[str, Any]:
|
|
15
|
+
"""
|
|
16
|
+
Validate all Pydantic models in a package for completeness.
|
|
17
|
+
|
|
18
|
+
This function can be used in tests or development scripts to catch
|
|
19
|
+
forward reference issues before they cause runtime errors.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
package_name: Name of the package to validate (e.g., 'vanna.core')
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Dictionary with validation results
|
|
26
|
+
"""
|
|
27
|
+
results: Dict[str, Any] = {
|
|
28
|
+
"total_models": 0,
|
|
29
|
+
"incomplete_models": [],
|
|
30
|
+
"models": {},
|
|
31
|
+
"summary": "",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
# Import the package
|
|
36
|
+
package = importlib.import_module(package_name)
|
|
37
|
+
|
|
38
|
+
# Get all submodules
|
|
39
|
+
submodules = []
|
|
40
|
+
if hasattr(package, "__path__"):
|
|
41
|
+
import pkgutil
|
|
42
|
+
|
|
43
|
+
for _, name, _ in pkgutil.iter_modules(
|
|
44
|
+
package.__path__, package_name + "."
|
|
45
|
+
):
|
|
46
|
+
try:
|
|
47
|
+
submodule = importlib.import_module(name)
|
|
48
|
+
submodules.append((name, submodule))
|
|
49
|
+
except ImportError:
|
|
50
|
+
continue
|
|
51
|
+
else:
|
|
52
|
+
submodules = [(package_name, package)]
|
|
53
|
+
|
|
54
|
+
# Check all Pydantic models in each submodule
|
|
55
|
+
for module_name, module in submodules:
|
|
56
|
+
for name, obj in inspect.getmembers(module):
|
|
57
|
+
if (
|
|
58
|
+
inspect.isclass(obj)
|
|
59
|
+
and issubclass(obj, BaseModel)
|
|
60
|
+
and obj is not BaseModel
|
|
61
|
+
):
|
|
62
|
+
model_key = f"{module_name}.{name}"
|
|
63
|
+
results["total_models"] += 1
|
|
64
|
+
|
|
65
|
+
# Check for forward references
|
|
66
|
+
forward_refs: List[Tuple[str, str]] = []
|
|
67
|
+
for field_name, field_info in obj.model_fields.items():
|
|
68
|
+
annotation = field_info.annotation
|
|
69
|
+
if annotation is not None and hasattr(
|
|
70
|
+
annotation, "__forward_arg__"
|
|
71
|
+
):
|
|
72
|
+
forward_refs.append(
|
|
73
|
+
(field_name, annotation.__forward_arg__)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Check completeness
|
|
77
|
+
try:
|
|
78
|
+
obj.model_json_schema()
|
|
79
|
+
is_complete = True
|
|
80
|
+
error = None
|
|
81
|
+
except Exception as e:
|
|
82
|
+
is_complete = False
|
|
83
|
+
error = str(e)
|
|
84
|
+
results["incomplete_models"].append(model_key)
|
|
85
|
+
|
|
86
|
+
results["models"][model_key] = {
|
|
87
|
+
"class": obj,
|
|
88
|
+
"forward_references": forward_refs,
|
|
89
|
+
"is_complete": is_complete,
|
|
90
|
+
"error": error,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Generate summary
|
|
94
|
+
incomplete_models = results["incomplete_models"]
|
|
95
|
+
incomplete_count = len(incomplete_models)
|
|
96
|
+
total_models = results["total_models"]
|
|
97
|
+
if incomplete_count == 0:
|
|
98
|
+
results["summary"] = (
|
|
99
|
+
f"✓ All {total_models} Pydantic models are complete and valid!"
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
results["summary"] = (
|
|
103
|
+
f"⚠ {incomplete_count} of {total_models} models are incomplete: "
|
|
104
|
+
f"{', '.join(incomplete_models)}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
except Exception as e:
|
|
108
|
+
results["summary"] = f"Error validating package {package_name}: {e}"
|
|
109
|
+
|
|
110
|
+
return results
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def check_models_health() -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Quick health check for all core Pydantic models.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
True if all models are healthy, False otherwise
|
|
119
|
+
"""
|
|
120
|
+
core_packages = [
|
|
121
|
+
"vanna.core.tool.models",
|
|
122
|
+
"vanna.core.user.models",
|
|
123
|
+
"vanna.core.llm.models",
|
|
124
|
+
"vanna.core.storage.models",
|
|
125
|
+
"vanna.core.agent.models",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
all_healthy = True
|
|
129
|
+
|
|
130
|
+
for package in core_packages:
|
|
131
|
+
try:
|
|
132
|
+
results = validate_pydantic_models_in_package(package)
|
|
133
|
+
if results["incomplete_models"]:
|
|
134
|
+
print(f"❌ Issues in {package}: {results['incomplete_models']}")
|
|
135
|
+
all_healthy = False
|
|
136
|
+
else:
|
|
137
|
+
print(f"✅ {package}: {results['total_models']} models OK")
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print(f"❌ Error checking {package}: {e}")
|
|
140
|
+
all_healthy = False
|
|
141
|
+
|
|
142
|
+
return all_healthy
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
print("Checking Pydantic model health across core packages...")
|
|
147
|
+
print("=" * 60)
|
|
148
|
+
|
|
149
|
+
healthy = check_models_health()
|
|
150
|
+
|
|
151
|
+
print("=" * 60)
|
|
152
|
+
if healthy:
|
|
153
|
+
print("🎉 All Pydantic models are healthy!")
|
|
154
|
+
else:
|
|
155
|
+
print("⚠️ Some models need attention.")
|
|
156
|
+
print("\nTo fix forward reference issues:")
|
|
157
|
+
print("1. Ensure all referenced classes are imported")
|
|
158
|
+
print("2. Call model_rebuild() after imports")
|
|
159
|
+
print("3. Use proper TYPE_CHECKING imports for circular deps")
|
|
160
|
+
|
|
161
|
+
print("\nNote: You can also catch these issues at development time using:")
|
|
162
|
+
print(" - mypy static type checking")
|
|
163
|
+
print(" - This validation script in your test suite")
|
|
164
|
+
print(" - Pre-commit hooks")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Workflow handler system for deterministic workflow execution.
|
|
3
|
+
|
|
4
|
+
This module provides the WorkflowHandler interface for intercepting user messages
|
|
5
|
+
and executing deterministic workflows before they reach the LLM. This is useful
|
|
6
|
+
for command handling, pattern-based routing, and state-based workflows.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .base import WorkflowHandler, WorkflowResult
|
|
10
|
+
from .default import DefaultWorkflowHandler
|
|
11
|
+
|
|
12
|
+
__all__ = ["WorkflowHandler", "WorkflowResult", "DefaultWorkflowHandler"]
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base workflow handler interface.
|
|
3
|
+
|
|
4
|
+
Workflow triggers allow you to execute deterministic workflows in response to
|
|
5
|
+
user messages before they are sent to the LLM. This is useful for:
|
|
6
|
+
- Command handling (e.g., /help, /reset)
|
|
7
|
+
- Pattern-based routing (e.g., report generation)
|
|
8
|
+
- State-based workflows (e.g., onboarding flows)
|
|
9
|
+
- Quota enforcement with custom responses
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from typing import (
|
|
14
|
+
TYPE_CHECKING,
|
|
15
|
+
Optional,
|
|
16
|
+
Union,
|
|
17
|
+
List,
|
|
18
|
+
AsyncGenerator,
|
|
19
|
+
Callable,
|
|
20
|
+
Awaitable,
|
|
21
|
+
)
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from ..user.models import User
|
|
26
|
+
from ..storage import Conversation
|
|
27
|
+
from ...components import UiComponent
|
|
28
|
+
from ..agent.agent import Agent
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class WorkflowResult:
|
|
33
|
+
"""Result from a workflow handler attempt.
|
|
34
|
+
|
|
35
|
+
When a workflow handles a message, it can optionally return UI components to stream
|
|
36
|
+
to the user and/or mutate the conversation state.
|
|
37
|
+
|
|
38
|
+
Attributes:
|
|
39
|
+
should_skip_llm: If True, the workflow handled the message and LLM processing is skipped.
|
|
40
|
+
If False, the message continues to the agent/LLM.
|
|
41
|
+
components: Optional UI components to stream back to the user.
|
|
42
|
+
Can be a list or async generator for streaming responses.
|
|
43
|
+
conversation_mutation: Optional async callback to modify conversation state
|
|
44
|
+
(e.g., clearing messages, adding system events).
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
# Simple command response
|
|
48
|
+
WorkflowResult(
|
|
49
|
+
should_skip_llm=True,
|
|
50
|
+
components=[RichTextComponent(content="Help text here")]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# With conversation mutation
|
|
54
|
+
async def clear_history(conv):
|
|
55
|
+
conv.messages.clear()
|
|
56
|
+
|
|
57
|
+
WorkflowResult(
|
|
58
|
+
should_skip_llm=True,
|
|
59
|
+
components=[StatusCardComponent(...)],
|
|
60
|
+
conversation_mutation=clear_history
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Not handled, continue to agent
|
|
64
|
+
WorkflowResult(should_skip_llm=False)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
should_skip_llm: bool
|
|
68
|
+
components: Optional[
|
|
69
|
+
Union[List["UiComponent"], AsyncGenerator["UiComponent", None]]
|
|
70
|
+
] = None
|
|
71
|
+
conversation_mutation: Optional[Callable[["Conversation"], Awaitable[None]]] = None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class WorkflowHandler(ABC):
|
|
75
|
+
"""Base class for handling deterministic workflows before LLM processing.
|
|
76
|
+
|
|
77
|
+
Implement this interface to intercept user messages and execute deterministic
|
|
78
|
+
workflows instead of sending to the LLM. This is the first extensibility point
|
|
79
|
+
in the agent's message processing pipeline, running after user resolution and
|
|
80
|
+
conversation loading but before the message is added to conversation history
|
|
81
|
+
or sent to the LLM.
|
|
82
|
+
|
|
83
|
+
Use cases:
|
|
84
|
+
- Slash commands (/help, /reset, /report)
|
|
85
|
+
- Pattern-based routing (regex matching)
|
|
86
|
+
- State-based workflows (onboarding, surveys)
|
|
87
|
+
- Custom quota enforcement with helpful messages
|
|
88
|
+
- Deterministic report generation
|
|
89
|
+
- Starter UI (buttons, welcome messages) when conversation begins
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
class CommandWorkflow(WorkflowHandler):
|
|
93
|
+
async def try_handle(self, agent, user, conversation, message):
|
|
94
|
+
if message.startswith("/help"):
|
|
95
|
+
return WorkflowResult(
|
|
96
|
+
should_skip_llm=True,
|
|
97
|
+
components=[
|
|
98
|
+
RichTextComponent(
|
|
99
|
+
content="Available commands:\\n- /help\\n- /reset",
|
|
100
|
+
markdown=True
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Execute tool for reports
|
|
106
|
+
if message.startswith("/report"):
|
|
107
|
+
tool = await agent.tool_registry.get_tool("generate_report")
|
|
108
|
+
result = await tool.execute(ToolContext(user=user), {})
|
|
109
|
+
return WorkflowResult(should_skip_llm=True, components=[result.ui_component])
|
|
110
|
+
|
|
111
|
+
# Not handled, continue to agent
|
|
112
|
+
return WorkflowResult(should_skip_llm=False)
|
|
113
|
+
|
|
114
|
+
async def get_starter_ui(self, agent, user, conversation):
|
|
115
|
+
return [
|
|
116
|
+
RichTextComponent(content=f"Welcome {user.username}!"),
|
|
117
|
+
ButtonComponent(label="Generate Report", value="/report"),
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
agent = Agent(
|
|
121
|
+
llm_service=...,
|
|
122
|
+
tool_registry=...,
|
|
123
|
+
user_resolver=...,
|
|
124
|
+
workflow_handler=CommandWorkflow()
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
Observability:
|
|
128
|
+
The agent automatically creates an "agent.workflow_handler" span when
|
|
129
|
+
a WorkflowHandler is configured, allowing you to monitor handler
|
|
130
|
+
performance and outcomes.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
@abstractmethod
|
|
134
|
+
async def try_handle(
|
|
135
|
+
self, agent: "Agent", user: "User", conversation: "Conversation", message: str
|
|
136
|
+
) -> WorkflowResult:
|
|
137
|
+
"""Attempt to handle a workflow for the given message.
|
|
138
|
+
|
|
139
|
+
This method is called for every user message before it reaches the LLM.
|
|
140
|
+
Inspect the message content, user context, and conversation state to
|
|
141
|
+
decide whether to execute a deterministic workflow or allow normal
|
|
142
|
+
agent processing.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
agent: The agent instance, providing access to tool_registry, config,
|
|
146
|
+
and observability_provider for tool execution and logging.
|
|
147
|
+
user: The user who sent the message, including their ID, permissions,
|
|
148
|
+
and metadata. Use this for permission checks or personalization.
|
|
149
|
+
conversation: The current conversation context, including message history.
|
|
150
|
+
Can be inspected for state-based workflows.
|
|
151
|
+
message: The user's raw message content.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
WorkflowResult with should_skip_llm=True to execute a workflow and skip LLM,
|
|
155
|
+
or should_skip_llm=False to continue normal agent processing.
|
|
156
|
+
|
|
157
|
+
When should_skip_llm=True:
|
|
158
|
+
- The message is NOT added to conversation history automatically
|
|
159
|
+
- The components are streamed to the user
|
|
160
|
+
- The conversation_mutation callback (if provided) is executed
|
|
161
|
+
- The agent returns without calling the LLM
|
|
162
|
+
|
|
163
|
+
When should_skip_llm=False:
|
|
164
|
+
- The message is added to conversation history
|
|
165
|
+
- Normal agent processing continues (LLM call, tool execution, etc.)
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
async def try_handle(self, agent, user, conversation, message):
|
|
169
|
+
# Pattern matching with tool execution
|
|
170
|
+
if message.startswith("/report"):
|
|
171
|
+
# Execute tool from registry
|
|
172
|
+
tool = await agent.tool_registry.get_tool("generate_sales_report")
|
|
173
|
+
context = ToolContext(user=user, conversation=conversation)
|
|
174
|
+
result = await tool.execute(context, {})
|
|
175
|
+
|
|
176
|
+
return WorkflowResult(
|
|
177
|
+
should_skip_llm=True,
|
|
178
|
+
components=[...]
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# State-based workflow
|
|
182
|
+
if user.metadata.get("needs_onboarding"):
|
|
183
|
+
return await self._onboarding_flow(agent, user, message)
|
|
184
|
+
|
|
185
|
+
# Permission check
|
|
186
|
+
if message.startswith("/admin") and "admin" not in user.permissions:
|
|
187
|
+
return WorkflowResult(
|
|
188
|
+
should_skip_llm=True,
|
|
189
|
+
components=[RichTextComponent(content="Access denied.")]
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Continue to agent
|
|
193
|
+
return WorkflowResult(should_skip_llm=False)
|
|
194
|
+
"""
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
async def get_starter_ui(
|
|
198
|
+
self, agent: "Agent", user: "User", conversation: "Conversation"
|
|
199
|
+
) -> Optional[List["UiComponent"]]:
|
|
200
|
+
"""Provide UI components when a conversation starts.
|
|
201
|
+
|
|
202
|
+
Override this method to show starter buttons, welcome messages,
|
|
203
|
+
or quick actions when a new chat is opened by the user.
|
|
204
|
+
|
|
205
|
+
This is called by the frontend/server when initializing a new
|
|
206
|
+
conversation, before any user messages are sent.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
agent: The agent instance, providing access to tool_registry, config,
|
|
210
|
+
and observability_provider for dynamic UI generation.
|
|
211
|
+
user: The user starting the conversation
|
|
212
|
+
conversation: The new conversation (typically empty)
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
List of UI components to display, or None for no starter UI.
|
|
216
|
+
Components can include buttons, welcome text, quick actions, etc.
|
|
217
|
+
|
|
218
|
+
Example:
|
|
219
|
+
async def get_starter_ui(self, agent, user, conversation):
|
|
220
|
+
# Show role-based quick actions
|
|
221
|
+
if "analyst" in user.permissions:
|
|
222
|
+
# Dynamically generate buttons based on available tools
|
|
223
|
+
report_tools = [
|
|
224
|
+
tool for tool in agent.tool_registry.list_tools()
|
|
225
|
+
if tool.startswith("report_")
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
buttons = [
|
|
229
|
+
ButtonComponent(label=f"📊 {tool}", value=f"/{tool}")
|
|
230
|
+
for tool in report_tools
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
return [
|
|
234
|
+
RichTextComponent(
|
|
235
|
+
content=f"Welcome back, {user.username}!",
|
|
236
|
+
markdown=True
|
|
237
|
+
),
|
|
238
|
+
*buttons
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
# New user onboarding
|
|
242
|
+
if user.metadata.get("is_new_user"):
|
|
243
|
+
return [
|
|
244
|
+
RichTextComponent(
|
|
245
|
+
content="# Welcome to Vanna!\\n\\nTry one of these to get started:",
|
|
246
|
+
markdown=True
|
|
247
|
+
),
|
|
248
|
+
ButtonComponent(label="Show Example Query", value="/example"),
|
|
249
|
+
ButtonComponent(label="View Tutorial", value="/tutorial"),
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
return None
|
|
253
|
+
"""
|
|
254
|
+
return None
|