dao-ai 0.1.6__tar.gz → 0.1.8__tar.gz

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.
Files changed (299) hide show
  1. {dao_ai-0.1.6 → dao_ai-0.1.8}/PKG-INFO +1 -1
  2. {dao_ai-0.1.6 → dao_ai-0.1.8}/pyproject.toml +1 -1
  3. {dao_ai-0.1.6 → dao_ai-0.1.8}/requirements.txt +0 -1
  4. {dao_ai-0.1.6 → dao_ai-0.1.8}/schemas/model_config_schema.json +34 -37
  5. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/config.py +100 -31
  6. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/mcp.py +60 -40
  7. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_config.py +20 -14
  8. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_function_parsing.py +1 -1
  9. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_genie.py +1 -0
  10. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_genie_room_model.py +157 -3
  11. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_mcp.py +52 -2
  12. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_filtering_integration.py +360 -3
  13. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_function_model.py +145 -1
  14. dao_ai-0.1.8/tests/dao_ai/test_warehouse_model.py +166 -0
  15. {dao_ai-0.1.6 → dao_ai-0.1.8}/.gitignore +0 -0
  16. {dao_ai-0.1.6 → dao_ai-0.1.8}/.python-version +0 -0
  17. {dao_ai-0.1.6 → dao_ai-0.1.8}/CHANGELOG.md +0 -0
  18. {dao_ai-0.1.6 → dao_ai-0.1.8}/CONTRIBUTING.md +0 -0
  19. {dao_ai-0.1.6 → dao_ai-0.1.8}/LICENSE +0 -0
  20. {dao_ai-0.1.6 → dao_ai-0.1.8}/Makefile +0 -0
  21. {dao_ai-0.1.6 → dao_ai-0.1.8}/README.md +0 -0
  22. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/01_getting_started/README.md +0 -0
  23. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/01_getting_started/minimal.yaml +0 -0
  24. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/02_mcp/README.md +0 -0
  25. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/02_mcp/custom_mcp.yaml +0 -0
  26. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/02_mcp/external_mcp.yaml +0 -0
  27. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/02_mcp/filtered_mcp.yaml +0 -0
  28. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/02_mcp/managed_mcp.yaml +0 -0
  29. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/02_mcp/slack_integration.yaml +0 -0
  30. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/03_reranking/README.md +0 -0
  31. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/03_reranking/vector_search_with_reranking.yaml +0 -0
  32. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/04_genie/README.md +0 -0
  33. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/04_genie/genie_basic.yaml +0 -0
  34. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/04_genie/genie_lru_cache.yaml +0 -0
  35. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/04_genie/genie_semantic_cache.yaml +0 -0
  36. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/04_genie/genie_with_conversation_id.yaml +0 -0
  37. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/05_memory/README.md +0 -0
  38. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/05_memory/conversation_summarization.yaml +0 -0
  39. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/05_memory/in_memory_basic.yaml +0 -0
  40. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/05_memory/lakebase_persistence.yaml +0 -0
  41. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/05_memory/postgres_persistence.yaml +0 -0
  42. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/06_on_behalf_of_user/README.md +0 -0
  43. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/06_on_behalf_of_user/obo_basic.yaml +0 -0
  44. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/07_human_in_the_loop/README.md +0 -0
  45. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/07_human_in_the_loop/human_in_the_loop.yaml +0 -0
  46. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/08_guardrails/README.md +0 -0
  47. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/08_guardrails/guardrails_basic.yaml +0 -0
  48. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/09_structured_output/README.md +0 -0
  49. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/09_structured_output/structured_output.yaml +0 -0
  50. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/10_agent_integrations/README.md +0 -0
  51. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/10_agent_integrations/agent_bricks.yaml +0 -0
  52. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/10_agent_integrations/kasal.yaml +0 -0
  53. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/README.md +0 -0
  54. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/prompt_optimization.yaml +0 -0
  55. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/prompt_registry.yaml +0 -0
  56. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/README.md +0 -0
  57. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/combined_middleware.yaml +0 -0
  58. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/context_management.yaml +0 -0
  59. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/custom_field_validation.yaml +0 -0
  60. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/limit_middleware.yaml +0 -0
  61. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/logging_middleware.yaml +0 -0
  62. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/pii_middleware.yaml +0 -0
  63. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/retry_middleware.yaml +0 -0
  64. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/12_middleware/tool_selector_middleware.yaml +0 -0
  65. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/13_orchestration/README.md +0 -0
  66. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/13_orchestration/supervisor_pattern.yaml +0 -0
  67. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/13_orchestration/swarm_pattern.yaml +0 -0
  68. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/14_basic_tools/README.md +0 -0
  69. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/14_basic_tools/sql_tool_example.yaml +0 -0
  70. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/README.md +0 -0
  71. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/brick_store.yaml +0 -0
  72. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/deep_research.yaml +0 -0
  73. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/executive_assistant.yaml +0 -0
  74. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/genie_and_genie_mcp.yaml +0 -0
  75. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/genie_vector_search_hybrid.yaml +0 -0
  76. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store.yaml +0 -0
  77. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store_lakebase.yaml +0 -0
  78. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store_swarm.yaml +0 -0
  79. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/quick_serve_restaurant.yaml +0 -0
  80. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/15_complete_applications/reservations_system.yaml +0 -0
  81. {dao_ai-0.1.6 → dao_ai-0.1.8}/config/examples/README.md +0 -0
  82. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/appointments.sql +0 -0
  83. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/appointments_data.sql +0 -0
  84. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_data.sql +0 -0
  85. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_queries.sql +0 -0
  86. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_tables.sql +0 -0
  87. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_validation.sql +0 -0
  88. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/customers.sql +0 -0
  89. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/customers_data.sql +0 -0
  90. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/dim_stores.sql +0 -0
  91. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/dim_stores_data.sql +0 -0
  92. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/employee_performance.sql +0 -0
  93. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/employee_performance_data.sql +0 -0
  94. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/employee_tasks.sql +0 -0
  95. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/employee_tasks_data.sql +0 -0
  96. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/inventory.sql +0 -0
  97. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/inventory_data.sql +0 -0
  98. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/managers.sql +0 -0
  99. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/managers_data.sql +0 -0
  100. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/product_data.sql +0 -0
  101. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/products.sql +0 -0
  102. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/dais2025/task_assignments.sql +0 -0
  103. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/hardware_store/inventory.snappy.parquet +0 -0
  104. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/hardware_store/inventory.sql +0 -0
  105. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/hardware_store/products.snappy.parquet +0 -0
  106. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/hardware_store/products.sql +0 -0
  107. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/.gitkeep +0 -0
  108. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/fulfil_item_orders.sql +0 -0
  109. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_description.csv +0 -0
  110. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_description.sql +0 -0
  111. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_raw.csv +0 -0
  112. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_raw.sql +0 -0
  113. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/orders_raw.csv +0 -0
  114. {dao_ai-0.1.6 → dao_ai-0.1.8}/data/quick_serve_restaurant/orders_raw.sql +0 -0
  115. {dao_ai-0.1.6 → dao_ai-0.1.8}/databricks.yaml.template +0 -0
  116. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/architecture.md +0 -0
  117. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/cli-reference.md +0 -0
  118. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/configuration-reference.md +0 -0
  119. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/contributing.md +0 -0
  120. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/examples.md +0 -0
  121. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/faq.md +0 -0
  122. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/hardware_store/README.md +0 -0
  123. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/hardware_store/retail_supervisor.png +0 -0
  124. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/hardware_store/retail_swarm.png +0 -0
  125. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/images/genie.png +0 -0
  126. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/key-capabilities.md +0 -0
  127. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/python-api.md +0 -0
  128. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/quick_serve_restaurant/.gitkeep +0 -0
  129. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/quick_serve_restaurant/quick-serve-restaurant.png +0 -0
  130. {dao_ai-0.1.6 → dao_ai-0.1.8}/docs/why-dao.md +0 -0
  131. {dao_ai-0.1.6 → dao_ai-0.1.8}/environment.yaml +0 -0
  132. {dao_ai-0.1.6 → dao_ai-0.1.8}/examples/dais2025/examples.yaml +0 -0
  133. {dao_ai-0.1.6 → dao_ai-0.1.8}/examples/deep_research/examples.yaml +0 -0
  134. {dao_ai-0.1.6 → dao_ai-0.1.8}/examples/executive_assistant/examples.yaml +0 -0
  135. {dao_ai-0.1.6 → dao_ai-0.1.8}/examples/hardware_store/examples.yaml +0 -0
  136. {dao_ai-0.1.6 → dao_ai-0.1.8}/examples/quick_serve_restaurant/.gitkeep +0 -0
  137. {dao_ai-0.1.6 → dao_ai-0.1.8}/examples/quick_serve_restaurant/examples.yaml +0 -0
  138. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/extract_store_numbers.sql +0 -0
  139. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_inventory_by_sku.sql +0 -0
  140. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_inventory_by_upc.sql +0 -0
  141. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_product_by_sku.sql +0 -0
  142. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_product_by_upc.sql +0 -0
  143. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_store_by_number.sql +0 -0
  144. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_store_inventory_by_sku.sql +0 -0
  145. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/dais2025/find_store_inventory_by_upc.sql +0 -0
  146. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/hardware_store/find_inventory_by_sku.sql +0 -0
  147. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/hardware_store/find_inventory_by_upc.sql +0 -0
  148. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/hardware_store/find_product_by_sku.sql +0 -0
  149. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/hardware_store/find_product_by_upc.sql +0 -0
  150. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/hardware_store/find_store_inventory_by_sku.sql +0 -0
  151. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/hardware_store/find_store_inventory_by_upc.sql +0 -0
  152. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/quick_serve_restaurant/.gitkeep +0 -0
  153. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/quick_serve_restaurant/insert_coffee_order.sql +0 -0
  154. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/quick_serve_restaurant/lookup_items_by_descriptions.sql +0 -0
  155. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/quick_serve_restaurant/match_historical_item_order_by_date.sql +0 -0
  156. {dao_ai-0.1.6 → dao_ai-0.1.8}/functions/quick_serve_restaurant/match_item_by_description_and_price.sql +0 -0
  157. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/01_ingest_and_transform.py +0 -0
  158. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/02_provision_vector_search.py +0 -0
  159. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/03_provision_lakebase.py +0 -0
  160. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/04_unity_catalog_tools.py +0 -0
  161. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/05_deploy_agent.py +0 -0
  162. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/06_generate_evaluation_data.py +0 -0
  163. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/07_run_evaluation.py +0 -0
  164. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/08_run_examples.py +0 -0
  165. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/09_evaluate_inferences.py +0 -0
  166. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/10_optimize_prompts.py +0 -0
  167. {dao_ai-0.1.6 → dao_ai-0.1.8}/notebooks/99_scratchpad.py +0 -0
  168. {dao_ai-0.1.6 → dao_ai-0.1.8}/schemas/bundle_config_schema.json +0 -0
  169. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/__init__.py +0 -0
  170. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/models.py +0 -0
  171. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/__init__.py +0 -0
  172. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/customer.py +0 -0
  173. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/employee.py +0 -0
  174. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/executive.py +0 -0
  175. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/genie.py +0 -0
  176. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/inventory.py +0 -0
  177. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/models.py +0 -0
  178. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dais2025/tools/store.py +0 -0
  179. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/__init__.py +0 -0
  180. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/agent_as_code.py +0 -0
  181. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/catalog.py +0 -0
  182. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/cli.py +0 -0
  183. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/__init__.py +0 -0
  184. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/cache/__init__.py +0 -0
  185. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/cache/base.py +0 -0
  186. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/cache/core.py +0 -0
  187. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/cache/lru.py +0 -0
  188. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/cache/semantic.py +0 -0
  189. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/genie/core.py +0 -0
  190. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/graph.py +0 -0
  191. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/hooks/__init__.py +0 -0
  192. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/hooks/core.py +0 -0
  193. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/logging.py +0 -0
  194. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/memory/__init__.py +0 -0
  195. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/memory/base.py +0 -0
  196. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/memory/core.py +0 -0
  197. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/memory/databricks.py +0 -0
  198. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/memory/postgres.py +0 -0
  199. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/messages.py +0 -0
  200. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/__init__.py +0 -0
  201. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/assertions.py +0 -0
  202. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/base.py +0 -0
  203. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/context_editing.py +0 -0
  204. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/core.py +0 -0
  205. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/guardrails.py +0 -0
  206. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/human_in_the_loop.py +0 -0
  207. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/message_validation.py +0 -0
  208. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/model_call_limit.py +0 -0
  209. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/model_retry.py +0 -0
  210. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/pii.py +0 -0
  211. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/summarization.py +0 -0
  212. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_call_limit.py +0 -0
  213. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_retry.py +0 -0
  214. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_selector.py +0 -0
  215. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/models.py +0 -0
  216. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/nodes.py +0 -0
  217. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/optimization.py +0 -0
  218. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/orchestration/__init__.py +0 -0
  219. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/orchestration/core.py +0 -0
  220. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/orchestration/supervisor.py +0 -0
  221. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/orchestration/swarm.py +0 -0
  222. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/prompts.py +0 -0
  223. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/providers/__init__.py +0 -0
  224. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/providers/base.py +0 -0
  225. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/providers/databricks.py +0 -0
  226. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/state.py +0 -0
  227. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/__init__.py +0 -0
  228. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/agent.py +0 -0
  229. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/core.py +0 -0
  230. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/email.py +0 -0
  231. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/genie.py +0 -0
  232. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/memory.py +0 -0
  233. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/python.py +0 -0
  234. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/search.py +0 -0
  235. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/slack.py +0 -0
  236. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/sql.py +0 -0
  237. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/time.py +0 -0
  238. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/unity_catalog.py +0 -0
  239. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/tools/vector_search.py +0 -0
  240. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/types.py +0 -0
  241. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/utils.py +0 -0
  242. {dao_ai-0.1.6 → dao_ai-0.1.8}/src/dao_ai/vector_search.py +0 -0
  243. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/config/test_model_config.yaml +0 -0
  244. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/conftest.py +0 -0
  245. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_context_editing.py +0 -0
  246. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_model_call_limit.py +0 -0
  247. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_model_retry.py +0 -0
  248. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_pii.py +0 -0
  249. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_call_limit.py +0 -0
  250. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_retry.py +0 -0
  251. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_selector.py +0 -0
  252. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_agent_response_format.py +0 -0
  253. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_assertions_middleware.py +0 -0
  254. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_catalog.py +0 -0
  255. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_chat_history.py +0 -0
  256. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_databricks.py +0 -0
  257. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_genie_conversation_ids_in_outputs.py +0 -0
  258. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_genie_databricks_integration.py +0 -0
  259. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_guardrail_retry.py +0 -0
  260. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_hitl_config_model.py +0 -0
  261. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_hitl_responses_agent.py +0 -0
  262. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_hooks.py +0 -0
  263. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_human_in_the_loop.py +0 -0
  264. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_inference.py +0 -0
  265. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_inference_integration.py +0 -0
  266. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_input_output_structure.py +0 -0
  267. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_interrupt_type.py +0 -0
  268. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_llm_interrupt_handling.py +0 -0
  269. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_filtering.py +0 -0
  270. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_message_validation_middleware.py +0 -0
  271. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_messages.py +0 -0
  272. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_models.py +0 -0
  273. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_optimization.py +0 -0
  274. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_postgres_integration.py +0 -0
  275. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_prompt_optimizations.py +0 -0
  276. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_prompts.py +0 -0
  277. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_reranking.py +0 -0
  278. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_reranking_integration.py +0 -0
  279. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_resources_model_genie_integration.py +0 -0
  280. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_response_format.py +0 -0
  281. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_responses_agent_structured_output_unit.py +0 -0
  282. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_semantic_cache_context.py +0 -0
  283. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_sql_tool.py +0 -0
  284. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_sql_tool_integration.py +0 -0
  285. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_state.py +0 -0
  286. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_summarization_inference.py +0 -0
  287. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_swarm_middleware.py +0 -0
  288. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_tools.py +0 -0
  289. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_types.py +0 -0
  290. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_unity_catalog.py +0 -0
  291. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_utils.py +0 -0
  292. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_utils_type_from_fqn.py +0 -0
  293. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/test_vector_search.py +0 -0
  294. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/dao_ai/weather_server_mcp.py +0 -0
  295. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/hardware_store/.gitkeep +0 -0
  296. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/hardware_store/test_graph.py +0 -0
  297. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/images/doritos_upc.png +0 -0
  298. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/images/lays_upc.png +0 -0
  299. {dao_ai-0.1.6 → dao_ai-0.1.8}/tests/quick_serve_restaurant/.gitkeep +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dao-ai
3
- Version: 0.1.6
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dao-ai"
7
- version = "0.1.6"
7
+ version = "0.1.8"
8
8
  description = "DAO AI: A modular, multi-agent orchestration framework for complex AI workflows. Supports agent handoff, tool integration, and dynamic configuration via YAML."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -63,7 +63,6 @@ grandalf==0.8
63
63
  graphene==3.4.3
64
64
  graphql-core==3.2.7
65
65
  graphql-relay==3.2.0
66
- greenlet==3.3.0
67
66
  grpcio==1.76.0
68
67
  grpcio-status==1.76.0
69
68
  gunicorn==23.0.0
@@ -1196,6 +1196,7 @@
1196
1196
  },
1197
1197
  "DatabricksAppModel": {
1198
1198
  "additionalProperties": false,
1199
+ "description": "Configuration for a Databricks App resource.\n\nThe `name` is the unique instance name of the Databricks App within the workspace.\nThe `url` is dynamically retrieved from the workspace client by calling\n`apps.get(name)` and returning the app's URL.\n\nExample:\n ```yaml\n resources:\n apps:\n my_app:\n name: my-databricks-app\n ```",
1199
1200
  "properties": {
1200
1201
  "on_behalf_of_user": {
1201
1202
  "anyOf": [
@@ -1355,40 +1356,10 @@
1355
1356
  "name": {
1356
1357
  "title": "Name",
1357
1358
  "type": "string"
1358
- },
1359
- "url": {
1360
- "anyOf": [
1361
- {
1362
- "$ref": "#/$defs/CompositeVariableModel"
1363
- },
1364
- {
1365
- "$ref": "#/$defs/EnvironmentVariableModel"
1366
- },
1367
- {
1368
- "$ref": "#/$defs/SecretVariableModel"
1369
- },
1370
- {
1371
- "$ref": "#/$defs/PrimitiveVariableModel"
1372
- },
1373
- {
1374
- "type": "string"
1375
- },
1376
- {
1377
- "type": "integer"
1378
- },
1379
- {
1380
- "type": "number"
1381
- },
1382
- {
1383
- "type": "boolean"
1384
- }
1385
- ],
1386
- "title": "Url"
1387
1359
  }
1388
1360
  },
1389
1361
  "required": [
1390
- "name",
1391
- "url"
1362
+ "name"
1392
1363
  ],
1393
1364
  "title": "DatabricksAppModel",
1394
1365
  "type": "object"
@@ -2082,8 +2053,16 @@
2082
2053
  "title": "Pat"
2083
2054
  },
2084
2055
  "name": {
2085
- "title": "Name",
2086
- "type": "string"
2056
+ "anyOf": [
2057
+ {
2058
+ "type": "string"
2059
+ },
2060
+ {
2061
+ "type": "null"
2062
+ }
2063
+ ],
2064
+ "default": null,
2065
+ "title": "Name"
2087
2066
  },
2088
2067
  "description": {
2089
2068
  "anyOf": [
@@ -2128,7 +2107,6 @@
2128
2107
  }
2129
2108
  },
2130
2109
  "required": [
2131
- "name",
2132
2110
  "space_id"
2133
2111
  ],
2134
2112
  "title": "GenieRoomModel",
@@ -2935,6 +2913,17 @@
2935
2913
  "title": "Args",
2936
2914
  "type": "array"
2937
2915
  },
2916
+ "app": {
2917
+ "anyOf": [
2918
+ {
2919
+ "$ref": "#/$defs/DatabricksAppModel"
2920
+ },
2921
+ {
2922
+ "type": "null"
2923
+ }
2924
+ ],
2925
+ "default": null
2926
+ },
2938
2927
  "connection": {
2939
2928
  "anyOf": [
2940
2929
  {
@@ -4932,6 +4921,7 @@
4932
4921
  "type": "object"
4933
4922
  },
4934
4923
  "WarehouseModel": {
4924
+ "additionalProperties": false,
4935
4925
  "properties": {
4936
4926
  "on_behalf_of_user": {
4937
4927
  "anyOf": [
@@ -5089,8 +5079,16 @@
5089
5079
  "title": "Pat"
5090
5080
  },
5091
5081
  "name": {
5092
- "title": "Name",
5093
- "type": "string"
5082
+ "anyOf": [
5083
+ {
5084
+ "type": "string"
5085
+ },
5086
+ {
5087
+ "type": "null"
5088
+ }
5089
+ ],
5090
+ "default": null,
5091
+ "title": "Name"
5094
5092
  },
5095
5093
  "description": {
5096
5094
  "anyOf": [
@@ -5135,7 +5133,6 @@
5135
5133
  }
5136
5134
  },
5137
5135
  "required": [
5138
- "name",
5139
5136
  "warehouse_id"
5140
5137
  ],
5141
5138
  "title": "WarehouseModel",
@@ -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,44 @@ 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."""
421
438
 
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
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
427
457
 
428
458
  @property
429
459
  def full_name(self) -> str:
@@ -445,7 +475,7 @@ class TableModel(IsDatabricksResource, HasFullName):
445
475
  name: Optional[str] = None
446
476
 
447
477
  @model_validator(mode="after")
448
- def validate_name_or_schema_required(self) -> "TableModel":
478
+ def validate_name_or_schema_required(self) -> Self:
449
479
  if not self.name and not self.schema_model:
450
480
  raise ValueError(
451
481
  "Either 'name' or 'schema_model' must be provided for TableModel"
@@ -730,11 +760,20 @@ class FunctionModel(IsDatabricksResource, HasFullName):
730
760
 
731
761
 
732
762
  class WarehouseModel(IsDatabricksResource):
733
- model_config = ConfigDict()
734
- name: str
763
+ model_config = ConfigDict(use_enum_values=True, extra="forbid")
764
+ name: Optional[str] = None
735
765
  description: Optional[str] = None
736
766
  warehouse_id: AnyVariable
737
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
+
738
777
  @property
739
778
  def api_scopes(self) -> Sequence[str]:
740
779
  return [
@@ -755,10 +794,22 @@ class WarehouseModel(IsDatabricksResource):
755
794
  self.warehouse_id = value_of(self.warehouse_id)
756
795
  return self
757
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
+
758
809
 
759
810
  class GenieRoomModel(IsDatabricksResource):
760
811
  model_config = ConfigDict(use_enum_values=True, extra="forbid")
761
- name: str
812
+ name: Optional[str] = None
762
813
  description: Optional[str] = None
763
814
  space_id: AnyVariable
764
815
 
@@ -967,15 +1018,17 @@ class GenieRoomModel(IsDatabricksResource):
967
1018
  return self
968
1019
 
969
1020
  @model_validator(mode="after")
970
- def update_description_from_space(self) -> Self:
971
- """Populate description from GenieSpace if not provided."""
972
- 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):
973
1024
  try:
974
1025
  space_details = self._get_space_details()
975
- if space_details.description:
1026
+ if not self.name and space_details.title:
1027
+ self.name = space_details.title
1028
+ if not self.description and space_details.description:
976
1029
  self.description = space_details.description
977
1030
  except Exception as e:
978
- logger.debug(f"Could not fetch description from Genie space: {e}")
1031
+ logger.debug(f"Could not fetch details from Genie space: {e}")
979
1032
  return self
980
1033
 
981
1034
 
@@ -1011,7 +1064,7 @@ class VolumePathModel(BaseModel, HasFullName):
1011
1064
  path: Optional[str] = None
1012
1065
 
1013
1066
  @model_validator(mode="after")
1014
- def validate_path_or_volume(self) -> "VolumePathModel":
1067
+ def validate_path_or_volume(self) -> Self:
1015
1068
  if not self.volume and not self.path:
1016
1069
  raise ValueError("Either 'volume' or 'path' must be provided")
1017
1070
  return self
@@ -1853,6 +1906,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1853
1906
  headers: dict[str, AnyVariable] = Field(default_factory=dict)
1854
1907
  args: list[str] = Field(default_factory=list)
1855
1908
  # MCP-specific fields
1909
+ app: Optional[DatabricksAppModel] = None
1856
1910
  connection: Optional[ConnectionModel] = None
1857
1911
  functions: Optional[SchemaModel] = None
1858
1912
  genie_room: Optional[GenieRoomModel] = None
@@ -1940,6 +1994,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1940
1994
 
1941
1995
  Returns the URL based on the configured source:
1942
1996
  - If url is set, returns it directly
1997
+ - If app is set, retrieves URL from Databricks App via workspace client
1943
1998
  - If connection is set, constructs URL from connection
1944
1999
  - If genie_room is set, constructs Genie MCP URL
1945
2000
  - If sql is set, constructs DBSQL MCP URL (serverless)
@@ -1952,6 +2007,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1952
2007
  - Vector Search: https://{host}/api/2.0/mcp/vector-search/{catalog}/{schema}
1953
2008
  - UC Functions: https://{host}/api/2.0/mcp/functions/{catalog}/{schema}
1954
2009
  - Connection: https://{host}/api/2.0/mcp/external/{connection_name}
2010
+ - Databricks App: Retrieved dynamically from workspace
1955
2011
  """
1956
2012
  # Direct URL provided
1957
2013
  if self.url:
@@ -1974,6 +2030,10 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1974
2030
  if self.sql:
1975
2031
  return f"{workspace_host}/api/2.0/mcp/sql"
1976
2032
 
2033
+ # Databricks App
2034
+ if self.app:
2035
+ return self.app.url
2036
+
1977
2037
  # Vector Search
1978
2038
  if self.vector_search:
1979
2039
  if (
@@ -1983,33 +2043,35 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
1983
2043
  raise ValueError(
1984
2044
  "vector_search must have an index with a schema (catalog/schema) configured"
1985
2045
  )
1986
- catalog: str = self.vector_search.index.schema_model.catalog_name
1987
- schema: str = self.vector_search.index.schema_model.schema_name
2046
+ catalog: str = value_of(self.vector_search.index.schema_model.catalog_name)
2047
+ schema: str = value_of(self.vector_search.index.schema_model.schema_name)
1988
2048
  return f"{workspace_host}/api/2.0/mcp/vector-search/{catalog}/{schema}"
1989
2049
 
1990
2050
  # UC Functions MCP server
1991
2051
  if self.functions:
1992
- catalog: str = self.functions.catalog_name
1993
- schema: str = self.functions.schema_name
2052
+ catalog: str = value_of(self.functions.catalog_name)
2053
+ schema: str = value_of(self.functions.schema_name)
1994
2054
  return f"{workspace_host}/api/2.0/mcp/functions/{catalog}/{schema}"
1995
2055
 
1996
2056
  raise ValueError(
1997
- "No URL source configured. Provide one of: url, connection, genie_room, "
2057
+ "No URL source configured. Provide one of: url, app, connection, genie_room, "
1998
2058
  "sql, vector_search, or functions"
1999
2059
  )
2000
2060
 
2001
2061
  @field_serializer("transport")
2002
- def serialize_transport(self, value) -> str:
2062
+ def serialize_transport(self, value: TransportType) -> str:
2063
+ """Serialize transport enum to string."""
2003
2064
  if isinstance(value, TransportType):
2004
2065
  return value.value
2005
2066
  return str(value)
2006
2067
 
2007
2068
  @model_validator(mode="after")
2008
- def validate_mutually_exclusive(self) -> "McpFunctionModel":
2069
+ def validate_mutually_exclusive(self) -> Self:
2009
2070
  """Validate that exactly one URL source is provided."""
2010
2071
  # Count how many URL sources are provided
2011
2072
  url_sources: list[tuple[str, Any]] = [
2012
2073
  ("url", self.url),
2074
+ ("app", self.app),
2013
2075
  ("connection", self.connection),
2014
2076
  ("genie_room", self.genie_room),
2015
2077
  ("sql", self.sql),
@@ -2025,13 +2087,13 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
2025
2087
  if len(provided_sources) == 0:
2026
2088
  raise ValueError(
2027
2089
  "For STREAMABLE_HTTP transport, exactly one of the following must be provided: "
2028
- "url, connection, genie_room, sql, vector_search, or functions"
2090
+ "url, app, connection, genie_room, sql, vector_search, or functions"
2029
2091
  )
2030
2092
  if len(provided_sources) > 1:
2031
2093
  raise ValueError(
2032
2094
  f"For STREAMABLE_HTTP transport, only one URL source can be provided. "
2033
2095
  f"Found: {', '.join(provided_sources)}. "
2034
- f"Please provide only one of: url, connection, genie_room, sql, vector_search, or functions"
2096
+ f"Please provide only one of: url, app, connection, genie_room, sql, vector_search, or functions"
2035
2097
  )
2036
2098
 
2037
2099
  if self.transport == TransportType.STDIO:
@@ -2043,18 +2105,25 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
2043
2105
  return self
2044
2106
 
2045
2107
  @model_validator(mode="after")
2046
- def update_url(self) -> "McpFunctionModel":
2047
- self.url = value_of(self.url)
2108
+ def update_url(self) -> Self:
2109
+ """Resolve AnyVariable to concrete value for URL."""
2110
+ if self.url is not None:
2111
+ resolved_value: Any = value_of(self.url)
2112
+ # Cast to string since URL must be a string
2113
+ self.url = str(resolved_value) if resolved_value else None
2048
2114
  return self
2049
2115
 
2050
2116
  @model_validator(mode="after")
2051
- def update_headers(self) -> "McpFunctionModel":
2117
+ def update_headers(self) -> Self:
2118
+ """Resolve AnyVariable to concrete values for headers."""
2052
2119
  for key, value in self.headers.items():
2053
- self.headers[key] = value_of(value)
2120
+ resolved_value: Any = value_of(value)
2121
+ # Headers must be strings
2122
+ self.headers[key] = str(resolved_value) if resolved_value else ""
2054
2123
  return self
2055
2124
 
2056
2125
  @model_validator(mode="after")
2057
- def validate_tool_filters(self) -> "McpFunctionModel":
2126
+ def validate_tool_filters(self) -> Self:
2058
2127
  """Validate tool filter configuration."""
2059
2128
  from loguru import logger
2060
2129
 
@@ -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 with UC Connection, use DatabricksOAuthClientProvider
166
- if function.connection:
167
- from databricks_mcp import DatabricksOAuthClientProvider
207
+ # For HTTP transport, use DatabricksOAuthClientProvider with unified auth
208
+ from databricks_mcp import DatabricksOAuthClientProvider
168
209
 
169
- workspace_client = function.connection.workspace_client
170
- auth_provider = DatabricksOAuthClientProvider(workspace_client)
210
+ # Get the resource to use for authentication
211
+ auth_resource = _get_auth_resource(function)
171
212
 
172
- logger.trace(
173
- "Using DatabricksOAuthClientProvider for authentication",
174
- connection_name=function.connection.name,
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
- if "Authorization" not in headers:
189
- logger.trace("Generating fresh authentication token")
190
-
191
- from dao_ai.providers.databricks import DatabricksProvider
192
-
193
- try:
194
- provider = DatabricksProvider(
195
- workspace_host=value_of(function.workspace_host),
196
- client_id=value_of(function.client_id),
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
- "headers": headers,
230
+ "auth": auth_provider,
211
231
  }
212
232
 
213
233
 
@@ -1,5 +1,4 @@
1
1
  import sys
2
- from unittest.mock import patch
3
2
 
4
3
  import pytest
5
4
  import yaml
@@ -282,21 +281,28 @@ def test_mcp_function_model_partial_oauth_credentials() -> None:
282
281
 
283
282
  @pytest.mark.unit
284
283
  def test_mcp_function_model_existing_authorization_header() -> None:
285
- """Test that existing Authorization header is preserved and authentication is skipped."""
286
- with patch("dao_ai.providers.databricks.DatabricksProvider") as mock_provider_class:
287
- mcp_function = McpFunctionModel(
288
- transport=TransportType.STREAMABLE_HTTP,
289
- url="https://example.com",
290
- headers={"Authorization": "Bearer existing_token"},
291
- pat="test_pat",
292
- workspace_host="https://test-workspace.cloud.databricks.com",
293
- )
284
+ """Test that Authorization header can be stored in headers dict.
285
+
286
+ Note: With unified auth (via DatabricksOAuthClientProvider), the Authorization
287
+ header in the headers dict is not used for authentication. Instead, the
288
+ workspace_client from IsDatabricksResource provides authentication.
289
+
290
+ This test verifies that the model can store headers, but authentication
291
+ happens through the OAuth provider at invocation time.
292
+ """
293
+ mcp_function = McpFunctionModel(
294
+ transport=TransportType.STREAMABLE_HTTP,
295
+ url="https://example.com",
296
+ headers={"Authorization": "Bearer existing_token"},
297
+ pat="test_pat",
298
+ workspace_host="https://test-workspace.cloud.databricks.com",
299
+ )
294
300
 
295
- # DatabricksProvider should not be called since Authorization header exists
296
- mock_provider_class.assert_not_called()
301
+ # Header is stored in the model
302
+ assert mcp_function.headers["Authorization"] == "Bearer existing_token"
297
303
 
298
- # Existing header should be preserved
299
- assert mcp_function.headers["Authorization"] == "Bearer existing_token"
304
+ # But authentication will happen via workspace_client at invocation time
305
+ # The stored header won't be used by _build_connection_config
300
306
 
301
307
 
302
308
  @pytest.mark.unit
@@ -182,7 +182,7 @@ class TestFunctionModelParsing:
182
182
 
183
183
  with pytest.raises(
184
184
  ValidationError,
185
- match="exactly one of the following must be provided: url, connection, genie_room, sql, vector_search, or functions",
185
+ match="url, app, connection, genie_room, sql, vector_search, or functions",
186
186
  ):
187
187
  ToolModel(**yaml_data)
188
188
 
@@ -1770,6 +1770,7 @@ def test_create_genie_tool_with_cache_parameters() -> None:
1770
1770
 
1771
1771
  # Create mock warehouse with IsDatabricksResource attrs
1772
1772
  mock_warehouse = Mock(spec=WarehouseModel)
1773
+ mock_warehouse.name = "Test Warehouse"
1773
1774
  mock_warehouse.warehouse_id = "test-warehouse"
1774
1775
  mock_warehouse.workspace_client = Mock()
1775
1776
  add_databricks_resource_attrs(mock_warehouse)