dao-ai 0.1.13__tar.gz → 0.1.15__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 (304) hide show
  1. {dao_ai-0.1.13 → dao_ai-0.1.15}/PKG-INFO +1 -1
  2. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/managed_mcp.yaml +10 -6
  3. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/quick_serve_restaurant.yaml +1 -32
  4. {dao_ai-0.1.13 → dao_ai-0.1.15}/databricks.yaml.template +5 -5
  5. {dao_ai-0.1.13 → dao_ai-0.1.15}/pyproject.toml +1 -1
  6. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/handlers.py +22 -1
  7. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/cli.py +16 -4
  8. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/config.py +57 -33
  9. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/genie.py +72 -20
  10. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/mcp.py +36 -13
  11. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/slack.py +13 -2
  12. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/sql.py +7 -3
  13. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/unity_catalog.py +32 -10
  14. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/vector_search.py +61 -35
  15. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_databricks.py +46 -25
  16. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_reranking.py +8 -5
  17. {dao_ai-0.1.13 → dao_ai-0.1.15}/.gitignore +0 -0
  18. {dao_ai-0.1.13 → dao_ai-0.1.15}/.python-version +0 -0
  19. {dao_ai-0.1.13 → dao_ai-0.1.15}/CHANGELOG.md +0 -0
  20. {dao_ai-0.1.13 → dao_ai-0.1.15}/CONTRIBUTING.md +0 -0
  21. {dao_ai-0.1.13 → dao_ai-0.1.15}/LICENSE +0 -0
  22. {dao_ai-0.1.13 → dao_ai-0.1.15}/Makefile +0 -0
  23. {dao_ai-0.1.13 → dao_ai-0.1.15}/README.md +0 -0
  24. {dao_ai-0.1.13 → dao_ai-0.1.15}/app.yaml +0 -0
  25. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/01_getting_started/README.md +0 -0
  26. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/01_getting_started/minimal.yaml +0 -0
  27. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/README.md +0 -0
  28. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/custom_mcp.yaml +0 -0
  29. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/external_mcp.yaml +0 -0
  30. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/filtered_mcp.yaml +0 -0
  31. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/slack_integration.yaml +0 -0
  32. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/03_reranking/README.md +0 -0
  33. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/03_reranking/vector_search_with_reranking.yaml +0 -0
  34. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/README.md +0 -0
  35. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_basic.yaml +0 -0
  36. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_lru_cache.yaml +0 -0
  37. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_semantic_cache.yaml +0 -0
  38. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_with_conversation_id.yaml +0 -0
  39. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/README.md +0 -0
  40. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/conversation_summarization.yaml +0 -0
  41. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/in_memory_basic.yaml +0 -0
  42. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/lakebase_persistence.yaml +0 -0
  43. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/postgres_persistence.yaml +0 -0
  44. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/06_on_behalf_of_user/README.md +0 -0
  45. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/06_on_behalf_of_user/obo_basic.yaml +0 -0
  46. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/07_human_in_the_loop/README.md +0 -0
  47. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/07_human_in_the_loop/human_in_the_loop.yaml +0 -0
  48. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/08_guardrails/README.md +0 -0
  49. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/08_guardrails/guardrails_basic.yaml +0 -0
  50. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/09_structured_output/README.md +0 -0
  51. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/09_structured_output/structured_output.yaml +0 -0
  52. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/10_agent_integrations/README.md +0 -0
  53. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/10_agent_integrations/agent_bricks.yaml +0 -0
  54. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/10_agent_integrations/kasal.yaml +0 -0
  55. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/11_prompt_engineering/README.md +0 -0
  56. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/11_prompt_engineering/prompt_optimization.yaml +0 -0
  57. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/11_prompt_engineering/prompt_registry.yaml +0 -0
  58. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/README.md +0 -0
  59. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/combined_middleware.yaml +0 -0
  60. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/context_management.yaml +0 -0
  61. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/custom_field_validation.yaml +0 -0
  62. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/limit_middleware.yaml +0 -0
  63. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/logging_middleware.yaml +0 -0
  64. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/pii_middleware.yaml +0 -0
  65. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/retry_middleware.yaml +0 -0
  66. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/tool_selector_middleware.yaml +0 -0
  67. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/13_orchestration/README.md +0 -0
  68. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/13_orchestration/supervisor_pattern.yaml +0 -0
  69. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/13_orchestration/swarm_pattern.yaml +0 -0
  70. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/14_basic_tools/README.md +0 -0
  71. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/14_basic_tools/sql_tool_example.yaml +0 -0
  72. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/README.md +0 -0
  73. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/brick_store.yaml +0 -0
  74. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/deep_research.yaml +0 -0
  75. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/executive_assistant.yaml +0 -0
  76. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/genie_and_genie_mcp.yaml +0 -0
  77. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/genie_vector_search_hybrid.yaml +0 -0
  78. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/hardware_store.yaml +0 -0
  79. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/hardware_store_lakebase.yaml +0 -0
  80. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/hardware_store_swarm.yaml +0 -0
  81. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/reservations_system.yaml +0 -0
  82. {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/README.md +0 -0
  83. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/appointments.sql +0 -0
  84. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/appointments_data.sql +0 -0
  85. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_data.sql +0 -0
  86. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_queries.sql +0 -0
  87. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_tables.sql +0 -0
  88. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_validation.sql +0 -0
  89. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/customers.sql +0 -0
  90. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/customers_data.sql +0 -0
  91. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/dim_stores.sql +0 -0
  92. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/dim_stores_data.sql +0 -0
  93. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_performance.sql +0 -0
  94. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_performance_data.sql +0 -0
  95. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_tasks.sql +0 -0
  96. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_tasks_data.sql +0 -0
  97. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/inventory.sql +0 -0
  98. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/inventory_data.sql +0 -0
  99. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/managers.sql +0 -0
  100. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/managers_data.sql +0 -0
  101. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/product_data.sql +0 -0
  102. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/products.sql +0 -0
  103. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/task_assignments.sql +0 -0
  104. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/inventory.snappy.parquet +0 -0
  105. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/inventory.sql +0 -0
  106. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/products.snappy.parquet +0 -0
  107. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/products.sql +0 -0
  108. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/.gitkeep +0 -0
  109. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/fulfil_item_orders.sql +0 -0
  110. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_description.csv +0 -0
  111. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_description.sql +0 -0
  112. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_raw.csv +0 -0
  113. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_raw.sql +0 -0
  114. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/orders_raw.csv +0 -0
  115. {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/orders_raw.sql +0 -0
  116. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/architecture.md +0 -0
  117. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/cli-reference.md +0 -0
  118. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/configuration-reference.md +0 -0
  119. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/contributing.md +0 -0
  120. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/examples.md +0 -0
  121. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/faq.md +0 -0
  122. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/hardware_store/README.md +0 -0
  123. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/hardware_store/retail_supervisor.png +0 -0
  124. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/hardware_store/retail_swarm.png +0 -0
  125. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/images/genie.png +0 -0
  126. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/key-capabilities.md +0 -0
  127. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/python-api.md +0 -0
  128. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/quick_serve_restaurant/.gitkeep +0 -0
  129. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/quick_serve_restaurant/quick-serve-restaurant.png +0 -0
  130. {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/why-dao.md +0 -0
  131. {dao_ai-0.1.13 → dao_ai-0.1.15}/environment.yaml +0 -0
  132. {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/dais2025/examples.yaml +0 -0
  133. {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/deep_research/examples.yaml +0 -0
  134. {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/executive_assistant/examples.yaml +0 -0
  135. {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/hardware_store/examples.yaml +0 -0
  136. {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/quick_serve_restaurant/.gitkeep +0 -0
  137. {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/quick_serve_restaurant/examples.yaml +0 -0
  138. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/extract_store_numbers.sql +0 -0
  139. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_inventory_by_sku.sql +0 -0
  140. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_inventory_by_upc.sql +0 -0
  141. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_product_by_sku.sql +0 -0
  142. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_product_by_upc.sql +0 -0
  143. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_store_by_number.sql +0 -0
  144. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_store_inventory_by_sku.sql +0 -0
  145. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_store_inventory_by_upc.sql +0 -0
  146. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_inventory_by_sku.sql +0 -0
  147. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_inventory_by_upc.sql +0 -0
  148. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_product_by_sku.sql +0 -0
  149. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_product_by_upc.sql +0 -0
  150. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_store_inventory_by_sku.sql +0 -0
  151. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_store_inventory_by_upc.sql +0 -0
  152. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/.gitkeep +0 -0
  153. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/insert_coffee_order.sql +0 -0
  154. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/lookup_items_by_descriptions.sql +0 -0
  155. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/match_historical_item_order_by_date.sql +0 -0
  156. {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/match_item_by_description_and_price.sql +0 -0
  157. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/01_ingest_and_transform.py +0 -0
  158. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/02_provision_vector_search.py +0 -0
  159. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/03_provision_lakebase.py +0 -0
  160. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/04_unity_catalog_tools.py +0 -0
  161. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/05_deploy_agent.py +0 -0
  162. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/06_generate_evaluation_data.py +0 -0
  163. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/07_run_evaluation.py +0 -0
  164. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/08_run_examples.py +0 -0
  165. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/09_evaluate_inferences.py +0 -0
  166. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/10_optimize_prompts.py +0 -0
  167. {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/99_scratchpad.py +0 -0
  168. {dao_ai-0.1.13 → dao_ai-0.1.15}/requirements.txt +0 -0
  169. {dao_ai-0.1.13 → dao_ai-0.1.15}/schemas/bundle_config_schema.json +0 -0
  170. {dao_ai-0.1.13 → dao_ai-0.1.15}/schemas/model_config_schema.json +0 -0
  171. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/__init__.py +0 -0
  172. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/models.py +0 -0
  173. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/__init__.py +0 -0
  174. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/customer.py +0 -0
  175. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/employee.py +0 -0
  176. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/executive.py +0 -0
  177. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/genie.py +0 -0
  178. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/inventory.py +0 -0
  179. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/models.py +0 -0
  180. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/store.py +0 -0
  181. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/__init__.py +0 -0
  182. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/__init__.py +0 -0
  183. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/model_serving.py +0 -0
  184. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/resources.py +0 -0
  185. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/server.py +0 -0
  186. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/catalog.py +0 -0
  187. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/__init__.py +0 -0
  188. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/__init__.py +0 -0
  189. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/base.py +0 -0
  190. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/core.py +0 -0
  191. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/lru.py +0 -0
  192. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/semantic.py +0 -0
  193. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/core.py +0 -0
  194. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/graph.py +0 -0
  195. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/hooks/__init__.py +0 -0
  196. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/hooks/core.py +0 -0
  197. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/logging.py +0 -0
  198. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/__init__.py +0 -0
  199. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/base.py +0 -0
  200. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/core.py +0 -0
  201. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/databricks.py +0 -0
  202. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/postgres.py +0 -0
  203. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/messages.py +0 -0
  204. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/__init__.py +0 -0
  205. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/assertions.py +0 -0
  206. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/base.py +0 -0
  207. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/context_editing.py +0 -0
  208. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/core.py +0 -0
  209. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/guardrails.py +0 -0
  210. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/human_in_the_loop.py +0 -0
  211. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/message_validation.py +0 -0
  212. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/model_call_limit.py +0 -0
  213. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/model_retry.py +0 -0
  214. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/pii.py +0 -0
  215. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/summarization.py +0 -0
  216. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/tool_call_limit.py +0 -0
  217. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/tool_retry.py +0 -0
  218. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/tool_selector.py +0 -0
  219. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/models.py +0 -0
  220. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/nodes.py +0 -0
  221. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/optimization.py +0 -0
  222. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/__init__.py +0 -0
  223. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/core.py +0 -0
  224. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/supervisor.py +0 -0
  225. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/swarm.py +0 -0
  226. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/prompts.py +0 -0
  227. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/providers/__init__.py +0 -0
  228. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/providers/base.py +0 -0
  229. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/providers/databricks.py +0 -0
  230. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/state.py +0 -0
  231. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/__init__.py +0 -0
  232. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/agent.py +0 -0
  233. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/core.py +0 -0
  234. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/email.py +0 -0
  235. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/memory.py +0 -0
  236. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/python.py +0 -0
  237. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/search.py +0 -0
  238. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/time.py +0 -0
  239. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/types.py +0 -0
  240. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/utils.py +0 -0
  241. {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/vector_search.py +0 -0
  242. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/config/test_model_config.yaml +0 -0
  243. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/conftest.py +0 -0
  244. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_context_editing.py +0 -0
  245. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_model_call_limit.py +0 -0
  246. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_model_retry.py +0 -0
  247. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_pii.py +0 -0
  248. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_tool_call_limit.py +0 -0
  249. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_tool_retry.py +0 -0
  250. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_tool_selector.py +0 -0
  251. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_agent_response_format.py +0 -0
  252. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_assertions_middleware.py +0 -0
  253. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_catalog.py +0 -0
  254. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_chat_history.py +0 -0
  255. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_config.py +0 -0
  256. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_function_parsing.py +0 -0
  257. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie.py +0 -0
  258. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie_conversation_ids_in_outputs.py +0 -0
  259. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie_databricks_integration.py +0 -0
  260. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie_room_model.py +0 -0
  261. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_guardrail_retry.py +0 -0
  262. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_hitl_config_model.py +0 -0
  263. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_hitl_responses_agent.py +0 -0
  264. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_hooks.py +0 -0
  265. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_human_in_the_loop.py +0 -0
  266. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_inference.py +0 -0
  267. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_inference_integration.py +0 -0
  268. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_input_output_structure.py +0 -0
  269. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_interrupt_type.py +0 -0
  270. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_llm_interrupt_handling.py +0 -0
  271. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp.py +0 -0
  272. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp_filtering.py +0 -0
  273. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp_filtering_integration.py +0 -0
  274. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp_function_model.py +0 -0
  275. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_message_validation_middleware.py +0 -0
  276. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_messages.py +0 -0
  277. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_models.py +0 -0
  278. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_optimization.py +0 -0
  279. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_postgres_integration.py +0 -0
  280. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_prompt_optimizations.py +0 -0
  281. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_prompts.py +0 -0
  282. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_reranking_integration.py +0 -0
  283. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_resources_model_genie_integration.py +0 -0
  284. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_response_format.py +0 -0
  285. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_responses_agent_structured_output_unit.py +0 -0
  286. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_semantic_cache_context.py +0 -0
  287. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_sql_tool.py +0 -0
  288. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_sql_tool_integration.py +0 -0
  289. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_state.py +0 -0
  290. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_summarization_inference.py +0 -0
  291. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_swarm_middleware.py +0 -0
  292. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_tools.py +0 -0
  293. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_types.py +0 -0
  294. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_unity_catalog.py +0 -0
  295. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_utils.py +0 -0
  296. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_utils_type_from_fqn.py +0 -0
  297. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_vector_search.py +0 -0
  298. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_warehouse_model.py +0 -0
  299. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/weather_server_mcp.py +0 -0
  300. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/hardware_store/.gitkeep +0 -0
  301. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/hardware_store/test_graph.py +0 -0
  302. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/images/doritos_upc.png +0 -0
  303. {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/images/lays_upc.png +0 -0
  304. {dao_ai-0.1.13 → dao_ai-0.1.15}/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.13
3
+ Version: 0.1.15
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
@@ -3,19 +3,19 @@
3
3
  variables:
4
4
  client_id: &client_id # Service principal client ID
5
5
  options:
6
- - env: RETAIL_AI_DATABRICKS_CLIENT_ID
7
- - scope: retail_ai
6
+ - scope: retail_consumer_goods
8
7
  secret: RETAIL_AI_DATABRICKS_CLIENT_ID
8
+ - env: RETAIL_AI_DATABRICKS_CLIENT_ID
9
9
  client_secret: &client_secret # Service principal secret
10
10
  options:
11
- - env: RETAIL_AI_DATABRICKS_CLIENT_SECRET
12
- - scope: retail_ai
11
+ - scope: retail_consumer_goods
13
12
  secret: RETAIL_AI_DATABRICKS_CLIENT_SECRET
13
+ - env: RETAIL_AI_DATABRICKS_CLIENT_SECRET
14
14
  workspace_host: &workspace_host # Databricks workspace URL
15
15
  options:
16
- - env: RETAIL_AI_DATABRICKS_HOST
17
- - scope: retail_ai
16
+ - scope: retail_consumer_goods
18
17
  secret: RETAIL_AI_DATABRICKS_HOST
18
+ - env: RETAIL_AI_DATABRICKS_HOST
19
19
 
20
20
  schemas:
21
21
  retail_schema: &retail_schema # Unity Catalog schema configuration
@@ -112,6 +112,10 @@ app:
112
112
  registered_model: # MLflow registered model configuration
113
113
  schema: *retail_schema # Schema where model will be registered
114
114
  name: mcp_agent # Model name in MLflow registry
115
+ environment_vars:
116
+ RETAIL_AI_DATABRICKS_CLIENT_ID: *client_id
117
+ RETAIL_AI_DATABRICKS_CLIENT_SECRET: *client_secret
118
+ RETAIL_AI_DATABRICKS_HOST: *workspace_host
115
119
  agents: # List of agents included in the system
116
120
  - *mcp_agent # MCP agent
117
121
  orchestration: # Agent orchestration configuration
@@ -144,38 +144,7 @@ resources:
144
144
  description: "A warehouse for shared endpoints" # Description
145
145
  warehouse_id: 148ccb90800933a1 # Databricks warehouse ID
146
146
 
147
- # ---------------------------------------------------------------------------
148
- # DATABASES
149
- # ---------------------------------------------------------------------------
150
- # Configure external databases (e.g., PostgreSQL for checkpoints and memory)
151
- # Supports both traditional user/password auth and OAuth2 client credentials
152
- databases:
153
- # PostgreSQL database for agent memory and checkpoints
154
- qsr_database: &qsr_database
155
- name: "retail-consumer-goods"
156
- description: "Database for agent memory and checkpoints"
157
-
158
- # Database connection parameters
159
- # These can be set via environment variables or Databricks secrets
160
- host: *database_host_var
161
- port: *port_var
162
- database: *database_var
163
- client_id: *client_id_var
164
- client_secret: *client_secret_var
165
- workspace_host: *workspace_host_var
166
-
167
-
168
- # Optional: Additional PostgreSQL connection parameters
169
- # Uncomment and modify as needed for your specific setup
170
- # connection_kwargs: # Additional connection parameters
171
- # autocommit: True # Enable autocommit mode
172
- # prepare_threshold: 0 # Disable prepared statements
173
- # sslmode: require # Require SSL connection
174
- # connect_timeout: 10 # Connection timeout in seconds
175
-
176
- # Optional: Connection pool configuration
177
- # max_pool_size: 20 # Maximum connections in pool
178
- # timeout: 5 # Connection timeout in seconds
147
+
179
148
 
180
149
  # =============================================================================
181
150
  # RETRIEVERS CONFIGURATION
@@ -98,22 +98,22 @@ resources:
98
98
  config-path: ${var.config_path}
99
99
  environment_key: dao-ai-env
100
100
 
101
- # Cloud-specific targets with appropriate node types
102
- # The CLI auto-selects the target based on workspace cloud detection
101
+ # App-specific cloud targets - ensures each app has its own deployment identity per cloud
102
+ # The CLI auto-detects cloud provider and selects the appropriate target (e.g., app_name-azure)
103
103
  targets:
104
- azure:
104
+ __APP_NAME__-azure:
105
105
  mode: development
106
106
  variables:
107
107
  cloud: azure
108
108
  node_type: Standard_D4ads_v5
109
109
 
110
- aws:
110
+ __APP_NAME__-aws:
111
111
  mode: development
112
112
  variables:
113
113
  cloud: aws
114
114
  node_type: i3.xlarge
115
115
 
116
- gcp:
116
+ __APP_NAME__-gcp:
117
117
  mode: development
118
118
  variables:
119
119
  cloud: gcp
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dao-ai"
7
- version = "0.1.13"
7
+ version = "0.1.15"
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" }
@@ -14,7 +14,7 @@ from typing import AsyncGenerator
14
14
 
15
15
  import mlflow
16
16
  from dotenv import load_dotenv
17
- from mlflow.genai.agent_server import invoke, stream
17
+ from mlflow.genai.agent_server import get_request_headers, invoke, stream
18
18
  from mlflow.types.responses import (
19
19
  ResponsesAgentRequest,
20
20
  ResponsesAgentResponse,
@@ -25,6 +25,23 @@ from dao_ai.config import AppConfig
25
25
  from dao_ai.logging import configure_logging
26
26
  from dao_ai.models import LanggraphResponsesAgent
27
27
 
28
+
29
+ def _inject_headers_into_request(request: ResponsesAgentRequest) -> None:
30
+ """Inject request headers into custom_inputs for Context propagation.
31
+
32
+ Captures headers from the MLflow AgentServer context (where they're available)
33
+ and injects them into request.custom_inputs.configurable.headers so they
34
+ flow through to Context and can be used for OBO authentication.
35
+ """
36
+ headers: dict[str, str] = get_request_headers()
37
+ if headers:
38
+ if request.custom_inputs is None:
39
+ request.custom_inputs = {}
40
+ if "configurable" not in request.custom_inputs:
41
+ request.custom_inputs["configurable"] = {}
42
+ request.custom_inputs["configurable"]["headers"] = headers
43
+
44
+
28
45
  # Load environment variables from .env.local if it exists
29
46
  load_dotenv(dotenv_path=".env.local", override=True)
30
47
 
@@ -61,6 +78,8 @@ async def non_streaming(request: ResponsesAgentRequest) -> ResponsesAgentRespons
61
78
  Returns:
62
79
  ResponsesAgentResponse with the complete output
63
80
  """
81
+ # Capture headers while in the AgentServer async context (before they're lost)
82
+ _inject_headers_into_request(request)
64
83
  return await _responses_agent.apredict(request)
65
84
 
66
85
 
@@ -80,5 +99,7 @@ async def streaming(
80
99
  Yields:
81
100
  ResponsesAgentStreamEvent objects as they are generated
82
101
  """
102
+ # Capture headers while in the AgentServer async context (before they're lost)
103
+ _inject_headers_into_request(request)
83
104
  async for event in _responses_agent.apredict_stream(request):
84
105
  yield event
@@ -63,16 +63,26 @@ def detect_cloud_provider(profile: Optional[str] = None) -> Optional[str]:
63
63
  Cloud provider string ('azure', 'aws', 'gcp') or None if detection fails
64
64
  """
65
65
  try:
66
+ import os
66
67
  from databricks.sdk import WorkspaceClient
67
68
 
69
+ # Check for environment variables that might override profile
70
+ if profile and os.environ.get("DATABRICKS_HOST"):
71
+ logger.warning(
72
+ f"DATABRICKS_HOST environment variable is set, which may override --profile {profile}"
73
+ )
74
+
68
75
  # Create workspace client with optional profile
69
76
  if profile:
77
+ logger.debug(f"Creating WorkspaceClient with profile: {profile}")
70
78
  w = WorkspaceClient(profile=profile)
71
79
  else:
80
+ logger.debug("Creating WorkspaceClient with default/ambient credentials")
72
81
  w = WorkspaceClient()
73
82
 
74
83
  # Get the workspace URL from config
75
84
  host = w.config.host
85
+ logger.debug(f"WorkspaceClient host: {host}, profile used: {profile}")
76
86
  if not host:
77
87
  logger.warning("Could not determine workspace URL for cloud detection")
78
88
  return None
@@ -1143,7 +1153,7 @@ def run_databricks_command(
1143
1153
  app_config: AppConfig = AppConfig.from_file(config_path) if config_path else None
1144
1154
  normalized_name: str = normalize_name(app_config.app.name) if app_config else None
1145
1155
 
1146
- # Auto-detect cloud provider if not specified
1156
+ # Auto-detect cloud provider if not specified (used for node_type selection)
1147
1157
  if not cloud:
1148
1158
  cloud = detect_cloud_provider(profile)
1149
1159
  if cloud:
@@ -1156,10 +1166,12 @@ def run_databricks_command(
1156
1166
  if config_path and app_config:
1157
1167
  generate_bundle_from_template(config_path, normalized_name)
1158
1168
 
1159
- # Use cloud as target (azure, aws, gcp) - can be overridden with explicit --target
1169
+ # Use app-specific cloud target: {app_name}-{cloud}
1170
+ # This ensures each app has unique deployment identity while supporting cloud-specific settings
1171
+ # Can be overridden with explicit --target
1160
1172
  if not target:
1161
- target = cloud
1162
- logger.debug(f"Using cloud-based target: {target}")
1173
+ target = f"{normalized_name}-{cloud}"
1174
+ logger.info(f"Using app-specific cloud target: {target}")
1163
1175
 
1164
1176
  # Build databricks command
1165
1177
  # --profile is a global flag, --target is a subcommand flag for 'bundle'
@@ -7,6 +7,7 @@ from enum import Enum
7
7
  from os import PathLike
8
8
  from pathlib import Path
9
9
  from typing import (
10
+ TYPE_CHECKING,
10
11
  Any,
11
12
  Callable,
12
13
  Iterator,
@@ -18,6 +19,9 @@ from typing import (
18
19
  Union,
19
20
  )
20
21
 
22
+ if TYPE_CHECKING:
23
+ from dao_ai.state import Context
24
+
21
25
  from databricks.sdk import WorkspaceClient
22
26
  from databricks.sdk.credentials_provider import (
23
27
  CredentialsStrategy,
@@ -284,8 +288,8 @@ class IsDatabricksResource(ABC, BaseModel):
284
288
 
285
289
  Authentication priority:
286
290
  1. On-Behalf-Of User (on_behalf_of_user=True):
287
- - Forwarded headers (Databricks Apps)
288
- - ModelServingUserCredentials (Model Serving)
291
+ - Uses ModelServingUserCredentials (Model Serving)
292
+ - For Databricks Apps with headers, use workspace_client_from(context)
289
293
  2. Service Principal (client_id + client_secret + workspace_host)
290
294
  3. PAT (pat + workspace_host)
291
295
  4. Ambient/default authentication
@@ -294,37 +298,6 @@ class IsDatabricksResource(ABC, BaseModel):
294
298
 
295
299
  # Check for OBO first (highest priority)
296
300
  if self.on_behalf_of_user:
297
- # In Databricks Apps, use forwarded headers for per-user auth
298
- from mlflow.genai.agent_server import get_request_headers
299
-
300
- headers = get_request_headers()
301
- logger.debug(f"Headers received: {list(headers.keys())}")
302
- # Try both lowercase and title-case header names (HTTP headers are case-insensitive)
303
- forwarded_token = headers.get("x-forwarded-access-token") or headers.get(
304
- "X-Forwarded-Access-Token"
305
- )
306
-
307
- if forwarded_token:
308
- forwarded_user = headers.get("x-forwarded-user") or headers.get(
309
- "X-Forwarded-User", "unknown"
310
- )
311
- logger.debug(
312
- f"Creating WorkspaceClient for {self.__class__.__name__} "
313
- f"with OBO using forwarded token from Databricks Apps",
314
- forwarded_user=forwarded_user,
315
- )
316
- # Use workspace_host if configured, otherwise SDK will auto-detect
317
- workspace_host_value: str | None = (
318
- normalize_host(value_of(self.workspace_host))
319
- if self.workspace_host
320
- else None
321
- )
322
- return WorkspaceClient(
323
- host=workspace_host_value,
324
- token=forwarded_token,
325
- auth_type="pat",
326
- )
327
-
328
301
  credentials_strategy: CredentialsStrategy = ModelServingUserCredentials()
329
302
  logger.debug(
330
303
  f"Creating WorkspaceClient for {self.__class__.__name__} "
@@ -383,6 +356,57 @@ class IsDatabricksResource(ABC, BaseModel):
383
356
  )
384
357
  return WorkspaceClient()
385
358
 
359
+ def workspace_client_from(self, context: "Context | None") -> WorkspaceClient:
360
+ """
361
+ Get a WorkspaceClient using headers from the provided Context.
362
+
363
+ Use this method from tools that have access to ToolRuntime[Context].
364
+ This allows OBO authentication to work in Databricks Apps where headers
365
+ are captured at request entry and passed through the Context.
366
+
367
+ Args:
368
+ context: Runtime context containing headers for OBO auth.
369
+ If None or no headers, falls back to workspace_client property.
370
+
371
+ Returns:
372
+ WorkspaceClient configured with appropriate authentication.
373
+ """
374
+ from dao_ai.utils import normalize_host
375
+
376
+ logger.trace(f"workspace_client_from called", context=context, on_behalf_of_user=self.on_behalf_of_user)
377
+
378
+ # Check if we have headers in context for OBO
379
+ if context and context.headers and self.on_behalf_of_user:
380
+ headers = context.headers
381
+ # Try both lowercase and title-case header names (HTTP headers are case-insensitive)
382
+ forwarded_token: str = headers.get("x-forwarded-access-token") or headers.get(
383
+ "X-Forwarded-Access-Token"
384
+ )
385
+
386
+ if forwarded_token:
387
+ forwarded_user = headers.get("x-forwarded-user") or headers.get(
388
+ "X-Forwarded-User", "unknown"
389
+ )
390
+ logger.debug(
391
+ f"Creating WorkspaceClient for {self.__class__.__name__} "
392
+ f"with OBO using forwarded token from Context",
393
+ forwarded_user=forwarded_user,
394
+ )
395
+ # Use workspace_host if configured, otherwise SDK will auto-detect
396
+ workspace_host_value: str | None = (
397
+ normalize_host(value_of(self.workspace_host))
398
+ if self.workspace_host
399
+ else None
400
+ )
401
+ return WorkspaceClient(
402
+ host=workspace_host_value,
403
+ token=forwarded_token,
404
+ auth_type="pat",
405
+ )
406
+
407
+ # Fall back to existing workspace_client property
408
+ return self.workspace_client
409
+
386
410
 
387
411
  class DeploymentTarget(str, Enum):
388
412
  """Target platform for agent deployment."""
@@ -139,29 +139,53 @@ Returns:
139
139
  GenieResponse: A response object containing the conversation ID and result from Genie."""
140
140
  tool_description = tool_description + function_docs
141
141
 
142
- genie: Genie = Genie(
143
- space_id=space_id,
144
- client=genie_room.workspace_client,
145
- truncate_results=truncate_results,
146
- )
142
+ # Cache for genie service - created lazily on first call
143
+ # This allows us to use workspace_client_from with runtime context for OBO
144
+ _cached_genie_service: GenieServiceBase | None = None
145
+
146
+ def _get_genie_service(context: Context | None) -> GenieServiceBase:
147
+ """Get or create the Genie service, using context for OBO auth if available."""
148
+ nonlocal _cached_genie_service
149
+
150
+ # Use cached service if available (for non-OBO or after first call)
151
+ # For OBO, we need fresh workspace client each time to use the user's token
152
+ if _cached_genie_service is not None and not genie_room.on_behalf_of_user:
153
+ return _cached_genie_service
154
+
155
+ # Get workspace client using context for OBO support
156
+ from databricks.sdk import WorkspaceClient
147
157
 
148
- genie_service: GenieServiceBase = GenieService(genie)
149
-
150
- # Wrap with semantic cache first (checked second due to decorator pattern)
151
- if semantic_cache_parameters is not None:
152
- genie_service = SemanticCacheService(
153
- impl=genie_service,
154
- parameters=semantic_cache_parameters,
155
- workspace_client=genie_room.workspace_client, # Pass workspace client for conversation history
156
- ).initialize() # Eagerly initialize to fail fast and create table
157
-
158
- # Wrap with LRU cache last (checked first - fast O(1) exact match)
159
- if lru_cache_parameters is not None:
160
- genie_service = LRUCacheService(
161
- impl=genie_service,
162
- parameters=lru_cache_parameters,
158
+ workspace_client: WorkspaceClient = genie_room.workspace_client_from(context)
159
+
160
+ genie: Genie = Genie(
161
+ space_id=space_id,
162
+ client=workspace_client,
163
+ truncate_results=truncate_results,
163
164
  )
164
165
 
166
+ genie_service: GenieServiceBase = GenieService(genie)
167
+
168
+ # Wrap with semantic cache first (checked second due to decorator pattern)
169
+ if semantic_cache_parameters is not None:
170
+ genie_service = SemanticCacheService(
171
+ impl=genie_service,
172
+ parameters=semantic_cache_parameters,
173
+ workspace_client=workspace_client,
174
+ ).initialize()
175
+
176
+ # Wrap with LRU cache last (checked first - fast O(1) exact match)
177
+ if lru_cache_parameters is not None:
178
+ genie_service = LRUCacheService(
179
+ impl=genie_service,
180
+ parameters=lru_cache_parameters,
181
+ )
182
+
183
+ # Cache for non-OBO scenarios
184
+ if not genie_room.on_behalf_of_user:
185
+ _cached_genie_service = genie_service
186
+
187
+ return genie_service
188
+
165
189
  @tool(
166
190
  name_or_callable=tool_name,
167
191
  description=tool_description,
@@ -177,6 +201,10 @@ GenieResponse: A response object containing the conversation ID and result from
177
201
  # Access state through runtime
178
202
  state: AgentState = runtime.state
179
203
  tool_call_id: str = runtime.tool_call_id
204
+ context: Context | None = runtime.context
205
+
206
+ # Get genie service with OBO support via context
207
+ genie_service: GenieServiceBase = _get_genie_service(context)
180
208
 
181
209
  # Ensure space_id is a string for state keys
182
210
  space_id_str: str = str(space_id)
@@ -194,6 +222,14 @@ GenieResponse: A response object containing the conversation ID and result from
194
222
  conversation_id=existing_conversation_id,
195
223
  )
196
224
 
225
+ # Log the prompt being sent to Genie
226
+ logger.trace(
227
+ "Sending prompt to Genie",
228
+ space_id=space_id_str,
229
+ conversation_id=existing_conversation_id,
230
+ prompt=question[:500] + "..." if len(question) > 500 else question,
231
+ )
232
+
197
233
  # Call ask_question which always returns CacheResult with cache metadata
198
234
  cache_result: CacheResult = genie_service.ask_question(
199
235
  question, conversation_id=existing_conversation_id
@@ -211,6 +247,22 @@ GenieResponse: A response object containing the conversation ID and result from
211
247
  cache_key=cache_key,
212
248
  )
213
249
 
250
+ # Log truncated response for debugging
251
+ result_preview: str = str(genie_response.result)
252
+ if len(result_preview) > 500:
253
+ result_preview = result_preview[:500] + "..."
254
+ logger.trace(
255
+ "Genie response content",
256
+ question=question[:100] + "..." if len(question) > 100 else question,
257
+ query=genie_response.query,
258
+ description=(
259
+ genie_response.description[:200] + "..."
260
+ if genie_response.description and len(genie_response.description) > 200
261
+ else genie_response.description
262
+ ),
263
+ result_preview=result_preview,
264
+ )
265
+
214
266
  # Update session state with cache information
215
267
  if persist_conversation:
216
268
  session.genie.update_space(
@@ -30,6 +30,7 @@ from dao_ai.config import (
30
30
  McpFunctionModel,
31
31
  TransportType,
32
32
  )
33
+ from dao_ai.state import Context
33
34
 
34
35
 
35
36
  @dataclass
@@ -173,6 +174,7 @@ def _get_auth_resource(function: McpFunctionModel) -> IsDatabricksResource:
173
174
 
174
175
  def _build_connection_config(
175
176
  function: McpFunctionModel,
177
+ context: Context | None = None,
176
178
  ) -> dict[str, Any]:
177
179
  """
178
180
  Build the connection configuration dictionary for MultiServerMCPClient.
@@ -193,6 +195,7 @@ def _build_connection_config(
193
195
 
194
196
  Args:
195
197
  function: The MCP function model configuration.
198
+ context: Optional runtime context with headers for OBO auth.
196
199
 
197
200
  Returns:
198
201
  A dictionary containing the transport-specific connection settings.
@@ -205,14 +208,17 @@ def _build_connection_config(
205
208
  }
206
209
 
207
210
  # For HTTP transport, use DatabricksOAuthClientProvider with unified auth
211
+ from databricks.sdk import WorkspaceClient
208
212
  from databricks_mcp import DatabricksOAuthClientProvider
209
213
 
210
214
  # Get the resource to use for authentication
211
- auth_resource = _get_auth_resource(function)
215
+ auth_resource: IsDatabricksResource = _get_auth_resource(function)
212
216
 
213
- # Get workspace client from the auth resource
214
- workspace_client = auth_resource.workspace_client
215
- auth_provider = DatabricksOAuthClientProvider(workspace_client)
217
+ # Get workspace client from the auth resource with OBO support via context
218
+ workspace_client: WorkspaceClient = auth_resource.workspace_client_from(context)
219
+ auth_provider: DatabricksOAuthClientProvider = DatabricksOAuthClientProvider(
220
+ workspace_client
221
+ )
216
222
 
217
223
  # Log which resource is providing auth
218
224
  resource_name = (
@@ -509,19 +515,28 @@ async def acreate_mcp_tools(
509
515
  def _create_tool_wrapper(mcp_tool: Tool) -> RunnableLike:
510
516
  """
511
517
  Create a LangChain tool wrapper for an MCP tool.
518
+
519
+ Supports OBO authentication via context headers.
512
520
  """
521
+ from langchain.tools import ToolRuntime
513
522
 
514
523
  @create_tool(
515
524
  mcp_tool.name,
516
525
  description=mcp_tool.description or f"MCP tool: {mcp_tool.name}",
517
526
  args_schema=mcp_tool.inputSchema,
518
527
  )
519
- async def tool_wrapper(**kwargs: Any) -> str:
528
+ async def tool_wrapper(
529
+ runtime: ToolRuntime[Context] = None,
530
+ **kwargs: Any,
531
+ ) -> str:
520
532
  """Execute MCP tool with fresh session."""
521
533
  logger.trace("Invoking MCP tool", tool_name=mcp_tool.name, args=kwargs)
522
534
 
523
- invocation_client = MultiServerMCPClient(
524
- {"mcp_function": _build_connection_config(function)}
535
+ # Get context for OBO support
536
+ context: Context | None = runtime.context if runtime else None
537
+
538
+ invocation_client: MultiServerMCPClient = MultiServerMCPClient(
539
+ {"mcp_function": _build_connection_config(function, context)}
525
540
  )
526
541
 
527
542
  try:
@@ -530,7 +545,7 @@ async def acreate_mcp_tools(
530
545
  mcp_tool.name, kwargs
531
546
  )
532
547
 
533
- text_result = _extract_text_content(result)
548
+ text_result: str = _extract_text_content(result)
534
549
 
535
550
  logger.trace(
536
551
  "MCP tool completed",
@@ -625,20 +640,28 @@ def create_mcp_tools(
625
640
  This wrapper handles:
626
641
  - Fresh session creation per invocation (stateless)
627
642
  - Content extraction to plain text (avoiding extra fields)
643
+ - OBO authentication via context headers
628
644
  """
645
+ from langchain.tools import ToolRuntime
629
646
 
630
647
  @create_tool(
631
648
  mcp_tool.name,
632
649
  description=mcp_tool.description or f"MCP tool: {mcp_tool.name}",
633
650
  args_schema=mcp_tool.inputSchema,
634
651
  )
635
- async def tool_wrapper(**kwargs: Any) -> str:
652
+ async def tool_wrapper(
653
+ runtime: ToolRuntime[Context] = None,
654
+ **kwargs: Any,
655
+ ) -> str:
636
656
  """Execute MCP tool with fresh session."""
637
657
  logger.trace("Invoking MCP tool", tool_name=mcp_tool.name, args=kwargs)
638
658
 
639
- # Create a fresh client/session for each invocation
640
- invocation_client = MultiServerMCPClient(
641
- {"mcp_function": _build_connection_config(function)}
659
+ # Get context for OBO support
660
+ context: Context | None = runtime.context if runtime else None
661
+
662
+ # Create a fresh client/session for each invocation with OBO support
663
+ invocation_client: MultiServerMCPClient = MultiServerMCPClient(
664
+ {"mcp_function": _build_connection_config(function, context)}
642
665
  )
643
666
 
644
667
  try:
@@ -648,7 +671,7 @@ def create_mcp_tools(
648
671
  )
649
672
 
650
673
  # Extract text content, avoiding extra fields
651
- text_result = _extract_text_content(result)
674
+ text_result: str = _extract_text_content(result)
652
675
 
653
676
  logger.trace(
654
677
  "MCP tool completed",
@@ -1,11 +1,13 @@
1
1
  from typing import Any, Callable, Optional
2
2
 
3
3
  from databricks.sdk.service.serving import ExternalFunctionRequestHttpMethod
4
+ from langchain.tools import ToolRuntime
4
5
  from langchain_core.tools import tool
5
6
  from loguru import logger
6
7
  from requests import Response
7
8
 
8
9
  from dao_ai.config import ConnectionModel
10
+ from dao_ai.state import Context
9
11
 
10
12
 
11
13
  def _find_channel_id_by_name(
@@ -129,8 +131,17 @@ def create_send_slack_message_tool(
129
131
  name_or_callable=name,
130
132
  description=description,
131
133
  )
132
- def send_slack_message(text: str) -> str:
133
- response: Response = connection.workspace_client.serving_endpoints.http_request(
134
+ def send_slack_message(
135
+ text: str,
136
+ runtime: ToolRuntime[Context] = None,
137
+ ) -> str:
138
+ from databricks.sdk import WorkspaceClient
139
+
140
+ # Get workspace client with OBO support via context
141
+ context: Context | None = runtime.context if runtime else None
142
+ workspace_client: WorkspaceClient = connection.workspace_client_from(context)
143
+
144
+ response: Response = workspace_client.serving_endpoints.http_request(
134
145
  conn=connection.name,
135
146
  method=ExternalFunctionRequestHttpMethod.POST,
136
147
  path="/api/chat.postMessage",
@@ -7,10 +7,11 @@ pre-configured SQL statements against a Databricks SQL warehouse.
7
7
 
8
8
  from databricks.sdk import WorkspaceClient
9
9
  from databricks.sdk.service.sql import StatementResponse, StatementState
10
- from langchain.tools import tool
10
+ from langchain.tools import ToolRuntime, tool
11
11
  from loguru import logger
12
12
 
13
13
  from dao_ai.config import WarehouseModel, value_of
14
+ from dao_ai.state import Context
14
15
 
15
16
 
16
17
  def create_execute_statement_tool(
@@ -63,7 +64,6 @@ def create_execute_statement_tool(
63
64
  description = f"Execute a pre-configured SQL query against the {warehouse.name} warehouse and return the results."
64
65
 
65
66
  warehouse_id: str = value_of(warehouse.warehouse_id)
66
- workspace_client: WorkspaceClient = warehouse.workspace_client
67
67
 
68
68
  logger.debug(
69
69
  "Creating SQL execution tool",
@@ -74,7 +74,7 @@ def create_execute_statement_tool(
74
74
  )
75
75
 
76
76
  @tool(name_or_callable=name, description=description)
77
- def execute_statement_tool() -> str:
77
+ def execute_statement_tool(runtime: ToolRuntime[Context] = None) -> str:
78
78
  """
79
79
  Execute the pre-configured SQL statement against the Databricks SQL warehouse.
80
80
 
@@ -88,6 +88,10 @@ def create_execute_statement_tool(
88
88
  sql_preview=statement[:100] + "..." if len(statement) > 100 else statement,
89
89
  )
90
90
 
91
+ # Get workspace client with OBO support via context
92
+ context: Context | None = runtime.context if runtime else None
93
+ workspace_client: WorkspaceClient = warehouse.workspace_client_from(context)
94
+
91
95
  try:
92
96
  # Execute the SQL statement
93
97
  statement_response: StatementResponse = (