unique_orchestrator 2026.30.0.dev0__tar.gz → 2026.30.0.dev1__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 (32) hide show
  1. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/PKG-INFO +2 -2
  2. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/pyproject.toml +2 -2
  3. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/config.py +4 -0
  4. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +41 -21
  5. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/unique_ai_builder.py +40 -4
  6. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/README.md +0 -0
  7. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/__init__.py +0 -0
  8. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/_builders/__init__.py +0 -0
  9. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
  10. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/_builders/loop_iteration_runner.py +0 -0
  11. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/_builders/open_file_setup.py +0 -0
  12. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/_builders/skill_setup.py +0 -0
  13. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
  14. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
  15. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
  16. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/settings.py +0 -0
  17. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_agent_config_rjsf_ui_schema.py +0 -0
  18. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +0 -0
  19. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
  20. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
  21. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
  22. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
  23. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +0 -0
  24. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_get_filtered_user_metadata.py +0 -0
  25. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
  26. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_loop_debug_params.py +0 -0
  27. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
  28. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_reference_order.py +0 -0
  29. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_unique_ai_render_system_prompt_user_instructions.py +0 -0
  30. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/tests/test_utils_resolve_other_options.py +0 -0
  31. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/unique_orchestrator/unique_ai.py +0 -0
  32. {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev1}/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.dev0
3
+ Version: 2026.30.0.dev1
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.dev0,<2026.30.0rc0
12
+ Requires-Dist: unique-toolkit>=2026.30.0.dev3,<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.dev0"
3
+ version = "2026.30.0.dev1"
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.dev0,<2026.30.0rc0",
16
+ "unique-toolkit>=2026.30.0.dev3,<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",
@@ -457,6 +457,10 @@ class ExperimentalConfig(BaseToolConfig):
457
457
  description="If set, the main agent will use the Responses API from OpenAI",
458
458
  )
459
459
 
460
+ use_experimental_python_streaming: bool = Field(
461
+ default=False,
462
+ )
463
+
460
464
 
461
465
  class UniqueAIAgentConfig(BaseToolConfig):
462
466
  max_loop_iterations: Annotated[
@@ -9,7 +9,10 @@ from unittest.mock import AsyncMock, MagicMock
9
9
  import pytest
10
10
  from unique_toolkit.agentic.tools.tool_manager import ResponsesApiToolManager
11
11
 
12
- from unique_orchestrator.unique_ai_builder import ResponsesStreamingHandler
12
+ from unique_orchestrator.unique_ai_builder import (
13
+ ResponsesStreamingHandler,
14
+ _ChatServiceResponsesStreaming,
15
+ )
13
16
 
14
17
 
15
18
  def _make_handler(
@@ -17,15 +20,11 @@ def _make_handler(
17
20
  ) -> ResponsesStreamingHandler:
18
21
  tool_manager = MagicMock(spec=ResponsesApiToolManager)
19
22
  tool_manager.get_required_include_params.return_value = include_params
20
- chat_service = MagicMock()
21
- chat_service.complete_responses_with_references_async = AsyncMock(
22
- return_value=MagicMock()
23
- )
24
- chat_service.complete_responses_with_references = MagicMock(
25
- return_value=MagicMock()
26
- )
23
+ inner = MagicMock()
24
+ inner.complete_with_references_async = AsyncMock(return_value=MagicMock())
25
+ inner.complete_with_references = MagicMock(return_value=MagicMock())
27
26
  return ResponsesStreamingHandler(
28
- chat_service=chat_service,
27
+ inner=inner,
29
28
  tool_manager=tool_manager,
30
29
  )
31
30
 
@@ -45,9 +44,7 @@ async def test_responses_streaming_handler__injects_include__when_tool_manager_r
45
44
 
46
45
  await handler.complete_with_references_async(messages=[], model_name="model")
47
46
 
48
- call_kwargs = (
49
- handler._chat_service.complete_responses_with_references_async.await_args.kwargs
50
- )
47
+ call_kwargs = handler._inner.complete_with_references_async.await_args.kwargs
51
48
  assert call_kwargs["include"] == ["code_interpreter_call.outputs"]
52
49
  handler._tool_manager.get_required_include_params.assert_called_once()
53
50
 
@@ -66,9 +63,7 @@ async def test_responses_streaming_handler__omits_include__when_tool_manager_ret
66
63
 
67
64
  await handler.complete_with_references_async(messages=[], model_name="model")
68
65
 
69
- call_kwargs = (
70
- handler._chat_service.complete_responses_with_references_async.await_args.kwargs
71
- )
66
+ call_kwargs = handler._inner.complete_with_references_async.await_args.kwargs
72
67
  assert "include" not in call_kwargs
73
68
 
74
69
 
@@ -81,9 +76,7 @@ def test_responses_streaming_handler__injects_include__sync_path() -> None:
81
76
 
82
77
  handler.complete_with_references(messages=[], model_name="model")
83
78
 
84
- call_kwargs = (
85
- handler._chat_service.complete_responses_with_references.call_args.kwargs
86
- )
79
+ call_kwargs = handler._inner.complete_with_references.call_args.kwargs
87
80
  assert call_kwargs["include"] == ["code_interpreter_call.outputs"]
88
81
 
89
82
 
@@ -94,7 +87,34 @@ def test_responses_streaming_handler__omits_include__sync_path_when_empty() -> N
94
87
 
95
88
  handler.complete_with_references(messages=[], model_name="model")
96
89
 
97
- call_kwargs = (
98
- handler._chat_service.complete_responses_with_references.call_args.kwargs
99
- )
90
+ call_kwargs = handler._inner.complete_with_references.call_args.kwargs
100
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()
@@ -65,9 +65,13 @@ from unique_toolkit.agentic.tools.tool_manager import (
65
65
  )
66
66
  from unique_toolkit.agentic.tools.tool_progress_reporter import ToolProgressReporter
67
67
  from unique_toolkit.app.schemas import ChatEvent, McpServer
68
+ from unique_toolkit.app.unique_settings import UniqueSettings
68
69
  from unique_toolkit.chat.service import ChatService
69
70
  from unique_toolkit.content import Content
70
71
  from unique_toolkit.content.service import ContentService
72
+ from unique_toolkit.experimental.integrations.openai.streaming.event_routing import (
73
+ ResponsesCompleteWithReferences,
74
+ )
71
75
  from unique_toolkit.language_model.infos import LanguageModelInfo, ModelCapabilities
72
76
  from unique_toolkit.protocols.support import ResponsesSupportCompleteWithReferences
73
77
  from unique_user_memory.user_memory import load_user_memory
@@ -99,14 +103,19 @@ class ResponsesStreamingHandler(ResponsesSupportCompleteWithReferences):
99
103
 
100
104
  Injects `include` params from the tool manager on every call so the
101
105
  orchestrator loop stays generic (no isinstance checks in UniqueAI).
106
+
107
+ Delegates the actual streaming to an injected
108
+ `ResponsesSupportCompleteWithReferences` (the event-routing
109
+ `ResponsesCompleteWithReferences` orchestrator), so this class only owns
110
+ the `include` injection.
102
111
  """
103
112
 
104
113
  def __init__(
105
114
  self,
106
- chat_service: ChatService,
115
+ inner: ResponsesSupportCompleteWithReferences,
107
116
  tool_manager: ResponsesApiToolManager,
108
117
  ) -> None:
109
- self._chat_service: ChatService = chat_service
118
+ self._inner: ResponsesSupportCompleteWithReferences = inner
110
119
  self._tool_manager: ResponsesApiToolManager = tool_manager
111
120
 
112
121
  @override
@@ -114,13 +123,26 @@ class ResponsesStreamingHandler(ResponsesSupportCompleteWithReferences):
114
123
  include = self._tool_manager.get_required_include_params()
115
124
  if include:
116
125
  kwargs["include"] = include
117
- return self._chat_service.complete_responses_with_references(*args, **kwargs)
126
+ return self._inner.complete_with_references(*args, **kwargs)
118
127
 
119
128
  @override
120
129
  async def complete_with_references_async(self, *args: Any, **kwargs: Any) -> Any:
121
130
  include = self._tool_manager.get_required_include_params()
122
131
  if include:
123
132
  kwargs["include"] = include
133
+ return await self._inner.complete_with_references_async(*args, **kwargs)
134
+
135
+
136
+ class _ChatServiceResponsesStreaming(ResponsesSupportCompleteWithReferences):
137
+ def __init__(self, chat_service: ChatService) -> None:
138
+ self._chat_service: ChatService = chat_service
139
+
140
+ @override
141
+ def complete_with_references(self, *args: Any, **kwargs: Any) -> Any:
142
+ return self._chat_service.complete_responses_with_references(*args, **kwargs)
143
+
144
+ @override
145
+ async def complete_with_references_async(self, *args: Any, **kwargs: Any) -> Any:
124
146
  return await self._chat_service.complete_responses_with_references_async(
125
147
  *args, **kwargs
126
148
  )
@@ -572,8 +594,22 @@ async def _build_responses(
572
594
  openai_client=client,
573
595
  )
574
596
 
597
+ if config.agent.experimental.use_experimental_python_streaming:
598
+ inner: ResponsesSupportCompleteWithReferences = ResponsesCompleteWithReferences(
599
+ UniqueSettings.from_chat_event(event),
600
+ additional_headers={
601
+ "x-model": config.space.language_model.name,
602
+ "x-user-id": event.user_id,
603
+ "x-company-id": event.company_id,
604
+ "x-assistant-id": event.payload.assistant_id,
605
+ "x-chat-id": event.payload.chat_id,
606
+ },
607
+ )
608
+ else:
609
+ inner = _ChatServiceResponsesStreaming(common_components.chat_service)
610
+
575
611
  streaming_handler = ResponsesStreamingHandler(
576
- chat_service=common_components.chat_service,
612
+ inner=inner,
577
613
  tool_manager=tool_manager,
578
614
  )
579
615