dao-ai 0.1.16__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 +12 -4
- dao_ai/config.py +471 -44
- dao_ai/evaluation.py +543 -0
- dao_ai/memory/postgres.py +146 -35
- dao_ai/orchestration/core.py +33 -9
- dao_ai/orchestration/supervisor.py +23 -8
- dao_ai/orchestration/swarm.py +6 -1
- dao_ai/{prompts.py → prompts/__init__.py} +10 -1
- dao_ai/prompts/instructed_retriever_decomposition.yaml +58 -0
- dao_ai/prompts/instruction_reranker.yaml +14 -0
- dao_ai/prompts/router.yaml +37 -0
- dao_ai/prompts/verifier.yaml +46 -0
- dao_ai/providers/databricks.py +33 -12
- dao_ai/tools/instructed_retriever.py +366 -0
- dao_ai/tools/instruction_reranker.py +202 -0
- dao_ai/tools/mcp.py +21 -10
- dao_ai/tools/router.py +89 -0
- dao_ai/tools/vector_search.py +441 -134
- dao_ai/tools/verifier.py +159 -0
- dao_ai/utils.py +182 -2
- dao_ai/vector_search.py +9 -1
- {dao_ai-0.1.16.dist-info → dao_ai-0.1.18.dist-info}/METADATA +3 -3
- {dao_ai-0.1.16.dist-info → dao_ai-0.1.18.dist-info}/RECORD +26 -17
- {dao_ai-0.1.16.dist-info → dao_ai-0.1.18.dist-info}/WHEEL +0 -0
- {dao_ai-0.1.16.dist-info → dao_ai-0.1.18.dist-info}/entry_points.txt +0 -0
- {dao_ai-0.1.16.dist-info → dao_ai-0.1.18.dist-info}/licenses/LICENSE +0 -0
dao_ai/config.py
CHANGED
|
@@ -373,15 +373,19 @@ class IsDatabricksResource(ABC, BaseModel):
|
|
|
373
373
|
"""
|
|
374
374
|
from dao_ai.utils import normalize_host
|
|
375
375
|
|
|
376
|
-
logger.trace(
|
|
376
|
+
logger.trace(
|
|
377
|
+
"workspace_client_from called",
|
|
378
|
+
context=context,
|
|
379
|
+
on_behalf_of_user=self.on_behalf_of_user,
|
|
380
|
+
)
|
|
377
381
|
|
|
378
382
|
# Check if we have headers in context for OBO
|
|
379
383
|
if context and context.headers and self.on_behalf_of_user:
|
|
380
384
|
headers = context.headers
|
|
381
385
|
# Try both lowercase and title-case header names (HTTP headers are case-insensitive)
|
|
382
|
-
forwarded_token: str = headers.get(
|
|
383
|
-
"
|
|
384
|
-
)
|
|
386
|
+
forwarded_token: str = headers.get(
|
|
387
|
+
"x-forwarded-access-token"
|
|
388
|
+
) or headers.get("X-Forwarded-Access-Token")
|
|
385
389
|
|
|
386
390
|
if forwarded_token:
|
|
387
391
|
forwarded_user = headers.get("x-forwarded-user") or headers.get(
|
|
@@ -1398,13 +1402,20 @@ class DatabaseModel(IsDatabricksResource):
|
|
|
1398
1402
|
- Databricks Lakebase: Provide `instance_name` (authentication optional, supports ambient auth)
|
|
1399
1403
|
- Standard PostgreSQL: Provide `host` (authentication required via user/password)
|
|
1400
1404
|
|
|
1401
|
-
Note:
|
|
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
|
+
```
|
|
1402
1414
|
|
|
1403
1415
|
Example Databricks Lakebase with Service Principal:
|
|
1404
1416
|
```yaml
|
|
1405
1417
|
databases:
|
|
1406
1418
|
my_lakebase:
|
|
1407
|
-
name: my-database
|
|
1408
1419
|
instance_name: my-lakebase-instance
|
|
1409
1420
|
service_principal:
|
|
1410
1421
|
client_id:
|
|
@@ -1420,7 +1431,6 @@ class DatabaseModel(IsDatabricksResource):
|
|
|
1420
1431
|
```yaml
|
|
1421
1432
|
databases:
|
|
1422
1433
|
my_lakebase:
|
|
1423
|
-
name: my-database
|
|
1424
1434
|
instance_name: my-lakebase-instance
|
|
1425
1435
|
on_behalf_of_user: true
|
|
1426
1436
|
```
|
|
@@ -1440,7 +1450,7 @@ class DatabaseModel(IsDatabricksResource):
|
|
|
1440
1450
|
"""
|
|
1441
1451
|
|
|
1442
1452
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
1443
|
-
name: str
|
|
1453
|
+
name: Optional[str] = None
|
|
1444
1454
|
instance_name: Optional[str] = None
|
|
1445
1455
|
description: Optional[str] = None
|
|
1446
1456
|
host: Optional[AnyVariable] = None
|
|
@@ -1489,6 +1499,17 @@ class DatabaseModel(IsDatabricksResource):
|
|
|
1489
1499
|
)
|
|
1490
1500
|
return self
|
|
1491
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
|
+
|
|
1492
1513
|
@model_validator(mode="after")
|
|
1493
1514
|
def update_user(self) -> Self:
|
|
1494
1515
|
# Skip if using OBO (passive auth), explicit credentials, or explicit user
|
|
@@ -1586,10 +1607,10 @@ class DatabaseModel(IsDatabricksResource):
|
|
|
1586
1607
|
username: str | None = None
|
|
1587
1608
|
password_value: str | None = None
|
|
1588
1609
|
|
|
1589
|
-
# Resolve host -
|
|
1610
|
+
# Resolve host - fetch from API at runtime for Lakebase if not provided
|
|
1590
1611
|
host_value: Any = self.host
|
|
1591
|
-
if host_value is None and self.is_lakebase
|
|
1592
|
-
# Fetch host
|
|
1612
|
+
if host_value is None and self.is_lakebase:
|
|
1613
|
+
# Fetch host from Lakebase instance API
|
|
1593
1614
|
existing_instance: DatabaseInstance = (
|
|
1594
1615
|
self.workspace_client.database.get_database_instance(
|
|
1595
1616
|
name=self.instance_name
|
|
@@ -1759,43 +1780,83 @@ class SearchParametersModel(BaseModel):
|
|
|
1759
1780
|
query_type: Optional[str] = "ANN"
|
|
1760
1781
|
|
|
1761
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
|
+
|
|
1762
1820
|
class RerankParametersModel(BaseModel):
|
|
1763
1821
|
"""
|
|
1764
|
-
Configuration for reranking retrieved documents
|
|
1822
|
+
Configuration for reranking retrieved documents.
|
|
1765
1823
|
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
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`
|
|
1769
1828
|
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
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
|
+
```
|
|
1774
1840
|
|
|
1775
|
-
Example:
|
|
1841
|
+
Example with FlashRank:
|
|
1776
1842
|
```yaml
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
rerank:
|
|
1781
|
-
model: ms-marco-MiniLM-L-12-v2
|
|
1782
|
-
top_n: 5 # Return top 5 after reranking
|
|
1843
|
+
rerank:
|
|
1844
|
+
model: ms-marco-MiniLM-L-12-v2 # FlashRank model
|
|
1845
|
+
top_n: 10
|
|
1783
1846
|
```
|
|
1784
1847
|
|
|
1785
|
-
Available models (see https://github.com/PrithivirajDamodaran/FlashRank):
|
|
1848
|
+
Available FlashRank models (see https://github.com/PrithivirajDamodaran/FlashRank):
|
|
1786
1849
|
- "ms-marco-TinyBERT-L-2-v2" (~4MB, fastest)
|
|
1787
|
-
- "ms-marco-MiniLM-L-12-v2" (~34MB, best cross-encoder
|
|
1850
|
+
- "ms-marco-MiniLM-L-12-v2" (~34MB, best cross-encoder)
|
|
1788
1851
|
- "rank-T5-flan" (~110MB, best non cross-encoder)
|
|
1789
1852
|
- "ms-marco-MultiBERT-L-12" (~150MB, multilingual 100+ languages)
|
|
1790
|
-
- "ce-esci-MiniLM-L12-v2" (e-commerce optimized, Amazon ESCI)
|
|
1791
|
-
- "miniReranker_arabic_v1" (Arabic language)
|
|
1792
1853
|
"""
|
|
1793
1854
|
|
|
1794
1855
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
1795
1856
|
|
|
1796
|
-
model: str = Field(
|
|
1797
|
-
default=
|
|
1798
|
-
description="FlashRank model name.
|
|
1857
|
+
model: Optional[str] = Field(
|
|
1858
|
+
default=None,
|
|
1859
|
+
description="FlashRank model name. If None, FlashRank is not used (use columns for Databricks reranking).",
|
|
1799
1860
|
)
|
|
1800
1861
|
top_n: Optional[int] = Field(
|
|
1801
1862
|
default=None,
|
|
@@ -1808,6 +1869,289 @@ class RerankParametersModel(BaseModel):
|
|
|
1808
1869
|
columns: Optional[list[str]] = Field(
|
|
1809
1870
|
default_factory=list, description="Columns to rerank using DatabricksReranker"
|
|
1810
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
|
+
)
|
|
1811
2155
|
|
|
1812
2156
|
|
|
1813
2157
|
class RetrieverModel(BaseModel):
|
|
@@ -1817,10 +2161,22 @@ class RetrieverModel(BaseModel):
|
|
|
1817
2161
|
search_parameters: SearchParametersModel = Field(
|
|
1818
2162
|
default_factory=SearchParametersModel
|
|
1819
2163
|
)
|
|
2164
|
+
router: Optional[RouterModel] = Field(
|
|
2165
|
+
default=None,
|
|
2166
|
+
description="Optional query router for selecting execution mode (standard vs instructed).",
|
|
2167
|
+
)
|
|
1820
2168
|
rerank: Optional[RerankParametersModel | bool] = Field(
|
|
1821
2169
|
default=None,
|
|
1822
2170
|
description="Optional reranking configuration. Set to true for defaults, or provide ReRankParametersModel for custom settings.",
|
|
1823
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
|
+
)
|
|
1824
2180
|
|
|
1825
2181
|
@model_validator(mode="after")
|
|
1826
2182
|
def set_default_columns(self) -> Self:
|
|
@@ -1831,9 +2187,13 @@ class RetrieverModel(BaseModel):
|
|
|
1831
2187
|
|
|
1832
2188
|
@model_validator(mode="after")
|
|
1833
2189
|
def set_default_reranker(self) -> Self:
|
|
1834
|
-
"""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
|
+
"""
|
|
1835
2195
|
if isinstance(self.rerank, bool) and self.rerank:
|
|
1836
|
-
self.rerank = RerankParametersModel()
|
|
2196
|
+
self.rerank = RerankParametersModel(model="ms-marco-MiniLM-L-12-v2")
|
|
1837
2197
|
return self
|
|
1838
2198
|
|
|
1839
2199
|
|
|
@@ -2091,9 +2451,47 @@ class McpFunctionModel(BaseFunctionModel, IsDatabricksResource):
|
|
|
2091
2451
|
return f"{workspace_host}/api/2.0/mcp/sql"
|
|
2092
2452
|
|
|
2093
2453
|
# Databricks App - MCP endpoint is at {app_url}/mcp
|
|
2454
|
+
# Try McpFunctionModel's workspace_client first (which may have credentials),
|
|
2455
|
+
# then fall back to DatabricksAppModel.url property (which uses its own workspace_client)
|
|
2094
2456
|
if self.app:
|
|
2095
|
-
|
|
2096
|
-
|
|
2457
|
+
from databricks.sdk.service.apps import App
|
|
2458
|
+
|
|
2459
|
+
app_url: str | None = None
|
|
2460
|
+
|
|
2461
|
+
# First, try using McpFunctionModel's workspace_client
|
|
2462
|
+
try:
|
|
2463
|
+
app: App = self.workspace_client.apps.get(self.app.name)
|
|
2464
|
+
app_url = app.url
|
|
2465
|
+
logger.trace(
|
|
2466
|
+
"Got app URL using McpFunctionModel workspace_client",
|
|
2467
|
+
app_name=self.app.name,
|
|
2468
|
+
url=app_url,
|
|
2469
|
+
)
|
|
2470
|
+
except Exception as e:
|
|
2471
|
+
logger.debug(
|
|
2472
|
+
"Failed to get app URL using McpFunctionModel workspace_client, "
|
|
2473
|
+
"trying DatabricksAppModel.url property",
|
|
2474
|
+
app_name=self.app.name,
|
|
2475
|
+
error=str(e),
|
|
2476
|
+
)
|
|
2477
|
+
|
|
2478
|
+
# Fall back to DatabricksAppModel.url property
|
|
2479
|
+
if not app_url:
|
|
2480
|
+
try:
|
|
2481
|
+
app_url = self.app.url
|
|
2482
|
+
logger.trace(
|
|
2483
|
+
"Got app URL using DatabricksAppModel.url property",
|
|
2484
|
+
app_name=self.app.name,
|
|
2485
|
+
url=app_url,
|
|
2486
|
+
)
|
|
2487
|
+
except Exception as e:
|
|
2488
|
+
raise RuntimeError(
|
|
2489
|
+
f"Databricks App '{self.app.name}' does not have a URL. "
|
|
2490
|
+
"The app may not be deployed yet, or credentials may be invalid. "
|
|
2491
|
+
f"Error: {e}"
|
|
2492
|
+
) from e
|
|
2493
|
+
|
|
2494
|
+
return f"{app_url.rstrip('/')}/mcp"
|
|
2097
2495
|
|
|
2098
2496
|
# Vector Search
|
|
2099
2497
|
if self.vector_search:
|
|
@@ -2608,7 +3006,6 @@ class SupervisorModel(BaseModel):
|
|
|
2608
3006
|
|
|
2609
3007
|
class SwarmModel(BaseModel):
|
|
2610
3008
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
2611
|
-
model: LLMModel
|
|
2612
3009
|
default_agent: Optional[AgentModel | str] = None
|
|
2613
3010
|
middleware: list[MiddlewareModel] = Field(
|
|
2614
3011
|
default_factory=list,
|
|
@@ -2622,11 +3019,17 @@ class SwarmModel(BaseModel):
|
|
|
2622
3019
|
class OrchestrationModel(BaseModel):
|
|
2623
3020
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
2624
3021
|
supervisor: Optional[SupervisorModel] = None
|
|
2625
|
-
swarm: Optional[SwarmModel] = None
|
|
3022
|
+
swarm: Optional[SwarmModel | Literal[True]] = None
|
|
2626
3023
|
memory: Optional[MemoryModel] = None
|
|
2627
3024
|
|
|
2628
3025
|
@model_validator(mode="after")
|
|
2629
|
-
def
|
|
3026
|
+
def validate_and_normalize(self) -> Self:
|
|
3027
|
+
"""Validate orchestration and normalize swarm shorthand."""
|
|
3028
|
+
# Convert swarm: true to SwarmModel()
|
|
3029
|
+
if self.swarm is True:
|
|
3030
|
+
self.swarm = SwarmModel()
|
|
3031
|
+
|
|
3032
|
+
# Validate mutually exclusive
|
|
2630
3033
|
if self.supervisor is not None and self.swarm is not None:
|
|
2631
3034
|
raise ValueError("Cannot specify both supervisor and swarm")
|
|
2632
3035
|
if self.supervisor is None and self.swarm is None:
|
|
@@ -2898,9 +3301,7 @@ class AppModel(BaseModel):
|
|
|
2898
3301
|
elif len(self.agents) == 1:
|
|
2899
3302
|
default_agent: AgentModel = self.agents[0]
|
|
2900
3303
|
self.orchestration = OrchestrationModel(
|
|
2901
|
-
swarm=SwarmModel(
|
|
2902
|
-
model=default_agent.model, default_agent=default_agent
|
|
2903
|
-
)
|
|
3304
|
+
swarm=SwarmModel(default_agent=default_agent)
|
|
2904
3305
|
)
|
|
2905
3306
|
else:
|
|
2906
3307
|
raise ValueError("At least one agent must be specified")
|
|
@@ -2940,8 +3341,24 @@ class GuidelineModel(BaseModel):
|
|
|
2940
3341
|
|
|
2941
3342
|
|
|
2942
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
|
+
|
|
2943
3358
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
|
2944
|
-
model: LLMModel
|
|
3359
|
+
model: LLMModel = Field(
|
|
3360
|
+
..., description="LLM model used as the judge for LLM-based evaluation scorers"
|
|
3361
|
+
)
|
|
2945
3362
|
table: TableModel
|
|
2946
3363
|
num_evals: int
|
|
2947
3364
|
agent_description: Optional[str] = None
|
|
@@ -2949,6 +3366,16 @@ class EvaluationModel(BaseModel):
|
|
|
2949
3366
|
custom_inputs: dict[str, Any] = Field(default_factory=dict)
|
|
2950
3367
|
guidelines: list[GuidelineModel] = Field(default_factory=list)
|
|
2951
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
|
+
|
|
2952
3379
|
|
|
2953
3380
|
class EvaluationDatasetExpectationsModel(BaseModel):
|
|
2954
3381
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|