vanna 0.7.8__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 +247 -223
- 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.8.dist-info/METADATA +0 -408
- vanna-0.7.8.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.8.dist-info → vanna-2.0.0.dist-info}/WHEEL +0 -0
- {vanna-0.7.8.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,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,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)
|