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.
- {dao_ai-0.1.13 → dao_ai-0.1.15}/PKG-INFO +1 -1
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/managed_mcp.yaml +10 -6
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/quick_serve_restaurant.yaml +1 -32
- {dao_ai-0.1.13 → dao_ai-0.1.15}/databricks.yaml.template +5 -5
- {dao_ai-0.1.13 → dao_ai-0.1.15}/pyproject.toml +1 -1
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/handlers.py +22 -1
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/cli.py +16 -4
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/config.py +57 -33
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/genie.py +72 -20
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/mcp.py +36 -13
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/slack.py +13 -2
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/sql.py +7 -3
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/unity_catalog.py +32 -10
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/vector_search.py +61 -35
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_databricks.py +46 -25
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_reranking.py +8 -5
- {dao_ai-0.1.13 → dao_ai-0.1.15}/.gitignore +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/.python-version +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/CHANGELOG.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/CONTRIBUTING.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/LICENSE +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/Makefile +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/app.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/01_getting_started/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/01_getting_started/minimal.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/custom_mcp.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/external_mcp.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/filtered_mcp.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/02_mcp/slack_integration.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/03_reranking/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/03_reranking/vector_search_with_reranking.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_basic.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_lru_cache.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_semantic_cache.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/04_genie/genie_with_conversation_id.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/conversation_summarization.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/in_memory_basic.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/lakebase_persistence.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/05_memory/postgres_persistence.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/06_on_behalf_of_user/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/06_on_behalf_of_user/obo_basic.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/07_human_in_the_loop/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/07_human_in_the_loop/human_in_the_loop.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/08_guardrails/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/08_guardrails/guardrails_basic.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/09_structured_output/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/09_structured_output/structured_output.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/10_agent_integrations/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/10_agent_integrations/agent_bricks.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/10_agent_integrations/kasal.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/11_prompt_engineering/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/11_prompt_engineering/prompt_optimization.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/11_prompt_engineering/prompt_registry.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/combined_middleware.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/context_management.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/custom_field_validation.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/limit_middleware.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/logging_middleware.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/pii_middleware.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/retry_middleware.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/12_middleware/tool_selector_middleware.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/13_orchestration/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/13_orchestration/supervisor_pattern.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/13_orchestration/swarm_pattern.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/14_basic_tools/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/14_basic_tools/sql_tool_example.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/brick_store.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/deep_research.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/executive_assistant.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/genie_and_genie_mcp.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/genie_vector_search_hybrid.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/hardware_store.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/hardware_store_lakebase.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/hardware_store_swarm.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/reservations_system.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/appointments.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/appointments_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_queries.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_tables.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/brand_rep_demo_validation.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/customers.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/customers_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/dim_stores.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/dim_stores_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_performance.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_performance_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_tasks.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/employee_tasks_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/inventory.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/inventory_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/managers.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/managers_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/product_data.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/products.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/dais2025/task_assignments.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/inventory.snappy.parquet +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/inventory.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/products.snappy.parquet +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/hardware_store/products.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/fulfil_item_orders.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_description.csv +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_description.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_raw.csv +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/items_raw.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/orders_raw.csv +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/data/quick_serve_restaurant/orders_raw.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/architecture.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/cli-reference.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/configuration-reference.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/contributing.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/examples.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/faq.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/hardware_store/README.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/hardware_store/retail_supervisor.png +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/hardware_store/retail_swarm.png +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/images/genie.png +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/key-capabilities.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/python-api.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/quick_serve_restaurant/quick-serve-restaurant.png +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/docs/why-dao.md +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/environment.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/dais2025/examples.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/deep_research/examples.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/executive_assistant/examples.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/hardware_store/examples.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/examples/quick_serve_restaurant/examples.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/extract_store_numbers.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_product_by_sku.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_product_by_upc.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_store_by_number.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_store_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/dais2025/find_store_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_product_by_sku.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_product_by_upc.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_store_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/hardware_store/find_store_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/insert_coffee_order.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/lookup_items_by_descriptions.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/match_historical_item_order_by_date.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/functions/quick_serve_restaurant/match_item_by_description_and_price.sql +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/01_ingest_and_transform.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/02_provision_vector_search.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/03_provision_lakebase.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/04_unity_catalog_tools.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/05_deploy_agent.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/06_generate_evaluation_data.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/07_run_evaluation.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/08_run_examples.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/09_evaluate_inferences.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/10_optimize_prompts.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/notebooks/99_scratchpad.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/requirements.txt +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/schemas/bundle_config_schema.json +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/schemas/model_config_schema.json +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/models.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/customer.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/employee.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/executive.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/genie.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/inventory.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/models.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dais2025/tools/store.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/model_serving.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/resources.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/apps/server.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/catalog.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/base.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/lru.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/cache/semantic.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/genie/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/graph.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/hooks/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/hooks/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/logging.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/base.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/databricks.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/memory/postgres.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/messages.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/assertions.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/base.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/context_editing.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/guardrails.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/human_in_the_loop.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/message_validation.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/model_call_limit.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/model_retry.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/pii.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/summarization.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/tool_call_limit.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/tool_retry.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/middleware/tool_selector.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/models.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/nodes.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/optimization.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/supervisor.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/orchestration/swarm.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/prompts.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/providers/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/providers/base.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/providers/databricks.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/state.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/__init__.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/agent.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/core.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/email.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/memory.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/python.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/search.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/tools/time.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/types.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/utils.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/src/dao_ai/vector_search.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/config/test_model_config.yaml +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/conftest.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_context_editing.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_model_call_limit.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_model_retry.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_pii.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_tool_call_limit.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_tool_retry.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/middleware/test_tool_selector.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_agent_response_format.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_assertions_middleware.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_catalog.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_chat_history.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_config.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_function_parsing.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie_conversation_ids_in_outputs.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie_databricks_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_genie_room_model.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_guardrail_retry.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_hitl_config_model.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_hitl_responses_agent.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_hooks.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_human_in_the_loop.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_inference.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_inference_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_input_output_structure.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_interrupt_type.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_llm_interrupt_handling.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp_filtering.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp_filtering_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_mcp_function_model.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_message_validation_middleware.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_messages.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_models.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_optimization.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_postgres_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_prompt_optimizations.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_prompts.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_reranking_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_resources_model_genie_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_response_format.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_responses_agent_structured_output_unit.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_semantic_cache_context.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_sql_tool.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_sql_tool_integration.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_state.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_summarization_inference.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_swarm_middleware.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_tools.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_types.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_unity_catalog.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_utils.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_utils_type_from_fqn.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_vector_search.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/test_warehouse_model.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/dao_ai/weather_server_mcp.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/hardware_store/.gitkeep +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/hardware_store/test_graph.py +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/images/doritos_upc.png +0 -0
- {dao_ai-0.1.13 → dao_ai-0.1.15}/tests/images/lays_upc.png +0 -0
- {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.
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
{dao_ai-0.1.13 → dao_ai-0.1.15}/config/examples/15_complete_applications/quick_serve_restaurant.yaml
RENAMED
|
@@ -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
|
-
#
|
|
102
|
-
# The CLI auto-selects the target
|
|
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.
|
|
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
|
|
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.
|
|
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
|
-
-
|
|
288
|
-
-
|
|
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
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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.
|
|
215
|
-
auth_provider = DatabricksOAuthClientProvider(
|
|
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(
|
|
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
|
-
|
|
524
|
-
|
|
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(
|
|
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
|
-
#
|
|
640
|
-
|
|
641
|
-
|
|
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(
|
|
133
|
-
|
|
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 = (
|