dao-ai 0.1.7__py3-none-any.whl → 0.1.8__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/config.py +38 -16
- dao_ai/tools/mcp.py +60 -40
- {dao_ai-0.1.7.dist-info → dao_ai-0.1.8.dist-info}/METADATA +1 -1
- {dao_ai-0.1.7.dist-info → dao_ai-0.1.8.dist-info}/RECORD +7 -7
- {dao_ai-0.1.7.dist-info → dao_ai-0.1.8.dist-info}/WHEEL +0 -0
- {dao_ai-0.1.7.dist-info → dao_ai-0.1.8.dist-info}/entry_points.txt +0 -0
- {dao_ai-0.1.7.dist-info → dao_ai-0.1.8.dist-info}/licenses/LICENSE +0 -0
dao_ai/config.py
CHANGED
|
@@ -418,11 +418,11 @@ class SchemaModel(BaseModel, HasFullName):
|
|
|
418
418
|
class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
419
419
|
"""
|
|
420
420
|
Configuration for a Databricks App resource.
|
|
421
|
-
|
|
421
|
+
|
|
422
422
|
The `name` is the unique instance name of the Databricks App within the workspace.
|
|
423
|
-
The `url` is dynamically retrieved from the workspace client by calling
|
|
423
|
+
The `url` is dynamically retrieved from the workspace client by calling
|
|
424
424
|
`apps.get(name)` and returning the app's URL.
|
|
425
|
-
|
|
425
|
+
|
|
426
426
|
Example:
|
|
427
427
|
```yaml
|
|
428
428
|
resources:
|
|
@@ -431,7 +431,7 @@ class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
|
431
431
|
name: my-databricks-app
|
|
432
432
|
```
|
|
433
433
|
"""
|
|
434
|
-
|
|
434
|
+
|
|
435
435
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
436
436
|
name: str
|
|
437
437
|
"""The unique instance name of the Databricks App in the workspace."""
|
|
@@ -440,10 +440,10 @@ class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
|
440
440
|
def url(self) -> str:
|
|
441
441
|
"""
|
|
442
442
|
Retrieve the URL of the Databricks App from the workspace.
|
|
443
|
-
|
|
443
|
+
|
|
444
444
|
Returns:
|
|
445
445
|
The URL of the deployed Databricks App.
|
|
446
|
-
|
|
446
|
+
|
|
447
447
|
Raises:
|
|
448
448
|
RuntimeError: If the app is not found or URL is not available.
|
|
449
449
|
"""
|
|
@@ -455,7 +455,6 @@ class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
|
455
455
|
)
|
|
456
456
|
return app.url
|
|
457
457
|
|
|
458
|
-
|
|
459
458
|
@property
|
|
460
459
|
def full_name(self) -> str:
|
|
461
460
|
return self.name
|
|
@@ -761,11 +760,20 @@ class FunctionModel(IsDatabricksResource, HasFullName):
|
|
|
761
760
|
|
|
762
761
|
|
|
763
762
|
class WarehouseModel(IsDatabricksResource):
|
|
764
|
-
model_config = ConfigDict()
|
|
765
|
-
name: str
|
|
763
|
+
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
764
|
+
name: Optional[str] = None
|
|
766
765
|
description: Optional[str] = None
|
|
767
766
|
warehouse_id: AnyVariable
|
|
768
767
|
|
|
768
|
+
_warehouse_details: Optional[GetWarehouseResponse] = PrivateAttr(default=None)
|
|
769
|
+
|
|
770
|
+
def _get_warehouse_details(self) -> GetWarehouseResponse:
|
|
771
|
+
if self._warehouse_details is None:
|
|
772
|
+
self._warehouse_details = self.workspace_client.warehouses.get(
|
|
773
|
+
id=value_of(self.warehouse_id)
|
|
774
|
+
)
|
|
775
|
+
return self._warehouse_details
|
|
776
|
+
|
|
769
777
|
@property
|
|
770
778
|
def api_scopes(self) -> Sequence[str]:
|
|
771
779
|
return [
|
|
@@ -786,10 +794,22 @@ class WarehouseModel(IsDatabricksResource):
|
|
|
786
794
|
self.warehouse_id = value_of(self.warehouse_id)
|
|
787
795
|
return self
|
|
788
796
|
|
|
797
|
+
@model_validator(mode="after")
|
|
798
|
+
def populate_name(self) -> Self:
|
|
799
|
+
"""Populate name from warehouse details if not provided."""
|
|
800
|
+
if self.warehouse_id and not self.name:
|
|
801
|
+
try:
|
|
802
|
+
warehouse_details = self._get_warehouse_details()
|
|
803
|
+
if warehouse_details.name:
|
|
804
|
+
self.name = warehouse_details.name
|
|
805
|
+
except Exception as e:
|
|
806
|
+
logger.debug(f"Could not fetch details from warehouse: {e}")
|
|
807
|
+
return self
|
|
808
|
+
|
|
789
809
|
|
|
790
810
|
class GenieRoomModel(IsDatabricksResource):
|
|
791
811
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
792
|
-
name: str
|
|
812
|
+
name: Optional[str] = None
|
|
793
813
|
description: Optional[str] = None
|
|
794
814
|
space_id: AnyVariable
|
|
795
815
|
|
|
@@ -998,15 +1018,17 @@ class GenieRoomModel(IsDatabricksResource):
|
|
|
998
1018
|
return self
|
|
999
1019
|
|
|
1000
1020
|
@model_validator(mode="after")
|
|
1001
|
-
def
|
|
1002
|
-
"""Populate description from GenieSpace if not provided."""
|
|
1003
|
-
if not self.description:
|
|
1021
|
+
def populate_name_and_description(self) -> Self:
|
|
1022
|
+
"""Populate name and description from GenieSpace if not provided."""
|
|
1023
|
+
if self.space_id and (not self.name or not self.description):
|
|
1004
1024
|
try:
|
|
1005
1025
|
space_details = self._get_space_details()
|
|
1006
|
-
if space_details.
|
|
1026
|
+
if not self.name and space_details.title:
|
|
1027
|
+
self.name = space_details.title
|
|
1028
|
+
if not self.description and space_details.description:
|
|
1007
1029
|
self.description = space_details.description
|
|
1008
1030
|
except Exception as e:
|
|
1009
|
-
logger.debug(f"Could not fetch
|
|
1031
|
+
logger.debug(f"Could not fetch details from Genie space: {e}")
|
|
1010
1032
|
return self
|
|
1011
1033
|
|
|
1012
1034
|
|
|
@@ -2007,7 +2029,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
|
|
|
2007
2029
|
# DBSQL MCP server (serverless, workspace-level)
|
|
2008
2030
|
if self.sql:
|
|
2009
2031
|
return f"{workspace_host}/api/2.0/mcp/sql"
|
|
2010
|
-
|
|
2032
|
+
|
|
2011
2033
|
# Databricks App
|
|
2012
2034
|
if self.app:
|
|
2013
2035
|
return self.app.url
|
dao_ai/tools/mcp.py
CHANGED
|
@@ -26,9 +26,9 @@ from loguru import logger
|
|
|
26
26
|
from mcp.types import CallToolResult, TextContent, Tool
|
|
27
27
|
|
|
28
28
|
from dao_ai.config import (
|
|
29
|
+
IsDatabricksResource,
|
|
29
30
|
McpFunctionModel,
|
|
30
31
|
TransportType,
|
|
31
|
-
value_of,
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
|
|
@@ -143,12 +143,54 @@ def _should_include_tool(
|
|
|
143
143
|
return True
|
|
144
144
|
|
|
145
145
|
|
|
146
|
+
def _get_auth_resource(function: McpFunctionModel) -> IsDatabricksResource:
|
|
147
|
+
"""
|
|
148
|
+
Get the IsDatabricksResource to use for authentication.
|
|
149
|
+
|
|
150
|
+
Follows a priority hierarchy:
|
|
151
|
+
1. Explicit resource with auth (app, connection, genie_room, vector_search, functions)
|
|
152
|
+
2. McpFunctionModel itself (which also inherits from IsDatabricksResource)
|
|
153
|
+
|
|
154
|
+
Returns the resource whose workspace_client should be used for authentication.
|
|
155
|
+
"""
|
|
156
|
+
# Check each possible resource source in priority order
|
|
157
|
+
# These resources may have their own auth configured
|
|
158
|
+
if function.app:
|
|
159
|
+
return function.app
|
|
160
|
+
if function.connection:
|
|
161
|
+
return function.connection
|
|
162
|
+
if function.genie_room:
|
|
163
|
+
return function.genie_room
|
|
164
|
+
if function.vector_search:
|
|
165
|
+
return function.vector_search
|
|
166
|
+
if function.functions:
|
|
167
|
+
# SchemaModel doesn't have auth - fall through to McpFunctionModel
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
# Fall back to McpFunctionModel itself (it inherits from IsDatabricksResource)
|
|
171
|
+
return function
|
|
172
|
+
|
|
173
|
+
|
|
146
174
|
def _build_connection_config(
|
|
147
175
|
function: McpFunctionModel,
|
|
148
176
|
) -> dict[str, Any]:
|
|
149
177
|
"""
|
|
150
178
|
Build the connection configuration dictionary for MultiServerMCPClient.
|
|
151
179
|
|
|
180
|
+
Authentication Strategy:
|
|
181
|
+
-----------------------
|
|
182
|
+
For HTTP transport, authentication is handled consistently using
|
|
183
|
+
DatabricksOAuthClientProvider with the workspace_client from the appropriate
|
|
184
|
+
IsDatabricksResource. The auth resource is selected in this priority:
|
|
185
|
+
|
|
186
|
+
1. Nested resource (app, connection, genie_room, vector_search) if it has auth
|
|
187
|
+
2. McpFunctionModel itself (inherits from IsDatabricksResource)
|
|
188
|
+
|
|
189
|
+
This approach ensures:
|
|
190
|
+
- Consistent auth handling across all MCP sources
|
|
191
|
+
- Automatic token refresh for long-running connections
|
|
192
|
+
- Support for OBO, service principal, PAT, and ambient auth
|
|
193
|
+
|
|
152
194
|
Args:
|
|
153
195
|
function: The MCP function model configuration.
|
|
154
196
|
|
|
@@ -162,52 +204,30 @@ def _build_connection_config(
|
|
|
162
204
|
"transport": function.transport.value,
|
|
163
205
|
}
|
|
164
206
|
|
|
165
|
-
# For HTTP transport
|
|
166
|
-
|
|
167
|
-
from databricks_mcp import DatabricksOAuthClientProvider
|
|
207
|
+
# For HTTP transport, use DatabricksOAuthClientProvider with unified auth
|
|
208
|
+
from databricks_mcp import DatabricksOAuthClientProvider
|
|
168
209
|
|
|
169
|
-
|
|
170
|
-
|
|
210
|
+
# Get the resource to use for authentication
|
|
211
|
+
auth_resource = _get_auth_resource(function)
|
|
171
212
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
"url": function.mcp_url,
|
|
179
|
-
"transport": "http",
|
|
180
|
-
"auth": auth_provider,
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
# For HTTP transport with headers-based authentication
|
|
184
|
-
headers: dict[str, str] = {
|
|
185
|
-
key: str(value_of(val)) for key, val in function.headers.items()
|
|
186
|
-
}
|
|
213
|
+
# Get workspace client from the auth resource
|
|
214
|
+
workspace_client = auth_resource.workspace_client
|
|
215
|
+
auth_provider = DatabricksOAuthClientProvider(workspace_client)
|
|
187
216
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
client_secret=value_of(function.client_secret),
|
|
198
|
-
pat=value_of(function.pat),
|
|
199
|
-
)
|
|
200
|
-
headers["Authorization"] = f"Bearer {provider.create_token()}"
|
|
201
|
-
logger.trace("Generated fresh authentication token")
|
|
202
|
-
except Exception as e:
|
|
203
|
-
logger.error("Failed to create fresh token", error=str(e))
|
|
204
|
-
else:
|
|
205
|
-
logger.trace("Using existing authentication token")
|
|
217
|
+
# Log which resource is providing auth
|
|
218
|
+
resource_name = (
|
|
219
|
+
getattr(auth_resource, "name", None) or auth_resource.__class__.__name__
|
|
220
|
+
)
|
|
221
|
+
logger.trace(
|
|
222
|
+
"Using DatabricksOAuthClientProvider for authentication",
|
|
223
|
+
auth_resource=resource_name,
|
|
224
|
+
resource_type=auth_resource.__class__.__name__,
|
|
225
|
+
)
|
|
206
226
|
|
|
207
227
|
return {
|
|
208
228
|
"url": function.mcp_url,
|
|
209
229
|
"transport": "http",
|
|
210
|
-
"
|
|
230
|
+
"auth": auth_provider,
|
|
211
231
|
}
|
|
212
232
|
|
|
213
233
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dao-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: DAO AI: A modular, multi-agent orchestration framework for complex AI workflows. Supports agent handoff, tool integration, and dynamic configuration via YAML.
|
|
5
5
|
Project-URL: Homepage, https://github.com/natefleming/dao-ai
|
|
6
6
|
Project-URL: Documentation, https://natefleming.github.io/dao-ai
|
|
@@ -2,7 +2,7 @@ dao_ai/__init__.py,sha256=18P98ExEgUaJ1Byw440Ct1ty59v6nxyWtc5S6Uq2m9Q,1062
|
|
|
2
2
|
dao_ai/agent_as_code.py,sha256=xIlLDpPVfmDVzLvbdY_V_CrC4Jvj2ItCWJ-NzdrszTo,538
|
|
3
3
|
dao_ai/catalog.py,sha256=sPZpHTD3lPx4EZUtIWeQV7VQM89WJ6YH__wluk1v2lE,4947
|
|
4
4
|
dao_ai/cli.py,sha256=7LGrVDRgSBpznr8c8EksAhzPW_8NJ9h4St3DSpx-0z4,48196
|
|
5
|
-
dao_ai/config.py,sha256=
|
|
5
|
+
dao_ai/config.py,sha256=OSAsqb2Rxn3ghnM5Nq7-wh13DizHzWI_6cxuRuT4_j8,125773
|
|
6
6
|
dao_ai/graph.py,sha256=1-uQlo7iXZQTT3uU8aYu0N5rnhw5_g_2YLwVsAs6M-U,1119
|
|
7
7
|
dao_ai/logging.py,sha256=lYy4BmucCHvwW7aI3YQkQXKJtMvtTnPDu9Hnd7_O4oc,1556
|
|
8
8
|
dao_ai/messages.py,sha256=4ZBzO4iFdktGSLrmhHzFjzMIt2tpaL-aQLHOQJysGnY,6959
|
|
@@ -55,7 +55,7 @@ dao_ai/tools/agent.py,sha256=plIWALywRjaDSnot13nYehBsrHRpBUpsVZakoGeajOE,1858
|
|
|
55
55
|
dao_ai/tools/core.py,sha256=bRIN3BZhRQX8-Kpu3HPomliodyskCqjxynQmYbk6Vjs,3783
|
|
56
56
|
dao_ai/tools/email.py,sha256=A3TsCoQgJR7UUWR0g45OPRGDpVoYwctFs1MOZMTt_d4,7389
|
|
57
57
|
dao_ai/tools/genie.py,sha256=4e_5MeAe7kDzHbYeXuNPFbY5z8ci3ouj8l5254CZ2lA,8874
|
|
58
|
-
dao_ai/tools/mcp.py,sha256=
|
|
58
|
+
dao_ai/tools/mcp.py,sha256=ZNalYo2atZECatZjMT8w4mHEsaUZJQ_fsCjia7px1nc,18689
|
|
59
59
|
dao_ai/tools/memory.py,sha256=lwObKimAand22Nq3Y63tsv-AXQ5SXUigN9PqRjoWKes,1836
|
|
60
60
|
dao_ai/tools/python.py,sha256=jWFnZPni2sCdtd8D1CqXnZIPHnWkdK27bCJnBXpzhvo,1879
|
|
61
61
|
dao_ai/tools/search.py,sha256=cJ3D9FKr1GAR6xz55dLtRkjtQsI0WRueGt9TPDFpOxc,433
|
|
@@ -64,8 +64,8 @@ dao_ai/tools/sql.py,sha256=tKd1gjpLuKdQDyfmyYYtMiNRHDW6MGRbdEVaeqyB8Ok,7632
|
|
|
64
64
|
dao_ai/tools/time.py,sha256=tufJniwivq29y0LIffbgeBTIDE6VgrLpmVf8Qr90qjw,9224
|
|
65
65
|
dao_ai/tools/unity_catalog.py,sha256=AjQfW7bvV8NurqDLIyntYRv2eJuTwNdbvex1L5CRjOk,15534
|
|
66
66
|
dao_ai/tools/vector_search.py,sha256=oe2uBwl2TfeJIXPpwiS6Rmz7wcHczSxNyqS9P3hE6co,14542
|
|
67
|
-
dao_ai-0.1.
|
|
68
|
-
dao_ai-0.1.
|
|
69
|
-
dao_ai-0.1.
|
|
70
|
-
dao_ai-0.1.
|
|
71
|
-
dao_ai-0.1.
|
|
67
|
+
dao_ai-0.1.8.dist-info/METADATA,sha256=kbbCJVZAI-U3czxwWr9z36m14PQk8poQdzOB_6RRLII,16685
|
|
68
|
+
dao_ai-0.1.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
69
|
+
dao_ai-0.1.8.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
|
|
70
|
+
dao_ai-0.1.8.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
|
|
71
|
+
dao_ai-0.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|