dao-ai 0.1.2__py3-none-any.whl → 0.1.20__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.
- dao_ai/apps/__init__.py +24 -0
- dao_ai/apps/handlers.py +105 -0
- dao_ai/apps/model_serving.py +29 -0
- dao_ai/apps/resources.py +1122 -0
- dao_ai/apps/server.py +39 -0
- dao_ai/cli.py +546 -37
- dao_ai/config.py +1179 -139
- dao_ai/evaluation.py +543 -0
- dao_ai/genie/__init__.py +55 -7
- dao_ai/genie/cache/__init__.py +34 -7
- dao_ai/genie/cache/base.py +143 -2
- dao_ai/genie/cache/context_aware/__init__.py +31 -0
- dao_ai/genie/cache/context_aware/base.py +1151 -0
- dao_ai/genie/cache/context_aware/in_memory.py +609 -0
- dao_ai/genie/cache/context_aware/persistent.py +802 -0
- dao_ai/genie/cache/context_aware/postgres.py +1166 -0
- dao_ai/genie/cache/core.py +1 -1
- dao_ai/genie/cache/lru.py +257 -75
- dao_ai/genie/cache/optimization.py +890 -0
- dao_ai/genie/core.py +235 -11
- dao_ai/memory/postgres.py +175 -39
- dao_ai/middleware/__init__.py +38 -0
- dao_ai/middleware/assertions.py +3 -3
- dao_ai/middleware/context_editing.py +230 -0
- dao_ai/middleware/core.py +4 -4
- dao_ai/middleware/guardrails.py +3 -3
- dao_ai/middleware/human_in_the_loop.py +3 -2
- dao_ai/middleware/message_validation.py +4 -4
- dao_ai/middleware/model_call_limit.py +77 -0
- dao_ai/middleware/model_retry.py +121 -0
- dao_ai/middleware/pii.py +157 -0
- dao_ai/middleware/summarization.py +1 -1
- dao_ai/middleware/tool_call_limit.py +210 -0
- dao_ai/middleware/tool_retry.py +174 -0
- dao_ai/middleware/tool_selector.py +129 -0
- dao_ai/models.py +327 -370
- dao_ai/nodes.py +9 -16
- dao_ai/orchestration/core.py +33 -9
- dao_ai/orchestration/supervisor.py +29 -13
- dao_ai/orchestration/swarm.py +6 -1
- dao_ai/{prompts.py → prompts/__init__.py} +12 -61
- dao_ai/prompts/instructed_retriever_decomposition.yaml +58 -0
- dao_ai/prompts/instruction_reranker.yaml +14 -0
- dao_ai/prompts/router.yaml +37 -0
- dao_ai/prompts/verifier.yaml +46 -0
- dao_ai/providers/base.py +28 -2
- dao_ai/providers/databricks.py +363 -33
- dao_ai/state.py +1 -0
- dao_ai/tools/__init__.py +5 -3
- dao_ai/tools/genie.py +103 -26
- dao_ai/tools/instructed_retriever.py +366 -0
- dao_ai/tools/instruction_reranker.py +202 -0
- dao_ai/tools/mcp.py +539 -97
- dao_ai/tools/router.py +89 -0
- dao_ai/tools/slack.py +13 -2
- dao_ai/tools/sql.py +7 -3
- dao_ai/tools/unity_catalog.py +32 -10
- dao_ai/tools/vector_search.py +493 -160
- dao_ai/tools/verifier.py +159 -0
- dao_ai/utils.py +182 -2
- dao_ai/vector_search.py +46 -1
- {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/METADATA +45 -9
- dao_ai-0.1.20.dist-info/RECORD +89 -0
- dao_ai/agent_as_code.py +0 -22
- dao_ai/genie/cache/semantic.py +0 -970
- dao_ai-0.1.2.dist-info/RECORD +0 -64
- {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/WHEEL +0 -0
- {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/entry_points.txt +0 -0
- {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/licenses/LICENSE +0 -0
dao_ai/genie/cache/base.py
CHANGED
|
@@ -13,10 +13,92 @@ from dataclasses import dataclass
|
|
|
13
13
|
from datetime import datetime
|
|
14
14
|
from typing import TYPE_CHECKING
|
|
15
15
|
|
|
16
|
-
from
|
|
16
|
+
from databricks.sdk import WorkspaceClient
|
|
17
|
+
from databricks.sdk.service.dashboards import GenieFeedbackRating
|
|
18
|
+
from loguru import logger
|
|
17
19
|
|
|
18
20
|
if TYPE_CHECKING:
|
|
19
21
|
from dao_ai.genie.cache.base import CacheResult
|
|
22
|
+
from dao_ai.genie.core import GenieResponse
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_latest_message_id(
|
|
26
|
+
workspace_client: WorkspaceClient,
|
|
27
|
+
space_id: str,
|
|
28
|
+
conversation_id: str,
|
|
29
|
+
) -> str | None:
|
|
30
|
+
"""
|
|
31
|
+
Look up the most recent message ID for a conversation.
|
|
32
|
+
|
|
33
|
+
This is used when sending feedback without a specific message_id.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
workspace_client: The Databricks workspace client
|
|
37
|
+
space_id: The Genie space ID
|
|
38
|
+
conversation_id: The conversation ID to look up
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
The message_id of the most recent message, or None if not found
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
response = workspace_client.genie.list_conversation_messages(
|
|
45
|
+
space_id=space_id,
|
|
46
|
+
conversation_id=conversation_id,
|
|
47
|
+
)
|
|
48
|
+
if response.messages:
|
|
49
|
+
# Messages are returned in order; get the last one (most recent)
|
|
50
|
+
messages = list(response.messages)
|
|
51
|
+
if messages:
|
|
52
|
+
# Use message_id if available, fall back to id (legacy)
|
|
53
|
+
latest = messages[-1]
|
|
54
|
+
return latest.message_id if latest.message_id else latest.id
|
|
55
|
+
return None
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.warning(
|
|
58
|
+
"Failed to look up latest message_id",
|
|
59
|
+
space_id=space_id,
|
|
60
|
+
conversation_id=conversation_id,
|
|
61
|
+
error=str(e),
|
|
62
|
+
)
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_message_content(
|
|
67
|
+
workspace_client: WorkspaceClient,
|
|
68
|
+
space_id: str,
|
|
69
|
+
conversation_id: str,
|
|
70
|
+
message_id: str,
|
|
71
|
+
) -> str | None:
|
|
72
|
+
"""
|
|
73
|
+
Get the content (question text) of a specific message.
|
|
74
|
+
|
|
75
|
+
This is used to find matching cache entries when invalidating on negative feedback.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
workspace_client: The Databricks workspace client
|
|
79
|
+
space_id: The Genie space ID
|
|
80
|
+
conversation_id: The conversation ID
|
|
81
|
+
message_id: The message ID to look up
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
The message content (question text), or None if not found
|
|
85
|
+
"""
|
|
86
|
+
try:
|
|
87
|
+
message = workspace_client.genie.get_message(
|
|
88
|
+
space_id=space_id,
|
|
89
|
+
conversation_id=conversation_id,
|
|
90
|
+
message_id=message_id,
|
|
91
|
+
)
|
|
92
|
+
return message.content if message else None
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.warning(
|
|
95
|
+
"Failed to get message content",
|
|
96
|
+
space_id=space_id,
|
|
97
|
+
conversation_id=conversation_id,
|
|
98
|
+
message_id=message_id,
|
|
99
|
+
error=str(e),
|
|
100
|
+
)
|
|
101
|
+
return None
|
|
20
102
|
|
|
21
103
|
|
|
22
104
|
class GenieServiceBase(ABC):
|
|
@@ -40,6 +122,46 @@ class GenieServiceBase(ABC):
|
|
|
40
122
|
"""The space ID for the Genie service."""
|
|
41
123
|
pass
|
|
42
124
|
|
|
125
|
+
@property
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def workspace_client(self) -> WorkspaceClient | None:
|
|
128
|
+
"""Optional WorkspaceClient for API operations like from_space() and feedback."""
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
@abstractmethod
|
|
132
|
+
def send_feedback(
|
|
133
|
+
self,
|
|
134
|
+
conversation_id: str,
|
|
135
|
+
rating: GenieFeedbackRating,
|
|
136
|
+
message_id: str | None = None,
|
|
137
|
+
was_cache_hit: bool = False,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Send feedback for a Genie message.
|
|
141
|
+
|
|
142
|
+
This method sends user feedback (positive/negative/none) to the Genie service
|
|
143
|
+
and handles cache invalidation for negative feedback.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
conversation_id: The conversation containing the message
|
|
147
|
+
rating: The feedback rating (POSITIVE, NEGATIVE, or NONE)
|
|
148
|
+
message_id: Optional message ID. If None, looks up the most recent message
|
|
149
|
+
in the conversation.
|
|
150
|
+
was_cache_hit: Whether the response being rated was served from cache.
|
|
151
|
+
If True and rating is NEGATIVE, the cache entry is invalidated but
|
|
152
|
+
no feedback is sent to the Genie API (since no Genie message exists
|
|
153
|
+
for cached responses).
|
|
154
|
+
|
|
155
|
+
Note:
|
|
156
|
+
For cached responses (was_cache_hit=True), the message_id from the
|
|
157
|
+
original Genie response is stored in the cache entry. This enables
|
|
158
|
+
sending feedback to Genie even for cache hits.
|
|
159
|
+
|
|
160
|
+
The cache_entry_id in CacheResult can be used to trace which cache
|
|
161
|
+
entry was used for a particular prompt in genie_prompt_history.
|
|
162
|
+
"""
|
|
163
|
+
pass
|
|
164
|
+
|
|
43
165
|
|
|
44
166
|
@dataclass
|
|
45
167
|
class SQLCacheEntry:
|
|
@@ -48,12 +170,22 @@ class SQLCacheEntry:
|
|
|
48
170
|
|
|
49
171
|
Instead of caching the full result, we cache the SQL query so that
|
|
50
172
|
on cache hit we can re-execute it to get fresh data.
|
|
173
|
+
|
|
174
|
+
Attributes:
|
|
175
|
+
query: The SQL query to execute
|
|
176
|
+
description: Description of the query
|
|
177
|
+
conversation_id: The conversation ID where this query originated
|
|
178
|
+
created_at: When the entry was created
|
|
179
|
+
message_id: The original Genie message ID (for feedback on cache hits)
|
|
180
|
+
cache_entry_id: The database row ID (for persistent caches)
|
|
51
181
|
"""
|
|
52
182
|
|
|
53
183
|
query: str
|
|
54
184
|
description: str
|
|
55
185
|
conversation_id: str
|
|
56
186
|
created_at: datetime
|
|
187
|
+
message_id: str | None = None
|
|
188
|
+
cache_entry_id: int | None = None
|
|
57
189
|
|
|
58
190
|
|
|
59
191
|
@dataclass
|
|
@@ -62,11 +194,20 @@ class CacheResult:
|
|
|
62
194
|
Result of a cache-aware query with metadata about cache behavior.
|
|
63
195
|
|
|
64
196
|
Attributes:
|
|
65
|
-
response: The GenieResponse (fresh data, possibly from cached SQL)
|
|
197
|
+
response: The GenieResponse (fresh data, possibly from cached SQL).
|
|
198
|
+
This is the extended dao_ai.genie.GenieResponse which includes message_id,
|
|
199
|
+
not the base databricks_ai_bridge.genie.GenieResponse.
|
|
66
200
|
cache_hit: Whether the SQL query came from cache
|
|
67
201
|
served_by: Name of the layer that served the cached SQL (None if from origin)
|
|
202
|
+
message_id: The Genie message ID (for sending feedback). Available from
|
|
203
|
+
the extended Genie class on cache miss, or from stored cache entry on hit.
|
|
204
|
+
cache_entry_id: The database row ID of the cache entry that served this hit.
|
|
205
|
+
Only populated on cache hits from persistent caches (PostgreSQL).
|
|
206
|
+
Can be used to trace back to genie_prompt_history entries.
|
|
68
207
|
"""
|
|
69
208
|
|
|
70
209
|
response: GenieResponse
|
|
71
210
|
cache_hit: bool
|
|
72
211
|
served_by: str | None = None
|
|
212
|
+
message_id: str | None = None
|
|
213
|
+
cache_entry_id: int | None = None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Context-aware cache implementations for Genie SQL queries.
|
|
3
|
+
|
|
4
|
+
This package provides context-aware caching layers that use semantic similarity
|
|
5
|
+
matching with dual embeddings (question + conversation context) for high-precision
|
|
6
|
+
cache lookups.
|
|
7
|
+
|
|
8
|
+
Available implementations:
|
|
9
|
+
- InMemoryContextAwareGenieService: In-memory cache with L2 distance matching
|
|
10
|
+
- PostgresContextAwareGenieService: PostgreSQL pg_vector-based persistent cache
|
|
11
|
+
|
|
12
|
+
Base classes:
|
|
13
|
+
- ContextAwareGenieService: Abstract base for all context-aware cache implementations
|
|
14
|
+
- PersistentContextAwareGenieCacheService: Abstract base for database-backed implementations
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from dao_ai.genie.cache.context_aware.base import ContextAwareGenieService
|
|
18
|
+
from dao_ai.genie.cache.context_aware.in_memory import InMemoryContextAwareGenieService
|
|
19
|
+
from dao_ai.genie.cache.context_aware.persistent import (
|
|
20
|
+
PersistentContextAwareGenieCacheService,
|
|
21
|
+
)
|
|
22
|
+
from dao_ai.genie.cache.context_aware.postgres import PostgresContextAwareGenieService
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
# Base classes
|
|
26
|
+
"ContextAwareGenieService",
|
|
27
|
+
"PersistentContextAwareGenieCacheService",
|
|
28
|
+
# Implementations
|
|
29
|
+
"InMemoryContextAwareGenieService",
|
|
30
|
+
"PostgresContextAwareGenieService",
|
|
31
|
+
]
|