unique_orchestrator 2026.30.0.dev9__tar.gz → 2026.30.0.dev11__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.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/PKG-INFO +3 -3
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/pyproject.toml +3 -3
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/config.py +12 -2
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +8 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_loop_debug_params.py +8 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_reference_order.py +4 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_token_usage.py +4 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/unique_ai.py +38 -6
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/unique_ai_builder.py +23 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/README.md +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/__init__.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/__init__.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/history_manager.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/loop_iteration_runner.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/open_file_setup.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/skill_setup.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/settings.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_agent_config_rjsf_ui_schema.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_get_filtered_user_metadata.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_render_system_prompt_user_instructions.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_utils_resolve_other_options.py +0 -0
- {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-orchestrator
|
|
3
|
-
Version: 2026.30.0.
|
|
3
|
+
Version: 2026.30.0.dev11
|
|
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.30.0.
|
|
12
|
+
Requires-Dist: unique-toolkit>=2026.30.0.dev22,<2026.30.0rc0
|
|
13
13
|
Requires-Dist: unique-stock-ticker>=2026.30.0.dev0,<2026.30.0rc0
|
|
14
14
|
Requires-Dist: unique-follow-up-questions>=2026.30.0.dev1,<2026.30.0rc0
|
|
15
15
|
Requires-Dist: unique-internal-search>=2026.30.0.dev0,<2026.30.0rc0
|
|
@@ -17,7 +17,7 @@ Requires-Dist: unique-deep-research>=2026.30.0.dev0,<2026.30.0rc0
|
|
|
17
17
|
Requires-Dist: unique-web-search>=2026.30.0.dev4,<2026.30.0rc0
|
|
18
18
|
Requires-Dist: unique-swot>=2026.30.0.dev0,<2026.30.0rc0
|
|
19
19
|
Requires-Dist: unique-skill-tool>=2026.30.0.dev0,<2026.30.0rc0
|
|
20
|
-
Requires-Dist: unique-user-memory>=2026.30.0.
|
|
20
|
+
Requires-Dist: unique-user-memory>=2026.30.0.dev2,<2026.30.0rc0
|
|
21
21
|
Requires-Dist: openai>=1.109.1,<3
|
|
22
22
|
Requires-Python: >=3.12, <4
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique_orchestrator"
|
|
3
|
-
version = "2026.30.0.
|
|
3
|
+
version = "2026.30.0.dev11"
|
|
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.30.0.
|
|
16
|
+
"unique-toolkit>=2026.30.0.dev22,<2026.30.0rc0",
|
|
17
17
|
"unique-stock-ticker>=2026.30.0.dev0,<2026.30.0rc0",
|
|
18
18
|
"unique-follow-up-questions>=2026.30.0.dev1,<2026.30.0rc0",
|
|
19
19
|
"unique-internal-search>=2026.30.0.dev0,<2026.30.0rc0",
|
|
@@ -21,7 +21,7 @@ dependencies = [
|
|
|
21
21
|
"unique-web-search>=2026.30.0.dev4,<2026.30.0rc0",
|
|
22
22
|
"unique-swot>=2026.30.0.dev0,<2026.30.0rc0",
|
|
23
23
|
"unique-skill-tool>=2026.30.0.dev0,<2026.30.0rc0",
|
|
24
|
-
"unique-user-memory>=2026.30.0.
|
|
24
|
+
"unique-user-memory>=2026.30.0.dev2,<2026.30.0rc0",
|
|
25
25
|
"openai>=1.109.1,<3",
|
|
26
26
|
]
|
|
27
27
|
|
|
@@ -96,6 +96,16 @@ class SwitchableLanguageModelConfig(BaseToolConfig):
|
|
|
96
96
|
"`languageModel`."
|
|
97
97
|
),
|
|
98
98
|
)
|
|
99
|
+
temperature: float | None = Field(
|
|
100
|
+
default=None,
|
|
101
|
+
ge=0.0,
|
|
102
|
+
le=10.0,
|
|
103
|
+
description=(
|
|
104
|
+
"Optional per-model temperature override applied when chat users "
|
|
105
|
+
"select this model. Bounds mirror agent.experimental.temperature so "
|
|
106
|
+
"the value validates when copied on model choice."
|
|
107
|
+
),
|
|
108
|
+
)
|
|
99
109
|
|
|
100
110
|
|
|
101
111
|
class SpaceConfigBase(BaseToolConfig, Generic[T]):
|
|
@@ -301,8 +311,8 @@ class UniqueAIServices(BaseToolConfig):
|
|
|
301
311
|
)
|
|
302
312
|
|
|
303
313
|
user_memory_config: UserMemoryConfig = Field(
|
|
304
|
-
title="
|
|
305
|
-
description="Configuration for persistent
|
|
314
|
+
title="Context Memory",
|
|
315
|
+
description="Configuration for persistent context memory.",
|
|
306
316
|
default_factory=UserMemoryConfig,
|
|
307
317
|
)
|
|
308
318
|
|
|
@@ -33,6 +33,10 @@ def _build_unique_ai(**overrides):
|
|
|
33
33
|
|
|
34
34
|
mock_config = MagicMock()
|
|
35
35
|
mock_config.agent.prompt_config.user_metadata = []
|
|
36
|
+
mock_config.space.language_model.resolve_temp_and_reasoning.return_value = (
|
|
37
|
+
0.0,
|
|
38
|
+
None,
|
|
39
|
+
)
|
|
36
40
|
|
|
37
41
|
mock_debug_info_manager = MagicMock()
|
|
38
42
|
mock_debug_info_manager.get.return_value = {}
|
|
@@ -658,6 +662,10 @@ class TestRunExecutionTimingIntegration:
|
|
|
658
662
|
mock_config = MagicMock()
|
|
659
663
|
mock_config.effective_max_loop_iterations = 1
|
|
660
664
|
mock_config.agent.prompt_config.user_metadata = []
|
|
665
|
+
mock_config.space.language_model.resolve_temp_and_reasoning.return_value = (
|
|
666
|
+
0.0,
|
|
667
|
+
None,
|
|
668
|
+
)
|
|
661
669
|
|
|
662
670
|
mock_history_manager = MagicMock()
|
|
663
671
|
mock_history_manager.get_history_for_model_call = AsyncMock(
|
|
@@ -44,6 +44,10 @@ def _build_unique_ai(**overrides):
|
|
|
44
44
|
|
|
45
45
|
mock_config = MagicMock()
|
|
46
46
|
mock_config.agent.prompt_config.user_metadata = []
|
|
47
|
+
mock_config.space.language_model.resolve_temp_and_reasoning.return_value = (
|
|
48
|
+
0.0,
|
|
49
|
+
None,
|
|
50
|
+
)
|
|
47
51
|
|
|
48
52
|
mock_debug_info_manager = MagicMock()
|
|
49
53
|
mock_debug_info_manager.get.return_value = {}
|
|
@@ -287,6 +291,10 @@ class TestRunLoopDebugParams:
|
|
|
287
291
|
mock_config.space.language_model.name = "AZURE_GPT_5_2025_0807"
|
|
288
292
|
mock_config.space.language_model.family = "openai"
|
|
289
293
|
mock_config.space.language_model.provider = "AZURE"
|
|
294
|
+
mock_config.space.language_model.resolve_temp_and_reasoning.return_value = (
|
|
295
|
+
0.0,
|
|
296
|
+
None,
|
|
297
|
+
)
|
|
290
298
|
|
|
291
299
|
mock_history_manager = MagicMock()
|
|
292
300
|
mock_history_manager.get_history_for_model_call = AsyncMock(
|
|
@@ -38,6 +38,10 @@ async def test_history_updated_before_reference_extraction(monkeypatch):
|
|
|
38
38
|
mock_config = MagicMock()
|
|
39
39
|
mock_config.agent.max_loop_iterations = 1
|
|
40
40
|
mock_config.space.language_model.name = "dummy-model"
|
|
41
|
+
mock_config.space.language_model.resolve_temp_and_reasoning.return_value = (
|
|
42
|
+
0.0,
|
|
43
|
+
None,
|
|
44
|
+
)
|
|
41
45
|
mock_config.agent.experimental.temperature = 0.0
|
|
42
46
|
mock_config.agent.experimental.additional_llm_options = {}
|
|
43
47
|
|
|
@@ -56,6 +56,10 @@ def _build_run_ua(loop_responses: list[MagicMock], max_iterations: int):
|
|
|
56
56
|
mock_config.effective_max_loop_iterations = max_iterations
|
|
57
57
|
mock_config.agent.prompt_config.user_metadata = []
|
|
58
58
|
mock_config.space.language_model.name = MODEL_NAME
|
|
59
|
+
mock_config.space.language_model.resolve_temp_and_reasoning.return_value = (
|
|
60
|
+
0.0,
|
|
61
|
+
None,
|
|
62
|
+
)
|
|
59
63
|
|
|
60
64
|
mock_history_manager = MagicMock()
|
|
61
65
|
mock_history_manager.get_history_for_model_call = AsyncMock(
|
|
@@ -62,6 +62,7 @@ from unique_toolkit.protocols.support import (
|
|
|
62
62
|
ResponsesSupportCompleteWithReferences,
|
|
63
63
|
SupportCompleteWithReferences,
|
|
64
64
|
)
|
|
65
|
+
from unique_user_memory.user_memory_postprocessor import UserMemoryPostprocessor
|
|
65
66
|
|
|
66
67
|
from unique_orchestrator._builders.inject_tool_reminders import (
|
|
67
68
|
inject_tool_reminders_into_user_message,
|
|
@@ -208,6 +209,9 @@ class UniqueAI:
|
|
|
208
209
|
self._current_loop_timing: dict[str, Any] = {}
|
|
209
210
|
self._loop_debug_params: list[dict[str, Any]] = []
|
|
210
211
|
self._generated_files_info: ArtifactsDebugInfo | None = None
|
|
212
|
+
# None when the user-memory postprocessor is not activated for this turn;
|
|
213
|
+
# True/False when it ran and did/didn't update the stored memory profile.
|
|
214
|
+
self._context_memory_updated: bool | None = None
|
|
211
215
|
self._invocation_stats: list[LanguageModelInvocationStats] = []
|
|
212
216
|
|
|
213
217
|
async def _on_cancellation(self, _event: CancellationEvent) -> None:
|
|
@@ -238,6 +242,7 @@ class UniqueAI:
|
|
|
238
242
|
# reaching _handle_no_tool_calls (tool takes control / empty response /
|
|
239
243
|
# cancellation) must not report the previous run's artifacts.
|
|
240
244
|
self._generated_files_info = None
|
|
245
|
+
self._context_memory_updated = None
|
|
241
246
|
self._invocation_stats = []
|
|
242
247
|
run_start = time.perf_counter()
|
|
243
248
|
|
|
@@ -409,6 +414,7 @@ class UniqueAI:
|
|
|
409
414
|
loop_iteration_count=len(self._execution_times),
|
|
410
415
|
total_time_to_answer_ms=total_time_to_answer_ms,
|
|
411
416
|
artifacts=self._generated_files_info,
|
|
417
|
+
context_memory_updated=self._context_memory_updated,
|
|
412
418
|
)
|
|
413
419
|
|
|
414
420
|
# Get current debug info from chat service and add debug info from run. Do not update if DeepResearch is in the tool names.
|
|
@@ -443,18 +449,38 @@ class UniqueAI:
|
|
|
443
449
|
return round((assistant_completed_at - user_created_at).total_seconds() * 1000)
|
|
444
450
|
|
|
445
451
|
def _record_loop_debug_params(self, other_options: dict) -> None:
|
|
446
|
-
|
|
447
|
-
thinking_level: str =
|
|
448
|
-
other_options.get("reasoning_effort")
|
|
449
|
-
if not isinstance(reasoning, dict)
|
|
450
|
-
else reasoning.get("effort")
|
|
451
|
-
) or "None"
|
|
452
|
+
reasoning_effort = self._resolve_effective_reasoning_effort(other_options)
|
|
453
|
+
thinking_level: str = reasoning_effort or "None"
|
|
452
454
|
self._loop_debug_params.append(
|
|
453
455
|
{
|
|
454
456
|
"loop_number": self.current_iteration_index,
|
|
455
457
|
"thinking_level": thinking_level,
|
|
456
458
|
}
|
|
457
459
|
)
|
|
460
|
+
model_info = self._config.space.language_model
|
|
461
|
+
resolved_temperature, _ = model_info.resolve_temp_and_reasoning(
|
|
462
|
+
self._config.agent.experimental.temperature,
|
|
463
|
+
reasoning_effort=reasoning_effort,
|
|
464
|
+
)
|
|
465
|
+
self._debug_info_manager.add("temperature", resolved_temperature)
|
|
466
|
+
|
|
467
|
+
def _resolve_effective_reasoning_effort(self, other_options: dict) -> str | None:
|
|
468
|
+
"""Read the reasoning effort from the same source the active LLM API uses.
|
|
469
|
+
|
|
470
|
+
Mirrors ``resolve_other_options``: the completions API reads the flat
|
|
471
|
+
``reasoning_effort`` key, while the responses API prefers the nested
|
|
472
|
+
``reasoning.effort`` and falls back to the flat key. Keeping this in sync
|
|
473
|
+
ensures the debug temperature matches what is actually sent to the model.
|
|
474
|
+
"""
|
|
475
|
+
use_responses_api = (
|
|
476
|
+
self._config.agent.experimental.responses_api_config.use_responses_api
|
|
477
|
+
or self._config.agent.experimental.use_responses_api
|
|
478
|
+
)
|
|
479
|
+
if use_responses_api:
|
|
480
|
+
reasoning = other_options.get("reasoning")
|
|
481
|
+
if isinstance(reasoning, dict) and reasoning.get("effort") is not None:
|
|
482
|
+
return reasoning.get("effort")
|
|
483
|
+
return other_options.get("reasoning_effort")
|
|
458
484
|
|
|
459
485
|
def _get_activated_skills_debug_info(self) -> list[dict[str, str | bool]]:
|
|
460
486
|
skill_tool = self._tool_manager.get_tool_by_name(SkillTool.name)
|
|
@@ -755,6 +781,12 @@ class UniqueAI:
|
|
|
755
781
|
DisplayCodeInterpreterFilesPostProcessor.__name__
|
|
756
782
|
),
|
|
757
783
|
)
|
|
784
|
+
# Absent key => postprocessor not activated this turn (stays None);
|
|
785
|
+
# present => bool telling whether the memory profile was updated.
|
|
786
|
+
self._context_memory_updated = cast(
|
|
787
|
+
"bool | None",
|
|
788
|
+
postprocessor_outputs.get(UserMemoryPostprocessor.__name__),
|
|
789
|
+
)
|
|
758
790
|
self._current_loop_timing["post_processing"].update(
|
|
759
791
|
self._postprocessor_manager.get_execution_times()
|
|
760
792
|
)
|
|
@@ -234,6 +234,18 @@ def _apply_model_choice_override(
|
|
|
234
234
|
|
|
235
235
|
config_data = config.model_dump()
|
|
236
236
|
config_data["space"]["language_model"] = selected_model
|
|
237
|
+
|
|
238
|
+
switchable_entry = _find_switchable_language_model_entry(
|
|
239
|
+
selected_model=selected_model,
|
|
240
|
+
switchable_language_models=config.space.switchable_language_models,
|
|
241
|
+
)
|
|
242
|
+
if switchable_entry is not None and switchable_entry.temperature is not None:
|
|
243
|
+
config_data.setdefault("agent", {})
|
|
244
|
+
config_data["agent"].setdefault("experimental", {})
|
|
245
|
+
config_data["agent"]["experimental"]["temperature"] = (
|
|
246
|
+
switchable_entry.temperature
|
|
247
|
+
)
|
|
248
|
+
|
|
237
249
|
validated_config = UniqueAIConfig.model_validate(config_data)
|
|
238
250
|
|
|
239
251
|
logger.info(
|
|
@@ -258,6 +270,17 @@ def _record_language_model_debug_info(
|
|
|
258
270
|
)
|
|
259
271
|
|
|
260
272
|
|
|
273
|
+
def _find_switchable_language_model_entry(
|
|
274
|
+
*,
|
|
275
|
+
selected_model: LanguageModelInfo,
|
|
276
|
+
switchable_language_models: list[SwitchableLanguageModelConfig],
|
|
277
|
+
) -> SwitchableLanguageModelConfig | None:
|
|
278
|
+
for switchable_model in switchable_language_models:
|
|
279
|
+
if selected_model == switchable_model.language_model:
|
|
280
|
+
return switchable_model
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
|
|
261
284
|
def _is_switchable_language_model_choice(
|
|
262
285
|
*,
|
|
263
286
|
selected_model: LanguageModelInfo,
|
|
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
|