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.
Files changed (34) hide show
  1. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/PKG-INFO +3 -3
  2. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/pyproject.toml +3 -3
  3. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/config.py +12 -2
  4. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +8 -0
  5. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_loop_debug_params.py +8 -0
  6. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_reference_order.py +4 -0
  7. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_token_usage.py +4 -0
  8. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/unique_ai.py +38 -6
  9. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/unique_ai_builder.py +23 -0
  10. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/README.md +0 -0
  11. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/__init__.py +0 -0
  12. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/__init__.py +0 -0
  13. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/history_manager.py +0 -0
  14. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
  15. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/loop_iteration_runner.py +0 -0
  16. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/open_file_setup.py +0 -0
  17. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/_builders/skill_setup.py +0 -0
  18. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
  19. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
  20. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
  21. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/settings.py +0 -0
  22. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_agent_config_rjsf_ui_schema.py +0 -0
  23. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +0 -0
  24. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
  25. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
  26. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
  27. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
  28. {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
  29. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
  30. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
  31. {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
  32. {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
  33. {unique_orchestrator-2026.30.0.dev9 → unique_orchestrator-2026.30.0.dev11}/unique_orchestrator/tests/test_utils_resolve_other_options.py +0 -0
  34. {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.dev9
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.dev21,<2026.30.0rc0
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.dev1,<2026.30.0rc0
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.dev9"
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.dev21,<2026.30.0rc0",
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.dev1,<2026.30.0rc0",
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="User Memory (Experimental)",
305
- description="Configuration for persistent user memory. Experimental feature - requires activation via feature flag.",
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
- reasoning = other_options.get("reasoning")
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,