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.

Files changed (105) hide show
  1. unique_toolkit/__init__.py +20 -0
  2. unique_toolkit/_common/api_calling/human_verification_manager.py +121 -28
  3. unique_toolkit/_common/chunk_relevancy_sorter/config.py +3 -3
  4. unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +2 -5
  5. unique_toolkit/_common/default_language_model.py +9 -3
  6. unique_toolkit/_common/docx_generator/__init__.py +7 -0
  7. unique_toolkit/_common/docx_generator/config.py +12 -0
  8. unique_toolkit/_common/docx_generator/schemas.py +80 -0
  9. unique_toolkit/_common/docx_generator/service.py +252 -0
  10. unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
  11. unique_toolkit/_common/endpoint_builder.py +138 -117
  12. unique_toolkit/_common/endpoint_requestor.py +240 -14
  13. unique_toolkit/_common/exception.py +20 -0
  14. unique_toolkit/_common/feature_flags/schema.py +1 -5
  15. unique_toolkit/_common/referencing.py +53 -0
  16. unique_toolkit/_common/string_utilities.py +52 -1
  17. unique_toolkit/_common/tests/test_referencing.py +521 -0
  18. unique_toolkit/_common/tests/test_string_utilities.py +506 -0
  19. unique_toolkit/_common/utils/files.py +43 -0
  20. unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +16 -6
  21. unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +278 -0
  22. unique_toolkit/agentic/evaluation/config.py +3 -2
  23. unique_toolkit/agentic/evaluation/context_relevancy/service.py +2 -2
  24. unique_toolkit/agentic/evaluation/evaluation_manager.py +9 -5
  25. unique_toolkit/agentic/evaluation/hallucination/constants.py +1 -1
  26. unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +26 -3
  27. unique_toolkit/agentic/history_manager/history_manager.py +14 -11
  28. unique_toolkit/agentic/history_manager/loop_token_reducer.py +3 -4
  29. unique_toolkit/agentic/history_manager/utils.py +10 -87
  30. unique_toolkit/agentic/postprocessor/postprocessor_manager.py +107 -16
  31. unique_toolkit/agentic/reference_manager/reference_manager.py +1 -1
  32. unique_toolkit/agentic/responses_api/__init__.py +19 -0
  33. unique_toolkit/agentic/responses_api/postprocessors/code_display.py +63 -0
  34. unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +145 -0
  35. unique_toolkit/agentic/responses_api/stream_handler.py +15 -0
  36. unique_toolkit/agentic/tools/a2a/__init__.py +18 -2
  37. unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +2 -0
  38. unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +3 -3
  39. unique_toolkit/agentic/tools/a2a/evaluation/config.py +1 -1
  40. unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +143 -91
  41. unique_toolkit/agentic/tools/a2a/manager.py +7 -1
  42. unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +11 -3
  43. unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +185 -0
  44. unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +73 -0
  45. unique_toolkit/agentic/tools/a2a/postprocessing/config.py +21 -0
  46. unique_toolkit/agentic/tools/a2a/postprocessing/display.py +180 -0
  47. unique_toolkit/agentic/tools/a2a/postprocessing/references.py +101 -0
  48. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +1335 -0
  49. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +603 -0
  50. unique_toolkit/agentic/tools/a2a/prompts.py +46 -0
  51. unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +6 -0
  52. unique_toolkit/agentic/tools/a2a/response_watcher/service.py +91 -0
  53. unique_toolkit/agentic/tools/a2a/tool/config.py +15 -5
  54. unique_toolkit/agentic/tools/a2a/tool/service.py +69 -36
  55. unique_toolkit/agentic/tools/config.py +16 -2
  56. unique_toolkit/agentic/tools/factory.py +4 -0
  57. unique_toolkit/agentic/tools/mcp/tool_wrapper.py +7 -35
  58. unique_toolkit/agentic/tools/openai_builtin/__init__.py +11 -0
  59. unique_toolkit/agentic/tools/openai_builtin/base.py +30 -0
  60. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +8 -0
  61. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +57 -0
  62. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +230 -0
  63. unique_toolkit/agentic/tools/openai_builtin/manager.py +62 -0
  64. unique_toolkit/agentic/tools/test/test_mcp_manager.py +95 -7
  65. unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +240 -0
  66. unique_toolkit/agentic/tools/tool.py +0 -11
  67. unique_toolkit/agentic/tools/tool_manager.py +337 -122
  68. unique_toolkit/agentic/tools/tool_progress_reporter.py +81 -15
  69. unique_toolkit/agentic/tools/utils/__init__.py +18 -0
  70. unique_toolkit/agentic/tools/utils/execution/execution.py +8 -4
  71. unique_toolkit/agentic/tools/utils/source_handling/schema.py +1 -1
  72. unique_toolkit/chat/__init__.py +8 -1
  73. unique_toolkit/chat/deprecated/service.py +232 -0
  74. unique_toolkit/chat/functions.py +54 -40
  75. unique_toolkit/chat/rendering.py +34 -0
  76. unique_toolkit/chat/responses_api.py +461 -0
  77. unique_toolkit/chat/schemas.py +1 -1
  78. unique_toolkit/chat/service.py +96 -1569
  79. unique_toolkit/content/functions.py +116 -1
  80. unique_toolkit/content/schemas.py +59 -0
  81. unique_toolkit/content/service.py +5 -37
  82. unique_toolkit/content/smart_rules.py +301 -0
  83. unique_toolkit/framework_utilities/langchain/client.py +27 -3
  84. unique_toolkit/framework_utilities/openai/client.py +12 -1
  85. unique_toolkit/framework_utilities/openai/message_builder.py +85 -1
  86. unique_toolkit/language_model/default_language_model.py +3 -0
  87. unique_toolkit/language_model/functions.py +25 -9
  88. unique_toolkit/language_model/infos.py +72 -4
  89. unique_toolkit/language_model/schemas.py +246 -40
  90. unique_toolkit/protocols/support.py +91 -9
  91. unique_toolkit/services/__init__.py +7 -0
  92. unique_toolkit/services/chat_service.py +1630 -0
  93. unique_toolkit/services/knowledge_base.py +861 -0
  94. unique_toolkit/smart_rules/compile.py +56 -301
  95. unique_toolkit/test_utilities/events.py +197 -0
  96. {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/METADATA +173 -3
  97. {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/RECORD +99 -67
  98. unique_toolkit/agentic/tools/a2a/postprocessing/_display.py +0 -122
  99. unique_toolkit/agentic/tools/a2a/postprocessing/_utils.py +0 -19
  100. unique_toolkit/agentic/tools/a2a/postprocessing/postprocessor.py +0 -230
  101. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_consolidate_references.py +0 -665
  102. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +0 -391
  103. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_postprocessor_reference_functions.py +0 -256
  104. {unique_toolkit-1.8.1.dist-info → unique_toolkit-1.23.0.dist-info}/LICENSE +0 -0
  105. {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
- def add_postprocessor(self, postprocessor: Postprocessor):
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 get_postprocessors(self, name: str) -> list[Postprocessor]:
68
- return self._postprocessors
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 self._postprocessors
138
+ for postprocessor in postprocessors
85
139
  ]
86
140
  postprocessor_results = await asyncio.gather(*tasks)
87
141
 
88
- for i, result in enumerate(postprocessor_results):
89
- if not result.success:
90
- self._logger.warning(
91
- f"Postprocessor {self._postprocessors[i].get_name()} failed to run."
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 self._postprocessors
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._postprocessors:
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}-{chunk.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"![image](unique://content/{content.id})",
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
- SubAgentResponsesPostprocessor,
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
- "SubAgentResponsesPostprocessor",
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 _sort_assessments(
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 _worst_label(
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 _get_valid_assessments(
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.