unique_toolkit 0.8.2__py3-none-any.whl → 0.8.4__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.
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from typing import Self, TypeVar
4
4
  from urllib.parse import urlparse, urlunparse
5
5
 
6
- from pydantic import Field, SecretStr, model_validator
6
+ from pydantic import AliasChoices, Field, SecretStr, model_validator
7
7
  from pydantic_settings import BaseSettings, SettingsConfigDict
8
8
 
9
9
  logger = getLogger(__name__)
@@ -13,18 +13,34 @@ T = TypeVar("T", bound=BaseSettings)
13
13
 
14
14
  def warn_about_defaults(instance: T) -> T:
15
15
  """Log warnings for fields that are using default values."""
16
- for field_name, model_field in instance.model_fields.items():
16
+ for field_name, model_field in instance.__class__.model_fields.items():
17
17
  field_value = getattr(instance, field_name)
18
- if field_value == model_field.default:
19
- logger.warning(
20
- f"Using default value for '{field_name}': {model_field.default}"
21
- )
18
+ default_value = model_field.default
19
+
20
+ # Handle SecretStr comparison by comparing the secret values
21
+ if isinstance(field_value, SecretStr) and isinstance(default_value, SecretStr):
22
+ if field_value.get_secret_value() == default_value.get_secret_value():
23
+ logger.warning(
24
+ f"Using default value for '{field_name}': {default_value.get_secret_value()}"
25
+ )
26
+ elif field_value == default_value:
27
+ logger.warning(f"Using default value for '{field_name}': {default_value}")
22
28
  return instance
23
29
 
24
30
 
25
31
  class UniqueApp(BaseSettings):
26
- id: SecretStr = Field(default=SecretStr("dummy_id"))
27
- key: SecretStr = Field(default=SecretStr("dummy_key"))
32
+ id: SecretStr = Field(
33
+ default=SecretStr("dummy_id"),
34
+ validation_alias=AliasChoices(
35
+ "unique_app_id", "app_id", "UNIQUE_APP_ID", "APP_ID"
36
+ ),
37
+ )
38
+ key: SecretStr = Field(
39
+ default=SecretStr("dummy_key"),
40
+ validation_alias=AliasChoices(
41
+ "unique_app_key", "key", "UNIQUE_APP_KEY", "KEY", "API_KEY", "api_key"
42
+ ),
43
+ )
28
44
  base_url: str = Field(
29
45
  default="http://localhost:8092/",
30
46
  deprecated="Use UniqueApi.base_url instead",
@@ -48,8 +64,16 @@ class UniqueApi(BaseSettings):
48
64
  base_url: str = Field(
49
65
  default="http://localhost:8092/",
50
66
  description="The base URL of the Unique API. Ask your admin to provide you with the correct URL.",
67
+ validation_alias=AliasChoices(
68
+ "unique_api_base_url", "base_url", "UNIQUE_API_BASE_URL", "BASE_URL"
69
+ ),
70
+ )
71
+ version: str = Field(
72
+ default="2023-12-06",
73
+ validation_alias=AliasChoices(
74
+ "unique_api_version", "version", "UNIQUE_API_VERSION", "VERSION"
75
+ ),
51
76
  )
52
- version: str = Field(default="2023-12-06")
53
77
 
54
78
  model_config = SettingsConfigDict(
55
79
  env_prefix="unique_api_",
@@ -82,14 +106,29 @@ class UniqueApi(BaseSettings):
82
106
 
83
107
  def openai_proxy_url(self) -> str:
84
108
  parsed = urlparse(self.base_url)
85
- return urlunparse(
86
- parsed._replace(path="/public/openai-proxy/", query=None, fragment=None)
87
- )
109
+ path = "/public/chat/openai-proxy/"
110
+ if parsed.hostname and "qa.unique" in parsed.hostname:
111
+ path = "/public/chat-gen2/openai-proxy/"
112
+
113
+ return urlunparse(parsed._replace(path=path, query=None, fragment=None))
88
114
 
89
115
 
90
116
  class UniqueAuth(BaseSettings):
91
- company_id: SecretStr = Field(default=SecretStr("dummy_company_id"))
92
- user_id: SecretStr = Field(default=SecretStr("dummy_user_id"))
117
+ company_id: SecretStr = Field(
118
+ default=SecretStr("dummy_company_id"),
119
+ validation_alias=AliasChoices(
120
+ "unique_auth_company_id",
121
+ "company_id",
122
+ "UNIQUE_AUTH_COMPANY_ID",
123
+ "COMPANY_ID",
124
+ ),
125
+ )
126
+ user_id: SecretStr = Field(
127
+ default=SecretStr("dummy_user_id"),
128
+ validation_alias=AliasChoices(
129
+ "unique_auth_user_id", "user_id", "UNIQUE_AUTH_USER_ID", "USER_ID"
130
+ ),
131
+ )
93
132
 
94
133
  model_config = SettingsConfigDict(
95
134
  env_prefix="unique_auth_",
@@ -128,7 +167,7 @@ class UniqueSettings:
128
167
 
129
168
  # Initialize settings with environment file if provided
130
169
  env_file_str = str(env_file) if env_file else None
131
- auth = UniqueAuth(_env_file=env_file_str)
132
- app = UniqueApp(_env_file=env_file_str)
133
- api = UniqueApi(_env_file=env_file_str)
170
+ auth = UniqueAuth(_env_file=env_file_str) # type: ignore[call-arg]
171
+ app = UniqueApp(_env_file=env_file_str) # type: ignore[call-arg]
172
+ api = UniqueApi(_env_file=env_file_str) # type: ignore[call-arg]
134
173
  return cls(auth=auth, app=app, api=api)
@@ -34,6 +34,7 @@ def search_content_chunks(
34
34
  chat_only: bool | None = None,
35
35
  metadata_filter: dict | None = None,
36
36
  content_ids: list[str] | None = None,
37
+ score_threshold: float | None = None,
37
38
  ) -> list[ContentChunk]:
38
39
  """
39
40
  Performs a synchronous search for content chunks in the knowledge base.
@@ -48,6 +49,7 @@ def search_content_chunks(
48
49
  chat_only (bool | None): Whether to search only in the current chat. Defaults to None.
49
50
  metadata_filter (dict | None): UniqueQL metadata filter. If unspecified/None, it tries to use the metadata filter from the event. Defaults to None.
50
51
  content_ids (list[str] | None): The content IDs to search. Defaults to None.
52
+ score_threshold (float | None): The minimum score threshold for results. Defaults to 0.
51
53
  Returns:
52
54
  list[ContentChunk]: The search results.
53
55
  """
@@ -73,6 +75,7 @@ def search_content_chunks(
73
75
  chatOnly=chat_only,
74
76
  metaDataFilter=metadata_filter,
75
77
  contentIds=content_ids,
78
+ scoreThreshold=score_threshold,
76
79
  )
77
80
  return map_to_content_chunks(searches)
78
81
  except Exception as e:
@@ -93,6 +96,7 @@ async def search_content_chunks_async(
93
96
  chat_only: bool | None = None,
94
97
  metadata_filter: dict | None = None,
95
98
  content_ids: list[str] | None = None,
99
+ score_threshold: float | None = None,
96
100
  ):
97
101
  """
98
102
  Performs an asynchronous search for content chunks in the knowledge base.
@@ -121,6 +125,7 @@ async def search_content_chunks_async(
121
125
  chatOnly=chat_only,
122
126
  metaDataFilter=metadata_filter,
123
127
  contentIds=content_ids,
128
+ scoreThreshold=score_threshold,
124
129
  )
125
130
  return map_to_content_chunks(searches)
126
131
  except Exception as e:
@@ -236,6 +236,7 @@ class ContentService:
236
236
  chat_only: bool | None = None,
237
237
  metadata_filter: dict | None = None,
238
238
  content_ids: list[str] | None = None,
239
+ score_threshold: float | None = None,
239
240
  ) -> list[ContentChunk]:
240
241
  """
241
242
  Performs a synchronous search for content chunks in the knowledge base.
@@ -251,6 +252,7 @@ class ContentService:
251
252
  chat_only (bool | None, optional): Whether to search only in the current chat. Defaults to None.
252
253
  metadata_filter (dict | None, optional): UniqueQL metadata filter. If unspecified/None, it tries to use the metadata filter from the event. Defaults to None.
253
254
  content_ids (list[str] | None, optional): The content IDs to search within. Defaults to None.
255
+ score_threshold (float | None, optional): Sets the minimum similarity score for search results to be considered. Defaults to 0.
254
256
 
255
257
  Returns:
256
258
  list[ContentChunk]: The search results.
@@ -281,6 +283,7 @@ class ContentService:
281
283
  chat_only=chat_only,
282
284
  metadata_filter=metadata_filter,
283
285
  content_ids=content_ids,
286
+ score_threshold=score_threshold,
284
287
  )
285
288
  return searches
286
289
  except Exception as e:
@@ -299,6 +302,7 @@ class ContentService:
299
302
  chat_only: bool | None = None,
300
303
  metadata_filter: dict | None = None,
301
304
  content_ids: list[str] | None = None,
305
+ score_threshold: float | None = None,
302
306
  ):
303
307
  """
304
308
  Performs an asynchronous search for content chunks in the knowledge base.
@@ -314,6 +318,7 @@ class ContentService:
314
318
  chat_only (bool | None, optional): Whether to search only in the current chat. Defaults to None.
315
319
  metadata_filter (dict | None, optional): UniqueQL metadata filter. If unspecified/None, it tries to use the metadata filter from the event. Defaults to None.
316
320
  content_ids (list[str] | None, optional): The content IDs to search within. Defaults to None.
321
+ score_threshold (float | None, optional): Sets the minimum similarity score for search results to be considered. Defaults to 0.
317
322
 
318
323
  Returns:
319
324
  list[ContentChunk]: The search results.
@@ -343,6 +348,7 @@ class ContentService:
343
348
  chat_only=chat_only,
344
349
  metadata_filter=metadata_filter,
345
350
  content_ids=content_ids,
351
+ score_threshold=score_threshold,
346
352
  )
347
353
  return searches
348
354
  except Exception as e:
@@ -10,9 +10,9 @@ def unique_history_to_langchain_history(
10
10
  history = []
11
11
  for m in unique_history:
12
12
  if m.role == UniqueRole.ASSISTANT:
13
- history.append(AIMessage(content=m.content))
13
+ history.append(AIMessage(content=m.content or ""))
14
14
  elif m.role == UniqueRole.USER:
15
- history.append(HumanMessage(content=m.content))
15
+ history.append(HumanMessage(content=m.content or ""))
16
16
  else:
17
17
  raise Exception("Unknown message role.")
18
18
 
@@ -37,7 +37,7 @@ def get_openai_client(unique_settings: UniqueSettings) -> OpenAI:
37
37
  default_headers = get_default_headers(unique_settings.app, unique_settings.auth)
38
38
 
39
39
  return OpenAI(
40
- api_key=unique_settings.app.key.get_secret_value(),
40
+ api_key="dummy_key",
41
41
  base_url=unique_settings.api.openai_proxy_url(),
42
42
  default_headers=default_headers,
43
43
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Martin Fadler
@@ -113,6 +113,11 @@ All notable changes to this project will be documented in this file.
113
113
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
114
114
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
115
115
 
116
+ ## [0.8.4] - 2025-08-06
117
+ - Make unique settings compatible with legacy environment variables
118
+
119
+ ## [0.8.3] - 2025-08-05
120
+ - Expose threshold field for search.
116
121
 
117
122
  ## [0.8.2] - 2025-08-05
118
123
  - Implement overloads for services for clearer dev experience
@@ -11,7 +11,7 @@ unique_toolkit/app/init_sdk.py,sha256=5_oDoETr6akwYyBCb0ivTdMNu3SVgPSkrXcDS6ELyY
11
11
  unique_toolkit/app/performance/async_tasks.py,sha256=H0l3OAcosLwNHZ8d2pd-Di4wHIXfclEvagi5kfqLFPA,1941
12
12
  unique_toolkit/app/performance/async_wrapper.py,sha256=yVVcRDkcdyfjsxro-N29SBvi-7773wnfDplef6-y8xw,1077
13
13
  unique_toolkit/app/schemas.py,sha256=JdC2rNVPRrr6QhGMZweE0ID760onbRY2oq9m1LVFego,7429
14
- unique_toolkit/app/unique_settings.py,sha256=YwKXKKSrLSoNk5kow5ChC0uOqii37gd3ttFL4rSAVF4,4443
14
+ unique_toolkit/app/unique_settings.py,sha256=Gn8qxy_hNraVTTlP4wfZJzgxPU8cU6s84Uw6FK6ixGg,5946
15
15
  unique_toolkit/app/verification.py,sha256=GxFFwcJMy25fCA_Xe89wKW7bgqOu8PAs5y8QpHF0GSc,3861
16
16
  unique_toolkit/chat/__init__.py,sha256=LRs2G-JTVuci4lbtHTkVUiNcZcSR6uqqfnAyo7af6nY,619
17
17
  unique_toolkit/chat/constants.py,sha256=05kq6zjqUVB2d6_P7s-90nbljpB3ryxwCI-CAz0r2O4,83
@@ -22,9 +22,9 @@ unique_toolkit/chat/state.py,sha256=Cjgwv_2vhDFbV69xxsn7SefhaoIAEqLx3ferdVFCnOg,
22
22
  unique_toolkit/chat/utils.py,sha256=ihm-wQykBWhB4liR3LnwPVPt_qGW6ETq21Mw4HY0THE,854
23
23
  unique_toolkit/content/__init__.py,sha256=EdJg_A_7loEtCQf4cah3QARQreJx6pdz89Rm96YbMVg,940
24
24
  unique_toolkit/content/constants.py,sha256=1iy4Y67xobl5VTnJB6SxSyuoBWbdLl9244xfVMUZi5o,60
25
- unique_toolkit/content/functions.py,sha256=0ELepm3_sl0SD_SYzvQVQ-jTdrcUqK5mVJZv0nQBuAw,18367
25
+ unique_toolkit/content/functions.py,sha256=HZtDIAH3ZxWleq5Si_v30n_7vTnNRu6BCxpTHb9TxdA,18635
26
26
  unique_toolkit/content/schemas.py,sha256=KJ604BOx0vBh2AwlTCZkOo55aHsI6yj8vxDAARKKqEo,2995
27
- unique_toolkit/content/service.py,sha256=9Re_UK7pkv-zNROlKRmfoomfhUQcuzFQvQ05aUl8q6Y,20502
27
+ unique_toolkit/content/service.py,sha256=GA4KHNmppp2L2TRKbFUI1grYUJbuNscfoVBYTPilY7s,20972
28
28
  unique_toolkit/content/utils.py,sha256=qNVmHTuETaPNGqheg7TbgPr1_1jbNHDc09N5RrmUIyo,7901
29
29
  unique_toolkit/embedding/__init__.py,sha256=uUyzjonPvuDCYsvXCIt7ErQXopLggpzX-MEQd3_e2kE,250
30
30
  unique_toolkit/embedding/constants.py,sha256=Lj8-Lcy1FvuC31PM9Exq7vaFuxQV4pEI1huUMFX-J2M,52
@@ -47,8 +47,8 @@ unique_toolkit/evaluators/hallucination/utils.py,sha256=gO2AOzDQwVTev2_5vDKgJ9A6
47
47
  unique_toolkit/evaluators/output_parser.py,sha256=eI72qkzK1dZyUvnfP2SOAQCGBj_-PwX5wy_aLPMsJMY,883
48
48
  unique_toolkit/evaluators/schemas.py,sha256=Jaue6Uhx75X1CyHKWj8sT3RE1JZXTqoLtfLt2xQNCX8,2507
49
49
  unique_toolkit/framework_utilities/langchain/client.py,sha256=Msfmr7uezwqagyRJ2zjWbQRFqzDExWYK0y5KLEnDNqM,1329
50
- unique_toolkit/framework_utilities/langchain/history.py,sha256=8ejA0utPUIlX4z8gtX9IkVFiooY-vOT1UcjI_MoJaAU,638
51
- unique_toolkit/framework_utilities/openai/client.py,sha256=Zs5zxFbRfa0XrlbZvhGf-GpHomKLBJx0D28iowk1Eek,1236
50
+ unique_toolkit/framework_utilities/langchain/history.py,sha256=R9RuCeSFNaUO3OZ0G_LmIC4gmOCIANcl91MfyWLnZ1c,650
51
+ unique_toolkit/framework_utilities/openai/client.py,sha256=IasxPXlVJHIsZdXHin7yq-5tO4RNLUu9cEuhrgb4ghE,1205
52
52
  unique_toolkit/framework_utilities/openai/message_builder.py,sha256=meJVGPaSQ6xEafNZtACujcD0KXdiSYGTNiv02FSJULE,3834
53
53
  unique_toolkit/framework_utilities/utils.py,sha256=JK7g2yMfEx3eMprug26769xqNpS5WJcizf8n2zWMBng,789
54
54
  unique_toolkit/language_model/__init__.py,sha256=lRQyLlbwHbNFf4-0foBU13UGb09lwEeodbVsfsSgaCk,1971
@@ -73,7 +73,7 @@ unique_toolkit/tools/tool_definitions.py,sha256=YYu53vXMJBeJtuSU1L_FJBsiN52LSA5L
73
73
  unique_toolkit/tools/tool_definitionsV2.py,sha256=yjLmP85pFGd1QtIVMC3oLQPSQ2NckBj9hIihjIr2FZg,5728
74
74
  unique_toolkit/tools/tool_factory.py,sha256=ux11jd7Oobb-6eBeS51T-tviH14k6HKqsKmljA7h6qA,879
75
75
  unique_toolkit/tools/tool_progress_reporter.py,sha256=AyPdgxpd48qotJyPB8qJ7h7ghiv2w2EK8nlyqQVFRt4,8048
76
- unique_toolkit-0.8.2.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
77
- unique_toolkit-0.8.2.dist-info/METADATA,sha256=nfNsDNjxM9xxW1FdMV3UVLEIXry-6kQU5Uu7RGUPrsc,26025
78
- unique_toolkit-0.8.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
79
- unique_toolkit-0.8.2.dist-info/RECORD,,
76
+ unique_toolkit-0.8.4.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
77
+ unique_toolkit-0.8.4.dist-info/METADATA,sha256=l-59S348l-GsL9MWnt8MbhSalpI1wTmv61Tp7EUmchk,26179
78
+ unique_toolkit-0.8.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
79
+ unique_toolkit-0.8.4.dist-info/RECORD,,