asksql 2.1.0__tar.gz → 2.2.0__tar.gz
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.
- {asksql-2.1.0 → asksql-2.2.0}/PKG-INFO +11 -2
- {asksql-2.1.0 → asksql-2.2.0}/README.md +10 -1
- {asksql-2.1.0 → asksql-2.2.0}/pyproject.toml +1 -1
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/__init__.py +1 -1
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/agent/agent.py +28 -1
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/agent/config.py +17 -1
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/workflow/default.py +51 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/postgres/sql_runner.py +4 -5
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/sqlite/sql_runner.py +4 -5
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/adapter.py +5 -4
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/fastapi/app.py +13 -1
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/tools/agent_memory.py +72 -2
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/tools/run_sql.py +26 -3
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/utils/sql_safety.py +19 -0
- {asksql-2.1.0 → asksql-2.2.0}/LICENSE +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/agents/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/agent_memory/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/agent_memory/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/agent_memory/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/file_system/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/file_system/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/file_system/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/sql_runner/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/sql_runner/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/capabilities/sql_runner/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/containers/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/containers/card.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/data/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/data/chart.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/data/dataframe.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/badge.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/icon_text.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/log_viewer.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/notification.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/progress.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/status_card.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/feedback/status_indicator.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/interactive/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/interactive/button.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/interactive/task_list.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/interactive/ui_state.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/specialized/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/specialized/artifact.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/rich/text.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/simple/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/simple/image.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/simple/link.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/components/simple/text.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/_compat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/agent/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/audit/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/audit/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/audit/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/component_manager.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/components.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/enhancer/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/enhancer/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/enhancer/default.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/enricher/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/enricher/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/errors.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/evaluation/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/evaluation/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/evaluation/dataset.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/evaluation/evaluators.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/evaluation/report.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/evaluation/runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/filter/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/filter/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/lifecycle/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/lifecycle/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/llm/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/llm/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/llm/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/middleware/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/middleware/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/observability/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/observability/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/observability/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/recovery/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/recovery/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/recovery/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/registry.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/rich_component.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/simple_component.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/storage/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/storage/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/storage/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/system_prompt/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/system_prompt/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/system_prompt/default.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/tool/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/tool/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/tool/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/user/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/user/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/user/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/user/request_context.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/user/resolver.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/validation.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/workflow/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/core/workflow/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/__main__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/anthropic_quickstart.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/artifact_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/claude_sqlite_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/coding_agent_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/custom_system_prompt_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/default_workflow_handler_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/email_auth_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/evaluation_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/extensibility_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/minimal_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/mock_auth_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/mock_custom_tool.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/mock_quickstart.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/mock_quota_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/mock_rich_components_demo.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/mock_sqlite_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/openai_quickstart.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/primitive_components_demo.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/quota_lifecycle_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/examples/visualization_example.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/anthropic/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/anthropic/llm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/azureopenai/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/azureopenai/llm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/azuresearch/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/azuresearch/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/bigquery/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/bigquery/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/chromadb/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/chromadb/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/clickhouse/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/clickhouse/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/duckdb/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/duckdb/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/faiss/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/faiss/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/google/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/google/gemini.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/hive/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/hive/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/agent_memory/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/agent_memory/in_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/audit.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/file_system.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/file_system_conversation_store.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/local/storage.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/marqo/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/marqo/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/milvus/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/milvus/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/mock/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/mock/llm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/mssql/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/mssql/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/mysql/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/mysql/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/ollama/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/ollama/llm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/openai/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/openai/llm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/openai/responses.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/opensearch/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/opensearch/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/oracle/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/oracle/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/pinecone/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/pinecone/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/plotly/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/plotly/chart_generator.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/postgres/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/premium/agent_memory/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/premium/agent_memory/premium.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/presto/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/presto/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/qdrant/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/qdrant/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/snowflake/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/snowflake/sql_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/sqlite/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/weaviate/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/integrations/weaviate/agent_memory.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/ZhipuAI/ZhipuAI_Chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/ZhipuAI/ZhipuAI_embeddings.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/ZhipuAI/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/advanced/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/anthropic/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/anthropic/anthropic_chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/azuresearch/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/azuresearch/azuresearch_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/base/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/base/base.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/base/safe_exec.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/bedrock/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/bedrock/bedrock_converse.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/chromadb/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/chromadb/chromadb_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/cohere/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/cohere/cohere_chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/cohere/cohere_embeddings.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/deepseek/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/deepseek/deepseek_chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/exceptions/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/faiss/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/faiss/faiss.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/flask/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/flask/assets.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/flask/auth.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/google/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/google/bigquery_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/google/gemini_chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/hf/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/hf/hf.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/local.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/marqo/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/marqo/marqo.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/milvus/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/milvus/milvus_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/mistral/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/mistral/mistral.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/mock/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/mock/embedding.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/mock/llm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/mock/vectordb.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/ollama/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/ollama/ollama.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/openai/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/openai/openai_chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/openai/openai_embeddings.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/opensearch/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/opensearch/opensearch_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/opensearch/opensearch_vector_semantic.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/oracle/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/oracle/oracle_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/pgvector/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/pgvector/pgvector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/pinecone/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/pinecone/pinecone_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qdrant/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qdrant/qdrant.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qianfan/Qianfan_Chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qianfan/Qianfan_embeddings.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qianfan/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qianwen/QianwenAI_chat.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qianwen/QianwenAI_embeddings.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/qianwen/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/remote.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/types/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/utils.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/vannadb/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/vannadb/vannadb_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/vllm/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/vllm/vllm.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/weaviate/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/weaviate/weaviate_vector.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/xinference/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/legacy/xinference/xinference.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/py.typed +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/__main__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/base/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/base/chat_handler.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/base/models.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/base/rich_chat_handler.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/base/templates.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/cli/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/cli/server_runner.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/fastapi/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/fastapi/routes.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/flask/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/flask/app.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/servers/flask/routes.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/tools/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/tools/file_system.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/tools/python.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/tools/visualize_data.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/utils/__init__.py +0 -0
- {asksql-2.1.0 → asksql-2.2.0}/src/vanna/web_components/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asksql
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Community-maintained, security-focused fork of Vanna: generate SQL from natural language. Drop-in: keeps `import vanna`.
|
|
5
5
|
Author-email: Zain Hoda <zain@vanna.ai>
|
|
6
6
|
Maintainer: Ayush Srivastava
|
|
@@ -176,6 +176,11 @@ Provides-Extra: zhipuai
|
|
|
176
176
|
|
|
177
177
|
# asksql
|
|
178
178
|
|
|
179
|
+
[](https://pypi.org/project/asksql/)
|
|
180
|
+
[](https://github.com/buhtig47/vanna/actions/workflows/tests.yml)
|
|
181
|
+
[](https://github.com/buhtig47/vanna/actions/workflows/tests.yml)
|
|
182
|
+
[](https://github.com/buhtig47/vanna/blob/main/LICENSE)
|
|
183
|
+
|
|
179
184
|
**The community-maintained, security-focused continuation of [Vanna](https://github.com/vanna-ai/vanna).**
|
|
180
185
|
|
|
181
186
|
> 🚧 **Work in progress.** Vanna was archived on 29 March 2026 with ~227 open issues. asksql picks it up: security fixes first, then bug fixes and dependency updates.
|
|
@@ -187,7 +192,11 @@ pip uninstall vanna
|
|
|
187
192
|
pip install asksql
|
|
188
193
|
```
|
|
189
194
|
|
|
190
|
-
asksql keeps the `vanna` import name (like Pillow did for PIL), so `import vanna`, `from vanna import Agent` and the legacy
|
|
195
|
+
asksql keeps the `vanna` import name (like Pillow did for PIL), so `import vanna`, `from vanna import Agent` and the legacy `VannaBase` API work as before. Uninstall Vanna **before** installing asksql, because both ship the same `vanna/` folder.
|
|
196
|
+
|
|
197
|
+
📘 **[Migration guide](https://github.com/buhtig47/vanna/blob/main/MIGRATING_FROM_VANNA.md)**: requirements files, coming from Vanna 0.x, behaviour changes, troubleshooting.
|
|
198
|
+
|
|
199
|
+
🔌 **[Server API](https://github.com/buhtig47/vanna/blob/main/SERVER_API.md)**: HTTP endpoints (`/health`, `chat_poll`, `chat_sse`), request/response format, CORS.
|
|
191
200
|
|
|
192
201
|
### What's fixed so far
|
|
193
202
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# asksql
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/asksql/)
|
|
4
|
+
[](https://github.com/buhtig47/vanna/actions/workflows/tests.yml)
|
|
5
|
+
[](https://github.com/buhtig47/vanna/actions/workflows/tests.yml)
|
|
6
|
+
[](https://github.com/buhtig47/vanna/blob/main/LICENSE)
|
|
7
|
+
|
|
3
8
|
**The community-maintained, security-focused continuation of [Vanna](https://github.com/vanna-ai/vanna).**
|
|
4
9
|
|
|
5
10
|
> 🚧 **Work in progress.** Vanna was archived on 29 March 2026 with ~227 open issues. asksql picks it up: security fixes first, then bug fixes and dependency updates.
|
|
@@ -11,7 +16,11 @@ pip uninstall vanna
|
|
|
11
16
|
pip install asksql
|
|
12
17
|
```
|
|
13
18
|
|
|
14
|
-
asksql keeps the `vanna` import name (like Pillow did for PIL), so `import vanna`, `from vanna import Agent` and the legacy
|
|
19
|
+
asksql keeps the `vanna` import name (like Pillow did for PIL), so `import vanna`, `from vanna import Agent` and the legacy `VannaBase` API work as before. Uninstall Vanna **before** installing asksql, because both ship the same `vanna/` folder.
|
|
20
|
+
|
|
21
|
+
📘 **[Migration guide](https://github.com/buhtig47/vanna/blob/main/MIGRATING_FROM_VANNA.md)**: requirements files, coming from Vanna 0.x, behaviour changes, troubleshooting.
|
|
22
|
+
|
|
23
|
+
🔌 **[Server API](https://github.com/buhtig47/vanna/blob/main/SERVER_API.md)**: HTTP endpoints (`/health`, `chat_poll`, `chat_sse`), request/response format, CORS.
|
|
15
24
|
|
|
16
25
|
### What's fixed so far
|
|
17
26
|
|
|
@@ -6,7 +6,7 @@ with tool execution, conversation management, and user scoping.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
# Version information
|
|
9
|
-
__version__ = "2.
|
|
9
|
+
__version__ = "2.2.0" # keep in sync with pyproject.toml
|
|
10
10
|
|
|
11
11
|
# Import core framework components
|
|
12
12
|
from .core import (
|
|
@@ -980,8 +980,35 @@ class Agent:
|
|
|
980
980
|
|
|
981
981
|
if has_tool_error_access:
|
|
982
982
|
yield result.ui_component
|
|
983
|
+
elif result.metadata.get("contains_schema_details"):
|
|
984
|
+
# Table/column listings from schema lookups are only shown to
|
|
985
|
+
# users with the "schema_details" UI feature (vanna-ai/vanna#1105)
|
|
986
|
+
has_schema_access = (
|
|
987
|
+
self.config.ui_features.can_user_access_feature(
|
|
988
|
+
UiFeature.UI_FEATURE_SHOW_SCHEMA_DETAILS, user
|
|
989
|
+
)
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
if (
|
|
993
|
+
self.audit_logger
|
|
994
|
+
and self.config.audit_config.enabled
|
|
995
|
+
and self.config.audit_config.log_ui_feature_checks
|
|
996
|
+
):
|
|
997
|
+
await self.audit_logger.log_ui_feature_access(
|
|
998
|
+
user=user,
|
|
999
|
+
feature_name=UiFeature.UI_FEATURE_SHOW_SCHEMA_DETAILS,
|
|
1000
|
+
access_granted=has_schema_access,
|
|
1001
|
+
required_groups=self.config.ui_features.feature_group_access.get(
|
|
1002
|
+
UiFeature.UI_FEATURE_SHOW_SCHEMA_DETAILS, []
|
|
1003
|
+
),
|
|
1004
|
+
conversation_id=conversation.id,
|
|
1005
|
+
request_id=request_id,
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
if has_schema_access:
|
|
1009
|
+
yield result.ui_component
|
|
983
1010
|
else:
|
|
984
|
-
#
|
|
1011
|
+
# Other success results are always shown if they exist
|
|
985
1012
|
yield result.ui_component
|
|
986
1013
|
|
|
987
1014
|
# Collect tool result data
|
|
@@ -6,7 +6,7 @@ This module contains configuration models that control agent behavior.
|
|
|
6
6
|
|
|
7
7
|
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
8
8
|
|
|
9
|
-
from pydantic import BaseModel, Field
|
|
9
|
+
from pydantic import BaseModel, Field, field_validator
|
|
10
10
|
|
|
11
11
|
from .._compat import StrEnum
|
|
12
12
|
|
|
@@ -20,6 +20,8 @@ class UiFeature(StrEnum):
|
|
|
20
20
|
UI_FEATURE_SHOW_TOOL_ERROR = "tool_error"
|
|
21
21
|
UI_FEATURE_SHOW_TOOL_INVOCATION_MESSAGE_IN_CHAT = "tool_invocation_message_in_chat"
|
|
22
22
|
UI_FEATURE_SHOW_MEMORY_DETAILED_RESULTS = "memory_detailed_results"
|
|
23
|
+
# Results of queries that inspect the database structure (tables, columns)
|
|
24
|
+
UI_FEATURE_SHOW_SCHEMA_DETAILS = "schema_details"
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
# Optional: you can also define defaults if you want a shared baseline
|
|
@@ -29,6 +31,7 @@ DEFAULT_UI_FEATURES: Dict[str, List[str]] = {
|
|
|
29
31
|
UiFeature.UI_FEATURE_SHOW_TOOL_ERROR: ["admin"],
|
|
30
32
|
UiFeature.UI_FEATURE_SHOW_TOOL_INVOCATION_MESSAGE_IN_CHAT: ["admin"],
|
|
31
33
|
UiFeature.UI_FEATURE_SHOW_MEMORY_DETAILED_RESULTS: ["admin"],
|
|
34
|
+
UiFeature.UI_FEATURE_SHOW_SCHEMA_DETAILS: ["admin"],
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
|
|
@@ -46,6 +49,19 @@ class UiFeatures(BaseModel):
|
|
|
46
49
|
description="Which groups can access UI features",
|
|
47
50
|
)
|
|
48
51
|
|
|
52
|
+
@field_validator("feature_group_access")
|
|
53
|
+
@classmethod
|
|
54
|
+
def _add_new_default_features(
|
|
55
|
+
cls, value: Dict[str, List[str]]
|
|
56
|
+
) -> Dict[str, List[str]]:
|
|
57
|
+
# Configs written before "schema_details" existed would otherwise hide it from
|
|
58
|
+
# everyone (unknown features are denied), so give it its default groups.
|
|
59
|
+
value.setdefault(
|
|
60
|
+
UiFeature.UI_FEATURE_SHOW_SCHEMA_DETAILS,
|
|
61
|
+
list(DEFAULT_UI_FEATURES[UiFeature.UI_FEATURE_SHOW_SCHEMA_DETAILS]),
|
|
62
|
+
)
|
|
63
|
+
return value
|
|
64
|
+
|
|
49
65
|
def can_user_access_feature(self, feature_name: str, user: "User") -> bool:
|
|
50
66
|
"""Check if user can access a UI feature using same logic as tools.
|
|
51
67
|
|
|
@@ -158,6 +158,10 @@ class DefaultWorkflowHandler(WorkflowHandler):
|
|
|
158
158
|
memory_id = message.strip()[8:].strip() # Extract ID after "/delete "
|
|
159
159
|
return await self._delete_memory(agent, user, conversation, memory_id)
|
|
160
160
|
|
|
161
|
+
if message.strip().lower().startswith("/save_memory "):
|
|
162
|
+
pending_id = message.strip()[len("/save_memory ") :].strip()
|
|
163
|
+
return await self._approve_memory(agent, user, conversation, pending_id)
|
|
164
|
+
|
|
161
165
|
# Don't handle other messages, pass to LLM
|
|
162
166
|
return WorkflowResult(should_skip_llm=False)
|
|
163
167
|
|
|
@@ -680,6 +684,53 @@ class DefaultWorkflowHandler(WorkflowHandler):
|
|
|
680
684
|
],
|
|
681
685
|
)
|
|
682
686
|
|
|
687
|
+
async def _approve_memory(
|
|
688
|
+
self,
|
|
689
|
+
agent: "Agent",
|
|
690
|
+
user: "User",
|
|
691
|
+
conversation: "Conversation",
|
|
692
|
+
pending_id: str,
|
|
693
|
+
) -> WorkflowResult:
|
|
694
|
+
"""Save a memory the user approved with the "Save to memory" button (vanna-ai/vanna#1103)."""
|
|
695
|
+
from vanna.core.tool import ToolContext
|
|
696
|
+
from vanna.tools.agent_memory import SaveQuestionToolArgsTool
|
|
697
|
+
|
|
698
|
+
def reply(content: str) -> WorkflowResult:
|
|
699
|
+
return WorkflowResult(
|
|
700
|
+
should_skip_llm=True,
|
|
701
|
+
components=[
|
|
702
|
+
UiComponent(
|
|
703
|
+
rich_component=RichTextComponent(
|
|
704
|
+
content=content, markdown=True
|
|
705
|
+
),
|
|
706
|
+
simple_component=None,
|
|
707
|
+
)
|
|
708
|
+
],
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
tool = await agent.tool_registry.get_tool("save_question_tool_args")
|
|
712
|
+
# register_local_tool(..., access_groups=[...]) wraps the tool
|
|
713
|
+
tool = getattr(tool, "_wrapped_tool", tool)
|
|
714
|
+
if not isinstance(tool, SaveQuestionToolArgsTool) or not getattr(
|
|
715
|
+
agent, "agent_memory", None
|
|
716
|
+
):
|
|
717
|
+
return reply("# ⚠️ Nothing to save\n\nMemory saving is not configured.")
|
|
718
|
+
|
|
719
|
+
context = ToolContext(
|
|
720
|
+
user=user,
|
|
721
|
+
conversation_id=conversation.id,
|
|
722
|
+
request_id=str(uuid.uuid4()),
|
|
723
|
+
agent_memory=agent.agent_memory,
|
|
724
|
+
)
|
|
725
|
+
if await tool.approve(pending_id, context):
|
|
726
|
+
return reply(
|
|
727
|
+
"# ✅ Saved to memory\n\nSimilar questions will reuse this query."
|
|
728
|
+
)
|
|
729
|
+
return reply(
|
|
730
|
+
"# ❌ Nothing to save\n\nThis save request has expired, was already saved, "
|
|
731
|
+
"or belongs to another user."
|
|
732
|
+
)
|
|
733
|
+
|
|
683
734
|
async def _delete_memory(
|
|
684
735
|
self, agent: "Agent", user: "User", conversation: "Conversation", memory_id: str
|
|
685
736
|
) -> WorkflowResult:
|
|
@@ -87,11 +87,10 @@ class PostgresRunner(SqlRunner):
|
|
|
87
87
|
# Execute the query
|
|
88
88
|
cursor.execute(args.sql)
|
|
89
89
|
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# Fetch results for SELECT queries
|
|
90
|
+
# DB-API: description is set only for statements that return rows. Checking the
|
|
91
|
+
# first word missed WITH ... SELECT and queries starting with a comment.
|
|
92
|
+
if cursor.description is not None:
|
|
93
|
+
# Fetch results for queries that return rows
|
|
95
94
|
rows = cursor.fetchall()
|
|
96
95
|
if not rows:
|
|
97
96
|
# Return empty DataFrame
|
|
@@ -40,11 +40,10 @@ class SqliteRunner(SqlRunner):
|
|
|
40
40
|
# Execute the query
|
|
41
41
|
cursor.execute(args.sql)
|
|
42
42
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# Fetch results for SELECT queries
|
|
43
|
+
# DB-API: description is set only for statements that return rows. Checking the
|
|
44
|
+
# first word missed WITH ... SELECT and queries starting with a comment.
|
|
45
|
+
if cursor.description is not None:
|
|
46
|
+
# Fetch results for queries that return rows
|
|
48
47
|
rows = cursor.fetchall()
|
|
49
48
|
if not rows:
|
|
50
49
|
# Return empty DataFrame
|
|
@@ -125,17 +125,18 @@ class LegacyVannaAdapter(ToolRegistry, AgentMemory):
|
|
|
125
125
|
sql_runner = LegacySqlRunner(self.vn)
|
|
126
126
|
|
|
127
127
|
# Register the RunSqlTool with user and admin access
|
|
128
|
+
config = getattr(self.vn, "config", None) or {}
|
|
128
129
|
run_sql_tool = RunSqlTool(
|
|
129
130
|
sql_runner,
|
|
130
|
-
allow_write_sql=bool(
|
|
131
|
-
(getattr(self.vn, "config", None) or {}).get("allow_write_sql", False)
|
|
132
|
-
),
|
|
131
|
+
allow_write_sql=bool(config.get("allow_write_sql", False)),
|
|
133
132
|
)
|
|
134
133
|
self.register_local_tool(run_sql_tool, access_groups=["user", "admin"])
|
|
135
134
|
|
|
136
135
|
# Register memory tools using the internal _agent_memory instance
|
|
137
136
|
# SaveQuestionToolArgsTool - for saving question-tool-args patterns (admin only)
|
|
138
|
-
save_memory_tool = SaveQuestionToolArgsTool(
|
|
137
|
+
save_memory_tool = SaveQuestionToolArgsTool(
|
|
138
|
+
require_approval=bool(config.get("require_memory_approval", False))
|
|
139
|
+
)
|
|
139
140
|
self.register_local_tool(save_memory_tool, access_groups=["admin"])
|
|
140
141
|
|
|
141
142
|
# SearchSavedCorrectToolUsesTool - for searching similar patterns (user and admin)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
FastAPI server factory for Vanna Agents.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import warnings
|
|
5
6
|
from typing import Any, Dict, Optional
|
|
6
7
|
|
|
7
8
|
from fastapi import FastAPI
|
|
@@ -49,7 +50,18 @@ class VannaFastAPIServer:
|
|
|
49
50
|
|
|
50
51
|
# Set sensible defaults
|
|
51
52
|
cors_params.setdefault("allow_origins", ["*"])
|
|
52
|
-
|
|
53
|
+
# Wildcard origins + credentials lets ANY website send requests with the
|
|
54
|
+
# user's cookies and read the answers. Only allow credentials by default
|
|
55
|
+
# when the origins are an explicit allow-list.
|
|
56
|
+
wildcard = "*" in cors_params["allow_origins"]
|
|
57
|
+
cors_params.setdefault("allow_credentials", not wildcard)
|
|
58
|
+
if wildcard and cors_params["allow_credentials"]:
|
|
59
|
+
warnings.warn(
|
|
60
|
+
"CORS allow_origins=['*'] with allow_credentials=True lets any website "
|
|
61
|
+
"call this server with your users' cookies. List your frontend origins "
|
|
62
|
+
"in config['cors']['allow_origins'] instead.",
|
|
63
|
+
stacklevel=2,
|
|
64
|
+
)
|
|
53
65
|
cors_params.setdefault("allow_methods", ["*"])
|
|
54
66
|
cors_params.setdefault("allow_headers", ["*"])
|
|
55
67
|
|
|
@@ -7,7 +7,9 @@ The tools access AgentMemory via ToolContext, which is populated by the Agent.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import logging
|
|
10
|
-
|
|
10
|
+
import uuid
|
|
11
|
+
from collections import OrderedDict
|
|
12
|
+
from typing import Any, Dict, List, Optional, Tuple, Type
|
|
11
13
|
from pydantic import BaseModel, Field
|
|
12
14
|
|
|
13
15
|
logger = logging.getLogger(__name__)
|
|
@@ -19,8 +21,14 @@ from vanna.components import (
|
|
|
19
21
|
UiComponent,
|
|
20
22
|
StatusBarUpdateComponent,
|
|
21
23
|
CardComponent,
|
|
24
|
+
ButtonComponent,
|
|
25
|
+
SimpleTextComponent,
|
|
22
26
|
)
|
|
23
27
|
|
|
28
|
+
# Chat command the "Save to memory" button sends (handled by DefaultWorkflowHandler)
|
|
29
|
+
SAVE_MEMORY_COMMAND = "/save_memory"
|
|
30
|
+
MAX_PENDING_APPROVALS = 1000
|
|
31
|
+
|
|
24
32
|
|
|
25
33
|
class SaveQuestionToolArgsParams(BaseModel):
|
|
26
34
|
"""Parameters for saving question-tool-argument combinations."""
|
|
@@ -58,7 +66,66 @@ class SaveTextMemoryParams(BaseModel):
|
|
|
58
66
|
|
|
59
67
|
|
|
60
68
|
class SaveQuestionToolArgsTool(Tool[SaveQuestionToolArgsParams]):
|
|
61
|
-
"""Tool for saving successful question-tool-argument combinations.
|
|
69
|
+
"""Tool for saving successful question-tool-argument combinations.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
require_approval: If True, the LLM can only *propose* a memory. The user
|
|
73
|
+
sees a "Save to memory" button and the pattern is saved only when
|
|
74
|
+
they click it. Prevents SQL that ran but returned wrong data from
|
|
75
|
+
being saved and reused (vanna-ai/vanna#1103).
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, require_approval: bool = False):
|
|
79
|
+
self.require_approval = require_approval
|
|
80
|
+
# pending_id -> (user_id, args); bounded so it can't grow forever
|
|
81
|
+
self._pending: "OrderedDict[str, Tuple[str, SaveQuestionToolArgsParams]]" = (
|
|
82
|
+
OrderedDict()
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
async def approve(self, pending_id: str, context: ToolContext) -> bool:
|
|
86
|
+
"""Save a pending memory. Only the user it was proposed to can approve it, once."""
|
|
87
|
+
pending = self._pending.get(pending_id)
|
|
88
|
+
if pending is None or pending[0] != context.user.id:
|
|
89
|
+
return False
|
|
90
|
+
del self._pending[pending_id]
|
|
91
|
+
_, args = pending
|
|
92
|
+
await context.agent_memory.save_tool_usage(
|
|
93
|
+
question=args.question,
|
|
94
|
+
tool_name=args.tool_name,
|
|
95
|
+
args=args.args,
|
|
96
|
+
context=context,
|
|
97
|
+
success=True,
|
|
98
|
+
)
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
def _propose(
|
|
102
|
+
self, context: ToolContext, args: SaveQuestionToolArgsParams
|
|
103
|
+
) -> ToolResult:
|
|
104
|
+
pending_id = uuid.uuid4().hex[:12]
|
|
105
|
+
self._pending[pending_id] = (context.user.id, args)
|
|
106
|
+
while len(self._pending) > MAX_PENDING_APPROVALS:
|
|
107
|
+
self._pending.popitem(last=False)
|
|
108
|
+
|
|
109
|
+
command = f"{SAVE_MEMORY_COMMAND} {pending_id}"
|
|
110
|
+
return ToolResult(
|
|
111
|
+
success=True,
|
|
112
|
+
result_for_llm=(
|
|
113
|
+
"Not saved yet: the user must approve saving this pattern to memory. "
|
|
114
|
+
"A 'Save to memory' button has been shown to them. Do not call this tool again for it."
|
|
115
|
+
),
|
|
116
|
+
ui_component=UiComponent(
|
|
117
|
+
rich_component=ButtonComponent(
|
|
118
|
+
label="Save to memory",
|
|
119
|
+
action=command,
|
|
120
|
+
variant="secondary",
|
|
121
|
+
size="small",
|
|
122
|
+
icon="👍",
|
|
123
|
+
),
|
|
124
|
+
simple_component=SimpleTextComponent(
|
|
125
|
+
text=f"If this answer is correct, send `{command}` to save it to memory."
|
|
126
|
+
),
|
|
127
|
+
),
|
|
128
|
+
)
|
|
62
129
|
|
|
63
130
|
@property
|
|
64
131
|
def name(self) -> str:
|
|
@@ -77,6 +144,9 @@ class SaveQuestionToolArgsTool(Tool[SaveQuestionToolArgsParams]):
|
|
|
77
144
|
self, context: ToolContext, args: SaveQuestionToolArgsParams
|
|
78
145
|
) -> ToolResult:
|
|
79
146
|
"""Save the tool usage pattern to agent memory."""
|
|
147
|
+
if self.require_approval:
|
|
148
|
+
return self._propose(context, args)
|
|
149
|
+
|
|
80
150
|
try:
|
|
81
151
|
await context.agent_memory.save_tool_usage(
|
|
82
152
|
question=args.question,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Generic SQL query execution tool with dependency injection."""
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, List, Optional, Type, cast
|
|
4
|
+
import re
|
|
4
5
|
import uuid
|
|
5
6
|
from vanna.core.tool import Tool, ToolContext, ToolResult
|
|
6
7
|
from vanna.components import (
|
|
@@ -13,7 +14,22 @@ from vanna.components import (
|
|
|
13
14
|
from vanna.capabilities.sql_runner import SqlRunner, RunSqlToolArgs
|
|
14
15
|
from vanna.capabilities.file_system import FileSystem
|
|
15
16
|
from vanna.integrations.local import LocalFileSystem
|
|
16
|
-
from vanna.utils.sql_safety import ensure_read_only_sql
|
|
17
|
+
from vanna.utils.sql_safety import ensure_read_only_sql, sql_returns_rows
|
|
18
|
+
|
|
19
|
+
# Catalog tables/commands that describe the database structure rather than business data
|
|
20
|
+
_SCHEMA_QUERY = re.compile(
|
|
21
|
+
r"\b(information_schema|sqlite_master|sqlite_schema|pg_catalog|pg_tables|pg_class"
|
|
22
|
+
r"|pg_attribute|sys\.(tables|columns|objects|schemas)|all_tables|user_tables"
|
|
23
|
+
r"|all_tab_columns|user_tab_columns|all_views|system\.(tables|columns)"
|
|
24
|
+
r"|duckdb_tables|duckdb_columns|table_info|pragma_\w+)\b"
|
|
25
|
+
r"|^\s*(show|describe|desc|pragma)\b",
|
|
26
|
+
re.IGNORECASE,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def is_schema_query(sql: str) -> bool:
|
|
31
|
+
"""True if the query inspects database structure (tables, columns) - vanna-ai/vanna#1105."""
|
|
32
|
+
return bool(_SCHEMA_QUERY.search(sql or ""))
|
|
17
33
|
|
|
18
34
|
|
|
19
35
|
class RunSqlTool(Tool[RunSqlToolArgs]):
|
|
@@ -70,7 +86,7 @@ class RunSqlTool(Tool[RunSqlToolArgs]):
|
|
|
70
86
|
# Determine query type
|
|
71
87
|
query_type = args.sql.strip().upper().split()[0]
|
|
72
88
|
|
|
73
|
-
if
|
|
89
|
+
if sql_returns_rows(args.sql):
|
|
74
90
|
# Handle SELECT queries with results
|
|
75
91
|
if df.empty:
|
|
76
92
|
result = "Query executed successfully. No rows returned."
|
|
@@ -135,7 +151,11 @@ class RunSqlTool(Tool[RunSqlToolArgs]):
|
|
|
135
151
|
else:
|
|
136
152
|
# For non-SELECT queries (INSERT, UPDATE, DELETE, etc.)
|
|
137
153
|
# The SqlRunner should return a DataFrame with affected row count
|
|
138
|
-
rows_affected
|
|
154
|
+
if "rows_affected" in df.columns and not df.empty:
|
|
155
|
+
# Built-in runners report the count as a one-row DataFrame
|
|
156
|
+
rows_affected = int(df["rows_affected"].iloc[0])
|
|
157
|
+
else:
|
|
158
|
+
rows_affected = len(df)
|
|
139
159
|
result = (
|
|
140
160
|
f"Query executed successfully. {rows_affected} row(s) affected."
|
|
141
161
|
)
|
|
@@ -148,6 +168,9 @@ class RunSqlTool(Tool[RunSqlToolArgs]):
|
|
|
148
168
|
simple_component=SimpleTextComponent(text=result),
|
|
149
169
|
)
|
|
150
170
|
|
|
171
|
+
# The agent hides these results from users without the "schema_details" UI feature
|
|
172
|
+
metadata["contains_schema_details"] = is_schema_query(args.sql)
|
|
173
|
+
|
|
151
174
|
return ToolResult(
|
|
152
175
|
success=True,
|
|
153
176
|
result_for_llm=result,
|
|
@@ -82,3 +82,22 @@ def ensure_read_only_sql(sql: str) -> None:
|
|
|
82
82
|
word = token.value.strip('`"[]').upper()
|
|
83
83
|
if word in DENIED_WORDS or word.startswith(DENIED_PREFIXES):
|
|
84
84
|
raise UnsafeSqlError(f"'{token.value}' is not allowed. {WRITE_SQL_HINT}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Leading keywords of statements that return a result set but that sqlparse types as UNKNOWN
|
|
88
|
+
_ROW_RETURNING_KEYWORDS = {"SHOW", "DESCRIBE", "DESC", "EXPLAIN", "PRAGMA", "VALUES"}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def sql_returns_rows(sql: str) -> bool:
|
|
92
|
+
"""True if `sql` returns a result set (SELECT, WITH ... SELECT, SHOW, ...).
|
|
93
|
+
|
|
94
|
+
Unlike checking the first word, this handles CTEs and leading comments.
|
|
95
|
+
"""
|
|
96
|
+
statements = [s for s in sqlparse.parse(sql or "") if s.value.strip(" \t\r\n;")]
|
|
97
|
+
if not statements:
|
|
98
|
+
return False
|
|
99
|
+
statement = statements[-1]
|
|
100
|
+
if statement.get_type() == "SELECT":
|
|
101
|
+
return True
|
|
102
|
+
first = statement.token_first(skip_cm=True, skip_ws=True)
|
|
103
|
+
return first is not None and first.normalized.upper() in _ROW_RETURNING_KEYWORDS
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|