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.
Files changed (302) hide show
  1. vanna/__init__.py +167 -395
  2. vanna/agents/__init__.py +7 -0
  3. vanna/capabilities/__init__.py +17 -0
  4. vanna/capabilities/agent_memory/__init__.py +21 -0
  5. vanna/capabilities/agent_memory/base.py +103 -0
  6. vanna/capabilities/agent_memory/models.py +53 -0
  7. vanna/capabilities/file_system/__init__.py +14 -0
  8. vanna/capabilities/file_system/base.py +71 -0
  9. vanna/capabilities/file_system/models.py +25 -0
  10. vanna/capabilities/sql_runner/__init__.py +13 -0
  11. vanna/capabilities/sql_runner/base.py +37 -0
  12. vanna/capabilities/sql_runner/models.py +13 -0
  13. vanna/components/__init__.py +92 -0
  14. vanna/components/base.py +11 -0
  15. vanna/components/rich/__init__.py +83 -0
  16. vanna/components/rich/containers/__init__.py +7 -0
  17. vanna/components/rich/containers/card.py +20 -0
  18. vanna/components/rich/data/__init__.py +9 -0
  19. vanna/components/rich/data/chart.py +17 -0
  20. vanna/components/rich/data/dataframe.py +93 -0
  21. vanna/components/rich/feedback/__init__.py +21 -0
  22. vanna/components/rich/feedback/badge.py +16 -0
  23. vanna/components/rich/feedback/icon_text.py +14 -0
  24. vanna/components/rich/feedback/log_viewer.py +41 -0
  25. vanna/components/rich/feedback/notification.py +19 -0
  26. vanna/components/rich/feedback/progress.py +37 -0
  27. vanna/components/rich/feedback/status_card.py +28 -0
  28. vanna/components/rich/feedback/status_indicator.py +14 -0
  29. vanna/components/rich/interactive/__init__.py +21 -0
  30. vanna/components/rich/interactive/button.py +95 -0
  31. vanna/components/rich/interactive/task_list.py +58 -0
  32. vanna/components/rich/interactive/ui_state.py +93 -0
  33. vanna/components/rich/specialized/__init__.py +7 -0
  34. vanna/components/rich/specialized/artifact.py +20 -0
  35. vanna/components/rich/text.py +16 -0
  36. vanna/components/simple/__init__.py +15 -0
  37. vanna/components/simple/image.py +15 -0
  38. vanna/components/simple/link.py +15 -0
  39. vanna/components/simple/text.py +11 -0
  40. vanna/core/__init__.py +193 -0
  41. vanna/core/_compat.py +19 -0
  42. vanna/core/agent/__init__.py +10 -0
  43. vanna/core/agent/agent.py +1407 -0
  44. vanna/core/agent/config.py +123 -0
  45. vanna/core/audit/__init__.py +28 -0
  46. vanna/core/audit/base.py +299 -0
  47. vanna/core/audit/models.py +131 -0
  48. vanna/core/component_manager.py +329 -0
  49. vanna/core/components.py +53 -0
  50. vanna/core/enhancer/__init__.py +11 -0
  51. vanna/core/enhancer/base.py +94 -0
  52. vanna/core/enhancer/default.py +118 -0
  53. vanna/core/enricher/__init__.py +10 -0
  54. vanna/core/enricher/base.py +59 -0
  55. vanna/core/errors.py +47 -0
  56. vanna/core/evaluation/__init__.py +81 -0
  57. vanna/core/evaluation/base.py +186 -0
  58. vanna/core/evaluation/dataset.py +254 -0
  59. vanna/core/evaluation/evaluators.py +376 -0
  60. vanna/core/evaluation/report.py +289 -0
  61. vanna/core/evaluation/runner.py +313 -0
  62. vanna/core/filter/__init__.py +10 -0
  63. vanna/core/filter/base.py +67 -0
  64. vanna/core/lifecycle/__init__.py +10 -0
  65. vanna/core/lifecycle/base.py +83 -0
  66. vanna/core/llm/__init__.py +16 -0
  67. vanna/core/llm/base.py +40 -0
  68. vanna/core/llm/models.py +61 -0
  69. vanna/core/middleware/__init__.py +10 -0
  70. vanna/core/middleware/base.py +69 -0
  71. vanna/core/observability/__init__.py +11 -0
  72. vanna/core/observability/base.py +88 -0
  73. vanna/core/observability/models.py +47 -0
  74. vanna/core/recovery/__init__.py +11 -0
  75. vanna/core/recovery/base.py +84 -0
  76. vanna/core/recovery/models.py +32 -0
  77. vanna/core/registry.py +278 -0
  78. vanna/core/rich_component.py +156 -0
  79. vanna/core/simple_component.py +27 -0
  80. vanna/core/storage/__init__.py +14 -0
  81. vanna/core/storage/base.py +46 -0
  82. vanna/core/storage/models.py +46 -0
  83. vanna/core/system_prompt/__init__.py +13 -0
  84. vanna/core/system_prompt/base.py +36 -0
  85. vanna/core/system_prompt/default.py +157 -0
  86. vanna/core/tool/__init__.py +18 -0
  87. vanna/core/tool/base.py +70 -0
  88. vanna/core/tool/models.py +84 -0
  89. vanna/core/user/__init__.py +17 -0
  90. vanna/core/user/base.py +29 -0
  91. vanna/core/user/models.py +25 -0
  92. vanna/core/user/request_context.py +70 -0
  93. vanna/core/user/resolver.py +42 -0
  94. vanna/core/validation.py +164 -0
  95. vanna/core/workflow/__init__.py +12 -0
  96. vanna/core/workflow/base.py +254 -0
  97. vanna/core/workflow/default.py +789 -0
  98. vanna/examples/__init__.py +1 -0
  99. vanna/examples/__main__.py +44 -0
  100. vanna/examples/anthropic_quickstart.py +80 -0
  101. vanna/examples/artifact_example.py +293 -0
  102. vanna/examples/claude_sqlite_example.py +236 -0
  103. vanna/examples/coding_agent_example.py +300 -0
  104. vanna/examples/custom_system_prompt_example.py +174 -0
  105. vanna/examples/default_workflow_handler_example.py +208 -0
  106. vanna/examples/email_auth_example.py +340 -0
  107. vanna/examples/evaluation_example.py +269 -0
  108. vanna/examples/extensibility_example.py +262 -0
  109. vanna/examples/minimal_example.py +67 -0
  110. vanna/examples/mock_auth_example.py +227 -0
  111. vanna/examples/mock_custom_tool.py +311 -0
  112. vanna/examples/mock_quickstart.py +79 -0
  113. vanna/examples/mock_quota_example.py +145 -0
  114. vanna/examples/mock_rich_components_demo.py +396 -0
  115. vanna/examples/mock_sqlite_example.py +223 -0
  116. vanna/examples/openai_quickstart.py +83 -0
  117. vanna/examples/primitive_components_demo.py +305 -0
  118. vanna/examples/quota_lifecycle_example.py +139 -0
  119. vanna/examples/visualization_example.py +251 -0
  120. vanna/integrations/__init__.py +17 -0
  121. vanna/integrations/anthropic/__init__.py +9 -0
  122. vanna/integrations/anthropic/llm.py +270 -0
  123. vanna/integrations/azureopenai/__init__.py +9 -0
  124. vanna/integrations/azureopenai/llm.py +329 -0
  125. vanna/integrations/azuresearch/__init__.py +7 -0
  126. vanna/integrations/azuresearch/agent_memory.py +413 -0
  127. vanna/integrations/bigquery/__init__.py +5 -0
  128. vanna/integrations/bigquery/sql_runner.py +81 -0
  129. vanna/integrations/chromadb/__init__.py +104 -0
  130. vanna/integrations/chromadb/agent_memory.py +416 -0
  131. vanna/integrations/clickhouse/__init__.py +5 -0
  132. vanna/integrations/clickhouse/sql_runner.py +82 -0
  133. vanna/integrations/duckdb/__init__.py +5 -0
  134. vanna/integrations/duckdb/sql_runner.py +65 -0
  135. vanna/integrations/faiss/__init__.py +7 -0
  136. vanna/integrations/faiss/agent_memory.py +431 -0
  137. vanna/integrations/google/__init__.py +9 -0
  138. vanna/integrations/google/gemini.py +370 -0
  139. vanna/integrations/hive/__init__.py +5 -0
  140. vanna/integrations/hive/sql_runner.py +87 -0
  141. vanna/integrations/local/__init__.py +17 -0
  142. vanna/integrations/local/agent_memory/__init__.py +7 -0
  143. vanna/integrations/local/agent_memory/in_memory.py +285 -0
  144. vanna/integrations/local/audit.py +59 -0
  145. vanna/integrations/local/file_system.py +242 -0
  146. vanna/integrations/local/file_system_conversation_store.py +255 -0
  147. vanna/integrations/local/storage.py +62 -0
  148. vanna/integrations/marqo/__init__.py +7 -0
  149. vanna/integrations/marqo/agent_memory.py +354 -0
  150. vanna/integrations/milvus/__init__.py +7 -0
  151. vanna/integrations/milvus/agent_memory.py +458 -0
  152. vanna/integrations/mock/__init__.py +9 -0
  153. vanna/integrations/mock/llm.py +65 -0
  154. vanna/integrations/mssql/__init__.py +5 -0
  155. vanna/integrations/mssql/sql_runner.py +66 -0
  156. vanna/integrations/mysql/__init__.py +5 -0
  157. vanna/integrations/mysql/sql_runner.py +92 -0
  158. vanna/integrations/ollama/__init__.py +7 -0
  159. vanna/integrations/ollama/llm.py +252 -0
  160. vanna/integrations/openai/__init__.py +10 -0
  161. vanna/integrations/openai/llm.py +267 -0
  162. vanna/integrations/openai/responses.py +163 -0
  163. vanna/integrations/opensearch/__init__.py +7 -0
  164. vanna/integrations/opensearch/agent_memory.py +411 -0
  165. vanna/integrations/oracle/__init__.py +5 -0
  166. vanna/integrations/oracle/sql_runner.py +75 -0
  167. vanna/integrations/pinecone/__init__.py +7 -0
  168. vanna/integrations/pinecone/agent_memory.py +329 -0
  169. vanna/integrations/plotly/__init__.py +5 -0
  170. vanna/integrations/plotly/chart_generator.py +313 -0
  171. vanna/integrations/postgres/__init__.py +9 -0
  172. vanna/integrations/postgres/sql_runner.py +112 -0
  173. vanna/integrations/premium/agent_memory/__init__.py +7 -0
  174. vanna/integrations/premium/agent_memory/premium.py +186 -0
  175. vanna/integrations/presto/__init__.py +5 -0
  176. vanna/integrations/presto/sql_runner.py +107 -0
  177. vanna/integrations/qdrant/__init__.py +7 -0
  178. vanna/integrations/qdrant/agent_memory.py +439 -0
  179. vanna/integrations/snowflake/__init__.py +5 -0
  180. vanna/integrations/snowflake/sql_runner.py +147 -0
  181. vanna/integrations/sqlite/__init__.py +9 -0
  182. vanna/integrations/sqlite/sql_runner.py +65 -0
  183. vanna/integrations/weaviate/__init__.py +7 -0
  184. vanna/integrations/weaviate/agent_memory.py +428 -0
  185. vanna/{ZhipuAI → legacy/ZhipuAI}/ZhipuAI_embeddings.py +11 -11
  186. vanna/legacy/__init__.py +403 -0
  187. vanna/legacy/adapter.py +463 -0
  188. vanna/{advanced → legacy/advanced}/__init__.py +3 -1
  189. vanna/{anthropic → legacy/anthropic}/anthropic_chat.py +9 -7
  190. vanna/{azuresearch → legacy/azuresearch}/azuresearch_vector.py +79 -41
  191. vanna/{base → legacy/base}/base.py +224 -217
  192. vanna/legacy/bedrock/__init__.py +1 -0
  193. vanna/{bedrock → legacy/bedrock}/bedrock_converse.py +13 -12
  194. vanna/{chromadb → legacy/chromadb}/chromadb_vector.py +3 -1
  195. vanna/legacy/cohere/__init__.py +2 -0
  196. vanna/{cohere → legacy/cohere}/cohere_chat.py +19 -14
  197. vanna/{cohere → legacy/cohere}/cohere_embeddings.py +25 -19
  198. vanna/{deepseek → legacy/deepseek}/deepseek_chat.py +5 -6
  199. vanna/legacy/faiss/__init__.py +1 -0
  200. vanna/{faiss → legacy/faiss}/faiss.py +113 -59
  201. vanna/{flask → legacy/flask}/__init__.py +84 -43
  202. vanna/{flask → legacy/flask}/assets.py +5 -5
  203. vanna/{flask → legacy/flask}/auth.py +5 -4
  204. vanna/{google → legacy/google}/bigquery_vector.py +75 -42
  205. vanna/{google → legacy/google}/gemini_chat.py +7 -3
  206. vanna/{hf → legacy/hf}/hf.py +0 -1
  207. vanna/{milvus → legacy/milvus}/milvus_vector.py +58 -35
  208. vanna/{mock → legacy/mock}/llm.py +0 -1
  209. vanna/legacy/mock/vectordb.py +67 -0
  210. vanna/legacy/ollama/ollama.py +110 -0
  211. vanna/{openai → legacy/openai}/openai_chat.py +2 -6
  212. vanna/legacy/opensearch/opensearch_vector.py +369 -0
  213. vanna/legacy/opensearch/opensearch_vector_semantic.py +200 -0
  214. vanna/legacy/oracle/oracle_vector.py +584 -0
  215. vanna/{pgvector → legacy/pgvector}/pgvector.py +42 -13
  216. vanna/{qdrant → legacy/qdrant}/qdrant.py +2 -6
  217. vanna/legacy/qianfan/Qianfan_Chat.py +170 -0
  218. vanna/legacy/qianfan/Qianfan_embeddings.py +36 -0
  219. vanna/legacy/qianwen/QianwenAI_chat.py +132 -0
  220. vanna/{remote.py → legacy/remote.py} +28 -26
  221. vanna/{utils.py → legacy/utils.py} +6 -11
  222. vanna/{vannadb → legacy/vannadb}/vannadb_vector.py +115 -46
  223. vanna/{vllm → legacy/vllm}/vllm.py +5 -6
  224. vanna/{weaviate → legacy/weaviate}/weaviate_vector.py +59 -40
  225. vanna/{xinference → legacy/xinference}/xinference.py +6 -6
  226. vanna/py.typed +0 -0
  227. vanna/servers/__init__.py +16 -0
  228. vanna/servers/__main__.py +8 -0
  229. vanna/servers/base/__init__.py +18 -0
  230. vanna/servers/base/chat_handler.py +65 -0
  231. vanna/servers/base/models.py +111 -0
  232. vanna/servers/base/rich_chat_handler.py +141 -0
  233. vanna/servers/base/templates.py +331 -0
  234. vanna/servers/cli/__init__.py +7 -0
  235. vanna/servers/cli/server_runner.py +204 -0
  236. vanna/servers/fastapi/__init__.py +7 -0
  237. vanna/servers/fastapi/app.py +163 -0
  238. vanna/servers/fastapi/routes.py +183 -0
  239. vanna/servers/flask/__init__.py +7 -0
  240. vanna/servers/flask/app.py +132 -0
  241. vanna/servers/flask/routes.py +137 -0
  242. vanna/tools/__init__.py +41 -0
  243. vanna/tools/agent_memory.py +322 -0
  244. vanna/tools/file_system.py +879 -0
  245. vanna/tools/python.py +222 -0
  246. vanna/tools/run_sql.py +165 -0
  247. vanna/tools/visualize_data.py +195 -0
  248. vanna/utils/__init__.py +0 -0
  249. vanna/web_components/__init__.py +44 -0
  250. vanna-2.0.0rc1.dist-info/METADATA +868 -0
  251. vanna-2.0.0rc1.dist-info/RECORD +289 -0
  252. vanna-2.0.0rc1.dist-info/entry_points.txt +3 -0
  253. vanna/bedrock/__init__.py +0 -1
  254. vanna/cohere/__init__.py +0 -2
  255. vanna/faiss/__init__.py +0 -1
  256. vanna/mock/vectordb.py +0 -55
  257. vanna/ollama/ollama.py +0 -103
  258. vanna/opensearch/opensearch_vector.py +0 -392
  259. vanna/opensearch/opensearch_vector_semantic.py +0 -175
  260. vanna/oracle/oracle_vector.py +0 -585
  261. vanna/qianfan/Qianfan_Chat.py +0 -165
  262. vanna/qianfan/Qianfan_embeddings.py +0 -36
  263. vanna/qianwen/QianwenAI_chat.py +0 -133
  264. vanna-0.7.9.dist-info/METADATA +0 -408
  265. vanna-0.7.9.dist-info/RECORD +0 -79
  266. /vanna/{ZhipuAI → legacy/ZhipuAI}/ZhipuAI_Chat.py +0 -0
  267. /vanna/{ZhipuAI → legacy/ZhipuAI}/__init__.py +0 -0
  268. /vanna/{anthropic → legacy/anthropic}/__init__.py +0 -0
  269. /vanna/{azuresearch → legacy/azuresearch}/__init__.py +0 -0
  270. /vanna/{base → legacy/base}/__init__.py +0 -0
  271. /vanna/{chromadb → legacy/chromadb}/__init__.py +0 -0
  272. /vanna/{deepseek → legacy/deepseek}/__init__.py +0 -0
  273. /vanna/{exceptions → legacy/exceptions}/__init__.py +0 -0
  274. /vanna/{google → legacy/google}/__init__.py +0 -0
  275. /vanna/{hf → legacy/hf}/__init__.py +0 -0
  276. /vanna/{local.py → legacy/local.py} +0 -0
  277. /vanna/{marqo → legacy/marqo}/__init__.py +0 -0
  278. /vanna/{marqo → legacy/marqo}/marqo.py +0 -0
  279. /vanna/{milvus → legacy/milvus}/__init__.py +0 -0
  280. /vanna/{mistral → legacy/mistral}/__init__.py +0 -0
  281. /vanna/{mistral → legacy/mistral}/mistral.py +0 -0
  282. /vanna/{mock → legacy/mock}/__init__.py +0 -0
  283. /vanna/{mock → legacy/mock}/embedding.py +0 -0
  284. /vanna/{ollama → legacy/ollama}/__init__.py +0 -0
  285. /vanna/{openai → legacy/openai}/__init__.py +0 -0
  286. /vanna/{openai → legacy/openai}/openai_embeddings.py +0 -0
  287. /vanna/{opensearch → legacy/opensearch}/__init__.py +0 -0
  288. /vanna/{oracle → legacy/oracle}/__init__.py +0 -0
  289. /vanna/{pgvector → legacy/pgvector}/__init__.py +0 -0
  290. /vanna/{pinecone → legacy/pinecone}/__init__.py +0 -0
  291. /vanna/{pinecone → legacy/pinecone}/pinecone_vector.py +0 -0
  292. /vanna/{qdrant → legacy/qdrant}/__init__.py +0 -0
  293. /vanna/{qianfan → legacy/qianfan}/__init__.py +0 -0
  294. /vanna/{qianwen → legacy/qianwen}/QianwenAI_embeddings.py +0 -0
  295. /vanna/{qianwen → legacy/qianwen}/__init__.py +0 -0
  296. /vanna/{types → legacy/types}/__init__.py +0 -0
  297. /vanna/{vannadb → legacy/vannadb}/__init__.py +0 -0
  298. /vanna/{vllm → legacy/vllm}/__init__.py +0 -0
  299. /vanna/{weaviate → legacy/weaviate}/__init__.py +0 -0
  300. /vanna/{xinference → legacy/xinference}/__init__.py +0 -0
  301. {vanna-0.7.9.dist-info → vanna-2.0.0rc1.dist-info}/WHEEL +0 -0
  302. {vanna-0.7.9.dist-info → vanna-2.0.0rc1.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
@@ -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