Skip to content

Commit 892fc3f

Browse files
cosminachoclaude
andcommitted
Feat: use _UNSET sentinel to exclude unset UiPathChat params from payload
Fields default to `_UNSET = object()` instead of `None`. `_default_params` filters with `v is not _UNSET` — unset params are omitted, explicit `None` passes through to the API as null. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4a0d9a5 commit 892fc3f

1 file changed

Lines changed: 28 additions & 52 deletions

File tree

  • packages/uipath_langchain_client/src/uipath_langchain_client/clients/normalized

packages/uipath_langchain_client/src/uipath_langchain_client/clients/normalized/chat_models.py

Lines changed: 28 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
convert_to_openai_tool,
6363
)
6464
from langchain_core.utils.pydantic import is_basemodel_subclass
65-
from pydantic import AliasChoices, BaseModel, Field
65+
from pydantic import AliasChoices, BaseModel, ConfigDict, Field
6666

6767
from uipath_langchain_client.base_client import UiPathBaseChatModel
6868
from uipath_langchain_client.settings import ApiType, RoutingMode, UiPathAPIConfig
@@ -72,30 +72,8 @@
7272
_DictOrPydantic = Union[dict[str, Any], BaseModel]
7373

7474

75-
class _UnsetType:
76-
"""Singleton sentinel for request params that should be omitted from the payload.
77-
78-
`UiPathChat` param fields default to `_UNSET` so we can tell "caller did not
79-
pass anything" apart from "caller explicitly passed `None`". `_default_params`
80-
filters out `_UNSET` values only — explicit `None` (and other falsy values)
81-
flow through to the normalized API as `null`.
82-
"""
83-
84-
_instance: "_UnsetType | None" = None
85-
86-
def __new__(cls) -> "_UnsetType":
87-
if cls._instance is None:
88-
cls._instance = super().__new__(cls)
89-
return cls._instance
90-
91-
def __bool__(self) -> Literal[False]:
92-
return False
93-
94-
def __repr__(self) -> str:
95-
return "_UNSET"
96-
97-
98-
_UNSET = _UnsetType()
75+
# Sentinel — params whose value is still _UNSET are omitted from the request payload.
76+
_UNSET: Any = object()
9977

10078

10179
def _oai_structured_outputs_parser(ai_msg: AIMessage, schema: type[BaseModel]) -> BaseModel:
@@ -184,48 +162,50 @@ class UiPathChat(UiPathBaseChatModel):
184162
freeze_base_url=True,
185163
)
186164

165+
model_config = ConfigDict(validate_default=False)
166+
187167
# Common
188-
max_tokens: int | None | _UnsetType = Field(
168+
max_tokens: int | None = Field(
189169
default=_UNSET,
190170
validation_alias=AliasChoices("max_tokens", "max_output_tokens", "max_completion_tokens"),
191171
)
192-
temperature: float | None | _UnsetType = _UNSET
193-
top_p: float | None | _UnsetType = _UNSET
194-
top_k: int | None | _UnsetType = _UNSET
195-
stop: list[str] | str | None | _UnsetType = Field(
172+
temperature: float | None = _UNSET # type: ignore[assignment]
173+
top_p: float | None = _UNSET # type: ignore[assignment]
174+
top_k: int | None = _UNSET # type: ignore[assignment]
175+
stop: list[str] | str | None = Field(
196176
default=_UNSET,
197177
validation_alias=AliasChoices("stop", "stop_sequences"),
198178
)
199-
n: int | None | _UnsetType = Field(
179+
n: int | None = Field(
200180
default=_UNSET,
201181
validation_alias=AliasChoices("n", "candidate_count"),
202182
)
203-
frequency_penalty: float | None | _UnsetType = _UNSET
204-
presence_penalty: float | None | _UnsetType = _UNSET
205-
seed: int | None | _UnsetType = _UNSET
183+
frequency_penalty: float | None = _UNSET # type: ignore[assignment]
184+
presence_penalty: float | None = _UNSET # type: ignore[assignment]
185+
seed: int | None = _UNSET # type: ignore[assignment]
206186

207187
model_kwargs: dict[str, Any] = Field(default_factory=dict)
208188
disabled_params: dict[str, Any] | None = None
209189

210190
# OpenAI
211-
logit_bias: dict[str, int] | None | _UnsetType = _UNSET
212-
logprobs: bool | None | _UnsetType = _UNSET
213-
top_logprobs: int | None | _UnsetType = _UNSET
214-
parallel_tool_calls: bool | None | _UnsetType = _UNSET
215-
reasoning_effort: str | None | _UnsetType = _UNSET
216-
reasoning: dict[str, Any] | None | _UnsetType = _UNSET
191+
logit_bias: dict[str, int] | None = _UNSET # type: ignore[assignment]
192+
logprobs: bool | None = _UNSET # type: ignore[assignment]
193+
top_logprobs: int | None = _UNSET # type: ignore[assignment]
194+
parallel_tool_calls: bool | None = _UNSET # type: ignore[assignment]
195+
reasoning_effort: str | None = _UNSET # type: ignore[assignment]
196+
reasoning: dict[str, Any] | None = _UNSET # type: ignore[assignment]
217197

218198
# Anthropic
219-
thinking: dict[str, Any] | None | _UnsetType = _UNSET
199+
thinking: dict[str, Any] | None = _UNSET # type: ignore[assignment]
220200

221201
# Google
222-
thinking_level: str | None | _UnsetType = _UNSET
223-
thinking_budget: int | None | _UnsetType = _UNSET
224-
include_thoughts: bool | None | _UnsetType = _UNSET
225-
safety_settings: list[dict[str, Any]] | None | _UnsetType = _UNSET
202+
thinking_level: str | None = _UNSET # type: ignore[assignment]
203+
thinking_budget: int | None = _UNSET # type: ignore[assignment]
204+
include_thoughts: bool | None = _UNSET # type: ignore[assignment]
205+
safety_settings: list[dict[str, Any]] | None = _UNSET # type: ignore[assignment]
226206

227207
# Shared
228-
verbosity: str | None | _UnsetType = _UNSET
208+
verbosity: str | None = _UNSET # type: ignore[assignment]
229209

230210
@property
231211
def _llm_type(self) -> str:
@@ -239,11 +219,7 @@ def _identifying_params(self) -> dict[str, Any]:
239219

240220
@property
241221
def _default_params(self) -> dict[str, Any]:
242-
"""Get the default parameters for the normalized API request.
243-
244-
Fields default to `_UNSET` and are excluded from the payload only when
245-
still `_UNSET`. Explicit `None` passes through as JSON `null`.
246-
"""
222+
"""Get the default parameters for the normalized API request."""
247223
candidates: dict[str, Any] = {
248224
"max_tokens": self.max_tokens,
249225
"temperature": self.temperature,
@@ -273,7 +249,7 @@ def _default_params(self) -> dict[str, Any]:
273249
}
274250

275251
return {
276-
**{k: v for k, v in candidates.items() if not isinstance(v, _UnsetType)},
252+
**{k: v for k, v in candidates.items() if v is not _UNSET},
277253
**self.model_kwargs,
278254
}
279255

0 commit comments

Comments
 (0)