dao-ai 0.1.2__py3-none-any.whl → 0.1.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. dao_ai/apps/__init__.py +24 -0
  2. dao_ai/apps/handlers.py +105 -0
  3. dao_ai/apps/model_serving.py +29 -0
  4. dao_ai/apps/resources.py +1122 -0
  5. dao_ai/apps/server.py +39 -0
  6. dao_ai/cli.py +546 -37
  7. dao_ai/config.py +1179 -139
  8. dao_ai/evaluation.py +543 -0
  9. dao_ai/genie/__init__.py +55 -7
  10. dao_ai/genie/cache/__init__.py +34 -7
  11. dao_ai/genie/cache/base.py +143 -2
  12. dao_ai/genie/cache/context_aware/__init__.py +31 -0
  13. dao_ai/genie/cache/context_aware/base.py +1151 -0
  14. dao_ai/genie/cache/context_aware/in_memory.py +609 -0
  15. dao_ai/genie/cache/context_aware/persistent.py +802 -0
  16. dao_ai/genie/cache/context_aware/postgres.py +1166 -0
  17. dao_ai/genie/cache/core.py +1 -1
  18. dao_ai/genie/cache/lru.py +257 -75
  19. dao_ai/genie/cache/optimization.py +890 -0
  20. dao_ai/genie/core.py +235 -11
  21. dao_ai/memory/postgres.py +175 -39
  22. dao_ai/middleware/__init__.py +38 -0
  23. dao_ai/middleware/assertions.py +3 -3
  24. dao_ai/middleware/context_editing.py +230 -0
  25. dao_ai/middleware/core.py +4 -4
  26. dao_ai/middleware/guardrails.py +3 -3
  27. dao_ai/middleware/human_in_the_loop.py +3 -2
  28. dao_ai/middleware/message_validation.py +4 -4
  29. dao_ai/middleware/model_call_limit.py +77 -0
  30. dao_ai/middleware/model_retry.py +121 -0
  31. dao_ai/middleware/pii.py +157 -0
  32. dao_ai/middleware/summarization.py +1 -1
  33. dao_ai/middleware/tool_call_limit.py +210 -0
  34. dao_ai/middleware/tool_retry.py +174 -0
  35. dao_ai/middleware/tool_selector.py +129 -0
  36. dao_ai/models.py +327 -370
  37. dao_ai/nodes.py +9 -16
  38. dao_ai/orchestration/core.py +33 -9
  39. dao_ai/orchestration/supervisor.py +29 -13
  40. dao_ai/orchestration/swarm.py +6 -1
  41. dao_ai/{prompts.py → prompts/__init__.py} +12 -61
  42. dao_ai/prompts/instructed_retriever_decomposition.yaml +58 -0
  43. dao_ai/prompts/instruction_reranker.yaml +14 -0
  44. dao_ai/prompts/router.yaml +37 -0
  45. dao_ai/prompts/verifier.yaml +46 -0
  46. dao_ai/providers/base.py +28 -2
  47. dao_ai/providers/databricks.py +363 -33
  48. dao_ai/state.py +1 -0
  49. dao_ai/tools/__init__.py +5 -3
  50. dao_ai/tools/genie.py +103 -26
  51. dao_ai/tools/instructed_retriever.py +366 -0
  52. dao_ai/tools/instruction_reranker.py +202 -0
  53. dao_ai/tools/mcp.py +539 -97
  54. dao_ai/tools/router.py +89 -0
  55. dao_ai/tools/slack.py +13 -2
  56. dao_ai/tools/sql.py +7 -3
  57. dao_ai/tools/unity_catalog.py +32 -10
  58. dao_ai/tools/vector_search.py +493 -160
  59. dao_ai/tools/verifier.py +159 -0
  60. dao_ai/utils.py +182 -2
  61. dao_ai/vector_search.py +46 -1
  62. {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/METADATA +45 -9
  63. dao_ai-0.1.20.dist-info/RECORD +89 -0
  64. dao_ai/agent_as_code.py +0 -22
  65. dao_ai/genie/cache/semantic.py +0 -970
  66. dao_ai-0.1.2.dist-info/RECORD +0 -64
  67. {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/WHEEL +0 -0
  68. {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/entry_points.txt +0 -0
  69. {dao_ai-0.1.2.dist-info → dao_ai-0.1.20.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,159 @@
1
+ """
2
+ Result verifier for validating search results against user constraints.
3
+
4
+ Provides structured feedback for intelligent retry when results don't match intent.
5
+ """
6
+
7
+ import json
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ import mlflow
12
+ import yaml
13
+ from langchain_core.documents import Document
14
+ from langchain_core.language_models import BaseChatModel
15
+ from langchain_core.runnables import Runnable
16
+ from loguru import logger
17
+ from mlflow.entities import SpanType
18
+
19
+ from dao_ai.config import VerificationResult
20
+
21
+ # Load prompt template
22
+ _PROMPT_PATH = Path(__file__).parent.parent / "prompts" / "verifier.yaml"
23
+
24
+
25
+ def _load_prompt_template() -> dict[str, Any]:
26
+ """Load the verifier prompt template from YAML."""
27
+ with open(_PROMPT_PATH) as f:
28
+ return yaml.safe_load(f)
29
+
30
+
31
+ def _format_results_summary(documents: list[Document], max_docs: int = 5) -> str:
32
+ """Format top documents for verification prompt."""
33
+ if not documents:
34
+ return "No results retrieved."
35
+
36
+ summaries = []
37
+ for i, doc in enumerate(documents[:max_docs]):
38
+ metadata_str = ", ".join(
39
+ f"{k}: {v}"
40
+ for k, v in doc.metadata.items()
41
+ if not k.startswith("_") and k not in ("rrf_score", "reranker_score")
42
+ )
43
+ content_preview = (
44
+ doc.page_content[:200] + "..."
45
+ if len(doc.page_content) > 200
46
+ else doc.page_content
47
+ )
48
+ summaries.append(f"{i + 1}. {content_preview}\n Metadata: {metadata_str}")
49
+
50
+ return "\n\n".join(summaries)
51
+
52
+
53
+ def _format_constraints(constraints: list[str] | None) -> str:
54
+ """Format constraints list for prompt."""
55
+ if not constraints:
56
+ return "No explicit constraints specified."
57
+ return "\n".join(f"- {c}" for c in constraints)
58
+
59
+
60
+ @mlflow.trace(name="verify_results", span_type=SpanType.LLM)
61
+ def verify_results(
62
+ llm: BaseChatModel,
63
+ query: str,
64
+ documents: list[Document],
65
+ schema_description: str,
66
+ constraints: list[str] | None = None,
67
+ previous_feedback: str | None = None,
68
+ ) -> VerificationResult:
69
+ """
70
+ Verify that search results satisfy user constraints.
71
+
72
+ Args:
73
+ llm: Language model for verification
74
+ query: User's original search query
75
+ documents: Retrieved documents to verify
76
+ schema_description: Column names, types, and filter syntax
77
+ constraints: Explicit constraints to verify
78
+ previous_feedback: Feedback from previous failed attempt (for retry)
79
+
80
+ Returns:
81
+ VerificationResult with pass/fail status and structured feedback
82
+ """
83
+ prompt_config = _load_prompt_template()
84
+ prompt_template = prompt_config["template"]
85
+
86
+ prompt = prompt_template.format(
87
+ query=query,
88
+ schema_description=schema_description,
89
+ constraints=_format_constraints(constraints),
90
+ num_results=len(documents),
91
+ results_summary=_format_results_summary(documents),
92
+ previous_feedback=previous_feedback or "N/A (first attempt)",
93
+ )
94
+
95
+ logger.trace("Verifying results", query=query[:100], num_docs=len(documents))
96
+
97
+ # Use LangChain's with_structured_output for automatic strategy selection
98
+ # (JSON schema vs tool calling based on model capabilities)
99
+ try:
100
+ structured_llm: Runnable[str, VerificationResult] = llm.with_structured_output(
101
+ VerificationResult
102
+ )
103
+ result: VerificationResult = structured_llm.invoke(prompt)
104
+ except Exception as e:
105
+ logger.warning(
106
+ "Verifier failed, treating as passed with low confidence", error=str(e)
107
+ )
108
+ return VerificationResult(
109
+ passed=True,
110
+ confidence=0.0,
111
+ feedback="Verification failed to produce a valid result",
112
+ )
113
+
114
+ # Log for observability
115
+ mlflow.log_text(
116
+ json.dumps(result.model_dump(), indent=2),
117
+ "verification_result.json",
118
+ )
119
+
120
+ logger.debug(
121
+ "Verification complete",
122
+ passed=result.passed,
123
+ confidence=result.confidence,
124
+ unmet_constraints=result.unmet_constraints,
125
+ )
126
+
127
+ return result
128
+
129
+
130
+ def add_verification_metadata(
131
+ documents: list[Document],
132
+ result: VerificationResult,
133
+ exhausted: bool = False,
134
+ ) -> list[Document]:
135
+ """
136
+ Add verification metadata to documents.
137
+
138
+ Args:
139
+ documents: Documents to annotate
140
+ result: Verification result
141
+ exhausted: Whether max retries were exhausted
142
+
143
+ Returns:
144
+ Documents with verification metadata added
145
+ """
146
+ status = "exhausted" if exhausted else ("passed" if result.passed else "failed")
147
+
148
+ annotated = []
149
+ for doc in documents:
150
+ metadata = {
151
+ **doc.metadata,
152
+ "_verification_status": status,
153
+ "_verification_confidence": result.confidence,
154
+ }
155
+ if result.feedback:
156
+ metadata["_verification_feedback"] = result.feedback
157
+ annotated.append(Document(page_content=doc.page_content, metadata=metadata))
158
+
159
+ return annotated
dao_ai/utils.py CHANGED
@@ -1,17 +1,22 @@
1
1
  import importlib
2
2
  import importlib.metadata
3
+ import json
3
4
  import os
4
5
  import re
5
6
  import site
6
7
  from importlib.metadata import PackageNotFoundError, version
7
8
  from pathlib import Path
8
- from typing import Any, Callable, Sequence
9
+ from typing import Any, Callable, Sequence, TypeVar
9
10
 
11
+ from langchain_core.language_models import BaseChatModel
10
12
  from langchain_core.tools import BaseTool
11
13
  from loguru import logger
14
+ from pydantic import BaseModel
12
15
 
13
16
  import dao_ai
14
17
 
18
+ T = TypeVar("T", bound=BaseModel)
19
+
15
20
 
16
21
  def is_lib_provided(lib_name: str, pip_requirements: Sequence[str]) -> bool:
17
22
  return any(
@@ -152,7 +157,7 @@ def get_installed_packages() -> dict[str, str]:
152
157
 
153
158
  packages: Sequence[str] = [
154
159
  f"databricks-agents=={version('databricks-agents')}",
155
- f"databricks-langchain=={version('databricks-langchain')}",
160
+ f"databricks-langchain[memory]=={version('databricks-langchain')}",
156
161
  f"databricks-mcp=={version('databricks-mcp')}",
157
162
  f"databricks-sdk[openai]=={version('databricks-sdk')}",
158
163
  f"ddgs=={version('ddgs')}",
@@ -322,3 +327,178 @@ def is_in_model_serving() -> bool:
322
327
  return True
323
328
 
324
329
  return False
330
+
331
+
332
+ def get_databricks_response_format(model_class: type[BaseModel]) -> dict[str, Any]:
333
+ """Create a Databricks-compatible response_format for structured output.
334
+
335
+ Databricks requires the json_schema response format to have a 'name' field.
336
+ This function creates the properly formatted response_format dictionary
337
+ from a Pydantic model.
338
+
339
+ Args:
340
+ model_class: A Pydantic model class to use as the output schema
341
+
342
+ Returns:
343
+ A dictionary suitable for use with llm.bind(response_format=...)
344
+
345
+ Example:
346
+ >>> response_format = get_databricks_response_format(MyModel)
347
+ >>> bound_llm = llm.bind(response_format=response_format)
348
+ >>> result = bound_llm.invoke(prompt)
349
+ """
350
+ schema = model_class.model_json_schema()
351
+
352
+ # Remove $defs from the schema - Databricks doesn't support complex refs
353
+ # We need to inline any referenced definitions
354
+ if "$defs" in schema:
355
+ schema = _inline_schema_defs(schema)
356
+
357
+ return {
358
+ "type": "json_schema",
359
+ "json_schema": {
360
+ "name": model_class.__name__,
361
+ "schema": schema,
362
+ "strict": True,
363
+ },
364
+ }
365
+
366
+
367
+ def _inline_schema_defs(schema: dict[str, Any]) -> dict[str, Any]:
368
+ """Inline $defs references in a JSON schema.
369
+
370
+ Databricks doesn't support $ref and complex nested definitions,
371
+ so we need to inline them.
372
+
373
+ Args:
374
+ schema: The original JSON schema with $defs
375
+
376
+ Returns:
377
+ A schema with all references inlined
378
+ """
379
+ defs = schema.pop("$defs", {})
380
+ if not defs:
381
+ return schema
382
+
383
+ def resolve_refs(obj: Any) -> Any:
384
+ if isinstance(obj, dict):
385
+ if "$ref" in obj:
386
+ # Extract the definition name from #/$defs/DefinitionName
387
+ ref_path = obj["$ref"]
388
+ if ref_path.startswith("#/$defs/"):
389
+ def_name = ref_path[len("#/$defs/") :]
390
+ if def_name in defs:
391
+ # Return a copy of the definition with refs resolved
392
+ return resolve_refs(defs[def_name].copy())
393
+ return obj
394
+ return {k: resolve_refs(v) for k, v in obj.items()}
395
+ elif isinstance(obj, list):
396
+ return [resolve_refs(item) for item in obj]
397
+ return obj
398
+
399
+ return resolve_refs(schema)
400
+
401
+
402
+ def _repair_json(content: str) -> str | None:
403
+ """Attempt to repair malformed JSON from LLM output.
404
+
405
+ Handles common issues:
406
+ - Extra text before/after JSON object
407
+ - Truncated JSON (unclosed brackets/braces)
408
+ - Trailing commas
409
+
410
+ Args:
411
+ content: The potentially malformed JSON string
412
+
413
+ Returns:
414
+ Repaired JSON string if successful, None otherwise
415
+ """
416
+ # 1. Extract JSON object if wrapped in extra text
417
+ start = content.find("{")
418
+ end = content.rfind("}")
419
+ if start == -1 or end == -1 or start >= end:
420
+ return None
421
+ content = content[start : end + 1]
422
+
423
+ # 2. Try parsing as-is first
424
+ try:
425
+ json.loads(content)
426
+ return content
427
+ except json.JSONDecodeError:
428
+ pass
429
+
430
+ # 3. Fix trailing commas before closing brackets
431
+ content = re.sub(r",\s*}", "}", content)
432
+ content = re.sub(r",\s*]", "]", content)
433
+
434
+ # 4. Try to close unclosed brackets/braces
435
+ open_braces = content.count("{") - content.count("}")
436
+ open_brackets = content.count("[") - content.count("]")
437
+
438
+ if open_braces > 0 or open_brackets > 0:
439
+ # Remove trailing comma if present
440
+ content = content.rstrip().rstrip(",")
441
+ content += "]" * open_brackets + "}" * open_braces
442
+
443
+ # 5. Final validation
444
+ try:
445
+ json.loads(content)
446
+ return content
447
+ except json.JSONDecodeError:
448
+ return None
449
+
450
+
451
+ def invoke_with_structured_output(
452
+ llm: BaseChatModel,
453
+ prompt: str,
454
+ model_class: type[T],
455
+ ) -> T | None:
456
+ """Invoke an LLM with Databricks-compatible structured output.
457
+
458
+ Uses response_format with json_schema type and proper 'name' field
459
+ as required by Databricks Foundation Model APIs.
460
+
461
+ Args:
462
+ llm: The language model to invoke
463
+ prompt: The prompt to send to the model
464
+ model_class: The Pydantic model class for the expected output
465
+
466
+ Returns:
467
+ An instance of model_class, or None if parsing fails
468
+ """
469
+ response_format = get_databricks_response_format(model_class)
470
+ bound_llm = llm.bind(response_format=response_format)
471
+
472
+ response = bound_llm.invoke(prompt)
473
+
474
+ content = response.content
475
+ if not isinstance(content, str):
476
+ return None
477
+
478
+ try:
479
+ # Try parsing the JSON directly
480
+ result_dict = json.loads(content)
481
+ return model_class.model_validate(result_dict)
482
+ except json.JSONDecodeError as e:
483
+ # Attempt JSON repair
484
+ repaired = _repair_json(content)
485
+ if repaired:
486
+ try:
487
+ result_dict = json.loads(repaired)
488
+ logger.debug("JSON repair successful", model_class=model_class.__name__)
489
+ return model_class.model_validate(result_dict)
490
+ except (json.JSONDecodeError, Exception):
491
+ pass
492
+ logger.warning(
493
+ "Failed to parse structured output",
494
+ error=str(e),
495
+ model_class=model_class.__name__,
496
+ )
497
+ return None
498
+ except Exception as e:
499
+ logger.warning(
500
+ "Failed to parse structured output",
501
+ error=str(e),
502
+ model_class=model_class.__name__,
503
+ )
504
+ return None
dao_ai/vector_search.py CHANGED
@@ -64,7 +64,12 @@ def index_exists(
64
64
  return True
65
65
  except Exception as e:
66
66
  # Check if this is a "not exists" error or something else
67
- if "RESOURCE_DOES_NOT_EXIST" not in str(e):
67
+ # Handle both "RESOURCE_DOES_NOT_EXIST" and "does not exist" error patterns
68
+ error_str = str(e).lower()
69
+ if (
70
+ "does not exist" not in error_str
71
+ and "resource_does_not_exist" not in error_str
72
+ ):
68
73
  # For unexpected errors, provide a more helpful message
69
74
  print(
70
75
  "Unexpected error describing the index. This could be a permission issue."
@@ -72,3 +77,43 @@ def index_exists(
72
77
  raise e
73
78
  # If we reach here, the index doesn't exist
74
79
  return False
80
+
81
+
82
+ def find_index(
83
+ vsc: VectorSearchClient, index_full_name: str
84
+ ) -> tuple[bool, str | None]:
85
+ """
86
+ Find a Vector Search index across all endpoints.
87
+
88
+ Searches all available endpoints to find where the index is located.
89
+
90
+ Args:
91
+ vsc: Databricks Vector Search client instance
92
+ index_full_name: Fully qualified name of the index (catalog.schema.index)
93
+
94
+ Returns:
95
+ Tuple of (exists: bool, endpoint_name: str | None)
96
+ - (True, endpoint_name) if index is found
97
+ - (False, None) if index is not found on any endpoint
98
+ """
99
+ try:
100
+ endpoints = vsc.list_endpoints().get("endpoints", [])
101
+ except Exception as e:
102
+ if "REQUEST_LIMIT_EXCEEDED" in str(e):
103
+ print("WARN: couldn't list endpoints due to REQUEST_LIMIT_EXCEEDED error.")
104
+ return (False, None)
105
+ raise e
106
+
107
+ for endpoint in endpoints:
108
+ endpoint_name: str = endpoint["name"]
109
+ try:
110
+ vsc.get_index(endpoint_name, index_full_name).describe()
111
+ return (True, endpoint_name)
112
+ except Exception:
113
+ # Index not on this endpoint, try next
114
+ # Catches both "does not exist" and "RESOURCE_DOES_NOT_EXIST" errors,
115
+ # as well as other errors (permission issues, etc.) - we continue
116
+ # searching other endpoints regardless of error type
117
+ continue
118
+
119
+ return (False, None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dao-ai
3
- Version: 0.1.2
3
+ Version: 0.1.20
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
@@ -26,9 +26,9 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Classifier: Topic :: System :: Distributed Computing
27
27
  Requires-Python: >=3.11
28
28
  Requires-Dist: databricks-agents>=1.9.0
29
- Requires-Dist: databricks-langchain[memory]>=0.12.1
29
+ Requires-Dist: databricks-langchain[memory]>=0.13.0
30
30
  Requires-Dist: databricks-mcp>=0.5.0
31
- Requires-Dist: databricks-sdk[openai]>=0.76.0
31
+ Requires-Dist: databricks-sdk[openai]>=0.77.0
32
32
  Requires-Dist: ddgs>=9.10.0
33
33
  Requires-Dist: dspy>=2.6.27
34
34
  Requires-Dist: flashrank>=0.2.10
@@ -43,7 +43,7 @@ Requires-Dist: langgraph>=1.0.5
43
43
  Requires-Dist: langmem>=0.0.30
44
44
  Requires-Dist: loguru>=0.7.3
45
45
  Requires-Dist: mcp>=1.24.0
46
- Requires-Dist: mlflow>=3.8.1
46
+ Requires-Dist: mlflow[databricks]>=3.8.1
47
47
  Requires-Dist: nest-asyncio>=1.6.0
48
48
  Requires-Dist: openevals>=0.1.3
49
49
  Requires-Dist: openpyxl>=3.1.5
@@ -79,7 +79,7 @@ Description-Content-Type: text/markdown
79
79
 
80
80
  # DAO: Declarative Agent Orchestration
81
81
 
82
- [![Version](https://img.shields.io/badge/version-0.1.0-blue.svg)](CHANGELOG.md)
82
+ [![Version](https://img.shields.io/badge/version-0.1.2-blue.svg)](CHANGELOG.md)
83
83
  [![Python](https://img.shields.io/badge/python-3.11+-green.svg)](https://www.python.org/)
84
84
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
85
85
 
@@ -125,7 +125,7 @@ DAO AI Builder generates valid YAML configurations that work seamlessly with thi
125
125
  - **[Architecture](docs/architecture.md)** - Understand how DAO works under the hood
126
126
 
127
127
  ### Core Concepts
128
- - **[Key Capabilities](docs/key-capabilities.md)** - Explore 14 powerful features for production agents
128
+ - **[Key Capabilities](docs/key-capabilities.md)** - Explore 15 powerful features for production agents
129
129
  - **[Configuration Reference](docs/configuration-reference.md)** - Complete YAML configuration guide
130
130
  - **[Examples](docs/examples.md)** - Ready-to-use example configurations
131
131
 
@@ -148,7 +148,7 @@ Before you begin, you'll need:
148
148
  - **Python 3.11 or newer** installed on your computer ([download here](https://www.python.org/downloads/))
149
149
  - **A Databricks workspace** (ask your IT team or see [Databricks docs](https://docs.databricks.com/))
150
150
  - Access to **Unity Catalog** (your organization's data catalog)
151
- - **Model Serving** enabled (for deploying AI agents)
151
+ - **Model Serving** or **Databricks Apps** enabled (for deploying AI agents)
152
152
  - *Optional*: Vector Search, Genie (for advanced features)
153
153
 
154
154
  **Not sure if you have access?** Your Databricks administrator can grant you permissions.
@@ -235,7 +235,7 @@ app:
235
235
  - *assistant
236
236
  orchestration:
237
237
  swarm:
238
- model: *default_llm
238
+ default_agent: *assistant
239
239
  ```
240
240
 
241
241
  **💡 What's happening here?**
@@ -293,6 +293,16 @@ This single command:
293
293
  3. Deploys it to Databricks
294
294
  4. Creates a serving endpoint
295
295
 
296
+ **Deploying to a specific workspace:**
297
+
298
+ ```bash
299
+ # Deploy to AWS workspace
300
+ dao-ai bundle --deploy --run -c config/my_agent.yaml --profile aws-field-eng
301
+
302
+ # Deploy to Azure workspace
303
+ dao-ai bundle --deploy --run -c config/my_agent.yaml --profile azure-retail
304
+ ```
305
+
296
306
  **Step 5: Interact with your agent**
297
307
 
298
308
  Once deployed, you can chat with your agent using Python:
@@ -335,6 +345,7 @@ DAO provides powerful capabilities for building production-ready AI agents:
335
345
 
336
346
  | Feature | Description |
337
347
  |---------|-------------|
348
+ | **Dual Deployment Targets** | Deploy to Databricks Model Serving or Databricks Apps with a single config |
338
349
  | **Multi-Tool Support** | Python functions, Unity Catalog, MCP, Agent Endpoints |
339
350
  | **On-Behalf-Of User** | Per-user permissions and governance |
340
351
  | **Advanced Caching** | Two-tier (LRU + Semantic) caching for cost optimization |
@@ -398,7 +409,8 @@ The `config/examples/` directory contains ready-to-use configurations organized
398
409
 
399
410
  - `01_getting_started/minimal.yaml` - Simplest possible agent
400
411
  - `02_tools/vector_search_with_reranking.yaml` - RAG with improved accuracy
401
- - `04_genie/genie_semantic_cache.yaml` - NL-to-SQL with two-tier caching
412
+ - `04_genie/genie_semantic_cache.yaml` - NL-to-SQL with PostgreSQL semantic caching
413
+ - `04_genie/genie_in_memory_semantic_cache.yaml` - NL-to-SQL with in-memory semantic caching (no database)
402
414
  - `05_memory/conversation_summarization.yaml` - Long conversation handling
403
415
  - `06_on_behalf_of_user/obo_basic.yaml` - User-level access control
404
416
  - `07_human_in_the_loop/human_in_the_loop.yaml` - Approval workflows
@@ -422,10 +434,34 @@ dao-ai graph -c config/my_config.yaml -o workflow.png
422
434
  # Deploy with Databricks Asset Bundles
423
435
  dao-ai bundle --deploy --run -c config/my_config.yaml
424
436
 
437
+ # Deploy to a specific workspace (multi-cloud support)
438
+ dao-ai bundle --deploy -c config/my_config.yaml --profile aws-field-eng
439
+ dao-ai bundle --deploy -c config/my_config.yaml --profile azure-retail
440
+
425
441
  # Interactive chat with agent
426
442
  dao-ai chat -c config/my_config.yaml
427
443
  ```
428
444
 
445
+ ### Multi-Cloud Deployment
446
+
447
+ DAO AI supports deploying to Azure, AWS, and GCP workspaces with automatic cloud detection:
448
+
449
+ ```bash
450
+ # Deploy to AWS workspace
451
+ dao-ai bundle --deploy -c config/my_config.yaml --profile aws-prod
452
+
453
+ # Deploy to Azure workspace
454
+ dao-ai bundle --deploy -c config/my_config.yaml --profile azure-prod
455
+
456
+ # Deploy to GCP workspace
457
+ dao-ai bundle --deploy -c config/my_config.yaml --profile gcp-prod
458
+ ```
459
+
460
+ The CLI automatically:
461
+ - Detects the cloud provider from your profile's workspace URL
462
+ - Selects appropriate compute node types for each cloud
463
+ - Creates isolated deployment state per profile
464
+
429
465
  👉 **Learn more:** [CLI Reference Documentation](docs/cli-reference.md)
430
466
 
431
467
  ---
@@ -0,0 +1,89 @@
1
+ dao_ai/__init__.py,sha256=18P98ExEgUaJ1Byw440Ct1ty59v6nxyWtc5S6Uq2m9Q,1062
2
+ dao_ai/catalog.py,sha256=sPZpHTD3lPx4EZUtIWeQV7VQM89WJ6YH__wluk1v2lE,4947
3
+ dao_ai/cli.py,sha256=7hVCC8mn9S3c4wW-eRt-WoFKzV1wPdJVAeNhkyhfUGc,53251
4
+ dao_ai/config.py,sha256=08eHf7HzXNEsFY1_-lCESCtC-RhEZy9nikTiWPmc25A,156770
5
+ dao_ai/evaluation.py,sha256=4dveWDwFnUxaybswr0gag3ydZ5RGVCTRaiE3eKLClD4,18161
6
+ dao_ai/graph.py,sha256=1-uQlo7iXZQTT3uU8aYu0N5rnhw5_g_2YLwVsAs6M-U,1119
7
+ dao_ai/logging.py,sha256=lYy4BmucCHvwW7aI3YQkQXKJtMvtTnPDu9Hnd7_O4oc,1556
8
+ dao_ai/messages.py,sha256=4ZBzO4iFdktGSLrmhHzFjzMIt2tpaL-aQLHOQJysGnY,6959
9
+ dao_ai/models.py,sha256=NaHj91Gra4M8thlKX1DSufLqtJfZSZ55lm1H1dJL_O8,77320
10
+ dao_ai/nodes.py,sha256=H7_C0ev0TpS5KWkGZD6eE4Wn6ouBwnN5HgYUyBeKe0A,10881
11
+ dao_ai/optimization.py,sha256=phK6t4wYmWPObCjGUBHdZzsaFXGhQOjhAek2bAEfwXo,22971
12
+ dao_ai/state.py,sha256=ifDTAC7epdowk3Z1CP3Xqw4uH2dIxQEVF3C747dA8yI,6436
13
+ dao_ai/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ dao_ai/utils.py,sha256=ImgH0jnHPCK2AR7KcueG_Zb7kltcBzTw78ujDUpARIE,17184
15
+ dao_ai/vector_search.py,sha256=PfmT2PDMymk-3dTm2uszlOZNyHyiDge--imxKpKJRsY,4440
16
+ dao_ai/apps/__init__.py,sha256=RLuhZf4gQ4pemwKDz1183aXib8UfaRhwfKvRx68GRlM,661
17
+ dao_ai/apps/handlers.py,sha256=6-IhhklHSPnS8aqKp155wPaSnYWTU1BSOPwbdWYBkFU,3594
18
+ dao_ai/apps/model_serving.py,sha256=XLt3_0pGSRceMK6YtOrND9Jnh7mKLPCtwjVDLIaptQU,847
19
+ dao_ai/apps/resources.py,sha256=5l6UxfMq6uspOql-HNDyUikfqRAa9eH_TiJHrGgMb6s,40029
20
+ dao_ai/apps/server.py,sha256=neWbVnC2z9f-tJZBnho70FytNDEVOdOM1YngoGc5KHI,1264
21
+ dao_ai/genie/__init__.py,sha256=UpSvP6gZO8H-eAPokYpkshvFxYD4ETYZHz-pRPoK2sI,2786
22
+ dao_ai/genie/core.py,sha256=eKZo4pRagwI6QglAXqHYjUaC3AicmGMiy_9WIuZ-tzw,9119
23
+ dao_ai/genie/cache/__init__.py,sha256=hMD2q7VRX8fQmkd4rXhxGJdRIVbDqLh2DyjBu0CdM_k,2129
24
+ dao_ai/genie/cache/base.py,sha256=nbWl-KTstUPGagdUtO8xtVUSosuqkaNc_hx-PgT1ROo,7155
25
+ dao_ai/genie/cache/core.py,sha256=48sDY7dbrsmflb96OFEE8DYarNB6zyiFxZQG-qfhXD4,2537
26
+ dao_ai/genie/cache/lru.py,sha256=dWoNottME8y6y_OKnQZ1xH4NmQxk2PdXvUgKcdzjlxI,19935
27
+ dao_ai/genie/cache/optimization.py,sha256=8tyMrthHCxdOdvqDwGnGAvRNCWk9e0nIb3dJkE05t-4,30167
28
+ dao_ai/genie/cache/context_aware/__init__.py,sha256=EaW2CmWxb0CDNcm3pKL6Vyaw2KuwanKV1LiFVlT2M-s,1197
29
+ dao_ai/genie/cache/context_aware/base.py,sha256=o3S3f0B0jCB7vjb2XKWm5hFcO6v-jNYc6ZELyBkeBrg,40452
30
+ dao_ai/genie/cache/context_aware/in_memory.py,sha256=Z-ZrMTVD1pjC55irhodBWsrs6D2h3Sv-Iifk0Cr1mRo,22316
31
+ dao_ai/genie/cache/context_aware/persistent.py,sha256=cpn25Go6ZyN65lY_vh5cWcuqr__nNH7RPWJA_LP7wcE,28154
32
+ dao_ai/genie/cache/context_aware/postgres.py,sha256=cakJuba3dFjKoEHlpBYgmr3pEuM88dM8ambgL6Vq_54,46403
33
+ dao_ai/hooks/__init__.py,sha256=uA4DQdP9gDf4SyNjNx9mWPoI8UZOcTyFsCXV0NraFvQ,463
34
+ dao_ai/hooks/core.py,sha256=yZAfRfB0MyMo--uwGr4STtVxxen5s4ZUrNTnR3a3qkA,1721
35
+ dao_ai/memory/__init__.py,sha256=Us3wFehvug_h83m-UJ7OXdq2qZ0e9nHBQE7m5RwoAd8,559
36
+ dao_ai/memory/base.py,sha256=99nfr2UZJ4jmfTL_KrqUlRSCoRxzkZyWyx5WqeUoMdQ,338
37
+ dao_ai/memory/core.py,sha256=38H-JLIyUrRDIECLvpXK3iJlWG35X97E-DTo_4c3Jzc,6317
38
+ dao_ai/memory/databricks.py,sha256=SM6nwLjhSRJO4hLc3GUuht5YydYtTi3BAOae6jPwTm4,14377
39
+ dao_ai/memory/postgres.py,sha256=bSjtvEht0h6jy2ADN2vqISVQDxm_DeM586VDdGaShJQ,23168
40
+ dao_ai/middleware/__init__.py,sha256=Qy8wbvjXF7TrUzi3tWziOwxqsrUcT1rzE3UWd3x5CrU,5108
41
+ dao_ai/middleware/assertions.py,sha256=C1K-TnNZfBEwWouioHCt6c48i1ux9QKfQaX6AzghhgE,27408
42
+ dao_ai/middleware/base.py,sha256=uG2tpdnjL5xY5jCKvb_m3UTBtl4ZC6fJQUkDsQvV8S4,1279
43
+ dao_ai/middleware/context_editing.py,sha256=5rNKqH1phFFQTVW-4nzlVH5cbqomD-HFEIy2Z841D4I,7687
44
+ dao_ai/middleware/core.py,sha256=XFzL-A1_jS_pUCw7Q-z1WD0gutmpWZhfCMqHI6ifbhA,2096
45
+ dao_ai/middleware/guardrails.py,sha256=wjH4OwDRagkrKpcGNf7_bf3eJhtMpIWK_RIrrvMeqDs,14023
46
+ dao_ai/middleware/human_in_the_loop.py,sha256=YS11oQcRVzHVuZi309tuXfB4ItGuOz-PRT0fXwupdsY,7537
47
+ dao_ai/middleware/message_validation.py,sha256=SGvXow76BB1eW8zDSSuLJgRkIvLU-WOwA1Cd5_DatmQ,19818
48
+ dao_ai/middleware/model_call_limit.py,sha256=sxv15iNOUMjVLTEXwMBR5zAkxkWbnysbSIsBcuJbNUI,2216
49
+ dao_ai/middleware/model_retry.py,sha256=SlWjAcaEmvj6KBOkjUicChYjhlg7bAJM7-e6KLpHS9Q,3908
50
+ dao_ai/middleware/pii.py,sha256=zetfoz1WlJ-V0vjJp37v8NGimXB27EkZfetUHpGCXno,5137
51
+ dao_ai/middleware/summarization.py,sha256=gp2s9uc4DEJat-mWjWEzMaR-zAAeUOXYvu5EEYtqae4,7143
52
+ dao_ai/middleware/tool_call_limit.py,sha256=WQ3NmA3pLo-pNPBmwM7KwkYpT1segEnWqkhgW1xNkCE,6321
53
+ dao_ai/middleware/tool_retry.py,sha256=QfJ7yTHneME8VtnA88QcmnjXIegSFeJztyngy49wTgM,5568
54
+ dao_ai/middleware/tool_selector.py,sha256=POj72YdzZEiNGfW4AQXPBeVVS1RUBsiG7PBuSENEhe0,4516
55
+ dao_ai/orchestration/__init__.py,sha256=i85CLfRR335NcCFhaXABcMkn6WZfXnJ8cHH4YZsZN0s,1622
56
+ dao_ai/orchestration/core.py,sha256=8bPigzWtHUZ0Gw4Q_91uvcREucVQstxlelC06W_qmn0,10683
57
+ dao_ai/orchestration/supervisor.py,sha256=FoQ1fYP_e0taKC4ByITqJLOYvwJd1zecYYLs4RcY1lk,10605
58
+ dao_ai/orchestration/swarm.py,sha256=BloDI0TWhGisv9r3-zTgJWZQy9l3hbQ5tXYggovr5i8,9467
59
+ dao_ai/prompts/__init__.py,sha256=r91BY_yq28iUL0Pz5NbMo1VEDQh-aE44GqN0tBrIKfc,3011
60
+ dao_ai/prompts/instructed_retriever_decomposition.yaml,sha256=OkBLLlgU8MXtvGPlhESXgFfwYCUGRDcyD8O1iWOsmbk,2107
61
+ dao_ai/prompts/instruction_reranker.yaml,sha256=4OGZLNbdcWk6slBY5vnt_C-nGLPZM6e21smTNyaRPmE,406
62
+ dao_ai/prompts/router.yaml,sha256=79C_O98cpNndeMO0Vdn91CC7vxZx0hZ1rl1BAgnGjYc,1319
63
+ dao_ai/prompts/verifier.yaml,sha256=9snFQuxfYuEr46F4gv13VqL9q2PJCtWlbBhN3_IO2zI,1455
64
+ dao_ai/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
+ dao_ai/providers/base.py,sha256=cJGo3UjUTPgS91dv38ePOHwQQtYhIa84ebb167CBXjk,2111
66
+ dao_ai/providers/databricks.py,sha256=cg-TY9IS3-OqIo1gkLe1YwOR1H-s8YTBGrqDrkOWR6c,73569
67
+ dao_ai/tools/__init__.py,sha256=4dX_N6G_WrkV2BhS7hN8tR9zrMNlorLTKQYM388KTR4,1758
68
+ dao_ai/tools/agent.py,sha256=plIWALywRjaDSnot13nYehBsrHRpBUpsVZakoGeajOE,1858
69
+ dao_ai/tools/core.py,sha256=bRIN3BZhRQX8-Kpu3HPomliodyskCqjxynQmYbk6Vjs,3783
70
+ dao_ai/tools/email.py,sha256=A3TsCoQgJR7UUWR0g45OPRGDpVoYwctFs1MOZMTt_d4,7389
71
+ dao_ai/tools/genie.py,sha256=MWW2nCutl5-Wxwt4m7AxrS0ufqZimTKXa-lbojhwRYQ,12219
72
+ dao_ai/tools/instructed_retriever.py,sha256=iEu7oH1Z9_-Id0SMaq-dAgCNigeRrJDDTSZTcOJLl6k,12990
73
+ dao_ai/tools/instruction_reranker.py,sha256=_1kGwrXkJk4QR2p8n3lAaYkUVoidxCxV9wNCtoS0qco,6730
74
+ dao_ai/tools/mcp.py,sha256=4uvag52OJPInUEnxFLwpE0JRugTrgHeWbkP5lzIx4lg,22620
75
+ dao_ai/tools/memory.py,sha256=lwObKimAand22Nq3Y63tsv-AXQ5SXUigN9PqRjoWKes,1836
76
+ dao_ai/tools/python.py,sha256=jWFnZPni2sCdtd8D1CqXnZIPHnWkdK27bCJnBXpzhvo,1879
77
+ dao_ai/tools/router.py,sha256=YIVzSk4-ZFQHgvkhyrPHGLbDyzE9koa5QmbcTT-npnI,2872
78
+ dao_ai/tools/search.py,sha256=cJ3D9FKr1GAR6xz55dLtRkjtQsI0WRueGt9TPDFpOxc,433
79
+ dao_ai/tools/slack.py,sha256=QnMsA7cYD1MnEcqGqqSr6bKIhV0RgDpkyaiPmDqnAts,5433
80
+ dao_ai/tools/sql.py,sha256=FG-Aa0FAUAnhCuZvao1J-y-cMM6bU5eCujNbsYn0xDw,7864
81
+ dao_ai/tools/time.py,sha256=tufJniwivq29y0LIffbgeBTIDE6VgrLpmVf8Qr90qjw,9224
82
+ dao_ai/tools/unity_catalog.py,sha256=oBlW6pH-Ne08g60QW9wVi_tyeVYDiecuNoxQbIIFmN8,16515
83
+ dao_ai/tools/vector_search.py,sha256=34uhd58FKHzvcdgHHoACRdZAUJWTaUuPYiwIqBwvGqk,29061
84
+ dao_ai/tools/verifier.py,sha256=ociBVsGkQNyhWS6F6G8x17V7zAQfSuTe4Xcd6Y-7lPE,4975
85
+ dao_ai-0.1.20.dist-info/METADATA,sha256=v-u8GWz1AnJXLm1LfSMrD-cHZYT-nbyU57VNOxG9KWY,16954
86
+ dao_ai-0.1.20.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
87
+ dao_ai-0.1.20.dist-info/entry_points.txt,sha256=Xa-UFyc6gWGwMqMJOt06ZOog2vAfygV_DSwg1AiP46g,43
88
+ dao_ai-0.1.20.dist-info/licenses/LICENSE,sha256=YZt3W32LtPYruuvHE9lGk2bw6ZPMMJD8yLrjgHybyz4,1069
89
+ dao_ai-0.1.20.dist-info/RECORD,,
dao_ai/agent_as_code.py DELETED
@@ -1,22 +0,0 @@
1
- import mlflow
2
- from mlflow.models import ModelConfig
3
- from mlflow.pyfunc import ResponsesAgent
4
-
5
- from dao_ai.config import AppConfig
6
- from dao_ai.logging import configure_logging
7
-
8
- mlflow.set_registry_uri("databricks-uc")
9
- mlflow.set_tracking_uri("databricks")
10
-
11
- mlflow.langchain.autolog()
12
-
13
- model_config: ModelConfig = ModelConfig()
14
- config: AppConfig = AppConfig(**model_config.to_dict())
15
-
16
- log_level: str = config.app.log_level
17
-
18
- configure_logging(level=log_level)
19
-
20
- app: ResponsesAgent = config.as_responses_agent()
21
-
22
- mlflow.models.set_model(app)