dao-ai 0.1.17__py3-none-any.whl → 0.1.18__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.
dao_ai/cli.py CHANGED
@@ -521,14 +521,19 @@ def handle_chat_command(options: Namespace) -> None:
521
521
  )
522
522
  continue
523
523
 
524
+ # Normalize user_id for memory namespace compatibility (replace . with _)
525
+ # This matches the normalization in models.py _convert_to_context
526
+ if configurable.get("user_id"):
527
+ configurable["user_id"] = configurable["user_id"].replace(".", "_")
528
+
524
529
  # Create Context object from configurable dict
525
530
  from dao_ai.state import Context
526
531
 
527
532
  context = Context(**configurable)
528
533
 
529
- # Prepare config with thread_id for checkpointer
530
- # Note: thread_id is needed in config for checkpointer/memory
531
- config = {"configurable": {"thread_id": options.thread_id}}
534
+ # Prepare config with all context fields for checkpointer/memory
535
+ # Note: langmem tools require user_id in config.configurable for namespace resolution
536
+ config = {"configurable": context.model_dump()}
532
537
 
533
538
  # Invoke the graph and handle interrupts (HITL)
534
539
  # Wrap in async function to maintain connection pool throughout
dao_ai/config.py CHANGED
@@ -1402,13 +1402,20 @@ class DatabaseModel(IsDatabricksResource):
1402
1402
  - Databricks Lakebase: Provide `instance_name` (authentication optional, supports ambient auth)
1403
1403
  - Standard PostgreSQL: Provide `host` (authentication required via user/password)
1404
1404
 
1405
- Note: `instance_name` and `host` are mutually exclusive. Provide one or the other.
1405
+ Note: For Lakebase connections, `name` is optional and defaults to `instance_name`.
1406
+ For PostgreSQL connections, `name` is required.
1407
+
1408
+ Example Databricks Lakebase (minimal):
1409
+ ```yaml
1410
+ databases:
1411
+ my_lakebase:
1412
+ instance_name: my-lakebase-instance # name defaults to instance_name
1413
+ ```
1406
1414
 
1407
1415
  Example Databricks Lakebase with Service Principal:
1408
1416
  ```yaml
1409
1417
  databases:
1410
1418
  my_lakebase:
1411
- name: my-database
1412
1419
  instance_name: my-lakebase-instance
1413
1420
  service_principal:
1414
1421
  client_id:
@@ -1424,7 +1431,6 @@ class DatabaseModel(IsDatabricksResource):
1424
1431
  ```yaml
1425
1432
  databases:
1426
1433
  my_lakebase:
1427
- name: my-database
1428
1434
  instance_name: my-lakebase-instance
1429
1435
  on_behalf_of_user: true
1430
1436
  ```
@@ -1444,7 +1450,7 @@ class DatabaseModel(IsDatabricksResource):
1444
1450
  """
1445
1451
 
1446
1452
  model_config = ConfigDict(use_enum_values=True, extra="forbid")
1447
- name: str
1453
+ name: Optional[str] = None
1448
1454
  instance_name: Optional[str] = None
1449
1455
  description: Optional[str] = None
1450
1456
  host: Optional[AnyVariable] = None
@@ -1493,6 +1499,17 @@ class DatabaseModel(IsDatabricksResource):
1493
1499
  )
1494
1500
  return self
1495
1501
 
1502
+ @model_validator(mode="after")
1503
+ def populate_name_from_instance_name(self) -> Self:
1504
+ """Populate name from instance_name if not provided for Lakebase connections."""
1505
+ if self.name is None and self.instance_name:
1506
+ self.name = self.instance_name
1507
+ elif self.name is None:
1508
+ raise ValueError(
1509
+ "Either 'name' or 'instance_name' must be provided for DatabaseModel."
1510
+ )
1511
+ return self
1512
+
1496
1513
  @model_validator(mode="after")
1497
1514
  def update_user(self) -> Self:
1498
1515
  # Skip if using OBO (passive auth), explicit credentials, or explicit user
@@ -1590,10 +1607,10 @@ class DatabaseModel(IsDatabricksResource):
1590
1607
  username: str | None = None
1591
1608
  password_value: str | None = None
1592
1609
 
1593
- # Resolve host - may need to fetch at runtime for OBO mode
1610
+ # Resolve host - fetch from API at runtime for Lakebase if not provided
1594
1611
  host_value: Any = self.host
1595
- if host_value is None and self.is_lakebase and self.on_behalf_of_user:
1596
- # Fetch host at runtime for OBO mode
1612
+ if host_value is None and self.is_lakebase:
1613
+ # Fetch host from Lakebase instance API
1597
1614
  existing_instance: DatabaseInstance = (
1598
1615
  self.workspace_client.database.get_database_instance(
1599
1616
  name=self.instance_name
@@ -1763,43 +1780,83 @@ class SearchParametersModel(BaseModel):
1763
1780
  query_type: Optional[str] = "ANN"
1764
1781
 
1765
1782
 
1783
+ class InstructionAwareRerankModel(BaseModel):
1784
+ """
1785
+ LLM-based reranking considering user instructions and constraints.
1786
+
1787
+ Use fast models (GPT-3.5, Haiku, Llama 3 8B) to minimize latency (~100ms).
1788
+ Runs AFTER FlashRank as an additional constraint-aware reranking stage.
1789
+ Skipped for 'standard' mode when auto_bypass=true in router config.
1790
+
1791
+ Example:
1792
+ ```yaml
1793
+ rerank:
1794
+ model: ms-marco-MiniLM-L-12-v2
1795
+ top_n: 20
1796
+ instruction_aware:
1797
+ model: *fast_llm
1798
+ instructions: |
1799
+ Prioritize results matching price and brand constraints.
1800
+ top_n: 10
1801
+ ```
1802
+ """
1803
+
1804
+ model_config = ConfigDict(use_enum_values=True, extra="forbid")
1805
+
1806
+ model: Optional["LLMModel"] = Field(
1807
+ default=None,
1808
+ description="LLM for instruction reranking (fast model recommended)",
1809
+ )
1810
+ instructions: Optional[str] = Field(
1811
+ default=None,
1812
+ description="Custom reranking instructions for constraint prioritization",
1813
+ )
1814
+ top_n: Optional[int] = Field(
1815
+ default=None,
1816
+ description="Number of documents to return after instruction reranking",
1817
+ )
1818
+
1819
+
1766
1820
  class RerankParametersModel(BaseModel):
1767
1821
  """
1768
- Configuration for reranking retrieved documents using FlashRank.
1822
+ Configuration for reranking retrieved documents.
1769
1823
 
1770
- FlashRank provides fast, local reranking without API calls using lightweight
1771
- cross-encoder models. Reranking improves retrieval quality by reordering results
1772
- based on semantic relevance to the query.
1824
+ Supports three reranking options that can be combined:
1825
+ 1. FlashRank (local cross-encoder) - set `model`
1826
+ 2. Databricks server-side reranking - set `columns`
1827
+ 3. LLM instruction-aware reranking - set `instruction_aware`
1773
1828
 
1774
- Typical workflow:
1775
- 1. Retrieve more documents than needed (e.g., 50 via num_results)
1776
- 2. Rerank all retrieved documents
1777
- 3. Return top_n best matches (e.g., 5)
1829
+ Example with Databricks columns + instruction-aware (no FlashRank):
1830
+ ```yaml
1831
+ rerank:
1832
+ columns: # Databricks server-side reranking
1833
+ - product_name
1834
+ - brand_name
1835
+ instruction_aware: # LLM-based constraint reranking
1836
+ model: *fast_llm
1837
+ instructions: "Prioritize by brand preferences"
1838
+ top_n: 10
1839
+ ```
1778
1840
 
1779
- Example:
1841
+ Example with FlashRank:
1780
1842
  ```yaml
1781
- retriever:
1782
- search_parameters:
1783
- num_results: 50 # Retrieve more candidates
1784
- rerank:
1785
- model: ms-marco-MiniLM-L-12-v2
1786
- top_n: 5 # Return top 5 after reranking
1843
+ rerank:
1844
+ model: ms-marco-MiniLM-L-12-v2 # FlashRank model
1845
+ top_n: 10
1787
1846
  ```
1788
1847
 
1789
- Available models (see https://github.com/PrithivirajDamodaran/FlashRank):
1848
+ Available FlashRank models (see https://github.com/PrithivirajDamodaran/FlashRank):
1790
1849
  - "ms-marco-TinyBERT-L-2-v2" (~4MB, fastest)
1791
- - "ms-marco-MiniLM-L-12-v2" (~34MB, best cross-encoder, default)
1850
+ - "ms-marco-MiniLM-L-12-v2" (~34MB, best cross-encoder)
1792
1851
  - "rank-T5-flan" (~110MB, best non cross-encoder)
1793
1852
  - "ms-marco-MultiBERT-L-12" (~150MB, multilingual 100+ languages)
1794
- - "ce-esci-MiniLM-L12-v2" (e-commerce optimized, Amazon ESCI)
1795
- - "miniReranker_arabic_v1" (Arabic language)
1796
1853
  """
1797
1854
 
1798
1855
  model_config = ConfigDict(use_enum_values=True, extra="forbid")
1799
1856
 
1800
- model: str = Field(
1801
- default="ms-marco-MiniLM-L-12-v2",
1802
- description="FlashRank model name. Default provides good balance of speed and accuracy.",
1857
+ model: Optional[str] = Field(
1858
+ default=None,
1859
+ description="FlashRank model name. If None, FlashRank is not used (use columns for Databricks reranking).",
1803
1860
  )
1804
1861
  top_n: Optional[int] = Field(
1805
1862
  default=None,
@@ -1812,6 +1869,289 @@ class RerankParametersModel(BaseModel):
1812
1869
  columns: Optional[list[str]] = Field(
1813
1870
  default_factory=list, description="Columns to rerank using DatabricksReranker"
1814
1871
  )
1872
+ instruction_aware: Optional[InstructionAwareRerankModel] = Field(
1873
+ default=None,
1874
+ description="Optional LLM-based reranking stage after FlashRank",
1875
+ )
1876
+
1877
+
1878
+ class FilterItem(BaseModel):
1879
+ """A metadata filter for vector search.
1880
+
1881
+ Filters constrain search results by matching column values.
1882
+ Use column names from the provided schema description.
1883
+ """
1884
+
1885
+ model_config = ConfigDict(extra="forbid")
1886
+ key: str = Field(
1887
+ description=(
1888
+ "Column name with optional operator suffix. "
1889
+ "Operators: (none) for equality, NOT for exclusion, "
1890
+ "< <= > >= for numeric comparison, "
1891
+ "LIKE for token match, NOT LIKE to exclude tokens."
1892
+ )
1893
+ )
1894
+ value: Union[str, int, float, bool, list[Union[str, int, float, bool]]] = Field(
1895
+ description=(
1896
+ "The filter value matching the column type. "
1897
+ "Use an array for IN-style matching multiple values."
1898
+ )
1899
+ )
1900
+
1901
+
1902
+ class SearchQuery(BaseModel):
1903
+ """A single search query with optional metadata filters.
1904
+
1905
+ Represents one focused search intent extracted from the user's request.
1906
+ The text should be a natural language query optimized for semantic search.
1907
+ Filters constrain results to match specific metadata values.
1908
+ """
1909
+
1910
+ model_config = ConfigDict(extra="forbid")
1911
+ text: str = Field(
1912
+ description=(
1913
+ "Natural language search query text optimized for semantic similarity. "
1914
+ "Should be focused on a single search intent. "
1915
+ "Do NOT include filter criteria in the text; use the filters field instead."
1916
+ )
1917
+ )
1918
+ filters: Optional[list[FilterItem]] = Field(
1919
+ default=None,
1920
+ description=(
1921
+ "Metadata filters to constrain search results. "
1922
+ "Set to null if no filters apply. "
1923
+ "Extract filter values from explicit constraints in the user query."
1924
+ ),
1925
+ )
1926
+
1927
+
1928
+ class DecomposedQueries(BaseModel):
1929
+ """Decomposed search queries extracted from a user request.
1930
+
1931
+ Break down complex user queries into multiple focused search queries.
1932
+ Each query targets a distinct search intent with appropriate filters.
1933
+ Generate 1-3 queries depending on the complexity of the user request.
1934
+ """
1935
+
1936
+ model_config = ConfigDict(extra="forbid")
1937
+ queries: list[SearchQuery] = Field(
1938
+ description=(
1939
+ "List of search queries extracted from the user request. "
1940
+ "Each query should target a distinct search intent. "
1941
+ "Order queries by importance, with the most relevant first."
1942
+ )
1943
+ )
1944
+
1945
+
1946
+ class ColumnInfo(BaseModel):
1947
+ """Column metadata for dynamic schema generation in structured output.
1948
+
1949
+ When provided, column information is embedded directly into the JSON schema
1950
+ that with_structured_output sends to the LLM, improving filter accuracy.
1951
+ """
1952
+
1953
+ model_config = ConfigDict(extra="forbid")
1954
+
1955
+ name: str = Field(description="Column name as it appears in the database")
1956
+ type: Literal["string", "number", "boolean", "datetime"] = Field(
1957
+ default="string",
1958
+ description="Column data type for value validation",
1959
+ )
1960
+ operators: list[str] = Field(
1961
+ default=["", "NOT", "<", "<=", ">", ">=", "LIKE", "NOT LIKE"],
1962
+ description="Valid filter operators for this column",
1963
+ )
1964
+
1965
+
1966
+ class InstructedRetrieverModel(BaseModel):
1967
+ """
1968
+ Configuration for instructed retrieval with query decomposition and RRF merging.
1969
+
1970
+ Instructed retrieval decomposes user queries into multiple subqueries with
1971
+ metadata filters, executes them in parallel, and merges results using
1972
+ Reciprocal Rank Fusion (RRF) before reranking.
1973
+
1974
+ Example:
1975
+ ```yaml
1976
+ retriever:
1977
+ vector_store: *products_vector_store
1978
+ instructed:
1979
+ decomposition_model: *fast_llm
1980
+ schema_description: |
1981
+ Products table: product_id, brand_name, category, price, updated_at
1982
+ Filter operators: {"col": val}, {"col >": val}, {"col NOT": val}
1983
+ columns:
1984
+ - name: brand_name
1985
+ type: string
1986
+ - name: price
1987
+ type: number
1988
+ operators: ["", "<", "<=", ">", ">="]
1989
+ constraints:
1990
+ - "Prefer recent products"
1991
+ max_subqueries: 3
1992
+ examples:
1993
+ - query: "cheap drills"
1994
+ filters: {"price <": 100}
1995
+ ```
1996
+ """
1997
+
1998
+ model_config = ConfigDict(use_enum_values=True, extra="forbid")
1999
+
2000
+ decomposition_model: Optional["LLMModel"] = Field(
2001
+ default=None,
2002
+ description="LLM for query decomposition (smaller/faster model recommended)",
2003
+ )
2004
+ schema_description: str = Field(
2005
+ description="Column names, types, and valid filter syntax for the LLM"
2006
+ )
2007
+ columns: Optional[list[ColumnInfo]] = Field(
2008
+ default=None,
2009
+ description=(
2010
+ "Structured column info for dynamic schema generation. "
2011
+ "When provided, column names are embedded in the JSON schema for better LLM accuracy."
2012
+ ),
2013
+ )
2014
+ constraints: Optional[list[str]] = Field(
2015
+ default=None, description="Default constraints to always apply"
2016
+ )
2017
+ max_subqueries: int = Field(
2018
+ default=3, description="Maximum number of parallel subqueries"
2019
+ )
2020
+ rrf_k: int = Field(
2021
+ default=60,
2022
+ description="RRF constant (lower values weight top ranks more heavily)",
2023
+ )
2024
+ examples: Optional[list[dict[str, Any]]] = Field(
2025
+ default=None,
2026
+ description="Few-shot examples for domain-specific filter translation",
2027
+ )
2028
+ normalize_filter_case: Optional[Literal["uppercase", "lowercase"]] = Field(
2029
+ default=None,
2030
+ description="Auto-normalize filter string values to uppercase or lowercase",
2031
+ )
2032
+
2033
+
2034
+ class RouterModel(BaseModel):
2035
+ """
2036
+ Select internal execution mode based on query characteristics.
2037
+
2038
+ Use fast models (GPT-3.5, Haiku, Llama 3 8B) to minimize latency (~50-100ms).
2039
+ Routes to internal modes within the same retriever, not external retrievers.
2040
+ Cross-index routing belongs at the agent/tool-selection level.
2041
+
2042
+ Execution Modes:
2043
+ - "standard": Single similarity_search() for simple keyword/product searches
2044
+ - "instructed": Decompose -> Parallel Search -> RRF for constrained queries
2045
+
2046
+ Example:
2047
+ ```yaml
2048
+ retriever:
2049
+ router:
2050
+ model: *fast_llm
2051
+ default_mode: standard
2052
+ auto_bypass: true
2053
+ ```
2054
+ """
2055
+
2056
+ model_config = ConfigDict(use_enum_values=True, extra="forbid")
2057
+
2058
+ model: Optional["LLMModel"] = Field(
2059
+ default=None,
2060
+ description="LLM for routing decision (fast model recommended)",
2061
+ )
2062
+ default_mode: Literal["standard", "instructed"] = Field(
2063
+ default="standard",
2064
+ description="Fallback mode if routing fails",
2065
+ )
2066
+ auto_bypass: bool = Field(
2067
+ default=True,
2068
+ description="Skip Instruction Reranker and Verifier for standard mode",
2069
+ )
2070
+
2071
+
2072
+ class VerificationResult(BaseModel):
2073
+ """Verification of whether search results satisfy the user's constraints.
2074
+
2075
+ Analyze the retrieved results against the original query and any explicit
2076
+ constraints to determine if a retry with modified filters is needed.
2077
+ """
2078
+
2079
+ model_config = ConfigDict(extra="forbid")
2080
+
2081
+ passed: bool = Field(
2082
+ description="True if results satisfy the user's query intent and constraints."
2083
+ )
2084
+ confidence: float = Field(
2085
+ ge=0.0,
2086
+ le=1.0,
2087
+ description="Confidence in the verification decision, from 0.0 (uncertain) to 1.0 (certain).",
2088
+ )
2089
+ feedback: Optional[str] = Field(
2090
+ default=None,
2091
+ description="Explanation of why verification passed or failed. Include specific issues found.",
2092
+ )
2093
+ suggested_filter_relaxation: Optional[dict[str, Any]] = Field(
2094
+ default=None,
2095
+ description=(
2096
+ "Suggested filter modifications for retry. "
2097
+ "Keys are column names, values indicate changes (e.g., 'REMOVE', 'WIDEN', or new values)."
2098
+ ),
2099
+ )
2100
+ unmet_constraints: Optional[list[str]] = Field(
2101
+ default=None,
2102
+ description="List of user constraints that the results failed to satisfy.",
2103
+ )
2104
+
2105
+
2106
+ class VerifierModel(BaseModel):
2107
+ """
2108
+ Validate results against user constraints with structured feedback.
2109
+
2110
+ Use fast models (GPT-3.5, Haiku, Llama 3 8B) to minimize latency (~50-100ms).
2111
+ Skipped for 'standard' mode when auto_bypass=true in router config.
2112
+ Returns structured feedback for intelligent retry, not blind retry.
2113
+
2114
+ Example:
2115
+ ```yaml
2116
+ retriever:
2117
+ verifier:
2118
+ model: *fast_llm
2119
+ on_failure: warn_and_retry
2120
+ max_retries: 1
2121
+ ```
2122
+ """
2123
+
2124
+ model_config = ConfigDict(use_enum_values=True, extra="forbid")
2125
+
2126
+ model: Optional["LLMModel"] = Field(
2127
+ default=None,
2128
+ description="LLM for verification (fast model recommended)",
2129
+ )
2130
+ on_failure: Literal["warn", "retry", "warn_and_retry"] = Field(
2131
+ default="warn",
2132
+ description="Behavior when verification fails",
2133
+ )
2134
+ max_retries: int = Field(
2135
+ default=1,
2136
+ description="Maximum retry attempts before returning with warning",
2137
+ )
2138
+
2139
+
2140
+ class RankedDocument(BaseModel):
2141
+ """Single ranked document."""
2142
+
2143
+ index: int = Field(description="Document index from input list")
2144
+ score: float = Field(description="0.0-1.0 relevance score")
2145
+ reason: str = Field(default="", description="Why this score")
2146
+
2147
+
2148
+ class RankingResult(BaseModel):
2149
+ """Reranking output."""
2150
+
2151
+ rankings: list[RankedDocument] = Field(
2152
+ default_factory=list,
2153
+ description="Ranked documents, highest score first",
2154
+ )
1815
2155
 
1816
2156
 
1817
2157
  class RetrieverModel(BaseModel):
@@ -1821,10 +2161,22 @@ class RetrieverModel(BaseModel):
1821
2161
  search_parameters: SearchParametersModel = Field(
1822
2162
  default_factory=SearchParametersModel
1823
2163
  )
2164
+ router: Optional[RouterModel] = Field(
2165
+ default=None,
2166
+ description="Optional query router for selecting execution mode (standard vs instructed).",
2167
+ )
1824
2168
  rerank: Optional[RerankParametersModel | bool] = Field(
1825
2169
  default=None,
1826
2170
  description="Optional reranking configuration. Set to true for defaults, or provide ReRankParametersModel for custom settings.",
1827
2171
  )
2172
+ instructed: Optional[InstructedRetrieverModel] = Field(
2173
+ default=None,
2174
+ description="Optional instructed retrieval with query decomposition and RRF merging.",
2175
+ )
2176
+ verifier: Optional[VerifierModel] = Field(
2177
+ default=None,
2178
+ description="Optional result verification with structured feedback for retry.",
2179
+ )
1828
2180
 
1829
2181
  @model_validator(mode="after")
1830
2182
  def set_default_columns(self) -> Self:
@@ -1835,9 +2187,13 @@ class RetrieverModel(BaseModel):
1835
2187
 
1836
2188
  @model_validator(mode="after")
1837
2189
  def set_default_reranker(self) -> Self:
1838
- """Convert bool to ReRankParametersModel with defaults."""
2190
+ """Convert bool to ReRankParametersModel with defaults.
2191
+
2192
+ When rerank: true is used, sets the default FlashRank model
2193
+ (ms-marco-MiniLM-L-12-v2) to enable reranking.
2194
+ """
1839
2195
  if isinstance(self.rerank, bool) and self.rerank:
1840
- self.rerank = RerankParametersModel()
2196
+ self.rerank = RerankParametersModel(model="ms-marco-MiniLM-L-12-v2")
1841
2197
  return self
1842
2198
 
1843
2199
 
@@ -2985,8 +3341,24 @@ class GuidelineModel(BaseModel):
2985
3341
 
2986
3342
 
2987
3343
  class EvaluationModel(BaseModel):
3344
+ """
3345
+ Configuration for MLflow GenAI evaluation.
3346
+
3347
+ Attributes:
3348
+ model: LLM model used as the judge for LLM-based scorers (e.g., Guidelines, Safety).
3349
+ This model evaluates agent responses during evaluation.
3350
+ table: Table to store evaluation results.
3351
+ num_evals: Number of evaluation samples to generate.
3352
+ agent_description: Description of the agent for evaluation data generation.
3353
+ question_guidelines: Guidelines for generating evaluation questions.
3354
+ custom_inputs: Custom inputs to pass to the agent during evaluation.
3355
+ guidelines: List of guideline configurations for Guidelines scorers.
3356
+ """
3357
+
2988
3358
  model_config = ConfigDict(use_enum_values=True, extra="forbid")
2989
- model: LLMModel
3359
+ model: LLMModel = Field(
3360
+ ..., description="LLM model used as the judge for LLM-based evaluation scorers"
3361
+ )
2990
3362
  table: TableModel
2991
3363
  num_evals: int
2992
3364
  agent_description: Optional[str] = None
@@ -2994,6 +3366,16 @@ class EvaluationModel(BaseModel):
2994
3366
  custom_inputs: dict[str, Any] = Field(default_factory=dict)
2995
3367
  guidelines: list[GuidelineModel] = Field(default_factory=list)
2996
3368
 
3369
+ @property
3370
+ def judge_model_endpoint(self) -> str:
3371
+ """
3372
+ Get the judge model endpoint string for MLflow scorers.
3373
+
3374
+ Returns:
3375
+ Endpoint string in format 'databricks:/model-name'
3376
+ """
3377
+ return f"databricks:/{self.model.name}"
3378
+
2997
3379
 
2998
3380
  class EvaluationDatasetExpectationsModel(BaseModel):
2999
3381
  model_config = ConfigDict(use_enum_values=True, extra="forbid")