unique_toolkit 1.8.1__py3-none-any.whl → 1.23.0__py3-none-any.whl
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.
Potentially problematic release.
This version of unique_toolkit might be problematic. Click here for more details.
- unique_toolkit/__init__.py +20 -0
- unique_toolkit/_common/api_calling/human_verification_manager.py +121 -28
- unique_toolkit/_common/chunk_relevancy_sorter/config.py +3 -3
- unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +2 -5
- unique_toolkit/_common/default_language_model.py +9 -3
- unique_toolkit/_common/docx_generator/__init__.py +7 -0
- unique_toolkit/_common/docx_generator/config.py +12 -0
- unique_toolkit/_common/docx_generator/schemas.py +80 -0
- unique_toolkit/_common/docx_generator/service.py +252 -0
- unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
- unique_toolkit/_common/endpoint_builder.py +138 -117
- unique_toolkit/_common/endpoint_requestor.py +240 -14
- unique_toolkit/_common/exception.py +20 -0
- unique_toolkit/_common/feature_flags/schema.py +1 -5
- unique_toolkit/_common/referencing.py +53 -0
- unique_toolkit/_common/string_utilities.py +52 -1
- unique_toolkit/_common/tests/test_referencing.py +521 -0
- unique_toolkit/_common/tests/test_string_utilities.py +506 -0
- unique_toolkit/_common/utils/files.py +43 -0
- unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +16 -6
- unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +278 -0
- unique_toolkit/agentic/evaluation/config.py +3 -2
- unique_toolkit/agentic/evaluation/context_relevancy/service.py +2 -2
- unique_toolkit/agentic/evaluation/evaluation_manager.py +9 -5
- unique_toolkit/agentic/evaluation/hallucination/constants.py +1 -1
- unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +26 -3
- unique_toolkit/agentic/history_manager/history_manager.py +14 -11
- unique_toolkit/agentic/history_manager/loop_token_reducer.py +3 -4
- unique_toolkit/agentic/history_manager/utils.py +10 -87
- unique_toolkit/agentic/postprocessor/postprocessor_manager.py +107 -16
- unique_toolkit/agentic/reference_manager/reference_manager.py +1 -1
- unique_toolkit/agentic/responses_api/__init__.py +19 -0
- unique_toolkit/agentic/responses_api/postprocessors/code_display.py +63 -0
- unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +145 -0
- unique_toolkit/agentic/responses_api/stream_handler.py +15 -0
- unique_toolkit/agentic/tools/a2a/__init__.py +18 -2
- unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +2 -0
- unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +3 -3
- unique_toolkit/agentic/tools/a2a/evaluation/config.py +1 -1
- unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +143 -91
- unique_toolkit/agentic/tools/a2a/manager.py +7 -1
- unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +11 -3
- unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +185 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +73 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/config.py +21 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/display.py +180 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/references.py +101 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +1335 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +603 -0
- unique_toolkit/agentic/tools/a2a/prompts.py +46 -0
- unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +6 -0
- unique_toolkit/agentic/tools/a2a/response_watcher/service.py +91 -0
- unique_toolkit/agentic/tools/a2a/tool/config.py +15 -5
- unique_toolkit/agentic/tools/a2a/tool/service.py +69 -36
- unique_toolkit/agentic/tools/config.py +16 -2
- unique_toolkit/agentic/tools/factory.py +4 -0
- unique_toolkit/agentic/tools/mcp/tool_wrapper.py +7 -35
- unique_toolkit/agentic/tools/openai_builtin/__init__.py +11 -0
- unique_toolkit/agentic/tools/openai_builtin/base.py +30 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +8 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +57 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +230 -0
- unique_toolkit/agentic/tools/openai_builtin/manager.py +62 -0
- unique_toolkit/agentic/tools/test/test_mcp_manager.py +95 -7
- unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +240 -0
- unique_toolkit/agentic/tools/tool.py +0 -11
- unique_toolkit/agentic/tools/tool_manager.py +337 -122
- unique_toolkit/agentic/tools/tool_progress_reporter.py +81 -15
- unique_toolkit/agentic/tools/utils/__init__.py +18 -0
- unique_toolkit/agentic/tools/utils/execution/execution.py +8 -4
- unique_toolkit/agentic/tools/utils/source_handling/schema.py +1 -1
- unique_toolkit/chat/__init__.py +8 -1
- unique_toolkit/chat/deprecated/service.py +232 -0
- unique_toolkit/chat/functions.py +54 -40
- unique_toolkit/chat/rendering.py +34 -0
- unique_toolkit/chat/responses_api.py +461 -0
- unique_toolkit/chat/schemas.py +1 -1
- unique_toolkit/chat/service.py +96 -1569
- unique_toolkit/content/functions.py +116 -1
- unique_toolkit/content/schemas.py +59 -0
- unique_toolkit/content/service.py +5 -37
- unique_toolkit/content/smart_rules.py +301 -0
- unique_toolkit/framework_utilities/langchain/client.py +27 -3
- unique_toolkit/framework_utilities/openai/client.py +12 -1
- unique_toolkit/framework_utilities/openai/message_builder.py +85 -1
- unique_toolkit/language_model/default_language_model.py +3 -0
- unique_toolkit/language_model/functions.py +25 -9
- unique_toolkit/language_model/infos.py +72 -4
- unique_toolkit/language_model/schemas.py +246 -40
- unique_toolkit/protocols/support.py +91 -9
- unique_toolkit/services/__init__.py +7 -0
- unique_toolkit/services/chat_service.py +1630 -0
- unique_toolkit/services/knowledge_base.py +861 -0
- unique_toolkit/smart_rules/compile.py +56 -301
- unique_toolkit/test_utilities/events.py +197 -0
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/METADATA +173 -3
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/RECORD +99 -67
- unique_toolkit/agentic/tools/a2a/postprocessing/_display.py +0 -122
- unique_toolkit/agentic/tools/a2a/postprocessing/_utils.py +0 -19
- unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +0 -230
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_consolidate_references.py +0 -665
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +0 -391
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +0 -256
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/LICENSE +0 -0
- {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/WHEEL +0 -0
|
@@ -6,6 +6,7 @@ from unique_toolkit.agentic.tools.utils.execution.execution import SafeTaskExecu
|
|
|
6
6
|
from unique_toolkit.chat.service import ChatService
|
|
7
7
|
from unique_toolkit.language_model.schemas import (
|
|
8
8
|
LanguageModelStreamResponse,
|
|
9
|
+
ResponsesLanguageModelStreamResponse,
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
|
|
@@ -26,7 +27,30 @@ class Postprocessor(ABC):
|
|
|
26
27
|
"Subclasses must implement this method to apply post-processing to the response."
|
|
27
28
|
)
|
|
28
29
|
|
|
29
|
-
async def remove_from_text(self, text) -> str:
|
|
30
|
+
async def remove_from_text(self, text: str) -> str:
|
|
31
|
+
raise NotImplementedError(
|
|
32
|
+
"Subclasses must implement this method to remove post-processing from the message."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ResponsesApiPostprocessor(ABC):
|
|
37
|
+
def __init__(self, name: str):
|
|
38
|
+
self.name = name
|
|
39
|
+
|
|
40
|
+
def get_name(self) -> str:
|
|
41
|
+
return self.name
|
|
42
|
+
|
|
43
|
+
async def run(self, loop_response: ResponsesLanguageModelStreamResponse) -> None:
|
|
44
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
45
|
+
|
|
46
|
+
def apply_postprocessing_to_response(
|
|
47
|
+
self, loop_response: ResponsesLanguageModelStreamResponse
|
|
48
|
+
) -> bool:
|
|
49
|
+
raise NotImplementedError(
|
|
50
|
+
"Subclasses must implement this method to apply post-processing to the response."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
async def remove_from_text(self, text: str) -> str:
|
|
30
54
|
raise NotImplementedError(
|
|
31
55
|
"Subclasses must implement this method to remove post-processing from the message."
|
|
32
56
|
)
|
|
@@ -59,13 +83,41 @@ class PostprocessorManager:
|
|
|
59
83
|
):
|
|
60
84
|
self._logger = logger
|
|
61
85
|
self._chat_service = chat_service
|
|
62
|
-
self._postprocessors: list[Postprocessor] = []
|
|
86
|
+
self._postprocessors: list[Postprocessor | ResponsesApiPostprocessor] = []
|
|
63
87
|
|
|
64
|
-
|
|
88
|
+
# Allow to add postprocessors that should be run before or after the others.
|
|
89
|
+
self._first_postprocessor: Postprocessor | ResponsesApiPostprocessor | None = (
|
|
90
|
+
None
|
|
91
|
+
)
|
|
92
|
+
self._last_postprocessor: Postprocessor | ResponsesApiPostprocessor | None = (
|
|
93
|
+
None
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def add_postprocessor(
|
|
97
|
+
self, postprocessor: Postprocessor | ResponsesApiPostprocessor
|
|
98
|
+
):
|
|
65
99
|
self._postprocessors.append(postprocessor)
|
|
66
100
|
|
|
67
|
-
def
|
|
68
|
-
|
|
101
|
+
def set_first_postprocessor(
|
|
102
|
+
self, postprocessor: Postprocessor | ResponsesApiPostprocessor
|
|
103
|
+
) -> None:
|
|
104
|
+
if self._first_postprocessor is not None:
|
|
105
|
+
raise ValueError("Cannot set first postprocessor if already set.")
|
|
106
|
+
|
|
107
|
+
self._first_postprocessor = postprocessor
|
|
108
|
+
|
|
109
|
+
def set_last_postprocessor(
|
|
110
|
+
self, postprocessor: Postprocessor | ResponsesApiPostprocessor
|
|
111
|
+
) -> None:
|
|
112
|
+
if self._last_postprocessor is not None:
|
|
113
|
+
raise ValueError("Cannot set last postprocessor if already set.")
|
|
114
|
+
|
|
115
|
+
self._last_postprocessor = postprocessor
|
|
116
|
+
|
|
117
|
+
def get_postprocessors(
|
|
118
|
+
self, name: str
|
|
119
|
+
) -> list[Postprocessor | ResponsesApiPostprocessor]:
|
|
120
|
+
return self._get_all_postprocessors()
|
|
69
121
|
|
|
70
122
|
async def run_postprocessors(
|
|
71
123
|
self,
|
|
@@ -75,25 +127,26 @@ class PostprocessorManager:
|
|
|
75
127
|
logger=self._logger,
|
|
76
128
|
)
|
|
77
129
|
|
|
130
|
+
postprocessors = self._get_valid_postprocessors_for_loop_response(loop_response)
|
|
131
|
+
|
|
78
132
|
tasks = [
|
|
79
133
|
task_executor.execute_async(
|
|
80
134
|
self.execute_postprocessors,
|
|
81
135
|
loop_response=loop_response,
|
|
82
136
|
postprocessor_instance=postprocessor,
|
|
83
137
|
)
|
|
84
|
-
for postprocessor in
|
|
138
|
+
for postprocessor in postprocessors
|
|
85
139
|
]
|
|
86
140
|
postprocessor_results = await asyncio.gather(*tasks)
|
|
87
141
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
)
|
|
142
|
+
successful_postprocessors: list[Postprocessor | ResponsesApiPostprocessor] = []
|
|
143
|
+
for i in range(len(postprocessors)):
|
|
144
|
+
if postprocessor_results[i].success:
|
|
145
|
+
successful_postprocessors.append(postprocessors[i])
|
|
93
146
|
|
|
94
147
|
modification_results = [
|
|
95
|
-
postprocessor.apply_postprocessing_to_response(loop_response)
|
|
96
|
-
for postprocessor in
|
|
148
|
+
postprocessor.apply_postprocessing_to_response(loop_response) # type: ignore (checked in `get_valid_postprocessors_for_loop_response`)
|
|
149
|
+
for postprocessor in successful_postprocessors
|
|
97
150
|
]
|
|
98
151
|
|
|
99
152
|
has_been_modified = any(modification_results)
|
|
@@ -108,14 +161,52 @@ class PostprocessorManager:
|
|
|
108
161
|
async def execute_postprocessors(
|
|
109
162
|
self,
|
|
110
163
|
loop_response: LanguageModelStreamResponse,
|
|
111
|
-
postprocessor_instance: Postprocessor,
|
|
164
|
+
postprocessor_instance: Postprocessor | ResponsesApiPostprocessor,
|
|
112
165
|
) -> None:
|
|
113
|
-
await postprocessor_instance.run(loop_response)
|
|
166
|
+
await postprocessor_instance.run(loop_response) # type: ignore
|
|
114
167
|
|
|
115
168
|
async def remove_from_text(
|
|
116
169
|
self,
|
|
117
170
|
text: str,
|
|
118
171
|
) -> str:
|
|
119
|
-
for postprocessor in self.
|
|
172
|
+
for postprocessor in self._get_all_postprocessors():
|
|
120
173
|
text = await postprocessor.remove_from_text(text)
|
|
121
174
|
return text
|
|
175
|
+
|
|
176
|
+
def _get_all_postprocessors(
|
|
177
|
+
self,
|
|
178
|
+
) -> list[Postprocessor | ResponsesApiPostprocessor]:
|
|
179
|
+
return [
|
|
180
|
+
postprocessor
|
|
181
|
+
for postprocessor in [
|
|
182
|
+
self._first_postprocessor,
|
|
183
|
+
*self._postprocessors,
|
|
184
|
+
self._last_postprocessor,
|
|
185
|
+
]
|
|
186
|
+
if postprocessor is not None
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
def _get_valid_postprocessors_for_loop_response(
|
|
190
|
+
self, loop_response: LanguageModelStreamResponse
|
|
191
|
+
) -> list[Postprocessor | ResponsesApiPostprocessor]:
|
|
192
|
+
all_postprocessors = self._get_all_postprocessors()
|
|
193
|
+
|
|
194
|
+
postprocessors: list[Postprocessor | ResponsesApiPostprocessor] = []
|
|
195
|
+
|
|
196
|
+
if isinstance(loop_response, ResponsesLanguageModelStreamResponse):
|
|
197
|
+
"""
|
|
198
|
+
All processore can be executed, since `ResponsesLanguageModelStreamResponse`
|
|
199
|
+
is a subclass of `LanguageModelStreamResponse`
|
|
200
|
+
"""
|
|
201
|
+
postprocessors = all_postprocessors
|
|
202
|
+
else:
|
|
203
|
+
"""
|
|
204
|
+
Cannot execute Responses API-specific postprocessors
|
|
205
|
+
"""
|
|
206
|
+
postprocessors = [
|
|
207
|
+
postprocessor
|
|
208
|
+
for postprocessor in all_postprocessors
|
|
209
|
+
if isinstance(postprocessor, Postprocessor)
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
return postprocessors
|
|
@@ -98,6 +98,6 @@ class ReferenceManager:
|
|
|
98
98
|
referenced_chunks: list[ContentChunk] = []
|
|
99
99
|
for ref in references:
|
|
100
100
|
for chunk in self._chunks:
|
|
101
|
-
if ref.source_id == f"{chunk.id}
|
|
101
|
+
if ref.source_id == f"{chunk.id}_{chunk.chunk_id}":
|
|
102
102
|
referenced_chunks.append(chunk)
|
|
103
103
|
return referenced_chunks
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from unique_toolkit.agentic.responses_api.postprocessors.code_display import (
|
|
2
|
+
ShowExecutedCodePostprocessor,
|
|
3
|
+
ShowExecutedCodePostprocessorConfig,
|
|
4
|
+
)
|
|
5
|
+
from unique_toolkit.agentic.responses_api.postprocessors.generated_files import (
|
|
6
|
+
DisplayCodeInterpreterFilesPostProcessor,
|
|
7
|
+
DisplayCodeInterpreterFilesPostProcessorConfig,
|
|
8
|
+
)
|
|
9
|
+
from unique_toolkit.agentic.responses_api.stream_handler import (
|
|
10
|
+
ResponsesStreamingHandler,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"ShowExecutedCodePostprocessor",
|
|
15
|
+
"ShowExecutedCodePostprocessorConfig",
|
|
16
|
+
"DisplayCodeInterpreterFilesPostProcessorConfig",
|
|
17
|
+
"DisplayCodeInterpreterFilesPostProcessor",
|
|
18
|
+
"ResponsesStreamingHandler",
|
|
19
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from typing import override
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from unique_toolkit.agentic.postprocessor.postprocessor_manager import (
|
|
8
|
+
ResponsesApiPostprocessor,
|
|
9
|
+
)
|
|
10
|
+
from unique_toolkit.agentic.tools.config import get_configuration_dict
|
|
11
|
+
from unique_toolkit.language_model.schemas import ResponsesLanguageModelStreamResponse
|
|
12
|
+
|
|
13
|
+
_TEMPLATE = """
|
|
14
|
+
<details><summary>Code Interpreter Call</summary>
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
{code}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
</details>
|
|
21
|
+
</br>
|
|
22
|
+
|
|
23
|
+
""".lstrip()
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ShowExecutedCodePostprocessorConfig(BaseModel):
|
|
29
|
+
model_config = get_configuration_dict()
|
|
30
|
+
remove_from_history: bool = Field(
|
|
31
|
+
default=False,
|
|
32
|
+
description="If set, the code interpreter call will be removed from the history on subsequent calls to the assistant.",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ShowExecutedCodePostprocessor(ResponsesApiPostprocessor):
|
|
37
|
+
def __init__(self, config: ShowExecutedCodePostprocessorConfig):
|
|
38
|
+
super().__init__(self.__class__.__name__)
|
|
39
|
+
self._config = config
|
|
40
|
+
|
|
41
|
+
@override
|
|
42
|
+
async def run(self, loop_response: ResponsesLanguageModelStreamResponse) -> None:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
@override
|
|
46
|
+
def apply_postprocessing_to_response(
|
|
47
|
+
self, loop_response: ResponsesLanguageModelStreamResponse
|
|
48
|
+
) -> bool:
|
|
49
|
+
prepended_text = ""
|
|
50
|
+
for output in loop_response.code_interpreter_calls:
|
|
51
|
+
prepended_text += _TEMPLATE.format(code=output.code)
|
|
52
|
+
|
|
53
|
+
loop_response.message.text = prepended_text + loop_response.message.text
|
|
54
|
+
|
|
55
|
+
return prepended_text != ""
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
async def remove_from_text(self, text) -> str:
|
|
59
|
+
if not self._config.remove_from_history:
|
|
60
|
+
return text
|
|
61
|
+
# Remove code interpreter blocks using regex
|
|
62
|
+
pattern = r"<details><summary>Code Interpreter Call</summary>.*?</details>"
|
|
63
|
+
return re.sub(pattern, "", text, flags=re.DOTALL)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import re
|
|
3
|
+
from mimetypes import guess_type
|
|
4
|
+
from typing import override
|
|
5
|
+
|
|
6
|
+
from openai import AsyncOpenAI
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from unique_sdk import Content
|
|
9
|
+
|
|
10
|
+
from unique_toolkit.agentic.postprocessor.postprocessor_manager import (
|
|
11
|
+
ResponsesApiPostprocessor,
|
|
12
|
+
)
|
|
13
|
+
from unique_toolkit.agentic.tools.config import get_configuration_dict
|
|
14
|
+
from unique_toolkit.content.schemas import ContentReference
|
|
15
|
+
from unique_toolkit.content.service import ContentService
|
|
16
|
+
from unique_toolkit.language_model.schemas import ResponsesLanguageModelStreamResponse
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DisplayCodeInterpreterFilesPostProcessorConfig(BaseModel):
|
|
22
|
+
model_config = get_configuration_dict()
|
|
23
|
+
upload_scope_id: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DisplayCodeInterpreterFilesPostProcessor(
|
|
27
|
+
ResponsesApiPostprocessor,
|
|
28
|
+
):
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
client: AsyncOpenAI,
|
|
32
|
+
content_service: ContentService,
|
|
33
|
+
config: DisplayCodeInterpreterFilesPostProcessorConfig,
|
|
34
|
+
) -> None:
|
|
35
|
+
super().__init__(self.__class__.__name__)
|
|
36
|
+
self._content_service = content_service
|
|
37
|
+
self._config = config
|
|
38
|
+
self._client = client
|
|
39
|
+
self._content_map = {}
|
|
40
|
+
|
|
41
|
+
@override
|
|
42
|
+
async def run(self, loop_response: ResponsesLanguageModelStreamResponse) -> None:
|
|
43
|
+
logger.info("Fetching and adding code interpreter files to the response")
|
|
44
|
+
|
|
45
|
+
container_files = loop_response.container_files
|
|
46
|
+
logger.info("Found %s container files", len(container_files))
|
|
47
|
+
|
|
48
|
+
self._content_map = {}
|
|
49
|
+
for container_file in container_files:
|
|
50
|
+
logger.info("Fetching file content for %s", container_file.filename)
|
|
51
|
+
file_content = await self._client.containers.files.content.retrieve(
|
|
52
|
+
container_id=container_file.container_id, file_id=container_file.file_id
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
logger.info(
|
|
56
|
+
"Uploading file content for %s to knowledge base",
|
|
57
|
+
container_file.filename,
|
|
58
|
+
)
|
|
59
|
+
content = self._content_service.upload_content_from_bytes(
|
|
60
|
+
content=file_content.content,
|
|
61
|
+
content_name=container_file.filename,
|
|
62
|
+
skip_ingestion=True,
|
|
63
|
+
mime_type=guess_type(container_file.filename)[0] or "text/plain",
|
|
64
|
+
scope_id=self._config.upload_scope_id,
|
|
65
|
+
)
|
|
66
|
+
self._content_map[container_file.filename] = content
|
|
67
|
+
|
|
68
|
+
@override
|
|
69
|
+
def apply_postprocessing_to_response(
|
|
70
|
+
self, loop_response: ResponsesLanguageModelStreamResponse
|
|
71
|
+
) -> bool:
|
|
72
|
+
ref_number = _get_next_ref_number(loop_response.message.references)
|
|
73
|
+
changed = False
|
|
74
|
+
# images
|
|
75
|
+
for filename, content in self._content_map.items():
|
|
76
|
+
# Images
|
|
77
|
+
loop_response.message.text, replaced = _replace_container_image_citation(
|
|
78
|
+
text=loop_response.message.text, filename=filename, content=content
|
|
79
|
+
)
|
|
80
|
+
changed |= replaced
|
|
81
|
+
|
|
82
|
+
# Files
|
|
83
|
+
loop_response.message.text, replaced = _replace_container_file_citation(
|
|
84
|
+
text=loop_response.message.text,
|
|
85
|
+
filename=filename,
|
|
86
|
+
ref_number=ref_number,
|
|
87
|
+
)
|
|
88
|
+
changed |= replaced
|
|
89
|
+
if replaced:
|
|
90
|
+
loop_response.message.references.append(
|
|
91
|
+
ContentReference(
|
|
92
|
+
sequence_number=ref_number,
|
|
93
|
+
source_id=content.id,
|
|
94
|
+
source="node-ingestion-chunks",
|
|
95
|
+
url=f"unique://content/{content.id}",
|
|
96
|
+
name=filename,
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
ref_number += 1
|
|
100
|
+
return changed
|
|
101
|
+
|
|
102
|
+
@override
|
|
103
|
+
async def remove_from_text(self, text) -> str:
|
|
104
|
+
return text
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _get_next_ref_number(references: list[ContentReference]) -> int:
|
|
108
|
+
max_ref_number = 0
|
|
109
|
+
for ref in references:
|
|
110
|
+
max_ref_number = max(max_ref_number, ref.sequence_number)
|
|
111
|
+
return max_ref_number + 1
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _replace_container_image_citation(
|
|
115
|
+
text: str, filename: str, content: Content
|
|
116
|
+
) -> tuple[str, bool]:
|
|
117
|
+
image_markdown = rf"!\[.*?\]\(sandbox:/mnt/data/{re.escape(filename)}\)"
|
|
118
|
+
|
|
119
|
+
if not re.search(image_markdown, text):
|
|
120
|
+
logger.info("No image markdown found for %s", filename)
|
|
121
|
+
return text, False
|
|
122
|
+
|
|
123
|
+
logger.info("Displaying image %s", filename)
|
|
124
|
+
return re.sub(
|
|
125
|
+
image_markdown,
|
|
126
|
+
f"",
|
|
127
|
+
text,
|
|
128
|
+
), True
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _replace_container_file_citation(
|
|
132
|
+
text: str, filename: str, ref_number: int
|
|
133
|
+
) -> tuple[str, bool]:
|
|
134
|
+
file_markdown = rf"\[.*?\]\(sandbox:/mnt/data/{re.escape(filename)}\)"
|
|
135
|
+
|
|
136
|
+
if not re.search(file_markdown, text):
|
|
137
|
+
logger.info("No file markdown found for %s", filename)
|
|
138
|
+
return text, False
|
|
139
|
+
|
|
140
|
+
logger.info("Displaying file %s", filename)
|
|
141
|
+
return re.sub(
|
|
142
|
+
file_markdown,
|
|
143
|
+
f"<sup>{ref_number}</sup>",
|
|
144
|
+
text,
|
|
145
|
+
), True
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from unique_toolkit.protocols.support import ResponsesSupportCompleteWithReferences
|
|
2
|
+
from unique_toolkit.services.chat_service import ChatService
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ResponsesStreamingHandler(ResponsesSupportCompleteWithReferences):
|
|
6
|
+
def __init__(self, chat_service: ChatService):
|
|
7
|
+
self._chat_service = chat_service
|
|
8
|
+
|
|
9
|
+
def complete_with_references(self, *args, **kwargs):
|
|
10
|
+
return self._chat_service.complete_responses_with_references(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
async def complete_with_references_async(self, *args, **kwargs):
|
|
13
|
+
return await self._chat_service.complete_responses_with_references_async(
|
|
14
|
+
*args, **kwargs
|
|
15
|
+
)
|
|
@@ -2,19 +2,35 @@ from unique_toolkit.agentic.tools.a2a.config import ExtendedSubAgentToolConfig
|
|
|
2
2
|
from unique_toolkit.agentic.tools.a2a.evaluation import (
|
|
3
3
|
SubAgentEvaluationService,
|
|
4
4
|
SubAgentEvaluationServiceConfig,
|
|
5
|
+
SubAgentEvaluationSpec,
|
|
5
6
|
)
|
|
6
7
|
from unique_toolkit.agentic.tools.a2a.manager import A2AManager
|
|
7
8
|
from unique_toolkit.agentic.tools.a2a.postprocessing import (
|
|
8
|
-
|
|
9
|
+
SubAgentDisplaySpec,
|
|
10
|
+
SubAgentReferencesPostprocessor,
|
|
11
|
+
SubAgentResponsesDisplayPostprocessor,
|
|
12
|
+
SubAgentResponsesPostprocessorConfig,
|
|
9
13
|
)
|
|
14
|
+
from unique_toolkit.agentic.tools.a2a.prompts import (
|
|
15
|
+
REFERENCING_INSTRUCTIONS_FOR_SYSTEM_PROMPT,
|
|
16
|
+
REFERENCING_INSTRUCTIONS_FOR_USER_PROMPT,
|
|
17
|
+
)
|
|
18
|
+
from unique_toolkit.agentic.tools.a2a.response_watcher import SubAgentResponseWatcher
|
|
10
19
|
from unique_toolkit.agentic.tools.a2a.tool import SubAgentTool, SubAgentToolConfig
|
|
11
20
|
|
|
12
21
|
__all__ = [
|
|
13
22
|
"SubAgentToolConfig",
|
|
14
23
|
"SubAgentTool",
|
|
15
|
-
"
|
|
24
|
+
"SubAgentResponsesDisplayPostprocessor",
|
|
25
|
+
"SubAgentResponsesPostprocessorConfig",
|
|
26
|
+
"SubAgentDisplaySpec",
|
|
16
27
|
"A2AManager",
|
|
17
28
|
"ExtendedSubAgentToolConfig",
|
|
18
29
|
"SubAgentEvaluationServiceConfig",
|
|
19
30
|
"SubAgentEvaluationService",
|
|
31
|
+
"REFERENCING_INSTRUCTIONS_FOR_SYSTEM_PROMPT",
|
|
32
|
+
"REFERENCING_INSTRUCTIONS_FOR_USER_PROMPT",
|
|
33
|
+
"SubAgentResponseWatcher",
|
|
34
|
+
"SubAgentReferencesPostprocessor",
|
|
35
|
+
"SubAgentEvaluationSpec",
|
|
20
36
|
]
|
|
@@ -4,10 +4,12 @@ from unique_toolkit.agentic.tools.a2a.evaluation.config import (
|
|
|
4
4
|
)
|
|
5
5
|
from unique_toolkit.agentic.tools.a2a.evaluation.evaluator import (
|
|
6
6
|
SubAgentEvaluationService,
|
|
7
|
+
SubAgentEvaluationSpec,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
__all__ = [
|
|
10
11
|
"SubAgentEvaluationService",
|
|
11
12
|
"SubAgentEvaluationServiceConfig",
|
|
12
13
|
"SubAgentEvaluationConfig",
|
|
14
|
+
"SubAgentEvaluationSpec",
|
|
13
15
|
]
|
|
@@ -16,7 +16,7 @@ _ASSESSMENT_LABEL_COMPARISON_DICT: dict[str, int] = {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def
|
|
19
|
+
def sort_assessments(
|
|
20
20
|
assessments: list[unique_sdk.Space.Assessment],
|
|
21
21
|
) -> list[unique_sdk.Space.Assessment]:
|
|
22
22
|
return sorted(
|
|
@@ -25,7 +25,7 @@ def _sort_assessments(
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def
|
|
28
|
+
def get_worst_label(
|
|
29
29
|
*labels: str,
|
|
30
30
|
) -> str:
|
|
31
31
|
return min(
|
|
@@ -34,7 +34,7 @@ def _worst_label(
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def
|
|
37
|
+
def get_valid_assessments(
|
|
38
38
|
assessments: list[unique_sdk.Space.Assessment],
|
|
39
39
|
display_name: str,
|
|
40
40
|
sequence_number: int,
|
|
@@ -2,12 +2,12 @@ from pathlib import Path
|
|
|
2
2
|
|
|
3
3
|
from pydantic import AliasChoices, BaseModel, Field
|
|
4
4
|
|
|
5
|
-
from unique_toolkit._common.default_language_model import DEFAULT_GPT_4o
|
|
6
5
|
from unique_toolkit._common.pydantic_helpers import get_configuration_dict
|
|
7
6
|
from unique_toolkit._common.validators import LMI, get_LMI_default_field
|
|
8
7
|
from unique_toolkit.chat.schemas import (
|
|
9
8
|
ChatMessageAssessmentType,
|
|
10
9
|
)
|
|
10
|
+
from unique_toolkit.language_model.default_language_model import DEFAULT_GPT_4o
|
|
11
11
|
|
|
12
12
|
DEFAULT_EVALUATION_SYSTEM_MESSAGE_TEMPLATE = """
|
|
13
13
|
You are a through and precise summarization model.
|