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.
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 +461 -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.0.dist-info/METADATA +485 -0
  251. vanna-2.0.0.dist-info/RECORD +289 -0
  252. vanna-2.0.0.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.0.dist-info}/WHEEL +0 -0
  302. {vanna-0.7.9.dist-info → vanna-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,204 @@
1
+ """
2
+ CLI for running Vanna Agents servers with example agents.
3
+ """
4
+
5
+ import importlib
6
+ import json
7
+ from typing import Dict, Optional, Any, cast, TextIO, Union
8
+
9
+ import click
10
+
11
+ from ...core import Agent
12
+
13
+
14
+ class ExampleAgentLoader:
15
+ """Loads example agents for the CLI."""
16
+
17
+ @staticmethod
18
+ def list_available_examples() -> Dict[str, str]:
19
+ """Return available examples with descriptions."""
20
+ return {
21
+ "mock_quickstart": "Basic agent with mock LLM service",
22
+ "anthropic_quickstart": "Agent configured for Anthropic's Claude API",
23
+ "openai_quickstart": "Agent configured for OpenAI's GPT models",
24
+ "mock_custom_tool": "Agent with custom tool demonstration (mock LLM)",
25
+ "mock_quota_example": "Agent with usage quota management (mock LLM)",
26
+ "mock_rich_components_demo": "Rich components demonstration with cards, tasks, and progress (mock LLM)",
27
+ "coding_agent_example": "Coding agent with file system tools (list, read, write files)",
28
+ "email_auth_example": "Email-based authentication demonstration (mock LLM)",
29
+ "claude_sqlite_example": "Claude agent with SQLite database querying capabilities",
30
+ "mock_sqlite_example": "Mock agent with SQLite database demonstration",
31
+ }
32
+
33
+ @staticmethod
34
+ def load_example_agent(example_name: str) -> Agent:
35
+ """Load an example agent by name.
36
+
37
+ Args:
38
+ example_name: Name of the example to load
39
+
40
+ Returns:
41
+ Configured agent instance
42
+
43
+ Raises:
44
+ ValueError: If example not found or failed to load
45
+ """
46
+ try:
47
+ # Import the example module
48
+ module = importlib.import_module(f"vanna.examples.{example_name}")
49
+
50
+ # Look for standard factory functions
51
+ factory_functions = [
52
+ "create_demo_agent",
53
+ "create_agent",
54
+ "create_basic_demo",
55
+ ]
56
+
57
+ for func_name in factory_functions:
58
+ if hasattr(module, func_name):
59
+ factory = getattr(module, func_name)
60
+ return cast(Agent, factory())
61
+
62
+ # Look for module-level agent instances
63
+ if hasattr(module, "main_agent"):
64
+ return cast(Agent, module.main_agent)
65
+
66
+ raise AttributeError(f"No agent factory found in {example_name}")
67
+
68
+ except ImportError as e:
69
+ raise ValueError(f"Example '{example_name}' not found: {e}")
70
+ except Exception as e:
71
+ raise ValueError(f"Failed to load example '{example_name}': {e}")
72
+
73
+
74
+ @click.command()
75
+ @click.option(
76
+ "--framework",
77
+ type=click.Choice(["flask", "fastapi"]),
78
+ default="fastapi",
79
+ help="Web framework to use",
80
+ )
81
+ @click.option("--port", default=8000, help="Port to run server on")
82
+ @click.option("--host", default="0.0.0.0", help="Host to bind server to")
83
+ @click.option(
84
+ "--example", help="Example agent to use (use --list-examples to see options)"
85
+ )
86
+ @click.option("--list-examples", is_flag=True, help="List available example agents")
87
+ @click.option(
88
+ "--config", type=click.File("r"), help="JSON config file for server settings"
89
+ )
90
+ @click.option("--debug", is_flag=True, help="Enable debug mode")
91
+ @click.option(
92
+ "--dev",
93
+ is_flag=True,
94
+ help="Enable development mode (load components from local assets)",
95
+ )
96
+ @click.option(
97
+ "--static-folder", default=None, help="Static folder path for development mode"
98
+ )
99
+ @click.option(
100
+ "--cdn-url",
101
+ default="https://img.vanna.ai/vanna-components.js",
102
+ help="CDN URL for web components",
103
+ )
104
+ def main(
105
+ framework: str,
106
+ port: int,
107
+ host: str,
108
+ example: Optional[str],
109
+ list_examples: bool,
110
+ config: Optional[click.File],
111
+ debug: bool,
112
+ dev: bool,
113
+ static_folder: Optional[str],
114
+ cdn_url: str,
115
+ ) -> None:
116
+ """Run Vanna Agents server with optional example agent."""
117
+
118
+ if list_examples:
119
+ click.echo("Available example agents:")
120
+ examples = ExampleAgentLoader.list_available_examples()
121
+ for name, description in examples.items():
122
+ click.echo(f" {name:20} - {description}")
123
+ return
124
+
125
+ # Load configuration
126
+ server_config = {}
127
+ if config:
128
+ server_config = json.load(cast(TextIO, config))
129
+
130
+ # Set default static folder based on dev mode
131
+ if static_folder is None:
132
+ static_folder = "frontend/webcomponent/static" if dev else "static"
133
+
134
+ # Add CLI options to config
135
+ server_config.update(
136
+ {
137
+ "dev_mode": dev,
138
+ "static_folder": static_folder,
139
+ "cdn_url": cdn_url,
140
+ "api_base_url": "", # Can be overridden in config file
141
+ }
142
+ )
143
+
144
+ # Create agent
145
+ if example:
146
+ try:
147
+ agent = ExampleAgentLoader.load_example_agent(example)
148
+ click.echo(f"✓ Loaded example agent: {example}")
149
+ except ValueError as e:
150
+ click.echo(f"Error: {e}", err=True)
151
+ return
152
+ else:
153
+ # Fallback to basic agent
154
+ try:
155
+ from ...agents import create_basic_agent
156
+ from ...integrations.mock import MockLlmService
157
+
158
+ llm_service = MockLlmService(
159
+ response_content="Hello! I'm a Vanna Agents demo server. How can I help you?"
160
+ )
161
+ agent = create_basic_agent(llm_service)
162
+ click.echo(
163
+ "✓ Using basic demo agent (use --example to specify different agent)"
164
+ )
165
+ except ImportError as e:
166
+ click.echo(f"Error: Could not create basic agent: {e}", err=True)
167
+ return
168
+
169
+ from ..flask.app import VannaFlaskServer
170
+ from ..fastapi.app import VannaFastAPIServer
171
+
172
+ # Create and run server
173
+ server: Union[VannaFlaskServer, VannaFastAPIServer]
174
+ if framework == "flask":
175
+ server = VannaFlaskServer(agent, config=server_config)
176
+ click.echo(f"🚀 Starting Flask server on http://{host}:{port}")
177
+ if dev:
178
+ click.echo(
179
+ f"📦 Development mode: loading web components from ./{static_folder}/"
180
+ )
181
+ else:
182
+ click.echo(f"🌍 Production mode: loading web components from CDN")
183
+ try:
184
+ server.run(host=host, port=port, debug=debug)
185
+ except KeyboardInterrupt:
186
+ click.echo("\n👋 Server stopped")
187
+ else:
188
+ server = VannaFastAPIServer(agent, config=server_config)
189
+ click.echo(f"🚀 Starting FastAPI server on http://{host}:{port}")
190
+ click.echo(f"📖 API docs available at http://{host}:{port}/docs")
191
+ if dev:
192
+ click.echo(
193
+ f"📦 Development mode: loading web components from ./{static_folder}/"
194
+ )
195
+ else:
196
+ click.echo(f"🌍 Production mode: loading web components from CDN")
197
+ try:
198
+ server.run(host=host, port=port)
199
+ except KeyboardInterrupt:
200
+ click.echo("\n👋 Server stopped")
201
+
202
+
203
+ if __name__ == "__main__":
204
+ main()
@@ -0,0 +1,7 @@
1
+ """
2
+ FastAPI server implementation for Vanna Agents.
3
+ """
4
+
5
+ from .app import VannaFastAPIServer
6
+
7
+ __all__ = ["VannaFastAPIServer"]
@@ -0,0 +1,163 @@
1
+ """
2
+ FastAPI server factory for Vanna Agents.
3
+ """
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from fastapi import FastAPI
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from fastapi.staticfiles import StaticFiles
10
+
11
+ from ...core import Agent
12
+ from ..base import ChatHandler
13
+ from .routes import register_chat_routes
14
+
15
+
16
+ class VannaFastAPIServer:
17
+ """FastAPI server factory for Vanna Agents."""
18
+
19
+ def __init__(self, agent: Agent, config: Optional[Dict[str, Any]] = None):
20
+ """Initialize FastAPI server.
21
+
22
+ Args:
23
+ agent: The agent to serve (must have user_resolver configured)
24
+ config: Optional server configuration
25
+ """
26
+ self.agent = agent
27
+ self.config = config or {}
28
+ self.chat_handler = ChatHandler(agent)
29
+
30
+ def create_app(self) -> FastAPI:
31
+ """Create configured FastAPI app.
32
+
33
+ Returns:
34
+ Configured FastAPI application
35
+ """
36
+ # Create FastAPI app
37
+ app_config = self.config.get("fastapi", {})
38
+ app = FastAPI(
39
+ title="Vanna Agents API",
40
+ description="API server for Vanna Agents framework",
41
+ version="0.1.0",
42
+ **app_config,
43
+ )
44
+
45
+ # Configure CORS if enabled
46
+ cors_config = self.config.get("cors", {})
47
+ if cors_config.get("enabled", True):
48
+ cors_params = {k: v for k, v in cors_config.items() if k != "enabled"}
49
+
50
+ # Set sensible defaults
51
+ cors_params.setdefault("allow_origins", ["*"])
52
+ cors_params.setdefault("allow_credentials", True)
53
+ cors_params.setdefault("allow_methods", ["*"])
54
+ cors_params.setdefault("allow_headers", ["*"])
55
+
56
+ app.add_middleware(CORSMiddleware, **cors_params)
57
+
58
+ # Add static file serving in dev mode
59
+ dev_mode = self.config.get("dev_mode", False)
60
+ if dev_mode:
61
+ static_folder = self.config.get("static_folder", "static")
62
+ try:
63
+ import os
64
+
65
+ if os.path.exists(static_folder):
66
+ app.mount(
67
+ "/static", StaticFiles(directory=static_folder), name="static"
68
+ )
69
+ except Exception:
70
+ pass # Static files not available
71
+
72
+ # Register routes
73
+ register_chat_routes(app, self.chat_handler, self.config)
74
+
75
+ # Add health check
76
+ @app.get("/health")
77
+ async def health_check() -> Dict[str, str]:
78
+ return {"status": "healthy", "service": "vanna"}
79
+
80
+ return app
81
+
82
+ def run(self, **kwargs: Any) -> None:
83
+ """Run the FastAPI server.
84
+
85
+ This method automatically detects if running in an async environment
86
+ (Jupyter, Colab, IPython, etc.) and:
87
+ - Uses appropriate async handling for existing event loops
88
+ - Sets up port forwarding if in Google Colab
89
+ - Displays the correct URL for accessing the app
90
+
91
+ Args:
92
+ **kwargs: Arguments passed to uvicorn configuration
93
+ """
94
+ import sys
95
+ import asyncio
96
+ import uvicorn
97
+
98
+ # Check if we're in an environment with a running event loop FIRST
99
+ in_async_env = False
100
+ try:
101
+ asyncio.get_running_loop()
102
+ in_async_env = True
103
+ except RuntimeError:
104
+ in_async_env = False
105
+
106
+ # If in async environment, apply nest_asyncio BEFORE creating the app
107
+ if in_async_env:
108
+ try:
109
+ import nest_asyncio
110
+
111
+ nest_asyncio.apply()
112
+ except ImportError:
113
+ print("Warning: nest_asyncio not installed. Installing...")
114
+ import subprocess
115
+
116
+ subprocess.check_call(
117
+ [sys.executable, "-m", "pip", "install", "nest_asyncio"]
118
+ )
119
+ import nest_asyncio
120
+
121
+ nest_asyncio.apply()
122
+
123
+ # Now create the app after nest_asyncio is applied
124
+ app = self.create_app()
125
+
126
+ # Set defaults
127
+ run_kwargs = {"host": "0.0.0.0", "port": 8000, "log_level": "info", **kwargs}
128
+
129
+ # Get the port and other config from run_kwargs
130
+ port = run_kwargs.get("port", 8000)
131
+ host = run_kwargs.get("host", "0.0.0.0")
132
+ log_level = run_kwargs.get("log_level", "info")
133
+
134
+ # Check if we're specifically in Google Colab for port forwarding
135
+ in_colab = "google.colab" in sys.modules
136
+
137
+ if in_colab:
138
+ try:
139
+ from google.colab import output
140
+
141
+ output.serve_kernel_port_as_window(port)
142
+ from google.colab.output import eval_js
143
+
144
+ print("Your app is running at:")
145
+ print(eval_js(f"google.colab.kernel.proxyPort({port})"))
146
+ except Exception as e:
147
+ print(f"Warning: Could not set up Colab port forwarding: {e}")
148
+ print(f"Your app is running at: http://localhost:{port}")
149
+ else:
150
+ print("Your app is running at:")
151
+ print(f"http://localhost:{port}")
152
+
153
+ if in_async_env:
154
+ # In Jupyter/Colab, create config with loop="asyncio" and use asyncio.run()
155
+ # This matches the working pattern from Colab
156
+ config = uvicorn.Config(
157
+ app, host=host, port=port, log_level=log_level, loop="asyncio"
158
+ )
159
+ server = uvicorn.Server(config)
160
+ asyncio.run(server.serve())
161
+ else:
162
+ # Normal execution outside of Jupyter/Colab
163
+ uvicorn.run(app, **run_kwargs)
@@ -0,0 +1,183 @@
1
+ """
2
+ FastAPI route implementations for Vanna Agents.
3
+ """
4
+
5
+ import json
6
+ import traceback
7
+ from typing import Any, AsyncGenerator, Dict, Optional
8
+
9
+ from fastapi import FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect
10
+ from fastapi.responses import StreamingResponse, HTMLResponse
11
+
12
+ from ..base import ChatHandler, ChatRequest, ChatResponse
13
+ from ..base.templates import get_index_html
14
+ from ...core.user.request_context import RequestContext
15
+
16
+
17
+ def register_chat_routes(
18
+ app: FastAPI, chat_handler: ChatHandler, config: Optional[Dict[str, Any]] = None
19
+ ) -> None:
20
+ """Register chat routes on FastAPI app.
21
+
22
+ Args:
23
+ app: FastAPI application
24
+ chat_handler: Chat handler instance
25
+ config: Server configuration
26
+ """
27
+ config = config or {}
28
+
29
+ @app.get("/", response_class=HTMLResponse)
30
+ async def index() -> str:
31
+ """Serve the main chat interface."""
32
+ dev_mode = config.get("dev_mode", False)
33
+ cdn_url = config.get("cdn_url", "https://img.vanna.ai/vanna-components.js")
34
+ api_base_url = config.get("api_base_url", "")
35
+
36
+ return get_index_html(
37
+ dev_mode=dev_mode, cdn_url=cdn_url, api_base_url=api_base_url
38
+ )
39
+
40
+ @app.post("/api/vanna/v2/chat_sse")
41
+ async def chat_sse(
42
+ chat_request: ChatRequest, http_request: Request
43
+ ) -> StreamingResponse:
44
+ """Server-Sent Events endpoint for streaming chat."""
45
+ # Extract request context for user resolution
46
+ chat_request.request_context = RequestContext(
47
+ cookies=dict(http_request.cookies),
48
+ headers=dict(http_request.headers),
49
+ remote_addr=http_request.client.host if http_request.client else None,
50
+ query_params=dict(http_request.query_params),
51
+ metadata=chat_request.metadata,
52
+ )
53
+
54
+ async def generate() -> AsyncGenerator[str, None]:
55
+ """Generate SSE stream."""
56
+ try:
57
+ async for chunk in chat_handler.handle_stream(chat_request):
58
+ chunk_json = chunk.model_dump_json()
59
+ yield f"data: {chunk_json}\n\n"
60
+ yield "data: [DONE]\n\n"
61
+ except Exception as e:
62
+ traceback.print_stack()
63
+ traceback.print_exc()
64
+ error_data = {
65
+ "type": "error",
66
+ "data": {"message": str(e)},
67
+ "conversation_id": chat_request.conversation_id or "",
68
+ "request_id": chat_request.request_id or "",
69
+ }
70
+ yield f"data: {json.dumps(error_data)}\n\n"
71
+
72
+ return StreamingResponse(
73
+ generate(),
74
+ media_type="text/event-stream",
75
+ headers={
76
+ "Cache-Control": "no-cache",
77
+ "Connection": "keep-alive",
78
+ "X-Accel-Buffering": "no", # Disable nginx buffering
79
+ },
80
+ )
81
+
82
+ @app.websocket("/api/vanna/v2/chat_websocket")
83
+ async def chat_websocket(websocket: WebSocket) -> None:
84
+ """WebSocket endpoint for real-time chat."""
85
+ await websocket.accept()
86
+
87
+ try:
88
+ while True:
89
+ # Receive message
90
+ try:
91
+ data = await websocket.receive_json()
92
+
93
+ # Extract request context for user resolution
94
+ metadata = data.get("metadata", {})
95
+ data["request_context"] = RequestContext(
96
+ cookies=dict(websocket.cookies),
97
+ headers=dict(websocket.headers),
98
+ remote_addr=websocket.client.host if websocket.client else None,
99
+ query_params=dict(websocket.query_params),
100
+ metadata=metadata,
101
+ )
102
+
103
+ chat_request = ChatRequest(**data)
104
+ except Exception as e:
105
+ traceback.print_stack()
106
+ traceback.print_exc()
107
+ await websocket.send_json(
108
+ {
109
+ "type": "error",
110
+ "data": {"message": f"Invalid request: {str(e)}"},
111
+ }
112
+ )
113
+ continue
114
+
115
+ # Stream response
116
+ try:
117
+ async for chunk in chat_handler.handle_stream(chat_request):
118
+ await websocket.send_json(chunk.model_dump())
119
+
120
+ # Send completion signal
121
+ await websocket.send_json(
122
+ {
123
+ "type": "completion",
124
+ "data": {"status": "done"},
125
+ "conversation_id": chunk.conversation_id
126
+ if "chunk" in locals()
127
+ else "",
128
+ "request_id": chunk.request_id
129
+ if "chunk" in locals()
130
+ else "",
131
+ }
132
+ )
133
+
134
+ except Exception as e:
135
+ traceback.print_stack()
136
+ traceback.print_exc()
137
+ await websocket.send_json(
138
+ {
139
+ "type": "error",
140
+ "data": {"message": str(e)},
141
+ "conversation_id": chat_request.conversation_id or "",
142
+ "request_id": chat_request.request_id or "",
143
+ }
144
+ )
145
+
146
+ except WebSocketDisconnect:
147
+ pass
148
+ except Exception as e:
149
+ traceback.print_stack()
150
+ traceback.print_exc()
151
+ try:
152
+ await websocket.send_json(
153
+ {
154
+ "type": "error",
155
+ "data": {"message": f"WebSocket error: {str(e)}"},
156
+ }
157
+ )
158
+ except Exception:
159
+ pass
160
+ finally:
161
+ await websocket.close()
162
+
163
+ @app.post("/api/vanna/v2/chat_poll")
164
+ async def chat_poll(
165
+ chat_request: ChatRequest, http_request: Request
166
+ ) -> ChatResponse:
167
+ """Polling endpoint for chat."""
168
+ # Extract request context for user resolution
169
+ chat_request.request_context = RequestContext(
170
+ cookies=dict(http_request.cookies),
171
+ headers=dict(http_request.headers),
172
+ remote_addr=http_request.client.host if http_request.client else None,
173
+ query_params=dict(http_request.query_params),
174
+ metadata=chat_request.metadata,
175
+ )
176
+
177
+ try:
178
+ result = await chat_handler.handle_poll(chat_request)
179
+ return result
180
+ except Exception as e:
181
+ traceback.print_stack()
182
+ traceback.print_exc()
183
+ raise HTTPException(status_code=500, detail=f"Chat failed: {str(e)}")
@@ -0,0 +1,7 @@
1
+ """
2
+ Flask server implementation for Vanna Agents.
3
+ """
4
+
5
+ from .app import VannaFlaskServer
6
+
7
+ __all__ = ["VannaFlaskServer"]
@@ -0,0 +1,132 @@
1
+ """
2
+ Flask server factory for Vanna Agents.
3
+ """
4
+
5
+ import asyncio
6
+ from typing import Any, Dict, Optional
7
+
8
+ from flask import Flask
9
+ from flask_cors import CORS
10
+
11
+ from ...core import Agent
12
+ from ..base import ChatHandler
13
+ from .routes import register_chat_routes
14
+
15
+
16
+ class VannaFlaskServer:
17
+ """Flask server factory for Vanna Agents."""
18
+
19
+ def __init__(self, agent: Agent, config: Optional[Dict[str, Any]] = None):
20
+ """Initialize Flask server.
21
+
22
+ Args:
23
+ agent: The agent to serve (must have user_resolver configured)
24
+ config: Optional server configuration
25
+ """
26
+ self.agent = agent
27
+ self.config = config or {}
28
+ self.chat_handler = ChatHandler(agent)
29
+
30
+ def create_app(self) -> Flask:
31
+ """Create configured Flask app.
32
+
33
+ Returns:
34
+ Configured Flask application
35
+ """
36
+ # Check if dev mode is enabled
37
+ dev_mode = self.config.get("dev_mode", False)
38
+ static_folder = self.config.get("static_folder", "static") if dev_mode else None
39
+
40
+ app = Flask(__name__, static_folder=static_folder, static_url_path="/static")
41
+
42
+ # Apply configuration
43
+ app.config.update(self.config.get("flask", {}))
44
+
45
+ # Enable CORS if configured
46
+ cors_config = self.config.get("cors", {})
47
+ if cors_config.get("enabled", True):
48
+ CORS(app, **{k: v for k, v in cors_config.items() if k != "enabled"})
49
+
50
+ # Register routes
51
+ register_chat_routes(app, self.chat_handler, self.config)
52
+
53
+ # Add health check
54
+ @app.route("/health")
55
+ def health_check() -> Dict[str, str]:
56
+ return {"status": "healthy", "service": "vanna"}
57
+
58
+ return app
59
+
60
+ def run(self, **kwargs: Any) -> None:
61
+ """Run the Flask server.
62
+
63
+ This method automatically detects if running in an async environment
64
+ (Jupyter, Colab, IPython, etc.) and:
65
+ - Installs and applies nest_asyncio to handle existing event loops
66
+ - Sets up port forwarding if in Google Colab
67
+ - Displays the correct URL for accessing the app
68
+
69
+ Args:
70
+ **kwargs: Arguments passed to Flask.run()
71
+ """
72
+ import sys
73
+
74
+ app = self.create_app()
75
+
76
+ # Set defaults
77
+ run_kwargs = {"host": "0.0.0.0", "port": 5000, "debug": False, **kwargs}
78
+
79
+ # Get the port from run_kwargs
80
+ port = run_kwargs.get("port", 5000)
81
+
82
+ # Check if we're in an environment with a running event loop
83
+ # (Jupyter, Colab, IPython, VS Code notebooks, etc.)
84
+ in_async_env = False
85
+ try:
86
+ import asyncio
87
+
88
+ try:
89
+ asyncio.get_running_loop()
90
+ in_async_env = True
91
+ except RuntimeError:
92
+ in_async_env = False
93
+ except Exception:
94
+ pass
95
+
96
+ if in_async_env:
97
+ # Apply nest_asyncio to allow nested event loops
98
+ try:
99
+ import nest_asyncio
100
+
101
+ nest_asyncio.apply()
102
+ except ImportError:
103
+ print("Warning: nest_asyncio not installed. Installing...")
104
+ import subprocess
105
+
106
+ subprocess.check_call(
107
+ [sys.executable, "-m", "pip", "install", "nest_asyncio"]
108
+ )
109
+ import nest_asyncio
110
+
111
+ nest_asyncio.apply()
112
+
113
+ # Check if we're specifically in Google Colab for port forwarding
114
+ in_colab = "google.colab" in sys.modules
115
+
116
+ if in_colab:
117
+ try:
118
+ from google.colab import output
119
+
120
+ output.serve_kernel_port_as_window(port)
121
+ from google.colab.output import eval_js
122
+
123
+ print("Your app is running at:")
124
+ print(eval_js(f"google.colab.kernel.proxyPort({port})"))
125
+ except Exception as e:
126
+ print(f"Warning: Could not set up Colab port forwarding: {e}")
127
+ print(f"Your app is running at: http://localhost:{port}")
128
+ else:
129
+ print("Your app is running at:")
130
+ print(f"http://localhost:{port}")
131
+
132
+ app.run(**run_kwargs)