dao-ai 0.1.7__tar.gz → 0.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dao_ai-0.1.7 → dao_ai-0.1.8}/PKG-INFO +1 -1
- {dao_ai-0.1.7 → dao_ai-0.1.8}/pyproject.toml +1 -1
- {dao_ai-0.1.7 → dao_ai-0.1.8}/requirements.txt +0 -1
- {dao_ai-0.1.7 → dao_ai-0.1.8}/schemas/model_config_schema.json +34 -37
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/config.py +38 -16
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/mcp.py +60 -40
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_config.py +20 -14
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie.py +1 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie_room_model.py +157 -3
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp.py +7 -1
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_filtering_integration.py +360 -3
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_function_model.py +14 -5
- dao_ai-0.1.8/tests/dao_ai/test_warehouse_model.py +166 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/.gitignore +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/.python-version +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/CHANGELOG.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/CONTRIBUTING.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/LICENSE +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/Makefile +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/01_getting_started/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/01_getting_started/minimal.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/custom_mcp.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/external_mcp.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/filtered_mcp.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/managed_mcp.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/02_mcp/slack_integration.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/03_reranking/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/03_reranking/vector_search_with_reranking.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_basic.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_lru_cache.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_semantic_cache.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/04_genie/genie_with_conversation_id.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/conversation_summarization.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/in_memory_basic.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/lakebase_persistence.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/05_memory/postgres_persistence.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/06_on_behalf_of_user/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/06_on_behalf_of_user/obo_basic.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/07_human_in_the_loop/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/07_human_in_the_loop/human_in_the_loop.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/08_guardrails/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/08_guardrails/guardrails_basic.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/09_structured_output/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/09_structured_output/structured_output.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/10_agent_integrations/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/10_agent_integrations/agent_bricks.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/10_agent_integrations/kasal.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/prompt_optimization.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/11_prompt_engineering/prompt_registry.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/combined_middleware.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/context_management.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/custom_field_validation.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/limit_middleware.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/logging_middleware.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/pii_middleware.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/retry_middleware.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/12_middleware/tool_selector_middleware.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/13_orchestration/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/13_orchestration/supervisor_pattern.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/13_orchestration/swarm_pattern.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/14_basic_tools/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/14_basic_tools/sql_tool_example.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/brick_store.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/deep_research.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/executive_assistant.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/genie_and_genie_mcp.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/genie_vector_search_hybrid.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store_lakebase.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/hardware_store_swarm.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/quick_serve_restaurant.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/15_complete_applications/reservations_system.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/config/examples/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/appointments.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/appointments_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_queries.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_tables.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/brand_rep_demo_validation.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/customers.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/customers_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/dim_stores.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/dim_stores_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_performance.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_performance_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_tasks.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/employee_tasks_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/inventory.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/inventory_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/managers.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/managers_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/product_data.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/products.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/dais2025/task_assignments.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/inventory.snappy.parquet +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/inventory.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/products.snappy.parquet +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/hardware_store/products.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/fulfil_item_orders.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_description.csv +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_description.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_raw.csv +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/items_raw.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/orders_raw.csv +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/data/quick_serve_restaurant/orders_raw.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/databricks.yaml.template +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/architecture.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/cli-reference.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/configuration-reference.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/contributing.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/examples.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/faq.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/hardware_store/README.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/hardware_store/retail_supervisor.png +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/hardware_store/retail_swarm.png +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/images/genie.png +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/key-capabilities.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/python-api.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/quick_serve_restaurant/quick-serve-restaurant.png +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/docs/why-dao.md +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/environment.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/dais2025/examples.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/deep_research/examples.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/executive_assistant/examples.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/hardware_store/examples.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/examples/quick_serve_restaurant/examples.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/extract_store_numbers.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_product_by_sku.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_product_by_upc.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_store_by_number.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_store_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/dais2025/find_store_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_product_by_sku.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_product_by_upc.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_store_inventory_by_sku.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/hardware_store/find_store_inventory_by_upc.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/.gitkeep +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/insert_coffee_order.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/lookup_items_by_descriptions.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/match_historical_item_order_by_date.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/functions/quick_serve_restaurant/match_item_by_description_and_price.sql +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/01_ingest_and_transform.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/02_provision_vector_search.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/03_provision_lakebase.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/04_unity_catalog_tools.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/05_deploy_agent.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/06_generate_evaluation_data.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/07_run_evaluation.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/08_run_examples.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/09_evaluate_inferences.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/10_optimize_prompts.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/notebooks/99_scratchpad.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/schemas/bundle_config_schema.json +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/models.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/customer.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/employee.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/executive.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/genie.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/inventory.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/models.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dais2025/tools/store.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/agent_as_code.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/catalog.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/cli.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/base.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/lru.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/cache/semantic.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/genie/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/graph.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/hooks/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/hooks/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/logging.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/base.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/databricks.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/memory/postgres.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/messages.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/assertions.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/base.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/context_editing.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/guardrails.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/human_in_the_loop.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/message_validation.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/model_call_limit.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/model_retry.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/pii.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/summarization.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_call_limit.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_retry.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/middleware/tool_selector.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/models.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/nodes.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/optimization.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/supervisor.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/orchestration/swarm.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/prompts.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/providers/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/providers/base.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/providers/databricks.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/state.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/__init__.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/agent.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/core.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/email.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/genie.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/memory.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/python.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/search.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/slack.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/sql.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/time.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/unity_catalog.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/tools/vector_search.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/types.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/utils.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/src/dao_ai/vector_search.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/config/test_model_config.yaml +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/conftest.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_context_editing.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_model_call_limit.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_model_retry.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_pii.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_call_limit.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_retry.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/middleware/test_tool_selector.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_agent_response_format.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_assertions_middleware.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_catalog.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_chat_history.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_databricks.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_function_parsing.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie_conversation_ids_in_outputs.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_genie_databricks_integration.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_guardrail_retry.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_hitl_config_model.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_hitl_responses_agent.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_hooks.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_human_in_the_loop.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_inference.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_inference_integration.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_input_output_structure.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_interrupt_type.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_llm_interrupt_handling.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_mcp_filtering.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_message_validation_middleware.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_messages.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_models.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_optimization.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_postgres_integration.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_prompt_optimizations.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_prompts.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_reranking.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_reranking_integration.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_resources_model_genie_integration.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_response_format.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_responses_agent_structured_output_unit.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_semantic_cache_context.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_sql_tool.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_sql_tool_integration.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_state.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_summarization_inference.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_swarm_middleware.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_tools.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_types.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_unity_catalog.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_utils.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_utils_type_from_fqn.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/test_vector_search.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/dao_ai/weather_server_mcp.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/hardware_store/.gitkeep +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/hardware_store/test_graph.py +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/images/doritos_upc.png +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/images/lays_upc.png +0 -0
- {dao_ai-0.1.7 → dao_ai-0.1.8}/tests/quick_serve_restaurant/.gitkeep +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dao-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: DAO AI: A modular, multi-agent orchestration framework for complex AI workflows. Supports agent handoff, tool integration, and dynamic configuration via YAML.
|
|
5
5
|
Project-URL: Homepage, https://github.com/natefleming/dao-ai
|
|
6
6
|
Project-URL: Documentation, https://natefleming.github.io/dao-ai
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dao-ai"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8"
|
|
8
8
|
description = "DAO AI: A modular, multi-agent orchestration framework for complex AI workflows. Supports agent handoff, tool integration, and dynamic configuration via YAML."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -1196,6 +1196,7 @@
|
|
|
1196
1196
|
},
|
|
1197
1197
|
"DatabricksAppModel": {
|
|
1198
1198
|
"additionalProperties": false,
|
|
1199
|
+
"description": "Configuration for a Databricks App resource.\n\nThe `name` is the unique instance name of the Databricks App within the workspace.\nThe `url` is dynamically retrieved from the workspace client by calling\n`apps.get(name)` and returning the app's URL.\n\nExample:\n ```yaml\n resources:\n apps:\n my_app:\n name: my-databricks-app\n ```",
|
|
1199
1200
|
"properties": {
|
|
1200
1201
|
"on_behalf_of_user": {
|
|
1201
1202
|
"anyOf": [
|
|
@@ -1355,40 +1356,10 @@
|
|
|
1355
1356
|
"name": {
|
|
1356
1357
|
"title": "Name",
|
|
1357
1358
|
"type": "string"
|
|
1358
|
-
},
|
|
1359
|
-
"url": {
|
|
1360
|
-
"anyOf": [
|
|
1361
|
-
{
|
|
1362
|
-
"$ref": "#/$defs/CompositeVariableModel"
|
|
1363
|
-
},
|
|
1364
|
-
{
|
|
1365
|
-
"$ref": "#/$defs/EnvironmentVariableModel"
|
|
1366
|
-
},
|
|
1367
|
-
{
|
|
1368
|
-
"$ref": "#/$defs/SecretVariableModel"
|
|
1369
|
-
},
|
|
1370
|
-
{
|
|
1371
|
-
"$ref": "#/$defs/PrimitiveVariableModel"
|
|
1372
|
-
},
|
|
1373
|
-
{
|
|
1374
|
-
"type": "string"
|
|
1375
|
-
},
|
|
1376
|
-
{
|
|
1377
|
-
"type": "integer"
|
|
1378
|
-
},
|
|
1379
|
-
{
|
|
1380
|
-
"type": "number"
|
|
1381
|
-
},
|
|
1382
|
-
{
|
|
1383
|
-
"type": "boolean"
|
|
1384
|
-
}
|
|
1385
|
-
],
|
|
1386
|
-
"title": "Url"
|
|
1387
1359
|
}
|
|
1388
1360
|
},
|
|
1389
1361
|
"required": [
|
|
1390
|
-
"name"
|
|
1391
|
-
"url"
|
|
1362
|
+
"name"
|
|
1392
1363
|
],
|
|
1393
1364
|
"title": "DatabricksAppModel",
|
|
1394
1365
|
"type": "object"
|
|
@@ -2082,8 +2053,16 @@
|
|
|
2082
2053
|
"title": "Pat"
|
|
2083
2054
|
},
|
|
2084
2055
|
"name": {
|
|
2085
|
-
"
|
|
2086
|
-
|
|
2056
|
+
"anyOf": [
|
|
2057
|
+
{
|
|
2058
|
+
"type": "string"
|
|
2059
|
+
},
|
|
2060
|
+
{
|
|
2061
|
+
"type": "null"
|
|
2062
|
+
}
|
|
2063
|
+
],
|
|
2064
|
+
"default": null,
|
|
2065
|
+
"title": "Name"
|
|
2087
2066
|
},
|
|
2088
2067
|
"description": {
|
|
2089
2068
|
"anyOf": [
|
|
@@ -2128,7 +2107,6 @@
|
|
|
2128
2107
|
}
|
|
2129
2108
|
},
|
|
2130
2109
|
"required": [
|
|
2131
|
-
"name",
|
|
2132
2110
|
"space_id"
|
|
2133
2111
|
],
|
|
2134
2112
|
"title": "GenieRoomModel",
|
|
@@ -2935,6 +2913,17 @@
|
|
|
2935
2913
|
"title": "Args",
|
|
2936
2914
|
"type": "array"
|
|
2937
2915
|
},
|
|
2916
|
+
"app": {
|
|
2917
|
+
"anyOf": [
|
|
2918
|
+
{
|
|
2919
|
+
"$ref": "#/$defs/DatabricksAppModel"
|
|
2920
|
+
},
|
|
2921
|
+
{
|
|
2922
|
+
"type": "null"
|
|
2923
|
+
}
|
|
2924
|
+
],
|
|
2925
|
+
"default": null
|
|
2926
|
+
},
|
|
2938
2927
|
"connection": {
|
|
2939
2928
|
"anyOf": [
|
|
2940
2929
|
{
|
|
@@ -4932,6 +4921,7 @@
|
|
|
4932
4921
|
"type": "object"
|
|
4933
4922
|
},
|
|
4934
4923
|
"WarehouseModel": {
|
|
4924
|
+
"additionalProperties": false,
|
|
4935
4925
|
"properties": {
|
|
4936
4926
|
"on_behalf_of_user": {
|
|
4937
4927
|
"anyOf": [
|
|
@@ -5089,8 +5079,16 @@
|
|
|
5089
5079
|
"title": "Pat"
|
|
5090
5080
|
},
|
|
5091
5081
|
"name": {
|
|
5092
|
-
"
|
|
5093
|
-
|
|
5082
|
+
"anyOf": [
|
|
5083
|
+
{
|
|
5084
|
+
"type": "string"
|
|
5085
|
+
},
|
|
5086
|
+
{
|
|
5087
|
+
"type": "null"
|
|
5088
|
+
}
|
|
5089
|
+
],
|
|
5090
|
+
"default": null,
|
|
5091
|
+
"title": "Name"
|
|
5094
5092
|
},
|
|
5095
5093
|
"description": {
|
|
5096
5094
|
"anyOf": [
|
|
@@ -5135,7 +5133,6 @@
|
|
|
5135
5133
|
}
|
|
5136
5134
|
},
|
|
5137
5135
|
"required": [
|
|
5138
|
-
"name",
|
|
5139
5136
|
"warehouse_id"
|
|
5140
5137
|
],
|
|
5141
5138
|
"title": "WarehouseModel",
|
|
@@ -418,11 +418,11 @@ class SchemaModel(BaseModel, HasFullName):
|
|
|
418
418
|
class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
419
419
|
"""
|
|
420
420
|
Configuration for a Databricks App resource.
|
|
421
|
-
|
|
421
|
+
|
|
422
422
|
The `name` is the unique instance name of the Databricks App within the workspace.
|
|
423
|
-
The `url` is dynamically retrieved from the workspace client by calling
|
|
423
|
+
The `url` is dynamically retrieved from the workspace client by calling
|
|
424
424
|
`apps.get(name)` and returning the app's URL.
|
|
425
|
-
|
|
425
|
+
|
|
426
426
|
Example:
|
|
427
427
|
```yaml
|
|
428
428
|
resources:
|
|
@@ -431,7 +431,7 @@ class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
|
431
431
|
name: my-databricks-app
|
|
432
432
|
```
|
|
433
433
|
"""
|
|
434
|
-
|
|
434
|
+
|
|
435
435
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
436
436
|
name: str
|
|
437
437
|
"""The unique instance name of the Databricks App in the workspace."""
|
|
@@ -440,10 +440,10 @@ class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
|
440
440
|
def url(self) -> str:
|
|
441
441
|
"""
|
|
442
442
|
Retrieve the URL of the Databricks App from the workspace.
|
|
443
|
-
|
|
443
|
+
|
|
444
444
|
Returns:
|
|
445
445
|
The URL of the deployed Databricks App.
|
|
446
|
-
|
|
446
|
+
|
|
447
447
|
Raises:
|
|
448
448
|
RuntimeError: If the app is not found or URL is not available.
|
|
449
449
|
"""
|
|
@@ -455,7 +455,6 @@ class DatabricksAppModel(IsDatabricksResource, HasFullName):
|
|
|
455
455
|
)
|
|
456
456
|
return app.url
|
|
457
457
|
|
|
458
|
-
|
|
459
458
|
@property
|
|
460
459
|
def full_name(self) -> str:
|
|
461
460
|
return self.name
|
|
@@ -761,11 +760,20 @@ class FunctionModel(IsDatabricksResource, HasFullName):
|
|
|
761
760
|
|
|
762
761
|
|
|
763
762
|
class WarehouseModel(IsDatabricksResource):
|
|
764
|
-
model_config = ConfigDict()
|
|
765
|
-
name: str
|
|
763
|
+
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
764
|
+
name: Optional[str] = None
|
|
766
765
|
description: Optional[str] = None
|
|
767
766
|
warehouse_id: AnyVariable
|
|
768
767
|
|
|
768
|
+
_warehouse_details: Optional[GetWarehouseResponse] = PrivateAttr(default=None)
|
|
769
|
+
|
|
770
|
+
def _get_warehouse_details(self) -> GetWarehouseResponse:
|
|
771
|
+
if self._warehouse_details is None:
|
|
772
|
+
self._warehouse_details = self.workspace_client.warehouses.get(
|
|
773
|
+
id=value_of(self.warehouse_id)
|
|
774
|
+
)
|
|
775
|
+
return self._warehouse_details
|
|
776
|
+
|
|
769
777
|
@property
|
|
770
778
|
def api_scopes(self) -> Sequence[str]:
|
|
771
779
|
return [
|
|
@@ -786,10 +794,22 @@ class WarehouseModel(IsDatabricksResource):
|
|
|
786
794
|
self.warehouse_id = value_of(self.warehouse_id)
|
|
787
795
|
return self
|
|
788
796
|
|
|
797
|
+
@model_validator(mode="after")
|
|
798
|
+
def populate_name(self) -> Self:
|
|
799
|
+
"""Populate name from warehouse details if not provided."""
|
|
800
|
+
if self.warehouse_id and not self.name:
|
|
801
|
+
try:
|
|
802
|
+
warehouse_details = self._get_warehouse_details()
|
|
803
|
+
if warehouse_details.name:
|
|
804
|
+
self.name = warehouse_details.name
|
|
805
|
+
except Exception as e:
|
|
806
|
+
logger.debug(f"Could not fetch details from warehouse: {e}")
|
|
807
|
+
return self
|
|
808
|
+
|
|
789
809
|
|
|
790
810
|
class GenieRoomModel(IsDatabricksResource):
|
|
791
811
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
792
|
-
name: str
|
|
812
|
+
name: Optional[str] = None
|
|
793
813
|
description: Optional[str] = None
|
|
794
814
|
space_id: AnyVariable
|
|
795
815
|
|
|
@@ -998,15 +1018,17 @@ class GenieRoomModel(IsDatabricksResource):
|
|
|
998
1018
|
return self
|
|
999
1019
|
|
|
1000
1020
|
@model_validator(mode="after")
|
|
1001
|
-
def
|
|
1002
|
-
"""Populate description from GenieSpace if not provided."""
|
|
1003
|
-
if not self.description:
|
|
1021
|
+
def populate_name_and_description(self) -> Self:
|
|
1022
|
+
"""Populate name and description from GenieSpace if not provided."""
|
|
1023
|
+
if self.space_id and (not self.name or not self.description):
|
|
1004
1024
|
try:
|
|
1005
1025
|
space_details = self._get_space_details()
|
|
1006
|
-
if space_details.
|
|
1026
|
+
if not self.name and space_details.title:
|
|
1027
|
+
self.name = space_details.title
|
|
1028
|
+
if not self.description and space_details.description:
|
|
1007
1029
|
self.description = space_details.description
|
|
1008
1030
|
except Exception as e:
|
|
1009
|
-
logger.debug(f"Could not fetch
|
|
1031
|
+
logger.debug(f"Could not fetch details from Genie space: {e}")
|
|
1010
1032
|
return self
|
|
1011
1033
|
|
|
1012
1034
|
|
|
@@ -2007,7 +2029,7 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
|
|
|
2007
2029
|
# DBSQL MCP server (serverless, workspace-level)
|
|
2008
2030
|
if self.sql:
|
|
2009
2031
|
return f"{workspace_host}/api/2.0/mcp/sql"
|
|
2010
|
-
|
|
2032
|
+
|
|
2011
2033
|
# Databricks App
|
|
2012
2034
|
if self.app:
|
|
2013
2035
|
return self.app.url
|
|
@@ -26,9 +26,9 @@ from loguru import logger
|
|
|
26
26
|
from mcp.types import CallToolResult, TextContent, Tool
|
|
27
27
|
|
|
28
28
|
from dao_ai.config import (
|
|
29
|
+
IsDatabricksResource,
|
|
29
30
|
McpFunctionModel,
|
|
30
31
|
TransportType,
|
|
31
|
-
value_of,
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
|
|
@@ -143,12 +143,54 @@ def _should_include_tool(
|
|
|
143
143
|
return True
|
|
144
144
|
|
|
145
145
|
|
|
146
|
+
def _get_auth_resource(function: McpFunctionModel) -> IsDatabricksResource:
|
|
147
|
+
"""
|
|
148
|
+
Get the IsDatabricksResource to use for authentication.
|
|
149
|
+
|
|
150
|
+
Follows a priority hierarchy:
|
|
151
|
+
1. Explicit resource with auth (app, connection, genie_room, vector_search, functions)
|
|
152
|
+
2. McpFunctionModel itself (which also inherits from IsDatabricksResource)
|
|
153
|
+
|
|
154
|
+
Returns the resource whose workspace_client should be used for authentication.
|
|
155
|
+
"""
|
|
156
|
+
# Check each possible resource source in priority order
|
|
157
|
+
# These resources may have their own auth configured
|
|
158
|
+
if function.app:
|
|
159
|
+
return function.app
|
|
160
|
+
if function.connection:
|
|
161
|
+
return function.connection
|
|
162
|
+
if function.genie_room:
|
|
163
|
+
return function.genie_room
|
|
164
|
+
if function.vector_search:
|
|
165
|
+
return function.vector_search
|
|
166
|
+
if function.functions:
|
|
167
|
+
# SchemaModel doesn't have auth - fall through to McpFunctionModel
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
# Fall back to McpFunctionModel itself (it inherits from IsDatabricksResource)
|
|
171
|
+
return function
|
|
172
|
+
|
|
173
|
+
|
|
146
174
|
def _build_connection_config(
|
|
147
175
|
function: McpFunctionModel,
|
|
148
176
|
) -> dict[str, Any]:
|
|
149
177
|
"""
|
|
150
178
|
Build the connection configuration dictionary for MultiServerMCPClient.
|
|
151
179
|
|
|
180
|
+
Authentication Strategy:
|
|
181
|
+
-----------------------
|
|
182
|
+
For HTTP transport, authentication is handled consistently using
|
|
183
|
+
DatabricksOAuthClientProvider with the workspace_client from the appropriate
|
|
184
|
+
IsDatabricksResource. The auth resource is selected in this priority:
|
|
185
|
+
|
|
186
|
+
1. Nested resource (app, connection, genie_room, vector_search) if it has auth
|
|
187
|
+
2. McpFunctionModel itself (inherits from IsDatabricksResource)
|
|
188
|
+
|
|
189
|
+
This approach ensures:
|
|
190
|
+
- Consistent auth handling across all MCP sources
|
|
191
|
+
- Automatic token refresh for long-running connections
|
|
192
|
+
- Support for OBO, service principal, PAT, and ambient auth
|
|
193
|
+
|
|
152
194
|
Args:
|
|
153
195
|
function: The MCP function model configuration.
|
|
154
196
|
|
|
@@ -162,52 +204,30 @@ def _build_connection_config(
|
|
|
162
204
|
"transport": function.transport.value,
|
|
163
205
|
}
|
|
164
206
|
|
|
165
|
-
# For HTTP transport
|
|
166
|
-
|
|
167
|
-
from databricks_mcp import DatabricksOAuthClientProvider
|
|
207
|
+
# For HTTP transport, use DatabricksOAuthClientProvider with unified auth
|
|
208
|
+
from databricks_mcp import DatabricksOAuthClientProvider
|
|
168
209
|
|
|
169
|
-
|
|
170
|
-
|
|
210
|
+
# Get the resource to use for authentication
|
|
211
|
+
auth_resource = _get_auth_resource(function)
|
|
171
212
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
"url": function.mcp_url,
|
|
179
|
-
"transport": "http",
|
|
180
|
-
"auth": auth_provider,
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
# For HTTP transport with headers-based authentication
|
|
184
|
-
headers: dict[str, str] = {
|
|
185
|
-
key: str(value_of(val)) for key, val in function.headers.items()
|
|
186
|
-
}
|
|
213
|
+
# Get workspace client from the auth resource
|
|
214
|
+
workspace_client = auth_resource.workspace_client
|
|
215
|
+
auth_provider = DatabricksOAuthClientProvider(workspace_client)
|
|
187
216
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
client_secret=value_of(function.client_secret),
|
|
198
|
-
pat=value_of(function.pat),
|
|
199
|
-
)
|
|
200
|
-
headers["Authorization"] = f"Bearer {provider.create_token()}"
|
|
201
|
-
logger.trace("Generated fresh authentication token")
|
|
202
|
-
except Exception as e:
|
|
203
|
-
logger.error("Failed to create fresh token", error=str(e))
|
|
204
|
-
else:
|
|
205
|
-
logger.trace("Using existing authentication token")
|
|
217
|
+
# Log which resource is providing auth
|
|
218
|
+
resource_name = (
|
|
219
|
+
getattr(auth_resource, "name", None) or auth_resource.__class__.__name__
|
|
220
|
+
)
|
|
221
|
+
logger.trace(
|
|
222
|
+
"Using DatabricksOAuthClientProvider for authentication",
|
|
223
|
+
auth_resource=resource_name,
|
|
224
|
+
resource_type=auth_resource.__class__.__name__,
|
|
225
|
+
)
|
|
206
226
|
|
|
207
227
|
return {
|
|
208
228
|
"url": function.mcp_url,
|
|
209
229
|
"transport": "http",
|
|
210
|
-
"
|
|
230
|
+
"auth": auth_provider,
|
|
211
231
|
}
|
|
212
232
|
|
|
213
233
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from unittest.mock import patch
|
|
3
2
|
|
|
4
3
|
import pytest
|
|
5
4
|
import yaml
|
|
@@ -282,21 +281,28 @@ def test_mcp_function_model_partial_oauth_credentials() -> None:
|
|
|
282
281
|
|
|
283
282
|
@pytest.mark.unit
|
|
284
283
|
def test_mcp_function_model_existing_authorization_header() -> None:
|
|
285
|
-
"""Test that
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
284
|
+
"""Test that Authorization header can be stored in headers dict.
|
|
285
|
+
|
|
286
|
+
Note: With unified auth (via DatabricksOAuthClientProvider), the Authorization
|
|
287
|
+
header in the headers dict is not used for authentication. Instead, the
|
|
288
|
+
workspace_client from IsDatabricksResource provides authentication.
|
|
289
|
+
|
|
290
|
+
This test verifies that the model can store headers, but authentication
|
|
291
|
+
happens through the OAuth provider at invocation time.
|
|
292
|
+
"""
|
|
293
|
+
mcp_function = McpFunctionModel(
|
|
294
|
+
transport=TransportType.STREAMABLE_HTTP,
|
|
295
|
+
url="https://example.com",
|
|
296
|
+
headers={"Authorization": "Bearer existing_token"},
|
|
297
|
+
pat="test_pat",
|
|
298
|
+
workspace_host="https://test-workspace.cloud.databricks.com",
|
|
299
|
+
)
|
|
294
300
|
|
|
295
|
-
|
|
296
|
-
|
|
301
|
+
# Header is stored in the model
|
|
302
|
+
assert mcp_function.headers["Authorization"] == "Bearer existing_token"
|
|
297
303
|
|
|
298
|
-
|
|
299
|
-
|
|
304
|
+
# But authentication will happen via workspace_client at invocation time
|
|
305
|
+
# The stored header won't be used by _build_connection_config
|
|
300
306
|
|
|
301
307
|
|
|
302
308
|
@pytest.mark.unit
|
|
@@ -1770,6 +1770,7 @@ def test_create_genie_tool_with_cache_parameters() -> None:
|
|
|
1770
1770
|
|
|
1771
1771
|
# Create mock warehouse with IsDatabricksResource attrs
|
|
1772
1772
|
mock_warehouse = Mock(spec=WarehouseModel)
|
|
1773
|
+
mock_warehouse.name = "Test Warehouse"
|
|
1773
1774
|
mock_warehouse.warehouse_id = "test-warehouse"
|
|
1774
1775
|
mock_warehouse.workspace_client = Mock()
|
|
1775
1776
|
add_databricks_resource_attrs(mock_warehouse)
|
|
@@ -400,6 +400,62 @@ class TestGenieRoomModelSerialization:
|
|
|
400
400
|
# The genie_space_id is stored as the 'name' attribute
|
|
401
401
|
assert resources[0].name == "test-space-123"
|
|
402
402
|
|
|
403
|
+
def test_name_populated_from_space(
|
|
404
|
+
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
405
|
+
):
|
|
406
|
+
"""Test that name is automatically populated from GenieSpace.title if not provided."""
|
|
407
|
+
# Set a title on the mock space
|
|
408
|
+
mock_genie_space_with_serialized_data.title = "My Retail Analytics Space"
|
|
409
|
+
|
|
410
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
411
|
+
mock_workspace_client.genie.get_space.return_value = (
|
|
412
|
+
mock_genie_space_with_serialized_data
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Create without name
|
|
416
|
+
genie_room = GenieRoomModel(space_id="test-space-123")
|
|
417
|
+
|
|
418
|
+
# Name should be populated from the space title
|
|
419
|
+
assert genie_room.name == "My Retail Analytics Space"
|
|
420
|
+
|
|
421
|
+
def test_name_not_overridden_if_provided(
|
|
422
|
+
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
423
|
+
):
|
|
424
|
+
"""Test that provided name is not overridden by GenieSpace title."""
|
|
425
|
+
# Set a title on the mock space
|
|
426
|
+
mock_genie_space_with_serialized_data.title = "Space Title from API"
|
|
427
|
+
|
|
428
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
429
|
+
mock_workspace_client.genie.get_space.return_value = (
|
|
430
|
+
mock_genie_space_with_serialized_data
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# Create with explicit name
|
|
434
|
+
genie_room = GenieRoomModel(
|
|
435
|
+
name="My Custom Name", space_id="test-space-123"
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Custom name should be preserved
|
|
439
|
+
assert genie_room.name == "My Custom Name"
|
|
440
|
+
|
|
441
|
+
def test_name_handles_none_from_space(
|
|
442
|
+
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
443
|
+
):
|
|
444
|
+
"""Test that None title from space is handled gracefully."""
|
|
445
|
+
# Set title to None
|
|
446
|
+
mock_genie_space_with_serialized_data.title = None
|
|
447
|
+
|
|
448
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
449
|
+
mock_workspace_client.genie.get_space.return_value = (
|
|
450
|
+
mock_genie_space_with_serialized_data
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
# Create without name
|
|
454
|
+
genie_room = GenieRoomModel(space_id="test-space-123")
|
|
455
|
+
|
|
456
|
+
# Name should remain None
|
|
457
|
+
assert genie_room.name is None
|
|
458
|
+
|
|
403
459
|
def test_description_populated_from_space(
|
|
404
460
|
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
405
461
|
):
|
|
@@ -415,13 +471,33 @@ class TestGenieRoomModelSerialization:
|
|
|
415
471
|
)
|
|
416
472
|
|
|
417
473
|
# Create without description
|
|
418
|
-
genie_room = GenieRoomModel(
|
|
419
|
-
name="test-genie-room", space_id="test-space-123"
|
|
420
|
-
)
|
|
474
|
+
genie_room = GenieRoomModel(space_id="test-space-123")
|
|
421
475
|
|
|
422
476
|
# Description should be populated from the space
|
|
423
477
|
assert genie_room.description == "This is a test Genie space description"
|
|
424
478
|
|
|
479
|
+
def test_name_and_description_populated_together(
|
|
480
|
+
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
481
|
+
):
|
|
482
|
+
"""Test that both name and description are populated from GenieSpace when not provided."""
|
|
483
|
+
# Set both title and description on the mock space
|
|
484
|
+
mock_genie_space_with_serialized_data.title = "Production Analytics"
|
|
485
|
+
mock_genie_space_with_serialized_data.description = (
|
|
486
|
+
"Production space for analytics queries"
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
490
|
+
mock_workspace_client.genie.get_space.return_value = (
|
|
491
|
+
mock_genie_space_with_serialized_data
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Create without name or description
|
|
495
|
+
genie_room = GenieRoomModel(space_id="test-space-123")
|
|
496
|
+
|
|
497
|
+
# Both should be populated from the space
|
|
498
|
+
assert genie_room.name == "Production Analytics"
|
|
499
|
+
assert genie_room.description == "Production space for analytics queries"
|
|
500
|
+
|
|
425
501
|
def test_description_not_overridden_if_provided(
|
|
426
502
|
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
427
503
|
):
|
|
@@ -464,6 +540,84 @@ class TestGenieRoomModelSerialization:
|
|
|
464
540
|
# Description should remain None
|
|
465
541
|
assert genie_room.description is None
|
|
466
542
|
|
|
543
|
+
def test_partial_population_name_provided_description_auto(
|
|
544
|
+
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
545
|
+
):
|
|
546
|
+
"""Test that description is still populated when name is provided."""
|
|
547
|
+
# Set both title and description on the mock space
|
|
548
|
+
mock_genie_space_with_serialized_data.title = "API Space Title"
|
|
549
|
+
mock_genie_space_with_serialized_data.description = "API Space Description"
|
|
550
|
+
|
|
551
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
552
|
+
mock_workspace_client.genie.get_space.return_value = (
|
|
553
|
+
mock_genie_space_with_serialized_data
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
# Create with name but without description
|
|
557
|
+
genie_room = GenieRoomModel(name="Custom Name", space_id="test-space-123")
|
|
558
|
+
|
|
559
|
+
# Custom name should be preserved, description should be auto-populated
|
|
560
|
+
assert genie_room.name == "Custom Name"
|
|
561
|
+
assert genie_room.description == "API Space Description"
|
|
562
|
+
|
|
563
|
+
def test_partial_population_description_provided_name_auto(
|
|
564
|
+
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
565
|
+
):
|
|
566
|
+
"""Test that name is still populated when description is provided."""
|
|
567
|
+
# Set both title and description on the mock space
|
|
568
|
+
mock_genie_space_with_serialized_data.title = "API Space Title"
|
|
569
|
+
mock_genie_space_with_serialized_data.description = "API Space Description"
|
|
570
|
+
|
|
571
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
572
|
+
mock_workspace_client.genie.get_space.return_value = (
|
|
573
|
+
mock_genie_space_with_serialized_data
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
# Create with description but without name
|
|
577
|
+
genie_room = GenieRoomModel(
|
|
578
|
+
description="Custom Description", space_id="test-space-123"
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
# Custom description should be preserved, name should be auto-populated
|
|
582
|
+
assert genie_room.name == "API Space Title"
|
|
583
|
+
assert genie_room.description == "Custom Description"
|
|
584
|
+
|
|
585
|
+
def test_populate_name_and_description_handles_api_error(
|
|
586
|
+
self, mock_workspace_client
|
|
587
|
+
):
|
|
588
|
+
"""Test that populate_name_and_description validator handles API errors gracefully."""
|
|
589
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
590
|
+
# Mock API to raise an error
|
|
591
|
+
mock_workspace_client.genie.get_space.side_effect = Exception(
|
|
592
|
+
"API Error: Connection timeout"
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
# Create without name or description - should not raise exception
|
|
596
|
+
genie_room = GenieRoomModel(space_id="test-space-123")
|
|
597
|
+
|
|
598
|
+
# Name and description should remain None (not populated due to API error)
|
|
599
|
+
assert genie_room.name is None
|
|
600
|
+
assert genie_room.description is None
|
|
601
|
+
|
|
602
|
+
def test_populate_name_and_description_not_called_when_both_provided(
|
|
603
|
+
self, mock_workspace_client
|
|
604
|
+
):
|
|
605
|
+
"""Test that API is not called when both name and description are provided."""
|
|
606
|
+
with patch("dao_ai.config.WorkspaceClient", return_value=mock_workspace_client):
|
|
607
|
+
# Create with both name and description
|
|
608
|
+
genie_room = GenieRoomModel(
|
|
609
|
+
name="Custom Name",
|
|
610
|
+
description="Custom Description",
|
|
611
|
+
space_id="test-space-123",
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
# API should not have been called
|
|
615
|
+
mock_workspace_client.genie.get_space.assert_not_called()
|
|
616
|
+
|
|
617
|
+
# Values should be preserved
|
|
618
|
+
assert genie_room.name == "Custom Name"
|
|
619
|
+
assert genie_room.description == "Custom Description"
|
|
620
|
+
|
|
467
621
|
def test_tables_and_functions_inherit_authentication(
|
|
468
622
|
self, mock_workspace_client, mock_genie_space_with_serialized_data
|
|
469
623
|
):
|
|
@@ -6,7 +6,13 @@ import pytest
|
|
|
6
6
|
from langchain_core.tools import BaseTool
|
|
7
7
|
from pydantic import ValidationError
|
|
8
8
|
|
|
9
|
-
from dao_ai.config import
|
|
9
|
+
from dao_ai.config import (
|
|
10
|
+
ConnectionModel,
|
|
11
|
+
DatabricksAppModel,
|
|
12
|
+
McpFunctionModel,
|
|
13
|
+
SchemaModel,
|
|
14
|
+
TransportType,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
@pytest.mark.integration
|