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
@@ -2,14 +2,27 @@
2
2
  from unique_toolkit.chat import ChatService
3
3
  from unique_toolkit.content import ContentService
4
4
  from unique_toolkit.embedding import EmbeddingService
5
+ from unique_toolkit.framework_utilities.openai.client import (
6
+ get_async_openai_client,
7
+ get_openai_client,
8
+ )
5
9
  from unique_toolkit.language_model import (
6
10
  LanguageModelMessages,
7
11
  LanguageModelName,
8
12
  LanguageModelService,
9
13
  LanguageModelToolDescription,
10
14
  )
15
+ from unique_toolkit.services.knowledge_base import KnowledgeBaseService
11
16
  from unique_toolkit.short_term_memory import ShortTermMemoryService
12
17
 
18
+ # Conditionally import langchain utilities if langchain is installed
19
+ try:
20
+ from unique_toolkit.framework_utilities.langchain.client import get_langchain_client # noqa: F401, I001
21
+
22
+ _LANGCHAIN_AVAILABLE = True
23
+ except ImportError:
24
+ _LANGCHAIN_AVAILABLE = False
25
+
13
26
  # You can add other classes you frequently use here as well
14
27
 
15
28
  __all__ = [
@@ -21,4 +34,11 @@ __all__ = [
21
34
  "ContentService",
22
35
  "EmbeddingService",
23
36
  "ShortTermMemoryService",
37
+ "KnowledgeBaseService",
38
+ "get_openai_client",
39
+ "get_async_openai_client",
24
40
  ]
41
+
42
+ # Add langchain-specific exports if available
43
+ if _LANGCHAIN_AVAILABLE:
44
+ __all__.append("get_langchain_client")
@@ -4,7 +4,7 @@ from logging import Logger
4
4
  from typing import Any, Generic
5
5
 
6
6
  import jinja2
7
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, ValidationError
8
8
 
9
9
  from unique_toolkit._common.endpoint_builder import (
10
10
  ApiOperationProtocol,
@@ -15,10 +15,14 @@ from unique_toolkit._common.endpoint_builder import (
15
15
  ResponseType,
16
16
  )
17
17
  from unique_toolkit._common.endpoint_requestor import (
18
+ RequestContext,
18
19
  RequestorType,
19
20
  build_requestor,
20
21
  )
21
- from unique_toolkit._common.pydantic_helpers import create_union_model
22
+ from unique_toolkit._common.pydantic_helpers import (
23
+ create_complement_model,
24
+ create_union_model,
25
+ )
22
26
  from unique_toolkit._common.string_utilities import (
23
27
  dict_to_markdown_table,
24
28
  extract_dicts_from_string,
@@ -37,11 +41,13 @@ NEXT_USER_MESSAGE_JINJA2_TEMPLATE = jinja2.Template("""I confirm the api call wi
37
41
  ```""")
38
42
 
39
43
 
40
- ASSISTANT_CONFIRMATION_MESSAGE_JINJA2_TEMPLATE = jinja2.Template("""I would like to call the api with the following data:
41
-
44
+ ASSISTANT_CONFIRMATION_MESSAGE_JINJA2_TEMPLATE = jinja2.Template(
45
+ """
46
+ \n
42
47
  {{ api_call_as_markdown_table }}
43
-
44
- [{{ button_text }}](https://prompt={{ next_user_message | urlencode }})""")
48
+ \n\n
49
+ [{{ button_text }}](https://prompt={{ next_user_message | urlencode }})"""
50
+ )
45
51
 
46
52
 
47
53
  class HumanVerificationManagerForApiCalling(
@@ -77,24 +83,67 @@ class HumanVerificationManagerForApiCalling(
77
83
  ]
78
84
  ],
79
85
  requestor_type: RequestorType = RequestorType.REQUESTS,
86
+ environment_payload_params: BaseModel | None = None,
87
+ modifiable_payload_params_model: type[BaseModel] | None = None,
80
88
  **kwargs: dict[str, Any],
81
89
  ):
90
+ """
91
+ Manages human verification for api calling.
92
+
93
+ Args:
94
+ logger: The logger to use for logging.
95
+ operation: The operation to use for the api calling.
96
+ requestor_type: The requestor type to use for the api calling.
97
+ environment_payload_params: The environment payload params to use for the api calling.
98
+ If None, the modifiable params model will be the operation payload model.
99
+ This can be useful for parameters in the payload that should not be modified by the user.
100
+ modifiable_payload_params_model: The modifiable payload params model to use for the api calling.
101
+ If None, a complement model will be created using the operation payload model
102
+ and the environment payload params.
103
+ If provided, it will be used instead of the complement model.
104
+ This is necessary if the modifiable params model is required
105
+ to use custom validators or serializers.
106
+ **kwargs: Additional keyword arguments to pass to the requestor.
107
+ """
82
108
  self._logger = logger
83
109
  self._operation = operation
84
-
110
+ self._environment_payload_params = environment_payload_params
85
111
  # Create internal models for this manager instance
86
112
 
87
- class ConcreteApiCall(BaseModel):
113
+ if self._environment_payload_params is None:
114
+ self._modifiable_payload_params_model = self._operation.payload_model()
115
+ else:
116
+ if modifiable_payload_params_model is None:
117
+ self._modifiable_payload_params_model = create_complement_model(
118
+ model_type_a=self._operation.payload_model(),
119
+ model_type_b=type(self._environment_payload_params),
120
+ )
121
+ else:
122
+ # This is necessary if the modifiable params model is required
123
+ # to use custom validators or serializers.
124
+ self._modifiable_payload_params_model = modifiable_payload_params_model
125
+
126
+ if self._environment_payload_params is not None:
127
+ combined_keys = set(
128
+ self._modifiable_payload_params_model.model_fields.keys()
129
+ ) | set(type(self._environment_payload_params).model_fields.keys())
130
+ payload_keys = set(self._operation.payload_model().model_fields.keys())
131
+ if not payload_keys.issubset(combined_keys):
132
+ raise ValueError(
133
+ "The modifiable params model + the environment parameters do not have all the keys of the operation payload model."
134
+ )
135
+
136
+ class VerificationModel(BaseModel):
88
137
  confirmation: HumanConfirmation
89
- payload: self._operation.payload_model() # type: ignore
138
+ modifiable_params: self._modifiable_payload_params_model # type: ignore
90
139
 
91
- self._api_call_model = ConcreteApiCall
140
+ self._verification_model = VerificationModel
92
141
 
142
+ self._requestor_type = requestor_type
93
143
  self._combined_params_model = create_union_model(
94
144
  model_type_a=self._operation.path_params_model(),
95
145
  model_type_b=self._operation.payload_model(),
96
146
  )
97
- self._requestor_type = requestor_type
98
147
  self._requestor = build_requestor(
99
148
  requestor_type=requestor_type,
100
149
  operation_type=operation,
@@ -103,7 +152,10 @@ class HumanVerificationManagerForApiCalling(
103
152
  )
104
153
 
105
154
  def detect_api_calls_from_user_message(
106
- self, *, last_assistant_message: ChatMessage, user_message: str
155
+ self,
156
+ *,
157
+ last_assistant_message: ChatMessage,
158
+ user_message: str,
107
159
  ) -> PayloadType | None:
108
160
  user_message_dicts = extract_dicts_from_string(user_message)
109
161
  if len(user_message_dicts) == 0:
@@ -113,13 +165,22 @@ class HumanVerificationManagerForApiCalling(
113
165
  for user_message_dict in user_message_dicts:
114
166
  try:
115
167
  # Convert dict to payload model first, then create payload
116
- api_call = self._api_call_model.model_validate(
168
+ verfication_data = self._verification_model.model_validate(
117
169
  user_message_dict, by_alias=True, by_name=True
118
170
  )
119
171
  if self._verify_human_verification(
120
- api_call.confirmation, last_assistant_message
172
+ verfication_data.confirmation, last_assistant_message
121
173
  ):
122
- return api_call.payload
174
+ payload_dict = verfication_data.modifiable_params.model_dump()
175
+ if self._environment_payload_params is not None:
176
+ payload_dict.update(
177
+ self._environment_payload_params.model_dump()
178
+ )
179
+
180
+ return self._operation.payload_model().model_validate(
181
+ payload_dict, by_alias=True, by_name=True
182
+ )
183
+
123
184
  except Exception as e:
124
185
  self._logger.error(f"Error detecting api calls from user message: {e}")
125
186
 
@@ -140,11 +201,29 @@ class HumanVerificationManagerForApiCalling(
140
201
  return confirmation.payload_hash in last_assistant_message.content
141
202
 
142
203
  def _create_next_user_message(self, payload: PayloadType) -> str:
143
- api_call = self._api_call_model(
144
- payload=payload,
204
+ # Extract only the modifiable fields from the payload
205
+ payload_dict = payload.model_dump()
206
+ if self._environment_payload_params is not None:
207
+ # Remove environment params from payload to avoid validation errors
208
+ environment_fields = set(
209
+ type(self._environment_payload_params).model_fields.keys()
210
+ )
211
+ modifiable_dict = {
212
+ k: v for k, v in payload_dict.items() if k not in environment_fields
213
+ }
214
+ else:
215
+ modifiable_dict = payload_dict
216
+
217
+ modifiable_params = self._modifiable_payload_params_model.model_validate(
218
+ modifiable_dict,
219
+ by_alias=True,
220
+ by_name=True,
221
+ )
222
+ api_call = self._verification_model(
223
+ modifiable_params=modifiable_params,
145
224
  confirmation=HumanConfirmation(
146
225
  payload_hash=hashlib.sha256(
147
- payload.model_dump_json().encode()
226
+ modifiable_params.model_dump_json().encode()
148
227
  ).hexdigest(),
149
228
  time_stamp=datetime.now(),
150
229
  ),
@@ -153,28 +232,42 @@ class HumanVerificationManagerForApiCalling(
153
232
  api_call_as_json=api_call.model_dump_json(indent=2)
154
233
  )
155
234
 
156
- def create_assistant_confirmation_message(self, *, payload: PayloadType) -> str:
235
+ def create_assistant_confirmation_message(
236
+ self, *, payload: PayloadType, button_text: str = "Confirm"
237
+ ) -> str:
157
238
  return ASSISTANT_CONFIRMATION_MESSAGE_JINJA2_TEMPLATE.render(
158
239
  api_call_as_markdown_table=dict_to_markdown_table(payload.model_dump()),
159
- button_text="Please confirm the call by pressing this button.",
240
+ button_text=button_text,
160
241
  next_user_message=self._create_next_user_message(payload),
161
242
  )
162
243
 
163
244
  def call_api(
164
245
  self,
165
246
  *,
166
- headers: dict[str, str],
247
+ context: RequestContext,
167
248
  path_params: PathParamsType,
168
249
  payload: PayloadType,
169
250
  ) -> ResponseType:
251
+ """
252
+ Call the api with the given path params, payload and secured payload params.
253
+
254
+ The `secured payload params` are params that are enforced by the application.
255
+ It should generally be not possible for the user to adapt those but here we
256
+ ensure that the application has the last word.
257
+
258
+ """
170
259
  params = path_params.model_dump()
171
260
  params.update(payload.model_dump())
172
261
 
173
262
  response = self._requestor.request(
174
- headers=headers,
263
+ context=context,
175
264
  **params,
176
265
  )
177
- return self._operation.handle_response(response)
266
+ try:
267
+ return self._operation.handle_response(response)
268
+ except ValidationError as e:
269
+ self._logger.error(f"Error calling api: {e}. Response: {response}")
270
+ raise e
178
271
 
179
272
 
180
273
  if __name__ == "__main__":
@@ -199,9 +292,9 @@ if __name__ == "__main__":
199
292
  class CombinedParams(GetUserPathParams, GetUserRequestBody):
200
293
  pass
201
294
 
202
- UserEndpoint = build_api_operation(
295
+ UserApiOperation = build_api_operation(
203
296
  method=EndpointMethods.GET,
204
- url_template=Template("https://api.example.com/users/{user_id}"),
297
+ path_template=Template("/users/{user_id}"),
205
298
  path_params_constructor=GetUserPathParams,
206
299
  payload_constructor=GetUserRequestBody,
207
300
  response_model_type=UserResponse,
@@ -209,15 +302,15 @@ if __name__ == "__main__":
209
302
 
210
303
  human_verification_manager = HumanVerificationManagerForApiCalling(
211
304
  logger=logging.getLogger(__name__),
212
- operation=UserEndpoint,
305
+ operation=UserApiOperation,
213
306
  requestor_type=RequestorType.FAKE,
214
307
  return_value={"id": 100, "name": "John Doe"},
215
308
  )
216
309
 
217
310
  payload = GetUserRequestBody(include_profile=True)
218
311
 
219
- api_call = human_verification_manager._api_call_model(
220
- payload=payload,
312
+ api_call = human_verification_manager._verification_model(
313
+ modifiable_params=payload,
221
314
  confirmation=HumanConfirmation(
222
315
  payload_hash=hashlib.sha256(payload.model_dump_json().encode()).hexdigest(),
223
316
  time_stamp=datetime.now(),
@@ -2,12 +2,12 @@ from typing import Annotated, Any
2
2
 
3
3
  from pydantic import BaseModel, Field
4
4
 
5
- from unique_toolkit._common.default_language_model import DEFAULT_GPT_35_TURBO
6
5
  from unique_toolkit._common.validators import LMI, get_LMI_default_field
7
6
  from unique_toolkit.agentic.evaluation.context_relevancy.schema import (
8
7
  StructuredOutputConfig,
9
8
  )
10
9
  from unique_toolkit.agentic.tools.config import get_configuration_dict
10
+ from unique_toolkit.language_model.default_language_model import DEFAULT_GPT_4o
11
11
 
12
12
 
13
13
  class ChunkRelevancySortConfig(BaseModel):
@@ -25,11 +25,11 @@ class ChunkRelevancySortConfig(BaseModel):
25
25
  description="The relevancy level order.",
26
26
  )
27
27
  language_model: LMI = get_LMI_default_field(
28
- DEFAULT_GPT_35_TURBO,
28
+ DEFAULT_GPT_4o,
29
29
  description="The language model to use for the chunk relevancy sort.",
30
30
  )
31
31
  fallback_language_model: LMI = get_LMI_default_field(
32
- DEFAULT_GPT_35_TURBO,
32
+ DEFAULT_GPT_4o,
33
33
  description="The language model to use as a fallback.",
34
34
  )
35
35
  additional_llm_options: dict[str, Any] = Field(
@@ -13,10 +13,6 @@ from unique_toolkit._common.chunk_relevancy_sorter.schemas import (
13
13
  ChunkRelevancySorterResult,
14
14
  )
15
15
  from unique_toolkit._common.chunk_relevancy_sorter.service import ChunkRelevancySorter
16
- from unique_toolkit._common.default_language_model import (
17
- DEFAULT_GPT_35_TURBO,
18
- DEFAULT_GPT_4o,
19
- )
20
16
  from unique_toolkit.agentic.evaluation.context_relevancy.schema import (
21
17
  StructuredOutputConfig,
22
18
  )
@@ -26,6 +22,7 @@ from unique_toolkit.agentic.evaluation.schemas import (
26
22
  )
27
23
  from unique_toolkit.app.schemas import ChatEvent
28
24
  from unique_toolkit.content.schemas import ContentChunk
25
+ from unique_toolkit.language_model.default_language_model import DEFAULT_GPT_4o
29
26
  from unique_toolkit.language_model.infos import LanguageModelInfo
30
27
 
31
28
 
@@ -60,7 +57,7 @@ def config():
60
57
  relevancy_levels_to_consider=["high", "medium", "low"],
61
58
  relevancy_level_order={"high": 0, "medium": 1, "low": 2},
62
59
  language_model=LanguageModelInfo.from_name(DEFAULT_GPT_4o),
63
- fallback_language_model=LanguageModelInfo.from_name(DEFAULT_GPT_35_TURBO),
60
+ fallback_language_model=LanguageModelInfo.from_name(DEFAULT_GPT_4o),
64
61
  structured_output_config=StructuredOutputConfig(
65
62
  enabled=False,
66
63
  extract_fact_list=False,
@@ -1,6 +1,12 @@
1
+ import warnings
2
+
1
3
  from unique_toolkit.language_model.infos import LanguageModelName
2
4
 
3
- DEFAULT_GPT_35_TURBO = LanguageModelName.AZURE_GPT_35_TURBO_0125
5
+ warnings.warn(
6
+ "unique_toolkit._common.default_language_model is deprecated. "
7
+ "Import DEFAULT_GPT_4o from unique_toolkit.language_model instead.",
8
+ DeprecationWarning,
9
+ stacklevel=2,
10
+ )
11
+
4
12
  DEFAULT_GPT_4o = LanguageModelName.AZURE_GPT_4o_2024_1120
5
- DEFAULT_GPT_4o_STRUCTURED_OUTPUT = LanguageModelName.AZURE_GPT_4o_2024_0806
6
- DEFAULT_GPT_4o_MINI = LanguageModelName.AZURE_GPT_4o_MINI_2024_0718
@@ -0,0 +1,7 @@
1
+ from unique_toolkit._common.docx_generator.config import DocxGeneratorConfig
2
+ from unique_toolkit._common.docx_generator.service import DocxGeneratorService
3
+
4
+ __all__ = [
5
+ "DocxGeneratorService",
6
+ "DocxGeneratorConfig",
7
+ ]
@@ -0,0 +1,12 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ from unique_toolkit._common.pydantic_helpers import get_configuration_dict
4
+
5
+
6
+ class DocxGeneratorConfig(BaseModel):
7
+ model_config = get_configuration_dict()
8
+
9
+ template_content_id: str = Field(
10
+ default="",
11
+ description="The content id of the template file uploaded to the knowledge base.",
12
+ )
@@ -0,0 +1,80 @@
1
+ from docx.document import Document as DocumentObject
2
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
3
+ from docxtpl import DocxTemplate
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class HeadingField(BaseModel):
8
+ text: str
9
+ level: int = 4
10
+ alignment: WD_PARAGRAPH_ALIGNMENT = WD_PARAGRAPH_ALIGNMENT.LEFT
11
+
12
+ def add(self, doc: DocumentObject):
13
+ p = doc.add_heading(self.text, level=self.level)
14
+ p.alignment = self.alignment
15
+ return p
16
+
17
+ def __str__(self):
18
+ return f"HeadingField(text={self.text}, level={self.level}, alignment={self.alignment})"
19
+
20
+
21
+ class ParagraphField(BaseModel):
22
+ text: str
23
+ style: str | None = None
24
+ alignment: WD_PARAGRAPH_ALIGNMENT = WD_PARAGRAPH_ALIGNMENT.LEFT
25
+
26
+ def add(self, doc: DocumentObject):
27
+ p = doc.add_paragraph(self.text, style=self.style)
28
+ p.alignment = self.alignment
29
+ return p
30
+
31
+ def __str__(self):
32
+ return f"ParagraphField(text={self.text}, style={self.style}, alignment={self.alignment})"
33
+
34
+
35
+ class RunField(BaseModel):
36
+ text: str
37
+ italic: bool | None = False
38
+ bold: bool | None = False
39
+ alignment: WD_PARAGRAPH_ALIGNMENT = WD_PARAGRAPH_ALIGNMENT.LEFT
40
+
41
+ def __str__(self):
42
+ return f"RunField(text={self.text}, italic={self.italic}, alignment={self.alignment})"
43
+
44
+
45
+ class RunsField(BaseModel):
46
+ runs: list[RunField]
47
+ style: str | None = None
48
+ alignment: WD_PARAGRAPH_ALIGNMENT = WD_PARAGRAPH_ALIGNMENT.LEFT
49
+
50
+ def add(self, doc: DocumentObject):
51
+ if not self.runs:
52
+ return None
53
+ p = doc.add_paragraph(style=self.style)
54
+ for run in self.runs:
55
+ r = p.add_run(run.text)
56
+ if run.italic:
57
+ r.italic = True
58
+ if run.bold:
59
+ r.bold = True
60
+ return p
61
+
62
+ def __str__(self):
63
+ return f"RunsField(runs={self.runs}, style={self.style}, alignment={self.alignment})"
64
+
65
+
66
+ class ContentField(BaseModel):
67
+ contents: list[HeadingField | ParagraphField | RunsField]
68
+
69
+ def add(self, doc: DocxTemplate):
70
+ sd = doc.new_subdoc()
71
+ for content in self.contents:
72
+ # if isinstance(content, ImageField):
73
+ # content.download_image(self.download_path)
74
+ # content.add(sd) # type: ignore
75
+ # else:
76
+ content.add(sd) # type: ignore
77
+ return sd
78
+
79
+ def __str__(self):
80
+ return f"ContentField(contents={self.contents})"