dao-ai 0.1.7__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.7 → dao_ai-0.1.8}/PKG-INFO +1 -1
  2. {dao_ai-0.1.7 → dao_ai-0.1.8}/pyproject.toml +1 -1
  3. {dao_ai-0.1.7 → dao_ai-0.1.8}/requirements.txt +0 -1
  4. {dao_ai-0.1.7 → dao_ai-0.1.8}/schemas/model_config_schema.json +34 -37
  5. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/config.py +38 -16
  6. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/mcp.py +60 -40
  7. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_config.py +20 -14
  8. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie.py +1 -0
  9. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie_room_model.py +157 -3
  10. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp.py +7 -1
  11. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_filtering_integration.py +360 -3
  12. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_function_model.py +14 -5
  13. dao_ai-0.1.8/tests/dao_ai/test_warehouse_model.py +166 -0
  14. {dao_ai-0.1.7 → dao_ai-0.1.8}/.gitignore +0 -0
  15. {dao_ai-0.1.7 → dao_ai-0.1.8}/.python-version +0 -0
  16. {dao_ai-0.1.7 → dao_ai-0.1.8}/CHANGELOG.md +0 -0
  17. {dao_ai-0.1.7 → dao_ai-0.1.8}/CONTRIBUTING.md +0 -0
  18. {dao_ai-0.1.7 → dao_ai-0.1.8}/LICENSE +0 -0
  19. {dao_ai-0.1.7 → dao_ai-0.1.8}/Makefile +0 -0
  20. {dao_ai-0.1.7 → dao_ai-0.1.8}/README.md +0 -0
  21. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/01_getting_started/README.md +0 -0
  22. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/01_getting_started/minimal.yaml +0 -0
  23. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/README.md +0 -0
  24. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/custom_mcp.yaml +0 -0
  25. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/external_mcp.yaml +0 -0
  26. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/filtered_mcp.yaml +0 -0
  27. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/managed_mcp.yaml +0 -0
  28. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/slack_integration.yaml +0 -0
  29. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/03_reranking/README.md +0 -0
  30. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/03_reranking/vector_search_with_reranking.yaml +0 -0
  31. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/README.md +0 -0
  32. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_basic.yaml +0 -0
  33. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_lru_cache.yaml +0 -0
  34. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_semantic_cache.yaml +0 -0
  35. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_with_conversation_id.yaml +0 -0
  36. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/README.md +0 -0
  37. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/conversation_summarization.yaml +0 -0
  38. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/in_memory_basic.yaml +0 -0
  39. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/lakebase_persistence.yaml +0 -0
  40. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/postgres_persistence.yaml +0 -0
  41. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/06_on_behalf_of_user/README.md +0 -0
  42. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/06_on_behalf_of_user/obo_basic.yaml +0 -0
  43. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/07_human_in_the_loop/README.md +0 -0
  44. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/07_human_in_the_loop/human_in_the_loop.yaml +0 -0
  45. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/08_guardrails/README.md +0 -0
  46. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/08_guardrails/guardrails_basic.yaml +0 -0
  47. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/09_structured_output/README.md +0 -0
  48. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/09_structured_output/structured_output.yaml +0 -0
  49. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/10_agent_integrations/README.md +0 -0
  50. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/10_agent_integrations/agent_bricks.yaml +0 -0
  51. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/10_agent_integrations/kasal.yaml +0 -0
  52. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/README.md +0 -0
  53. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/prompt_optimization.yaml +0 -0
  54. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/prompt_registry.yaml +0 -0
  55. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/README.md +0 -0
  56. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/combined_middleware.yaml +0 -0
  57. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/context_management.yaml +0 -0
  58. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/custom_field_validation.yaml +0 -0
  59. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/limit_middleware.yaml +0 -0
  60. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/logging_middleware.yaml +0 -0
  61. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/pii_middleware.yaml +0 -0
  62. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/retry_middleware.yaml +0 -0
  63. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/tool_selector_middleware.yaml +0 -0
  64. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/13_orchestration/README.md +0 -0
  65. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/13_orchestration/supervisor_pattern.yaml +0 -0
  66. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/13_orchestration/swarm_pattern.yaml +0 -0
  67. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/14_basic_tools/README.md +0 -0
  68. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/14_basic_tools/sql_tool_example.yaml +0 -0
  69. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/README.md +0 -0
  70. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/brick_store.yaml +0 -0
  71. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/deep_research.yaml +0 -0
  72. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/executive_assistant.yaml +0 -0
  73. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/genie_and_genie_mcp.yaml +0 -0
  74. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/genie_vector_search_hybrid.yaml +0 -0
  75. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store.yaml +0 -0
  76. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store_lakebase.yaml +0 -0
  77. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store_swarm.yaml +0 -0
  78. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/quick_serve_restaurant.yaml +0 -0
  79. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/reservations_system.yaml +0 -0
  80. {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/README.md +0 -0
  81. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/appointments.sql +0 -0
  82. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/appointments_data.sql +0 -0
  83. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_data.sql +0 -0
  84. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_queries.sql +0 -0
  85. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_tables.sql +0 -0
  86. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_validation.sql +0 -0
  87. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/customers.sql +0 -0
  88. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/customers_data.sql +0 -0
  89. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/dim_stores.sql +0 -0
  90. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/dim_stores_data.sql +0 -0
  91. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_performance.sql +0 -0
  92. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_performance_data.sql +0 -0
  93. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_tasks.sql +0 -0
  94. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_tasks_data.sql +0 -0
  95. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/inventory.sql +0 -0
  96. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/inventory_data.sql +0 -0
  97. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/managers.sql +0 -0
  98. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/managers_data.sql +0 -0
  99. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/product_data.sql +0 -0
  100. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/products.sql +0 -0
  101. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/task_assignments.sql +0 -0
  102. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/inventory.snappy.parquet +0 -0
  103. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/inventory.sql +0 -0
  104. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/products.snappy.parquet +0 -0
  105. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/products.sql +0 -0
  106. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/.gitkeep +0 -0
  107. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/fulfil_item_orders.sql +0 -0
  108. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_description.csv +0 -0
  109. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_description.sql +0 -0
  110. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_raw.csv +0 -0
  111. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_raw.sql +0 -0
  112. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/orders_raw.csv +0 -0
  113. {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/orders_raw.sql +0 -0
  114. {dao_ai-0.1.7 → dao_ai-0.1.8}/databricks.yaml.template +0 -0
  115. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/architecture.md +0 -0
  116. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/cli-reference.md +0 -0
  117. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/configuration-reference.md +0 -0
  118. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/contributing.md +0 -0
  119. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/examples.md +0 -0
  120. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/faq.md +0 -0
  121. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/hardware_store/README.md +0 -0
  122. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/hardware_store/retail_supervisor.png +0 -0
  123. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/hardware_store/retail_swarm.png +0 -0
  124. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/images/genie.png +0 -0
  125. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/key-capabilities.md +0 -0
  126. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/python-api.md +0 -0
  127. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/quick_serve_restaurant/.gitkeep +0 -0
  128. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/quick_serve_restaurant/quick-serve-restaurant.png +0 -0
  129. {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/why-dao.md +0 -0
  130. {dao_ai-0.1.7 → dao_ai-0.1.8}/environment.yaml +0 -0
  131. {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/dais2025/examples.yaml +0 -0
  132. {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/deep_research/examples.yaml +0 -0
  133. {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/executive_assistant/examples.yaml +0 -0
  134. {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/hardware_store/examples.yaml +0 -0
  135. {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/quick_serve_restaurant/.gitkeep +0 -0
  136. {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/quick_serve_restaurant/examples.yaml +0 -0
  137. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/extract_store_numbers.sql +0 -0
  138. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_inventory_by_sku.sql +0 -0
  139. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_inventory_by_upc.sql +0 -0
  140. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_product_by_sku.sql +0 -0
  141. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_product_by_upc.sql +0 -0
  142. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_store_by_number.sql +0 -0
  143. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_store_inventory_by_sku.sql +0 -0
  144. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_store_inventory_by_upc.sql +0 -0
  145. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_inventory_by_sku.sql +0 -0
  146. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_inventory_by_upc.sql +0 -0
  147. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_product_by_sku.sql +0 -0
  148. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_product_by_upc.sql +0 -0
  149. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_store_inventory_by_sku.sql +0 -0
  150. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_store_inventory_by_upc.sql +0 -0
  151. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/.gitkeep +0 -0
  152. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/insert_coffee_order.sql +0 -0
  153. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/lookup_items_by_descriptions.sql +0 -0
  154. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/match_historical_item_order_by_date.sql +0 -0
  155. {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/match_item_by_description_and_price.sql +0 -0
  156. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/01_ingest_and_transform.py +0 -0
  157. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/02_provision_vector_search.py +0 -0
  158. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/03_provision_lakebase.py +0 -0
  159. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/04_unity_catalog_tools.py +0 -0
  160. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/05_deploy_agent.py +0 -0
  161. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/06_generate_evaluation_data.py +0 -0
  162. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/07_run_evaluation.py +0 -0
  163. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/08_run_examples.py +0 -0
  164. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/09_evaluate_inferences.py +0 -0
  165. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/10_optimize_prompts.py +0 -0
  166. {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/99_scratchpad.py +0 -0
  167. {dao_ai-0.1.7 → dao_ai-0.1.8}/schemas/bundle_config_schema.json +0 -0
  168. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/__init__.py +0 -0
  169. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/models.py +0 -0
  170. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/__init__.py +0 -0
  171. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/customer.py +0 -0
  172. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/employee.py +0 -0
  173. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/executive.py +0 -0
  174. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/genie.py +0 -0
  175. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/inventory.py +0 -0
  176. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/models.py +0 -0
  177. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/store.py +0 -0
  178. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/__init__.py +0 -0
  179. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/agent_as_code.py +0 -0
  180. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/catalog.py +0 -0
  181. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/cli.py +0 -0
  182. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/__init__.py +0 -0
  183. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/__init__.py +0 -0
  184. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/base.py +0 -0
  185. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/core.py +0 -0
  186. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/lru.py +0 -0
  187. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/semantic.py +0 -0
  188. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/core.py +0 -0
  189. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/graph.py +0 -0
  190. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/hooks/__init__.py +0 -0
  191. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/hooks/core.py +0 -0
  192. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/logging.py +0 -0
  193. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/__init__.py +0 -0
  194. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/base.py +0 -0
  195. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/core.py +0 -0
  196. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/databricks.py +0 -0
  197. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/postgres.py +0 -0
  198. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/messages.py +0 -0
  199. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/__init__.py +0 -0
  200. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/assertions.py +0 -0
  201. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/base.py +0 -0
  202. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/context_editing.py +0 -0
  203. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/core.py +0 -0
  204. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/guardrails.py +0 -0
  205. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/human_in_the_loop.py +0 -0
  206. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/message_validation.py +0 -0
  207. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/model_call_limit.py +0 -0
  208. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/model_retry.py +0 -0
  209. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/pii.py +0 -0
  210. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/summarization.py +0 -0
  211. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_call_limit.py +0 -0
  212. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_retry.py +0 -0
  213. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_selector.py +0 -0
  214. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/models.py +0 -0
  215. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/nodes.py +0 -0
  216. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/optimization.py +0 -0
  217. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/__init__.py +0 -0
  218. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/core.py +0 -0
  219. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/supervisor.py +0 -0
  220. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/swarm.py +0 -0
  221. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/prompts.py +0 -0
  222. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/providers/__init__.py +0 -0
  223. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/providers/base.py +0 -0
  224. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/providers/databricks.py +0 -0
  225. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/state.py +0 -0
  226. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/__init__.py +0 -0
  227. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/agent.py +0 -0
  228. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/core.py +0 -0
  229. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/email.py +0 -0
  230. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/genie.py +0 -0
  231. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/memory.py +0 -0
  232. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/python.py +0 -0
  233. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/search.py +0 -0
  234. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/slack.py +0 -0
  235. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/sql.py +0 -0
  236. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/time.py +0 -0
  237. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/unity_catalog.py +0 -0
  238. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/vector_search.py +0 -0
  239. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/types.py +0 -0
  240. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/utils.py +0 -0
  241. {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/vector_search.py +0 -0
  242. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/config/test_model_config.yaml +0 -0
  243. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/conftest.py +0 -0
  244. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_context_editing.py +0 -0
  245. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_model_call_limit.py +0 -0
  246. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_model_retry.py +0 -0
  247. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_pii.py +0 -0
  248. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_call_limit.py +0 -0
  249. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_retry.py +0 -0
  250. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_selector.py +0 -0
  251. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_agent_response_format.py +0 -0
  252. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_assertions_middleware.py +0 -0
  253. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_catalog.py +0 -0
  254. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_chat_history.py +0 -0
  255. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_databricks.py +0 -0
  256. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_function_parsing.py +0 -0
  257. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie_conversation_ids_in_outputs.py +0 -0
  258. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie_databricks_integration.py +0 -0
  259. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_guardrail_retry.py +0 -0
  260. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_hitl_config_model.py +0 -0
  261. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_hitl_responses_agent.py +0 -0
  262. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_hooks.py +0 -0
  263. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_human_in_the_loop.py +0 -0
  264. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_inference.py +0 -0
  265. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_inference_integration.py +0 -0
  266. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_input_output_structure.py +0 -0
  267. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_interrupt_type.py +0 -0
  268. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_llm_interrupt_handling.py +0 -0
  269. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_filtering.py +0 -0
  270. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_message_validation_middleware.py +0 -0
  271. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_messages.py +0 -0
  272. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_models.py +0 -0
  273. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_optimization.py +0 -0
  274. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_postgres_integration.py +0 -0
  275. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_prompt_optimizations.py +0 -0
  276. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_prompts.py +0 -0
  277. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_reranking.py +0 -0
  278. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_reranking_integration.py +0 -0
  279. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_resources_model_genie_integration.py +0 -0
  280. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_response_format.py +0 -0
  281. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_responses_agent_structured_output_unit.py +0 -0
  282. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_semantic_cache_context.py +0 -0
  283. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_sql_tool.py +0 -0
  284. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_sql_tool_integration.py +0 -0
  285. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_state.py +0 -0
  286. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_summarization_inference.py +0 -0
  287. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_swarm_middleware.py +0 -0
  288. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_tools.py +0 -0
  289. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_types.py +0 -0
  290. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_unity_catalog.py +0 -0
  291. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_utils.py +0 -0
  292. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_utils_type_from_fqn.py +0 -0
  293. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_vector_search.py +0 -0
  294. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/weather_server_mcp.py +0 -0
  295. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/hardware_store/.gitkeep +0 -0
  296. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/hardware_store/test_graph.py +0 -0
  297. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/images/doritos_upc.png +0 -0
  298. {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/images/lays_upc.png +0 -0
  299. {dao_ai-0.1.7 → 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.7
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.7"
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",
@@ -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 update_description_from_space(self) -> Self:
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.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:
1007
1029
  self.description = space_details.description
1008
1030
  except Exception as e:
1009
- logger.debug(f"Could not fetch description from Genie space: {e}")
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
@@ -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
@@ -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)
@@ -400,6 +400,62 @@ class TestGenieRoomModelSerialization:
400
400
  # The genie_space_id is stored as the 'name' attribute
401
401
  assert resources[0].name == "test-space-123"
402
402
 
403
+ def test_name_populated_from_space(
404
+ self, mock_workspace_client, mock_genie_space_with_serialized_data
405
+ ):
406
+ """Test that name is automatically populated from GenieSpace.title if not provided."""
407
+ # Set a title on the mock space
408
+ mock_genie_space_with_serialized_data.title = "My Retail Analytics Space"
409
+
410
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
411
+ mock_workspace_client.genie.get_space.return_value = (
412
+ mock_genie_space_with_serialized_data
413
+ )
414
+
415
+ # Create without name
416
+ genie_room = GenieRoomModel(space_id="test-space-123")
417
+
418
+ # Name should be populated from the space title
419
+ assert genie_room.name == "My Retail Analytics Space"
420
+
421
+ def test_name_not_overridden_if_provided(
422
+ self, mock_workspace_client, mock_genie_space_with_serialized_data
423
+ ):
424
+ """Test that provided name is not overridden by GenieSpace title."""
425
+ # Set a title on the mock space
426
+ mock_genie_space_with_serialized_data.title = "Space Title from API"
427
+
428
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
429
+ mock_workspace_client.genie.get_space.return_value = (
430
+ mock_genie_space_with_serialized_data
431
+ )
432
+
433
+ # Create with explicit name
434
+ genie_room = GenieRoomModel(
435
+ name="My Custom Name", space_id="test-space-123"
436
+ )
437
+
438
+ # Custom name should be preserved
439
+ assert genie_room.name == "My Custom Name"
440
+
441
+ def test_name_handles_none_from_space(
442
+ self, mock_workspace_client, mock_genie_space_with_serialized_data
443
+ ):
444
+ """Test that None title from space is handled gracefully."""
445
+ # Set title to None
446
+ mock_genie_space_with_serialized_data.title = None
447
+
448
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
449
+ mock_workspace_client.genie.get_space.return_value = (
450
+ mock_genie_space_with_serialized_data
451
+ )
452
+
453
+ # Create without name
454
+ genie_room = GenieRoomModel(space_id="test-space-123")
455
+
456
+ # Name should remain None
457
+ assert genie_room.name is None
458
+
403
459
  def test_description_populated_from_space(
404
460
  self, mock_workspace_client, mock_genie_space_with_serialized_data
405
461
  ):
@@ -415,13 +471,33 @@ class TestGenieRoomModelSerialization:
415
471
  )
416
472
 
417
473
  # Create without description
418
- genie_room = GenieRoomModel(
419
- name="test-genie-room", space_id="test-space-123"
420
- )
474
+ genie_room = GenieRoomModel(space_id="test-space-123")
421
475
 
422
476
  # Description should be populated from the space
423
477
  assert genie_room.description == "This is a test Genie space description"
424
478
 
479
+ def test_name_and_description_populated_together(
480
+ self, mock_workspace_client, mock_genie_space_with_serialized_data
481
+ ):
482
+ """Test that both name and description are populated from GenieSpace when not provided."""
483
+ # Set both title and description on the mock space
484
+ mock_genie_space_with_serialized_data.title = "Production Analytics"
485
+ mock_genie_space_with_serialized_data.description = (
486
+ "Production space for analytics queries"
487
+ )
488
+
489
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
490
+ mock_workspace_client.genie.get_space.return_value = (
491
+ mock_genie_space_with_serialized_data
492
+ )
493
+
494
+ # Create without name or description
495
+ genie_room = GenieRoomModel(space_id="test-space-123")
496
+
497
+ # Both should be populated from the space
498
+ assert genie_room.name == "Production Analytics"
499
+ assert genie_room.description == "Production space for analytics queries"
500
+
425
501
  def test_description_not_overridden_if_provided(
426
502
  self, mock_workspace_client, mock_genie_space_with_serialized_data
427
503
  ):
@@ -464,6 +540,84 @@ class TestGenieRoomModelSerialization:
464
540
  # Description should remain None
465
541
  assert genie_room.description is None
466
542
 
543
+ def test_partial_population_name_provided_description_auto(
544
+ self, mock_workspace_client, mock_genie_space_with_serialized_data
545
+ ):
546
+ """Test that description is still populated when name is provided."""
547
+ # Set both title and description on the mock space
548
+ mock_genie_space_with_serialized_data.title = "API Space Title"
549
+ mock_genie_space_with_serialized_data.description = "API Space Description"
550
+
551
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
552
+ mock_workspace_client.genie.get_space.return_value = (
553
+ mock_genie_space_with_serialized_data
554
+ )
555
+
556
+ # Create with name but without description
557
+ genie_room = GenieRoomModel(name="Custom Name", space_id="test-space-123")
558
+
559
+ # Custom name should be preserved, description should be auto-populated
560
+ assert genie_room.name == "Custom Name"
561
+ assert genie_room.description == "API Space Description"
562
+
563
+ def test_partial_population_description_provided_name_auto(
564
+ self, mock_workspace_client, mock_genie_space_with_serialized_data
565
+ ):
566
+ """Test that name is still populated when description is provided."""
567
+ # Set both title and description on the mock space
568
+ mock_genie_space_with_serialized_data.title = "API Space Title"
569
+ mock_genie_space_with_serialized_data.description = "API Space Description"
570
+
571
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
572
+ mock_workspace_client.genie.get_space.return_value = (
573
+ mock_genie_space_with_serialized_data
574
+ )
575
+
576
+ # Create with description but without name
577
+ genie_room = GenieRoomModel(
578
+ description="Custom Description", space_id="test-space-123"
579
+ )
580
+
581
+ # Custom description should be preserved, name should be auto-populated
582
+ assert genie_room.name == "API Space Title"
583
+ assert genie_room.description == "Custom Description"
584
+
585
+ def test_populate_name_and_description_handles_api_error(
586
+ self, mock_workspace_client
587
+ ):
588
+ """Test that populate_name_and_description validator handles API errors gracefully."""
589
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
590
+ # Mock API to raise an error
591
+ mock_workspace_client.genie.get_space.side_effect = Exception(
592
+ "API Error: Connection timeout"
593
+ )
594
+
595
+ # Create without name or description - should not raise exception
596
+ genie_room = GenieRoomModel(space_id="test-space-123")
597
+
598
+ # Name and description should remain None (not populated due to API error)
599
+ assert genie_room.name is None
600
+ assert genie_room.description is None
601
+
602
+ def test_populate_name_and_description_not_called_when_both_provided(
603
+ self, mock_workspace_client
604
+ ):
605
+ """Test that API is not called when both name and description are provided."""
606
+ with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
607
+ # Create with both name and description
608
+ genie_room = GenieRoomModel(
609
+ name="Custom Name",
610
+ description="Custom Description",
611
+ space_id="test-space-123",
612
+ )
613
+
614
+ # API should not have been called
615
+ mock_workspace_client.genie.get_space.assert_not_called()
616
+
617
+ # Values should be preserved
618
+ assert genie_room.name == "Custom Name"
619
+ assert genie_room.description == "Custom Description"
620
+
467
621
  def test_tables_and_functions_inherit_authentication(
468
622
  self, mock_workspace_client, mock_genie_space_with_serialized_data
469
623
  ):
@@ -6,7 +6,13 @@ import pytest
6
6
  from langchain_core.tools import BaseTool
7
7
  from pydantic import ValidationError
8
8
 
9
- from dao_ai.config import ConnectionModel, DatabricksAppModel, McpFunctionModel, SchemaModel, TransportType
9
+ from dao_ai.config import (
10
+ ConnectionModel,
11
+ DatabricksAppModel,
12
+ McpFunctionModel,
13
+ SchemaModel,
14
+ TransportType,
15
+ )
10
16
 
11
17
 
12
18
  @pytest.mark.integration