unique_orchestrator 2026.40.0.dev4__tar.gz → 2026.40.0.dev5__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.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/PKG-INFO +2 -2
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/pyproject.toml +2 -2
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/_builders/loop_iteration_runner.py +10 -2
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +71 -3
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/README.md +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/__init__.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/_builders/__init__.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/_builders/history_manager.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/_builders/open_file_setup.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/_builders/skill_setup.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/config.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/settings.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_agent_config_rjsf_ui_schema.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_get_filtered_user_metadata.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_loop_debug_params.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_reference_order.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_render_system_prompt_user_instructions.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_unique_ai_token_usage.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/tests/test_utils_resolve_other_options.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/unique_ai.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/unique_ai_builder.py +0 -0
- {unique_orchestrator-2026.40.0.dev4 → unique_orchestrator-2026.40.0.dev5}/unique_orchestrator/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-orchestrator
|
|
3
|
-
Version: 2026.40.0.
|
|
3
|
+
Version: 2026.40.0.dev5
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Andreas Hauri
|
|
6
6
|
Author-email: Andreas Hauri <andreas.hauri@unique.ai>
|
|
@@ -9,7 +9,7 @@ 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.40.0.
|
|
12
|
+
Requires-Dist: unique-toolkit>=2026.40.0.dev12,<2026.40.0rc0
|
|
13
13
|
Requires-Dist: unique-stock-ticker>=2026.40.0.dev0,<2026.40.0rc0
|
|
14
14
|
Requires-Dist: unique-follow-up-questions>=2026.40.0.dev0,<2026.40.0rc0
|
|
15
15
|
Requires-Dist: unique-internal-search>=2026.40.0.dev0,<2026.40.0rc0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique_orchestrator"
|
|
3
|
-
version = "2026.40.0.
|
|
3
|
+
version = "2026.40.0.dev5"
|
|
4
4
|
description = ""
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Proprietary" }
|
|
@@ -13,7 +13,7 @@ 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.40.0.
|
|
16
|
+
"unique-toolkit>=2026.40.0.dev12,<2026.40.0rc0",
|
|
17
17
|
"unique-stock-ticker>=2026.40.0.dev0,<2026.40.0rc0",
|
|
18
18
|
"unique-follow-up-questions>=2026.40.0.dev0,<2026.40.0rc0",
|
|
19
19
|
"unique-internal-search>=2026.40.0.dev0,<2026.40.0rc0",
|
|
@@ -9,6 +9,7 @@ from unique_toolkit.agentic.loop_runner import (
|
|
|
9
9
|
LoopIterationRunner,
|
|
10
10
|
MistralLoopIterationRunner,
|
|
11
11
|
PlanningMiddleware,
|
|
12
|
+
PromptForcedToolLoopIterationRunner,
|
|
12
13
|
QwenLoopIterationRunner,
|
|
13
14
|
ResponsesBasicLoopIterationRunner,
|
|
14
15
|
ResponsesLoopIterationRunner,
|
|
@@ -52,9 +53,16 @@ def build_loop_iteration_runner(
|
|
|
52
53
|
)
|
|
53
54
|
family = get_model_family(str(config.space.language_model))
|
|
54
55
|
|
|
55
|
-
if
|
|
56
|
+
if not config.space.language_model.supports_forced_tool_choice:
|
|
57
|
+
# The model returns HTTP 400 for a named or "any" tool_choice, so the
|
|
58
|
+
# tool is requested in the prompt instead. Checked before the family
|
|
59
|
+
# runners because that failure is hard.
|
|
60
|
+
runner: LoopIterationRunner = PromptForcedToolLoopIterationRunner(
|
|
61
|
+
config=base_config
|
|
62
|
+
)
|
|
63
|
+
elif family == "qwen":
|
|
56
64
|
qwen_cfg = config.agent.experimental.loop_configuration.model_specific.qwen
|
|
57
|
-
runner
|
|
65
|
+
runner = QwenLoopIterationRunner(
|
|
58
66
|
config=BasicLoopIterationRunnerConfig(
|
|
59
67
|
max_loop_iterations=qwen_cfg.max_loop_iterations
|
|
60
68
|
),
|
|
@@ -16,10 +16,12 @@ from unique_toolkit.agentic.loop_runner import (
|
|
|
16
16
|
MistralLoopIterationRunner,
|
|
17
17
|
PlanningConfig,
|
|
18
18
|
PlanningMiddleware,
|
|
19
|
+
PromptForcedToolLoopIterationRunner,
|
|
19
20
|
QwenLoopIterationRunner,
|
|
20
21
|
ResponsesBasicLoopIterationRunner,
|
|
21
22
|
ResponsesPlanningMiddleware,
|
|
22
23
|
)
|
|
24
|
+
from unique_toolkit.language_model.infos import LanguageModelInfo, LanguageModelName
|
|
23
25
|
|
|
24
26
|
from unique_orchestrator._builders.loop_iteration_runner import (
|
|
25
27
|
build_loop_iteration_runner,
|
|
@@ -43,6 +45,7 @@ class TestGetModelFamily:
|
|
|
43
45
|
def testget_model_family__returns_none__for_other_models(self) -> None:
|
|
44
46
|
assert get_model_family("gpt-4o") is None
|
|
45
47
|
assert get_model_family("claude-3") is None
|
|
48
|
+
assert get_model_family("litellm:anthropic-claude-opus-5-5") is None
|
|
46
49
|
|
|
47
50
|
|
|
48
51
|
class TestBuildResponsesLoopIterationRunner:
|
|
@@ -518,7 +521,7 @@ class TestBuildLoopIterationRunnerModelFamilyIntegration:
|
|
|
518
521
|
self,
|
|
519
522
|
) -> None:
|
|
520
523
|
config: UniqueAIConfig = UniqueAIConfig()
|
|
521
|
-
config.space.language_model = "gpt-4o"
|
|
524
|
+
config.space.language_model = LanguageModelInfo(name="gpt-4o")
|
|
522
525
|
runner = build_loop_iteration_runner(
|
|
523
526
|
config=config,
|
|
524
527
|
history_manager=MagicMock(),
|
|
@@ -532,7 +535,7 @@ class TestBuildLoopIterationRunnerModelFamilyIntegration:
|
|
|
532
535
|
self,
|
|
533
536
|
) -> None:
|
|
534
537
|
config: UniqueAIConfig = UniqueAIConfig()
|
|
535
|
-
config.space.language_model = "mistral-large"
|
|
538
|
+
config.space.language_model = LanguageModelInfo(name="mistral-large")
|
|
536
539
|
runner = build_loop_iteration_runner(
|
|
537
540
|
config=config,
|
|
538
541
|
history_manager=MagicMock(),
|
|
@@ -546,7 +549,7 @@ class TestBuildLoopIterationRunnerModelFamilyIntegration:
|
|
|
546
549
|
self,
|
|
547
550
|
) -> None:
|
|
548
551
|
config: UniqueAIConfig = UniqueAIConfig()
|
|
549
|
-
config.space.language_model = "litellm/qwen3"
|
|
552
|
+
config.space.language_model = LanguageModelInfo(name="litellm/qwen3")
|
|
550
553
|
runner = build_loop_iteration_runner(
|
|
551
554
|
config=config,
|
|
552
555
|
history_manager=MagicMock(),
|
|
@@ -554,3 +557,68 @@ class TestBuildLoopIterationRunnerModelFamilyIntegration:
|
|
|
554
557
|
llm_service=MagicMock(),
|
|
555
558
|
)
|
|
556
559
|
assert isinstance(runner, QwenLoopIterationRunner)
|
|
560
|
+
|
|
561
|
+
@pytest.mark.ai
|
|
562
|
+
def test_build_loop_iteration_runner__returns_prompt_forced_tool_runner__for_opus_55(
|
|
563
|
+
self,
|
|
564
|
+
) -> None:
|
|
565
|
+
"""
|
|
566
|
+
Purpose: Verify Opus 5.5 model info builds the prompt-forced-tool runner.
|
|
567
|
+
Why this matters: Forced tool calls on that model must not send a named choice.
|
|
568
|
+
Setup summary: Use the Opus 5.5 LanguageModelInfo and build the runner.
|
|
569
|
+
"""
|
|
570
|
+
config: UniqueAIConfig = UniqueAIConfig()
|
|
571
|
+
config.space.language_model = LanguageModelInfo.from_name(
|
|
572
|
+
LanguageModelName.ANTHROPIC_CLAUDE_OPUS_5_5
|
|
573
|
+
)
|
|
574
|
+
assert config.space.language_model.supports_forced_tool_choice is False
|
|
575
|
+
runner = build_loop_iteration_runner(
|
|
576
|
+
config=config,
|
|
577
|
+
history_manager=MagicMock(),
|
|
578
|
+
chat_service=MagicMock(),
|
|
579
|
+
llm_service=MagicMock(),
|
|
580
|
+
)
|
|
581
|
+
assert isinstance(runner, PromptForcedToolLoopIterationRunner)
|
|
582
|
+
assert runner._config.max_loop_iterations == config.agent.max_loop_iterations
|
|
583
|
+
|
|
584
|
+
@pytest.mark.ai
|
|
585
|
+
def test_build_loop_iteration_runner__returns_basic_runner__for_opus_5(
|
|
586
|
+
self,
|
|
587
|
+
) -> None:
|
|
588
|
+
"""
|
|
589
|
+
Purpose: Verify Opus 5 keeps the basic runner with a named tool_choice.
|
|
590
|
+
Why this matters: Only models flagged in LanguageModelInfo lose forced tools.
|
|
591
|
+
Setup summary: Use the Opus 5 LanguageModelInfo and build the runner.
|
|
592
|
+
"""
|
|
593
|
+
config: UniqueAIConfig = UniqueAIConfig()
|
|
594
|
+
config.space.language_model = LanguageModelInfo.from_name(
|
|
595
|
+
LanguageModelName.ANTHROPIC_CLAUDE_OPUS_5
|
|
596
|
+
)
|
|
597
|
+
runner = build_loop_iteration_runner(
|
|
598
|
+
config=config,
|
|
599
|
+
history_manager=MagicMock(),
|
|
600
|
+
chat_service=MagicMock(),
|
|
601
|
+
llm_service=MagicMock(),
|
|
602
|
+
)
|
|
603
|
+
assert type(runner) is BasicLoopIterationRunner
|
|
604
|
+
|
|
605
|
+
@pytest.mark.ai
|
|
606
|
+
def test_build_loop_iteration_runner__prefers_prompt_forced_tool__over_family_runner(
|
|
607
|
+
self,
|
|
608
|
+
) -> None:
|
|
609
|
+
"""
|
|
610
|
+
Purpose: Verify the forced-tool flag wins over the Mistral family runner.
|
|
611
|
+
Why this matters: Mistral sends tool_choice any, which such a model rejects.
|
|
612
|
+
Setup summary: Flag a Mistral-named model info and build the runner.
|
|
613
|
+
"""
|
|
614
|
+
config: UniqueAIConfig = UniqueAIConfig()
|
|
615
|
+
config.space.language_model = LanguageModelInfo(
|
|
616
|
+
name="mistral-large", supports_forced_tool_choice=False
|
|
617
|
+
)
|
|
618
|
+
runner = build_loop_iteration_runner(
|
|
619
|
+
config=config,
|
|
620
|
+
history_manager=MagicMock(),
|
|
621
|
+
chat_service=MagicMock(),
|
|
622
|
+
llm_service=MagicMock(),
|
|
623
|
+
)
|
|
624
|
+
assert isinstance(runner, PromptForcedToolLoopIterationRunner)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|