unique_toolkit 0.5.55__py3-none-any.whl → 0.6.1__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 (35) hide show
  1. unique_toolkit/_common/validate_required_values.py +21 -0
  2. unique_toolkit/app/__init__.py +20 -0
  3. unique_toolkit/app/schemas.py +73 -7
  4. unique_toolkit/chat/__init__.py +5 -4
  5. unique_toolkit/chat/constants.py +3 -0
  6. unique_toolkit/chat/functions.py +805 -0
  7. unique_toolkit/chat/schemas.py +11 -11
  8. unique_toolkit/chat/service.py +483 -432
  9. unique_toolkit/content/__init__.py +1 -0
  10. unique_toolkit/content/constants.py +2 -0
  11. unique_toolkit/content/functions.py +475 -0
  12. unique_toolkit/content/service.py +163 -315
  13. unique_toolkit/content/utils.py +32 -0
  14. unique_toolkit/embedding/__init__.py +3 -0
  15. unique_toolkit/embedding/constants.py +2 -0
  16. unique_toolkit/embedding/functions.py +79 -0
  17. unique_toolkit/embedding/service.py +47 -34
  18. unique_toolkit/evaluators/__init__.py +1 -0
  19. unique_toolkit/evaluators/constants.py +1 -0
  20. unique_toolkit/evaluators/context_relevancy/constants.py +3 -3
  21. unique_toolkit/evaluators/context_relevancy/utils.py +5 -2
  22. unique_toolkit/evaluators/hallucination/utils.py +2 -1
  23. unique_toolkit/language_model/__init__.py +1 -0
  24. unique_toolkit/language_model/constants.py +4 -0
  25. unique_toolkit/language_model/functions.py +229 -0
  26. unique_toolkit/language_model/service.py +76 -343
  27. unique_toolkit/short_term_memory/__init__.py +5 -0
  28. unique_toolkit/short_term_memory/constants.py +1 -0
  29. unique_toolkit/short_term_memory/functions.py +175 -0
  30. unique_toolkit/short_term_memory/service.py +153 -27
  31. {unique_toolkit-0.5.55.dist-info → unique_toolkit-0.6.1.dist-info}/METADATA +38 -8
  32. unique_toolkit-0.6.1.dist-info/RECORD +64 -0
  33. unique_toolkit-0.5.55.dist-info/RECORD +0 -50
  34. {unique_toolkit-0.5.55.dist-info → unique_toolkit-0.6.1.dist-info}/LICENSE +0 -0
  35. {unique_toolkit-0.5.55.dist-info → unique_toolkit-0.6.1.dist-info}/WHEEL +0 -0
@@ -1,39 +1,94 @@
1
1
  import logging
2
- import re
3
2
  from typing import Optional
4
3
 
5
- import unique_sdk
6
- from unique_sdk._list_object import ListObject
4
+ from typing_extensions import deprecated
7
5
 
8
- from unique_toolkit._common import _time_utils
9
- from unique_toolkit._common._base_service import BaseService
10
- from unique_toolkit.app.schemas import Event
6
+ from unique_toolkit._common.validate_required_values import validate_required_values
7
+ from unique_toolkit.app.schemas import ChatEvent, Event
8
+ from unique_toolkit.chat.constants import (
9
+ DEFAULT_MAX_MESSAGES,
10
+ DEFAULT_PERCENT_OF_MAX_TOKENS,
11
+ DOMAIN_NAME,
12
+ )
13
+ from unique_toolkit.chat.functions import (
14
+ create_message,
15
+ create_message_assessment,
16
+ create_message_assessment_async,
17
+ create_message_async,
18
+ get_full_history,
19
+ get_full_history_async,
20
+ get_selection_from_history,
21
+ modify_message,
22
+ modify_message_assessment,
23
+ modify_message_assessment_async,
24
+ modify_message_async,
25
+ )
11
26
  from unique_toolkit.chat.schemas import (
12
27
  ChatMessage,
28
+ ChatMessageAssessment,
29
+ ChatMessageAssessmentLabel,
30
+ ChatMessageAssessmentStatus,
31
+ ChatMessageAssessmentType,
13
32
  ChatMessageRole,
14
- MessageAssessment,
15
- MessageAssessmentLabel,
16
- MessageAssessmentStatus,
17
- MessageAssessmentType,
18
33
  )
19
- from unique_toolkit.content.schemas import ContentReference
20
- from unique_toolkit.content.utils import count_tokens
34
+ from unique_toolkit.content.schemas import ContentChunk, ContentReference
35
+ from unique_toolkit.language_model.constants import (
36
+ DEFAULT_COMPLETE_TEMPERATURE,
37
+ DEFAULT_COMPLETE_TIMEOUT,
38
+ )
39
+ from unique_toolkit.language_model.infos import (
40
+ LanguageModelName,
41
+ )
42
+ from unique_toolkit.language_model.schemas import (
43
+ LanguageModelMessages,
44
+ LanguageModelStreamResponse,
45
+ LanguageModelTool,
46
+ )
47
+
48
+ from .functions import (
49
+ stream_complete_to_chat,
50
+ stream_complete_to_chat_async,
51
+ )
21
52
 
53
+ logger = logging.getLogger(f"toolkit.{DOMAIN_NAME}.{__name__}")
22
54
 
23
- class ChatService(BaseService):
55
+
56
+ class ChatService:
24
57
  """
25
58
  Provides all functionalities to manage the chat session.
26
59
 
27
60
  Attributes:
28
- event (Event): The Event object.
29
- logger (Optional[logging.Logger]): The logger. Defaults to None.
61
+ company_id (str | None): The company ID.
62
+ user_id (str | None): The user ID.
63
+ assistant_message_id (str | None): The assistant message ID.
64
+ user_message_id (str | None): The user message ID.
65
+ chat_id (str | None): The chat ID.
66
+ assistant_id (str | None): The assistant ID.
67
+ user_message_text (str | None): The user message text.
30
68
  """
31
69
 
32
- def __init__(self, event: Event, logger: Optional[logging.Logger] = None):
33
- super().__init__(event, logger)
70
+ def __init__(self, event: ChatEvent | Event):
71
+ self._event = event
72
+ self.company_id = event.company_id
73
+ self.user_id = event.user_id
74
+ self.assistant_message_id = event.payload.assistant_message.id
75
+ self.user_message_id = event.payload.user_message.id
76
+ self.chat_id = event.payload.chat_id
77
+ self.assistant_id = event.payload.assistant_id
78
+ self.user_message_text = event.payload.user_message.text
79
+
80
+ @property
81
+ @deprecated(
82
+ "The event property is deprecated and will be removed in a future version."
83
+ )
84
+ def event(self) -> Event | ChatEvent | None:
85
+ """
86
+ Get the event object (deprecated).
34
87
 
35
- DEFAULT_PERCENT_OF_MAX_TOKENS = 0.15
36
- DEFAULT_MAX_MESSAGES = 4
88
+ Returns:
89
+ Event | BaseEvent | None: The event object.
90
+ """
91
+ return self._event
37
92
 
38
93
  async def update_debug_info_async(self, debug_info: dict):
39
94
  """
@@ -42,14 +97,17 @@ class ChatService(BaseService):
42
97
  Args:
43
98
  debug_info (dict): The new debug information.
44
99
  """
45
- params = self._construct_message_modify_params(
46
- assistant=False, debug_info=debug_info
100
+
101
+ return await modify_message_async(
102
+ user_id=self.user_id,
103
+ company_id=self.company_id,
104
+ assistant_message_id=self.assistant_message_id,
105
+ chat_id=self.chat_id,
106
+ user_message_id=self.user_message_id,
107
+ user_message_text=self.user_message_text,
108
+ assistant=False,
109
+ debug_info=debug_info,
47
110
  )
48
- try:
49
- await unique_sdk.Message.modify_async(**params)
50
- except Exception as e:
51
- self.logger.error(f"Failed to update debug info: {e}")
52
- raise e
53
111
 
54
112
  def update_debug_info(self, debug_info: dict):
55
113
  """
@@ -58,25 +116,28 @@ class ChatService(BaseService):
58
116
  Args:
59
117
  debug_info (dict): The new debug information.
60
118
  """
61
- params = self._construct_message_modify_params(
62
- assistant=False, debug_info=debug_info
119
+
120
+ return modify_message(
121
+ user_id=self.user_id,
122
+ company_id=self.company_id,
123
+ assistant_message_id=self.assistant_message_id,
124
+ chat_id=self.chat_id,
125
+ user_message_id=self.user_message_id,
126
+ user_message_text=self.user_message_text,
127
+ assistant=False,
128
+ debug_info=debug_info,
63
129
  )
64
- try:
65
- unique_sdk.Message.modify(**params)
66
- except Exception as e:
67
- self.logger.error(f"Failed to update debug info: {e}")
68
- raise e
69
130
 
70
131
  def modify_user_message(
71
132
  self,
72
133
  content: str,
73
- references: Optional[list[ContentReference]] = None,
74
- debug_info: Optional[dict] = None,
75
- message_id: Optional[str] = None,
76
- set_completed_at: Optional[bool] = False,
134
+ references: list[ContentReference] = [],
135
+ debug_info: dict = {},
136
+ message_id: str | None = None,
137
+ set_completed_at: bool | None = False,
77
138
  ) -> ChatMessage:
78
139
  """
79
- Modifies a message in the chat session synchronously.
140
+ Modifies a user message in the chat session synchronously.
80
141
 
81
142
  Args:
82
143
  content (str): The new content for the message.
@@ -91,28 +152,28 @@ class ChatService(BaseService):
91
152
  Raises:
92
153
  Exception: If the modification fails.
93
154
  """
94
- try:
95
- params = self._construct_message_modify_params(
96
- assistant=False,
97
- content=content,
98
- references=references,
99
- debug_info=debug_info,
100
- message_id=message_id,
101
- set_completed_at=set_completed_at,
102
- )
103
- message = unique_sdk.Message.modify(**params)
104
- except Exception as e:
105
- self.logger.error(f"Failed to modify user message: {e}")
106
- raise e
107
- return ChatMessage(**message)
155
+ return modify_message(
156
+ user_id=self.user_id,
157
+ company_id=self.company_id,
158
+ assistant_message_id=self.assistant_message_id,
159
+ chat_id=self.chat_id,
160
+ user_message_id=self.user_message_id,
161
+ user_message_text=self.user_message_text,
162
+ assistant=False,
163
+ content=content,
164
+ references=references,
165
+ debug_info=debug_info,
166
+ message_id=message_id,
167
+ set_completed_at=set_completed_at or False,
168
+ )
108
169
 
109
170
  async def modify_user_message_async(
110
171
  self,
111
172
  content: str,
112
173
  references: list[ContentReference] = [],
113
174
  debug_info: dict = {},
114
- message_id: Optional[str] = None,
115
- set_completed_at: Optional[bool] = False,
175
+ message_id: str | None = None,
176
+ set_completed_at: bool | None = False,
116
177
  ) -> ChatMessage:
117
178
  """
118
179
  Modifies a message in the chat session asynchronously.
@@ -121,8 +182,8 @@ class ChatService(BaseService):
121
182
  content (str): The new content for the message.
122
183
  message_id (str, optional): The message ID. Defaults to None, then the ChatState user message id is used.
123
184
  references (list[ContentReference]): list of ContentReference objects. Defaults to None.
124
- debug_info (Optional[dict[str, Any]]], optional): Debug information. Defaults to None.
125
- set_completed_at (Optional[bool]): Whether to set the completedAt field with the current date time. Defaults to False.
185
+ debug_info (dict[str, Any]]]): Debug information. Defaults to {}.
186
+ set_completed_at (bool, optional): Whether to set the completedAt field with the current date time. Defaults to False.
126
187
 
127
188
  Returns:
128
189
  ChatMessage: The modified message.
@@ -130,20 +191,20 @@ class ChatService(BaseService):
130
191
  Raises:
131
192
  Exception: If the modification fails.
132
193
  """
133
- try:
134
- params = self._construct_message_modify_params(
135
- assistant=False,
136
- content=content,
137
- references=references,
138
- debug_info=debug_info,
139
- message_id=message_id,
140
- set_completed_at=set_completed_at,
141
- )
142
- message = await unique_sdk.Message.modify_async(**params)
143
- except Exception as e:
144
- self.logger.error(f"Failed to modify user message: {e}")
145
- raise e
146
- return ChatMessage(**message)
194
+ return await modify_message_async(
195
+ user_id=self.user_id,
196
+ company_id=self.company_id,
197
+ assistant_message_id=self.assistant_message_id,
198
+ chat_id=self.chat_id,
199
+ user_message_id=self.user_message_id,
200
+ user_message_text=self.user_message_text,
201
+ assistant=False,
202
+ content=content,
203
+ references=references,
204
+ debug_info=debug_info,
205
+ message_id=message_id,
206
+ set_completed_at=set_completed_at or False,
207
+ )
147
208
 
148
209
  def modify_assistant_message(
149
210
  self,
@@ -151,8 +212,8 @@ class ChatService(BaseService):
151
212
  original_content: str | None = None,
152
213
  references: list[ContentReference] = [],
153
214
  debug_info: dict = {},
154
- message_id: Optional[str] = None,
155
- set_completed_at: Optional[bool] = False,
215
+ message_id: str | None = None,
216
+ set_completed_at: bool | None = False,
156
217
  ) -> ChatMessage:
157
218
  """
158
219
  Modifies a message in the chat session synchronously.
@@ -171,21 +232,21 @@ class ChatService(BaseService):
171
232
  Raises:
172
233
  Exception: If the modification fails.
173
234
  """
174
- try:
175
- params = self._construct_message_modify_params(
176
- assistant=True,
177
- content=content,
178
- original_content=original_content,
179
- references=references,
180
- debug_info=debug_info,
181
- message_id=message_id,
182
- set_completed_at=set_completed_at,
183
- )
184
- message = unique_sdk.Message.modify(**params)
185
- except Exception as e:
186
- self.logger.error(f"Failed to modify assistant message: {e}")
187
- raise e
188
- return ChatMessage(**message)
235
+ return modify_message(
236
+ user_id=self.user_id,
237
+ company_id=self.company_id,
238
+ assistant_message_id=self.assistant_message_id,
239
+ chat_id=self.chat_id,
240
+ user_message_id=self.user_message_id,
241
+ user_message_text=self.user_message_text,
242
+ assistant=True,
243
+ content=content,
244
+ original_content=original_content,
245
+ references=references,
246
+ debug_info=debug_info,
247
+ message_id=message_id,
248
+ set_completed_at=set_completed_at or False,
249
+ )
189
250
 
190
251
  async def modify_assistant_message_async(
191
252
  self,
@@ -193,8 +254,8 @@ class ChatService(BaseService):
193
254
  original_content: str | None = None,
194
255
  references: list[ContentReference] = [],
195
256
  debug_info: dict = {},
196
- message_id: Optional[str] = None,
197
- set_completed_at: Optional[bool] = False,
257
+ message_id: str | None = None,
258
+ set_completed_at: bool | None = False,
198
259
  ) -> ChatMessage:
199
260
  """
200
261
  Modifies a message in the chat session asynchronously.
@@ -204,8 +265,8 @@ class ChatService(BaseService):
204
265
  original_content (str, optional): The original content for the message.
205
266
  message_id (str, optional): The message ID. Defaults to None, then the ChatState assistant message id is used.
206
267
  references (list[ContentReference]): list of ContentReference objects. Defaults to None.
207
- debug_info (Optional[dict[str, Any]]], optional): Debug information. Defaults to None.
208
- set_completed_at (Optional[bool]): Whether to set the completedAt field with the current date time. Defaults to False.
268
+ debug_info (dict[str, Any]], optional): Debug information. Defaults to None.
269
+ set_completed_at (bool, optional): Whether to set the completedAt field with the current date time. Defaults to False.
209
270
 
210
271
  Returns:
211
272
  ChatMessage: The modified message.
@@ -213,21 +274,22 @@ class ChatService(BaseService):
213
274
  Raises:
214
275
  Exception: If the modification fails.
215
276
  """
216
- try:
217
- params = self._construct_message_modify_params(
218
- assistant=True,
219
- content=content,
220
- original_content=original_content,
221
- references=references,
222
- debug_info=debug_info,
223
- message_id=message_id,
224
- set_completed_at=set_completed_at,
225
- )
226
- message = await unique_sdk.Message.modify_async(**params)
227
- except Exception as e:
228
- self.logger.error(f"Failed to modify assistant message: {e}")
229
- raise e
230
- return ChatMessage(**message)
277
+
278
+ return await modify_message_async(
279
+ user_id=self.user_id,
280
+ company_id=self.company_id,
281
+ assistant_message_id=self.assistant_message_id,
282
+ chat_id=self.chat_id,
283
+ user_message_id=self.user_message_id,
284
+ user_message_text=self.user_message_text,
285
+ assistant=True,
286
+ content=content,
287
+ original_content=original_content,
288
+ references=references,
289
+ debug_info=debug_info,
290
+ message_id=message_id,
291
+ set_completed_at=set_completed_at or False,
292
+ )
231
293
 
232
294
  def get_full_history(self) -> list[ChatMessage]:
233
295
  """
@@ -239,7 +301,11 @@ class ChatService(BaseService):
239
301
  Raises:
240
302
  Exception: If the loading fails.
241
303
  """
242
- return self._get_full_history()
304
+ return get_full_history(
305
+ event_user_id=self.user_id,
306
+ event_company_id=self.company_id,
307
+ event_payload_chat_id=self.chat_id,
308
+ )
243
309
 
244
310
  async def get_full_history_async(self) -> list[ChatMessage]:
245
311
  """
@@ -251,7 +317,11 @@ class ChatService(BaseService):
251
317
  Raises:
252
318
  Exception: If the loading fails.
253
319
  """
254
- return await self._get_full_history_async()
320
+ return await get_full_history_async(
321
+ event_user_id=self.user_id,
322
+ event_company_id=self.company_id,
323
+ event_payload_chat_id=self.chat_id,
324
+ )
255
325
 
256
326
  def get_full_and_selected_history(
257
327
  self,
@@ -273,8 +343,12 @@ class ChatService(BaseService):
273
343
  Raises:
274
344
  Exception: If the loading fails.
275
345
  """
276
- full_history = self._get_full_history()
277
- selected_history = self._get_selection_from_history(
346
+ full_history = get_full_history(
347
+ event_user_id=self.user_id,
348
+ event_company_id=self.company_id,
349
+ event_payload_chat_id=self.chat_id,
350
+ )
351
+ selected_history = get_selection_from_history(
278
352
  full_history=full_history,
279
353
  max_tokens=int(round(token_limit * percent_of_max_tokens)),
280
354
  max_messages=max_messages,
@@ -302,8 +376,12 @@ class ChatService(BaseService):
302
376
  Raises:
303
377
  Exception: If the loading fails.
304
378
  """
305
- full_history = await self._get_full_history_async()
306
- selected_history = self._get_selection_from_history(
379
+ full_history = await get_full_history_async(
380
+ event_user_id=self.user_id,
381
+ event_company_id=self.company_id,
382
+ event_payload_chat_id=self.chat_id,
383
+ )
384
+ selected_history = get_selection_from_history(
307
385
  full_history=full_history,
308
386
  max_tokens=int(round(token_limit * percent_of_max_tokens)),
309
387
  max_messages=max_messages,
@@ -317,8 +395,8 @@ class ChatService(BaseService):
317
395
  original_content: str | None = None,
318
396
  references: list[ContentReference] = [],
319
397
  debug_info: dict = {},
320
- set_completed_at: Optional[bool] = False,
321
- ):
398
+ set_completed_at: bool | None = False,
399
+ ) -> ChatMessage:
322
400
  """
323
401
  Creates a message in the chat session synchronously.
324
402
 
@@ -335,23 +413,21 @@ class ChatService(BaseService):
335
413
  Raises:
336
414
  Exception: If the creation fails.
337
415
  """
338
- if original_content is None:
339
- original_content = content
340
-
341
- try:
342
- params = self._construct_message_create_params(
343
- content=content,
344
- original_content=original_content,
345
- references=references,
346
- debug_info=debug_info,
347
- set_completed_at=set_completed_at,
348
- )
349
-
350
- message = unique_sdk.Message.create(**params)
351
- except Exception as e:
352
- self.logger.error(f"Failed to create assistant message: {e}")
353
- raise e
354
- return ChatMessage(**message)
416
+ chat_message = create_message(
417
+ user_id=self.user_id,
418
+ company_id=self.company_id,
419
+ chat_id=self.chat_id,
420
+ assistant_id=self.assistant_id,
421
+ role=ChatMessageRole.ASSISTANT,
422
+ content=content,
423
+ original_content=original_content,
424
+ references=references,
425
+ debug_info=debug_info,
426
+ set_completed_at=set_completed_at,
427
+ )
428
+ # Update the assistant message id
429
+ self.assistant_message_id = chat_message.id
430
+ return chat_message
355
431
 
356
432
  async def create_assistant_message_async(
357
433
  self,
@@ -359,8 +435,8 @@ class ChatService(BaseService):
359
435
  original_content: str | None = None,
360
436
  references: list[ContentReference] = [],
361
437
  debug_info: dict = {},
362
- set_completed_at: Optional[bool] = False,
363
- ):
438
+ set_completed_at: bool | None = False,
439
+ ) -> ChatMessage:
364
440
  """
365
441
  Creates a message in the chat session asynchronously.
366
442
 
@@ -377,233 +453,114 @@ class ChatService(BaseService):
377
453
  Raises:
378
454
  Exception: If the creation fails.
379
455
  """
380
- if original_content is None:
381
- original_content = content
382
-
383
- try:
384
- params = self._construct_message_create_params(
385
- content=content,
386
- original_content=original_content,
387
- references=references,
388
- debug_info=debug_info,
389
- set_completed_at=set_completed_at,
390
- )
391
-
392
- message = await unique_sdk.Message.create_async(**params)
393
- except Exception as e:
394
- self.logger.error(f"Failed to create assistant message: {e}")
395
- raise e
396
- return ChatMessage(**message)
397
-
398
- @staticmethod
399
- def _map_references(references: list[ContentReference]):
400
- return [
401
- {
402
- "name": ref.name,
403
- "url": ref.url,
404
- "sequenceNumber": ref.sequence_number,
405
- "sourceId": ref.source_id,
406
- "source": ref.source,
407
- }
408
- for ref in references
409
- ]
410
-
411
- def _get_full_history(self):
412
- messages = self._trigger_list_messages(self.event.payload.chat_id)
413
- messages = self._filter_valid_messages(messages)
414
-
415
- return self._map_to_chat_messages(messages)
416
-
417
- async def _get_full_history_async(self):
418
- messages = await self._trigger_list_messages_async(self.event.payload.chat_id)
419
- messages = self._filter_valid_messages(messages)
420
-
421
- return self._map_to_chat_messages(messages)
422
-
423
- @staticmethod
424
- def _filter_valid_messages(messages: ListObject[unique_sdk.Message]):
425
- SYSTEM_MESSAGE_PREFIX = "[SYSTEM] "
426
-
427
- # Remove the last two messages
428
- messages = messages["data"][:-2] # type: ignore
429
- filtered_messages = []
430
- for message in messages:
431
- if message["text"] is None:
432
- continue
433
- elif SYSTEM_MESSAGE_PREFIX in message["text"]:
434
- continue
435
- else:
436
- filtered_messages.append(message)
437
-
438
- return filtered_messages
439
-
440
- def _trigger_list_messages(self, chat_id: str):
441
- try:
442
- messages = unique_sdk.Message.list(
443
- user_id=self.event.user_id,
444
- company_id=self.event.company_id,
445
- chatId=chat_id,
446
- )
447
- return messages
448
- except Exception as e:
449
- self.logger.error(f"Failed to list chat history: {e}")
450
- raise e
451
-
452
- async def _trigger_list_messages_async(self, chat_id: str):
453
- try:
454
- messages = await unique_sdk.Message.list_async(
455
- user_id=self.event.user_id,
456
- company_id=self.event.company_id,
457
- chatId=chat_id,
458
- )
459
- return messages
460
- except Exception as e:
461
- self.logger.error(f"Failed to list chat history: {e}")
462
- raise e
463
-
464
- @staticmethod
465
- def _map_to_chat_messages(messages: list[dict]):
466
- return [ChatMessage(**msg) for msg in messages]
467
-
468
- def _get_selection_from_history(
469
- self,
470
- full_history: list[ChatMessage],
471
- max_tokens: int,
472
- max_messages=4,
473
- ):
474
- messages = full_history[-max_messages:]
475
- filtered_messages = [m for m in messages if m.content]
476
- mapped_messages = []
477
-
478
- for m in filtered_messages:
479
- m.content = re.sub(r"<sup>\d+</sup>", "", m.content)
480
- m.role = (
481
- ChatMessageRole.ASSISTANT
482
- if m.role == ChatMessageRole.ASSISTANT
483
- else ChatMessageRole.USER
484
- )
485
- mapped_messages.append(m)
486
-
487
- return self._pick_messages_in_reverse_for_token_window(
488
- messages=mapped_messages,
489
- limit=max_tokens,
456
+
457
+ chat_message = await create_message_async(
458
+ user_id=self.user_id,
459
+ company_id=self.company_id,
460
+ chat_id=self.chat_id,
461
+ assistant_id=self.assistant_id,
462
+ role=ChatMessageRole.ASSISTANT,
463
+ content=content,
464
+ original_content=original_content,
465
+ references=references,
466
+ debug_info=debug_info,
467
+ set_completed_at=set_completed_at,
490
468
  )
469
+ # Update the assistant message id
470
+ self.assistant_message_id = chat_message.id
471
+ return chat_message
491
472
 
492
- def _pick_messages_in_reverse_for_token_window(
473
+ def create_user_message(
493
474
  self,
494
- messages: list[ChatMessage],
495
- limit: int,
496
- ) -> list[ChatMessage]:
497
- if len(messages) < 1 or limit < 1:
498
- return []
499
-
500
- last_index = len(messages) - 1
501
- token_count = count_tokens(messages[last_index].content)
502
- while token_count > limit:
503
- self.logger.debug(
504
- f"Limit too low for the initial message. Last message TokenCount {token_count} available tokens {limit} - cutting message in half until it fits"
505
- )
506
- content = messages[last_index].content
507
- messages[last_index].content = content[: len(content) // 2] + "..."
508
- token_count = count_tokens(messages[last_index].content)
509
-
510
- while token_count <= limit and last_index > 0:
511
- token_count = count_tokens(
512
- "".join([msg.content for msg in messages[:last_index]])
513
- )
514
- if token_count <= limit:
515
- last_index -= 1
516
-
517
- last_index = max(0, last_index)
518
- return messages[last_index:]
519
-
520
- def _construct_message_modify_params(
521
- self,
522
- assistant: bool = True,
523
- content: Optional[str] = None,
524
- original_content: Optional[str] = None,
525
- references: Optional[list[ContentReference]] = None,
526
- debug_info: Optional[dict] = None,
527
- message_id: Optional[str] = None,
528
- set_completed_at: Optional[bool] = False,
529
- ):
530
- if message_id:
531
- # Message ID specified. No need to guess
532
- message_id = message_id
533
- elif assistant:
534
- # Assistant message ID
535
- message_id = self.event.payload.assistant_message.id
536
- else:
537
- # User message ID
538
- message_id = self.event.payload.user_message.id
539
- if content is None:
540
- content = self.event.payload.user_message.text
541
-
542
- if set_completed_at:
543
- completed_at_datetime = _time_utils.get_datetime_now()
544
- else:
545
- completed_at_datetime = None
546
-
547
- params = {
548
- "user_id": self.event.user_id,
549
- "company_id": self.event.company_id,
550
- "id": message_id,
551
- "chatId": self.event.payload.chat_id,
552
- "text": content,
553
- "originalText": original_content,
554
- "references": self._map_references(references) if references else [],
555
- "debugInfo": debug_info,
556
- "completedAt": completed_at_datetime,
557
- }
558
- return params
559
-
560
- def _construct_message_create_params(
475
+ content: str,
476
+ original_content: str | None = None,
477
+ references: list[ContentReference] = [],
478
+ debug_info: dict = {},
479
+ set_completed_at: bool | None = False,
480
+ ) -> ChatMessage:
481
+ """
482
+ Creates a user message in the chat session synchronously.
483
+
484
+ Args:
485
+ content (str): The content for the message.
486
+ original_content (str, optional): The original content for the message.
487
+ references (list[ContentReference]): list of ContentReference objects. Defaults to None.
488
+ debug_info (dict[str, Any]]): Debug information. Defaults to None.
489
+ set_completed_at (Optional[bool]): Whether to set the completedAt field with the current date time. Defaults to False.
490
+
491
+ Returns:
492
+ ChatMessage: The created message.
493
+
494
+ Raises:
495
+ Exception: If the creation fails.
496
+ """
497
+ chat_message = create_message(
498
+ user_id=self.user_id,
499
+ company_id=self.company_id,
500
+ chat_id=self.chat_id,
501
+ assistant_id=self.assistant_id,
502
+ role=ChatMessageRole.USER,
503
+ content=content,
504
+ original_content=original_content,
505
+ references=references,
506
+ debug_info=debug_info,
507
+ set_completed_at=set_completed_at,
508
+ )
509
+ # Update the user message id
510
+ self.user_message_id = chat_message.id
511
+ return chat_message
512
+
513
+ async def create_user_message_async(
561
514
  self,
562
- role: ChatMessageRole = ChatMessageRole.ASSISTANT,
563
- content: Optional[str] = None,
564
- original_content: Optional[str] = None,
565
- references: Optional[list[ContentReference]] = None,
566
- debug_info: Optional[dict] = None,
567
- assistantId: Optional[str] = None,
568
- set_completed_at: Optional[bool] = False,
569
- ):
570
- if assistantId:
571
- # Assistant ID specified. No need to guess
572
- assistantId = assistantId
573
- else:
574
- assistantId = self.event.payload.assistant_id
575
-
576
- if set_completed_at:
577
- completed_at_datetime = _time_utils.get_datetime_now()
578
- else:
579
- completed_at_datetime = None
580
-
581
- if original_content is None:
582
- original_content = content
583
-
584
- params = {
585
- "user_id": self.event.user_id,
586
- "company_id": self.event.company_id,
587
- "assistantId": assistantId,
588
- "role": role.value.upper(),
589
- "chatId": self.event.payload.chat_id,
590
- "text": content,
591
- "originalText": original_content,
592
- "references": self._map_references(references) if references else [],
593
- "debugInfo": debug_info,
594
- "completedAt": completed_at_datetime,
595
- }
596
- return params
515
+ content: str,
516
+ original_content: str | None = None,
517
+ references: list[ContentReference] = [],
518
+ debug_info: dict = {},
519
+ set_completed_at: bool | None = False,
520
+ ) -> ChatMessage:
521
+ """
522
+ Creates a user message in the chat session asynchronously.
523
+
524
+ Args:
525
+ content (str): The content for the message.
526
+ original_content (str, optional): The original content for the message.
527
+ references (list[ContentReference]): list of references. Defaults to None.
528
+ debug_info (dict[str, Any]]): Debug information. Defaults to None.
529
+ set_completed_at (Optional[bool]): Whether to set the completedAt field with the current date time. Defaults to False.
530
+
531
+ Returns:
532
+ ChatMessage: The created message.
533
+
534
+ Raises:
535
+ Exception: If the creation fails.
536
+ """
537
+
538
+ chat_message = await create_message_async(
539
+ user_id=self.user_id,
540
+ company_id=self.company_id,
541
+ chat_id=self.chat_id,
542
+ assistant_id=self.assistant_id,
543
+ role=ChatMessageRole.USER,
544
+ content=content,
545
+ original_content=original_content,
546
+ references=references,
547
+ debug_info=debug_info,
548
+ set_completed_at=set_completed_at,
549
+ )
550
+ # Update the user message id
551
+ self.user_message_id = chat_message.id
552
+ return chat_message
597
553
 
598
554
  def create_message_assessment(
599
555
  self,
600
556
  assistant_message_id: str,
601
- status: MessageAssessmentStatus,
602
- type: MessageAssessmentType,
557
+ status: ChatMessageAssessmentStatus,
558
+ type: ChatMessageAssessmentType,
559
+ title: str | None = None,
603
560
  explanation: str | None = None,
604
- label: MessageAssessmentLabel | None = None,
561
+ label: ChatMessageAssessmentLabel | None = None,
605
562
  is_visible: bool = True,
606
- ) -> MessageAssessment:
563
+ ) -> ChatMessageAssessment:
607
564
  """
608
565
  Creates a message assessment for an assistant message synchronously.
609
566
 
@@ -611,91 +568,88 @@ class ChatService(BaseService):
611
568
  assistant_message_id (str): The ID of the assistant message to assess
612
569
  status (MessageAssessmentStatus): The status of the assessment (e.g. "DONE")
613
570
  type (MessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
571
+ title (str | None): The title of the assessment
614
572
  explanation (str | None): Explanation of the assessment
615
- label (MessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
573
+ label (MessageAssessmentLabel | None): The assessment label (e.g. "RED")
616
574
  is_visible (bool): Whether the assessment is visible to users. Defaults to True.
617
575
 
618
576
  Returns:
619
- MessageAssessment: The created message assessment
577
+ ChatMessageAssessment: The created message assessment
620
578
 
621
579
  Raises:
622
580
  Exception: If the creation fails
623
581
  """
624
- try:
625
- assessment = unique_sdk.MessageAssessment.create(
626
- user_id=self.event.user_id,
627
- company_id=self.event.company_id,
628
- messageId=assistant_message_id,
629
- status=status.name,
630
- explanation=explanation,
631
- label=label.name if label else None,
632
- type=type.name if type else None,
633
- isVisible=is_visible,
634
- )
635
- return MessageAssessment(**assessment)
636
- except Exception as e:
637
- self.logger.error(f"Failed to create message assessment: {e}")
638
- raise e
582
+ return create_message_assessment(
583
+ user_id=self.user_id,
584
+ company_id=self.company_id,
585
+ assistant_message_id=assistant_message_id,
586
+ status=status,
587
+ type=type,
588
+ title=title,
589
+ explanation=explanation,
590
+ label=label,
591
+ is_visible=is_visible,
592
+ )
639
593
 
640
594
  async def create_message_assessment_async(
641
595
  self,
642
596
  assistant_message_id: str,
643
- status: MessageAssessmentStatus,
644
- type: MessageAssessmentType,
597
+ status: ChatMessageAssessmentStatus,
598
+ type: ChatMessageAssessmentType,
599
+ title: str | None = None,
645
600
  explanation: str | None = None,
646
- label: MessageAssessmentLabel | None = None,
601
+ label: ChatMessageAssessmentLabel | None = None,
647
602
  is_visible: bool = True,
648
- ) -> MessageAssessment:
603
+ ) -> ChatMessageAssessment:
649
604
  """
650
605
  Creates a message assessment for an assistant message asynchronously.
651
606
 
652
607
  Args:
653
608
  assistant_message_id (str): The ID of the assistant message to assess
654
- status (MessageAssessmentStatus): The status of the assessment (e.g. "DONE")
655
- type (MessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
609
+ status (ChatMessageAssessmentStatus): The status of the assessment (e.g. "DONE")
610
+ type (ChatMessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
611
+ title (str | None): The title of the assessment
656
612
  explanation (str | None): Explanation of the assessment
657
- label (MessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
613
+ label (ChatMessageAssessmentLabel | None): The assessment label (e.g. "RED")
658
614
  is_visible (bool): Whether the assessment is visible to users. Defaults to True.
659
615
 
660
616
  Returns:
661
- MessageAssessment: The created message assessment
617
+ ChatMessageAssessment: The created message assessment
662
618
 
663
619
  Raises:
664
620
  Exception: If the creation fails
665
621
  """
666
- try:
667
- assessment = await unique_sdk.MessageAssessment.create_async(
668
- user_id=self.event.user_id,
669
- company_id=self.event.company_id,
670
- messageId=assistant_message_id,
671
- status=status.name,
672
- explanation=explanation,
673
- label=label.name if label else None,
674
- type=type.name if type else None,
675
- isVisible=is_visible,
676
- )
677
- return MessageAssessment(**assessment)
678
- except Exception as e:
679
- self.logger.error(f"Failed to create message assessment: {e}")
680
- raise e
622
+ return await create_message_assessment_async(
623
+ user_id=self.user_id,
624
+ company_id=self.company_id,
625
+ assistant_message_id=assistant_message_id,
626
+ status=status,
627
+ type=type,
628
+ title=title,
629
+ explanation=explanation,
630
+ label=label,
631
+ is_visible=is_visible,
632
+ )
681
633
 
682
634
  def modify_message_assessment(
683
635
  self,
684
636
  assistant_message_id: str,
685
- status: MessageAssessmentStatus,
686
- type: MessageAssessmentType,
637
+ status: ChatMessageAssessmentStatus,
638
+ type: ChatMessageAssessmentType,
639
+ title: str | None = None,
687
640
  explanation: str | None = None,
688
- label: MessageAssessmentLabel | None = None,
689
- ) -> MessageAssessment:
641
+ label: ChatMessageAssessmentLabel | None = None,
642
+ ) -> ChatMessageAssessment:
690
643
  """
691
644
  Modifies a message assessment for an assistant message synchronously.
692
645
 
693
646
  Args:
694
647
  assistant_message_id (str): The ID of the assistant message to assess
695
648
  status (MessageAssessmentStatus): The status of the assessment (e.g. "DONE")
649
+ title (str | None): The title of the assessment
696
650
  explanation (str | None): Explanation of the assessment
697
- label (MessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
698
- type (MessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
651
+ label (ChatMessageAssessmentLabel | None): The assessment label (e.g. "RED")
652
+ type (ChatMessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
699
653
 
700
654
  Returns:
701
655
  dict: The modified message assessment
@@ -703,56 +657,153 @@ class ChatService(BaseService):
703
657
  Raises:
704
658
  Exception: If the modification fails
705
659
  """
706
- try:
707
- assessment = unique_sdk.MessageAssessment.modify(
708
- user_id=self.event.user_id,
709
- company_id=self.event.company_id,
710
- messageId=assistant_message_id,
711
- status=status.name,
712
- explanation=explanation,
713
- label=label.name if label else None,
714
- type=type.name,
715
- )
716
- return MessageAssessment(**assessment)
717
- except Exception as e:
718
- self.logger.error(f"Failed to modify message assessment: {e}")
719
- raise e
660
+ return modify_message_assessment(
661
+ user_id=self.user_id,
662
+ company_id=self.company_id,
663
+ assistant_message_id=assistant_message_id,
664
+ status=status,
665
+ type=type,
666
+ title=title,
667
+ explanation=explanation,
668
+ label=label,
669
+ )
720
670
 
721
671
  async def modify_message_assessment_async(
722
672
  self,
723
673
  assistant_message_id: str,
724
- type: MessageAssessmentType,
725
- status: MessageAssessmentStatus | None = None,
674
+ type: ChatMessageAssessmentType,
675
+ title: str | None = None,
676
+ status: ChatMessageAssessmentStatus | None = None,
726
677
  explanation: str | None = None,
727
- label: MessageAssessmentLabel | None = None,
728
- ) -> MessageAssessment:
678
+ label: ChatMessageAssessmentLabel | None = None,
679
+ ) -> ChatMessageAssessment:
729
680
  """
730
681
  Modifies a message assessment for an assistant message asynchronously.
731
682
 
732
683
  Args:
733
684
  assistant_message_id (str): The ID of the assistant message to assess
734
- status (MessageAssessmentStatus): The status of the assessment (e.g. "DONE")
685
+ status (ChatMessageAssessmentStatus): The status of the assessment (e.g. "DONE")
686
+ title (str | None): The title of the assessment
735
687
  explanation (str | None): Explanation of the assessment
736
- label (MessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
737
- type (MessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
688
+ label (ChatMessageAssessmentLabel | None): The assessment label (e.g. "RED")
689
+ type (ChatMessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
738
690
 
739
691
  Returns:
740
- MessageAssessment: The modified message assessment
692
+ ChatMessageAssessment: The modified message assessment
741
693
 
742
694
  Raises:
743
695
  Exception: If the modification fails
744
696
  """
745
- try:
746
- assessment = await unique_sdk.MessageAssessment.modify_async(
747
- user_id=self.event.user_id,
748
- company_id=self.event.company_id,
749
- messageId=assistant_message_id,
750
- status=status.name if status else None,
751
- explanation=explanation,
752
- label=label.name if label else None,
753
- type=type.name,
754
- )
755
- return MessageAssessment(**assessment)
756
- except Exception as e:
757
- self.logger.error(f"Failed to modify message assessment: {e}")
758
- raise e
697
+ return await modify_message_assessment_async(
698
+ user_id=self.user_id,
699
+ company_id=self.company_id,
700
+ assistant_message_id=assistant_message_id,
701
+ status=status,
702
+ type=type,
703
+ title=title,
704
+ explanation=explanation,
705
+ label=label,
706
+ )
707
+
708
+ def stream_complete(
709
+ self,
710
+ messages: LanguageModelMessages,
711
+ model_name: LanguageModelName | str,
712
+ content_chunks: list[ContentChunk] = [],
713
+ debug_info: dict = {},
714
+ temperature: float = DEFAULT_COMPLETE_TEMPERATURE,
715
+ timeout: int = DEFAULT_COMPLETE_TIMEOUT,
716
+ tools: Optional[list[LanguageModelTool]] = None,
717
+ start_text: Optional[str] = None,
718
+ other_options: Optional[dict] = None,
719
+ ) -> LanguageModelStreamResponse:
720
+ """
721
+ Streams a completion in the chat session synchronously.
722
+ """
723
+ [
724
+ company_id,
725
+ user_id,
726
+ assistant_message_id,
727
+ user_message_id,
728
+ chat_id,
729
+ assistant_id,
730
+ ] = validate_required_values(
731
+ [
732
+ self.company_id,
733
+ self.user_id,
734
+ self.assistant_message_id,
735
+ self.user_message_id,
736
+ self.chat_id,
737
+ self.assistant_id,
738
+ ]
739
+ )
740
+
741
+ return stream_complete_to_chat(
742
+ company_id=company_id,
743
+ user_id=user_id,
744
+ assistant_message_id=assistant_message_id,
745
+ user_message_id=user_message_id,
746
+ chat_id=chat_id,
747
+ assistant_id=assistant_id,
748
+ messages=messages,
749
+ model_name=model_name,
750
+ content_chunks=content_chunks,
751
+ debug_info=debug_info,
752
+ temperature=temperature,
753
+ timeout=timeout,
754
+ tools=tools,
755
+ start_text=start_text,
756
+ other_options=other_options,
757
+ )
758
+
759
+ async def stream_complete_async(
760
+ self,
761
+ messages: LanguageModelMessages,
762
+ model_name: LanguageModelName | str,
763
+ content_chunks: list[ContentChunk] = [],
764
+ debug_info: dict = {},
765
+ temperature: float = DEFAULT_COMPLETE_TEMPERATURE,
766
+ timeout: int = DEFAULT_COMPLETE_TIMEOUT,
767
+ tools: Optional[list[LanguageModelTool]] = None,
768
+ start_text: Optional[str] = None,
769
+ other_options: Optional[dict] = None,
770
+ ) -> LanguageModelStreamResponse:
771
+ """
772
+ Streams a completion in the chat session asynchronously.
773
+ """
774
+
775
+ [
776
+ company_id,
777
+ user_id,
778
+ assistant_message_id,
779
+ user_message_id,
780
+ chat_id,
781
+ assistant_id,
782
+ ] = validate_required_values(
783
+ [
784
+ self.company_id,
785
+ self.user_id,
786
+ self.assistant_message_id,
787
+ self.user_message_id,
788
+ self.chat_id,
789
+ self.assistant_id,
790
+ ]
791
+ )
792
+
793
+ return await stream_complete_to_chat_async(
794
+ company_id=company_id,
795
+ user_id=user_id,
796
+ assistant_message_id=assistant_message_id,
797
+ user_message_id=user_message_id,
798
+ chat_id=chat_id,
799
+ assistant_id=assistant_id,
800
+ messages=messages,
801
+ model_name=model_name,
802
+ content_chunks=content_chunks,
803
+ debug_info=debug_info,
804
+ temperature=temperature,
805
+ timeout=timeout,
806
+ tools=tools,
807
+ start_text=start_text,
808
+ other_options=other_options,
809
+ )