lm-deluge 0.0.31__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.31/src/lm_deluge.egg-info → lm_deluge-0.0.32}/PKG-INFO +1 -1
  2. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/pyproject.toml +1 -1
  3. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/anthropic.py +2 -2
  4. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/client.py +106 -14
  5. {lm_deluge-0.0.31 → lm_deluge-0.0.32/src/lm_deluge.egg-info}/PKG-INFO +1 -1
  6. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/LICENSE +0 -0
  7. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/README.md +0 -0
  8. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/setup.cfg +0 -0
  9. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/__init__.py +0 -0
  10. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/agent.py +0 -0
  11. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/__init__.py +0 -0
  12. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/base.py +0 -0
  13. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/bedrock.py +0 -0
  14. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/common.py +0 -0
  15. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/bedrock.py +0 -0
  16. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/cohere.py +0 -0
  17. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/deepseek.py +0 -0
  18. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/mistral.py +0 -0
  19. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/deprecated/vertex.py +0 -0
  20. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/gemini.py +0 -0
  21. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/mistral.py +0 -0
  22. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/openai.py +0 -0
  23. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/api_requests/response.py +0 -0
  24. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/batches.py +0 -0
  25. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/__init__.py +0 -0
  26. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/bash.py +0 -0
  27. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/computer_use.py +0 -0
  28. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/anthropic/editor.py +0 -0
  29. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/base.py +0 -0
  30. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/built_in_tools/openai.py +0 -0
  31. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/cache.py +0 -0
  32. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/config.py +0 -0
  33. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/embed.py +0 -0
  34. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/errors.py +0 -0
  35. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/file.py +0 -0
  36. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/gemini_limits.py +0 -0
  37. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/image.py +0 -0
  38. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/__init__.py +0 -0
  39. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/classify.py +0 -0
  40. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/extract.py +0 -0
  41. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/locate.py +0 -0
  42. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/ocr.py +0 -0
  43. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/score.py +0 -0
  44. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/llm_tools/translate.py +0 -0
  45. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/models.py +0 -0
  46. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/prompt.py +0 -0
  47. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/request_context.py +0 -0
  48. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/rerank.py +0 -0
  49. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/tool.py +0 -0
  50. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/tracker.py +0 -0
  51. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/usage.py +0 -0
  52. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/util/json.py +0 -0
  53. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/util/logprobs.py +0 -0
  54. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/util/spatial.py +0 -0
  55. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/util/validation.py +0 -0
  56. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge/util/xml.py +0 -0
  57. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/SOURCES.txt +0 -0
  58. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/dependency_links.txt +0 -0
  59. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/requires.txt +0 -0
  60. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/src/lm_deluge.egg-info/top_level.txt +0 -0
  61. {lm_deluge-0.0.31 → lm_deluge-0.0.32}/tests/test_builtin_tools.py +0 -0
  62. {lm_deluge-0.0.31 → 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.31
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.31"
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",
@@ -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,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lm_deluge
3
- Version: 0.0.31
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