lm-deluge 0.0.30__tar.gz → 0.0.32__tar.gz

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.

Files changed (62) hide show
  1. {lm_deluge-0.0.30/src/lm_deluge.egg-info → lm_deluge-0.0.32}/PKG-INFO +1 -1
  2. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/pyproject.toml +1 -1
  3. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/anthropic.py +2 -2
  4. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/bedrock.py +4 -1
  5. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/client.py +106 -14
  6. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/file.py +4 -1
  7. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/models.py +1 -1
  8. {lm_deluge-0.0.30 → lm_deluge-0.0.32/src/lm_deluge.egg-info}/PKG-INFO +1 -1
  9. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/LICENSE +0 -0
  10. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/README.md +0 -0
  11. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/setup.cfg +0 -0
  12. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/__init__.py +0 -0
  13. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/agent.py +0 -0
  14. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/__init__.py +0 -0
  15. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/base.py +0 -0
  16. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/common.py +0 -0
  17. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/bedrock.py +0 -0
  18. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/cohere.py +0 -0
  19. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/deepseek.py +0 -0
  20. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/mistral.py +0 -0
  21. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/vertex.py +0 -0
  22. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/gemini.py +0 -0
  23. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/mistral.py +0 -0
  24. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/openai.py +0 -0
  25. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/response.py +0 -0
  26. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/batches.py +0 -0
  27. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/__init__.py +0 -0
  28. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/bash.py +0 -0
  29. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/computer_use.py +0 -0
  30. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/editor.py +0 -0
  31. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/base.py +0 -0
  32. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/openai.py +0 -0
  33. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/cache.py +0 -0
  34. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/config.py +0 -0
  35. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/embed.py +0 -0
  36. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/errors.py +0 -0
  37. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/gemini_limits.py +0 -0
  38. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/image.py +0 -0
  39. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/__init__.py +0 -0
  40. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/classify.py +0 -0
  41. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/extract.py +0 -0
  42. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/locate.py +0 -0
  43. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/ocr.py +0 -0
  44. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/score.py +0 -0
  45. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/translate.py +0 -0
  46. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/prompt.py +0 -0
  47. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/request_context.py +0 -0
  48. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/rerank.py +0 -0
  49. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/tool.py +0 -0
  50. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/tracker.py +0 -0
  51. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/usage.py +0 -0
  52. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/util/json.py +0 -0
  53. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/util/logprobs.py +0 -0
  54. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/util/spatial.py +0 -0
  55. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/util/validation.py +0 -0
  56. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge/util/xml.py +0 -0
  57. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/SOURCES.txt +0 -0
  58. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/dependency_links.txt +0 -0
  59. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/requires.txt +0 -0
  60. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/top_level.txt +0 -0
  61. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/tests/test_builtin_tools.py +0 -0
  62. {lm_deluge-0.0.30 → lm_deluge-0.0.32}/tests/test_native_mcp_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lm_deluge
3
- Version: 0.0.30
3
+ Version: 0.0.32
4
4
  Summary: Python utility for using LLM API models.
5
5
  Author-email: Benjamin Anderson <ben@trytaylor.ai>
6
6
  Requires-Python: >=3.10
@@ -3,7 +3,7 @@ requires = ["setuptools", "wheel"]
3
3
 
4
4
  [project]
5
5
  name = "lm_deluge"
6
- version = "0.0.30"
6
+ version = "0.0.32"
7
7
  authors = [{ name = "Benjamin Anderson", email = "ben@trytaylor.ai" }]
8
8
  description = "Python utility for using LLM API models."
9
9
  readme = "README.md"
@@ -38,8 +38,8 @@ def _build_anthropic_request(
38
38
  tools = context.tools
39
39
  sampling_params = context.sampling_params
40
40
  system_message, messages = prompt.to_anthropic(cache_pattern=cache_pattern)
41
- if not system_message:
42
- print("WARNING: system_message is None")
41
+ # if not system_message:
42
+ # print("WARNING: system_message is None")
43
43
  base_headers = {
44
44
  "x-api-key": os.getenv(model.api_key_env_var),
45
45
  "anthropic-version": "2023-06-01",
@@ -132,7 +132,7 @@ async def _build_anthropic_bedrock_request(
132
132
  if len(mcp_servers) > 0:
133
133
  request_json["mcp_servers"] = mcp_servers
134
134
 
135
- return request_json, base_headers, auth, url
135
+ return request_json, base_headers, auth, url, region
136
136
 
137
137
 
138
138
  class BedrockRequest(APIRequestBase):
@@ -140,6 +140,7 @@ class BedrockRequest(APIRequestBase):
140
140
  super().__init__(context=context)
141
141
 
142
142
  self.model = APIModel.from_registry(self.context.model_name)
143
+ self.region = None # Will be set during build_request
143
144
 
144
145
  async def build_request(self):
145
146
  self.url = f"{self.model.api_base}/messages"
@@ -153,6 +154,7 @@ class BedrockRequest(APIRequestBase):
153
154
  base_headers,
154
155
  self.auth,
155
156
  self.url,
157
+ self.region,
156
158
  ) = await _build_anthropic_bedrock_request(self.model, self.context)
157
159
  self.request_header = self.merge_headers(
158
160
  base_headers, exclude_patterns=["anthropic", "openai", "gemini", "mistral"]
@@ -160,6 +162,7 @@ class BedrockRequest(APIRequestBase):
160
162
 
161
163
  async def execute_once(self) -> APIResponse:
162
164
  """Override execute_once to handle AWS4Auth signing."""
165
+ await self.build_request()
163
166
  import aiohttp
164
167
 
165
168
  assert self.context.status_tracker
@@ -4,7 +4,7 @@ from typing import Any, Literal, Self, Sequence, overload
4
4
 
5
5
  import numpy as np
6
6
  import yaml
7
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, PrivateAttr
8
8
  from pydantic.functional_validators import model_validator
9
9
 
10
10
  from lm_deluge.api_requests.openai import stream_chat
@@ -32,13 +32,7 @@ class LLMClient(BaseModel):
32
32
  Handles models, sampling params for each model, model weights, rate limits, etc.
33
33
  """
34
34
 
35
- model_names: list[str] = ["gpt-4.1-mini"]
36
-
37
- def __init__(self, model_name: str | list[str] | None = None, **kwargs):
38
- if model_name is not None:
39
- kwargs["model_names"] = model_name
40
- super().__init__(**kwargs)
41
-
35
+ model_names: str | list[str] = ["gpt-4.1-mini"]
42
36
  max_requests_per_minute: int = 1_000
43
37
  max_tokens_per_minute: int = 100_000
44
38
  max_concurrent_requests: int = 225
@@ -59,6 +53,13 @@ class LLMClient(BaseModel):
59
53
  top_logprobs: int | None = None
60
54
  force_local_mcp: bool = False
61
55
 
56
+ # Internal state for async task handling
57
+ _next_task_id: int = PrivateAttr(default=0)
58
+ _tasks: dict[int, asyncio.Task] = PrivateAttr(default_factory=dict)
59
+ _results: dict[int, APIResponse] = PrivateAttr(default_factory=dict)
60
+ _tracker: StatusTracker | None = PrivateAttr(default=None)
61
+ _capacity_lock: asyncio.Lock = PrivateAttr(default_factory=asyncio.Lock)
62
+
62
63
  # NEW! Builder methods
63
64
  def with_model(self, model: str):
64
65
  self.model_names = [model]
@@ -81,6 +82,18 @@ class LLMClient(BaseModel):
81
82
  if max_concurrent_requests:
82
83
  self.max_concurrent_requests = max_concurrent_requests
83
84
 
85
+ def _get_tracker(self) -> StatusTracker:
86
+ if self._tracker is None:
87
+ self._tracker = StatusTracker(
88
+ max_requests_per_minute=self.max_requests_per_minute,
89
+ max_tokens_per_minute=self.max_tokens_per_minute,
90
+ max_concurrent_requests=self.max_concurrent_requests,
91
+ use_progress_bar=False,
92
+ progress_bar_disable=True,
93
+ use_rich=False,
94
+ )
95
+ return self._tracker
96
+
84
97
  @property
85
98
  def models(self):
86
99
  return self.model_names # why? idk
@@ -90,6 +103,8 @@ class LLMClient(BaseModel):
90
103
  def fix_lists(cls, data) -> "LLMClient":
91
104
  if isinstance(data.get("model_names"), str):
92
105
  data["model_names"] = [data["model_names"]]
106
+ if not isinstance(data.get("sampling_params", []), list):
107
+ data["sampling_params"] = [data["sampling_params"]]
93
108
  if "sampling_params" not in data or len(data.get("sampling_params", [])) == 0:
94
109
  data["sampling_params"] = [
95
110
  SamplingParams(
@@ -192,14 +207,19 @@ class LLMClient(BaseModel):
192
207
  chosen_sp = self.sampling_params[self.models.index(chosen_model)]
193
208
  return chosen_model, chosen_sp
194
209
 
195
- async def _wait_for_capacity(self, num_tokens: int, tracker: StatusTracker):
210
+ async def _wait_for_capacity(
211
+ self, num_tokens: int, tracker: StatusTracker, *, retry: bool = False
212
+ ):
196
213
  while True:
197
- if tracker.check_capacity(num_tokens):
198
- tracker.set_limiting_factor(None)
199
- return
214
+ async with self._capacity_lock:
215
+ tracker.update_capacity()
216
+ if tracker.check_capacity(num_tokens, retry=retry):
217
+ tracker.set_limiting_factor(None)
218
+ return
219
+ seconds_to_pause = tracker.seconds_to_pause
200
220
 
201
- if tracker.seconds_to_pause > 0:
202
- await asyncio.sleep(tracker.seconds_to_pause)
221
+ if seconds_to_pause > 0:
222
+ await asyncio.sleep(seconds_to_pause)
203
223
  else:
204
224
  await asyncio.sleep(random.random())
205
225
 
@@ -446,6 +466,78 @@ class LLMClient(BaseModel):
446
466
  )
447
467
  )
448
468
 
469
+ async def _run_context(self, context: RequestContext) -> APIResponse:
470
+ tracker = self._get_tracker()
471
+ retry = False
472
+ retry_queue: asyncio.Queue[RequestContext] = asyncio.Queue()
473
+ current = context
474
+ while True:
475
+ await self._wait_for_capacity(current.num_tokens, tracker, retry=retry)
476
+ response = await self.process_single_request(current, retry_queue)
477
+ if not response.is_error or retry_queue.empty():
478
+ self._results[context.task_id] = response
479
+ return response
480
+ current = await retry_queue.get()
481
+ retry = True
482
+
483
+ def start_nowait(
484
+ self,
485
+ prompt: str | Conversation,
486
+ *,
487
+ tools: list[Tool | dict | MCPServer] | None = None,
488
+ cache: CachePattern | None = None,
489
+ use_responses_api: bool = False,
490
+ ) -> int:
491
+ tracker = self._get_tracker()
492
+ task_id = self._next_task_id
493
+ self._next_task_id += 1
494
+ model, sampling_params = self._select_model()
495
+ if isinstance(prompt, str):
496
+ prompt = Conversation.user(prompt)
497
+ context = RequestContext(
498
+ task_id=task_id,
499
+ model_name=model,
500
+ prompt=prompt,
501
+ sampling_params=sampling_params,
502
+ attempts_left=self.max_attempts,
503
+ request_timeout=self.request_timeout,
504
+ status_tracker=tracker,
505
+ tools=tools,
506
+ cache=cache,
507
+ use_responses_api=use_responses_api,
508
+ extra_headers=self.extra_headers,
509
+ force_local_mcp=self.force_local_mcp,
510
+ )
511
+ task = asyncio.create_task(self._run_context(context))
512
+ self._tasks[task_id] = task
513
+ return task_id
514
+
515
+ async def start(
516
+ self,
517
+ prompt: str | Conversation,
518
+ *,
519
+ tools: list[Tool | dict | MCPServer] | None = None,
520
+ cache: CachePattern | None = None,
521
+ use_responses_api: bool = False,
522
+ ) -> APIResponse | None:
523
+ task_id = self.start_nowait(
524
+ prompt, tools=tools, cache=cache, use_responses_api=use_responses_api
525
+ )
526
+ return await self.wait_for(task_id)
527
+
528
+ async def wait_for(self, task_id: int) -> APIResponse | None:
529
+ task = self._tasks.get(task_id)
530
+ if task:
531
+ return await task
532
+ return self._results.get(task_id)
533
+
534
+ async def wait_for_all(
535
+ self, task_ids: Sequence[int] | None = None
536
+ ) -> list[APIResponse | None]:
537
+ if task_ids is None:
538
+ task_ids = list(self._tasks.keys())
539
+ return [await self.wait_for(tid) for tid in task_ids]
540
+
449
541
  async def stream(
450
542
  self,
451
543
  prompt: str | Conversation,
@@ -37,7 +37,10 @@ class File:
37
37
  header, encoded = self.data.split(",", 1)
38
38
  return base64.b64decode(encoded)
39
39
  else:
40
- raise ValueError("unreadable file format")
40
+ err = f"unreadable file. self.data type: {type(self.data)}"
41
+ if isinstance(self.data, str) and len(self.data) < 1_000:
42
+ err += f". self.data: {len(self.data)}"
43
+ raise ValueError(err)
41
44
 
42
45
  def _mime(self) -> str:
43
46
  if self.media_type:
@@ -28,7 +28,7 @@ BUILTIN_MODELS = {
28
28
  "reasoning_model": False,
29
29
  },
30
30
  "llama-4-maverick": {
31
- "id": "llama-4-scout",
31
+ "id": "llama-4-maverick",
32
32
  "name": "Llama-4-Maverick-17B-128E-Instruct-FP8",
33
33
  "api_base": "https://api.llama.com/compat/v1",
34
34
  "api_key_env_var": "META_API_KEY",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lm_deluge
3
- Version: 0.0.30
3
+ Version: 0.0.32
4
4
  Summary: Python utility for using LLM API models.
5
5
  Author-email: Benjamin Anderson <ben@trytaylor.ai>
6
6
  Requires-Python: >=3.10
File without changes
File without changes
File without changes