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,27 @@
|
|
|
1
|
+
"""Base classes for simple UI components."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SimpleComponentType(str, Enum):
|
|
9
|
+
TEXT = "text"
|
|
10
|
+
IMAGE = "image"
|
|
11
|
+
LINK = "link"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SimpleComponent(BaseModel):
|
|
15
|
+
"""A simple UI component with basic attributes."""
|
|
16
|
+
|
|
17
|
+
type: SimpleComponentType = Field(..., description="Type of the component.")
|
|
18
|
+
semantic_type: Optional[str] = Field(
|
|
19
|
+
default=None, description="Semantic type for better categorization."
|
|
20
|
+
)
|
|
21
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
22
|
+
default=None, description="Additional metadata for the component."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def serialize_for_frontend(self) -> Dict[str, Any]:
|
|
26
|
+
"""Serialize simple component for API consumption."""
|
|
27
|
+
return self.model_dump()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Storage domain.
|
|
3
|
+
|
|
4
|
+
This module provides the core abstractions for conversation storage in the Vanna Agents framework.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import ConversationStore
|
|
8
|
+
from .models import Conversation, Message
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"ConversationStore",
|
|
12
|
+
"Conversation",
|
|
13
|
+
"Message",
|
|
14
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Storage domain interface.
|
|
3
|
+
|
|
4
|
+
This module contains the abstract base class for conversation storage.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import List, Optional
|
|
9
|
+
|
|
10
|
+
from .models import Conversation
|
|
11
|
+
from ..user.models import User
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConversationStore(ABC):
|
|
15
|
+
"""Abstract base class for conversation storage."""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
async def create_conversation(
|
|
19
|
+
self, conversation_id: str, user: User, initial_message: str
|
|
20
|
+
) -> Conversation:
|
|
21
|
+
"""Create a new conversation with the specified ID."""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
async def get_conversation(
|
|
26
|
+
self, conversation_id: str, user: User
|
|
27
|
+
) -> Optional[Conversation]:
|
|
28
|
+
"""Get conversation by ID, scoped to user."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
async def update_conversation(self, conversation: Conversation) -> None:
|
|
33
|
+
"""Update conversation with new messages."""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def delete_conversation(self, conversation_id: str, user: User) -> bool:
|
|
38
|
+
"""Delete conversation."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
async def list_conversations(
|
|
43
|
+
self, user: User, limit: int = 50, offset: int = 0
|
|
44
|
+
) -> List[Conversation]:
|
|
45
|
+
"""List conversations for user."""
|
|
46
|
+
pass
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Storage domain models.
|
|
3
|
+
|
|
4
|
+
This module contains data models for conversation storage.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from ..tool.models import ToolCall
|
|
13
|
+
from ..user.models import User
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Message(BaseModel):
|
|
17
|
+
"""Single message in a conversation."""
|
|
18
|
+
|
|
19
|
+
role: str = Field(description="Message role (user/assistant/system/tool)")
|
|
20
|
+
content: str = Field(description="Message content")
|
|
21
|
+
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
|
22
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
23
|
+
tool_calls: Optional[List[ToolCall]] = Field(default=None)
|
|
24
|
+
tool_call_id: Optional[str] = Field(
|
|
25
|
+
default=None, description="ID if this is a tool response"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Conversation(BaseModel):
|
|
30
|
+
"""Conversation containing multiple messages."""
|
|
31
|
+
|
|
32
|
+
id: str = Field(description="Unique conversation identifier")
|
|
33
|
+
user: User = Field(description="User this conversation belongs to")
|
|
34
|
+
messages: List[Message] = Field(
|
|
35
|
+
default_factory=list, description="Messages in conversation"
|
|
36
|
+
)
|
|
37
|
+
created_at: datetime = Field(default_factory=datetime.utcnow)
|
|
38
|
+
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
|
39
|
+
metadata: Dict[str, Any] = Field(
|
|
40
|
+
default_factory=dict, description="Additional conversation metadata"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def add_message(self, message: Message) -> None:
|
|
44
|
+
"""Add a message to the conversation."""
|
|
45
|
+
self.messages.append(message)
|
|
46
|
+
self.updated_at = datetime.utcnow()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
System prompt domain.
|
|
3
|
+
|
|
4
|
+
This module provides the core abstractions for building system prompts in the Vanna Agents framework.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import SystemPromptBuilder
|
|
8
|
+
from .default import DefaultSystemPromptBuilder
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"SystemPromptBuilder",
|
|
12
|
+
"DefaultSystemPromptBuilder",
|
|
13
|
+
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
System prompt builder interface.
|
|
3
|
+
|
|
4
|
+
This module contains the abstract base class for system prompt builders.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ..tool.models import ToolSchema
|
|
12
|
+
from ..user.models import User
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SystemPromptBuilder(ABC):
|
|
16
|
+
"""Abstract base class for system prompt builders.
|
|
17
|
+
|
|
18
|
+
Subclasses should implement the build_system_prompt method to generate
|
|
19
|
+
system prompts based on user context and available tools.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
async def build_system_prompt(
|
|
24
|
+
self, user: "User", tools: List["ToolSchema"]
|
|
25
|
+
) -> Optional[str]:
|
|
26
|
+
"""
|
|
27
|
+
Build a system prompt based on user context and available tools.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
user: The user making the request
|
|
31
|
+
tools: List of tools available to the user
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
System prompt string, or None if no system prompt should be used
|
|
35
|
+
"""
|
|
36
|
+
pass
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default system prompt builder implementation with memory workflow support.
|
|
3
|
+
|
|
4
|
+
This module provides a default implementation of the SystemPromptBuilder interface
|
|
5
|
+
that automatically includes memory workflow instructions when memory tools are available.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
from .base import SystemPromptBuilder
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..tool.models import ToolSchema
|
|
15
|
+
from ..user.models import User
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DefaultSystemPromptBuilder(SystemPromptBuilder):
|
|
19
|
+
"""Default system prompt builder with automatic memory workflow integration.
|
|
20
|
+
|
|
21
|
+
Dynamically generates system prompts that include memory workflow
|
|
22
|
+
instructions when memory tools (search_saved_correct_tool_uses and
|
|
23
|
+
save_question_tool_args) are available.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, base_prompt: Optional[str] = None):
|
|
27
|
+
"""Initialize with an optional base prompt.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
base_prompt: Optional base system prompt. If not provided, uses a default.
|
|
31
|
+
"""
|
|
32
|
+
self.base_prompt = base_prompt
|
|
33
|
+
|
|
34
|
+
async def build_system_prompt(
|
|
35
|
+
self, user: "User", tools: List["ToolSchema"]
|
|
36
|
+
) -> Optional[str]:
|
|
37
|
+
"""
|
|
38
|
+
Build a system prompt with memory workflow instructions.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
user: The user making the request
|
|
42
|
+
tools: List of tools available to the user
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
System prompt string with memory workflow instructions if applicable
|
|
46
|
+
"""
|
|
47
|
+
if self.base_prompt is not None:
|
|
48
|
+
return self.base_prompt
|
|
49
|
+
|
|
50
|
+
# Check which memory tools are available
|
|
51
|
+
tool_names = [tool.name for tool in tools]
|
|
52
|
+
has_search = "search_saved_correct_tool_uses" in tool_names
|
|
53
|
+
has_save = "save_question_tool_args" in tool_names
|
|
54
|
+
has_text_memory = "save_text_memory" in tool_names
|
|
55
|
+
|
|
56
|
+
# Get today's date
|
|
57
|
+
today_date = datetime.now().strftime("%Y-%m-%d")
|
|
58
|
+
|
|
59
|
+
# Base system prompt
|
|
60
|
+
prompt_parts = [
|
|
61
|
+
f"You are Vanna, an AI data analyst assistant created to help users with data analysis tasks. Today's date is {today_date}.",
|
|
62
|
+
"",
|
|
63
|
+
"Response Guidelines:",
|
|
64
|
+
"- Any summary of what you did or observations should be the final step.",
|
|
65
|
+
"- Use the available tools to help the user accomplish their goals.",
|
|
66
|
+
"- When you execute a query, that raw result is shown to the user outside of your response so YOU DO NOT need to include it in your response. Focus on summarizing and interpreting the results.",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
if tools:
|
|
70
|
+
prompt_parts.append(
|
|
71
|
+
f"\nYou have access to the following tools: {', '.join(tool_names)}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Add memory workflow instructions based on available tools
|
|
75
|
+
if has_search or has_save or has_text_memory:
|
|
76
|
+
prompt_parts.append("\n" + "=" * 60)
|
|
77
|
+
prompt_parts.append("MEMORY SYSTEM:")
|
|
78
|
+
prompt_parts.append("=" * 60)
|
|
79
|
+
|
|
80
|
+
if has_search or has_save:
|
|
81
|
+
prompt_parts.append("\n1. TOOL USAGE MEMORY (Structured Workflow):")
|
|
82
|
+
prompt_parts.append("-" * 50)
|
|
83
|
+
|
|
84
|
+
if has_search:
|
|
85
|
+
prompt_parts.extend(
|
|
86
|
+
[
|
|
87
|
+
"",
|
|
88
|
+
"• BEFORE executing any tool (run_sql, visualize_data, or calculator), you MUST first call search_saved_correct_tool_uses with the user's question to check if there are existing successful patterns for similar questions.",
|
|
89
|
+
"",
|
|
90
|
+
"• Review the search results (if any) to inform your approach before proceeding with other tool calls.",
|
|
91
|
+
]
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if has_save:
|
|
95
|
+
prompt_parts.extend(
|
|
96
|
+
[
|
|
97
|
+
"",
|
|
98
|
+
"• AFTER successfully executing a tool that produces correct and useful results, you MUST call save_question_tool_args to save the successful pattern for future use.",
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
if has_search or has_save:
|
|
103
|
+
prompt_parts.extend(
|
|
104
|
+
[
|
|
105
|
+
"",
|
|
106
|
+
"Example workflow:",
|
|
107
|
+
" • User asks a question",
|
|
108
|
+
f' • First: Call search_saved_correct_tool_uses(question="user\'s question")'
|
|
109
|
+
if has_search
|
|
110
|
+
else "",
|
|
111
|
+
" • Then: Execute the appropriate tool(s) based on search results and the question",
|
|
112
|
+
f' • Finally: If successful, call save_question_tool_args(question="user\'s question", tool_name="tool_used", args={{the args you used}})'
|
|
113
|
+
if has_save
|
|
114
|
+
else "",
|
|
115
|
+
"",
|
|
116
|
+
"Do NOT skip the search step, even if you think you know how to answer. Do NOT forget to save successful executions."
|
|
117
|
+
if has_search
|
|
118
|
+
else "",
|
|
119
|
+
"",
|
|
120
|
+
"The only exceptions to searching first are:",
|
|
121
|
+
' • When the user is explicitly asking about the tools themselves (like "list the tools")',
|
|
122
|
+
" • When the user is testing or asking you to demonstrate the save/search functionality itself",
|
|
123
|
+
]
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if has_text_memory:
|
|
127
|
+
prompt_parts.extend(
|
|
128
|
+
[
|
|
129
|
+
"",
|
|
130
|
+
"2. TEXT MEMORY (Domain Knowledge & Context):",
|
|
131
|
+
"-" * 50,
|
|
132
|
+
"",
|
|
133
|
+
"• save_text_memory: Save important context about the database, schema, or domain",
|
|
134
|
+
"",
|
|
135
|
+
"Use text memory to save:",
|
|
136
|
+
" • Database schema details (column meanings, data types, relationships)",
|
|
137
|
+
" • Company-specific terminology and definitions",
|
|
138
|
+
" • Query patterns or best practices for this database",
|
|
139
|
+
" • Domain knowledge about the business or data",
|
|
140
|
+
" • User preferences for queries or visualizations",
|
|
141
|
+
"",
|
|
142
|
+
"DO NOT save:",
|
|
143
|
+
" • Information already captured in tool usage memory",
|
|
144
|
+
" • One-time query results or temporary observations",
|
|
145
|
+
"",
|
|
146
|
+
"Examples:",
|
|
147
|
+
' • save_text_memory(content="The status column uses 1 for active, 0 for inactive")',
|
|
148
|
+
' • save_text_memory(content="MRR means Monthly Recurring Revenue in our schema")',
|
|
149
|
+
" • save_text_memory(content=\"Always exclude test accounts where email contains 'test'\")",
|
|
150
|
+
]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if has_search or has_save or has_text_memory:
|
|
154
|
+
# Remove empty strings from the list
|
|
155
|
+
prompt_parts = [part for part in prompt_parts if part != ""]
|
|
156
|
+
|
|
157
|
+
return "\n".join(prompt_parts)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool domain.
|
|
3
|
+
|
|
4
|
+
This module provides the core abstractions for tools in the Vanna Agents framework.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import T, Tool
|
|
8
|
+
from .models import ToolCall, ToolContext, ToolRejection, ToolResult, ToolSchema
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Tool",
|
|
12
|
+
"T",
|
|
13
|
+
"ToolCall",
|
|
14
|
+
"ToolContext",
|
|
15
|
+
"ToolRejection",
|
|
16
|
+
"ToolResult",
|
|
17
|
+
"ToolSchema",
|
|
18
|
+
]
|
vanna/core/tool/base.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool domain interface.
|
|
3
|
+
|
|
4
|
+
This module contains the abstract base class for tools.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Generic, List, Type, TypeVar
|
|
9
|
+
|
|
10
|
+
from .models import ToolContext, ToolResult, ToolSchema
|
|
11
|
+
|
|
12
|
+
# Type variable for tool argument types
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Tool(ABC, Generic[T]):
|
|
17
|
+
"""Abstract base class for tools."""
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def name(self) -> str:
|
|
22
|
+
"""Unique name for this tool."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def description(self) -> str:
|
|
28
|
+
"""Description of what this tool does."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def access_groups(self) -> List[str]:
|
|
33
|
+
"""Groups permitted to access this tool."""
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def get_args_schema(self) -> Type[T]:
|
|
38
|
+
"""Return the Pydantic model for arguments."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
async def execute(self, context: ToolContext, args: T) -> ToolResult:
|
|
43
|
+
"""Execute the tool with validated arguments.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
context: Execution context containing user, conversation_id, and request_id
|
|
47
|
+
args: Validated tool arguments
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
ToolResult with success status, result for LLM, and optional UI component
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def get_schema(self) -> ToolSchema:
|
|
55
|
+
"""Generate tool schema for LLM."""
|
|
56
|
+
from typing import Any, cast
|
|
57
|
+
|
|
58
|
+
args_model = self.get_args_schema()
|
|
59
|
+
# Get the schema - args_model should be a Pydantic model class
|
|
60
|
+
schema = (
|
|
61
|
+
cast(Any, args_model).model_json_schema()
|
|
62
|
+
if hasattr(args_model, "model_json_schema")
|
|
63
|
+
else {}
|
|
64
|
+
)
|
|
65
|
+
return ToolSchema(
|
|
66
|
+
name=self.name,
|
|
67
|
+
description=self.description,
|
|
68
|
+
parameters=schema,
|
|
69
|
+
access_groups=self.access_groups,
|
|
70
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool domain models.
|
|
3
|
+
|
|
4
|
+
This module contains data models for tool execution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field
|
|
10
|
+
|
|
11
|
+
# Import AgentMemory at runtime for Pydantic model resolution
|
|
12
|
+
from vanna.capabilities.agent_memory import AgentMemory
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ..components import UiComponent
|
|
16
|
+
from ..user.models import User
|
|
17
|
+
from ..observability import ObservabilityProvider
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ToolCall(BaseModel):
|
|
21
|
+
"""Represents a tool call from the LLM."""
|
|
22
|
+
|
|
23
|
+
id: str = Field(description="Unique identifier for this tool call")
|
|
24
|
+
name: str = Field(description="Name of the tool to execute")
|
|
25
|
+
arguments: Dict[str, Any] = Field(description="Raw arguments from LLM")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ToolContext(BaseModel):
|
|
29
|
+
"""Context passed to all tool executions."""
|
|
30
|
+
|
|
31
|
+
user: "User" # Forward reference to avoid circular import
|
|
32
|
+
conversation_id: str
|
|
33
|
+
request_id: str = Field(description="Unique request identifier for tracing")
|
|
34
|
+
agent_memory: AgentMemory = Field(
|
|
35
|
+
description="Agent memory for tool usage learning"
|
|
36
|
+
)
|
|
37
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
38
|
+
observability_provider: Optional["ObservabilityProvider"] = Field(
|
|
39
|
+
default=None,
|
|
40
|
+
description="Optional observability provider for metrics and spans",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
class Config:
|
|
44
|
+
arbitrary_types_allowed = True
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ToolResult(BaseModel):
|
|
48
|
+
"""Result from tool execution.
|
|
49
|
+
|
|
50
|
+
Changes:
|
|
51
|
+
- `result_for_llm`: string that will be sent back to the LLM.
|
|
52
|
+
- `ui_component`: optional UI payload for rendering in clients.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
success: bool = Field(description="Whether execution succeeded")
|
|
56
|
+
result_for_llm: str = Field(description="String content to send back to the LLM")
|
|
57
|
+
ui_component: Optional["UiComponent"] = Field(
|
|
58
|
+
default=None, description="Optional UI component for rendering"
|
|
59
|
+
)
|
|
60
|
+
error: Optional[str] = Field(default=None, description="Error message if failed")
|
|
61
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ToolSchema(BaseModel):
|
|
65
|
+
"""Schema describing a tool for LLM consumption."""
|
|
66
|
+
|
|
67
|
+
name: str = Field(description="Tool name")
|
|
68
|
+
description: str = Field(description="What this tool does")
|
|
69
|
+
parameters: Dict[str, Any] = Field(description="JSON Schema of parameters")
|
|
70
|
+
access_groups: List[str] = Field(
|
|
71
|
+
default_factory=list, description="Groups permitted to access this tool"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ToolRejection(BaseModel):
|
|
76
|
+
"""Indicates tool execution should be rejected with a message.
|
|
77
|
+
|
|
78
|
+
Used by transform_args to reject tool execution when arguments
|
|
79
|
+
cannot be appropriately transformed for the user's context.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
reason: str = Field(
|
|
83
|
+
description="Explanation of why the tool execution was rejected"
|
|
84
|
+
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User domain.
|
|
3
|
+
|
|
4
|
+
This module provides the core abstractions for user management in the Vanna Agents framework.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .base import UserService
|
|
8
|
+
from .models import User
|
|
9
|
+
from .resolver import UserResolver
|
|
10
|
+
from .request_context import RequestContext
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"UserService",
|
|
14
|
+
"User",
|
|
15
|
+
"UserResolver",
|
|
16
|
+
"RequestContext",
|
|
17
|
+
]
|
vanna/core/user/base.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User domain interface.
|
|
3
|
+
|
|
4
|
+
This module contains the abstract base class for user services.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from .models import User
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UserService(ABC):
|
|
14
|
+
"""Service for user management and authentication."""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def get_user(self, user_id: str) -> Optional[User]:
|
|
18
|
+
"""Get user by ID."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def authenticate(self, credentials: Dict[str, Any]) -> Optional[User]:
|
|
23
|
+
"""Authenticate user and return User object if successful."""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def has_permission(self, user: User, permission: str) -> bool:
|
|
28
|
+
"""Check if user has specific permission."""
|
|
29
|
+
pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User domain models.
|
|
3
|
+
|
|
4
|
+
This module contains data models for user management.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class User(BaseModel):
|
|
13
|
+
"""User model for authentication and scoping."""
|
|
14
|
+
|
|
15
|
+
id: str = Field(description="Unique user identifier")
|
|
16
|
+
username: Optional[str] = Field(default=None, description="Username")
|
|
17
|
+
email: Optional[str] = Field(default=None, description="User email")
|
|
18
|
+
metadata: Dict[str, Any] = Field(
|
|
19
|
+
default_factory=dict, description="Additional user metadata"
|
|
20
|
+
)
|
|
21
|
+
group_memberships: List[str] = Field(
|
|
22
|
+
default_factory=list, description="Groups the user belongs to"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
model_config = ConfigDict(extra="allow")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Request context for user resolution.
|
|
3
|
+
|
|
4
|
+
This module provides the RequestContext model for passing web request
|
|
5
|
+
information to UserResolver implementations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RequestContext(BaseModel):
|
|
14
|
+
"""Context from a web request for user resolution.
|
|
15
|
+
|
|
16
|
+
This structured object replaces raw dictionaries for passing request
|
|
17
|
+
data to UserResolver implementations, making it easier to access
|
|
18
|
+
cookies, headers, and other request metadata.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
context = RequestContext(
|
|
22
|
+
cookies={'vanna_email': 'alice@example.com'},
|
|
23
|
+
headers={'Authorization': 'Bearer token'},
|
|
24
|
+
remote_addr='127.0.0.1'
|
|
25
|
+
)
|
|
26
|
+
user = await resolver.resolve_user(context)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
cookies: Dict[str, str] = Field(default_factory=dict, description="Request cookies")
|
|
30
|
+
|
|
31
|
+
headers: Dict[str, str] = Field(default_factory=dict, description="Request headers")
|
|
32
|
+
|
|
33
|
+
remote_addr: Optional[str] = Field(default=None, description="Remote IP address")
|
|
34
|
+
|
|
35
|
+
query_params: Dict[str, str] = Field(
|
|
36
|
+
default_factory=dict, description="Query parameters"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
metadata: Dict[str, Any] = Field(
|
|
40
|
+
default_factory=dict, description="Additional framework-specific metadata"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def get_cookie(self, name: str, default: Optional[str] = None) -> Optional[str]:
|
|
44
|
+
"""Get cookie value by name.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
name: Cookie name
|
|
48
|
+
default: Default value if cookie not found
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Cookie value or default
|
|
52
|
+
"""
|
|
53
|
+
return self.cookies.get(name, default)
|
|
54
|
+
|
|
55
|
+
def get_header(self, name: str, default: Optional[str] = None) -> Optional[str]:
|
|
56
|
+
"""Get header value by name (case-insensitive).
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
name: Header name
|
|
60
|
+
default: Default value if header not found
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Header value or default
|
|
64
|
+
"""
|
|
65
|
+
# Case-insensitive header lookup
|
|
66
|
+
name_lower = name.lower()
|
|
67
|
+
for key, value in self.headers.items():
|
|
68
|
+
if key.lower() == name_lower:
|
|
69
|
+
return value
|
|
70
|
+
return default
|