unique_toolkit 0.8.6__py3-none-any.whl → 0.8.8__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.
@@ -38,14 +38,14 @@ from .schemas import (
38
38
  from .schemas import (
39
39
  EventUserMessage as EventUserMessage,
40
40
  )
41
- from .verification import (
42
- verify_signature_and_construct_event as verify_signature_and_construct_event,
43
- )
44
41
  from .schemas import (
45
42
  McpServer as McpServer,
46
43
  )
47
44
  from .schemas import (
48
45
  McpTool as McpTool,
49
46
  )
47
+ from .verification import (
48
+ verify_signature_and_construct_event as verify_signature_and_construct_event,
49
+ )
50
50
 
51
51
  DOMAIN_NAME = "app"
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import re
3
- from typing import Any, Dict, List, cast
3
+ from typing import Any
4
4
 
5
5
  import unique_sdk
6
6
  from typing_extensions import deprecated
@@ -22,7 +22,10 @@ from unique_toolkit.language_model.constants import (
22
22
  DEFAULT_COMPLETE_TEMPERATURE,
23
23
  DEFAULT_COMPLETE_TIMEOUT,
24
24
  )
25
- from unique_toolkit.language_model.functions import _prepare_completion_params_util
25
+ from unique_toolkit.language_model.functions import (
26
+ ChatCompletionMessageParam,
27
+ _prepare_all_completions_params_util,
28
+ )
26
29
  from unique_toolkit.language_model.infos import LanguageModelName
27
30
  from unique_toolkit.language_model.schemas import (
28
31
  LanguageModelMessages,
@@ -49,8 +52,7 @@ def modify_message(
49
52
  message_id: str | None = None,
50
53
  set_completed_at: bool = False,
51
54
  ) -> ChatMessage:
52
- """
53
- Modifies a chat message synchronously.
55
+ """Modifies a chat message synchronously.
54
56
 
55
57
  Args:
56
58
  user_id (str): The user ID.
@@ -72,6 +74,7 @@ def modify_message(
72
74
 
73
75
  Raises:
74
76
  Exception: If the modification fails.
77
+
75
78
  """
76
79
  try:
77
80
  params = _construct_message_modify_params(
@@ -111,8 +114,7 @@ async def modify_message_async(
111
114
  message_id: str | None = None,
112
115
  set_completed_at: bool = False,
113
116
  ) -> ChatMessage:
114
- """
115
- Modifies a chat message asynchronously.
117
+ """Modifies a chat message asynchronously.
116
118
 
117
119
  Args:
118
120
  user_id (str): The user ID.
@@ -134,6 +136,7 @@ async def modify_message_async(
134
136
 
135
137
  Raises:
136
138
  Exception: If the modification fails.
139
+
137
140
  """
138
141
  try:
139
142
  params = _construct_message_modify_params(
@@ -158,7 +161,7 @@ async def modify_message_async(
158
161
  raise e
159
162
 
160
163
 
161
- def map_references(references: List[ContentReference]) -> List[Dict[str, Any]]:
164
+ def map_references(references: list[ContentReference]) -> list[dict[str, Any]]:
162
165
  return [
163
166
  {
164
167
  "name": ref.name,
@@ -185,7 +188,7 @@ def _construct_message_modify_params(
185
188
  debug_info: dict | None = None,
186
189
  message_id: str | None = None,
187
190
  set_completed_at: bool = False,
188
- ) -> Dict[str, Any]:
191
+ ) -> dict[str, Any]:
189
192
  completed_at_datetime = None
190
193
 
191
194
  if message_id:
@@ -228,8 +231,7 @@ def create_message(
228
231
  debug_info: dict | None = None,
229
232
  set_completed_at: bool | None = False,
230
233
  ):
231
- """
232
- Creates a message in the chat session synchronously.
234
+ """Creates a message in the chat session synchronously.
233
235
 
234
236
  Args:
235
237
  user_id (str): The user ID.
@@ -248,6 +250,7 @@ def create_message(
248
250
 
249
251
  Raises:
250
252
  Exception: If the creation fails.
253
+
251
254
  """
252
255
  if original_content is None:
253
256
  original_content = content
@@ -285,8 +288,7 @@ async def create_message_async(
285
288
  debug_info: dict | None = None,
286
289
  set_completed_at: bool | None = False,
287
290
  ):
288
- """
289
- Creates a message in the chat session synchronously.
291
+ """Creates a message in the chat session synchronously.
290
292
 
291
293
  Args:
292
294
  user_id (str): The user ID.
@@ -305,6 +307,7 @@ async def create_message_async(
305
307
 
306
308
  Raises:
307
309
  Exception: If the creation fails.
310
+
308
311
  """
309
312
  if original_content is None:
310
313
  original_content = content
@@ -341,7 +344,7 @@ def _construct_message_create_params(
341
344
  references: list[ContentReference] | None = None,
342
345
  debug_info: dict | None = None,
343
346
  set_completed_at: bool | None = False,
344
- ) -> Dict[str, Any]:
347
+ ) -> dict[str, Any]:
345
348
  if original_content is None:
346
349
  original_content = content
347
350
 
@@ -354,7 +357,7 @@ def _construct_message_create_params(
354
357
  "text": content,
355
358
  "originalText": original_content,
356
359
  "references": map_references(references) if references else [],
357
- "debugInfo": debug_info,
360
+ "debugInfo": debug_info or {},
358
361
  "completedAt": _time_utils.get_datetime_now() if set_completed_at else None,
359
362
  }
360
363
 
@@ -363,7 +366,7 @@ def get_selection_from_history(
363
366
  full_history: list[ChatMessage],
364
367
  max_tokens: int,
365
368
  max_messages=DEFAULT_MAX_MESSAGES,
366
- ) -> List[ChatMessage]:
369
+ ) -> list[ChatMessage]:
367
370
  messages = full_history[-max_messages:]
368
371
  filtered_messages = [m for m in messages if m.content]
369
372
  mapped_messages = []
@@ -383,14 +386,14 @@ def get_selection_from_history(
383
386
  )
384
387
 
385
388
 
386
- def map_to_chat_messages(messages: list[dict]) -> List[ChatMessage]:
389
+ def map_to_chat_messages(messages: list[dict]) -> list[ChatMessage]:
387
390
  return [ChatMessage(**msg) for msg in messages]
388
391
 
389
392
 
390
393
  def pick_messages_in_reverse_for_token_window(
391
394
  messages: list[ChatMessage],
392
395
  limit: int,
393
- ) -> List[ChatMessage]:
396
+ ) -> list[ChatMessage]:
394
397
  if len(messages) < 1 or limit < 1:
395
398
  return []
396
399
 
@@ -398,7 +401,7 @@ def pick_messages_in_reverse_for_token_window(
398
401
  token_count = count_tokens(messages[last_index].content or "")
399
402
  while token_count > limit:
400
403
  logger.debug(
401
- f"Limit too low for the initial message. Last message TokenCount {token_count} available tokens {limit} - cutting message in half until it fits"
404
+ f"Limit too low for the initial message. Last message TokenCount {token_count} available tokens {limit} - cutting message in half until it fits",
402
405
  )
403
406
  content = messages[last_index].content or ""
404
407
  messages[last_index].content = content[: len(content) // 2] + "..."
@@ -406,7 +409,7 @@ def pick_messages_in_reverse_for_token_window(
406
409
 
407
410
  while token_count <= limit and last_index > 0:
408
411
  token_count = count_tokens(
409
- "".join([msg.content or "" for msg in messages[:last_index]])
412
+ "".join([msg.content or "" for msg in messages[:last_index]]),
410
413
  )
411
414
  if token_count <= limit:
412
415
  last_index -= 1
@@ -416,7 +419,9 @@ def pick_messages_in_reverse_for_token_window(
416
419
 
417
420
 
418
421
  def list_messages(
419
- event_user_id, event_company_id, chat_id: str
422
+ event_user_id: str,
423
+ event_company_id: str,
424
+ chat_id: str,
420
425
  ) -> ListObject[unique_sdk.Message]:
421
426
  try:
422
427
  messages = unique_sdk.Message.list(
@@ -431,7 +436,9 @@ def list_messages(
431
436
 
432
437
 
433
438
  async def list_messages_async(
434
- event_user_id: str, event_company_id: str, chat_id: str
439
+ event_user_id: str,
440
+ event_company_id: str,
441
+ chat_id: str,
435
442
  ) -> ListObject[unique_sdk.Message]:
436
443
  try:
437
444
  messages = await unique_sdk.Message.list_async(
@@ -446,8 +453,10 @@ async def list_messages_async(
446
453
 
447
454
 
448
455
  def get_full_history(
449
- event_user_id, event_company_id, event_payload_chat_id
450
- ) -> List[ChatMessage]:
456
+ event_user_id: str,
457
+ event_company_id: str,
458
+ event_payload_chat_id: str,
459
+ ) -> list[ChatMessage]:
451
460
  messages = list_messages(event_user_id, event_company_id, event_payload_chat_id)
452
461
  messages = filter_valid_messages(messages)
453
462
 
@@ -455,10 +464,14 @@ def get_full_history(
455
464
 
456
465
 
457
466
  async def get_full_history_async(
458
- event_user_id, event_company_id, event_payload_chat_id
459
- ) -> List[ChatMessage]:
467
+ event_user_id: str,
468
+ event_company_id: str,
469
+ event_payload_chat_id: str,
470
+ ) -> list[ChatMessage]:
460
471
  messages = await list_messages_async(
461
- event_user_id, event_company_id, event_payload_chat_id
472
+ event_user_id,
473
+ event_company_id,
474
+ event_payload_chat_id,
462
475
  )
463
476
  messages = filter_valid_messages(messages)
464
477
 
@@ -467,19 +480,16 @@ async def get_full_history_async(
467
480
 
468
481
  def filter_valid_messages(
469
482
  messages: ListObject[unique_sdk.Message],
470
- ) -> List[Dict[str, Any]]:
483
+ ) -> list[dict[str, Any]]:
471
484
  SYSTEM_MESSAGE_PREFIX = "[SYSTEM] "
472
485
 
473
486
  # Remove the last two messages
474
487
  messages = messages["data"][:-2] # type: ignore
475
488
  filtered_messages = []
476
489
  for message in messages:
477
- if message["text"] is None:
478
- continue
479
- elif SYSTEM_MESSAGE_PREFIX in message["text"]:
490
+ if message["text"] is None or SYSTEM_MESSAGE_PREFIX in message["text"]:
480
491
  continue
481
- else:
482
- filtered_messages.append(message)
492
+ filtered_messages.append(message)
483
493
 
484
494
  return filtered_messages
485
495
 
@@ -495,8 +505,7 @@ def create_message_assessment(
495
505
  label: ChatMessageAssessmentLabel | None = None,
496
506
  is_visible: bool = True,
497
507
  ) -> ChatMessageAssessment:
498
- """
499
- Creates a message assessment for an assistant message synchronously.
508
+ """Creates a message assessment for an assistant message synchronously.
500
509
 
501
510
  Args:
502
511
  user_id (str): The user ID.
@@ -514,6 +523,7 @@ def create_message_assessment(
514
523
 
515
524
  Raises:
516
525
  Exception: If the creation fails
526
+
517
527
  """
518
528
  try:
519
529
  assessment = unique_sdk.MessageAssessment.create(
@@ -544,8 +554,7 @@ async def create_message_assessment_async(
544
554
  label: ChatMessageAssessmentLabel | None = None,
545
555
  is_visible: bool = True,
546
556
  ) -> ChatMessageAssessment:
547
- """
548
- Creates a message assessment for an assistant message asynchronously.
557
+ """Creates a message assessment for an assistant message asynchronously.
549
558
 
550
559
  Args:
551
560
  user_id (str): The user ID.
@@ -563,6 +572,7 @@ async def create_message_assessment_async(
563
572
 
564
573
  Raises:
565
574
  Exception: If the creation fails
575
+
566
576
  """
567
577
  try:
568
578
  assessment = await unique_sdk.MessageAssessment.create_async(
@@ -592,8 +602,7 @@ def modify_message_assessment(
592
602
  explanation: str | None = None,
593
603
  label: ChatMessageAssessmentLabel | None = None,
594
604
  ) -> ChatMessageAssessment:
595
- """
596
- Modifies a message assessment for an assistant message synchronously.
605
+ """Modifies a message assessment for an assistant message synchronously.
597
606
 
598
607
  Args:
599
608
  user_id (str): The user ID.
@@ -610,6 +619,7 @@ def modify_message_assessment(
610
619
 
611
620
  Raises:
612
621
  Exception: If the modification fails
622
+
613
623
  """
614
624
  try:
615
625
  assessment = unique_sdk.MessageAssessment.modify(
@@ -638,8 +648,7 @@ async def modify_message_assessment_async(
638
648
  explanation: str | None = None,
639
649
  label: ChatMessageAssessmentLabel | None = None,
640
650
  ) -> ChatMessageAssessment:
641
- """
642
- Modifies a message assessment for an assistant message asynchronously.
651
+ """Modifies a message assessment for an assistant message asynchronously.
643
652
 
644
653
  Args:
645
654
  user_id (str): The user ID.
@@ -656,6 +665,7 @@ async def modify_message_assessment_async(
656
665
 
657
666
  Raises:
658
667
  Exception: If the modification fails
668
+
659
669
  """
660
670
  try:
661
671
  assessment = await unique_sdk.MessageAssessment.modify_async(
@@ -682,7 +692,7 @@ def stream_complete_to_chat(
682
692
  user_message_id: str,
683
693
  chat_id: str,
684
694
  assistant_id: str,
685
- messages: LanguageModelMessages,
695
+ messages: LanguageModelMessages | list[ChatCompletionMessageParam],
686
696
  model_name: LanguageModelName | str,
687
697
  content_chunks: list[ContentChunk] | None = None,
688
698
  debug_info: dict = {},
@@ -718,18 +728,17 @@ def stream_complete_with_references(
718
728
  user_message_id: str,
719
729
  chat_id: str,
720
730
  assistant_id: str,
721
- messages: LanguageModelMessages,
731
+ messages: LanguageModelMessages | list[ChatCompletionMessageParam],
722
732
  model_name: LanguageModelName | str,
723
733
  content_chunks: list[ContentChunk] | None = None,
724
- debug_info: dict = {},
734
+ debug_info: dict | None = None,
725
735
  temperature: float = DEFAULT_COMPLETE_TEMPERATURE,
726
736
  timeout: int = DEFAULT_COMPLETE_TIMEOUT,
727
737
  tools: list[LanguageModelTool | LanguageModelToolDescription] | None = None,
728
738
  start_text: str | None = None,
729
739
  other_options: dict | None = None,
730
740
  ) -> LanguageModelStreamResponse:
731
- """
732
- Streams a completion synchronously.
741
+ """Streams a completion synchronously.
733
742
 
734
743
  Args:
735
744
  company_id (str): The company ID associated with the request.
@@ -750,14 +759,17 @@ def stream_complete_with_references(
750
759
 
751
760
  Returns:
752
761
  LanguageModelStreamResponse: The streaming response object.
762
+
753
763
  """
754
- options, model, messages_dict, search_context = _prepare_completion_params_util(
755
- messages=messages,
756
- model_name=model_name,
757
- temperature=temperature,
758
- tools=tools,
759
- other_options=other_options,
760
- content_chunks=content_chunks or [],
764
+ options, model, messages_dict, search_context = (
765
+ _prepare_all_completions_params_util(
766
+ messages=messages,
767
+ model_name=model_name,
768
+ temperature=temperature,
769
+ tools=tools,
770
+ other_options=other_options,
771
+ content_chunks=content_chunks or [],
772
+ )
761
773
  )
762
774
 
763
775
  try:
@@ -766,16 +778,13 @@ def stream_complete_with_references(
766
778
  company_id=company_id,
767
779
  assistantMessageId=assistant_message_id,
768
780
  userMessageId=user_message_id,
769
- messages=cast(
770
- list[unique_sdk.Integrated.ChatCompletionRequestMessage],
771
- messages_dict,
772
- ),
781
+ messages=messages_dict,
773
782
  chatId=chat_id,
774
783
  searchContext=search_context,
775
784
  model=model,
776
785
  timeout=timeout,
777
786
  assistantId=assistant_id,
778
- debugInfo=debug_info,
787
+ debugInfo=debug_info or {},
779
788
  options=options, # type: ignore
780
789
  startText=start_text,
781
790
  )
@@ -793,7 +802,7 @@ async def stream_complete_to_chat_async(
793
802
  user_message_id: str,
794
803
  chat_id: str,
795
804
  assistant_id: str,
796
- messages: LanguageModelMessages,
805
+ messages: LanguageModelMessages | list[ChatCompletionMessageParam],
797
806
  model_name: LanguageModelName | str,
798
807
  content_chunks: list[ContentChunk] | None = None,
799
808
  debug_info: dict = {},
@@ -829,31 +838,33 @@ async def stream_complete_with_references_async(
829
838
  user_message_id: str,
830
839
  chat_id: str,
831
840
  assistant_id: str,
832
- messages: LanguageModelMessages,
841
+ messages: LanguageModelMessages | list[ChatCompletionMessageParam],
833
842
  model_name: LanguageModelName | str,
834
843
  content_chunks: list[ContentChunk] | None = None,
835
- debug_info: dict = {},
844
+ debug_info: dict | None = None,
836
845
  temperature: float = DEFAULT_COMPLETE_TEMPERATURE,
837
846
  timeout: int = DEFAULT_COMPLETE_TIMEOUT,
838
847
  tools: list[LanguageModelTool | LanguageModelToolDescription] | None = None,
839
848
  start_text: str | None = None,
840
849
  other_options: dict | None = None,
841
850
  ) -> LanguageModelStreamResponse:
842
- """
843
- Streams a completion asynchronously.
851
+ """Streams a completion asynchronously.
844
852
 
845
853
  Args: [same as stream_complete]
846
854
 
847
855
  Returns:
848
856
  LanguageModelStreamResponse: The streaming response object.
857
+
849
858
  """
850
- options, model, messages_dict, search_context = _prepare_completion_params_util(
851
- messages=messages,
852
- model_name=model_name,
853
- temperature=temperature,
854
- tools=tools,
855
- other_options=other_options,
856
- content_chunks=content_chunks or [],
859
+ options, model, messages_dict, search_context = (
860
+ _prepare_all_completions_params_util(
861
+ messages=messages,
862
+ model_name=model_name,
863
+ temperature=temperature,
864
+ tools=tools,
865
+ other_options=other_options,
866
+ content_chunks=content_chunks or [],
867
+ )
857
868
  )
858
869
 
859
870
  try:
@@ -862,16 +873,13 @@ async def stream_complete_with_references_async(
862
873
  company_id=company_id,
863
874
  assistantMessageId=assistant_message_id,
864
875
  userMessageId=user_message_id,
865
- messages=cast(
866
- list[unique_sdk.Integrated.ChatCompletionRequestMessage],
867
- messages_dict,
868
- ),
876
+ messages=messages_dict,
869
877
  chatId=chat_id,
870
878
  searchContext=search_context,
871
879
  model=model,
872
880
  timeout=timeout,
873
881
  assistantId=assistant_id,
874
- debugInfo=debug_info,
882
+ debugInfo=debug_info or {},
875
883
  options=options, # type: ignore
876
884
  startText=start_text,
877
885
  )
@@ -2,6 +2,16 @@ from datetime import datetime
2
2
  from enum import StrEnum
3
3
 
4
4
  from humps import camelize
5
+ from openai.types.chat import (
6
+ ChatCompletionAssistantMessageParam,
7
+ ChatCompletionUserMessageParam,
8
+ )
9
+ from openai.types.chat.chat_completion_message_function_tool_call_param import (
10
+ ChatCompletionMessageFunctionToolCallParam,
11
+ )
12
+ from openai.types.chat.chat_completion_message_function_tool_call_param import (
13
+ Function as OpenAIFunction,
14
+ )
5
15
  from pydantic import (
6
16
  BaseModel,
7
17
  ConfigDict,
@@ -14,14 +24,15 @@ from unique_toolkit.content.schemas import ContentReference
14
24
 
15
25
  # set config to convert camelCase to snake_case
16
26
  model_config = ConfigDict(
17
- alias_generator=camelize, populate_by_name=True, arbitrary_types_allowed=True
27
+ alias_generator=camelize,
28
+ populate_by_name=True,
18
29
  )
19
30
 
20
31
 
21
32
  class ChatMessageRole(StrEnum):
22
33
  USER = "user"
23
34
  ASSISTANT = "assistant"
24
- TOOL = "tool"
35
+ TOOL = "tool" # TODO: Unused according @unique-fabian. To be removed in separate PR
25
36
 
26
37
 
27
38
  class Function(BaseModel):
@@ -30,6 +41,12 @@ class Function(BaseModel):
30
41
  name: str
31
42
  arguments: str
32
43
 
44
+ def to_openai(self) -> OpenAIFunction:
45
+ return OpenAIFunction(
46
+ arguments=self.arguments,
47
+ name=self.name,
48
+ )
49
+
33
50
 
34
51
  class ToolCall(BaseModel):
35
52
  model_config = model_config
@@ -38,8 +55,16 @@ class ToolCall(BaseModel):
38
55
  type: str
39
56
  function: Function
40
57
 
58
+ def to_openai_param(self) -> ChatCompletionMessageFunctionToolCallParam:
59
+ return ChatCompletionMessageFunctionToolCallParam(
60
+ id=self.id,
61
+ function=self.function.to_openai(),
62
+ type="function",
63
+ )
64
+
41
65
 
42
66
  class ChatMessage(BaseModel):
67
+ # TODO: The below seems not to be True anymore @irina-unique. To be checked in separate PR
43
68
  # This model should strictly meets https://github.com/Unique-AG/monorepo/blob/master/node/apps/node-chat/src/public-api/2023-12-06/dtos/message/public-message.dto.ts
44
69
  model_config = model_config
45
70
 
@@ -71,6 +96,27 @@ class ChatMessage(BaseModel):
71
96
  raise ValueError("tool_call_ids is required when role is 'tool'")
72
97
  return self
73
98
 
99
+ def to_openai_param(self):
100
+ match self.role:
101
+ case ChatMessageRole.USER:
102
+ return ChatCompletionUserMessageParam(
103
+ role="user",
104
+ content=self.content or "",
105
+ )
106
+
107
+ case ChatMessageRole.ASSISTANT:
108
+ return ChatCompletionAssistantMessageParam(
109
+ role="assistant",
110
+ audio=None,
111
+ content=self.content or "",
112
+ function_call=None,
113
+ refusal=None,
114
+ tool_calls=[t.to_openai_param() for t in self.tool_calls or []],
115
+ )
116
+
117
+ case ChatMessageRole.TOOL:
118
+ raise NotImplementedError
119
+
74
120
 
75
121
  class ChatMessageAssessmentStatus(StrEnum):
76
122
  PENDING = "PENDING"