unique_toolkit 2026.24.0.dev3__py3-none-any.whl → 2026.24.0.dev5__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.
- unique_toolkit/_common/metadata_filter_scope.py +7 -4
- unique_toolkit/_common/pydantic/rjsf_tags.py +6 -1
- unique_toolkit/content/smart_rules.py +61 -3
- unique_toolkit/experimental/components/internal_search/knowledge_base/config.py +27 -4
- unique_toolkit/experimental/components/internal_search/knowledge_base/service.py +5 -1
- unique_toolkit/smart_rules/compile.py +6 -0
- {unique_toolkit-2026.24.0.dev3.dist-info → unique_toolkit-2026.24.0.dev5.dist-info}/METADATA +1 -1
- {unique_toolkit-2026.24.0.dev3.dist-info → unique_toolkit-2026.24.0.dev5.dist-info}/RECORD +9 -9
- {unique_toolkit-2026.24.0.dev3.dist-info → unique_toolkit-2026.24.0.dev5.dist-info}/WHEEL +0 -0
|
@@ -8,6 +8,8 @@ from typing import Any
|
|
|
8
8
|
from unique_toolkit.content.smart_rules import (
|
|
9
9
|
Operator,
|
|
10
10
|
Statement,
|
|
11
|
+
UniqueQL,
|
|
12
|
+
uniqueql_to_dict,
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
_UQL_AND = "and"
|
|
@@ -29,16 +31,17 @@ def build_folder_id_in_clause(scope_ids: list[str]) -> dict[str, Any]:
|
|
|
29
31
|
|
|
30
32
|
def merge_scope_clause_into_metadata_filter(
|
|
31
33
|
scope_clause: Mapping[str, Any],
|
|
32
|
-
metadata_filter:
|
|
34
|
+
metadata_filter: UniqueQL | Mapping[str, Any] | None,
|
|
33
35
|
) -> dict[str, Any]:
|
|
34
36
|
"""AND-merge *scope_clause* with *metadata_filter*, flattening a top-level ``and``."""
|
|
35
37
|
scope_dict = dict(scope_clause)
|
|
36
|
-
|
|
38
|
+
metadata_dict = uniqueql_to_dict(metadata_filter)
|
|
39
|
+
if not metadata_dict:
|
|
37
40
|
return scope_dict
|
|
38
|
-
inner_and =
|
|
41
|
+
inner_and = metadata_dict.get(_UQL_AND)
|
|
39
42
|
if isinstance(inner_and, list):
|
|
40
43
|
return {_UQL_AND: [scope_dict, *inner_and]}
|
|
41
|
-
return {_UQL_AND: [scope_dict,
|
|
44
|
+
return {_UQL_AND: [scope_dict, metadata_dict]}
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
__all__ = [
|
|
@@ -974,7 +974,12 @@ def ui_schema_for_model(
|
|
|
974
974
|
node["items"] = item_node
|
|
975
975
|
|
|
976
976
|
# Dict -> additionalProperties (value side)
|
|
977
|
-
|
|
977
|
+
# Skip when the node already carries a ui:widget (fully-specified
|
|
978
|
+
# widget) or an anyOf (fully-specified type branching): walking the
|
|
979
|
+
# dict value type in those cases adds a spurious "additionalProperties"
|
|
980
|
+
# node that confuses RJSF renderers. Presentation-only meta such as
|
|
981
|
+
# ui:expandable or ui:title does NOT prevent the walk.
|
|
982
|
+
elif origin is dict and "ui:widget" not in node and "anyOf" not in node:
|
|
978
983
|
_key_t, val_t = get_args(base) or (Any, Any)
|
|
979
984
|
val_base, val_meta = _walk_annotated_chain(val_t)
|
|
980
985
|
val_base = _unwrap_optional(val_base)
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import re
|
|
3
|
+
from collections.abc import Mapping
|
|
2
4
|
from datetime import datetime, timedelta, timezone
|
|
3
5
|
from enum import Enum
|
|
4
|
-
from typing import Any, Dict, List,
|
|
6
|
+
from typing import Annotated, Any, Dict, List, Self, Union
|
|
5
7
|
|
|
6
|
-
from pydantic import AliasChoices, BaseModel, Field
|
|
8
|
+
from pydantic import AliasChoices, BaseModel, BeforeValidator, Field, PlainSerializer
|
|
7
9
|
from pydantic.config import ConfigDict
|
|
10
|
+
from pydantic.json_schema import WithJsonSchema
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
class Operator(str, Enum):
|
|
@@ -54,6 +57,10 @@ class BaseStatement(BaseModel):
|
|
|
54
57
|
) -> Self:
|
|
55
58
|
return self.model_copy()
|
|
56
59
|
|
|
60
|
+
def to_dict(self) -> dict[str, Any]:
|
|
61
|
+
"""Convert to wire-format JSON dict (uses serialization aliases)."""
|
|
62
|
+
return self.model_dump(mode="json")
|
|
63
|
+
|
|
57
64
|
|
|
58
65
|
class Statement(BaseStatement):
|
|
59
66
|
operator: Operator
|
|
@@ -285,7 +292,6 @@ def get_fallback_values(
|
|
|
285
292
|
return values
|
|
286
293
|
|
|
287
294
|
|
|
288
|
-
# Example usage:
|
|
289
295
|
def parse_uniqueql(json_data: Dict[str, Any]) -> UniqueQL:
|
|
290
296
|
if "operator" in json_data:
|
|
291
297
|
return Statement.model_validate(json_data)
|
|
@@ -299,3 +305,55 @@ def parse_uniqueql(json_data: Dict[str, Any]) -> UniqueQL:
|
|
|
299
305
|
)
|
|
300
306
|
else:
|
|
301
307
|
raise ValueError("Invalid UniqueQL format")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def parse_uniqueql_input(v: Any) -> UniqueQL | None:
|
|
311
|
+
"""Parse config/API input (JSON string, dict, or model) into a UniqueQL model."""
|
|
312
|
+
if v is None:
|
|
313
|
+
return None
|
|
314
|
+
if isinstance(v, (Statement, AndStatement, OrStatement)):
|
|
315
|
+
return v
|
|
316
|
+
if isinstance(v, str):
|
|
317
|
+
if v.strip() == "":
|
|
318
|
+
return None
|
|
319
|
+
try:
|
|
320
|
+
v = json.loads(v)
|
|
321
|
+
except json.JSONDecodeError as e:
|
|
322
|
+
raise ValueError(f"Invalid JSON: {e}") from e
|
|
323
|
+
if isinstance(v, dict):
|
|
324
|
+
return parse_uniqueql(v)
|
|
325
|
+
raise ValueError(f"Expected JSON string or dict, got {type(v).__name__}")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def uniqueql_to_dict(
|
|
329
|
+
value: UniqueQL | Mapping[str, Any] | None,
|
|
330
|
+
) -> dict[str, Any] | None:
|
|
331
|
+
"""Convert a UniqueQL model (or wire dict) to a JSON-serializable dict for APIs."""
|
|
332
|
+
if value is None:
|
|
333
|
+
return None
|
|
334
|
+
if isinstance(value, BaseStatement):
|
|
335
|
+
return value.to_dict()
|
|
336
|
+
return dict(value)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _serialize_uniqueql_field(value: UniqueQL | None) -> dict[str, Any] | None:
|
|
340
|
+
return None if value is None else value.to_dict()
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
_UNIQUEQL_JSON_SCHEMA = {
|
|
344
|
+
"anyOf": [
|
|
345
|
+
{"type": "string", "title": "UniqueQL (JSON)"},
|
|
346
|
+
{"type": "null", "title": "Deactivated", "description": "None"},
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
UniqueQLField = Annotated[
|
|
351
|
+
UniqueQL | None,
|
|
352
|
+
BeforeValidator(parse_uniqueql_input),
|
|
353
|
+
WithJsonSchema(_UNIQUEQL_JSON_SCHEMA),
|
|
354
|
+
PlainSerializer(
|
|
355
|
+
_serialize_uniqueql_field,
|
|
356
|
+
when_used="json",
|
|
357
|
+
return_type=dict[str, Any] | None,
|
|
358
|
+
),
|
|
359
|
+
]
|
|
@@ -6,8 +6,10 @@ from typing import Annotated, Self
|
|
|
6
6
|
from pydantic import Field, model_validator
|
|
7
7
|
|
|
8
8
|
from unique_toolkit._common.config_checker import register_config
|
|
9
|
+
from unique_toolkit._common.pydantic.rjsf_tags import RJSFMetaTag
|
|
9
10
|
from unique_toolkit._common.pydantic_helpers import DeactivatedNone
|
|
10
11
|
from unique_toolkit.content.schemas import ContentRerankerConfig
|
|
12
|
+
from unique_toolkit.content.smart_rules import UniqueQLField
|
|
11
13
|
from unique_toolkit.experimental.components.internal_search.base.config import (
|
|
12
14
|
InternalSearchConfig,
|
|
13
15
|
)
|
|
@@ -15,9 +17,11 @@ from unique_toolkit.experimental.components.internal_search.base.config import (
|
|
|
15
17
|
|
|
16
18
|
@register_config()
|
|
17
19
|
class KnowledgeBaseInternalSearchConfig(InternalSearchConfig):
|
|
18
|
-
scope_ids:
|
|
20
|
+
scope_ids: Annotated[
|
|
21
|
+
Annotated[list[str], Field(title="Scope IDs")] | DeactivatedNone,
|
|
22
|
+
RJSFMetaTag({"anyOf": [{"items": {"ui:title": "Scope ID"}}, {}]}),
|
|
23
|
+
] = Field(
|
|
19
24
|
default=None,
|
|
20
|
-
title="Active",
|
|
21
25
|
deprecated=True,
|
|
22
26
|
description=(
|
|
23
27
|
"Not accepted for new configs. Use ``metadata_filter`` with "
|
|
@@ -26,10 +30,29 @@ class KnowledgeBaseInternalSearchConfig(InternalSearchConfig):
|
|
|
26
30
|
"a ``folderId in [scope_ids]`` metadata filter at search time."
|
|
27
31
|
),
|
|
28
32
|
)
|
|
29
|
-
metadata_filter:
|
|
33
|
+
metadata_filter: Annotated[
|
|
34
|
+
UniqueQLField,
|
|
35
|
+
# Put textarea attrs on the string branch (index 0) only, not at field level.
|
|
36
|
+
# If set at field level, RJSF's MultiSchemaField applies ui:widget to every
|
|
37
|
+
# anyOf branch during option iteration, which crashes on non-string branches.
|
|
38
|
+
RJSFMetaTag(
|
|
39
|
+
{
|
|
40
|
+
"ui:options": {"customValidation": "uniqueql"},
|
|
41
|
+
"anyOf": [
|
|
42
|
+
{
|
|
43
|
+
"ui:widget": "textarea",
|
|
44
|
+
"ui:placeholder": '{"operator": "equals", "value": "...", "path": ["fieldName"]}',
|
|
45
|
+
"ui:emptyValue": "",
|
|
46
|
+
},
|
|
47
|
+
{},
|
|
48
|
+
],
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
] = Field(
|
|
30
52
|
default=None,
|
|
31
53
|
description=(
|
|
32
|
-
"Static metadata filter applied to every KB search. "
|
|
54
|
+
"Static UniqueQL metadata filter applied to every KB search. "
|
|
55
|
+
"Pass as a JSON string or a plain dict. "
|
|
33
56
|
"Overridden by chat context filter or per-invocation state override."
|
|
34
57
|
),
|
|
35
58
|
)
|
|
@@ -105,7 +105,11 @@ class KnowledgeBaseInternalSearchService(
|
|
|
105
105
|
return cast("dict[str, Any] | None", self._state.metadata_filter_override)
|
|
106
106
|
if self._context.chat is not None:
|
|
107
107
|
return self._context.chat.metadata_filter
|
|
108
|
-
return
|
|
108
|
+
return (
|
|
109
|
+
self._config.metadata_filter.to_dict()
|
|
110
|
+
if self._config.metadata_filter
|
|
111
|
+
else None
|
|
112
|
+
)
|
|
109
113
|
|
|
110
114
|
@property
|
|
111
115
|
def _effective_scope_ids(self) -> list[str] | None:
|
|
@@ -7,6 +7,7 @@ from unique_toolkit.content.smart_rules import (
|
|
|
7
7
|
OrStatement,
|
|
8
8
|
Statement,
|
|
9
9
|
UniqueQL,
|
|
10
|
+
UniqueQLField,
|
|
10
11
|
array_operator,
|
|
11
12
|
binary_operator,
|
|
12
13
|
calculate_current_date,
|
|
@@ -19,9 +20,11 @@ from unique_toolkit.content.smart_rules import (
|
|
|
19
20
|
is_array_of_strings,
|
|
20
21
|
null_operator,
|
|
21
22
|
parse_uniqueql,
|
|
23
|
+
parse_uniqueql_input,
|
|
22
24
|
replace_tool_parameters_patterns,
|
|
23
25
|
replace_user_metadata_patterns,
|
|
24
26
|
replace_variables,
|
|
27
|
+
uniqueql_to_dict,
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
warnings.warn(
|
|
@@ -38,6 +41,7 @@ __all__ = [
|
|
|
38
41
|
"OrStatement",
|
|
39
42
|
"Statement",
|
|
40
43
|
"UniqueQL",
|
|
44
|
+
"UniqueQLField",
|
|
41
45
|
"array_operator",
|
|
42
46
|
"binary_operator",
|
|
43
47
|
"calculate_current_date",
|
|
@@ -50,7 +54,9 @@ __all__ = [
|
|
|
50
54
|
"is_array_of_strings",
|
|
51
55
|
"null_operator",
|
|
52
56
|
"parse_uniqueql",
|
|
57
|
+
"parse_uniqueql_input",
|
|
53
58
|
"replace_tool_parameters_patterns",
|
|
54
59
|
"replace_user_metadata_patterns",
|
|
55
60
|
"replace_variables",
|
|
61
|
+
"uniqueql_to_dict",
|
|
56
62
|
]
|
{unique_toolkit-2026.24.0.dev3.dist-info → unique_toolkit-2026.24.0.dev5.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-toolkit
|
|
3
|
-
Version: 2026.24.0.
|
|
3
|
+
Version: 2026.24.0.dev5
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
|
|
6
6
|
Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
|
|
@@ -58,8 +58,8 @@ unique_toolkit/_common/experimental/write_up_agent/services/template_handler/ser
|
|
|
58
58
|
unique_toolkit/_common/experimental/write_up_agent/services/template_handler/utils.py,sha256=FeJhTAU7AUlSFCfFwmEzzI22PtAdFJluzsGeiEPK49Q,6393
|
|
59
59
|
unique_toolkit/_common/experimental/write_up_agent/utils.py,sha256=I_OCDXCxY6uZk_fNQg_bIXHwjnN7ND_ngcisThnGGIc,739
|
|
60
60
|
unique_toolkit/_common/feature_flags/schema.py,sha256=X32VqH4VMK7bhEfSd8Wbddl8FVs7Gh7ucuIEbmqc4Kw,268
|
|
61
|
-
unique_toolkit/_common/metadata_filter_scope.py,sha256=
|
|
62
|
-
unique_toolkit/_common/pydantic/rjsf_tags.py,sha256=
|
|
61
|
+
unique_toolkit/_common/metadata_filter_scope.py,sha256=Lj7oYrqHUoiqSBn6PA1FmeqtZONhSdQ8u3CR5YolxcM,1432
|
|
62
|
+
unique_toolkit/_common/pydantic/rjsf_tags.py,sha256=B4iMCvKlzP-xE8pp56uJ4xYNB89Vz9u3z5MXXx68CPs,39955
|
|
63
63
|
unique_toolkit/_common/pydantic_helpers.py,sha256=LTI21litYJdzRe7cbgO1ntKf06yb5V8v7hASMM3Wwa8,5839
|
|
64
64
|
unique_toolkit/_common/referencing.py,sha256=UY7-9nJLcRqlHUNytfJZPVkdpjvy5wIps_7P33_qgA0,1496
|
|
65
65
|
unique_toolkit/_common/string_utilities.py,sha256=HOAOZbH3vRmTDwUCIF0YTE04IWcVHZyq3uOmevxWXn0,4433
|
|
@@ -253,7 +253,7 @@ unique_toolkit/content/constants.py,sha256=1iy4Y67xobl5VTnJB6SxSyuoBWbdLl9244xfV
|
|
|
253
253
|
unique_toolkit/content/functions.py,sha256=Rx3N9x23JjVt7mmUlFhOhjAjkWQCzzodtpDgoIsE2xg,33932
|
|
254
254
|
unique_toolkit/content/schemas.py,sha256=z3xlTkOh9MTwHGLwcc5vfT5OmDRCiWE0F0MaiPMV4dg,10281
|
|
255
255
|
unique_toolkit/content/service.py,sha256=fR93_76aPczx2voxR7cvYDs_-pxD-Mc9WP6-5IqA8ko,28301
|
|
256
|
-
unique_toolkit/content/smart_rules.py,sha256=
|
|
256
|
+
unique_toolkit/content/smart_rules.py,sha256=9GlHXm63POTi4jy5hW0qUjeqy-JSeL1wIbURM1GKhc4,11481
|
|
257
257
|
unique_toolkit/content/utils.py,sha256=keDkqu_ZzQY0y_g7g_XLOza3p4XsvfqVVDI9ylM91KY,10115
|
|
258
258
|
unique_toolkit/data_extraction/README.md,sha256=5KxzqPHC29wxBiWJ-87wBQisSgRBkuSnPTb38pGH1wA,3138
|
|
259
259
|
unique_toolkit/data_extraction/__init__.py,sha256=y6uSp8PAqgPZMoBgXWp1Ep0gsk9MD-NV8qn2QpOY-xY,330
|
|
@@ -299,9 +299,9 @@ unique_toolkit/experimental/components/internal_search/chat/config.py,sha256=2DQ
|
|
|
299
299
|
unique_toolkit/experimental/components/internal_search/chat/schemas.py,sha256=vWPulL7QVo9y8vPZTKAeHVYOCHG1DmKIh6W75S0jEnA,299
|
|
300
300
|
unique_toolkit/experimental/components/internal_search/chat/service.py,sha256=SO60zGtzJIp5kGLEXCzHwcKgdN4MGeiv5kjGA3ki9fc,2355
|
|
301
301
|
unique_toolkit/experimental/components/internal_search/knowledge_base/__init__.py,sha256=eAp_8qw5XP2z6GFbxvVCmwgirXxMvJuEDZGx-PsFi54,411
|
|
302
|
-
unique_toolkit/experimental/components/internal_search/knowledge_base/config.py,sha256=
|
|
302
|
+
unique_toolkit/experimental/components/internal_search/knowledge_base/config.py,sha256=QyewUg9tH5mmfL56N3mpFUZ3_2qSthitd8JCV1oX3oU,3232
|
|
303
303
|
unique_toolkit/experimental/components/internal_search/knowledge_base/schemas.py,sha256=q5tk4IcpyypzqMb_36nEpO_eZgE9PqQsFrlpXJIwVWk,912
|
|
304
|
-
unique_toolkit/experimental/components/internal_search/knowledge_base/service.py,sha256=
|
|
304
|
+
unique_toolkit/experimental/components/internal_search/knowledge_base/service.py,sha256=LBp6SPJ8mO5x9rAbey-mejHZFK8om9EviyRUNGm5W7s,4856
|
|
305
305
|
unique_toolkit/experimental/components/internal_search/post_processing/__init__.py,sha256=ndNXfBOya9AEzhg8inbJVRpOtG809cw9vURK1yXnlAY,1896
|
|
306
306
|
unique_toolkit/experimental/components/internal_search/post_processing/config.py,sha256=Rkfm1PZhotJ8yyOK_SufIlUNcknyNzqMZWl4wYHnZMM,2385
|
|
307
307
|
unique_toolkit/experimental/components/internal_search/post_processing/service.py,sha256=5GL0bm4a24saPLUamP59CPPjxGlpEgBHBDCn7OqWGEI,7499
|
|
@@ -389,8 +389,8 @@ unique_toolkit/short_term_memory/functions.py,sha256=RQpdcpAIKQ8rRDhP8bHqekIKttQ
|
|
|
389
389
|
unique_toolkit/short_term_memory/schemas.py,sha256=C6gopsnuIgWJBG7XF87gxMwzLJtqbSGmsKXUf2h1sn4,1666
|
|
390
390
|
unique_toolkit/short_term_memory/service.py,sha256=Ncw0_0CNM6kmzJSLVJGG5GcKpBduIBebYZlzZ7N4G8Q,8900
|
|
391
391
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
392
|
-
unique_toolkit/smart_rules/compile.py,sha256=
|
|
392
|
+
unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
|
|
393
393
|
unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
|
|
394
|
-
unique_toolkit-2026.24.0.
|
|
395
|
-
unique_toolkit-2026.24.0.
|
|
396
|
-
unique_toolkit-2026.24.0.
|
|
394
|
+
unique_toolkit-2026.24.0.dev5.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
395
|
+
unique_toolkit-2026.24.0.dev5.dist-info/METADATA,sha256=c9BRIxMsywMfMEN91oVAv0NOEOi2KlnDULi-ohknj_c,7392
|
|
396
|
+
unique_toolkit-2026.24.0.dev5.dist-info/RECORD,,
|
|
File without changes
|