unique_toolkit 0.7.9__py3-none-any.whl → 1.33.3__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.
Files changed (190) hide show
  1. unique_toolkit/__init__.py +36 -3
  2. unique_toolkit/_common/api_calling/human_verification_manager.py +357 -0
  3. unique_toolkit/_common/base_model_type_attribute.py +303 -0
  4. unique_toolkit/_common/chunk_relevancy_sorter/config.py +49 -0
  5. unique_toolkit/_common/chunk_relevancy_sorter/exception.py +5 -0
  6. unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +46 -0
  7. unique_toolkit/_common/chunk_relevancy_sorter/service.py +374 -0
  8. unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +275 -0
  9. unique_toolkit/_common/default_language_model.py +12 -0
  10. unique_toolkit/_common/docx_generator/__init__.py +7 -0
  11. unique_toolkit/_common/docx_generator/config.py +12 -0
  12. unique_toolkit/_common/docx_generator/schemas.py +80 -0
  13. unique_toolkit/_common/docx_generator/service.py +225 -0
  14. unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
  15. unique_toolkit/_common/endpoint_builder.py +368 -0
  16. unique_toolkit/_common/endpoint_requestor.py +480 -0
  17. unique_toolkit/_common/exception.py +24 -0
  18. unique_toolkit/_common/experimental/endpoint_builder.py +368 -0
  19. unique_toolkit/_common/experimental/endpoint_requestor.py +488 -0
  20. unique_toolkit/_common/feature_flags/schema.py +9 -0
  21. unique_toolkit/_common/pydantic/rjsf_tags.py +936 -0
  22. unique_toolkit/_common/pydantic_helpers.py +174 -0
  23. unique_toolkit/_common/referencing.py +53 -0
  24. unique_toolkit/_common/string_utilities.py +140 -0
  25. unique_toolkit/_common/tests/test_referencing.py +521 -0
  26. unique_toolkit/_common/tests/test_string_utilities.py +506 -0
  27. unique_toolkit/_common/token/image_token_counting.py +67 -0
  28. unique_toolkit/_common/token/token_counting.py +204 -0
  29. unique_toolkit/_common/utils/__init__.py +1 -0
  30. unique_toolkit/_common/utils/files.py +43 -0
  31. unique_toolkit/_common/utils/image/encode.py +25 -0
  32. unique_toolkit/_common/utils/jinja/helpers.py +10 -0
  33. unique_toolkit/_common/utils/jinja/render.py +18 -0
  34. unique_toolkit/_common/utils/jinja/schema.py +65 -0
  35. unique_toolkit/_common/utils/jinja/utils.py +80 -0
  36. unique_toolkit/_common/utils/structured_output/__init__.py +1 -0
  37. unique_toolkit/_common/utils/structured_output/schema.py +5 -0
  38. unique_toolkit/_common/utils/write_configuration.py +51 -0
  39. unique_toolkit/_common/validators.py +101 -4
  40. unique_toolkit/agentic/__init__.py +1 -0
  41. unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +28 -0
  42. unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +278 -0
  43. unique_toolkit/agentic/evaluation/config.py +36 -0
  44. unique_toolkit/{evaluators → agentic/evaluation}/context_relevancy/prompts.py +25 -0
  45. unique_toolkit/agentic/evaluation/context_relevancy/schema.py +80 -0
  46. unique_toolkit/agentic/evaluation/context_relevancy/service.py +273 -0
  47. unique_toolkit/agentic/evaluation/evaluation_manager.py +218 -0
  48. unique_toolkit/agentic/evaluation/hallucination/constants.py +61 -0
  49. unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +112 -0
  50. unique_toolkit/{evaluators → agentic/evaluation}/hallucination/prompts.py +1 -1
  51. unique_toolkit/{evaluators → agentic/evaluation}/hallucination/service.py +20 -16
  52. unique_toolkit/{evaluators → agentic/evaluation}/hallucination/utils.py +32 -21
  53. unique_toolkit/{evaluators → agentic/evaluation}/output_parser.py +20 -2
  54. unique_toolkit/{evaluators → agentic/evaluation}/schemas.py +27 -7
  55. unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +253 -0
  56. unique_toolkit/agentic/evaluation/tests/test_output_parser.py +87 -0
  57. unique_toolkit/agentic/history_manager/history_construction_with_contents.py +298 -0
  58. unique_toolkit/agentic/history_manager/history_manager.py +241 -0
  59. unique_toolkit/agentic/history_manager/loop_token_reducer.py +484 -0
  60. unique_toolkit/agentic/history_manager/utils.py +96 -0
  61. unique_toolkit/agentic/message_log_manager/__init__.py +5 -0
  62. unique_toolkit/agentic/message_log_manager/service.py +93 -0
  63. unique_toolkit/agentic/postprocessor/postprocessor_manager.py +212 -0
  64. unique_toolkit/agentic/reference_manager/reference_manager.py +103 -0
  65. unique_toolkit/agentic/responses_api/__init__.py +19 -0
  66. unique_toolkit/agentic/responses_api/postprocessors/code_display.py +71 -0
  67. unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +297 -0
  68. unique_toolkit/agentic/responses_api/stream_handler.py +15 -0
  69. unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +141 -0
  70. unique_toolkit/agentic/thinking_manager/thinking_manager.py +103 -0
  71. unique_toolkit/agentic/tools/__init__.py +1 -0
  72. unique_toolkit/agentic/tools/a2a/__init__.py +36 -0
  73. unique_toolkit/agentic/tools/a2a/config.py +17 -0
  74. unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +15 -0
  75. unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +66 -0
  76. unique_toolkit/agentic/tools/a2a/evaluation/config.py +55 -0
  77. unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +260 -0
  78. unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +9 -0
  79. unique_toolkit/agentic/tools/a2a/manager.py +55 -0
  80. unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +21 -0
  81. unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +240 -0
  82. unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +84 -0
  83. unique_toolkit/agentic/tools/a2a/postprocessing/config.py +78 -0
  84. unique_toolkit/agentic/tools/a2a/postprocessing/display.py +264 -0
  85. unique_toolkit/agentic/tools/a2a/postprocessing/references.py +101 -0
  86. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display.py +421 -0
  87. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +2103 -0
  88. unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +603 -0
  89. unique_toolkit/agentic/tools/a2a/prompts.py +46 -0
  90. unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +6 -0
  91. unique_toolkit/agentic/tools/a2a/response_watcher/service.py +91 -0
  92. unique_toolkit/agentic/tools/a2a/tool/__init__.py +4 -0
  93. unique_toolkit/agentic/tools/a2a/tool/_memory.py +26 -0
  94. unique_toolkit/agentic/tools/a2a/tool/_schema.py +9 -0
  95. unique_toolkit/agentic/tools/a2a/tool/config.py +158 -0
  96. unique_toolkit/agentic/tools/a2a/tool/service.py +393 -0
  97. unique_toolkit/agentic/tools/agent_chunks_hanlder.py +65 -0
  98. unique_toolkit/agentic/tools/config.py +128 -0
  99. unique_toolkit/agentic/tools/factory.py +44 -0
  100. unique_toolkit/agentic/tools/mcp/__init__.py +4 -0
  101. unique_toolkit/agentic/tools/mcp/manager.py +71 -0
  102. unique_toolkit/agentic/tools/mcp/models.py +28 -0
  103. unique_toolkit/agentic/tools/mcp/tool_wrapper.py +234 -0
  104. unique_toolkit/agentic/tools/openai_builtin/__init__.py +11 -0
  105. unique_toolkit/agentic/tools/openai_builtin/base.py +46 -0
  106. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +8 -0
  107. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +88 -0
  108. unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +250 -0
  109. unique_toolkit/agentic/tools/openai_builtin/manager.py +79 -0
  110. unique_toolkit/agentic/tools/schemas.py +145 -0
  111. unique_toolkit/agentic/tools/test/test_mcp_manager.py +536 -0
  112. unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +445 -0
  113. unique_toolkit/agentic/tools/tool.py +187 -0
  114. unique_toolkit/agentic/tools/tool_manager.py +492 -0
  115. unique_toolkit/agentic/tools/tool_progress_reporter.py +285 -0
  116. unique_toolkit/agentic/tools/utils/__init__.py +19 -0
  117. unique_toolkit/agentic/tools/utils/execution/__init__.py +1 -0
  118. unique_toolkit/agentic/tools/utils/execution/execution.py +286 -0
  119. unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
  120. unique_toolkit/agentic/tools/utils/source_handling/schema.py +21 -0
  121. unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +207 -0
  122. unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +216 -0
  123. unique_toolkit/app/__init__.py +9 -0
  124. unique_toolkit/app/dev_util.py +180 -0
  125. unique_toolkit/app/fast_api_factory.py +131 -0
  126. unique_toolkit/app/init_sdk.py +32 -1
  127. unique_toolkit/app/schemas.py +206 -31
  128. unique_toolkit/app/unique_settings.py +367 -0
  129. unique_toolkit/app/webhook.py +77 -0
  130. unique_toolkit/chat/__init__.py +8 -1
  131. unique_toolkit/chat/deprecated/service.py +232 -0
  132. unique_toolkit/chat/functions.py +648 -78
  133. unique_toolkit/chat/rendering.py +34 -0
  134. unique_toolkit/chat/responses_api.py +461 -0
  135. unique_toolkit/chat/schemas.py +134 -2
  136. unique_toolkit/chat/service.py +115 -767
  137. unique_toolkit/content/functions.py +353 -8
  138. unique_toolkit/content/schemas.py +128 -15
  139. unique_toolkit/content/service.py +321 -45
  140. unique_toolkit/content/smart_rules.py +301 -0
  141. unique_toolkit/content/utils.py +10 -3
  142. unique_toolkit/data_extraction/README.md +96 -0
  143. unique_toolkit/data_extraction/__init__.py +11 -0
  144. unique_toolkit/data_extraction/augmented/__init__.py +5 -0
  145. unique_toolkit/data_extraction/augmented/service.py +93 -0
  146. unique_toolkit/data_extraction/base.py +25 -0
  147. unique_toolkit/data_extraction/basic/__init__.py +11 -0
  148. unique_toolkit/data_extraction/basic/config.py +18 -0
  149. unique_toolkit/data_extraction/basic/prompt.py +13 -0
  150. unique_toolkit/data_extraction/basic/service.py +55 -0
  151. unique_toolkit/embedding/service.py +103 -12
  152. unique_toolkit/framework_utilities/__init__.py +1 -0
  153. unique_toolkit/framework_utilities/langchain/__init__.py +10 -0
  154. unique_toolkit/framework_utilities/langchain/client.py +71 -0
  155. unique_toolkit/framework_utilities/langchain/history.py +19 -0
  156. unique_toolkit/framework_utilities/openai/__init__.py +6 -0
  157. unique_toolkit/framework_utilities/openai/client.py +84 -0
  158. unique_toolkit/framework_utilities/openai/message_builder.py +229 -0
  159. unique_toolkit/framework_utilities/utils.py +23 -0
  160. unique_toolkit/language_model/__init__.py +3 -0
  161. unique_toolkit/language_model/_responses_api_utils.py +93 -0
  162. unique_toolkit/language_model/builder.py +27 -11
  163. unique_toolkit/language_model/default_language_model.py +3 -0
  164. unique_toolkit/language_model/functions.py +345 -43
  165. unique_toolkit/language_model/infos.py +1288 -46
  166. unique_toolkit/language_model/reference.py +242 -0
  167. unique_toolkit/language_model/schemas.py +481 -49
  168. unique_toolkit/language_model/service.py +229 -28
  169. unique_toolkit/protocols/support.py +145 -0
  170. unique_toolkit/services/__init__.py +7 -0
  171. unique_toolkit/services/chat_service.py +1631 -0
  172. unique_toolkit/services/knowledge_base.py +1094 -0
  173. unique_toolkit/short_term_memory/service.py +178 -41
  174. unique_toolkit/smart_rules/__init__.py +0 -0
  175. unique_toolkit/smart_rules/compile.py +56 -0
  176. unique_toolkit/test_utilities/events.py +197 -0
  177. unique_toolkit-1.33.3.dist-info/METADATA +1145 -0
  178. unique_toolkit-1.33.3.dist-info/RECORD +205 -0
  179. unique_toolkit/evaluators/__init__.py +0 -1
  180. unique_toolkit/evaluators/config.py +0 -35
  181. unique_toolkit/evaluators/constants.py +0 -1
  182. unique_toolkit/evaluators/context_relevancy/constants.py +0 -32
  183. unique_toolkit/evaluators/context_relevancy/service.py +0 -53
  184. unique_toolkit/evaluators/context_relevancy/utils.py +0 -142
  185. unique_toolkit/evaluators/hallucination/constants.py +0 -41
  186. unique_toolkit-0.7.9.dist-info/METADATA +0 -413
  187. unique_toolkit-0.7.9.dist-info/RECORD +0 -64
  188. /unique_toolkit/{evaluators → agentic/evaluation}/exception.py +0 -0
  189. {unique_toolkit-0.7.9.dist-info → unique_toolkit-1.33.3.dist-info}/LICENSE +0 -0
  190. {unique_toolkit-0.7.9.dist-info → unique_toolkit-1.33.3.dist-info}/WHEEL +0 -0
@@ -1,8 +1,11 @@
1
+ from typing import overload
2
+
1
3
  from typing_extensions import deprecated
2
4
 
3
5
  from unique_toolkit._common._base_service import BaseService
4
6
  from unique_toolkit._common.validate_required_values import validate_required_values
5
7
  from unique_toolkit.app.schemas import BaseEvent, Event
8
+ from unique_toolkit.app.unique_settings import UniqueSettings
6
9
  from unique_toolkit.embedding.constants import DEFAULT_TIMEOUT
7
10
  from unique_toolkit.embedding.functions import embed_texts, embed_texts_async
8
11
  from unique_toolkit.embedding.schemas import Embeddings
@@ -11,10 +14,23 @@ from unique_toolkit.embedding.schemas import Embeddings
11
14
  class EmbeddingService(BaseService):
12
15
  """
13
16
  Provides methods to interact with the Embedding service.
17
+ """
14
18
 
15
- Attributes:
16
- company_id (str | None): The company ID.
17
- user_id (str | None): The user ID.
19
+ @deprecated(
20
+ "Use __init__ with company_id and user_id instead or use the classmethod `from_event`"
21
+ )
22
+ @overload
23
+ def __init__(self, event: Event | BaseEvent): ...
24
+
25
+ """
26
+ Initialize the EmbeddingService with an event (deprecated)
27
+ """
28
+
29
+ @overload
30
+ def __init__(self, *, company_id: str, user_id: str): ...
31
+
32
+ """
33
+ Initialize the EmbeddingService with a company_id and user_id.
18
34
  """
19
35
 
20
36
  def __init__(
@@ -25,12 +41,35 @@ class EmbeddingService(BaseService):
25
41
  ):
26
42
  self._event = event
27
43
  if event:
28
- self.company_id = event.company_id
29
- self.user_id = event.user_id
44
+ self._company_id: str = event.company_id
45
+ self._user_id: str = event.user_id
30
46
  else:
31
47
  [company_id, user_id] = validate_required_values([company_id, user_id])
32
- self.company_id = company_id
33
- self.user_id = user_id
48
+ self._company_id: str = company_id
49
+ self._user_id: str = user_id
50
+
51
+ @classmethod
52
+ def from_event(cls, event: Event | BaseEvent):
53
+ """
54
+ Initialize the EmbeddingService with an event.
55
+ """
56
+ return cls(company_id=event.company_id, user_id=event.user_id)
57
+
58
+ @classmethod
59
+ def from_settings(cls, settings: UniqueSettings | str | None = None):
60
+ """
61
+ Initialize the EmbeddingService with a settings object.
62
+ """
63
+
64
+ if settings is None:
65
+ settings = UniqueSettings.from_env_auto_with_sdk_init()
66
+ elif isinstance(settings, str):
67
+ settings = UniqueSettings.from_env_auto_with_sdk_init(filename=settings)
68
+
69
+ return cls(
70
+ company_id=settings.auth.company_id.get_secret_value(),
71
+ user_id=settings.auth.user_id.get_secret_value(),
72
+ )
34
73
 
35
74
  @property
36
75
  @deprecated(
@@ -45,6 +84,58 @@ class EmbeddingService(BaseService):
45
84
  """
46
85
  return self._event
47
86
 
87
+ @property
88
+ @deprecated(
89
+ "The company_id property is deprecated and will be removed in a future version."
90
+ )
91
+ def company_id(self) -> str | None:
92
+ """
93
+ Get the company identifier (deprecated).
94
+
95
+ Returns:
96
+ str | None: The company identifier.
97
+ """
98
+ return self._company_id
99
+
100
+ @company_id.setter
101
+ @deprecated(
102
+ "The company_id setter is deprecated and will be removed in a future version."
103
+ )
104
+ def company_id(self, value: str) -> None:
105
+ """
106
+ Set the company identifier (deprecated).
107
+
108
+ Args:
109
+ value (str | None): The company identifier.
110
+ """
111
+ self._company_id = value
112
+
113
+ @property
114
+ @deprecated(
115
+ "The user_id property is deprecated and will be removed in a future version."
116
+ )
117
+ def user_id(self) -> str | None:
118
+ """
119
+ Get the user identifier (deprecated).
120
+
121
+ Returns:
122
+ str | None: The user identifier.
123
+ """
124
+ return self._user_id
125
+
126
+ @user_id.setter
127
+ @deprecated(
128
+ "The user_id setter is deprecated and will be removed in a future version."
129
+ )
130
+ def user_id(self, value: str) -> None:
131
+ """
132
+ Set the user identifier (deprecated).
133
+
134
+ Args:
135
+ value (str | None): The user identifier.
136
+ """
137
+ self._user_id = value
138
+
48
139
  def embed_texts(
49
140
  self,
50
141
  texts: list[str],
@@ -54,7 +145,7 @@ class EmbeddingService(BaseService):
54
145
  Embed text.
55
146
 
56
147
  Args:
57
- text (str): The text to embed.
148
+ texts (list[str]): The texts to embed.
58
149
  timeout (int): The timeout in milliseconds. Defaults to 600000.
59
150
 
60
151
  Returns:
@@ -64,8 +155,8 @@ class EmbeddingService(BaseService):
64
155
  Exception: If an error occurs.
65
156
  """
66
157
  return embed_texts(
67
- user_id=self.user_id,
68
- company_id=self.company_id,
158
+ user_id=self._user_id,
159
+ company_id=self._company_id,
69
160
  texts=texts,
70
161
  timeout=timeout,
71
162
  )
@@ -89,8 +180,8 @@ class EmbeddingService(BaseService):
89
180
  Exception: If an error occurs.
90
181
  """
91
182
  return await embed_texts_async(
92
- user_id=self.user_id,
93
- company_id=self.company_id,
183
+ user_id=self._user_id,
184
+ company_id=self._company_id,
94
185
  texts=texts,
95
186
  timeout=timeout,
96
187
  )
@@ -0,0 +1 @@
1
+ """Framework utilities for integrating with external frameworks."""
@@ -0,0 +1,10 @@
1
+ """Langchain framework utilities."""
2
+
3
+ try:
4
+ from .client import LangchainNotInstalledError, get_langchain_client
5
+
6
+ __all__ = ["get_langchain_client", "LangchainNotInstalledError"]
7
+ except (ImportError, Exception):
8
+ # If langchain is not installed, don't export anything
9
+ # This handles both ImportError and LangchainNotInstalledError
10
+ __all__ = []
@@ -0,0 +1,71 @@
1
+ import importlib.util
2
+ import logging
3
+
4
+ from typing_extensions import deprecated
5
+
6
+ from unique_toolkit.app.unique_settings import UniqueSettings
7
+ from unique_toolkit.framework_utilities.utils import get_default_headers
8
+
9
+ logger = logging.getLogger("toolkit.framework_utilities.langchain")
10
+
11
+
12
+ class LangchainNotInstalledError(ImportError):
13
+ """Raised when langchain-openai package is not installed but functionality requiring it is accessed."""
14
+
15
+ def __init__(self):
16
+ super().__init__(
17
+ "langchain-openai package is not installed. Install it with 'poetry install --with langchain'."
18
+ )
19
+
20
+
21
+ if importlib.util.find_spec("langchain_openai") is not None:
22
+ from langchain_openai import ChatOpenAI
23
+ else:
24
+ raise LangchainNotInstalledError()
25
+
26
+
27
+ def get_langchain_client(
28
+ *,
29
+ unique_settings: UniqueSettings | None = None,
30
+ model: str = "AZURE_GPT_4o_2024_0806",
31
+ additional_headers: dict[str, str] | None = None,
32
+ ) -> ChatOpenAI:
33
+ """Get a Langchain ChatOpenAI client instance.
34
+
35
+ Args:
36
+ unique_settings: UniqueSettings instance
37
+
38
+ Returns:
39
+ ChatOpenAI client instance
40
+
41
+ Raises:
42
+ LangchainNotInstalledError: If langchain-openai package is not installed
43
+ """
44
+ if unique_settings is None:
45
+ unique_settings = UniqueSettings.from_env_auto()
46
+
47
+ default_headers = get_default_headers(unique_settings.app, unique_settings.auth)
48
+ if additional_headers is not None:
49
+ default_headers.update(additional_headers)
50
+
51
+ return ChatOpenAI(
52
+ base_url=unique_settings.api.openai_proxy_url(),
53
+ default_headers=default_headers,
54
+ model=model,
55
+ api_key=unique_settings.app.key,
56
+ )
57
+
58
+
59
+ @deprecated("Use get_langchain_client instead")
60
+ def get_client(
61
+ unique_settings: UniqueSettings | None = None, model: str = "AZURE_GPT_4o_2024_0806"
62
+ ) -> ChatOpenAI:
63
+ """Get a Langchain ChatOpenAI client instance.
64
+
65
+ Args:
66
+ unique_settings: UniqueSettings instance
67
+
68
+ Returns:
69
+ ChatOpenAI client instance
70
+ """
71
+ return get_client(unique_settings, model)
@@ -0,0 +1,19 @@
1
+ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
2
+
3
+ from unique_toolkit.chat import ChatMessage as UniqueMessage
4
+ from unique_toolkit.chat import ChatMessageRole as UniqueRole
5
+
6
+
7
+ def unique_history_to_langchain_history(
8
+ unique_history: list[UniqueMessage],
9
+ ) -> list[BaseMessage]:
10
+ history = []
11
+ for m in unique_history:
12
+ if m.role == UniqueRole.ASSISTANT:
13
+ history.append(AIMessage(content=m.content or ""))
14
+ elif m.role == UniqueRole.USER:
15
+ history.append(HumanMessage(content=m.content or ""))
16
+ else:
17
+ raise Exception("Unknown message role.")
18
+
19
+ return history
@@ -0,0 +1,6 @@
1
+ """OpenAI framework utilities."""
2
+
3
+ from .client import get_async_openai_client, get_openai_client
4
+ from .message_builder import OpenAIMessageBuilder
5
+
6
+ __all__ = ["get_openai_client", "OpenAIMessageBuilder", "get_async_openai_client"]
@@ -0,0 +1,84 @@
1
+ import importlib.util
2
+ import logging
3
+
4
+ from unique_toolkit.app.unique_settings import UniqueSettings
5
+ from unique_toolkit.framework_utilities.utils import get_default_headers
6
+
7
+ logger = logging.getLogger("toolkit.framework_utilities.openai")
8
+
9
+
10
+ class OpenAINotInstalledError(ImportError):
11
+ """Raised when OpenAI package is not installed but functionality requiring it is accessed."""
12
+
13
+ def __init__(self):
14
+ super().__init__(
15
+ "OpenAI package is not installed. Install it with 'poetry install --with openai'."
16
+ )
17
+
18
+
19
+ if importlib.util.find_spec("openai") is not None:
20
+ from openai import AsyncOpenAI, OpenAI
21
+ else:
22
+ raise OpenAINotInstalledError()
23
+
24
+
25
+ def get_openai_client(
26
+ *,
27
+ unique_settings: UniqueSettings | None = None,
28
+ additional_headers: dict[str, str] | None = None,
29
+ ) -> OpenAI:
30
+ """Get an OpenAI client instance.
31
+
32
+ Args:
33
+ unique_settings (UniqueSettings | None): Optional UniqueSettings instance
34
+ additional_headers (dict[str, str] | None): Optional additional headers to add to the request
35
+
36
+ Returns:
37
+ OpenAI client instance
38
+
39
+ Raises:
40
+ OpenAINotInstalledError: If OpenAI package is not installed
41
+ """
42
+ if unique_settings is None:
43
+ unique_settings = UniqueSettings.from_env_auto()
44
+
45
+ default_headers = get_default_headers(unique_settings.app, unique_settings.auth)
46
+ if additional_headers is not None:
47
+ default_headers.update(additional_headers)
48
+
49
+ return OpenAI(
50
+ api_key="dummy_key",
51
+ base_url=unique_settings.api.openai_proxy_url(),
52
+ default_headers=default_headers,
53
+ )
54
+
55
+
56
+ def get_async_openai_client(
57
+ *,
58
+ unique_settings: UniqueSettings | None = None,
59
+ additional_headers: dict[str, str] | None = None,
60
+ ) -> AsyncOpenAI:
61
+ """Get an async OpenAI client instance.
62
+
63
+ Args:
64
+ unique_settings: Optional UniqueSettings instance
65
+
66
+ Returns:
67
+ AsyncOpenAI client instance
68
+
69
+ Raises:
70
+ OpenAINotInstalledError: If OpenAI package is not installed
71
+ """
72
+ if unique_settings is None:
73
+ unique_settings = UniqueSettings.from_env_auto()
74
+
75
+ default_headers = get_default_headers(unique_settings.app, unique_settings.auth)
76
+
77
+ if additional_headers is not None:
78
+ default_headers.update(additional_headers)
79
+
80
+ return AsyncOpenAI(
81
+ api_key="dummy_key",
82
+ base_url=unique_settings.api.openai_proxy_url(),
83
+ default_headers=default_headers,
84
+ )
@@ -0,0 +1,229 @@
1
+ import base64
2
+ import mimetypes
3
+ from collections.abc import Iterable
4
+ from pathlib import Path
5
+ from typing import Self, overload
6
+
7
+ from openai.types.chat.chat_completion_assistant_message_param import (
8
+ Audio,
9
+ ChatCompletionAssistantMessageParam,
10
+ ContentArrayOfContentPart,
11
+ FunctionCall,
12
+ )
13
+ from openai.types.chat.chat_completion_content_part_image_param import (
14
+ ChatCompletionContentPartImageParam,
15
+ ImageURL,
16
+ )
17
+ from openai.types.chat.chat_completion_content_part_param import (
18
+ ChatCompletionContentPartParam,
19
+ )
20
+ from openai.types.chat.chat_completion_content_part_text_param import (
21
+ ChatCompletionContentPartTextParam,
22
+ )
23
+ from openai.types.chat.chat_completion_developer_message_param import (
24
+ ChatCompletionDeveloperMessageParam,
25
+ )
26
+ from openai.types.chat.chat_completion_function_message_param import (
27
+ ChatCompletionFunctionMessageParam,
28
+ )
29
+ from openai.types.chat.chat_completion_message_param import ChatCompletionMessageParam
30
+ from openai.types.chat.chat_completion_message_tool_call_param import (
31
+ ChatCompletionMessageToolCallParam,
32
+ )
33
+ from openai.types.chat.chat_completion_system_message_param import (
34
+ ChatCompletionSystemMessageParam,
35
+ )
36
+ from openai.types.chat.chat_completion_tool_message_param import (
37
+ ChatCompletionToolMessageParam,
38
+ )
39
+ from openai.types.chat.chat_completion_user_message_param import (
40
+ ChatCompletionUserMessageParam,
41
+ )
42
+ from typing_extensions import Literal
43
+
44
+
45
+ class OpenAIUserMessageBuilder:
46
+ def __init__(
47
+ self,
48
+ ) -> None:
49
+ self._messages: list[ChatCompletionContentPartParam] = []
50
+
51
+ def append_text(self, content: str) -> Self:
52
+ part = ChatCompletionContentPartTextParam(
53
+ type="text",
54
+ text=content,
55
+ )
56
+ self._messages.append(part)
57
+ return self
58
+
59
+ @overload
60
+ def append_image(
61
+ self, *, url: str, detail: Literal["auto", "low", "high"] = "auto"
62
+ ) -> Self: ...
63
+
64
+ @overload
65
+ def append_image(
66
+ self, *, path: Path, detail: Literal["auto", "low", "high"] = "auto"
67
+ ) -> Self: ...
68
+
69
+ @overload
70
+ def append_image(
71
+ self,
72
+ *,
73
+ content: bytes,
74
+ mime_type: str,
75
+ detail: Literal["auto", "low", "high"] = "auto",
76
+ ) -> Self: ...
77
+
78
+ def append_image(
79
+ self,
80
+ *,
81
+ url: str | None = None,
82
+ path: Path | None = None,
83
+ content: bytes | None = None,
84
+ mime_type: str | None = None,
85
+ detail: Literal["auto", "low", "high"] = "auto",
86
+ ) -> Self:
87
+ if url is None and path is None and (content is None or mime_type is None):
88
+ raise ValueError("Either url or path must be provided")
89
+
90
+ if path is not None:
91
+ # Read image file and encode as base64 data URI
92
+ image_data = path.read_bytes()
93
+ base64_image = base64.b64encode(image_data).decode("utf-8")
94
+ mime_type = mimetypes.guess_type(str(path))[0] or "image/jpeg"
95
+ url = f"data:{mime_type};base64,{base64_image}"
96
+
97
+ if content is not None and mime_type is not None:
98
+ base64_image = base64.b64encode(content).decode("utf-8")
99
+ url = f"data:{mime_type};base64,{base64_image}"
100
+
101
+ image_url = ImageURL(url=url or "", detail=detail)
102
+ part = ChatCompletionContentPartImageParam(
103
+ type="image_url",
104
+ image_url=image_url,
105
+ )
106
+ self._messages.append(part)
107
+ return self
108
+
109
+ @property
110
+ def user_message(self) -> ChatCompletionUserMessageParam:
111
+ return ChatCompletionUserMessageParam(
112
+ content=self._messages,
113
+ role="user",
114
+ )
115
+
116
+ @property
117
+ def iterable_content(self) -> Iterable[ChatCompletionContentPartParam]:
118
+ return self._messages
119
+
120
+
121
+ class OpenAIMessageBuilder:
122
+ def __init__(
123
+ self,
124
+ messages: list[ChatCompletionMessageParam] | None = None,
125
+ ) -> None:
126
+ self._messages: list[ChatCompletionMessageParam] = []
127
+ if messages:
128
+ self._messages = messages
129
+
130
+ @classmethod
131
+ def from_messages(cls, messages: list[ChatCompletionMessageParam]) -> Self:
132
+ builder = cls()
133
+ builder._messages = messages.copy()
134
+ return builder
135
+
136
+ def append(self, message: ChatCompletionMessageParam):
137
+ self._messages.append(message)
138
+
139
+ def system_message_append(
140
+ self,
141
+ content: str | Iterable[ChatCompletionContentPartTextParam],
142
+ name: str = "system",
143
+ ) -> Self:
144
+ self._messages.append(
145
+ ChatCompletionSystemMessageParam(
146
+ content=content,
147
+ role="system",
148
+ ),
149
+ )
150
+ return self
151
+
152
+ def user_message_append(
153
+ self,
154
+ content: str | Iterable[ChatCompletionContentPartParam],
155
+ name: str = "user",
156
+ ) -> Self:
157
+ self._messages.append(
158
+ ChatCompletionUserMessageParam(
159
+ content=content,
160
+ role="user",
161
+ ),
162
+ )
163
+ return self
164
+
165
+ def assistant_message_append(
166
+ self,
167
+ content: str | Iterable[ContentArrayOfContentPart] | None = None,
168
+ name: str = "assistant",
169
+ audio: Audio | None = None,
170
+ function_call: FunctionCall | None = None,
171
+ refusal: str | None = None,
172
+ tool_calls: Iterable[ChatCompletionMessageToolCallParam] | None = None,
173
+ ) -> Self:
174
+ self._messages.append(
175
+ ChatCompletionAssistantMessageParam(
176
+ content=content,
177
+ role="assistant",
178
+ audio=audio,
179
+ function_call=function_call,
180
+ refusal=refusal,
181
+ tool_calls=tool_calls or [],
182
+ ),
183
+ )
184
+ return self
185
+
186
+ def developper_message_append(
187
+ self,
188
+ content: str | Iterable[ChatCompletionContentPartTextParam],
189
+ name: str = "developer",
190
+ ) -> Self:
191
+ self._messages.append(
192
+ ChatCompletionDeveloperMessageParam(
193
+ content=content,
194
+ role="developer",
195
+ ),
196
+ )
197
+ return self
198
+
199
+ def function_message_append(
200
+ self,
201
+ content: str | None,
202
+ name: str = "function",
203
+ ) -> Self:
204
+ self._messages.append(
205
+ ChatCompletionFunctionMessageParam(
206
+ content=content,
207
+ role="function",
208
+ name=name,
209
+ ),
210
+ )
211
+ return self
212
+
213
+ def tool_message_append(
214
+ self,
215
+ content: str | Iterable[ChatCompletionContentPartTextParam],
216
+ tool_call_id: str,
217
+ ) -> Self:
218
+ self._messages.append(
219
+ ChatCompletionToolMessageParam(
220
+ content=content,
221
+ role="tool",
222
+ tool_call_id=tool_call_id,
223
+ ),
224
+ )
225
+ return self
226
+
227
+ @property
228
+ def messages(self) -> list[ChatCompletionMessageParam]:
229
+ return self._messages
@@ -0,0 +1,23 @@
1
+ from unique_toolkit.app.unique_settings import UniqueApp, UniqueAuth
2
+
3
+
4
+ def auth_headers(auth_settings: UniqueAuth) -> dict[str, str]:
5
+ return {
6
+ "x-user-id": auth_settings.user_id.get_secret_value(),
7
+ "x-company-id": auth_settings.company_id.get_secret_value(),
8
+ }
9
+
10
+
11
+ def app_headers(app_settings: UniqueApp) -> dict[str, str]:
12
+ return {
13
+ "x-app-id": app_settings.id.get_secret_value(),
14
+ "Authorization": f"Bearer {app_settings.key.get_secret_value()}",
15
+ }
16
+
17
+
18
+ def get_default_headers(app_settings: UniqueApp, auth_settings: UniqueAuth):
19
+ default_headers = {}
20
+ default_headers.update(app_headers(app_settings))
21
+ default_headers.update(auth_headers(auth_settings))
22
+ default_headers["x-api-version"] = "2023-12-06"
23
+ return default_headers
@@ -43,6 +43,9 @@ from .schemas import (
43
43
  from .schemas import (
44
44
  LanguageModelTool as LanguageModelTool,
45
45
  )
46
+ from .schemas import (
47
+ LanguageModelToolDescription as LanguageModelToolDescription,
48
+ )
46
49
  from .schemas import (
47
50
  LanguageModelToolMessage as LanguageModelToolMessage,
48
51
  )