unique_orchestrator 2026.30.0.dev5__tar.gz → 2026.30.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.
Files changed (34) hide show
  1. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/PKG-INFO +2 -2
  2. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/pyproject.toml +2 -2
  3. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/_builders/history_manager.py +5 -0
  4. unique_orchestrator-2026.30.0.dev6/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +76 -0
  5. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/unique_ai.py +6 -1
  6. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/unique_ai_builder.py +5 -18
  7. unique_orchestrator-2026.30.0.dev5/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +0 -120
  8. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/README.md +0 -0
  9. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/__init__.py +0 -0
  10. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/_builders/__init__.py +0 -0
  11. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
  12. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/_builders/loop_iteration_runner.py +0 -0
  13. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/_builders/open_file_setup.py +0 -0
  14. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/_builders/skill_setup.py +0 -0
  15. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/config.py +0 -0
  16. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
  17. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
  18. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
  19. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/settings.py +0 -0
  20. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_agent_config_rjsf_ui_schema.py +0 -0
  21. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +0 -0
  22. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
  23. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
  24. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
  25. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
  26. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +0 -0
  27. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_get_filtered_user_metadata.py +0 -0
  28. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
  29. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_loop_debug_params.py +0 -0
  30. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
  31. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_reference_order.py +0 -0
  32. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_unique_ai_render_system_prompt_user_instructions.py +0 -0
  33. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.0.dev6}/unique_orchestrator/tests/test_utils_resolve_other_options.py +0 -0
  34. {unique_orchestrator-2026.30.0.dev5 → unique_orchestrator-2026.30.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.30.0.dev5
3
+ Version: 2026.30.0.dev6
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.dev15,<2026.30.0rc0
12
+ Requires-Dist: unique-toolkit>=2026.30.0.dev17,<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.dev0,<2026.30.0rc0
15
15
  Requires-Dist: unique-internal-search>=2026.30.0.dev0,<2026.30.0rc0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_orchestrator"
3
- version = "2026.30.0.dev5"
3
+ version = "2026.30.0.dev6"
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.dev15,<2026.30.0rc0",
16
+ "unique-toolkit>=2026.30.0.dev17,<2026.30.0rc0",
17
17
  "unique-stock-ticker>=2026.30.0.dev0,<2026.30.0rc0",
18
18
  "unique-follow-up-questions>=2026.30.0.dev0,<2026.30.0rc0",
19
19
  "unique-internal-search>=2026.30.0.dev0,<2026.30.0rc0",
@@ -14,6 +14,9 @@ from unique_toolkit.agentic.history_manager.history_manager import (
14
14
  )
15
15
  from unique_toolkit.agentic.reference_manager.reference_manager import ReferenceManager
16
16
  from unique_toolkit.agentic.tools.openai_builtin.base import OpenAIBuiltInToolName
17
+ from unique_toolkit.agentic.tools.openai_builtin.code_interpreter import (
18
+ CodeInterpreterActivatorTool,
19
+ )
17
20
  from unique_toolkit.agentic.tools.openai_builtin.code_interpreter.postprocessors.artifacts import (
18
21
  load_code_execution_metadata,
19
22
  )
@@ -64,6 +67,8 @@ def _get_file_content_serializer(
64
67
  code_interpreter_available=(
65
68
  tool_manager.get_tool_by_name(OpenAIBuiltInToolName.CODE_INTERPRETER)
66
69
  is not None
70
+ or tool_manager.get_tool_by_name(CodeInterpreterActivatorTool.NAME)
71
+ is not None
67
72
  ),
68
73
  )
69
74
 
@@ -0,0 +1,76 @@
1
+ """Tests for ResponsesStreamingHandler include injection (UN-17972).
2
+
3
+ The handler always requests code_interpreter_call.outputs so UniqueAI stays
4
+ generic (no isinstance checks) and code interpreter execution logs attach to
5
+ tool calls whenever code interpreter runs.
6
+ """
7
+
8
+ from unittest.mock import AsyncMock, MagicMock
9
+
10
+ import pytest
11
+
12
+ from unique_orchestrator.unique_ai_builder import (
13
+ ResponsesStreamingHandler,
14
+ _ChatServiceResponsesStreaming,
15
+ )
16
+
17
+
18
+ def _make_handler() -> ResponsesStreamingHandler:
19
+ inner = MagicMock()
20
+ inner.complete_with_references_async = AsyncMock(return_value=MagicMock())
21
+ inner.complete_with_references = MagicMock(return_value=MagicMock())
22
+ return ResponsesStreamingHandler(inner=inner)
23
+
24
+
25
+ @pytest.mark.ai
26
+ @pytest.mark.asyncio
27
+ async def test_responses_streaming_handler__always_injects_include__async_path() -> (
28
+ None
29
+ ):
30
+ """complete_with_references_async always sets include=code_interpreter_call.outputs."""
31
+ handler = _make_handler()
32
+
33
+ await handler.complete_with_references_async(messages=[], model_name="model")
34
+
35
+ call_kwargs = handler._inner.complete_with_references_async.await_args.kwargs
36
+ assert call_kwargs["include"] == ["code_interpreter_call.outputs"]
37
+
38
+
39
+ @pytest.mark.ai
40
+ def test_responses_streaming_handler__always_injects_include__sync_path() -> None:
41
+ """The synchronous complete_with_references also always sets include."""
42
+ handler = _make_handler()
43
+
44
+ handler.complete_with_references(messages=[], model_name="model")
45
+
46
+ call_kwargs = handler._inner.complete_with_references.call_args.kwargs
47
+ assert call_kwargs["include"] == ["code_interpreter_call.outputs"]
48
+
49
+
50
+ @pytest.mark.ai
51
+ @pytest.mark.asyncio
52
+ async def test_chat_service_responses_streaming__forwards_to_responses_methods() -> (
53
+ None
54
+ ):
55
+ """
56
+ Purpose: The legacy adapter must route the protocol's
57
+ `complete_with_references[_async]` to ChatService's Responses-specific
58
+ methods, not the Chat Completions ones.
59
+ Why this matters: ChatService exposes both; routing to the wrong method
60
+ would silently stream through the Chat Completions API.
61
+ """
62
+ chat_service = MagicMock()
63
+ chat_service.complete_responses_with_references = MagicMock(
64
+ return_value=MagicMock()
65
+ )
66
+ chat_service.complete_responses_with_references_async = AsyncMock(
67
+ return_value=MagicMock()
68
+ )
69
+ adapter = _ChatServiceResponsesStreaming(chat_service)
70
+
71
+ adapter.complete_with_references(messages=[], model_name="model")
72
+ await adapter.complete_with_references_async(messages=[], model_name="model")
73
+
74
+ chat_service.complete_responses_with_references.assert_called_once()
75
+ chat_service.complete_responses_with_references_async.assert_awaited_once()
76
+ chat_service.complete_with_references.assert_not_called()
@@ -755,7 +755,12 @@ class UniqueAI:
755
755
  # (see ``unique_skill_tool.SkillTool._log_skill_loaded``), so it is
756
756
  # redundant and noisy to also list it here.
757
757
 
758
- tool_names_not_to_log: set[str] = {"DeepResearch", "Skill", "AskUser"}
758
+ tool_names_not_to_log: set[str] = {
759
+ "DeepResearch",
760
+ "Skill",
761
+ "AskUser",
762
+ "ActivatePython",
763
+ }
759
764
 
760
765
  used_tools: dict[str, int] = {}
761
766
  for tool_call in tool_calls:
@@ -2,6 +2,7 @@ from logging import Logger
2
2
  from typing import Any, NamedTuple, cast
3
3
 
4
4
  from openai import AsyncOpenAI
5
+ from openai.types.responses import ResponseIncludable
5
6
  from typing_extensions import override
6
7
  from unique_follow_up_questions.follow_up_postprocessor import (
7
8
  FollowUpPostprocessor,
@@ -115,37 +116,24 @@ def _configure_follow_up_questions(
115
116
 
116
117
 
117
118
  class ResponsesStreamingHandler(ResponsesSupportCompleteWithReferences):
118
- """Streaming handler for Responses API runs.
119
+ """Streaming handler for Responses API runs."""
119
120
 
120
- Injects `include` params from the tool manager on every call so the
121
- orchestrator loop stays generic (no isinstance checks in UniqueAI).
122
-
123
- Delegates the actual streaming to an injected
124
- `ResponsesSupportCompleteWithReferences` (the event-routing
125
- `ResponsesCompleteWithReferences` orchestrator), so this class only owns
126
- the `include` injection.
127
- """
121
+ _INCLUDE: list[ResponseIncludable] = ["code_interpreter_call.outputs"]
128
122
 
129
123
  def __init__(
130
124
  self,
131
125
  inner: ResponsesSupportCompleteWithReferences,
132
- tool_manager: ResponsesApiToolManager,
133
126
  ) -> None:
134
127
  self._inner: ResponsesSupportCompleteWithReferences = inner
135
- self._tool_manager: ResponsesApiToolManager = tool_manager
136
128
 
137
129
  @override
138
130
  def complete_with_references(self, *args: Any, **kwargs: Any) -> Any:
139
- include = self._tool_manager.get_required_include_params()
140
- if include:
141
- kwargs["include"] = include
131
+ kwargs["include"] = self._INCLUDE
142
132
  return self._inner.complete_with_references(*args, **kwargs)
143
133
 
144
134
  @override
145
135
  async def complete_with_references_async(self, *args: Any, **kwargs: Any) -> Any:
146
- include = self._tool_manager.get_required_include_params()
147
- if include:
148
- kwargs["include"] = include
136
+ kwargs["include"] = self._INCLUDE
149
137
  return await self._inner.complete_with_references_async(*args, **kwargs)
150
138
 
151
139
 
@@ -602,7 +590,6 @@ async def _build_responses(
602
590
 
603
591
  streaming_handler = ResponsesStreamingHandler(
604
592
  inner=inner,
605
- tool_manager=tool_manager,
606
593
  )
607
594
 
608
595
  _add_sub_agents_postprocessor(
@@ -1,120 +0,0 @@
1
- """Tests for ResponsesStreamingHandler include injection (UN-17972).
2
-
3
- The include forwarding logic lives in ResponsesStreamingHandler in
4
- unique_ai_builder.py so that UniqueAI stays generic (no isinstance checks).
5
- """
6
-
7
- from unittest.mock import AsyncMock, MagicMock
8
-
9
- import pytest
10
- from unique_toolkit.agentic.tools.tool_manager import ResponsesApiToolManager
11
-
12
- from unique_orchestrator.unique_ai_builder import (
13
- ResponsesStreamingHandler,
14
- _ChatServiceResponsesStreaming,
15
- )
16
-
17
-
18
- def _make_handler(
19
- include_params: list[str],
20
- ) -> ResponsesStreamingHandler:
21
- tool_manager = MagicMock(spec=ResponsesApiToolManager)
22
- tool_manager.get_required_include_params.return_value = include_params
23
- inner = MagicMock()
24
- inner.complete_with_references_async = AsyncMock(return_value=MagicMock())
25
- inner.complete_with_references = MagicMock(return_value=MagicMock())
26
- return ResponsesStreamingHandler(
27
- inner=inner,
28
- tool_manager=tool_manager,
29
- )
30
-
31
-
32
- @pytest.mark.ai
33
- @pytest.mark.asyncio
34
- async def test_responses_streaming_handler__injects_include__when_tool_manager_returns_params() -> (
35
- None
36
- ):
37
- """
38
- Purpose: Verify complete_with_references_async injects include when the tool
39
- manager returns a non-empty list.
40
- Why this matters: UN-17972 requires code_interpreter_call.outputs on the
41
- Responses API so execution logs attach to tool calls.
42
- """
43
- handler = _make_handler(include_params=["code_interpreter_call.outputs"])
44
-
45
- await handler.complete_with_references_async(messages=[], model_name="model")
46
-
47
- call_kwargs = handler._inner.complete_with_references_async.await_args.kwargs
48
- assert call_kwargs["include"] == ["code_interpreter_call.outputs"]
49
- handler._tool_manager.get_required_include_params.assert_called_once()
50
-
51
-
52
- @pytest.mark.ai
53
- @pytest.mark.asyncio
54
- async def test_responses_streaming_handler__omits_include__when_tool_manager_returns_empty() -> (
55
- None
56
- ):
57
- """
58
- Purpose: Verify include is not added when get_required_include_params returns [].
59
- Why this matters: No extra include should reach the Responses API when no tool
60
- requests it — preserving legacy behaviour exactly.
61
- """
62
- handler = _make_handler(include_params=[])
63
-
64
- await handler.complete_with_references_async(messages=[], model_name="model")
65
-
66
- call_kwargs = handler._inner.complete_with_references_async.await_args.kwargs
67
- assert "include" not in call_kwargs
68
-
69
-
70
- @pytest.mark.ai
71
- def test_responses_streaming_handler__injects_include__sync_path() -> None:
72
- """
73
- Purpose: Verify the synchronous complete_with_references also injects include.
74
- """
75
- handler = _make_handler(include_params=["code_interpreter_call.outputs"])
76
-
77
- handler.complete_with_references(messages=[], model_name="model")
78
-
79
- call_kwargs = handler._inner.complete_with_references.call_args.kwargs
80
- assert call_kwargs["include"] == ["code_interpreter_call.outputs"]
81
-
82
-
83
- @pytest.mark.ai
84
- def test_responses_streaming_handler__omits_include__sync_path_when_empty() -> None:
85
- """Purpose: No include injected via sync path when tool manager returns []."""
86
- handler = _make_handler(include_params=[])
87
-
88
- handler.complete_with_references(messages=[], model_name="model")
89
-
90
- call_kwargs = handler._inner.complete_with_references.call_args.kwargs
91
- assert "include" not in call_kwargs
92
-
93
-
94
- @pytest.mark.ai
95
- @pytest.mark.asyncio
96
- async def test_chat_service_responses_streaming__forwards_to_responses_methods() -> (
97
- None
98
- ):
99
- """
100
- Purpose: The legacy adapter must route the protocol's
101
- `complete_with_references[_async]` to ChatService's Responses-specific
102
- methods, not the Chat Completions ones.
103
- Why this matters: ChatService exposes both; routing to the wrong method
104
- would silently stream through the Chat Completions API.
105
- """
106
- chat_service = MagicMock()
107
- chat_service.complete_responses_with_references = MagicMock(
108
- return_value=MagicMock()
109
- )
110
- chat_service.complete_responses_with_references_async = AsyncMock(
111
- return_value=MagicMock()
112
- )
113
- adapter = _ChatServiceResponsesStreaming(chat_service)
114
-
115
- adapter.complete_with_references(messages=[], model_name="model")
116
- await adapter.complete_with_references_async(messages=[], model_name="model")
117
-
118
- chat_service.complete_responses_with_references.assert_called_once()
119
- chat_service.complete_responses_with_references_async.assert_awaited_once()
120
- chat_service.complete_with_references.assert_not_called()