lm-deluge 0.0.59__py3-none-any.whl → 0.0.60__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.
Potentially problematic release.
This version of lm-deluge might be problematic. Click here for more details.
- lm_deluge/api_requests/bedrock.py +3 -4
- lm_deluge/api_requests/gemini.py +7 -6
- lm_deluge/api_requests/mistral.py +8 -9
- lm_deluge/api_requests/openai.py +16 -13
- lm_deluge/client.py +130 -2
- lm_deluge/models/openai.py +28 -0
- lm_deluge/prompt.py +70 -14
- lm_deluge/warnings.py +46 -0
- {lm_deluge-0.0.59.dist-info → lm_deluge-0.0.60.dist-info}/METADATA +1 -1
- {lm_deluge-0.0.59.dist-info → lm_deluge-0.0.60.dist-info}/RECORD +13 -12
- {lm_deluge-0.0.59.dist-info → lm_deluge-0.0.60.dist-info}/WHEEL +0 -0
- {lm_deluge-0.0.59.dist-info → lm_deluge-0.0.60.dist-info}/licenses/LICENSE +0 -0
- {lm_deluge-0.0.59.dist-info → lm_deluge-0.0.60.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
-
import warnings
|
|
5
4
|
|
|
6
5
|
from aiohttp import ClientResponse
|
|
7
6
|
|
|
7
|
+
from lm_deluge.warnings import maybe_warn
|
|
8
|
+
|
|
8
9
|
try:
|
|
9
10
|
from requests_aws4auth import AWS4Auth
|
|
10
11
|
except ImportError:
|
|
@@ -187,9 +188,7 @@ async def _build_openai_bedrock_request(
|
|
|
187
188
|
# Note: GPT-OSS on Bedrock doesn't support response_format parameter
|
|
188
189
|
# Even though the model supports JSON, we can't use the response_format parameter
|
|
189
190
|
if sampling_params.json_mode and model.supports_json:
|
|
190
|
-
|
|
191
|
-
f"JSON mode requested for {model.name} but response_format parameter not supported on Bedrock"
|
|
192
|
-
)
|
|
191
|
+
maybe_warn("WARN_JSON_MODE_UNSUPPORTED", model_name=model.name)
|
|
193
192
|
|
|
194
193
|
if tools:
|
|
195
194
|
request_tools = []
|
lm_deluge/api_requests/gemini.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
import warnings
|
|
4
3
|
from typing import Any
|
|
4
|
+
|
|
5
5
|
from aiohttp import ClientResponse
|
|
6
6
|
|
|
7
7
|
from lm_deluge.request_context import RequestContext
|
|
8
8
|
from lm_deluge.tool import Tool
|
|
9
|
+
from lm_deluge.warnings import maybe_warn
|
|
9
10
|
|
|
10
11
|
from ..config import SamplingParams
|
|
11
12
|
from ..models import APIModel
|
|
@@ -54,9 +55,7 @@ async def _build_gemini_request(
|
|
|
54
55
|
|
|
55
56
|
else:
|
|
56
57
|
if sampling_params.reasoning_effort:
|
|
57
|
-
|
|
58
|
-
f"Ignoring reasoning_effort param for non-reasoning model: {model.name}"
|
|
59
|
-
)
|
|
58
|
+
maybe_warn("WARN_REASONING_UNSUPPORTED", model_name=model.name)
|
|
60
59
|
|
|
61
60
|
# Add tools if provided
|
|
62
61
|
if tools:
|
|
@@ -76,8 +75,10 @@ class GeminiRequest(APIRequestBase):
|
|
|
76
75
|
|
|
77
76
|
# Warn if cache is specified for Gemini model
|
|
78
77
|
if self.context.cache is not None:
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
maybe_warn(
|
|
79
|
+
"WARN_CACHING_UNSUPPORTED",
|
|
80
|
+
model_name=self.context.model_name,
|
|
81
|
+
cache_param=self.context.cache,
|
|
81
82
|
)
|
|
82
83
|
|
|
83
84
|
self.model = APIModel.from_registry(self.context.model_name)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
import warnings
|
|
4
3
|
|
|
5
4
|
from aiohttp import ClientResponse
|
|
6
5
|
|
|
6
|
+
from lm_deluge.warnings import maybe_warn
|
|
7
|
+
|
|
7
8
|
from ..models import APIModel
|
|
8
9
|
from ..prompt import Message
|
|
9
10
|
from ..request_context import RequestContext
|
|
@@ -17,8 +18,10 @@ class MistralRequest(APIRequestBase):
|
|
|
17
18
|
|
|
18
19
|
# Warn if cache is specified for non-Anthropic model
|
|
19
20
|
if self.context.cache is not None:
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
maybe_warn(
|
|
22
|
+
"WARN_CACHING_UNSUPPORTED",
|
|
23
|
+
model_name=self.context.model_name,
|
|
24
|
+
cache_param=self.context.cache,
|
|
22
25
|
)
|
|
23
26
|
self.model = APIModel.from_registry(self.context.model_name)
|
|
24
27
|
|
|
@@ -38,13 +41,9 @@ class MistralRequest(APIRequestBase):
|
|
|
38
41
|
"max_tokens": self.context.sampling_params.max_new_tokens,
|
|
39
42
|
}
|
|
40
43
|
if self.context.sampling_params.reasoning_effort:
|
|
41
|
-
|
|
42
|
-
f"Ignoring reasoning_effort param for non-reasoning model: {self.context.model_name}"
|
|
43
|
-
)
|
|
44
|
+
maybe_warn("WARN_REASONING_UNSUPPORTED", model_name=self.context.model_name)
|
|
44
45
|
if self.context.sampling_params.logprobs:
|
|
45
|
-
|
|
46
|
-
f"Ignoring logprobs param for non-logprobs model: {self.context.model_name}"
|
|
47
|
-
)
|
|
46
|
+
maybe_warn("WARN_LOGPROBS_UNSUPPORTED", model_name=self.context.model_name)
|
|
48
47
|
if self.context.sampling_params.json_mode and self.model.supports_json:
|
|
49
48
|
self.request_json["response_format"] = {"type": "json_object"}
|
|
50
49
|
|
lm_deluge/api_requests/openai.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import traceback as tb
|
|
4
|
-
import warnings
|
|
5
4
|
from types import SimpleNamespace
|
|
6
5
|
|
|
7
6
|
import aiohttp
|
|
@@ -9,6 +8,7 @@ from aiohttp import ClientResponse
|
|
|
9
8
|
|
|
10
9
|
from lm_deluge.request_context import RequestContext
|
|
11
10
|
from lm_deluge.tool import MCPServer, Tool
|
|
11
|
+
from lm_deluge.warnings import maybe_warn
|
|
12
12
|
|
|
13
13
|
from ..config import SamplingParams
|
|
14
14
|
from ..models import APIModel
|
|
@@ -75,9 +75,8 @@ async def _build_oa_chat_request(
|
|
|
75
75
|
request_json["reasoning_effort"] = effort
|
|
76
76
|
else:
|
|
77
77
|
if sampling_params.reasoning_effort:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
78
|
+
maybe_warn("WARN_REASONING_UNSUPPORTED", model_name=context.model_name)
|
|
79
|
+
|
|
81
80
|
if sampling_params.logprobs:
|
|
82
81
|
request_json["logprobs"] = True
|
|
83
82
|
if sampling_params.top_logprobs is not None:
|
|
@@ -105,8 +104,10 @@ class OpenAIRequest(APIRequestBase):
|
|
|
105
104
|
|
|
106
105
|
# Warn if cache is specified for non-Anthropic model
|
|
107
106
|
if self.context.cache is not None:
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
maybe_warn(
|
|
108
|
+
"WARN_CACHING_UNSUPPORTED",
|
|
109
|
+
model_name=self.context.model_name,
|
|
110
|
+
cache_param=self.context.cache,
|
|
110
111
|
)
|
|
111
112
|
self.model = APIModel.from_registry(self.context.model_name)
|
|
112
113
|
|
|
@@ -283,9 +284,7 @@ async def _build_oa_responses_request(
|
|
|
283
284
|
}
|
|
284
285
|
else:
|
|
285
286
|
if sampling_params.reasoning_effort:
|
|
286
|
-
|
|
287
|
-
f"Ignoring reasoning_effort for non-reasoning model: {model.id}"
|
|
288
|
-
)
|
|
287
|
+
maybe_warn("WARN_REASONING_UNSUPPORTED", model_name=context.model_name)
|
|
289
288
|
|
|
290
289
|
if sampling_params.json_mode and model.supports_json:
|
|
291
290
|
request_json["text"] = {"format": {"type": "json_object"}}
|
|
@@ -322,8 +321,10 @@ class OpenAIResponsesRequest(APIRequestBase):
|
|
|
322
321
|
super().__init__(context)
|
|
323
322
|
# Warn if cache is specified for non-Anthropic model
|
|
324
323
|
if self.context.cache is not None:
|
|
325
|
-
|
|
326
|
-
|
|
324
|
+
maybe_warn(
|
|
325
|
+
"WARN_CACHING_UNSUPPORTED",
|
|
326
|
+
model_name=self.context.model_name,
|
|
327
|
+
cache_param=self.context.cache,
|
|
327
328
|
)
|
|
328
329
|
self.model = APIModel.from_registry(self.context.model_name)
|
|
329
330
|
|
|
@@ -526,8 +527,10 @@ async def stream_chat(
|
|
|
526
527
|
extra_headers: dict[str, str] | None = None,
|
|
527
528
|
):
|
|
528
529
|
if cache is not None:
|
|
529
|
-
|
|
530
|
-
|
|
530
|
+
maybe_warn(
|
|
531
|
+
"WARN_CACHING_UNSUPPORTED",
|
|
532
|
+
model_name=model_name,
|
|
533
|
+
cache_param=cache,
|
|
531
534
|
)
|
|
532
535
|
|
|
533
536
|
model = APIModel.from_registry(model_name)
|
lm_deluge/client.py
CHANGED
|
@@ -117,13 +117,120 @@ class _LLMClient(BaseModel):
|
|
|
117
117
|
|
|
118
118
|
# NEW! Builder methods
|
|
119
119
|
def with_model(self, model: str):
|
|
120
|
-
self.
|
|
120
|
+
self._update_models([model])
|
|
121
121
|
return self
|
|
122
122
|
|
|
123
123
|
def with_models(self, models: list[str]):
|
|
124
|
-
self.
|
|
124
|
+
self._update_models(models)
|
|
125
125
|
return self
|
|
126
126
|
|
|
127
|
+
def _update_models(self, models: list[str]) -> None:
|
|
128
|
+
normalized, per_model_efforts = self._normalize_model_names(models)
|
|
129
|
+
if self.reasoning_effort is None:
|
|
130
|
+
unique_efforts = {eff for eff in per_model_efforts if eff is not None}
|
|
131
|
+
if len(normalized) == 1 and per_model_efforts[0] is not None:
|
|
132
|
+
self.reasoning_effort = per_model_efforts[0]
|
|
133
|
+
elif (
|
|
134
|
+
len(unique_efforts) == 1
|
|
135
|
+
and len(unique_efforts) != 0
|
|
136
|
+
and None not in per_model_efforts
|
|
137
|
+
):
|
|
138
|
+
self.reasoning_effort = next(iter(unique_efforts)) # type: ignore
|
|
139
|
+
self.model_names = normalized
|
|
140
|
+
self._align_sampling_params(per_model_efforts)
|
|
141
|
+
self._reset_model_weights()
|
|
142
|
+
|
|
143
|
+
def _normalize_model_names(
|
|
144
|
+
self, models: list[str]
|
|
145
|
+
) -> tuple[list[str], list[Literal["low", "medium", "high"] | None]]:
|
|
146
|
+
reasoning_effort_suffixes: dict[str, Literal["low", "medium", "high"]] = {
|
|
147
|
+
"-low": "low",
|
|
148
|
+
"-medium": "medium",
|
|
149
|
+
"-high": "high",
|
|
150
|
+
}
|
|
151
|
+
normalized: list[str] = []
|
|
152
|
+
efforts: list[Literal["low", "medium", "high"] | None] = []
|
|
153
|
+
|
|
154
|
+
for name in models:
|
|
155
|
+
base_name = name
|
|
156
|
+
effort: Literal["low", "medium", "high"] | None = None
|
|
157
|
+
for suffix, candidate in reasoning_effort_suffixes.items():
|
|
158
|
+
if name.endswith(suffix) and len(name) > len(suffix):
|
|
159
|
+
base_name = name[: -len(suffix)]
|
|
160
|
+
effort = candidate
|
|
161
|
+
break
|
|
162
|
+
normalized.append(base_name)
|
|
163
|
+
efforts.append(effort)
|
|
164
|
+
|
|
165
|
+
return normalized, efforts
|
|
166
|
+
|
|
167
|
+
def _align_sampling_params(
|
|
168
|
+
self, per_model_efforts: list[Literal["low", "medium", "high"] | None]
|
|
169
|
+
) -> None:
|
|
170
|
+
if len(per_model_efforts) < len(self.model_names):
|
|
171
|
+
per_model_efforts = per_model_efforts + [None] * (
|
|
172
|
+
len(self.model_names) - len(per_model_efforts)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if not self.model_names:
|
|
176
|
+
self.sampling_params = []
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
if not self.sampling_params:
|
|
180
|
+
self.sampling_params = []
|
|
181
|
+
|
|
182
|
+
if len(self.sampling_params) == 0:
|
|
183
|
+
for _ in self.model_names:
|
|
184
|
+
self.sampling_params.append(
|
|
185
|
+
SamplingParams(
|
|
186
|
+
temperature=self.temperature,
|
|
187
|
+
top_p=self.top_p,
|
|
188
|
+
json_mode=self.json_mode,
|
|
189
|
+
max_new_tokens=self.max_new_tokens,
|
|
190
|
+
reasoning_effort=self.reasoning_effort,
|
|
191
|
+
logprobs=self.logprobs,
|
|
192
|
+
top_logprobs=self.top_logprobs,
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
elif len(self.sampling_params) == 1 and len(self.model_names) > 1:
|
|
196
|
+
base_param = self.sampling_params[0]
|
|
197
|
+
self.sampling_params = [
|
|
198
|
+
base_param.model_copy(deep=True) for _ in self.model_names
|
|
199
|
+
]
|
|
200
|
+
elif len(self.sampling_params) != len(self.model_names):
|
|
201
|
+
base_param = self.sampling_params[0]
|
|
202
|
+
self.sampling_params = [
|
|
203
|
+
base_param.model_copy(deep=True) for _ in self.model_names
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
if self.reasoning_effort is not None:
|
|
207
|
+
for sp in self.sampling_params:
|
|
208
|
+
sp.reasoning_effort = self.reasoning_effort
|
|
209
|
+
else:
|
|
210
|
+
for sp, effort in zip(self.sampling_params, per_model_efforts):
|
|
211
|
+
if effort is not None:
|
|
212
|
+
sp.reasoning_effort = effort
|
|
213
|
+
|
|
214
|
+
def _reset_model_weights(self) -> None:
|
|
215
|
+
if not self.model_names:
|
|
216
|
+
self.model_weights = []
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
if isinstance(self.model_weights, list):
|
|
220
|
+
if len(self.model_weights) == len(self.model_names) and any(
|
|
221
|
+
self.model_weights
|
|
222
|
+
):
|
|
223
|
+
total = sum(self.model_weights)
|
|
224
|
+
if total == 0:
|
|
225
|
+
self.model_weights = [
|
|
226
|
+
1 / len(self.model_names) for _ in self.model_names
|
|
227
|
+
]
|
|
228
|
+
else:
|
|
229
|
+
self.model_weights = [w / total for w in self.model_weights]
|
|
230
|
+
return
|
|
231
|
+
# Fallback to uniform distribution
|
|
232
|
+
self.model_weights = [1 / len(self.model_names) for _ in self.model_names]
|
|
233
|
+
|
|
127
234
|
def with_limits(
|
|
128
235
|
self,
|
|
129
236
|
max_requests_per_minute: int | None = None,
|
|
@@ -150,8 +257,29 @@ class _LLMClient(BaseModel):
|
|
|
150
257
|
@model_validator(mode="before")
|
|
151
258
|
@classmethod
|
|
152
259
|
def fix_lists(cls, data) -> "_LLMClient":
|
|
260
|
+
# Parse reasoning effort from model name suffixes (e.g., "gpt-5-high")
|
|
261
|
+
# Only applies when a single model string is provided
|
|
153
262
|
if isinstance(data.get("model_names"), str):
|
|
263
|
+
model_name = data["model_names"]
|
|
264
|
+
reasoning_effort_suffixes = {
|
|
265
|
+
"-low": "low",
|
|
266
|
+
"-medium": "medium",
|
|
267
|
+
"-high": "high",
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
for suffix, effort in reasoning_effort_suffixes.items():
|
|
271
|
+
if model_name.endswith(suffix):
|
|
272
|
+
# Extract base model name by removing suffix
|
|
273
|
+
base_model = model_name[: -len(suffix)]
|
|
274
|
+
data["model_names"] = base_model
|
|
275
|
+
|
|
276
|
+
# Set reasoning_effort if not already explicitly set
|
|
277
|
+
if data.get("reasoning_effort") is None:
|
|
278
|
+
data["reasoning_effort"] = effort
|
|
279
|
+
break
|
|
280
|
+
|
|
154
281
|
data["model_names"] = [data["model_names"]]
|
|
282
|
+
|
|
155
283
|
if not isinstance(data.get("sampling_params", []), list):
|
|
156
284
|
data["sampling_params"] = [data["sampling_params"]]
|
|
157
285
|
if "sampling_params" not in data or len(data.get("sampling_params", [])) == 0:
|
lm_deluge/models/openai.py
CHANGED
|
@@ -10,6 +10,20 @@ OPENAI_MODELS = {
|
|
|
10
10
|
# ░███
|
|
11
11
|
# █████
|
|
12
12
|
# ░░░░░
|
|
13
|
+
"gpt-5-codex": {
|
|
14
|
+
"id": "gpt-5-codex",
|
|
15
|
+
"name": "gpt-5-codex",
|
|
16
|
+
"api_base": "https://api.openai.com/v1",
|
|
17
|
+
"api_key_env_var": "OPENAI_API_KEY",
|
|
18
|
+
"supports_json": False,
|
|
19
|
+
"supports_logprobs": True,
|
|
20
|
+
"supports_responses": True,
|
|
21
|
+
"api_spec": "openai",
|
|
22
|
+
"input_cost": 1.25,
|
|
23
|
+
"cached_input_cost": 0.125,
|
|
24
|
+
"output_cost": 10.0,
|
|
25
|
+
"reasoning_model": True,
|
|
26
|
+
},
|
|
13
27
|
"gpt-5": {
|
|
14
28
|
"id": "gpt-5",
|
|
15
29
|
"name": "gpt-5",
|
|
@@ -79,6 +93,20 @@ OPENAI_MODELS = {
|
|
|
79
93
|
"output_cost": 12.0,
|
|
80
94
|
"reasoning_model": False,
|
|
81
95
|
},
|
|
96
|
+
"codex-mini-latest": {
|
|
97
|
+
"id": "codex-mini-latest",
|
|
98
|
+
"name": "codex-mini-latest",
|
|
99
|
+
"api_base": "https://api.openai.com/v1",
|
|
100
|
+
"api_key_env_var": "OPENAI_API_KEY",
|
|
101
|
+
"supports_json": True,
|
|
102
|
+
"supports_logprobs": False,
|
|
103
|
+
"supports_responses": True,
|
|
104
|
+
"api_spec": "openai",
|
|
105
|
+
"input_cost": 1.5,
|
|
106
|
+
"cached_input_cost": 0.375,
|
|
107
|
+
"output_cost": 6.0,
|
|
108
|
+
"reasoning_model": True,
|
|
109
|
+
},
|
|
82
110
|
"o3": {
|
|
83
111
|
"id": "o3",
|
|
84
112
|
"name": "o3-2025-04-16",
|
lm_deluge/prompt.py
CHANGED
|
@@ -9,6 +9,7 @@ import xxhash
|
|
|
9
9
|
|
|
10
10
|
from lm_deluge.file import File
|
|
11
11
|
from lm_deluge.image import Image, MediaType
|
|
12
|
+
from lm_deluge.warnings import deprecated
|
|
12
13
|
|
|
13
14
|
CachePattern = Literal[
|
|
14
15
|
"tools_only",
|
|
@@ -415,12 +416,17 @@ class Message:
|
|
|
415
416
|
|
|
416
417
|
return cls(role, parts)
|
|
417
418
|
|
|
418
|
-
def
|
|
419
|
+
def with_text(self, content: str) -> "Message":
|
|
419
420
|
"""Append a text block and return self for chaining."""
|
|
420
421
|
self.parts.append(Text(content))
|
|
421
422
|
return self
|
|
422
423
|
|
|
423
|
-
|
|
424
|
+
@deprecated("with_text")
|
|
425
|
+
def add_text(self, content: str) -> "Message":
|
|
426
|
+
"""Append a text block and return self for chaining."""
|
|
427
|
+
return self.with_text(content)
|
|
428
|
+
|
|
429
|
+
def with_image(
|
|
424
430
|
self,
|
|
425
431
|
data: bytes | str | Path | io.BytesIO | Image,
|
|
426
432
|
*,
|
|
@@ -446,7 +452,27 @@ class Message:
|
|
|
446
452
|
self.parts.append(img)
|
|
447
453
|
return self
|
|
448
454
|
|
|
449
|
-
|
|
455
|
+
@deprecated("with_image")
|
|
456
|
+
def add_image(
|
|
457
|
+
self,
|
|
458
|
+
data: bytes | str | Path | io.BytesIO | Image,
|
|
459
|
+
*,
|
|
460
|
+
media_type: MediaType | None = None,
|
|
461
|
+
detail: Literal["low", "high", "auto"] = "auto",
|
|
462
|
+
max_size: int | None = None,
|
|
463
|
+
) -> "Message":
|
|
464
|
+
"""
|
|
465
|
+
Append an image block and return self for chaining.
|
|
466
|
+
|
|
467
|
+
If max_size is provided, the image will be resized so that its longer
|
|
468
|
+
dimension equals max_size, but only if the longer dimension is currently
|
|
469
|
+
larger than max_size.
|
|
470
|
+
"""
|
|
471
|
+
return self.with_image(
|
|
472
|
+
data=data, media_type=media_type, detail=detail, max_size=max_size
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
def with_file(
|
|
450
476
|
self,
|
|
451
477
|
data: bytes | str | Path | io.BytesIO,
|
|
452
478
|
*,
|
|
@@ -460,11 +486,29 @@ class Message:
|
|
|
460
486
|
self.parts.append(file)
|
|
461
487
|
return self
|
|
462
488
|
|
|
463
|
-
|
|
489
|
+
@deprecated("with_file")
|
|
490
|
+
def add_file(
|
|
491
|
+
self,
|
|
492
|
+
data: bytes | str | Path | io.BytesIO,
|
|
493
|
+
*,
|
|
494
|
+
media_type: str | None = None,
|
|
495
|
+
filename: str | None = None,
|
|
496
|
+
) -> "Message":
|
|
497
|
+
"""
|
|
498
|
+
Append a file block and return self for chaining.
|
|
499
|
+
"""
|
|
500
|
+
return self.with_file(data, media_type=media_type, filename=filename)
|
|
501
|
+
|
|
502
|
+
def with_tool_call(self, id: str, name: str, arguments: dict) -> "Message":
|
|
464
503
|
"""Append a tool call block and return self for chaining."""
|
|
465
504
|
self.parts.append(ToolCall(id=id, name=name, arguments=arguments))
|
|
466
505
|
return self
|
|
467
506
|
|
|
507
|
+
@deprecated("with_tool_call")
|
|
508
|
+
def add_tool_call(self, id: str, name: str, arguments: dict) -> "Message":
|
|
509
|
+
"""Append a tool call block and return self for chaining."""
|
|
510
|
+
return self.with_tool_call(id, name, arguments)
|
|
511
|
+
|
|
468
512
|
def with_tool_result(
|
|
469
513
|
self, tool_call_id: str, result: str | list[ToolResultPart]
|
|
470
514
|
) -> "Message":
|
|
@@ -472,11 +516,23 @@ class Message:
|
|
|
472
516
|
self.parts.append(ToolResult(tool_call_id=tool_call_id, result=result))
|
|
473
517
|
return self
|
|
474
518
|
|
|
475
|
-
|
|
519
|
+
@deprecated("with_tool_result")
|
|
520
|
+
def add_tool_result(
|
|
521
|
+
self, tool_call_id: str, result: str | list[ToolResultPart]
|
|
522
|
+
) -> "Message":
|
|
523
|
+
"""Append a tool result block and return self for chaining."""
|
|
524
|
+
return self.with_tool_result(tool_call_id, result)
|
|
525
|
+
|
|
526
|
+
def with_thinking(self, content: str) -> "Message":
|
|
476
527
|
"""Append a thinking block and return self for chaining."""
|
|
477
528
|
self.parts.append(Thinking(content=content))
|
|
478
529
|
return self
|
|
479
530
|
|
|
531
|
+
@deprecated("with_thinking")
|
|
532
|
+
def add_thinking(self, content: str) -> "Message":
|
|
533
|
+
"""Append a thinking block and return self for chaining."""
|
|
534
|
+
return self.with_thinking(content)
|
|
535
|
+
|
|
480
536
|
# -------- convenient constructors --------
|
|
481
537
|
@classmethod
|
|
482
538
|
def user(
|
|
@@ -488,25 +544,25 @@ class Message:
|
|
|
488
544
|
) -> "Message":
|
|
489
545
|
res = cls("user", [])
|
|
490
546
|
if text is not None:
|
|
491
|
-
res.
|
|
547
|
+
res.with_text(text)
|
|
492
548
|
if image is not None:
|
|
493
|
-
res.
|
|
549
|
+
res.with_image(image)
|
|
494
550
|
if file is not None:
|
|
495
|
-
res.
|
|
551
|
+
res.with_file(file)
|
|
496
552
|
return res
|
|
497
553
|
|
|
498
554
|
@classmethod
|
|
499
555
|
def system(cls, text: str | None = None) -> "Message":
|
|
500
556
|
res = cls("system", [])
|
|
501
557
|
if text is not None:
|
|
502
|
-
res.
|
|
558
|
+
res.with_text(text)
|
|
503
559
|
return res
|
|
504
560
|
|
|
505
561
|
@classmethod
|
|
506
562
|
def ai(cls, text: str | None = None) -> "Message":
|
|
507
563
|
res = cls("assistant", [])
|
|
508
564
|
if text is not None:
|
|
509
|
-
res.
|
|
565
|
+
res.with_text(text)
|
|
510
566
|
return res
|
|
511
567
|
|
|
512
568
|
# ──── provider-specific constructors ───
|
|
@@ -698,9 +754,9 @@ class Conversation:
|
|
|
698
754
|
) -> "Conversation":
|
|
699
755
|
msg = Message.user(text)
|
|
700
756
|
if image is not None:
|
|
701
|
-
msg.
|
|
757
|
+
msg.with_image(image)
|
|
702
758
|
if file is not None:
|
|
703
|
-
msg.
|
|
759
|
+
msg.with_file(file)
|
|
704
760
|
return cls([msg])
|
|
705
761
|
|
|
706
762
|
@classmethod
|
|
@@ -1211,11 +1267,11 @@ class Conversation:
|
|
|
1211
1267
|
for i, tool_result in enumerate(m.tool_results):
|
|
1212
1268
|
images = tool_result.get_images()
|
|
1213
1269
|
if len(images) > 0:
|
|
1214
|
-
user_msg.
|
|
1270
|
+
user_msg.with_text(
|
|
1215
1271
|
f"[Images for Tool Call {tool_result.tool_call_id}]"
|
|
1216
1272
|
)
|
|
1217
1273
|
for img in images:
|
|
1218
|
-
user_msg.
|
|
1274
|
+
user_msg.with_image(img)
|
|
1219
1275
|
|
|
1220
1276
|
else:
|
|
1221
1277
|
result.append(m.oa_chat())
|
lm_deluge/warnings.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import os
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
WARNINGS: dict[str, str] = {
|
|
6
|
+
"WARN_JSON_MODE_UNSUPPORTED": "JSON mode requested for {model_name} but response_format parameter not supported.",
|
|
7
|
+
"WARN_REASONING_UNSUPPORTED": "Ignoring reasoning_effort param for non-reasoning model: {model_name}.",
|
|
8
|
+
"WARN_CACHING_UNSUPPORTED": "Cache parameter '{cache_param}' is not supported, ignoring for {model_name}.",
|
|
9
|
+
"WARN_LOGPROBS_UNSUPPORTED": "Ignoring logprobs param for non-logprobs model: {model_name}",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def maybe_warn(warning: str, **kwargs):
|
|
14
|
+
if os.getenv(warning):
|
|
15
|
+
pass
|
|
16
|
+
else:
|
|
17
|
+
warnings.warn(WARNINGS[warning].format(**kwargs))
|
|
18
|
+
os.environ[warning] = "1"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def deprecated(replacement: str):
|
|
22
|
+
"""Decorator to mark methods as deprecated and suggest replacement.
|
|
23
|
+
|
|
24
|
+
Only shows the warning once per method to avoid spam.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
replacement: The name of the replacement method to suggest
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def decorator(func):
|
|
31
|
+
warning_key = f"DEPRECATED_{func.__module__}_{func.__qualname__}"
|
|
32
|
+
|
|
33
|
+
@functools.wraps(func)
|
|
34
|
+
def wrapper(*args, **kwargs):
|
|
35
|
+
if not os.getenv(warning_key):
|
|
36
|
+
warnings.warn(
|
|
37
|
+
f"{func.__name__} is deprecated, use {replacement} instead",
|
|
38
|
+
DeprecationWarning,
|
|
39
|
+
stacklevel=2,
|
|
40
|
+
)
|
|
41
|
+
os.environ[warning_key] = "1"
|
|
42
|
+
return func(*args, **kwargs)
|
|
43
|
+
|
|
44
|
+
return wrapper
|
|
45
|
+
|
|
46
|
+
return decorator
|
|
@@ -2,26 +2,27 @@ lm_deluge/__init__.py,sha256=LKKIcqQoQyDpTck6fnB7iAs75BnfNNa3Bj5Nz7KU4Hk,376
|
|
|
2
2
|
lm_deluge/batches.py,sha256=Km6QM5_7BlF2qEyo4WPlhkaZkpzrLqf50AaveHXQOoY,25127
|
|
3
3
|
lm_deluge/cache.py,sha256=xO2AIYvP3tUpTMKQjwQQYfGRJSRi6e7sMlRhLjsS-u4,4873
|
|
4
4
|
lm_deluge/cli.py,sha256=Ilww5gOw3J5v0NReq_Ra4hhxU4BCIJBl1oTGxJZKedc,12065
|
|
5
|
-
lm_deluge/client.py,sha256=
|
|
5
|
+
lm_deluge/client.py,sha256=nxVxN0oXYLvOiMgiF7b_qmqQk6Hohnf4ZTtSx1SI_PQ,38845
|
|
6
6
|
lm_deluge/config.py,sha256=H1tQyJDNHGFuwxqQNL5Z-CjWAC0luHSBA3iY_pxmACM,932
|
|
7
7
|
lm_deluge/embed.py,sha256=CO-TOlC5kOTAM8lcnicoG4u4K664vCBwHF1vHa-nAGg,13382
|
|
8
8
|
lm_deluge/errors.py,sha256=oHjt7YnxWbh-eXMScIzov4NvpJMo0-2r5J6Wh5DQ1tk,209
|
|
9
9
|
lm_deluge/file.py,sha256=FGomcG8s2go_55Z2CChflHgmU-UqgFftgFY8c7f_G70,5631
|
|
10
10
|
lm_deluge/image.py,sha256=5AMXmn2x47yXeYNfMSMAOWcnlrOxxOel-4L8QCJwU70,8928
|
|
11
|
-
lm_deluge/prompt.py,sha256=
|
|
11
|
+
lm_deluge/prompt.py,sha256=1hGLOIwdyGFokKv0dPiVpke3OPHD6vK5qO6q9E8H89Y,62020
|
|
12
12
|
lm_deluge/request_context.py,sha256=cBayMFWupWhde2OjRugW3JH-Gin-WFGc6DK2Mb4Prdc,2576
|
|
13
13
|
lm_deluge/rerank.py,sha256=-NBAJdHz9OB-SWWJnHzkFmeVO4wR6lFV7Vw-SxG7aVo,11457
|
|
14
14
|
lm_deluge/tool.py,sha256=eZpzgkSIlGD7KdZQwzLF-UdyRJpRnNNXpceGJrNhRrE,26421
|
|
15
15
|
lm_deluge/tracker.py,sha256=aeS9GUJpgOSQRVXAnGDvlMO8qYpSxpTNLYj2hrMg0m8,14757
|
|
16
16
|
lm_deluge/usage.py,sha256=xz9tAw2hqaJvv9aAVhnQ6N1Arn7fS8Shb28VwCW26wI,5136
|
|
17
|
+
lm_deluge/warnings.py,sha256=nlDJMCw30VhDEFxqLO2-bfXH_Tv5qmlglzUSbokCSw8,1498
|
|
17
18
|
lm_deluge/api_requests/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
18
19
|
lm_deluge/api_requests/anthropic.py,sha256=7tTb_NMPodDHrCzakrLd9LyXuLqeTQyAGU-FvMoV3gI,8437
|
|
19
20
|
lm_deluge/api_requests/base.py,sha256=GCcydwBRx4_xAuYLvasXlyj-TgqvKAVhVvxRfJkvPbY,9471
|
|
20
|
-
lm_deluge/api_requests/bedrock.py,sha256=
|
|
21
|
+
lm_deluge/api_requests/bedrock.py,sha256=Uppne03GcIEk1tVYzoGu7GXK2Sg94a_xvFTLDRN_phY,15412
|
|
21
22
|
lm_deluge/api_requests/common.py,sha256=BZ3vRO5TB669_UsNKugkkuFSzoLHOYJIKt4nV4sf4vc,422
|
|
22
|
-
lm_deluge/api_requests/gemini.py,sha256=
|
|
23
|
-
lm_deluge/api_requests/mistral.py,sha256=
|
|
24
|
-
lm_deluge/api_requests/openai.py,sha256=
|
|
23
|
+
lm_deluge/api_requests/gemini.py,sha256=4uD7fQl0yWyAvYkPNi3oO1InBnvYfo5_QR6k-va-2GI,7838
|
|
24
|
+
lm_deluge/api_requests/mistral.py,sha256=8JZP2CDf1XZfaPcTk0WS4q-VfYYj58ptpoH8LD3MQG4,4528
|
|
25
|
+
lm_deluge/api_requests/openai.py,sha256=qRBakHOOMYJWvKO0HeeE5C1Dv_dbokuizZin9Ca4k_k,24855
|
|
25
26
|
lm_deluge/api_requests/response.py,sha256=vG194gAH5p7ulpNy4qy5Pryfb1p3ZV21-YGoj__ru3E,7436
|
|
26
27
|
lm_deluge/api_requests/deprecated/bedrock.py,sha256=WrcIShCoO8JCUSlFOCHxg6KQCNTZfw3TpYTvSpYk4mA,11320
|
|
27
28
|
lm_deluge/api_requests/deprecated/cohere.py,sha256=KgDScD6_bWhAzOY5BHZQKSA3kurt4KGENqC4wLsGmcU,5142
|
|
@@ -53,7 +54,7 @@ lm_deluge/models/grok.py,sha256=TDzr8yfTaHbdJhwMA-Du6L-efaKFJhjTQViuVElCCHI,2566
|
|
|
53
54
|
lm_deluge/models/groq.py,sha256=Mi5WE1xOBGoZlymD0UN6kzhH_NOmfJYU4N2l-TO0Z8Q,2552
|
|
54
55
|
lm_deluge/models/meta.py,sha256=BBgnscL1gMcIdPbRqrlDl_q9YAYGSrkw9JkAIabXtLs,1883
|
|
55
56
|
lm_deluge/models/mistral.py,sha256=x67o5gckBGmPcIGdVbS26XZAYFKBYM4tsxEAahGp8bk,4323
|
|
56
|
-
lm_deluge/models/openai.py,sha256=
|
|
57
|
+
lm_deluge/models/openai.py,sha256=6J4eAt6Iu5RopokyldUQzRlviFBXBqhLqpVP5tztzqI,11074
|
|
57
58
|
lm_deluge/models/openrouter.py,sha256=O-Po4tmHjAqFIVU96TUL0QnK01R4e2yDN7Z4sYJ-CuE,2120
|
|
58
59
|
lm_deluge/models/together.py,sha256=AjKhPsazqBgqyLwHkNQW07COM1n_oSrYQRp2BFVvn9o,4381
|
|
59
60
|
lm_deluge/presets/cerebras.py,sha256=MDkqj15qQRrj8wxSCDNNe_Cs7h1WN1UjV6lTmSY1olQ,479
|
|
@@ -64,8 +65,8 @@ lm_deluge/util/logprobs.py,sha256=UkBZakOxWluaLqHrjARu7xnJ0uCHVfLGHJdnYlEcutk,11
|
|
|
64
65
|
lm_deluge/util/spatial.py,sha256=BsF_UKhE-x0xBirc-bV1xSKZRTUhsOBdGqsMKme20C8,4099
|
|
65
66
|
lm_deluge/util/validation.py,sha256=hz5dDb3ebvZrZhnaWxOxbNSVMI6nmaOODBkk0htAUhs,1575
|
|
66
67
|
lm_deluge/util/xml.py,sha256=Ft4zajoYBJR3HHCt2oHwGfymGLdvp_gegVmJ-Wqk4Ck,10547
|
|
67
|
-
lm_deluge-0.0.
|
|
68
|
-
lm_deluge-0.0.
|
|
69
|
-
lm_deluge-0.0.
|
|
70
|
-
lm_deluge-0.0.
|
|
71
|
-
lm_deluge-0.0.
|
|
68
|
+
lm_deluge-0.0.60.dist-info/licenses/LICENSE,sha256=uNNXGXPCw2TC7CUs7SEBkA-Mz6QBQFWUUEWDMgEs1dU,1058
|
|
69
|
+
lm_deluge-0.0.60.dist-info/METADATA,sha256=uBr_1y__E5eT9sL6rOo3qf0MZ4rNKZe0hKVj4WMcqKE,13443
|
|
70
|
+
lm_deluge-0.0.60.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
71
|
+
lm_deluge-0.0.60.dist-info/top_level.txt,sha256=hqU-TJX93yBwpgkDtYcXyLr3t7TLSCCZ_reytJjwBaE,10
|
|
72
|
+
lm_deluge-0.0.60.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|