unique_orchestrator 2026.30.0.dev0__tar.gz → 2026.30.0.dev2__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.dev0 → unique_orchestrator-2026.30.0.dev2}/PKG-INFO +3 -3
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/pyproject.toml +3 -3
- unique_orchestrator-2026.30.0.dev2/unique_orchestrator/_builders/history_manager.py +97 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/_builders/open_file_setup.py +5 -43
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/config.py +9 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_plan_or_execute_include.py +41 -21
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/unique_ai_builder.py +98 -55
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/README.md +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/__init__.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/_builders/__init__.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/_builders/inject_tool_reminders.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/_builders/loop_iteration_runner.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/_builders/skill_setup.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/prompts/generic_reference_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/prompts/system_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/prompts/user_message_prompt.jinja2 +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/settings.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_agent_config_rjsf_ui_schema.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_build_loop_iteration_runner.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_config_services_validators.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_qwen_max_loop_iterations.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_register_code_interpreter_postprocessors.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_async_modify.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_execution_timing.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_get_filtered_user_metadata.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_log_tool_calls.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_loop_debug_params.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_persist_tool_calls.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_reference_order.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_unique_ai_render_system_prompt_user_instructions.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/tests/test_utils_resolve_other_options.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/unique_orchestrator/unique_ai.py +0 -0
- {unique_orchestrator-2026.30.0.dev0 → unique_orchestrator-2026.30.0.dev2}/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.dev2
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Andreas Hauri
|
|
6
6
|
Author-email: Andreas Hauri <andreas.hauri@unique.ai>
|
|
@@ -9,12 +9,12 @@ 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.dev13,<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
|
|
16
16
|
Requires-Dist: unique-deep-research>=2026.30.0.dev0,<2026.30.0rc0
|
|
17
|
-
Requires-Dist: unique-web-search>=2026.30.0.
|
|
17
|
+
Requires-Dist: unique-web-search>=2026.30.0.dev2,<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
20
|
Requires-Dist: unique-user-memory>=2026.30.0.dev0,<2026.30.0rc0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "unique_orchestrator"
|
|
3
|
-
version = "2026.30.0.
|
|
3
|
+
version = "2026.30.0.dev2"
|
|
4
4
|
description = ""
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "Proprietary" }
|
|
@@ -13,12 +13,12 @@ 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.dev13,<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",
|
|
20
20
|
"unique-deep-research>=2026.30.0.dev0,<2026.30.0rc0",
|
|
21
|
-
"unique-web-search>=2026.30.0.
|
|
21
|
+
"unique-web-search>=2026.30.0.dev2,<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
24
|
"unique-user-memory>=2026.30.0.dev0,<2026.30.0rc0",
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
from logging import Logger
|
|
3
|
+
|
|
4
|
+
from unique_internal_search.uploaded_search.service import UploadedSearchTool
|
|
5
|
+
from unique_toolkit.agentic.history_manager import (
|
|
6
|
+
history_manager as history_manager_module,
|
|
7
|
+
)
|
|
8
|
+
from unique_toolkit.agentic.history_manager.history_construction_with_contents import (
|
|
9
|
+
FileContentSerializer,
|
|
10
|
+
)
|
|
11
|
+
from unique_toolkit.agentic.history_manager.history_manager import (
|
|
12
|
+
HistoryManager,
|
|
13
|
+
HistoryManagerConfig,
|
|
14
|
+
)
|
|
15
|
+
from unique_toolkit.agentic.reference_manager.reference_manager import ReferenceManager
|
|
16
|
+
from unique_toolkit.agentic.tools.openai_builtin.base import OpenAIBuiltInToolName
|
|
17
|
+
from unique_toolkit.agentic.tools.openai_builtin.code_interpreter.postprocessors.artifacts import (
|
|
18
|
+
load_code_execution_metadata,
|
|
19
|
+
)
|
|
20
|
+
from unique_toolkit.agentic.tools.tool_manager import (
|
|
21
|
+
ResponsesApiToolManager,
|
|
22
|
+
ToolManager,
|
|
23
|
+
)
|
|
24
|
+
from unique_toolkit.app.schemas import ChatEvent
|
|
25
|
+
from unique_toolkit.content import Content
|
|
26
|
+
|
|
27
|
+
from unique_orchestrator.config import UniqueAIConfig
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def serialize_uploaded_file_for_history(
|
|
31
|
+
content: Content,
|
|
32
|
+
*,
|
|
33
|
+
uploaded_search_available: bool,
|
|
34
|
+
code_interpreter_available: bool,
|
|
35
|
+
) -> str | None:
|
|
36
|
+
"""Serialize user-uploaded file metadata for model history."""
|
|
37
|
+
if load_code_execution_metadata(content) is not None:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
lines = [f"User uploaded file: {content.key} ({content.id})"]
|
|
41
|
+
if (
|
|
42
|
+
uploaded_search_available
|
|
43
|
+
and content.is_ingested(default_if_unknown=True)
|
|
44
|
+
and not content.is_expired()
|
|
45
|
+
):
|
|
46
|
+
lines.append("- Searchable using UploadedSearchTool")
|
|
47
|
+
if code_interpreter_available:
|
|
48
|
+
lines.append("- Available for processing in the code execution container")
|
|
49
|
+
return "\n".join(lines)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _get_file_content_serializer(
|
|
53
|
+
config: UniqueAIConfig,
|
|
54
|
+
tool_manager: ToolManager | ResponsesApiToolManager,
|
|
55
|
+
) -> FileContentSerializer | None:
|
|
56
|
+
if not config.agent.input_token_distribution.serialize_uploaded_files_in_user_message:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
return partial(
|
|
60
|
+
serialize_uploaded_file_for_history,
|
|
61
|
+
uploaded_search_available=(
|
|
62
|
+
tool_manager.get_tool_by_name(UploadedSearchTool.name) is not None
|
|
63
|
+
),
|
|
64
|
+
code_interpreter_available=(
|
|
65
|
+
tool_manager.get_tool_by_name(OpenAIBuiltInToolName.CODE_INTERPRETER)
|
|
66
|
+
is not None
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def build_history_manager(
|
|
72
|
+
*,
|
|
73
|
+
event: ChatEvent,
|
|
74
|
+
logger: Logger,
|
|
75
|
+
config: UniqueAIConfig,
|
|
76
|
+
reference_manager: ReferenceManager,
|
|
77
|
+
tool_manager: ToolManager | ResponsesApiToolManager,
|
|
78
|
+
) -> HistoryManager:
|
|
79
|
+
"""Build a history manager after the active tools are known."""
|
|
80
|
+
history_manager_config = HistoryManagerConfig(
|
|
81
|
+
experimental_features=history_manager_module.ExperimentalFeatures(),
|
|
82
|
+
percent_of_max_tokens_for_history=config.agent.input_token_distribution.percent_for_history,
|
|
83
|
+
language_model=config.space.language_model,
|
|
84
|
+
uploaded_content_config=config.agent.services.uploaded_content_config,
|
|
85
|
+
enable_tool_call_persistence=config.agent.input_token_distribution.enable_tool_call_persistence,
|
|
86
|
+
)
|
|
87
|
+
return HistoryManager(
|
|
88
|
+
logger,
|
|
89
|
+
event,
|
|
90
|
+
history_manager_config,
|
|
91
|
+
config.space.language_model,
|
|
92
|
+
reference_manager,
|
|
93
|
+
file_content_serializer=_get_file_content_serializer(
|
|
94
|
+
config=config,
|
|
95
|
+
tool_manager=tool_manager,
|
|
96
|
+
),
|
|
97
|
+
)
|
|
@@ -8,30 +8,16 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from datetime import datetime, timezone
|
|
10
10
|
from logging import Logger
|
|
11
|
-
from typing import TYPE_CHECKING
|
|
12
11
|
|
|
13
|
-
from unique_toolkit.agentic.history_manager import (
|
|
14
|
-
history_manager as history_manager_module,
|
|
15
|
-
)
|
|
16
|
-
from unique_toolkit.agentic.history_manager.history_manager import (
|
|
17
|
-
HistoryManager,
|
|
18
|
-
HistoryManagerConfig,
|
|
19
|
-
)
|
|
20
12
|
from unique_toolkit.agentic.tools.experimental.open_file_tool import OpenFileTool
|
|
21
13
|
from unique_toolkit.agentic.tools.tool_manager import (
|
|
22
14
|
ResponsesApiToolManager,
|
|
23
15
|
)
|
|
24
16
|
from unique_toolkit.app.schemas import ChatEvent
|
|
25
17
|
from unique_toolkit.content import Content
|
|
26
|
-
from unique_toolkit.language_model.infos import LanguageModelInfo
|
|
27
18
|
|
|
28
19
|
from unique_orchestrator.config import UniqueAIConfig
|
|
29
20
|
|
|
30
|
-
if TYPE_CHECKING:
|
|
31
|
-
from unique_toolkit.agentic.reference_manager.reference_manager import (
|
|
32
|
-
ReferenceManager,
|
|
33
|
-
)
|
|
34
|
-
|
|
35
21
|
|
|
36
22
|
def handle_uploaded_file_tool_choices(
|
|
37
23
|
config: UniqueAIConfig,
|
|
@@ -76,39 +62,15 @@ def handle_uploaded_file_tool_choices(
|
|
|
76
62
|
def configure_file_payload(
|
|
77
63
|
config: UniqueAIConfig,
|
|
78
64
|
event: ChatEvent,
|
|
79
|
-
logger: Logger,
|
|
80
|
-
history_manager: HistoryManager,
|
|
81
|
-
reference_manager: ReferenceManager,
|
|
82
|
-
language_model: LanguageModelInfo,
|
|
83
65
|
tool_manager: ResponsesApiToolManager,
|
|
84
|
-
) ->
|
|
66
|
+
) -> list[str]:
|
|
85
67
|
"""Configure file-in-payload handling for the Responses API.
|
|
86
68
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
(which would duplicate what the input_file parts already provide).
|
|
69
|
+
Registers the OpenFileTool when send_files_in_payload is enabled, backed by
|
|
70
|
+
a shared registry for agent-requested KB file IDs.
|
|
90
71
|
|
|
91
|
-
|
|
92
|
-
backed by a shared registry for agent-requested KB file IDs.
|
|
93
|
-
|
|
94
|
-
Returns the (possibly updated) history_manager and the agent file registry.
|
|
72
|
+
Returns the registry shared with the OpenFileTool.
|
|
95
73
|
"""
|
|
96
|
-
if config.agent.experimental.open_file_tool_config.send_uploaded_files_in_payload:
|
|
97
|
-
upload_free_config = HistoryManagerConfig(
|
|
98
|
-
experimental_features=history_manager_module.ExperimentalFeatures(),
|
|
99
|
-
percent_of_max_tokens_for_history=config.agent.input_token_distribution.percent_for_history,
|
|
100
|
-
language_model=language_model,
|
|
101
|
-
uploaded_content_config=None,
|
|
102
|
-
enable_tool_call_persistence=config.agent.input_token_distribution.enable_tool_call_persistence,
|
|
103
|
-
)
|
|
104
|
-
history_manager = HistoryManager(
|
|
105
|
-
logger,
|
|
106
|
-
event,
|
|
107
|
-
upload_free_config,
|
|
108
|
-
language_model,
|
|
109
|
-
reference_manager,
|
|
110
|
-
)
|
|
111
|
-
|
|
112
74
|
agent_file_registry: list[str] = []
|
|
113
75
|
|
|
114
76
|
if config.agent.experimental.open_file_tool_config.send_files_in_payload:
|
|
@@ -120,4 +82,4 @@ def configure_file_payload(
|
|
|
120
82
|
)
|
|
121
83
|
)
|
|
122
84
|
|
|
123
|
-
return
|
|
85
|
+
return agent_file_registry
|
|
@@ -348,6 +348,11 @@ class HistoryConfig(BaseToolConfig):
|
|
|
348
348
|
description="Persist tool calls and reconstruct tool call history across turns.",
|
|
349
349
|
)
|
|
350
350
|
|
|
351
|
+
serialize_uploaded_files_in_user_message: bool = Field(
|
|
352
|
+
default=False,
|
|
353
|
+
description="Include an indication of each uploaded file in the user message.",
|
|
354
|
+
)
|
|
355
|
+
|
|
351
356
|
def max_history_tokens(self, max_input_token: int) -> int:
|
|
352
357
|
return int(self.percent_for_history * max_input_token)
|
|
353
358
|
|
|
@@ -457,6 +462,10 @@ class ExperimentalConfig(BaseToolConfig):
|
|
|
457
462
|
description="If set, the main agent will use the Responses API from OpenAI",
|
|
458
463
|
)
|
|
459
464
|
|
|
465
|
+
use_experimental_python_streaming: bool = Field(
|
|
466
|
+
default=False,
|
|
467
|
+
)
|
|
468
|
+
|
|
460
469
|
|
|
461
470
|
class UniqueAIAgentConfig(BaseToolConfig):
|
|
462
471
|
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
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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()
|
|
@@ -20,13 +20,7 @@ from unique_toolkit.agentic.evaluation.evaluation_manager import EvaluationManag
|
|
|
20
20
|
from unique_toolkit.agentic.evaluation.hallucination.hallucination_evaluation import (
|
|
21
21
|
HallucinationEvaluation,
|
|
22
22
|
)
|
|
23
|
-
from unique_toolkit.agentic.history_manager import
|
|
24
|
-
history_manager as history_manager_module,
|
|
25
|
-
)
|
|
26
|
-
from unique_toolkit.agentic.history_manager.history_manager import (
|
|
27
|
-
HistoryManager,
|
|
28
|
-
HistoryManagerConfig,
|
|
29
|
-
)
|
|
23
|
+
from unique_toolkit.agentic.history_manager.history_manager import HistoryManager
|
|
30
24
|
from unique_toolkit.agentic.message_log_manager.service import MessageStepLogger
|
|
31
25
|
from unique_toolkit.agentic.postprocessor.postprocessor_manager import (
|
|
32
26
|
PostprocessorManager,
|
|
@@ -65,9 +59,13 @@ from unique_toolkit.agentic.tools.tool_manager import (
|
|
|
65
59
|
)
|
|
66
60
|
from unique_toolkit.agentic.tools.tool_progress_reporter import ToolProgressReporter
|
|
67
61
|
from unique_toolkit.app.schemas import ChatEvent, McpServer
|
|
62
|
+
from unique_toolkit.app.unique_settings import UniqueSettings
|
|
68
63
|
from unique_toolkit.chat.service import ChatService
|
|
69
64
|
from unique_toolkit.content import Content
|
|
70
65
|
from unique_toolkit.content.service import ContentService
|
|
66
|
+
from unique_toolkit.experimental.integrations.openai.streaming.event_routing import (
|
|
67
|
+
ResponsesCompleteWithReferences,
|
|
68
|
+
)
|
|
71
69
|
from unique_toolkit.language_model.infos import LanguageModelInfo, ModelCapabilities
|
|
72
70
|
from unique_toolkit.protocols.support import ResponsesSupportCompleteWithReferences
|
|
73
71
|
from unique_user_memory.user_memory import load_user_memory
|
|
@@ -77,6 +75,7 @@ from unique_orchestrator._builders import (
|
|
|
77
75
|
build_loop_iteration_runner,
|
|
78
76
|
build_responses_loop_iteration_runner,
|
|
79
77
|
)
|
|
78
|
+
from unique_orchestrator._builders.history_manager import build_history_manager
|
|
80
79
|
from unique_orchestrator._builders.open_file_setup import (
|
|
81
80
|
configure_file_payload,
|
|
82
81
|
handle_uploaded_file_tool_choices,
|
|
@@ -94,19 +93,45 @@ from unique_orchestrator.unique_ai import UniqueAI
|
|
|
94
93
|
from unique_orchestrator.utils import filter_uploaded_documents_by_selection
|
|
95
94
|
|
|
96
95
|
|
|
96
|
+
def _configure_follow_up_questions(
|
|
97
|
+
*,
|
|
98
|
+
config: UniqueAIConfig,
|
|
99
|
+
event: ChatEvent,
|
|
100
|
+
logger: Logger,
|
|
101
|
+
history_manager: HistoryManager,
|
|
102
|
+
postprocessor_manager: PostprocessorManager,
|
|
103
|
+
) -> None:
|
|
104
|
+
follow_up_config = config.agent.services.follow_up_questions_config
|
|
105
|
+
if follow_up_config and follow_up_config.number_of_questions > 0:
|
|
106
|
+
postprocessor_manager.set_last_postprocessor(
|
|
107
|
+
FollowUpPostprocessor(
|
|
108
|
+
logger=logger,
|
|
109
|
+
config=follow_up_config,
|
|
110
|
+
event=event,
|
|
111
|
+
historyManager=history_manager,
|
|
112
|
+
llm_service=LanguageModelService.from_event(event),
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
97
117
|
class ResponsesStreamingHandler(ResponsesSupportCompleteWithReferences):
|
|
98
118
|
"""Streaming handler for Responses API runs.
|
|
99
119
|
|
|
100
120
|
Injects `include` params from the tool manager on every call so the
|
|
101
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.
|
|
102
127
|
"""
|
|
103
128
|
|
|
104
129
|
def __init__(
|
|
105
130
|
self,
|
|
106
|
-
|
|
131
|
+
inner: ResponsesSupportCompleteWithReferences,
|
|
107
132
|
tool_manager: ResponsesApiToolManager,
|
|
108
133
|
) -> None:
|
|
109
|
-
self.
|
|
134
|
+
self._inner: ResponsesSupportCompleteWithReferences = inner
|
|
110
135
|
self._tool_manager: ResponsesApiToolManager = tool_manager
|
|
111
136
|
|
|
112
137
|
@override
|
|
@@ -114,13 +139,26 @@ class ResponsesStreamingHandler(ResponsesSupportCompleteWithReferences):
|
|
|
114
139
|
include = self._tool_manager.get_required_include_params()
|
|
115
140
|
if include:
|
|
116
141
|
kwargs["include"] = include
|
|
117
|
-
return self.
|
|
142
|
+
return self._inner.complete_with_references(*args, **kwargs)
|
|
118
143
|
|
|
119
144
|
@override
|
|
120
145
|
async def complete_with_references_async(self, *args: Any, **kwargs: Any) -> Any:
|
|
121
146
|
include = self._tool_manager.get_required_include_params()
|
|
122
147
|
if include:
|
|
123
148
|
kwargs["include"] = include
|
|
149
|
+
return await self._inner.complete_with_references_async(*args, **kwargs)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class _ChatServiceResponsesStreaming(ResponsesSupportCompleteWithReferences):
|
|
153
|
+
def __init__(self, chat_service: ChatService) -> None:
|
|
154
|
+
self._chat_service: ChatService = chat_service
|
|
155
|
+
|
|
156
|
+
@override
|
|
157
|
+
def complete_with_references(self, *args: Any, **kwargs: Any) -> Any:
|
|
158
|
+
return self._chat_service.complete_responses_with_references(*args, **kwargs)
|
|
159
|
+
|
|
160
|
+
@override
|
|
161
|
+
async def complete_with_references_async(self, *args: Any, **kwargs: Any) -> Any:
|
|
124
162
|
return await self._chat_service.complete_responses_with_references_async(
|
|
125
163
|
*args, **kwargs
|
|
126
164
|
)
|
|
@@ -168,7 +206,6 @@ class _CommonComponents(NamedTuple):
|
|
|
168
206
|
uploaded_images: list[Content]
|
|
169
207
|
thinking_manager: ThinkingManager
|
|
170
208
|
reference_manager: ReferenceManager
|
|
171
|
-
history_manager: HistoryManager
|
|
172
209
|
evaluation_manager: EvaluationManager
|
|
173
210
|
postprocessor_manager: PostprocessorManager
|
|
174
211
|
message_step_logger: MessageStepLogger
|
|
@@ -290,21 +327,6 @@ async def _build_common(
|
|
|
290
327
|
|
|
291
328
|
reference_manager = ReferenceManager()
|
|
292
329
|
|
|
293
|
-
history_manager_config = HistoryManagerConfig(
|
|
294
|
-
experimental_features=history_manager_module.ExperimentalFeatures(),
|
|
295
|
-
percent_of_max_tokens_for_history=config.agent.input_token_distribution.percent_for_history,
|
|
296
|
-
language_model=config.space.language_model,
|
|
297
|
-
uploaded_content_config=config.agent.services.uploaded_content_config,
|
|
298
|
-
enable_tool_call_persistence=config.agent.input_token_distribution.enable_tool_call_persistence,
|
|
299
|
-
)
|
|
300
|
-
history_manager = HistoryManager(
|
|
301
|
-
logger,
|
|
302
|
-
event,
|
|
303
|
-
history_manager_config,
|
|
304
|
-
config.space.language_model,
|
|
305
|
-
reference_manager,
|
|
306
|
-
)
|
|
307
|
-
|
|
308
330
|
evaluation_manager = EvaluationManager(logger=logger, chat_service=chat_service)
|
|
309
331
|
if config.agent.services.evaluation_config:
|
|
310
332
|
evaluation_manager.add_evaluation(
|
|
@@ -348,21 +370,6 @@ async def _build_common(
|
|
|
348
370
|
)
|
|
349
371
|
)
|
|
350
372
|
|
|
351
|
-
if (
|
|
352
|
-
config.agent.services.follow_up_questions_config
|
|
353
|
-
and config.agent.services.follow_up_questions_config.number_of_questions > 0
|
|
354
|
-
):
|
|
355
|
-
# Should run last to make sure the follow up questions are displayed last.
|
|
356
|
-
postprocessor_manager.set_last_postprocessor(
|
|
357
|
-
FollowUpPostprocessor(
|
|
358
|
-
logger=logger,
|
|
359
|
-
config=config.agent.services.follow_up_questions_config,
|
|
360
|
-
event=event,
|
|
361
|
-
historyManager=history_manager,
|
|
362
|
-
llm_service=LanguageModelService.from_event(event),
|
|
363
|
-
)
|
|
364
|
-
)
|
|
365
|
-
|
|
366
373
|
user_memory_text = ""
|
|
367
374
|
if config.space.allow_user_memory:
|
|
368
375
|
user_memory_config = config.agent.services.user_memory_config
|
|
@@ -401,7 +408,6 @@ async def _build_common(
|
|
|
401
408
|
uploaded_images=uploaded_images,
|
|
402
409
|
thinking_manager=thinking_manager,
|
|
403
410
|
reference_manager=reference_manager,
|
|
404
|
-
history_manager=history_manager,
|
|
405
411
|
evaluation_manager=evaluation_manager,
|
|
406
412
|
tool_progress_reporter=tool_progress_reporter,
|
|
407
413
|
mcp_manager=mcp_manager,
|
|
@@ -543,18 +549,26 @@ async def _build_responses(
|
|
|
543
549
|
|
|
544
550
|
agent_file_registry: list[str] = []
|
|
545
551
|
if config.agent.experimental.open_file_tool_config.enabled:
|
|
546
|
-
|
|
552
|
+
agent_file_registry = configure_file_payload(
|
|
547
553
|
config,
|
|
548
554
|
event,
|
|
549
|
-
logger,
|
|
550
|
-
common_components.history_manager,
|
|
551
|
-
common_components.reference_manager,
|
|
552
|
-
config.space.language_model,
|
|
553
555
|
tool_manager,
|
|
554
556
|
)
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
557
|
+
|
|
558
|
+
history_manager = build_history_manager(
|
|
559
|
+
event=event,
|
|
560
|
+
logger=logger,
|
|
561
|
+
config=config,
|
|
562
|
+
reference_manager=common_components.reference_manager,
|
|
563
|
+
tool_manager=tool_manager,
|
|
564
|
+
)
|
|
565
|
+
_configure_follow_up_questions(
|
|
566
|
+
config=config,
|
|
567
|
+
event=event,
|
|
568
|
+
logger=logger,
|
|
569
|
+
history_manager=history_manager,
|
|
570
|
+
postprocessor_manager=postprocessor_manager,
|
|
571
|
+
)
|
|
558
572
|
|
|
559
573
|
await configure_skill_tool(
|
|
560
574
|
config=config,
|
|
@@ -568,12 +582,26 @@ async def _build_responses(
|
|
|
568
582
|
|
|
569
583
|
loop_iteration_runner = build_responses_loop_iteration_runner(
|
|
570
584
|
config=config,
|
|
571
|
-
history_manager=
|
|
585
|
+
history_manager=history_manager,
|
|
572
586
|
openai_client=client,
|
|
573
587
|
)
|
|
574
588
|
|
|
589
|
+
if config.agent.experimental.use_experimental_python_streaming:
|
|
590
|
+
inner: ResponsesSupportCompleteWithReferences = ResponsesCompleteWithReferences(
|
|
591
|
+
UniqueSettings.from_chat_event(event),
|
|
592
|
+
additional_headers={
|
|
593
|
+
"x-model": config.space.language_model.name,
|
|
594
|
+
"x-user-id": event.user_id,
|
|
595
|
+
"x-company-id": event.company_id,
|
|
596
|
+
"x-assistant-id": event.payload.assistant_id,
|
|
597
|
+
"x-chat-id": event.payload.chat_id,
|
|
598
|
+
},
|
|
599
|
+
)
|
|
600
|
+
else:
|
|
601
|
+
inner = _ChatServiceResponsesStreaming(common_components.chat_service)
|
|
602
|
+
|
|
575
603
|
streaming_handler = ResponsesStreamingHandler(
|
|
576
|
-
|
|
604
|
+
inner=inner,
|
|
577
605
|
tool_manager=tool_manager,
|
|
578
606
|
)
|
|
579
607
|
|
|
@@ -601,7 +629,7 @@ async def _build_responses(
|
|
|
601
629
|
tool_manager=tool_manager,
|
|
602
630
|
thinking_manager=common_components.thinking_manager,
|
|
603
631
|
streaming_handler=streaming_handler,
|
|
604
|
-
history_manager=
|
|
632
|
+
history_manager=history_manager,
|
|
605
633
|
reference_manager=common_components.reference_manager,
|
|
606
634
|
evaluation_manager=common_components.evaluation_manager,
|
|
607
635
|
postprocessor_manager=postprocessor_manager,
|
|
@@ -641,6 +669,21 @@ async def _build_completions(
|
|
|
641
669
|
if force_uploaded_search:
|
|
642
670
|
tool_manager.add_forced_tool(UploadedSearchTool.name)
|
|
643
671
|
|
|
672
|
+
history_manager = build_history_manager(
|
|
673
|
+
event=event,
|
|
674
|
+
logger=logger,
|
|
675
|
+
config=config,
|
|
676
|
+
reference_manager=common_components.reference_manager,
|
|
677
|
+
tool_manager=tool_manager,
|
|
678
|
+
)
|
|
679
|
+
_configure_follow_up_questions(
|
|
680
|
+
config=config,
|
|
681
|
+
event=event,
|
|
682
|
+
logger=logger,
|
|
683
|
+
history_manager=history_manager,
|
|
684
|
+
postprocessor_manager=common_components.postprocessor_manager,
|
|
685
|
+
)
|
|
686
|
+
|
|
644
687
|
await configure_skill_tool(
|
|
645
688
|
config=config,
|
|
646
689
|
logger=logger,
|
|
@@ -669,7 +712,7 @@ async def _build_completions(
|
|
|
669
712
|
|
|
670
713
|
loop_iteration_runner = build_loop_iteration_runner(
|
|
671
714
|
config=config,
|
|
672
|
-
history_manager=
|
|
715
|
+
history_manager=history_manager,
|
|
673
716
|
llm_service=common_components.llm_service,
|
|
674
717
|
chat_service=common_components.chat_service,
|
|
675
718
|
)
|
|
@@ -683,7 +726,7 @@ async def _build_completions(
|
|
|
683
726
|
uploaded_documents=common_components.uploaded_documents,
|
|
684
727
|
tool_manager=tool_manager,
|
|
685
728
|
thinking_manager=common_components.thinking_manager,
|
|
686
|
-
history_manager=
|
|
729
|
+
history_manager=history_manager,
|
|
687
730
|
reference_manager=common_components.reference_manager,
|
|
688
731
|
streaming_handler=common_components.chat_service,
|
|
689
732
|
evaluation_manager=common_components.evaluation_manager,
|
|
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
|