dao-ai 0.1.6__py3-none-any.whl → 0.1.7__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 CHANGED
@@ -24,6 +24,7 @@ from databricks.sdk.credentials_provider import (
24
24
  ModelServingUserCredentials,
25
25
  )
26
26
  from databricks.sdk.errors.platform import NotFound
27
+ from databricks.sdk.service.apps import App
27
28
  from databricks.sdk.service.catalog import FunctionInfo, TableInfo
28
29
  from databricks.sdk.service.dashboards import GenieSpace
29
30
  from databricks.sdk.service.database import DatabaseInstance
@@ -147,7 +148,7 @@ class PrimitiveVariableModel(BaseModel, HasValue):
147
148
  return str(value)
148
149
 
149
150
  @model_validator(mode="after")
150
- def validate_value(self) -> "PrimitiveVariableModel":
151
+ def validate_value(self) -> Self:
151
152
  if not isinstance(self.as_value(), (str, int, float, bool)):
152
153
  raise ValueError("Value must be a primitive type (str, int, float, bool)")
153
154
  return self
@@ -415,15 +416,45 @@ class SchemaModel(BaseModel, HasFullName):
415
416
 
416
417
 
417
418
  class DatabricksAppModel(IsDatabricksResource, HasFullName):
419
+ """
420
+ Configuration for a Databricks App resource.
421
+
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
424
+ `apps.get(name)` and returning the app's URL.
425
+
426
+ Example:
427
+ ```yaml
428
+ resources:
429
+ apps:
430
+ my_app:
431
+ name: my-databricks-app
432
+ ```
433
+ """
434
+
418
435
  model_config = ConfigDict(use_enum_values=True, extra="forbid")
419
436
  name: str
420
- url: AnyVariable
437
+ """The unique instance name of the Databricks App in the workspace."""
438
+
439
+ @property
440
+ def url(self) -> str:
441
+ """
442
+ Retrieve the URL of the Databricks App from the workspace.
443
+
444
+ Returns:
445
+ The URL of the deployed Databricks App.
446
+
447
+ Raises:
448
+ RuntimeError: If the app is not found or URL is not available.
449
+ """
450
+ app: App = self.workspace_client.apps.get(self.name)
451
+ if app.url is None:
452
+ raise RuntimeError(
453
+ f"Databricks App '{self.name}' does not have a URL. "
454
+ "The app may not be deployed yet."
455
+ )
456
+ return app.url
421
457
 
422
- @model_validator(mode="after")
423
- def resolve_variables(self) -> Self:
424
- """Resolve AnyVariable fields to their actual string values."""
425
- self.url = value_of(self.url)
426
- return self
427
458
 
428
459
  @property
429
460
  def full_name(self) -> str:
@@ -445,7 +476,7 @@ class TableModel(IsDatabricksResource, HasFullName):
445
476
  name: Optional[str] = None
446
477
 
447
478
  @model_validator(mode="after")
448
- def validate_name_or_schema_required(self) -> "TableModel":
479
+ def validate_name_or_schema_required(self) -> Self:
449
480
  if not self.name and not self.schema_model:
450
481
  raise ValueError(
451
482
  "Either 'name' or 'schema_model' must be provided for TableModel"
@@ -1011,7 +1042,7 @@ class VolumePathModel(BaseModel, HasFullName):
1011
1042
  path: Optional[str] = None
1012
1043
 
1013
1044
  @model_validator(mode="after")
1014
- def validate_path_or_volume(self) -> "VolumePathModel":
1045
+ def validate_path_or_volume(self) -> Self:
1015
1046
  if not self.volume and not self.path:
1016
1047
  raise ValueError("Either 'volume' or 'path' must be provided")
1017
1048
  return self
@@ -1853,6 +1884,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1853
1884
  headers: dict[str, AnyVariable] = Field(default_factory=dict)
1854
1885
  args: list[str] = Field(default_factory=list)
1855
1886
  # MCP-specific fields
1887
+ app: Optional[DatabricksAppModel] = None
1856
1888
  connection: Optional[ConnectionModel] = None
1857
1889
  functions: Optional[SchemaModel] = None
1858
1890
  genie_room: Optional[GenieRoomModel] = None
@@ -1940,6 +1972,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1940
1972
 
1941
1973
  Returns the URL based on the configured source:
1942
1974
  - If url is set, returns it directly
1975
+ - If app is set, retrieves URL from Databricks App via workspace client
1943
1976
  - If connection is set, constructs URL from connection
1944
1977
  - If genie_room is set, constructs Genie MCP URL
1945
1978
  - If sql is set, constructs DBSQL MCP URL (serverless)
@@ -1952,6 +1985,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1952
1985
  - Vector Search: https://{host}/api/2.0/mcp/vector-search/{catalog}/{schema}
1953
1986
  - UC Functions: https://{host}/api/2.0/mcp/functions/{catalog}/{schema}
1954
1987
  - Connection: https://{host}/api/2.0/mcp/external/{connection_name}
1988
+ - Databricks App: Retrieved dynamically from workspace
1955
1989
  """
1956
1990
  # Direct URL provided
1957
1991
  if self.url:
@@ -1973,6 +2007,10 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1973
2007
  # DBSQL MCP server (serverless, workspace-level)
1974
2008
  if self.sql:
1975
2009
  return f"{workspace_host}/api/2.0/mcp/sql"
2010
+
2011
+ # Databricks App
2012
+ if self.app:
2013
+ return self.app.url
1976
2014
 
1977
2015
  # Vector Search
1978
2016
  if self.vector_search:
@@ -1983,33 +2021,35 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1983
2021
  raise ValueError(
1984
2022
  "vector_search must have an index with a schema (catalog/schema) configured"
1985
2023
  )
1986
- catalog: str = self.vector_search.index.schema_model.catalog_name
1987
- schema: str = self.vector_search.index.schema_model.schema_name
2024
+ catalog: str = value_of(self.vector_search.index.schema_model.catalog_name)
2025
+ schema: str = value_of(self.vector_search.index.schema_model.schema_name)
1988
2026
  return f"{workspace_host}/api/2.0/mcp/vector-search/{catalog}/{schema}"
1989
2027
 
1990
2028
  # UC Functions MCP server
1991
2029
  if self.functions:
1992
- catalog: str = self.functions.catalog_name
1993
- schema: str = self.functions.schema_name
2030
+ catalog: str = value_of(self.functions.catalog_name)
2031
+ schema: str = value_of(self.functions.schema_name)
1994
2032
  return f"{workspace_host}/api/2.0/mcp/functions/{catalog}/{schema}"
1995
2033
 
1996
2034
  raise ValueError(
1997
- "No URL source configured. Provide one of: url, connection, genie_room, "
2035
+ "No URL source configured. Provide one of: url, app, connection, genie_room, "
1998
2036
  "sql, vector_search, or functions"
1999
2037
  )
2000
2038
 
2001
2039
  @field_serializer("transport")
2002
- def serialize_transport(self, value) -> str:
2040
+ def serialize_transport(self, value: TransportType) -> str:
2041
+ """Serialize transport enum to string."""
2003
2042
  if isinstance(value, TransportType):
2004
2043
  return value.value
2005
2044
  return str(value)
2006
2045
 
2007
2046
  @model_validator(mode="after")
2008
- def validate_mutually_exclusive(self) -> "McpFunctionModel":
2047
+ def validate_mutually_exclusive(self) -> Self:
2009
2048
  """Validate that exactly one URL source is provided."""
2010
2049
  # Count how many URL sources are provided
2011
2050
  url_sources: list[tuple[str, Any]] = [
2012
2051
  ("url", self.url),
2052
+ ("app", self.app),
2013
2053
  ("connection", self.connection),
2014
2054
  ("genie_room", self.genie_room),
2015
2055
  ("sql", self.sql),
@@ -2025,13 +2065,13 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
2025
2065
  if len(provided_sources) == 0:
2026
2066
  raise ValueError(
2027
2067
  "For STREAMABLE_HTTP transport, exactly one of the following must be provided: "
2028
- "url, connection, genie_room, sql, vector_search, or functions"
2068
+ "url, app, connection, genie_room, sql, vector_search, or functions"
2029
2069
  )
2030
2070
  if len(provided_sources) > 1:
2031
2071
  raise ValueError(
2032
2072
  f"For STREAMABLE_HTTP transport, only one URL source can be provided. "
2033
2073
  f"Found: {', '.join(provided_sources)}. "
2034
- f"Please provide only one of: url, connection, genie_room, sql, vector_search, or functions"
2074
+ f"Please provide only one of: url, app, connection, genie_room, sql, vector_search, or functions"
2035
2075
  )
2036
2076
 
2037
2077
  if self.transport == TransportType.STDIO:
@@ -2043,18 +2083,25 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
2043
2083
  return self
2044
2084
 
2045
2085
  @model_validator(mode="after")
2046
- def update_url(self) -> "McpFunctionModel":
2047
- self.url = value_of(self.url)
2086
+ def update_url(self) -> Self:
2087
+ """Resolve AnyVariable to concrete value for URL."""
2088
+ if self.url is not None:
2089
+ resolved_value: Any = value_of(self.url)
2090
+ # Cast to string since URL must be a string
2091
+ self.url = str(resolved_value) if resolved_value else None
2048
2092
  return self
2049
2093
 
2050
2094
  @model_validator(mode="after")
2051
- def update_headers(self) -> "McpFunctionModel":
2095
+ def update_headers(self) -> Self:
2096
+ """Resolve AnyVariable to concrete values for headers."""
2052
2097
  for key, value in self.headers.items():
2053
- self.headers[key] = value_of(value)
2098
+ resolved_value: Any = value_of(value)
2099
+ # Headers must be strings
2100
+ self.headers[key] = str(resolved_value) if resolved_value else ""
2054
2101
  return self
2055
2102
 
2056
2103
  @model_validator(mode="after")
2057
- def validate_tool_filters(self) -> "McpFunctionModel":
2104
+ def validate_tool_filters(self) -> Self:
2058
2105
  """Validate tool filter configuration."""
2059
2106
  from loguru import logger
2060
2107
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dao-ai
3
- Version: 0.1.6
3
+ Version: 0.1.7
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=rUm2wg0TPfj6YwzSoNxy6rgHi6GKWxXIRJ3NgGOjB04,123037
5
+ dao_ai/config.py,sha256=GY-n2PtPg4pYtO46KYpHFIGkNkCDVoPA5S7sKLjWpVc,124699
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
@@ -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.6.dist-info/METADATA,sha256=eux_1l0ANLlbQnRnq0IRgc8Q-ksY6sFmQa1_Vzd5_Kc,16685
68
- dao_ai-0.1.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
69
- dao_ai-0.1.6.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
70
- dao_ai-0.1.6.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
71
- dao_ai-0.1.6.dist-info/RECORD,,
67
+ dao_ai-0.1.7.dist-info/METADATA,sha256=jzaENv6Ic9S-uds3qTC4Pu7chwzYG8y0zvTBni4VCW8,16685
68
+ dao_ai-0.1.7.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
69
+ dao_ai-0.1.7.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
70
+ dao_ai-0.1.7.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
71
+ dao_ai-0.1.7.dist-info/RECORD,,
File without changes