dao-ai 0.1.15__py3-none-any.whl → 0.1.17__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/cli.py +4 -1
- dao_ai/config.py +58 -12
- dao_ai/orchestration/swarm.py +6 -1
- dao_ai/tools/mcp.py +21 -10
- {dao_ai-0.1.15.dist-info → dao_ai-0.1.17.dist-info}/METADATA +2 -2
- {dao_ai-0.1.15.dist-info → dao_ai-0.1.17.dist-info}/RECORD +9 -9
- {dao_ai-0.1.15.dist-info → dao_ai-0.1.17.dist-info}/WHEEL +0 -0
- {dao_ai-0.1.15.dist-info → dao_ai-0.1.17.dist-info}/entry_points.txt +0 -0
- {dao_ai-0.1.15.dist-info → dao_ai-0.1.17.dist-info}/licenses/LICENSE +0 -0
dao_ai/cli.py
CHANGED
|
@@ -64,6 +64,7 @@ def detect_cloud_provider(profile: Optional[str] = None) -> Optional[str]:
|
|
|
64
64
|
"""
|
|
65
65
|
try:
|
|
66
66
|
import os
|
|
67
|
+
|
|
67
68
|
from databricks.sdk import WorkspaceClient
|
|
68
69
|
|
|
69
70
|
# Check for environment variables that might override profile
|
|
@@ -1208,7 +1209,9 @@ def run_databricks_command(
|
|
|
1208
1209
|
f"Using CLI-specified deployment target: {resolved_deployment_target}"
|
|
1209
1210
|
)
|
|
1210
1211
|
elif app_config and app_config.app and app_config.app.deployment_target:
|
|
1211
|
-
|
|
1212
|
+
# deployment_target is DeploymentTarget enum (str subclass) or string
|
|
1213
|
+
# str() works for both since DeploymentTarget inherits from str
|
|
1214
|
+
resolved_deployment_target = str(app_config.app.deployment_target)
|
|
1212
1215
|
logger.debug(
|
|
1213
1216
|
f"Using config file deployment target: {resolved_deployment_target}"
|
|
1214
1217
|
)
|
dao_ai/config.py
CHANGED
|
@@ -373,15 +373,19 @@ class IsDatabricksResource(ABC, BaseModel):
|
|
|
373
373
|
"""
|
|
374
374
|
from dao_ai.utils import normalize_host
|
|
375
375
|
|
|
376
|
-
logger.trace(
|
|
376
|
+
logger.trace(
|
|
377
|
+
"workspace_client_from called",
|
|
378
|
+
context=context,
|
|
379
|
+
on_behalf_of_user=self.on_behalf_of_user,
|
|
380
|
+
)
|
|
377
381
|
|
|
378
382
|
# Check if we have headers in context for OBO
|
|
379
383
|
if context and context.headers and self.on_behalf_of_user:
|
|
380
384
|
headers = context.headers
|
|
381
385
|
# Try both lowercase and title-case header names (HTTP headers are case-insensitive)
|
|
382
|
-
forwarded_token: str = headers.get(
|
|
383
|
-
"
|
|
384
|
-
)
|
|
386
|
+
forwarded_token: str = headers.get(
|
|
387
|
+
"x-forwarded-access-token"
|
|
388
|
+
) or headers.get("X-Forwarded-Access-Token")
|
|
385
389
|
|
|
386
390
|
if forwarded_token:
|
|
387
391
|
forwarded_user = headers.get("x-forwarded-user") or headers.get(
|
|
@@ -2090,9 +2094,48 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
|
|
|
2090
2094
|
if self.sql:
|
|
2091
2095
|
return f"{workspace_host}/api/2.0/mcp/sql"
|
|
2092
2096
|
|
|
2093
|
-
# Databricks App
|
|
2097
|
+
# Databricks App - MCP endpoint is at {app_url}/mcp
|
|
2098
|
+
# Try McpFunctionModel's workspace_client first (which may have credentials),
|
|
2099
|
+
# then fall back to DatabricksAppModel.url property (which uses its own workspace_client)
|
|
2094
2100
|
if self.app:
|
|
2095
|
-
|
|
2101
|
+
from databricks.sdk.service.apps import App
|
|
2102
|
+
|
|
2103
|
+
app_url: str | None = None
|
|
2104
|
+
|
|
2105
|
+
# First, try using McpFunctionModel's workspace_client
|
|
2106
|
+
try:
|
|
2107
|
+
app: App = self.workspace_client.apps.get(self.app.name)
|
|
2108
|
+
app_url = app.url
|
|
2109
|
+
logger.trace(
|
|
2110
|
+
"Got app URL using McpFunctionModel workspace_client",
|
|
2111
|
+
app_name=self.app.name,
|
|
2112
|
+
url=app_url,
|
|
2113
|
+
)
|
|
2114
|
+
except Exception as e:
|
|
2115
|
+
logger.debug(
|
|
2116
|
+
"Failed to get app URL using McpFunctionModel workspace_client, "
|
|
2117
|
+
"trying DatabricksAppModel.url property",
|
|
2118
|
+
app_name=self.app.name,
|
|
2119
|
+
error=str(e),
|
|
2120
|
+
)
|
|
2121
|
+
|
|
2122
|
+
# Fall back to DatabricksAppModel.url property
|
|
2123
|
+
if not app_url:
|
|
2124
|
+
try:
|
|
2125
|
+
app_url = self.app.url
|
|
2126
|
+
logger.trace(
|
|
2127
|
+
"Got app URL using DatabricksAppModel.url property",
|
|
2128
|
+
app_name=self.app.name,
|
|
2129
|
+
url=app_url,
|
|
2130
|
+
)
|
|
2131
|
+
except Exception as e:
|
|
2132
|
+
raise RuntimeError(
|
|
2133
|
+
f"Databricks App '{self.app.name}' does not have a URL. "
|
|
2134
|
+
"The app may not be deployed yet, or credentials may be invalid. "
|
|
2135
|
+
f"Error: {e}"
|
|
2136
|
+
) from e
|
|
2137
|
+
|
|
2138
|
+
return f"{app_url.rstrip('/')}/mcp"
|
|
2096
2139
|
|
|
2097
2140
|
# Vector Search
|
|
2098
2141
|
if self.vector_search:
|
|
@@ -2607,7 +2650,6 @@ class SupervisorModel(BaseModel):
|
|
|
2607
2650
|
|
|
2608
2651
|
class SwarmModel(BaseModel):
|
|
2609
2652
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
2610
|
-
model: LLMModel
|
|
2611
2653
|
default_agent: Optional[AgentModel | str] = None
|
|
2612
2654
|
middleware: list[MiddlewareModel] = Field(
|
|
2613
2655
|
default_factory=list,
|
|
@@ -2621,11 +2663,17 @@ class SwarmModel(BaseModel):
|
|
|
2621
2663
|
class OrchestrationModel(BaseModel):
|
|
2622
2664
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
2623
2665
|
supervisor: Optional[SupervisorModel] = None
|
|
2624
|
-
swarm: Optional[SwarmModel] = None
|
|
2666
|
+
swarm: Optional[SwarmModel | Literal[True]] = None
|
|
2625
2667
|
memory: Optional[MemoryModel] = None
|
|
2626
2668
|
|
|
2627
2669
|
@model_validator(mode="after")
|
|
2628
|
-
def
|
|
2670
|
+
def validate_and_normalize(self) -> Self:
|
|
2671
|
+
"""Validate orchestration and normalize swarm shorthand."""
|
|
2672
|
+
# Convert swarm: true to SwarmModel()
|
|
2673
|
+
if self.swarm is True:
|
|
2674
|
+
self.swarm = SwarmModel()
|
|
2675
|
+
|
|
2676
|
+
# Validate mutually exclusive
|
|
2629
2677
|
if self.supervisor is not None and self.swarm is not None:
|
|
2630
2678
|
raise ValueError("Cannot specify both supervisor and swarm")
|
|
2631
2679
|
if self.supervisor is None and self.swarm is None:
|
|
@@ -2897,9 +2945,7 @@ class AppModel(BaseModel):
|
|
|
2897
2945
|
elif len(self.agents) == 1:
|
|
2898
2946
|
default_agent: AgentModel = self.agents[0]
|
|
2899
2947
|
self.orchestration = OrchestrationModel(
|
|
2900
|
-
swarm=SwarmModel(
|
|
2901
|
-
model=default_agent.model, default_agent=default_agent
|
|
2902
|
-
)
|
|
2948
|
+
swarm=SwarmModel(default_agent=default_agent)
|
|
2903
2949
|
)
|
|
2904
2950
|
else:
|
|
2905
2951
|
raise ValueError("At least one agent must be specified")
|
dao_ai/orchestration/swarm.py
CHANGED
|
@@ -167,8 +167,13 @@ def create_swarm_graph(config: AppConfig) -> CompiledStateGraph:
|
|
|
167
167
|
default_agent: str
|
|
168
168
|
if isinstance(swarm.default_agent, AgentModel):
|
|
169
169
|
default_agent = swarm.default_agent.name
|
|
170
|
-
|
|
170
|
+
elif swarm.default_agent is not None:
|
|
171
171
|
default_agent = swarm.default_agent
|
|
172
|
+
elif len(config.app.agents) > 0:
|
|
173
|
+
# Fallback to first agent if no default specified
|
|
174
|
+
default_agent = config.app.agents[0].name
|
|
175
|
+
else:
|
|
176
|
+
raise ValueError("Swarm requires at least one agent and a default_agent")
|
|
172
177
|
|
|
173
178
|
logger.info(
|
|
174
179
|
"Creating swarm graph",
|
dao_ai/tools/mcp.py
CHANGED
|
@@ -144,31 +144,42 @@ def _should_include_tool(
|
|
|
144
144
|
return True
|
|
145
145
|
|
|
146
146
|
|
|
147
|
+
def _has_auth_configured(resource: IsDatabricksResource) -> bool:
|
|
148
|
+
"""Check if a resource has explicit authentication configured."""
|
|
149
|
+
return bool(
|
|
150
|
+
resource.on_behalf_of_user
|
|
151
|
+
or resource.service_principal
|
|
152
|
+
or resource.client_id
|
|
153
|
+
or resource.pat
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
147
157
|
def _get_auth_resource(function: McpFunctionModel) -> IsDatabricksResource:
|
|
148
158
|
"""
|
|
149
159
|
Get the IsDatabricksResource to use for authentication.
|
|
150
160
|
|
|
151
161
|
Follows a priority hierarchy:
|
|
152
|
-
1.
|
|
162
|
+
1. Nested resource with explicit auth (app, connection, genie_room, vector_search)
|
|
153
163
|
2. McpFunctionModel itself (which also inherits from IsDatabricksResource)
|
|
154
164
|
|
|
165
|
+
Only uses a nested resource if it has authentication configured.
|
|
166
|
+
Otherwise falls back to McpFunctionModel which may have credentials set at the tool level.
|
|
167
|
+
|
|
155
168
|
Returns the resource whose workspace_client should be used for authentication.
|
|
156
169
|
"""
|
|
157
|
-
# Check each possible resource source
|
|
158
|
-
|
|
159
|
-
if function.app:
|
|
170
|
+
# Check each possible resource source - only use if it has auth configured
|
|
171
|
+
if function.app and _has_auth_configured(function.app):
|
|
160
172
|
return function.app
|
|
161
|
-
if function.connection:
|
|
173
|
+
if function.connection and _has_auth_configured(function.connection):
|
|
162
174
|
return function.connection
|
|
163
|
-
if function.genie_room:
|
|
175
|
+
if function.genie_room and _has_auth_configured(function.genie_room):
|
|
164
176
|
return function.genie_room
|
|
165
|
-
if function.vector_search:
|
|
177
|
+
if function.vector_search and _has_auth_configured(function.vector_search):
|
|
166
178
|
return function.vector_search
|
|
167
|
-
|
|
168
|
-
# SchemaModel doesn't have auth - fall through to McpFunctionModel
|
|
169
|
-
pass
|
|
179
|
+
# SchemaModel (functions) doesn't have auth - always fall through
|
|
170
180
|
|
|
171
181
|
# Fall back to McpFunctionModel itself (it inherits from IsDatabricksResource)
|
|
182
|
+
# This allows credentials to be set at the tool level
|
|
172
183
|
return function
|
|
173
184
|
|
|
174
185
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dao-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
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
|
|
@@ -235,7 +235,7 @@ app:
|
|
|
235
235
|
- *assistant
|
|
236
236
|
orchestration:
|
|
237
237
|
swarm:
|
|
238
|
-
|
|
238
|
+
default_agent: *assistant
|
|
239
239
|
```
|
|
240
240
|
|
|
241
241
|
**💡 What's happening here?**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
dao_ai/__init__.py,sha256=18P98ExEgUaJ1Byw440Ct1ty59v6nxyWtc5S6Uq2m9Q,1062
|
|
2
2
|
dao_ai/catalog.py,sha256=sPZpHTD3lPx4EZUtIWeQV7VQM89WJ6YH__wluk1v2lE,4947
|
|
3
|
-
dao_ai/cli.py,sha256=
|
|
4
|
-
dao_ai/config.py,sha256=
|
|
3
|
+
dao_ai/cli.py,sha256=YRFbr7BC721SrNiOHRbr1dpeX1Ona76bGhO5vrDqTfk,51784
|
|
4
|
+
dao_ai/config.py,sha256=w1M4q4vbOQrCdsAlpob_2p7e8OXBi9Om0VpihMB4sH0,132150
|
|
5
5
|
dao_ai/graph.py,sha256=1-uQlo7iXZQTT3uU8aYu0N5rnhw5_g_2YLwVsAs6M-U,1119
|
|
6
6
|
dao_ai/logging.py,sha256=lYy4BmucCHvwW7aI3YQkQXKJtMvtTnPDu9Hnd7_O4oc,1556
|
|
7
7
|
dao_ai/messages.py,sha256=4ZBzO4iFdktGSLrmhHzFjzMIt2tpaL-aQLHOQJysGnY,6959
|
|
@@ -50,7 +50,7 @@ dao_ai/middleware/tool_selector.py,sha256=POj72YdzZEiNGfW4AQXPBeVVS1RUBsiG7PBuSE
|
|
|
50
50
|
dao_ai/orchestration/__init__.py,sha256=i85CLfRR335NcCFhaXABcMkn6WZfXnJ8cHH4YZsZN0s,1622
|
|
51
51
|
dao_ai/orchestration/core.py,sha256=qoU7uMXBJCth-sqfu0jRE1L0GOn5H4LoZdRUY1Ib3DI,9585
|
|
52
52
|
dao_ai/orchestration/supervisor.py,sha256=alKMEEo9G5LhdpMvTVdAMel234cZj5_MguWl4wFB7XQ,9873
|
|
53
|
-
dao_ai/orchestration/swarm.py,sha256=
|
|
53
|
+
dao_ai/orchestration/swarm.py,sha256=BloDI0TWhGisv9r3-zTgJWZQy9l3hbQ5tXYggovr5i8,9467
|
|
54
54
|
dao_ai/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
55
|
dao_ai/providers/base.py,sha256=cJGo3UjUTPgS91dv38ePOHwQQtYhIa84ebb167CBXjk,2111
|
|
56
56
|
dao_ai/providers/databricks.py,sha256=bI8lWZ2DkNac9aJWCIJzTG3lCE8MJ8n2BPurEHM1SeE,72791
|
|
@@ -59,7 +59,7 @@ dao_ai/tools/agent.py,sha256=plIWALywRjaDSnot13nYehBsrHRpBUpsVZakoGeajOE,1858
|
|
|
59
59
|
dao_ai/tools/core.py,sha256=bRIN3BZhRQX8-Kpu3HPomliodyskCqjxynQmYbk6Vjs,3783
|
|
60
60
|
dao_ai/tools/email.py,sha256=A3TsCoQgJR7UUWR0g45OPRGDpVoYwctFs1MOZMTt_d4,7389
|
|
61
61
|
dao_ai/tools/genie.py,sha256=b0R51N5D58H1vpOCUCA88ALjLs58KSMn6nl80ap8_c0,11009
|
|
62
|
-
dao_ai/tools/mcp.py,sha256=
|
|
62
|
+
dao_ai/tools/mcp.py,sha256=4uvag52OJPInUEnxFLwpE0JRugTrgHeWbkP5lzIx4lg,22620
|
|
63
63
|
dao_ai/tools/memory.py,sha256=lwObKimAand22Nq3Y63tsv-AXQ5SXUigN9PqRjoWKes,1836
|
|
64
64
|
dao_ai/tools/python.py,sha256=jWFnZPni2sCdtd8D1CqXnZIPHnWkdK27bCJnBXpzhvo,1879
|
|
65
65
|
dao_ai/tools/search.py,sha256=cJ3D9FKr1GAR6xz55dLtRkjtQsI0WRueGt9TPDFpOxc,433
|
|
@@ -68,8 +68,8 @@ dao_ai/tools/sql.py,sha256=FG-Aa0FAUAnhCuZvao1J-y-cMM6bU5eCujNbsYn0xDw,7864
|
|
|
68
68
|
dao_ai/tools/time.py,sha256=tufJniwivq29y0LIffbgeBTIDE6VgrLpmVf8Qr90qjw,9224
|
|
69
69
|
dao_ai/tools/unity_catalog.py,sha256=oBlW6pH-Ne08g60QW9wVi_tyeVYDiecuNoxQbIIFmN8,16515
|
|
70
70
|
dao_ai/tools/vector_search.py,sha256=LF_72vlEF6TwUjKVv6nkUetLK766l9Kl6DQQTc9ebJI,15888
|
|
71
|
-
dao_ai-0.1.
|
|
72
|
-
dao_ai-0.1.
|
|
73
|
-
dao_ai-0.1.
|
|
74
|
-
dao_ai-0.1.
|
|
75
|
-
dao_ai-0.1.
|
|
71
|
+
dao_ai-0.1.17.dist-info/METADATA,sha256=fLwALpNC9d6hHemJ8Pt38Sfq-JLi3YGm7xxjm_wljd8,16836
|
|
72
|
+
dao_ai-0.1.17.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
73
|
+
dao_ai-0.1.17.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
|
|
74
|
+
dao_ai-0.1.17.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
|
|
75
|
+
dao_ai-0.1.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|