lm-deluge 0.0.56__py3-none-any.whl → 0.0.69__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.
- lm_deluge/__init__.py +12 -1
- lm_deluge/api_requests/anthropic.py +12 -1
- lm_deluge/api_requests/base.py +87 -5
- lm_deluge/api_requests/bedrock.py +3 -4
- lm_deluge/api_requests/chat_reasoning.py +4 -0
- lm_deluge/api_requests/gemini.py +7 -6
- lm_deluge/api_requests/mistral.py +8 -9
- lm_deluge/api_requests/openai.py +179 -124
- lm_deluge/batches.py +25 -9
- lm_deluge/client.py +280 -67
- lm_deluge/config.py +1 -1
- lm_deluge/file.py +382 -13
- lm_deluge/mock_openai.py +482 -0
- lm_deluge/models/__init__.py +12 -8
- lm_deluge/models/anthropic.py +12 -20
- lm_deluge/models/bedrock.py +0 -14
- lm_deluge/models/cohere.py +0 -16
- lm_deluge/models/google.py +0 -20
- lm_deluge/models/grok.py +48 -4
- lm_deluge/models/groq.py +2 -2
- lm_deluge/models/kimi.py +34 -0
- lm_deluge/models/meta.py +0 -8
- lm_deluge/models/minimax.py +10 -0
- lm_deluge/models/openai.py +28 -34
- lm_deluge/models/openrouter.py +64 -1
- lm_deluge/models/together.py +0 -16
- lm_deluge/prompt.py +138 -29
- lm_deluge/request_context.py +9 -11
- lm_deluge/tool.py +395 -19
- lm_deluge/tracker.py +11 -5
- lm_deluge/warnings.py +46 -0
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/METADATA +3 -1
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/RECORD +36 -33
- lm_deluge/agent.py +0 -0
- lm_deluge/gemini_limits.py +0 -65
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/WHEEL +0 -0
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/licenses/LICENSE +0 -0
- {lm_deluge-0.0.56.dist-info → lm_deluge-0.0.69.dist-info}/top_level.txt +0 -0
lm_deluge/models/together.py
CHANGED
|
@@ -20,8 +20,6 @@ TOGETHER_MODELS = {
|
|
|
20
20
|
"api_spec": "openai",
|
|
21
21
|
"input_cost": 3.0,
|
|
22
22
|
"output_cost": 7.0,
|
|
23
|
-
"requests_per_minute": None,
|
|
24
|
-
"tokens_per_minute": None,
|
|
25
23
|
},
|
|
26
24
|
"deepseek-v3-together": {
|
|
27
25
|
"id": "deepseek-v3-together",
|
|
@@ -32,8 +30,6 @@ TOGETHER_MODELS = {
|
|
|
32
30
|
"api_spec": "openai",
|
|
33
31
|
"input_cost": 1.25,
|
|
34
32
|
"output_cost": 1.25,
|
|
35
|
-
"requests_per_minute": None,
|
|
36
|
-
"tokens_per_minute": None,
|
|
37
33
|
},
|
|
38
34
|
"qwen-3-235b-together": {
|
|
39
35
|
"id": "qwen-3-235b-together",
|
|
@@ -44,8 +40,6 @@ TOGETHER_MODELS = {
|
|
|
44
40
|
"api_spec": "openai",
|
|
45
41
|
"input_cost": 0.2,
|
|
46
42
|
"output_cost": 0.6,
|
|
47
|
-
"requests_per_minute": None,
|
|
48
|
-
"tokens_per_minute": None,
|
|
49
43
|
},
|
|
50
44
|
"qwen-2.5-vl-together": {
|
|
51
45
|
"id": "qwen-2.5-vl-together",
|
|
@@ -56,8 +50,6 @@ TOGETHER_MODELS = {
|
|
|
56
50
|
"api_spec": "openai",
|
|
57
51
|
"input_cost": 1.95,
|
|
58
52
|
"output_cost": 8.0,
|
|
59
|
-
"requests_per_minute": None,
|
|
60
|
-
"tokens_per_minute": None,
|
|
61
53
|
},
|
|
62
54
|
"llama-4-maverick-together": {
|
|
63
55
|
"id": "llama-4-maverick-together",
|
|
@@ -68,8 +60,6 @@ TOGETHER_MODELS = {
|
|
|
68
60
|
"api_spec": "openai",
|
|
69
61
|
"input_cost": 0.27,
|
|
70
62
|
"output_cost": 0.85,
|
|
71
|
-
"requests_per_minute": None,
|
|
72
|
-
"tokens_per_minute": None,
|
|
73
63
|
},
|
|
74
64
|
"llama-4-scout-together": {
|
|
75
65
|
"id": "llama-4-scout-together",
|
|
@@ -80,8 +70,6 @@ TOGETHER_MODELS = {
|
|
|
80
70
|
"api_spec": "openai",
|
|
81
71
|
"input_cost": 0.18,
|
|
82
72
|
"output_cost": 0.59,
|
|
83
|
-
"requests_per_minute": None,
|
|
84
|
-
"tokens_per_minute": None,
|
|
85
73
|
},
|
|
86
74
|
"gpt-oss-120b-together": {
|
|
87
75
|
"id": "gpt-oss-120b-together",
|
|
@@ -92,8 +80,6 @@ TOGETHER_MODELS = {
|
|
|
92
80
|
"api_spec": "openai",
|
|
93
81
|
"input_cost": 0.18,
|
|
94
82
|
"output_cost": 0.59,
|
|
95
|
-
"requests_per_minute": None,
|
|
96
|
-
"tokens_per_minute": None,
|
|
97
83
|
"reasoning_model": True,
|
|
98
84
|
},
|
|
99
85
|
"gpt-oss-20b-together": {
|
|
@@ -105,8 +91,6 @@ TOGETHER_MODELS = {
|
|
|
105
91
|
"api_spec": "openai",
|
|
106
92
|
"input_cost": 0.18,
|
|
107
93
|
"output_cost": 0.59,
|
|
108
|
-
"requests_per_minute": None,
|
|
109
|
-
"tokens_per_minute": None,
|
|
110
94
|
"reasoning_model": True,
|
|
111
95
|
},
|
|
112
96
|
}
|
lm_deluge/prompt.py
CHANGED
|
@@ -2,13 +2,14 @@ import io
|
|
|
2
2
|
import json
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Literal, Sequence
|
|
5
|
+
from typing import Literal, Sequence, TypeAlias
|
|
6
6
|
|
|
7
7
|
import tiktoken
|
|
8
8
|
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",
|
|
@@ -144,8 +145,8 @@ class ToolResult:
|
|
|
144
145
|
def oa_chat(
|
|
145
146
|
self,
|
|
146
147
|
) -> dict: # OpenAI Chat Completions - tool results are separate messages
|
|
147
|
-
print("serializing toolresult with oa_chat...")
|
|
148
|
-
print("typeof self.result:", type(self.result))
|
|
148
|
+
# print("serializing toolresult with oa_chat...")
|
|
149
|
+
# print("typeof self.result:", type(self.result))
|
|
149
150
|
if isinstance(self.result, str):
|
|
150
151
|
return {
|
|
151
152
|
"role": "tool",
|
|
@@ -174,8 +175,7 @@ class ToolResult:
|
|
|
174
175
|
raise ValueError("result type not supported")
|
|
175
176
|
|
|
176
177
|
def oa_resp(self) -> dict: # OpenAI Responses
|
|
177
|
-
print("
|
|
178
|
-
print("typeof self.result:", type(self.result))
|
|
178
|
+
# print("typeof self.result:", type(self.result))
|
|
179
179
|
# if normal (not built-in just return the regular output
|
|
180
180
|
if not self.built_in:
|
|
181
181
|
result = (
|
|
@@ -329,6 +329,18 @@ class Message:
|
|
|
329
329
|
"""Get all thinking parts with proper typing."""
|
|
330
330
|
return [part for part in self.parts if part.type == "thinking"] # type: ignore
|
|
331
331
|
|
|
332
|
+
# @staticmethod
|
|
333
|
+
# def dump_part(part: Part):
|
|
334
|
+
# if isinstance(value, Text):
|
|
335
|
+
# return {"type": "text", "text": value.text}
|
|
336
|
+
# if isinstance(value, Image):
|
|
337
|
+
# w, h = value.size
|
|
338
|
+
# return {"type": "image", "tag": f"<Image ({w}×{h})>"}
|
|
339
|
+
# if isinstance(value, File):
|
|
340
|
+
# size = value.size
|
|
341
|
+
# return {"type": "file", "tag": f"<File ({size} bytes)>"}
|
|
342
|
+
# return repr(value)
|
|
343
|
+
|
|
332
344
|
def to_log(self) -> dict:
|
|
333
345
|
"""
|
|
334
346
|
Return a JSON-serialisable dict that fully captures the message.
|
|
@@ -416,12 +428,17 @@ class Message:
|
|
|
416
428
|
|
|
417
429
|
return cls(role, parts)
|
|
418
430
|
|
|
419
|
-
def
|
|
431
|
+
def with_text(self, content: str) -> "Message":
|
|
420
432
|
"""Append a text block and return self for chaining."""
|
|
421
433
|
self.parts.append(Text(content))
|
|
422
434
|
return self
|
|
423
435
|
|
|
424
|
-
|
|
436
|
+
@deprecated("with_text")
|
|
437
|
+
def add_text(self, content: str) -> "Message":
|
|
438
|
+
"""Append a text block and return self for chaining."""
|
|
439
|
+
return self.with_text(content)
|
|
440
|
+
|
|
441
|
+
def with_image(
|
|
425
442
|
self,
|
|
426
443
|
data: bytes | str | Path | io.BytesIO | Image,
|
|
427
444
|
*,
|
|
@@ -447,9 +464,50 @@ class Message:
|
|
|
447
464
|
self.parts.append(img)
|
|
448
465
|
return self
|
|
449
466
|
|
|
467
|
+
@deprecated("with_image")
|
|
468
|
+
def add_image(
|
|
469
|
+
self,
|
|
470
|
+
data: bytes | str | Path | io.BytesIO | Image,
|
|
471
|
+
*,
|
|
472
|
+
media_type: MediaType | None = None,
|
|
473
|
+
detail: Literal["low", "high", "auto"] = "auto",
|
|
474
|
+
max_size: int | None = None,
|
|
475
|
+
) -> "Message":
|
|
476
|
+
"""
|
|
477
|
+
Append an image block and return self for chaining.
|
|
478
|
+
|
|
479
|
+
If max_size is provided, the image will be resized so that its longer
|
|
480
|
+
dimension equals max_size, but only if the longer dimension is currently
|
|
481
|
+
larger than max_size.
|
|
482
|
+
"""
|
|
483
|
+
return self.with_image(
|
|
484
|
+
data=data, media_type=media_type, detail=detail, max_size=max_size
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def with_file(
|
|
488
|
+
self,
|
|
489
|
+
data: bytes | str | Path | io.BytesIO | File,
|
|
490
|
+
*,
|
|
491
|
+
media_type: str | None = None,
|
|
492
|
+
filename: str | None = None,
|
|
493
|
+
# remote: bool = False,
|
|
494
|
+
# provider: Literal["openai", "anthropic", "google"] | None = None,
|
|
495
|
+
) -> "Message":
|
|
496
|
+
"""
|
|
497
|
+
Append a file block and return self for chaining.
|
|
498
|
+
"""
|
|
499
|
+
if not isinstance(data, File):
|
|
500
|
+
file = File(data, media_type=media_type, filename=filename)
|
|
501
|
+
else:
|
|
502
|
+
file = data
|
|
503
|
+
|
|
504
|
+
self.parts.append(file)
|
|
505
|
+
return self
|
|
506
|
+
|
|
507
|
+
@deprecated("with_file")
|
|
450
508
|
def add_file(
|
|
451
509
|
self,
|
|
452
|
-
data: bytes | str | Path | io.BytesIO,
|
|
510
|
+
data: bytes | str | Path | io.BytesIO | File,
|
|
453
511
|
*,
|
|
454
512
|
media_type: str | None = None,
|
|
455
513
|
filename: str | None = None,
|
|
@@ -457,27 +515,66 @@ class Message:
|
|
|
457
515
|
"""
|
|
458
516
|
Append a file block and return self for chaining.
|
|
459
517
|
"""
|
|
460
|
-
|
|
518
|
+
return self.with_file(data, media_type=media_type, filename=filename)
|
|
519
|
+
|
|
520
|
+
async def with_remote_file(
|
|
521
|
+
self,
|
|
522
|
+
data: bytes | str | Path | io.BytesIO | File,
|
|
523
|
+
*,
|
|
524
|
+
media_type: str | None = None,
|
|
525
|
+
filename: str | None = None,
|
|
526
|
+
provider: Literal["openai", "anthropic", "google"] = "openai",
|
|
527
|
+
):
|
|
528
|
+
if not isinstance(data, File):
|
|
529
|
+
file = File(data, media_type=media_type, filename=filename)
|
|
530
|
+
else:
|
|
531
|
+
file = data
|
|
532
|
+
|
|
533
|
+
if not file.is_remote:
|
|
534
|
+
file = await file.as_remote(provider=provider)
|
|
535
|
+
else:
|
|
536
|
+
if file.remote_provider != provider:
|
|
537
|
+
raise ValueError(
|
|
538
|
+
f"File is already remote with provider {file.remote_provider}, cannot change provider"
|
|
539
|
+
)
|
|
540
|
+
|
|
461
541
|
self.parts.append(file)
|
|
462
542
|
return self
|
|
463
543
|
|
|
464
|
-
def
|
|
544
|
+
def with_tool_call(self, id: str, name: str, arguments: dict) -> "Message":
|
|
465
545
|
"""Append a tool call block and return self for chaining."""
|
|
466
546
|
self.parts.append(ToolCall(id=id, name=name, arguments=arguments))
|
|
467
547
|
return self
|
|
468
548
|
|
|
469
|
-
|
|
549
|
+
@deprecated("with_tool_call")
|
|
550
|
+
def add_tool_call(self, id: str, name: str, arguments: dict) -> "Message":
|
|
551
|
+
"""Append a tool call block and return self for chaining."""
|
|
552
|
+
return self.with_tool_call(id, name, arguments)
|
|
553
|
+
|
|
554
|
+
def with_tool_result(
|
|
470
555
|
self, tool_call_id: str, result: str | list[ToolResultPart]
|
|
471
556
|
) -> "Message":
|
|
472
557
|
"""Append a tool result block and return self for chaining."""
|
|
473
558
|
self.parts.append(ToolResult(tool_call_id=tool_call_id, result=result))
|
|
474
559
|
return self
|
|
475
560
|
|
|
476
|
-
|
|
561
|
+
@deprecated("with_tool_result")
|
|
562
|
+
def add_tool_result(
|
|
563
|
+
self, tool_call_id: str, result: str | list[ToolResultPart]
|
|
564
|
+
) -> "Message":
|
|
565
|
+
"""Append a tool result block and return self for chaining."""
|
|
566
|
+
return self.with_tool_result(tool_call_id, result)
|
|
567
|
+
|
|
568
|
+
def with_thinking(self, content: str) -> "Message":
|
|
477
569
|
"""Append a thinking block and return self for chaining."""
|
|
478
570
|
self.parts.append(Thinking(content=content))
|
|
479
571
|
return self
|
|
480
572
|
|
|
573
|
+
@deprecated("with_thinking")
|
|
574
|
+
def add_thinking(self, content: str) -> "Message":
|
|
575
|
+
"""Append a thinking block and return self for chaining."""
|
|
576
|
+
return self.with_thinking(content)
|
|
577
|
+
|
|
481
578
|
# -------- convenient constructors --------
|
|
482
579
|
@classmethod
|
|
483
580
|
def user(
|
|
@@ -489,25 +586,25 @@ class Message:
|
|
|
489
586
|
) -> "Message":
|
|
490
587
|
res = cls("user", [])
|
|
491
588
|
if text is not None:
|
|
492
|
-
res.
|
|
589
|
+
res.with_text(text)
|
|
493
590
|
if image is not None:
|
|
494
|
-
res.
|
|
591
|
+
res.with_image(image)
|
|
495
592
|
if file is not None:
|
|
496
|
-
res.
|
|
593
|
+
res.with_file(file)
|
|
497
594
|
return res
|
|
498
595
|
|
|
499
596
|
@classmethod
|
|
500
597
|
def system(cls, text: str | None = None) -> "Message":
|
|
501
598
|
res = cls("system", [])
|
|
502
599
|
if text is not None:
|
|
503
|
-
res.
|
|
600
|
+
res.with_text(text)
|
|
504
601
|
return res
|
|
505
602
|
|
|
506
603
|
@classmethod
|
|
507
604
|
def ai(cls, text: str | None = None) -> "Message":
|
|
508
605
|
res = cls("assistant", [])
|
|
509
606
|
if text is not None:
|
|
510
|
-
res.
|
|
607
|
+
res.with_text(text)
|
|
511
608
|
return res
|
|
512
609
|
|
|
513
610
|
# ──── provider-specific constructors ───
|
|
@@ -699,9 +796,9 @@ class Conversation:
|
|
|
699
796
|
) -> "Conversation":
|
|
700
797
|
msg = Message.user(text)
|
|
701
798
|
if image is not None:
|
|
702
|
-
msg.
|
|
799
|
+
msg.with_image(image)
|
|
703
800
|
if file is not None:
|
|
704
|
-
msg.
|
|
801
|
+
msg.with_file(file)
|
|
705
802
|
return cls([msg])
|
|
706
803
|
|
|
707
804
|
@classmethod
|
|
@@ -1189,11 +1286,11 @@ class Conversation:
|
|
|
1189
1286
|
"""
|
|
1190
1287
|
if self.messages and self.messages[-1].role == "tool":
|
|
1191
1288
|
# Append to existing tool message (parallel tool calls)
|
|
1192
|
-
self.messages[-1].
|
|
1289
|
+
self.messages[-1].with_tool_result(tool_call_id, result)
|
|
1193
1290
|
else:
|
|
1194
1291
|
# Create new tool message
|
|
1195
1292
|
tool_msg = Message("tool", [])
|
|
1196
|
-
tool_msg.
|
|
1293
|
+
tool_msg.with_tool_result(tool_call_id, result)
|
|
1197
1294
|
self.messages.append(tool_msg)
|
|
1198
1295
|
return self
|
|
1199
1296
|
|
|
@@ -1212,11 +1309,11 @@ class Conversation:
|
|
|
1212
1309
|
for i, tool_result in enumerate(m.tool_results):
|
|
1213
1310
|
images = tool_result.get_images()
|
|
1214
1311
|
if len(images) > 0:
|
|
1215
|
-
user_msg.
|
|
1312
|
+
user_msg.with_text(
|
|
1216
1313
|
f"[Images for Tool Call {tool_result.tool_call_id}]"
|
|
1217
1314
|
)
|
|
1218
1315
|
for img in images:
|
|
1219
|
-
user_msg.
|
|
1316
|
+
user_msg.with_image(img)
|
|
1220
1317
|
|
|
1221
1318
|
else:
|
|
1222
1319
|
result.append(m.oa_chat())
|
|
@@ -1496,9 +1593,21 @@ class Conversation:
|
|
|
1496
1593
|
return cls(msgs)
|
|
1497
1594
|
|
|
1498
1595
|
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1596
|
+
Prompt: TypeAlias = str | list[dict] | Message | Conversation
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
def prompts_to_conversations(prompts: Sequence[Prompt]) -> Sequence[Prompt]:
|
|
1600
|
+
converted = []
|
|
1601
|
+
for prompt in prompts:
|
|
1602
|
+
if isinstance(prompt, Conversation):
|
|
1603
|
+
converted.append(prompt)
|
|
1604
|
+
elif isinstance(prompt, Message):
|
|
1605
|
+
converted.append(Conversation([prompt]))
|
|
1606
|
+
elif isinstance(prompt, str):
|
|
1607
|
+
converted.append(Conversation.user(prompt))
|
|
1608
|
+
elif isinstance(prompt, list):
|
|
1609
|
+
conv, provider = Conversation.from_unknown(prompt)
|
|
1610
|
+
converted.append(conv)
|
|
1611
|
+
else:
|
|
1612
|
+
raise ValueError(f"Unknown prompt type {type(prompt)}")
|
|
1613
|
+
return converted
|
lm_deluge/request_context.py
CHANGED
|
@@ -26,28 +26,22 @@ class RequestContext:
|
|
|
26
26
|
|
|
27
27
|
# Infrastructure
|
|
28
28
|
status_tracker: StatusTracker | None = None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
29
|
+
# avoiding circular import
|
|
30
|
+
results_arr: list[Any] | None = None # list["APIRequestBase"]
|
|
32
31
|
callback: Callable | None = None
|
|
33
32
|
|
|
34
33
|
# Optional features
|
|
35
34
|
tools: list | None = None
|
|
36
35
|
cache: CachePattern | None = None
|
|
37
36
|
use_responses_api: bool = False
|
|
37
|
+
background: bool = False
|
|
38
|
+
service_tier: str | None = None
|
|
38
39
|
extra_headers: dict[str, str] | None = None
|
|
40
|
+
extra_body: dict[str, Any] | None = None
|
|
39
41
|
force_local_mcp: bool = False
|
|
40
42
|
|
|
41
43
|
# Computed properties
|
|
42
44
|
cache_key: str = field(init=False)
|
|
43
|
-
# num_tokens: int = field(init=False)
|
|
44
|
-
|
|
45
|
-
# def __post_init__(self):
|
|
46
|
-
# # Compute cache key from prompt fingerprint
|
|
47
|
-
# # self.cache_key = self.prompt.fingerprint
|
|
48
|
-
|
|
49
|
-
# # Compute token count
|
|
50
|
-
# self.num_tokens =
|
|
51
45
|
|
|
52
46
|
@cached_property
|
|
53
47
|
def num_tokens(self):
|
|
@@ -74,6 +68,10 @@ class RequestContext:
|
|
|
74
68
|
"tools": self.tools,
|
|
75
69
|
"cache": self.cache,
|
|
76
70
|
"use_responses_api": self.use_responses_api,
|
|
71
|
+
"background": self.background,
|
|
72
|
+
"service_tier": self.service_tier,
|
|
73
|
+
"extra_headers": self.extra_headers,
|
|
74
|
+
"extra_body": self.extra_body,
|
|
77
75
|
"force_local_mcp": self.force_local_mcp,
|
|
78
76
|
}
|
|
79
77
|
|