unique_toolkit 0.5.55__py3-none-any.whl → 0.6.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.
- unique_toolkit/_common/validate_required_values.py +21 -0
- unique_toolkit/app/__init__.py +20 -0
- unique_toolkit/app/schemas.py +73 -7
- unique_toolkit/chat/__init__.py +5 -4
- unique_toolkit/chat/constants.py +3 -0
- unique_toolkit/chat/functions.py +661 -0
- unique_toolkit/chat/schemas.py +11 -11
- unique_toolkit/chat/service.py +273 -430
- unique_toolkit/content/__init__.py +1 -0
- unique_toolkit/content/constants.py +2 -0
- unique_toolkit/content/functions.py +475 -0
- unique_toolkit/content/service.py +163 -315
- unique_toolkit/content/utils.py +32 -0
- unique_toolkit/embedding/__init__.py +3 -0
- unique_toolkit/embedding/constants.py +2 -0
- unique_toolkit/embedding/functions.py +79 -0
- unique_toolkit/embedding/service.py +47 -34
- unique_toolkit/evaluators/__init__.py +1 -0
- unique_toolkit/evaluators/constants.py +1 -0
- unique_toolkit/evaluators/context_relevancy/constants.py +3 -3
- unique_toolkit/evaluators/context_relevancy/utils.py +5 -2
- unique_toolkit/evaluators/hallucination/utils.py +2 -1
- unique_toolkit/language_model/__init__.py +1 -0
- unique_toolkit/language_model/constants.py +4 -0
- unique_toolkit/language_model/functions.py +362 -0
- unique_toolkit/language_model/service.py +246 -293
- unique_toolkit/short_term_memory/__init__.py +5 -0
- unique_toolkit/short_term_memory/constants.py +1 -0
- unique_toolkit/short_term_memory/functions.py +175 -0
- unique_toolkit/short_term_memory/service.py +153 -27
- {unique_toolkit-0.5.55.dist-info → unique_toolkit-0.6.0.dist-info}/METADATA +33 -7
- unique_toolkit-0.6.0.dist-info/RECORD +64 -0
- unique_toolkit-0.5.55.dist-info/RECORD +0 -50
- {unique_toolkit-0.5.55.dist-info → unique_toolkit-0.6.0.dist-info}/LICENSE +0 -0
- {unique_toolkit-0.5.55.dist-info → unique_toolkit-0.6.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,661 @@
|
|
1
|
+
import logging
|
2
|
+
import re
|
3
|
+
from typing import Any, Dict, List
|
4
|
+
|
5
|
+
import unique_sdk
|
6
|
+
from unique_sdk._list_object import ListObject
|
7
|
+
|
8
|
+
from unique_toolkit._common import _time_utils
|
9
|
+
from unique_toolkit.chat.constants import DEFAULT_MAX_MESSAGES
|
10
|
+
from unique_toolkit.chat.schemas import (
|
11
|
+
ChatMessage,
|
12
|
+
ChatMessageAssessment,
|
13
|
+
ChatMessageAssessmentLabel,
|
14
|
+
ChatMessageAssessmentStatus,
|
15
|
+
ChatMessageAssessmentType,
|
16
|
+
ChatMessageRole,
|
17
|
+
)
|
18
|
+
from unique_toolkit.content.schemas import ContentReference
|
19
|
+
from unique_toolkit.content.utils import count_tokens
|
20
|
+
|
21
|
+
logger = logging.getLogger(__name__)
|
22
|
+
|
23
|
+
|
24
|
+
def modify_message(
|
25
|
+
user_id: str,
|
26
|
+
company_id: str,
|
27
|
+
assistant_message_id: str,
|
28
|
+
chat_id: str,
|
29
|
+
user_message_id: str,
|
30
|
+
user_message_text: str,
|
31
|
+
assistant: bool,
|
32
|
+
content: str | None = None,
|
33
|
+
original_content: str | None = None,
|
34
|
+
references: list[ContentReference] | None = None,
|
35
|
+
debug_info: dict | None = None,
|
36
|
+
message_id: str | None = None,
|
37
|
+
set_completed_at: bool = False,
|
38
|
+
) -> ChatMessage:
|
39
|
+
"""
|
40
|
+
Modifies a chat message synchronously.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
user_id (str): The user ID.
|
44
|
+
company_id (str): The company ID.
|
45
|
+
assistant_message_id (str): The assistant message ID.
|
46
|
+
chat_id (str): The chat ID.
|
47
|
+
user_message_id (str): The user message ID.
|
48
|
+
user_message_text (str): The user message text.
|
49
|
+
assistant (bool): Whether the message is an assistant message.
|
50
|
+
content (str, optional): The new content for the message.
|
51
|
+
original_content (str, optional): The original content for the message.
|
52
|
+
message_id (str, optional): The message ID. Defaults to None, then the ChatState assistant message id is used.
|
53
|
+
references (list[ContentReference]): list of ContentReference objects. Defaults to None.
|
54
|
+
debug_info (dict[str, Any]], optional): Debug information. Defaults to None.
|
55
|
+
set_completed_at (bool, optional): Whether to set the completedAt field with the current date time. Defaults to False.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
ChatMessage: The modified message.
|
59
|
+
|
60
|
+
Raises:
|
61
|
+
Exception: If the modification fails.
|
62
|
+
"""
|
63
|
+
try:
|
64
|
+
params = _construct_message_modify_params(
|
65
|
+
user_id=user_id,
|
66
|
+
company_id=company_id,
|
67
|
+
assistant_message_id=assistant_message_id,
|
68
|
+
chat_id=chat_id,
|
69
|
+
user_message_id=user_message_id,
|
70
|
+
user_message_text=user_message_text,
|
71
|
+
assistant=assistant,
|
72
|
+
content=content,
|
73
|
+
original_content=original_content,
|
74
|
+
references=references,
|
75
|
+
debug_info=debug_info,
|
76
|
+
message_id=message_id,
|
77
|
+
set_completed_at=set_completed_at,
|
78
|
+
)
|
79
|
+
message = unique_sdk.Message.modify(**params)
|
80
|
+
return ChatMessage(**message)
|
81
|
+
except Exception as e:
|
82
|
+
logger.error(f"Failed to modify user message: {e}")
|
83
|
+
raise e
|
84
|
+
|
85
|
+
|
86
|
+
async def modify_message_async(
|
87
|
+
user_id: str,
|
88
|
+
company_id: str,
|
89
|
+
assistant_message_id: str,
|
90
|
+
chat_id: str,
|
91
|
+
user_message_id: str,
|
92
|
+
user_message_text: str,
|
93
|
+
assistant: bool,
|
94
|
+
content: str | None = None,
|
95
|
+
original_content: str | None = None,
|
96
|
+
references: list[ContentReference] | None = None,
|
97
|
+
debug_info: dict | None = None,
|
98
|
+
message_id: str | None = None,
|
99
|
+
set_completed_at: bool = False,
|
100
|
+
) -> ChatMessage:
|
101
|
+
"""
|
102
|
+
Modifies a chat message asynchronously.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
user_id (str): The user ID.
|
106
|
+
company_id (str): The company ID.
|
107
|
+
assistant_message_id (str): The assistant message ID.
|
108
|
+
chat_id (str): The chat ID.
|
109
|
+
user_message_id (str): The user message ID.
|
110
|
+
user_message_text (str): The user message text.
|
111
|
+
assistant (bool): Whether the message is an assistant message.
|
112
|
+
content (str, optional): The new content for the message.
|
113
|
+
original_content (str, optional): The original content for the message.
|
114
|
+
message_id (str, optional): The message ID. Defaults to None, then the ChatState assistant message id is used.
|
115
|
+
references (list[ContentReference]): list of ContentReference objects. Defaults to None.
|
116
|
+
debug_info (dict[str, Any]], optional): Debug information. Defaults to None.
|
117
|
+
set_completed_at (bool, optional): Whether to set the completedAt field with the current date time. Defaults to False.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
ChatMessage: The modified message.
|
121
|
+
|
122
|
+
Raises:
|
123
|
+
Exception: If the modification fails.
|
124
|
+
"""
|
125
|
+
try:
|
126
|
+
params = _construct_message_modify_params(
|
127
|
+
user_id=user_id,
|
128
|
+
company_id=company_id,
|
129
|
+
assistant_message_id=assistant_message_id,
|
130
|
+
chat_id=chat_id,
|
131
|
+
user_message_id=user_message_id,
|
132
|
+
user_message_text=user_message_text,
|
133
|
+
assistant=assistant,
|
134
|
+
content=content,
|
135
|
+
original_content=original_content,
|
136
|
+
references=references,
|
137
|
+
debug_info=debug_info,
|
138
|
+
message_id=message_id,
|
139
|
+
set_completed_at=set_completed_at,
|
140
|
+
)
|
141
|
+
message = await unique_sdk.Message.modify_async(**params)
|
142
|
+
return ChatMessage(**message)
|
143
|
+
except Exception as e:
|
144
|
+
logger.error(f"Failed to modify user message: {e}")
|
145
|
+
raise e
|
146
|
+
|
147
|
+
|
148
|
+
def map_references(references: List[ContentReference]) -> List[Dict[str, Any]]:
|
149
|
+
return [
|
150
|
+
{
|
151
|
+
"name": ref.name,
|
152
|
+
"url": ref.url,
|
153
|
+
"sequenceNumber": ref.sequence_number,
|
154
|
+
"sourceId": ref.source_id,
|
155
|
+
"source": ref.source,
|
156
|
+
}
|
157
|
+
for ref in references
|
158
|
+
]
|
159
|
+
|
160
|
+
|
161
|
+
def _construct_message_modify_params(
|
162
|
+
user_id: str,
|
163
|
+
company_id: str,
|
164
|
+
assistant_message_id: str,
|
165
|
+
chat_id: str,
|
166
|
+
user_message_id: str,
|
167
|
+
user_message_text: str,
|
168
|
+
assistant: bool = True,
|
169
|
+
content: str | None = None,
|
170
|
+
original_content: str | None = None,
|
171
|
+
references: list[ContentReference] | None = None,
|
172
|
+
debug_info: dict | None = None,
|
173
|
+
message_id: str | None = None,
|
174
|
+
set_completed_at: bool = False,
|
175
|
+
) -> Dict[str, Any]:
|
176
|
+
completed_at_datetime = None
|
177
|
+
|
178
|
+
if message_id:
|
179
|
+
# Message ID specified. No need to guess
|
180
|
+
message_id = message_id
|
181
|
+
elif assistant:
|
182
|
+
# Assistant message ID
|
183
|
+
message_id = assistant_message_id
|
184
|
+
else:
|
185
|
+
message_id = user_message_id
|
186
|
+
if content is None:
|
187
|
+
content = user_message_text
|
188
|
+
|
189
|
+
if set_completed_at:
|
190
|
+
completed_at_datetime = _time_utils.get_datetime_now()
|
191
|
+
|
192
|
+
params = {
|
193
|
+
"user_id": user_id,
|
194
|
+
"company_id": company_id,
|
195
|
+
"id": message_id,
|
196
|
+
"chatId": chat_id,
|
197
|
+
"text": content,
|
198
|
+
"originalText": original_content,
|
199
|
+
"references": map_references(references) if references else [],
|
200
|
+
"debugInfo": debug_info,
|
201
|
+
"completedAt": completed_at_datetime,
|
202
|
+
}
|
203
|
+
return params
|
204
|
+
|
205
|
+
|
206
|
+
def create_message(
|
207
|
+
user_id: str,
|
208
|
+
company_id: str,
|
209
|
+
chat_id: str,
|
210
|
+
assistant_id: str,
|
211
|
+
role: ChatMessageRole,
|
212
|
+
content: str | None = None,
|
213
|
+
original_content: str | None = None,
|
214
|
+
references: list[ContentReference] | None = None,
|
215
|
+
debug_info: dict | None = None,
|
216
|
+
set_completed_at: bool | None = False,
|
217
|
+
):
|
218
|
+
"""
|
219
|
+
Creates a message in the chat session synchronously.
|
220
|
+
|
221
|
+
Args:
|
222
|
+
user_id (str): The user ID.
|
223
|
+
company_id (str): The company ID.
|
224
|
+
chat_id (str): The chat ID.
|
225
|
+
assistant_id (str): The assistant ID.
|
226
|
+
role (ChatMessageRole): The role of the message.
|
227
|
+
content (str, optional): The content for the message. Defaults to None.
|
228
|
+
original_content (str, optional): The original content for the message. Defaults to None.
|
229
|
+
references (list[ContentReference], optional): list of ContentReference objects. Defaults to None.
|
230
|
+
debug_info (dict[str, Any]], optional): Debug information. Defaults to None.
|
231
|
+
set_completed_at (Optional[bool]): Whether to set the completedAt field with the current date time. Defaults to False.
|
232
|
+
|
233
|
+
Returns:
|
234
|
+
ChatMessage: The created message.
|
235
|
+
|
236
|
+
Raises:
|
237
|
+
Exception: If the creation fails.
|
238
|
+
"""
|
239
|
+
if original_content is None:
|
240
|
+
original_content = content
|
241
|
+
|
242
|
+
try:
|
243
|
+
params = _construct_message_create_params(
|
244
|
+
user_id=user_id,
|
245
|
+
company_id=company_id,
|
246
|
+
chat_id=chat_id,
|
247
|
+
assistant_id=assistant_id,
|
248
|
+
role=role,
|
249
|
+
content=content,
|
250
|
+
original_content=original_content,
|
251
|
+
references=references,
|
252
|
+
debug_info=debug_info,
|
253
|
+
set_completed_at=set_completed_at,
|
254
|
+
)
|
255
|
+
|
256
|
+
message = unique_sdk.Message.create(**params)
|
257
|
+
return ChatMessage(**message)
|
258
|
+
except Exception as e:
|
259
|
+
logger.error(f"Failed to create assistant message: {e}")
|
260
|
+
raise e
|
261
|
+
|
262
|
+
|
263
|
+
async def create_message_async(
|
264
|
+
user_id: str,
|
265
|
+
company_id: str,
|
266
|
+
chat_id: str,
|
267
|
+
assistant_id: str,
|
268
|
+
role: ChatMessageRole,
|
269
|
+
content: str | None = None,
|
270
|
+
original_content: str | None = None,
|
271
|
+
references: list[ContentReference] | None = None,
|
272
|
+
debug_info: dict | None = None,
|
273
|
+
set_completed_at: bool | None = False,
|
274
|
+
):
|
275
|
+
"""
|
276
|
+
Creates a message in the chat session synchronously.
|
277
|
+
|
278
|
+
Args:
|
279
|
+
user_id (str): The user ID.
|
280
|
+
company_id (str): The company ID.
|
281
|
+
chat_id (str): The chat ID.
|
282
|
+
assistant_id (str): The assistant ID.
|
283
|
+
role (ChatMessageRole): The role of the message.
|
284
|
+
content (str, optional): The content for the message. Defaults to None.
|
285
|
+
original_content (str, optional): The original content for the message. Defaults to None.
|
286
|
+
references (list[ContentReference], optional): list of ContentReference objects. Defaults to None.
|
287
|
+
debug_info (dict[str, Any]], optional): Debug information. Defaults to None.
|
288
|
+
set_completed_at (Optional[bool]): Whether to set the completedAt field with the current date time. Defaults to False.
|
289
|
+
|
290
|
+
Returns:
|
291
|
+
ChatMessage: The created message.
|
292
|
+
|
293
|
+
Raises:
|
294
|
+
Exception: If the creation fails.
|
295
|
+
"""
|
296
|
+
if original_content is None:
|
297
|
+
original_content = content
|
298
|
+
|
299
|
+
try:
|
300
|
+
params = _construct_message_create_params(
|
301
|
+
user_id=user_id,
|
302
|
+
company_id=company_id,
|
303
|
+
chat_id=chat_id,
|
304
|
+
assistant_id=assistant_id,
|
305
|
+
role=role,
|
306
|
+
content=content,
|
307
|
+
original_content=original_content,
|
308
|
+
references=references,
|
309
|
+
debug_info=debug_info,
|
310
|
+
set_completed_at=set_completed_at,
|
311
|
+
)
|
312
|
+
|
313
|
+
message = await unique_sdk.Message.create_async(**params)
|
314
|
+
return ChatMessage(**message)
|
315
|
+
except Exception as e:
|
316
|
+
logger.error(f"Failed to create assistant message: {e}")
|
317
|
+
raise e
|
318
|
+
|
319
|
+
|
320
|
+
def _construct_message_create_params(
|
321
|
+
user_id: str,
|
322
|
+
company_id: str,
|
323
|
+
chat_id: str,
|
324
|
+
assistant_id: str,
|
325
|
+
role: ChatMessageRole,
|
326
|
+
content: str | None = None,
|
327
|
+
original_content: str | None = None,
|
328
|
+
references: list[ContentReference] | None = None,
|
329
|
+
debug_info: dict | None = None,
|
330
|
+
set_completed_at: bool | None = False,
|
331
|
+
) -> Dict[str, Any]:
|
332
|
+
if original_content is None:
|
333
|
+
original_content = content
|
334
|
+
|
335
|
+
return {
|
336
|
+
"user_id": user_id,
|
337
|
+
"company_id": company_id,
|
338
|
+
"assistantId": assistant_id,
|
339
|
+
"role": role.value.upper(),
|
340
|
+
"chatId": chat_id,
|
341
|
+
"text": content,
|
342
|
+
"originalText": original_content,
|
343
|
+
"references": map_references(references) if references else [],
|
344
|
+
"debugInfo": debug_info,
|
345
|
+
"completedAt": _time_utils.get_datetime_now() if set_completed_at else None,
|
346
|
+
}
|
347
|
+
|
348
|
+
|
349
|
+
def get_selection_from_history(
|
350
|
+
full_history: list[ChatMessage],
|
351
|
+
max_tokens: int,
|
352
|
+
max_messages=DEFAULT_MAX_MESSAGES,
|
353
|
+
) -> List[ChatMessage]:
|
354
|
+
messages = full_history[-max_messages:]
|
355
|
+
filtered_messages = [m for m in messages if m.content]
|
356
|
+
mapped_messages = []
|
357
|
+
|
358
|
+
for m in filtered_messages:
|
359
|
+
m.content = re.sub(r"<sup>\d+</sup>", "", m.content or "")
|
360
|
+
m.role = (
|
361
|
+
ChatMessageRole.ASSISTANT
|
362
|
+
if m.role == ChatMessageRole.ASSISTANT
|
363
|
+
else ChatMessageRole.USER
|
364
|
+
)
|
365
|
+
mapped_messages.append(m)
|
366
|
+
|
367
|
+
return pick_messages_in_reverse_for_token_window(
|
368
|
+
messages=mapped_messages,
|
369
|
+
limit=max_tokens,
|
370
|
+
)
|
371
|
+
|
372
|
+
|
373
|
+
def map_to_chat_messages(messages: list[dict]) -> List[ChatMessage]:
|
374
|
+
return [ChatMessage(**msg) for msg in messages]
|
375
|
+
|
376
|
+
|
377
|
+
def pick_messages_in_reverse_for_token_window(
|
378
|
+
messages: list[ChatMessage],
|
379
|
+
limit: int,
|
380
|
+
) -> List[ChatMessage]:
|
381
|
+
if len(messages) < 1 or limit < 1:
|
382
|
+
return []
|
383
|
+
|
384
|
+
last_index = len(messages) - 1
|
385
|
+
token_count = count_tokens(messages[last_index].content or "")
|
386
|
+
while token_count > limit:
|
387
|
+
logger.debug(
|
388
|
+
f"Limit too low for the initial message. Last message TokenCount {token_count} available tokens {limit} - cutting message in half until it fits"
|
389
|
+
)
|
390
|
+
content = messages[last_index].content or ""
|
391
|
+
messages[last_index].content = content[: len(content) // 2] + "..."
|
392
|
+
token_count = count_tokens(messages[last_index].content or "")
|
393
|
+
|
394
|
+
while token_count <= limit and last_index > 0:
|
395
|
+
token_count = count_tokens(
|
396
|
+
"".join([msg.content or "" for msg in messages[:last_index]])
|
397
|
+
)
|
398
|
+
if token_count <= limit:
|
399
|
+
last_index -= 1
|
400
|
+
|
401
|
+
last_index = max(0, last_index)
|
402
|
+
return messages[last_index:]
|
403
|
+
|
404
|
+
|
405
|
+
def list_messages(
|
406
|
+
event_user_id, event_company_id, chat_id: str
|
407
|
+
) -> ListObject[unique_sdk.Message]:
|
408
|
+
try:
|
409
|
+
messages = unique_sdk.Message.list(
|
410
|
+
user_id=event_user_id,
|
411
|
+
company_id=event_company_id,
|
412
|
+
chatId=chat_id,
|
413
|
+
)
|
414
|
+
return messages
|
415
|
+
except Exception as e:
|
416
|
+
logger.error(f"Failed to list chat history: {e}")
|
417
|
+
raise e
|
418
|
+
|
419
|
+
|
420
|
+
async def list_messages_async(
|
421
|
+
event_user_id: str, event_company_id: str, chat_id: str
|
422
|
+
) -> ListObject[unique_sdk.Message]:
|
423
|
+
try:
|
424
|
+
messages = await unique_sdk.Message.list_async(
|
425
|
+
user_id=event_user_id,
|
426
|
+
company_id=event_company_id,
|
427
|
+
chatId=chat_id,
|
428
|
+
)
|
429
|
+
return messages
|
430
|
+
except Exception as e:
|
431
|
+
logger.error(f"Failed to list chat history: {e}")
|
432
|
+
raise e
|
433
|
+
|
434
|
+
|
435
|
+
def get_full_history(
|
436
|
+
event_user_id, event_company_id, event_payload_chat_id
|
437
|
+
) -> List[ChatMessage]:
|
438
|
+
messages = list_messages(event_user_id, event_company_id, event_payload_chat_id)
|
439
|
+
messages = filter_valid_messages(messages)
|
440
|
+
|
441
|
+
return map_to_chat_messages(messages)
|
442
|
+
|
443
|
+
|
444
|
+
async def get_full_history_async(
|
445
|
+
event_user_id, event_company_id, event_payload_chat_id
|
446
|
+
) -> List[ChatMessage]:
|
447
|
+
messages = await list_messages_async(
|
448
|
+
event_user_id, event_company_id, event_payload_chat_id
|
449
|
+
)
|
450
|
+
messages = filter_valid_messages(messages)
|
451
|
+
|
452
|
+
return map_to_chat_messages(messages)
|
453
|
+
|
454
|
+
|
455
|
+
def filter_valid_messages(
|
456
|
+
messages: ListObject[unique_sdk.Message],
|
457
|
+
) -> List[Dict[str, Any]]:
|
458
|
+
SYSTEM_MESSAGE_PREFIX = "[SYSTEM] "
|
459
|
+
|
460
|
+
# Remove the last two messages
|
461
|
+
messages = messages["data"][:-2] # type: ignore
|
462
|
+
filtered_messages = []
|
463
|
+
for message in messages:
|
464
|
+
if message["text"] is None:
|
465
|
+
continue
|
466
|
+
elif SYSTEM_MESSAGE_PREFIX in message["text"]:
|
467
|
+
continue
|
468
|
+
else:
|
469
|
+
filtered_messages.append(message)
|
470
|
+
|
471
|
+
return filtered_messages
|
472
|
+
|
473
|
+
|
474
|
+
def create_message_assessment(
|
475
|
+
user_id: str,
|
476
|
+
company_id: str,
|
477
|
+
assistant_message_id: str,
|
478
|
+
status: ChatMessageAssessmentStatus,
|
479
|
+
type: ChatMessageAssessmentType,
|
480
|
+
title: str | None = None,
|
481
|
+
explanation: str | None = None,
|
482
|
+
label: ChatMessageAssessmentLabel | None = None,
|
483
|
+
is_visible: bool = True,
|
484
|
+
) -> ChatMessageAssessment:
|
485
|
+
"""
|
486
|
+
Creates a message assessment for an assistant message synchronously.
|
487
|
+
|
488
|
+
Args:
|
489
|
+
user_id (str): The user ID.
|
490
|
+
company_id (str): The company ID.
|
491
|
+
assistant_message_id (str): The ID of the assistant message to assess
|
492
|
+
status (ChatMessageAssessmentStatus): The status of the assessment (e.g. "DONE")
|
493
|
+
type (ChatMessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
|
494
|
+
title (str | None): The title of the assessment
|
495
|
+
explanation (str | None): Explanation of the assessment
|
496
|
+
label (ChatMessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
|
497
|
+
is_visible (bool): Whether the assessment is visible to users. Defaults to True.
|
498
|
+
|
499
|
+
Returns:
|
500
|
+
ChatMessageAssessment: The created message assessment
|
501
|
+
|
502
|
+
Raises:
|
503
|
+
Exception: If the creation fails
|
504
|
+
"""
|
505
|
+
try:
|
506
|
+
assessment = unique_sdk.MessageAssessment.create(
|
507
|
+
user_id=user_id,
|
508
|
+
company_id=company_id,
|
509
|
+
messageId=assistant_message_id,
|
510
|
+
status=status.name,
|
511
|
+
explanation=explanation,
|
512
|
+
label=label.name if label else None,
|
513
|
+
title=title,
|
514
|
+
type=type.name,
|
515
|
+
isVisible=is_visible,
|
516
|
+
)
|
517
|
+
return ChatMessageAssessment(**assessment)
|
518
|
+
except Exception as e:
|
519
|
+
logger.error(f"Failed to create message assessment: {e}")
|
520
|
+
raise e
|
521
|
+
|
522
|
+
|
523
|
+
async def create_message_assessment_async(
|
524
|
+
user_id: str,
|
525
|
+
company_id: str,
|
526
|
+
assistant_message_id: str,
|
527
|
+
status: ChatMessageAssessmentStatus,
|
528
|
+
type: ChatMessageAssessmentType,
|
529
|
+
title: str | None = None,
|
530
|
+
explanation: str | None = None,
|
531
|
+
label: ChatMessageAssessmentLabel | None = None,
|
532
|
+
is_visible: bool = True,
|
533
|
+
) -> ChatMessageAssessment:
|
534
|
+
"""
|
535
|
+
Creates a message assessment for an assistant message asynchronously.
|
536
|
+
|
537
|
+
Args:
|
538
|
+
user_id (str): The user ID.
|
539
|
+
company_id (str): The company ID.
|
540
|
+
assistant_message_id (str): The ID of the assistant message to assess
|
541
|
+
status (ChatMessageAssessmentStatus): The status of the assessment (e.g. "DONE")
|
542
|
+
type (ChatMessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
|
543
|
+
title (str | None): The title of the assessment
|
544
|
+
explanation (str | None): Explanation of the assessment
|
545
|
+
label (ChatMessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
|
546
|
+
is_visible (bool): Whether the assessment is visible to users. Defaults to True.
|
547
|
+
|
548
|
+
Returns:
|
549
|
+
MessageAssessment: The created message assessment
|
550
|
+
|
551
|
+
Raises:
|
552
|
+
Exception: If the creation fails
|
553
|
+
"""
|
554
|
+
try:
|
555
|
+
assessment = await unique_sdk.MessageAssessment.create_async(
|
556
|
+
user_id=user_id,
|
557
|
+
company_id=company_id,
|
558
|
+
messageId=assistant_message_id,
|
559
|
+
status=status.name,
|
560
|
+
explanation=explanation,
|
561
|
+
label=label.name if label else None,
|
562
|
+
title=title,
|
563
|
+
type=type.name,
|
564
|
+
isVisible=is_visible,
|
565
|
+
)
|
566
|
+
return ChatMessageAssessment(**assessment)
|
567
|
+
except Exception as e:
|
568
|
+
logger.error(f"Failed to create message assessment: {e}")
|
569
|
+
raise e
|
570
|
+
|
571
|
+
|
572
|
+
def modify_message_assessment(
|
573
|
+
user_id: str,
|
574
|
+
company_id: str,
|
575
|
+
assistant_message_id: str,
|
576
|
+
status: ChatMessageAssessmentStatus,
|
577
|
+
type: ChatMessageAssessmentType,
|
578
|
+
title: str | None = None,
|
579
|
+
explanation: str | None = None,
|
580
|
+
label: ChatMessageAssessmentLabel | None = None,
|
581
|
+
) -> ChatMessageAssessment:
|
582
|
+
"""
|
583
|
+
Modifies a message assessment for an assistant message synchronously.
|
584
|
+
|
585
|
+
Args:
|
586
|
+
user_id (str): The user ID.
|
587
|
+
company_id (str): The company ID.
|
588
|
+
assistant_message_id (str): The ID of the assistant message to assess
|
589
|
+
status (MessageAssessmentStatus): The status of the assessment (e.g. "DONE")
|
590
|
+
title (str | None): The title of the assessment
|
591
|
+
explanation (str | None): Explanation of the assessment
|
592
|
+
label (ChatMessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
|
593
|
+
type (ChatMessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
|
594
|
+
|
595
|
+
Returns:
|
596
|
+
dict: The modified message assessment
|
597
|
+
|
598
|
+
Raises:
|
599
|
+
Exception: If the modification fails
|
600
|
+
"""
|
601
|
+
try:
|
602
|
+
assessment = unique_sdk.MessageAssessment.modify(
|
603
|
+
user_id=user_id,
|
604
|
+
company_id=company_id,
|
605
|
+
messageId=assistant_message_id,
|
606
|
+
status=status.name,
|
607
|
+
title=title,
|
608
|
+
explanation=explanation,
|
609
|
+
label=label.name if label else None,
|
610
|
+
type=type.name,
|
611
|
+
)
|
612
|
+
return ChatMessageAssessment(**assessment)
|
613
|
+
except Exception as e:
|
614
|
+
logger.error(f"Failed to modify message assessment: {e}")
|
615
|
+
raise e
|
616
|
+
|
617
|
+
|
618
|
+
async def modify_message_assessment_async(
|
619
|
+
user_id: str,
|
620
|
+
company_id: str,
|
621
|
+
assistant_message_id: str,
|
622
|
+
type: ChatMessageAssessmentType,
|
623
|
+
title: str | None = None,
|
624
|
+
status: ChatMessageAssessmentStatus | None = None,
|
625
|
+
explanation: str | None = None,
|
626
|
+
label: ChatMessageAssessmentLabel | None = None,
|
627
|
+
) -> ChatMessageAssessment:
|
628
|
+
"""
|
629
|
+
Modifies a message assessment for an assistant message asynchronously.
|
630
|
+
|
631
|
+
Args:
|
632
|
+
user_id (str): The user ID.
|
633
|
+
company_id (str): The company ID.
|
634
|
+
assistant_message_id (str): The ID of the assistant message to assess
|
635
|
+
type (MessageAssessmentType): The type of assessment (e.g. "HALLUCINATION")
|
636
|
+
title (str | None): The title of the assessment
|
637
|
+
status (MessageAssessmentStatus): The status of the assessment (e.g. "DONE")
|
638
|
+
explanation (str | None): Explanation of the assessment
|
639
|
+
label (MessageAssessmentLabel | None): The assessment label (e.g. "NEGATIVE")
|
640
|
+
|
641
|
+
Returns:
|
642
|
+
MessageAssessment: The modified message assessment
|
643
|
+
|
644
|
+
Raises:
|
645
|
+
Exception: If the modification fails
|
646
|
+
"""
|
647
|
+
try:
|
648
|
+
assessment = await unique_sdk.MessageAssessment.modify_async(
|
649
|
+
user_id=user_id,
|
650
|
+
company_id=company_id,
|
651
|
+
messageId=assistant_message_id,
|
652
|
+
status=status.name if status else None,
|
653
|
+
title=title,
|
654
|
+
explanation=explanation,
|
655
|
+
label=label.name if label else None,
|
656
|
+
type=type.name,
|
657
|
+
)
|
658
|
+
return ChatMessageAssessment(**assessment)
|
659
|
+
except Exception as e:
|
660
|
+
logger.error(f"Failed to modify message assessment: {e}")
|
661
|
+
raise e
|
unique_toolkit/chat/schemas.py
CHANGED
@@ -69,34 +69,34 @@ class ChatMessage(BaseModel):
|
|
69
69
|
return self
|
70
70
|
|
71
71
|
|
72
|
-
class
|
72
|
+
class ChatMessageAssessmentStatus(StrEnum):
|
73
73
|
PENDING = "PENDING"
|
74
74
|
DONE = "DONE"
|
75
75
|
ERROR = "ERROR"
|
76
76
|
|
77
77
|
|
78
|
-
class
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
UNVERIFIED = "UNVERIFIED"
|
78
|
+
class ChatMessageAssessmentLabel(StrEnum):
|
79
|
+
RED = "RED"
|
80
|
+
YELLOW = "YELLOW"
|
81
|
+
GREEN = "GREEN"
|
83
82
|
|
84
83
|
|
85
|
-
class
|
84
|
+
class ChatMessageAssessmentType(StrEnum):
|
86
85
|
HALLUCINATION = "HALLUCINATION"
|
87
86
|
COMPLIANCE = "COMPLIANCE"
|
88
87
|
|
89
88
|
|
90
|
-
class
|
89
|
+
class ChatMessageAssessment(BaseModel):
|
91
90
|
model_config = model_config
|
92
91
|
|
93
92
|
id: str
|
94
93
|
object: str
|
95
94
|
message_id: str
|
96
|
-
status:
|
97
|
-
type:
|
95
|
+
status: ChatMessageAssessmentStatus
|
96
|
+
type: ChatMessageAssessmentType
|
97
|
+
title: str | None = None
|
98
98
|
explanation: str | None = None
|
99
|
-
label:
|
99
|
+
label: ChatMessageAssessmentLabel | None = None
|
100
100
|
is_visible: bool
|
101
101
|
created_at: datetime | None = None
|
102
102
|
updated_at: datetime | None = None
|