unique_orchestrator 2026.22.0.dev4__tar.gz → 2026.22.0.dev6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/PKG-INFO +4 -4
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/pyproject.toml +4 -4
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/_builders/skill_setup.py +12 -7
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/config.py +19 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/unique_ai_builder.py +32 -32
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/README.md +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/__init__.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/_builders/__init__.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/_builders/loop_iteration_runner.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/_builders/open_file_setup.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/settings.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_get_filtered_user_metadata.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_reference_order.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/tests/test_unique_ai_render_system_prompt_user_instructions.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/unique_ai.py +0 -0
- {unique_orchestrator-2026.22.0.dev4 → unique_orchestrator-2026.22.0.dev6}/unique_orchestrator/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-orchestrator
|
|
3
|
-
Version: 2026.22.0.
|
|
3
|
+
Version: 2026.22.0.dev6
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Andreas Hauri
|
|
6
6
|
Author-email: Andreas Hauri <andreas.hauri@unique.ai>
|
|
@@ -9,14 +9,14 @@ Requires-Dist: pydantic>=2.8.2,<3
|
|
|
9
9
|
Requires-Dist: pydantic-settings>=2.10.1,<3
|
|
10
10
|
Requires-Dist: typing-extensions>=4.9.0,<5
|
|
11
11
|
Requires-Dist: jinja2>=3.1.0,<4
|
|
12
|
-
Requires-Dist: unique-toolkit>=2026.22.0.
|
|
12
|
+
Requires-Dist: unique-toolkit>=2026.22.0.dev7,<2026.22.0rc0
|
|
13
13
|
Requires-Dist: unique-stock-ticker>=2026.22.0.dev2,<2026.22.0rc0
|
|
14
14
|
Requires-Dist: unique-follow-up-questions>=2026.22.0.dev2,<2026.22.0rc0
|
|
15
|
-
Requires-Dist: unique-internal-search>=2026.22.0.
|
|
15
|
+
Requires-Dist: unique-internal-search>=2026.22.0.dev3,<2026.22.0rc0
|
|
16
16
|
Requires-Dist: unique-deep-research>=2026.22.0.dev2,<2026.22.0rc0
|
|
17
17
|
Requires-Dist: unique-web-search>=2026.22.0.dev4,<2026.22.0rc0
|
|
18
18
|
Requires-Dist: unique-swot>=2026.22.0.dev2,<2026.22.0rc0
|
|
19
|
-
Requires-Dist: unique-skill-tool>=2026.22.0.
|
|
19
|
+
Requires-Dist: unique-skill-tool>=2026.22.0.dev4,<2026.22.0rc0
|
|
20
20
|
Requires-Dist: openai>=1.109.1,<3
|
|
21
21
|
Requires-Dist: python-frontmatter>=1.1.0,<2
|
|
22
22
|
Requires-Python: >=3.12, <4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique_orchestrator"
|
|
3
|
-
version = "2026.22.0.
|
|
3
|
+
version = "2026.22.0.dev6"
|
|
4
4
|
description = ""
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Proprietary" }
|
|
@@ -13,14 +13,14 @@ dependencies = [
|
|
|
13
13
|
"pydantic-settings>=2.10.1,<3",
|
|
14
14
|
"typing-extensions>=4.9.0,<5",
|
|
15
15
|
"jinja2>=3.1.0,<4",
|
|
16
|
-
"unique-toolkit>=2026.22.0.
|
|
16
|
+
"unique-toolkit>=2026.22.0.dev7,<2026.22.0rc0",
|
|
17
17
|
"unique-stock-ticker>=2026.22.0.dev2,<2026.22.0rc0",
|
|
18
18
|
"unique-follow-up-questions>=2026.22.0.dev2,<2026.22.0rc0",
|
|
19
|
-
"unique-internal-search>=2026.22.0.
|
|
19
|
+
"unique-internal-search>=2026.22.0.dev3,<2026.22.0rc0",
|
|
20
20
|
"unique-deep-research>=2026.22.0.dev2,<2026.22.0rc0",
|
|
21
21
|
"unique-web-search>=2026.22.0.dev4,<2026.22.0rc0",
|
|
22
22
|
"unique-swot>=2026.22.0.dev2,<2026.22.0rc0",
|
|
23
|
-
"unique-skill-tool>=2026.22.0.
|
|
23
|
+
"unique-skill-tool>=2026.22.0.dev4,<2026.22.0rc0",
|
|
24
24
|
"openai>=1.109.1,<3",
|
|
25
25
|
"python-frontmatter>=1.1.0,<2",
|
|
26
26
|
]
|
|
@@ -50,7 +50,6 @@ from pydantic import ValidationError
|
|
|
50
50
|
from unique_skill_tool.config import SkillToolConfig
|
|
51
51
|
from unique_skill_tool.schemas import SkillDefinition
|
|
52
52
|
from unique_skill_tool.service import SkillTool
|
|
53
|
-
from unique_skill_tool.utils import normalize_skill_name
|
|
54
53
|
from unique_toolkit.agentic.tools.config import ToolBuildConfig
|
|
55
54
|
from unique_toolkit.agentic.tools.schemas import ToolCallResponse
|
|
56
55
|
from unique_toolkit.app.schemas import SkillReference
|
|
@@ -140,8 +139,8 @@ def _build_skill(
|
|
|
140
139
|
is rejected by ``SkillDefinition`` rather than silently flowing into
|
|
141
140
|
the OpenAI tool enum where the model could never emit it verbatim.
|
|
142
141
|
|
|
143
|
-
``content_id``
|
|
144
|
-
|
|
142
|
+
``content_id`` is stored on the returned definition so it can be used
|
|
143
|
+
as a lookup key to load the SKILL.md.
|
|
145
144
|
"""
|
|
146
145
|
if not file_text.strip():
|
|
147
146
|
return None
|
|
@@ -164,6 +163,7 @@ def _build_skill(
|
|
|
164
163
|
name=name,
|
|
165
164
|
description=description,
|
|
166
165
|
content=body,
|
|
166
|
+
content_id=content_id,
|
|
167
167
|
)
|
|
168
168
|
except ValidationError as exc:
|
|
169
169
|
logger.warning(
|
|
@@ -325,7 +325,8 @@ async def preload_invoked_skills(
|
|
|
325
325
|
indistinguishable from skills the model activates itself:
|
|
326
326
|
|
|
327
327
|
1. Resolves each ``skill_choices`` entry against the registered
|
|
328
|
-
``SkillTool`` registry
|
|
328
|
+
``SkillTool`` registry by ``content_id``. Entries without a
|
|
329
|
+
``content_id`` are skipped — every skill choice must carry one.
|
|
329
330
|
2. For each matched skill, synthesizes a ``LanguageModelFunction``
|
|
330
331
|
and runs it through the already-registered ``SkillTool`` — the
|
|
331
332
|
exact same code path ``UniqueAI._handle_tool_calls`` uses.
|
|
@@ -340,13 +341,17 @@ async def preload_invoked_skills(
|
|
|
340
341
|
if not isinstance(skill_tool, SkillTool):
|
|
341
342
|
return
|
|
342
343
|
|
|
344
|
+
# content_id → skill.
|
|
345
|
+
content_id_index: dict[str, SkillDefinition] = {
|
|
346
|
+
s.content_id: s for s in skill_tool.skill_registry.values() if s.content_id
|
|
347
|
+
}
|
|
348
|
+
|
|
343
349
|
forced_skills: list[SkillDefinition] = []
|
|
344
350
|
seen_forced: set[str] = set()
|
|
345
351
|
for choice in skill_choices:
|
|
346
352
|
forced_skill: SkillDefinition | None = None
|
|
347
|
-
if choice.
|
|
348
|
-
|
|
349
|
-
forced_skill = skill_tool.skill_registry.get(normalized_name)
|
|
353
|
+
if choice.content_id:
|
|
354
|
+
forced_skill = content_id_index.get(choice.content_id)
|
|
350
355
|
if forced_skill is None:
|
|
351
356
|
continue
|
|
352
357
|
if forced_skill.name in seen_forced:
|
|
@@ -9,6 +9,9 @@ from unique_deep_research.service import DeepResearchTool
|
|
|
9
9
|
from unique_follow_up_questions.config import FollowUpQuestionsConfig
|
|
10
10
|
from unique_internal_search.config import InternalSearchConfig
|
|
11
11
|
from unique_internal_search.service import InternalSearchTool
|
|
12
|
+
from unique_internal_search.uploaded_search.config import (
|
|
13
|
+
UploadedSearchConfig,
|
|
14
|
+
)
|
|
12
15
|
from unique_stock_ticker.config import StockTickerConfig
|
|
13
16
|
from unique_swot import SwotAnalysisTool, SwotAnalysisToolConfig
|
|
14
17
|
from unique_toolkit._common.validators import (
|
|
@@ -329,6 +332,20 @@ class ResponsesApiConfig(BaseToolConfig):
|
|
|
329
332
|
)
|
|
330
333
|
|
|
331
334
|
|
|
335
|
+
class UploadedSearchToolConfig(BaseToolConfig):
|
|
336
|
+
force: bool = Field(
|
|
337
|
+
default=True,
|
|
338
|
+
title="Force tool",
|
|
339
|
+
description="If set, the tool will be forced when the user uploads a file",
|
|
340
|
+
)
|
|
341
|
+
tool_config: UploadedSearchConfig = UploadedSearchConfig()
|
|
342
|
+
|
|
343
|
+
@model_validator(mode="after")
|
|
344
|
+
def validate_tool_config(self) -> "UploadedSearchToolConfig":
|
|
345
|
+
self.tool_config.enable_tool_call_system_reminder = self.force
|
|
346
|
+
return self
|
|
347
|
+
|
|
348
|
+
|
|
332
349
|
class ExperimentalConfig(BaseToolConfig):
|
|
333
350
|
"""Experimental features this part of the configuration might evolve in the future continuously"""
|
|
334
351
|
|
|
@@ -367,6 +384,8 @@ class ExperimentalConfig(BaseToolConfig):
|
|
|
367
384
|
default_factory=TodoConfig,
|
|
368
385
|
)
|
|
369
386
|
|
|
387
|
+
uploaded_search_tool_config: UploadedSearchToolConfig = UploadedSearchToolConfig()
|
|
388
|
+
|
|
370
389
|
use_responses_api: bool = Field(
|
|
371
390
|
default=False,
|
|
372
391
|
description="If set, the main agent will use the Responses API from OpenAI",
|
|
@@ -6,9 +6,6 @@ from typing_extensions import override
|
|
|
6
6
|
from unique_follow_up_questions.follow_up_postprocessor import (
|
|
7
7
|
FollowUpPostprocessor,
|
|
8
8
|
)
|
|
9
|
-
from unique_internal_search.uploaded_search.config import (
|
|
10
|
-
UploadedSearchConfig,
|
|
11
|
-
)
|
|
12
9
|
from unique_internal_search.uploaded_search.service import (
|
|
13
10
|
UploadedSearchTool,
|
|
14
11
|
)
|
|
@@ -86,7 +83,7 @@ from unique_orchestrator._builders.skill_setup import (
|
|
|
86
83
|
configure_skill_tool,
|
|
87
84
|
normalize_available_skills_for_tool,
|
|
88
85
|
)
|
|
89
|
-
from unique_orchestrator.config import UniqueAIConfig
|
|
86
|
+
from unique_orchestrator.config import UniqueAIConfig, UploadedSearchToolConfig
|
|
90
87
|
from unique_orchestrator.unique_ai import UniqueAI
|
|
91
88
|
from unique_orchestrator.utils import filter_uploaded_documents_by_selection
|
|
92
89
|
|
|
@@ -390,8 +387,7 @@ async def _build_responses(
|
|
|
390
387
|
in config.space.language_model.capabilities
|
|
391
388
|
)
|
|
392
389
|
|
|
393
|
-
|
|
394
|
-
has_tool_choices = False
|
|
390
|
+
force_uploaded_search = False
|
|
395
391
|
if config.agent.experimental.open_file_tool_config.enabled:
|
|
396
392
|
handle_uploaded_file_tool_choices(
|
|
397
393
|
config,
|
|
@@ -400,12 +396,11 @@ async def _build_responses(
|
|
|
400
396
|
logger,
|
|
401
397
|
)
|
|
402
398
|
else:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
)
|
|
399
|
+
force_uploaded_search = _configure_uploaded_search_tool(
|
|
400
|
+
event=event,
|
|
401
|
+
logger=logger,
|
|
402
|
+
common_components=common_components,
|
|
403
|
+
config=config.agent.experimental.uploaded_search_tool_config,
|
|
409
404
|
)
|
|
410
405
|
|
|
411
406
|
builtin_tool_manager = await OpenAIBuiltInToolManager.build_manager(
|
|
@@ -429,9 +424,11 @@ async def _build_responses(
|
|
|
429
424
|
a2a_manager=common_components.a2a_manager,
|
|
430
425
|
builtin_tool_manager=builtin_tool_manager,
|
|
431
426
|
)
|
|
432
|
-
if
|
|
433
|
-
|
|
434
|
-
|
|
427
|
+
if (
|
|
428
|
+
not config.agent.experimental.open_file_tool_config.enabled
|
|
429
|
+
and force_uploaded_search
|
|
430
|
+
):
|
|
431
|
+
tool_manager.add_forced_tool(UploadedSearchTool.name)
|
|
435
432
|
|
|
436
433
|
agent_file_registry: list[str] = []
|
|
437
434
|
if config.agent.experimental.open_file_tool_config.enabled:
|
|
@@ -512,10 +509,11 @@ async def _build_completions(
|
|
|
512
509
|
common_components: _CommonComponents,
|
|
513
510
|
debug_info_manager: DebugInfoManager,
|
|
514
511
|
) -> UniqueAI:
|
|
515
|
-
|
|
512
|
+
force_uploaded_search = _configure_uploaded_search_tool(
|
|
516
513
|
event=event,
|
|
517
514
|
logger=logger,
|
|
518
515
|
common_components=common_components,
|
|
516
|
+
config=config.agent.experimental.uploaded_search_tool_config,
|
|
519
517
|
)
|
|
520
518
|
|
|
521
519
|
tool_manager = ToolManager(
|
|
@@ -526,7 +524,7 @@ async def _build_completions(
|
|
|
526
524
|
mcp_manager=common_components.mcp_manager,
|
|
527
525
|
a2a_manager=common_components.a2a_manager,
|
|
528
526
|
)
|
|
529
|
-
if
|
|
527
|
+
if force_uploaded_search:
|
|
530
528
|
tool_manager.add_forced_tool(UploadedSearchTool.name)
|
|
531
529
|
|
|
532
530
|
await configure_skill_tool(
|
|
@@ -587,8 +585,9 @@ def _configure_uploaded_search_tool(
|
|
|
587
585
|
event: ChatEvent,
|
|
588
586
|
logger: Logger,
|
|
589
587
|
common_components: _CommonComponents,
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
config: UploadedSearchToolConfig,
|
|
589
|
+
) -> bool:
|
|
590
|
+
"""Add the uploaded search tool when documents are present and return whether it should be forced."""
|
|
592
591
|
valid_uploaded_documents: list[Content] = []
|
|
593
592
|
expired_uploaded_documents: list[Content] = []
|
|
594
593
|
for doc in common_components.uploaded_documents:
|
|
@@ -598,29 +597,30 @@ def _configure_uploaded_search_tool(
|
|
|
598
597
|
expired_uploaded_documents.append(doc)
|
|
599
598
|
else:
|
|
600
599
|
valid_uploaded_documents.append(doc)
|
|
601
|
-
has_tool_choices = len(event.payload.tool_choices) > 0
|
|
602
600
|
|
|
603
601
|
if expired_uploaded_documents:
|
|
604
602
|
logger.info(
|
|
605
603
|
f"Number of expired uploaded documents: {len(expired_uploaded_documents)}"
|
|
606
604
|
)
|
|
607
605
|
|
|
608
|
-
if valid_uploaded_documents:
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
606
|
+
if not valid_uploaded_documents:
|
|
607
|
+
return False
|
|
608
|
+
|
|
609
|
+
logger.info(f"Number of valid uploaded documents: {len(valid_uploaded_documents)}")
|
|
610
|
+
|
|
611
|
+
common_components.tool_manager_config.tools.append(
|
|
612
|
+
ToolBuildConfig(
|
|
613
|
+
name=UploadedSearchTool.name,
|
|
614
|
+
display_name=UploadedSearchTool.name,
|
|
615
|
+
configuration=config.tool_config,
|
|
618
616
|
)
|
|
617
|
+
)
|
|
619
618
|
|
|
620
|
-
if
|
|
619
|
+
if len(event.payload.tool_choices) > 0 and config.force:
|
|
621
620
|
event.payload.tool_choices.append(str(UploadedSearchTool.name))
|
|
621
|
+
return False
|
|
622
622
|
|
|
623
|
-
return
|
|
623
|
+
return config.force
|
|
624
624
|
|
|
625
625
|
|
|
626
626
|
def _add_sub_agents_postprocessor(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|