taskiq-llm-queue 0.1.0a1__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.
Files changed (65) hide show
  1. taskiq_llm_queue/__init__.py +138 -0
  2. taskiq_llm_queue/_logging.py +40 -0
  3. taskiq_llm_queue/_version.py +5 -0
  4. taskiq_llm_queue/broker/__init__.py +86 -0
  5. taskiq_llm_queue/broker/acker.py +239 -0
  6. taskiq_llm_queue/broker/batching.py +188 -0
  7. taskiq_llm_queue/broker/broker.py +937 -0
  8. taskiq_llm_queue/broker/delivery.py +50 -0
  9. taskiq_llm_queue/broker/errors.py +178 -0
  10. taskiq_llm_queue/broker/events.py +51 -0
  11. taskiq_llm_queue/broker/filters.py +32 -0
  12. taskiq_llm_queue/broker/heartbeat.py +157 -0
  13. taskiq_llm_queue/broker/queue.py +141 -0
  14. taskiq_llm_queue/broker/s3_offload.py +346 -0
  15. taskiq_llm_queue/broker/scheduler.py +113 -0
  16. taskiq_llm_queue/broker/serializer.py +32 -0
  17. taskiq_llm_queue/cli.py +488 -0
  18. taskiq_llm_queue/client.py +1409 -0
  19. taskiq_llm_queue/config.py +592 -0
  20. taskiq_llm_queue/context.py +62 -0
  21. taskiq_llm_queue/core.py +832 -0
  22. taskiq_llm_queue/envelope.py +136 -0
  23. taskiq_llm_queue/errors.py +71 -0
  24. taskiq_llm_queue/failover.py +286 -0
  25. taskiq_llm_queue/http_transport.py +220 -0
  26. taskiq_llm_queue/keys.py +87 -0
  27. taskiq_llm_queue/langchain/__init__.py +64 -0
  28. taskiq_llm_queue/langchain/_client.py +142 -0
  29. taskiq_llm_queue/langchain/bedrock.py +291 -0
  30. taskiq_llm_queue/langchain/openai.py +103 -0
  31. taskiq_llm_queue/lifecycle.py +512 -0
  32. taskiq_llm_queue/metrics/__init__.py +45 -0
  33. taskiq_llm_queue/metrics/emf.py +100 -0
  34. taskiq_llm_queue/metrics/prometheus.py +66 -0
  35. taskiq_llm_queue/metrics_names.py +19 -0
  36. taskiq_llm_queue/protocols.py +140 -0
  37. taskiq_llm_queue/providers/__init__.py +62 -0
  38. taskiq_llm_queue/providers/bedrock.py +188 -0
  39. taskiq_llm_queue/providers/classify.py +251 -0
  40. taskiq_llm_queue/providers/embeddings.py +87 -0
  41. taskiq_llm_queue/providers/hosts.py +34 -0
  42. taskiq_llm_queue/providers/openai_compat.py +142 -0
  43. taskiq_llm_queue/providers/registry.py +173 -0
  44. taskiq_llm_queue/py.typed +0 -0
  45. taskiq_llm_queue/reliability/__init__.py +59 -0
  46. taskiq_llm_queue/reliability/_lua.py +461 -0
  47. taskiq_llm_queue/reliability/breaker.py +86 -0
  48. taskiq_llm_queue/reliability/claims.py +226 -0
  49. taskiq_llm_queue/reliability/config_store.py +148 -0
  50. taskiq_llm_queue/reliability/fallback.py +111 -0
  51. taskiq_llm_queue/reliability/limiter.py +482 -0
  52. taskiq_llm_queue/reliability/local_limiter.py +153 -0
  53. taskiq_llm_queue/results/__init__.py +25 -0
  54. taskiq_llm_queue/results/backend.py +226 -0
  55. taskiq_llm_queue/results/dispatcher.py +366 -0
  56. taskiq_llm_queue/results/s3_backend.py +105 -0
  57. taskiq_llm_queue/routing.py +173 -0
  58. taskiq_llm_queue/routing_hooks.py +90 -0
  59. taskiq_llm_queue/tasks.py +453 -0
  60. taskiq_llm_queue-0.1.0a1.dist-info/METADATA +296 -0
  61. taskiq_llm_queue-0.1.0a1.dist-info/RECORD +65 -0
  62. taskiq_llm_queue-0.1.0a1.dist-info/WHEEL +4 -0
  63. taskiq_llm_queue-0.1.0a1.dist-info/entry_points.txt +2 -0
  64. taskiq_llm_queue-0.1.0a1.dist-info/licenses/LICENSE +21 -0
  65. taskiq_llm_queue-0.1.0a1.dist-info/licenses/NOTICE +15 -0
@@ -0,0 +1,296 @@
1
+ Metadata-Version: 2.5
2
+ Name: taskiq-llm-queue
3
+ Version: 0.1.0a1
4
+ Summary: An LLM request client with its own durable queue: taskiq + Amazon SQS, adaptive limits, retry and fallback.
5
+ Project-URL: Homepage, https://github.com/techiewonk/taskiq-llm-queue
6
+ Project-URL: Documentation, https://github.com/techiewonk/taskiq-llm-queue#readme
7
+ Project-URL: Source, https://github.com/techiewonk/taskiq-llm-queue
8
+ Project-URL: Issues, https://github.com/techiewonk/taskiq-llm-queue/issues
9
+ Project-URL: Changelog, https://github.com/techiewonk/taskiq-llm-queue/blob/main/CHANGELOG.md
10
+ Author: The taskiq-llm-queue authors
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ License-File: NOTICE
14
+ Keywords: bedrock,langchain,langgraph,llm,openai,queue,rate-limit,sqs,taskiq
15
+ Classifier: Development Status :: 2 - Pre-Alpha
16
+ Classifier: Framework :: AsyncIO
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Operating System :: OS Independent
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: System :: Distributed Computing
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.11
29
+ Requires-Dist: aiobotocore>=2.23
30
+ Requires-Dist: orjson<4,>=3.10
31
+ Requires-Dist: pydantic<3,>=2.10
32
+ Requires-Dist: redis>=5.0.1
33
+ Requires-Dist: structlog>=24.1
34
+ Requires-Dist: taskiq<0.14,>=0.13
35
+ Provides-Extra: langchain-aws
36
+ Requires-Dist: langchain-aws<2,>=0.2.24; extra == 'langchain-aws'
37
+ Requires-Dist: langchain-core<2,>=0.3.68; extra == 'langchain-aws'
38
+ Provides-Extra: langchain-openai
39
+ Requires-Dist: langchain-core<2,>=0.3.68; extra == 'langchain-openai'
40
+ Requires-Dist: langchain-openai<2,>=0.3.26; extra == 'langchain-openai'
41
+ Provides-Extra: metrics
42
+ Requires-Dist: prometheus-client>=0.20; extra == 'metrics'
43
+ Provides-Extra: openai
44
+ Requires-Dist: httpx<1,>=0.27; extra == 'openai'
45
+ Requires-Dist: openai<4,>=1.40; extra == 'openai'
46
+ Provides-Extra: speedups
47
+ Requires-Dist: hiredis>=3; extra == 'speedups'
48
+ Description-Content-Type: text/markdown
49
+
50
+ # taskiq-llm-queue
51
+
52
+ > **Status:** pre-alpha (`0.1.0a1`). Built and tested: the `LLMQueue` client, the SQS broker, adaptive limiter, claims, circuit breaker, retry and fallback, JSON and S3 result backends, Bedrock and OpenAI-compatible providers, LangChain/LangGraph adapters, canary routing, failover and the CLI. 619 tests on Valkey 8.1, ElasticMQ 1.7.1 and a fake LLM server; CI on Python 3.11–3.14. Breaking changes are still possible before 0.1 (see [CHANGELOG](https://github.com/techiewonk/taskiq-llm-queue/blob/main/CHANGELOG.md)).
53
+
54
+ An LLM request client with its own durable queue, built on [taskiq](https://github.com/taskiq-python/taskiq) and Amazon SQS.
55
+
56
+ Your code keeps calling LangChain models (`ainvoke`, `aembed_query`) or a small client API. Each call becomes a task on an SQS queue. Workers in your own app's processes run it under a limit learned from the provider's throttling, retry and fall back when needed, and return the result through Redis/Valkey.
57
+
58
+ ```
59
+ your code ──> client / LangChain adapter ──> SQS (per model: -rt queue first, base, bulk)
60
+ │
61
+ result <── Redis/Valkey <── worker in your pods ──> Bedrock / OpenAI-compatible
62
+ (per-pod dispatcher) (adaptive limiter, retry, fallback)
63
+ ```
64
+
65
+ ## Why
66
+
67
+ - **Spikes wait in a queue instead of failing.** Throttled calls aren't lost or retried in a storm.
68
+ - **No quota numbers to maintain.** The limit for each model and target is learned from throttle errors and shared by all pods.
69
+ - **Chat first.** Separate `-rt` queues and reserved capacity keep live traffic ahead of background and bulk work.
70
+ - **No extra servers.** The same app pods send and execute; SQS is only the broker.
71
+ - **Drop-in.** LangChain/LangGraph code doesn't change: tool calling, callbacks (Langfuse, LangSmith) and response objects are identical.
72
+ - **Safe rollout.** `direct` mode uses the same limiter, and a live canary moves traffic per model and lane without restarts.
73
+
74
+ ## Install
75
+
76
+ ```bash
77
+ uv add "taskiq-llm-queue[langchain-aws]" # Bedrock + LangChain
78
+ uv add "taskiq-llm-queue[openai,langchain-openai]" # OpenAI-compatible + LangChain
79
+ uv add "taskiq-llm-queue[speedups]" # hiredis parser for redis-py
80
+ ```
81
+
82
+ | | Packages |
83
+ |---|---|
84
+ | Core | `taskiq`, `aiobotocore`, `redis>=5.0.1`, `pydantic>=2.10`, `orjson`, `structlog` |
85
+ | `openai` | `openai`, `httpx` |
86
+ | `langchain-aws` / `langchain-openai` | LangChain **0.3 and 1.x** are both supported |
87
+ | `metrics` | `prometheus-client` |
88
+ | `speedups` | `hiredis` |
89
+
90
+ The package declares version **ranges**; pin exact versions in your app's lockfile. `aiobotocore` pins a narrow `botocore` window, so let your resolver pick an `aiobotocore` that matches your `boto3`.
91
+
92
+ ## Quickstart
93
+
94
+ ```python
95
+ from taskiq_llm_queue import LLMQueue, ModelConfig, QueueConfig, Target, request_context
96
+ from taskiq_llm_queue.langchain import QueuedChatBedrockConverse
97
+
98
+ llmq = LLMQueue(QueueConfig(
99
+ key_prefix="llmq:local",
100
+ redis_host="localhost", redis_port=6379, redis_password="localdev", redis_ssl=False,
101
+ sqs_endpoint_url="http://localhost:9324", # ElasticMQ locally; omit on AWS
102
+ models={
103
+ "gpt120b": ModelConfig(
104
+ provider="bedrock", model_id="openai.gpt-oss-120b-1:0",
105
+ targets={"primary": Target(region="ap-south-1"),
106
+ "apac": Target(profile_id="apac.openai.gpt-oss-120b-1:0")},
107
+ queues={"rt": "llm-gpt120b-rt", "default": "llm-gpt120b"}),
108
+ },
109
+ )) # or QueueConfig.from_env() (LLMQ_* variables)
110
+
111
+ async def main():
112
+ await llmq.start(mode="both") # "send" | "work" | "both"
113
+ model = QueuedChatBedrockConverse(llmq=llmq, model_alias="gpt120b")
114
+ with request_context(lane="rt", session_id="s-1", user_id="u-1"):
115
+ reply = await model.ainvoke("Summarise today's market in one line.")
116
+ print(reply.content, reply.response_metadata["served_model"])
117
+ await llmq.stop() # drains in-flight work
118
+ ```
119
+
120
+ In a FastAPI app, call `start()` in the lifespan startup and `stop()` on shutdown. Send-only processes, such as CLIs and scripts, use `mode="send"`.
121
+
122
+ ## Client API
123
+
124
+ Every call returns a result object with `.meta`: `served_model`, `served_target`, `fallback_used`, `attempts`, `queue_wait_ms`, `provider_ms`, `worker`.
125
+
126
+ ```python
127
+ res = await llmq.converse("gpt120b", {"messages": [...], "system": [...], "toolConfig": ...},
128
+ lane="rt", fallback="chain", timeout_secs=90, request_id="req-1")
129
+ res.response # exact Bedrock Converse dict
130
+
131
+ emb = await llmq.embed("titan", ["a", "b"], lane="bulk")
132
+ emb.vectors # list[list[float]]; `emb` itself also works as a list
133
+
134
+ chat = await llmq.chat("llama", {"messages": [{"role": "user", "content": "hi"}]})
135
+ chat.response # OpenAI-compatible chat completion dict
136
+
137
+ raw = await llmq.invoke_model("titan", {"inputText": "a"}, content_type="application/json")
138
+ raw.json(), raw.body, raw.content_type
139
+
140
+ await llmq.cancel("req-1") # not run if not started; the caller gets LLMQueueCancelled
141
+ ```
142
+
143
+ - **Timing:** `timeout_secs` (relative) or `deadline_at` (absolute epoch seconds) on every call.
144
+ - **Model ids** are aliases, optionally with a target: `gpt120b`, `gpt120b@apac`. Unknown aliases are rejected when the config is built.
145
+ - **Errors:** everything derives from `LLMQueueError`: `LLMQueueTimeout`, `LLMQueueCancelled`, `ProviderError(retryable, retry_after_secs)`, `QueueUnavailable`, `ConfigError`.
146
+ - **Sync callers:** `converse_sync`, `chat_sync`, `embed_sync`, `invoke_model_sync` run `direct` under the shared limiter (counted as sync fallbacks). Set `strict_async=True` to raise instead. `close_sync()` (or `stop()`) closes the sync runner.
147
+ - **Jobs:** `register_job(name, fn)` on workers, `send_job(name, payload, timeout_secs=...)` anywhere; jobs run on the job queues with the same claims and DLQ rules.
148
+
149
+ ## LangChain and LangGraph
150
+
151
+ | Class | Based on | How |
152
+ |---|---|---|
153
+ | `QueuedChatBedrockConverse` | `langchain_aws.ChatBedrockConverse` | Builds the request and parses the reply with the installed langchain-aws's own code, so `bind_tools`, structured output, cache usage and service tier match exactly |
154
+ | `QueuedBedrockEmbeddings` | `langchain_aws.BedrockEmbeddings` | `aembed_query` / `aembed_documents` go through the queue |
155
+ | `QueuedChatOpenAI` | `langchain_openai.ChatOpenAI` | `http_async_client` with a queued httpx transport: chat completions and embeddings go through the queue; queue meta lands in `response_metadata`. The Responses API (`/responses`) is not supported yet |
156
+ | `QueuedOpenAIEmbeddings` | `langchain_openai.OpenAIEmbeddings` | Same transport; `check_embedding_ctx_length=False` by default for non-OpenAI servers |
157
+
158
+ - **Model names:** pass the queue alias as `model_alias=` (plus `model_id=` or `llmq=`). Passing an alias as `model=` raises a clear error.
159
+ - **Streaming:** all queued classes force `disable_streaming=True` / `streaming=False`. `astream()` and LangGraph `stream_mode="messages"` return one complete message, so nothing bypasses the queue.
160
+ - **Laziness:** queued classes find the running `LLMQueue` on first use and check routing and the kill switch on every call, so you can create them at import time.
161
+
162
+ ## Configuration
163
+
164
+ `QueueConfig.from_env()` reads `LLMQ_*` variables (durations end in `_SECS`; older `_S` names still work with a one-time warning). Notable settings:
165
+
166
+ | Setting | Default | Note |
167
+ |---|---|---|
168
+ | `aws_region` | `None` | Left to the AWS credential chain / IRSA |
169
+ | `redis_url` or `redis_host`/`port`/`username`/`password`/`ssl` | localhost | `redis_password` is a `SecretStr` |
170
+ | `redis_socket_timeout_secs` / `connect_timeout` / `health_check_interval` | 5 / 2 / 30 | Connection errors retried 3× with backoff |
171
+ | `id_hash_salt` (`LLMQ_ID_HASH_SALT`) | library constant | **Set your own in production**; keys the BLAKE2b hash of session/user ids |
172
+ | `timeout_secs` per lane | rt 90, bg 300, bulk 900 | |
173
+ | `attempt_timeout_secs` | 180 | One provider attempt; a hung target moves to the next fallback step |
174
+ | `provider_read_timeout_secs` / `provider_connect_timeout_secs` | 300 / 10 | Bedrock botocore and OpenAI httpx timeouts |
175
+ | `sqs_send_timeout_secs` | 5 | SQS counts as down only when no send completed for this long; then the call runs direct |
176
+ | `throttle_retry_attempts` | 6 | Tries per fallback step for throttles (backoff holds no slot) |
177
+ | `valkey_wait_failover_secs` | 10 | Result reads failing this long make waiting callers run direct |
178
+ | `redis_max_connections` | 32 | Raised automatically to worker slots + 16 |
179
+
180
+ Redis keys are cluster-safe (hash-tagged per request and per model+target), so cluster-mode Valkey (e.g. ElastiCache Serverless) works.
181
+
182
+ ## Providers
183
+
184
+ ```python
185
+ ModelConfig(provider="openai", model_id="llama-3.1-70b",
186
+ targets={"primary": Target(base_url="http://vllm:8000/v1")}, api_key_env="LOCAL_LLM_KEY")
187
+ QueueConfig(allowed_providers={"bedrock"}, denied_hosts=["*.openai.azure.com"]) # enforced on sender and worker
188
+ QueueConfig(providers={"myprov": "mypkg.providers:MyProvider"}) # custom provider, loaded by every pod
189
+ ```
190
+
191
+ A custom provider implements `async call(op, params, target) -> dict` and `classify(exc) -> ErrorInfo(kind="throttle" | "retryable" | "fatal", retry_after_secs)`. It can also be registered through the `taskiq_llm_queue.providers` entry point. SDK-level retries are off inside workers (botocore `total_max_attempts=1`, openai `max_retries=0`), so throttles reach the limiter.
192
+
193
+ ## Lanes and queues
194
+
195
+ | Lane | Queue | Use |
196
+ |---|---|---|
197
+ | `rt` | `llm-<model>-rt` | Live user requests; polled first; may use the whole limit |
198
+ | `bg` | `llm-<model>` | Background work; at most 80 % of the limit |
199
+ | `bulk` | `llm-<model>-bulk` if configured (e.g. embeddings), else base | Ingestion/batch; lowest priority |
200
+
201
+ A queued `bg`/`bulk` call waits at most 15 s for a slot, then goes back to the queue with a delay instead of holding a worker.
202
+
203
+ ## Results
204
+
205
+ Results are stored as JSON (never pickle) for 5 minutes and read once (`GETDEL`). Each pod runs one result dispatcher: a pub/sub wake reads only the announced results, and a periodic batched sweep catches the rest, so 10K waiting calls don't mean 10K polling loops.
206
+
207
+ ## Fallbacks: how to check and update
208
+
209
+ A fallback chain is tried **inside the worker**. Each step is retried with backoff before moving on:
210
+
211
+ 1. same model, primary target
212
+ 2. same model, another target (e.g. a cross-region profile `gpt120b@apac`)
213
+ 3. another model (only when the call's policy is `chain`)
214
+
215
+ Embeddings never switch model. Throttles shrink the limiter but never trip the circuit breaker; only 5xx and timeouts do.
216
+
217
+ ```bash
218
+ uv run llm-queue fallback get # whole config (JSON); starts from your app's defaults
219
+ uv run llm-queue fallback get --model gpt120b # resolved chain for one model
220
+ uv run llm-queue fallback test --model gpt120b # dry run: which target would serve now
221
+ uv run llm-queue fallback set --model gpt120b --chain "gpt20b"
222
+ uv run llm-queue fallback set --file fallback.json # merges per model; --replace to overwrite all
223
+ uv run llm-queue fallback reset # back to the startup default
224
+ ```
225
+
226
+ Chains are validated and reloaded by every pod within 30 s with no restart. An invalid config is rejected and the last good one stays. Every change is written to an audit stream.
227
+
228
+ ```json
229
+ { "chains": {
230
+ "gpt120b": ["gpt20b"],
231
+ "gpt20b": [{"model": "gpt120b", "require_headroom": true}]
232
+ },
233
+ "skip_targets": { "gpt120b": ["apac"] } }
234
+ ```
235
+
236
+ **To see fallbacks happen:** `res.meta` / `response_metadata` show `served_model`, `served_target`, `fallback_used` and `attempts`, and the `llmq_fallback_total{model,step}` metric counts them. `samples/05_fallback_on_throttle.py` shows it end to end.
237
+
238
+ ## Operating it
239
+
240
+ ```bash
241
+ uv run llm-queue routing set --model gpt20b --lane rt --pct 10 # canary: 10 % of sessions via SQS
242
+ uv run llm-queue routing kill on # everything direct within 30 s
243
+ uv run llm-queue routing reset
244
+ uv run llm-queue limiter show --model gpt120b # learned limit, in-flight, pause
245
+ uv run llm-queue limiter reset --model gpt120b [--target apac] # forget the learned limit
246
+ uv run llm-queue breaker show | reset
247
+ uv run llm-queue queues # messages per queue and DLQ
248
+ uv run llm-queue job send ingest --payload-file payload.json --queue jobs-ingest
249
+ ```
250
+
251
+ ## Failure behaviour
252
+
253
+ | Failure | What happens |
254
+ |---|---|
255
+ | Provider throttles | Limiter shrinks and pauses that model + target; brokers stop receiving it unless another target in the chain has headroom; work waits in SQS |
256
+ | Provider down (5xx, timeouts) | Circuit breaker opens → next fallback step |
257
+ | Worker killed mid-task | Message reappears after the visibility timeout; the claim prevents a second provider call |
258
+ | Rolling deploy | Pods stop receiving at once, release unstarted work, finish running calls, then exit (90 s budget in the reference deployment) |
259
+ | **SQS unreachable** | That model switches to `direct` (+ shared limiter) for 300 s on transient send errors only. An ambiguous send is claimed first, so it can't run twice |
260
+ | **Redis/Valkey unreachable** | Workers release messages instead of losing them; senders run `direct` with a local per-pod cap (last known limit ÷ live pods) |
261
+
262
+ ## Logging
263
+
264
+ The library logs with structlog's key-value API on top of stdlib `logging` and **never configures logging itself**. Records are named `taskiq_llm_queue.*`; the message reads `event key=value ...` and the fields are also on `record.llmq` (a dict). Prompts, responses and params are never logged.
265
+
266
+ ```python
267
+ import logging
268
+ logging.getLogger("taskiq_llm_queue").setLevel(logging.INFO) # stdlib apps: that's all
269
+
270
+ # loguru apps: the usual InterceptHandler picks these up like any stdlib logger.
271
+ # structlog apps: use structlog.stdlib.ProcessorFormatter on your handler to render them.
272
+ ```
273
+
274
+ The `llm-queue` CLI logs warnings and errors to stderr, so its stdout is always plain JSON.
275
+
276
+ ## Security and data
277
+
278
+ - Encryption is server-side on the queues (SSE-SQS or SSE-KMS); the library needs no setting for either.
279
+ - Prompts and responses are never logged. `user_id` / `session_id` are hashed (keyed BLAKE2b) in task labels; raw ids stay inside the message body.
280
+ - Nothing secret goes into messages; credentials come from the AWS chain (IRSA / Pod Identity) or env.
281
+ - Unknown task names or newer message versions are released for another pod, never silently dropped.
282
+
283
+ ## Run it locally (no AWS needed)
284
+
285
+ ```bash
286
+ make up # Valkey (password) + ElasticMQ + fakellm
287
+ make test # unit + integration against the stack
288
+ make down
289
+ # Windows without make: uv run python tools/llmq_dev.py up | test | down
290
+ ```
291
+
292
+ Runnable examples are in [`samples/`](https://github.com/techiewonk/taskiq-llm-queue/tree/main/samples): quickstart, chat, embeddings, lanes, fallback on throttle, send-only and worker pods, LangChain, LangGraph, jobs, cancel and timeouts, sync calls, CLI, and `13_research_assistant_flow.py`, which replays a real app's flows locally or against real Bedrock (`--target aws`, settings from `.env` or AWS Secrets Manager). The stack, capability checks and test layers are described in [docs/TESTING.md](https://github.com/techiewonk/taskiq-llm-queue/blob/main/docs/TESTING.md); delivery-safety rules in [docs/DESIGN.md](https://github.com/techiewonk/taskiq-llm-queue/blob/main/docs/DESIGN.md).
293
+
294
+ ## Credits
295
+
296
+ Broker design and feature set build on [taskiq-aio-sqs](https://github.com/vonsteer/taskiq-aio-sqs) (MIT, © 2022-2024 Jesse Constante) and [taskiq-sqs](https://github.com/taskiq-python/taskiq-sqs) (MIT, © 2026 Mike Shultz and the Taskiq team). Licence: MIT.
@@ -0,0 +1,65 @@
1
+ taskiq_llm_queue/__init__.py,sha256=YbXgYe6Rnel0PYCxxMvTB18lNmT1IjRhzGlYI3ZYz-8,5061
2
+ taskiq_llm_queue/_logging.py,sha256=10VK1aDkD7rjSCC7F1ti7iKuC5zdbX8qMXbhdGo8hdY,1704
3
+ taskiq_llm_queue/_version.py,sha256=wJ7_Rt22BJ255pg0_dqlU1SMURFvmz5YSNLNi9dSlwg,150
4
+ taskiq_llm_queue/cli.py,sha256=vJlTmIsD1wh8pdi3ovsa4GSA_EM0NxwwS8ASB-g3HIA,20366
5
+ taskiq_llm_queue/client.py,sha256=hSlKCKDb1xDXKM2k-OeUrixIimjBb-7HBVVvdqZDQ4E,56771
6
+ taskiq_llm_queue/config.py,sha256=qdd76IoGxDYor7lrSKqSW2dNos5ZmZd794OoKg6w5KY,25638
7
+ taskiq_llm_queue/context.py,sha256=09AJL3I3vJLK7Ke2B7GeqtN8ElZk7DuN_40nG7jNhN4,1977
8
+ taskiq_llm_queue/core.py,sha256=YdNKGtIZLhOlJdvazojwvWHTsbCIoW8jaMjl2mRSsks,35468
9
+ taskiq_llm_queue/envelope.py,sha256=ZNLbssnB2dQWwYYPk5hMFkBxOPnTIMKUtcqTtesQMNA,4956
10
+ taskiq_llm_queue/errors.py,sha256=z7mkXXW9E-oSmnJx38aULncZ-A0SJEqOBEibuJWVkz0,2339
11
+ taskiq_llm_queue/failover.py,sha256=DNC32e0G2LSxeFY2UFzFT84AA6NryIZtvPXasIdqwvU,12089
12
+ taskiq_llm_queue/http_transport.py,sha256=a9so7lIofhqRsAVDL1iWOTckZa2WI0YDcMk1BQvEolY,9609
13
+ taskiq_llm_queue/keys.py,sha256=Me6C-feM7vOXyNyKUcudSR1OfJ85htY446ngNFI00Fg,3200
14
+ taskiq_llm_queue/lifecycle.py,sha256=NTgsMuVjPCLpdyOXW7FpiAUiNKLuMxzQyRJFFVG_sNc,20279
15
+ taskiq_llm_queue/metrics_names.py,sha256=IOfG6NsAGlkUR-_dIxFCyez-0GwSMnnT2s89UEfuyZQ,771
16
+ taskiq_llm_queue/protocols.py,sha256=mWzPP0mcbYnCvMk2TiaUxk-yDznnbsIUJObjYlGclGs,4408
17
+ taskiq_llm_queue/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ taskiq_llm_queue/routing.py,sha256=xcLM2Nmsz7gYIVqKMtwP-McKuO4Gv5NlJ24yGv9Y9Xo,7061
19
+ taskiq_llm_queue/routing_hooks.py,sha256=s7WI-KhO7LrvEU3G7EHmfN455pw5C2vicRJ2ctxsIQQ,3357
20
+ taskiq_llm_queue/tasks.py,sha256=B3Hd5OrXaAuPYCVAnCIS7j7DLjz5BwqHbbOKSkCeMWo,19505
21
+ taskiq_llm_queue/broker/__init__.py,sha256=Qsiy6A6yTxLeCM_V5Kln161PCXWE-IO3jNrNtR9eOHY,2330
22
+ taskiq_llm_queue/broker/acker.py,sha256=3N4gucLrgpbTONQH-CzqTINm_xW0TFR4WG2tQn_9opA,9288
23
+ taskiq_llm_queue/broker/batching.py,sha256=Nw0FhHxJ8yDgn0HR_CeJfmdvrBvT3oNKUpDnnbRtRQQ,6977
24
+ taskiq_llm_queue/broker/broker.py,sha256=oFEKVZz6T9roMGpzO0V9Z4mvak6EKmyHygVeyoRpvKs,41634
25
+ taskiq_llm_queue/broker/delivery.py,sha256=p6p7xo55f86hBaMoT2u2DfyT92vK_HWoohaBlFvO1-g,1767
26
+ taskiq_llm_queue/broker/errors.py,sha256=cwCSeBn9dZUPSBqUDGLdprKxOtZ9Q4IpWTjq4XJYvH8,6134
27
+ taskiq_llm_queue/broker/events.py,sha256=GGCsXZlA7FGpF2NWDt3OKPWHCAS7LddJCn9Bxym32YI,2113
28
+ taskiq_llm_queue/broker/filters.py,sha256=5WCl66ykcDXtFaRiBwUul3x7bRQ2Pp0MYpxd7c6bpfQ,1195
29
+ taskiq_llm_queue/broker/heartbeat.py,sha256=YoqUfr9esLvk73Omtv2w4RMhphWJccmeG8DlInPPVQY,6387
30
+ taskiq_llm_queue/broker/queue.py,sha256=rb4FsckJz_o5xRss44kFSgK9pQJ70rJh-LigAjhYYz0,5953
31
+ taskiq_llm_queue/broker/s3_offload.py,sha256=jOeIEELqTZSznd8c1ekNF40P12jnpChu36v54jTLho8,14584
32
+ taskiq_llm_queue/broker/scheduler.py,sha256=zf5u13ZzsnlOsU535znZ-_WolhVUYVvWyXV5lNN_be8,4751
33
+ taskiq_llm_queue/broker/serializer.py,sha256=YuPMNqv8X8EFi7EJtriqNx6BxwnYyD-6XmYfJS__uys,1363
34
+ taskiq_llm_queue/langchain/__init__.py,sha256=pleTIl0TOmC9F7wuMU61oPWI4EiYk7mIi_hsvA_QugA,2159
35
+ taskiq_llm_queue/langchain/_client.py,sha256=h6fdA73Tgb51OZkMzQaQxh0vii-icNM7saxrIcA9r7E,4352
36
+ taskiq_llm_queue/langchain/bedrock.py,sha256=UQmpb2bq-yFJ5aI6X9Q3ld-_IqjonxumUwiI1H8Ee6A,11616
37
+ taskiq_llm_queue/langchain/openai.py,sha256=cFwq-EEJMR62xaBrHynKSNH1_WAJxf6dCxIoZVRlhwI,4158
38
+ taskiq_llm_queue/metrics/__init__.py,sha256=XsUAn_NFMgrB8_qXnAFOuweF10qM0biyMy0ZyMk-xB4,1704
39
+ taskiq_llm_queue/metrics/emf.py,sha256=PvR2htszW2Ft8iRbXQeINqloXqj2yAW8Sua3WPASc4c,3919
40
+ taskiq_llm_queue/metrics/prometheus.py,sha256=COSeCv3yDAZCY72GOKCINcOzIK3t81R9Rcnsu7GCV10,2946
41
+ taskiq_llm_queue/providers/__init__.py,sha256=C45nWW0R6bTTioW7G_L3iaaEk-dRptGkBORpX6VotUE,1662
42
+ taskiq_llm_queue/providers/bedrock.py,sha256=X0ex2odFg3qouy4Q82VqtUp7-Oyj9pqGEa6Cw-JY1qs,8128
43
+ taskiq_llm_queue/providers/classify.py,sha256=JJPeAhMDxzTWxxZtz-IEU_Qz8_QhuiTot1w5cDej58U,9667
44
+ taskiq_llm_queue/providers/embeddings.py,sha256=7i6klmXAaau_iyhFdq__ROIhw2jVEybuI4Bbvd-JId8,3371
45
+ taskiq_llm_queue/providers/hosts.py,sha256=aywHB5S7-zI4Cc730_y9FngkvjGwQfRnrKa2rqf8l54,1290
46
+ taskiq_llm_queue/providers/openai_compat.py,sha256=xuz4XkZZhtRpmshJ5Tda_wVnBnHpK_X9OI9B44jxvT0,6191
47
+ taskiq_llm_queue/providers/registry.py,sha256=GsbwqPHB6ymMZYyIRkqax6hhR6KVOp-OFFra0G5IV50,7340
48
+ taskiq_llm_queue/reliability/__init__.py,sha256=FIUW0u-u_XnupyDxQd83VrUZvXNWd-b4OCj2q5QyizI,1502
49
+ taskiq_llm_queue/reliability/_lua.py,sha256=IpqqM-4dn-XGf6_riC2bDcDDuLiFnvCP9ZiPTTRqhRw,18622
50
+ taskiq_llm_queue/reliability/breaker.py,sha256=nbbEICcJmyxmjpkjLsoPKxHqcdiVxN43R-lrQon-FR0,3287
51
+ taskiq_llm_queue/reliability/claims.py,sha256=VbVzRogoMMtxlHtnkmY-eaT51I4_ehM_NCpGYFW0RuI,9838
52
+ taskiq_llm_queue/reliability/config_store.py,sha256=p9ceiNSMTDzqfbSNA-F7NehTq_nopWp56atSN1Z_pPY,5997
53
+ taskiq_llm_queue/reliability/fallback.py,sha256=HaSK5minJBmbvqkT_0tMsTseAypvFNigYdznlt2-03Y,4022
54
+ taskiq_llm_queue/reliability/limiter.py,sha256=bDwnD901gKh_FR604FIUgmkCq80EwygZ9hxSSpLB2Bo,19866
55
+ taskiq_llm_queue/reliability/local_limiter.py,sha256=DC4JDeq-0RVFhlS3igUQpr6fxF_ml8P48jZ0y0OkS7E,5937
56
+ taskiq_llm_queue/results/__init__.py,sha256=u-N4eMjqYHxH20kUIW2agLHrbXu-LZPElKCaQcO-XBY,719
57
+ taskiq_llm_queue/results/backend.py,sha256=mE5i3XXfuZyrJwdejl13UHAjuIwCl1lhMBqgzMDYb6g,9899
58
+ taskiq_llm_queue/results/dispatcher.py,sha256=GZeHiqlLwxuX6ISLhyS7Fc_8X9BtG7TZZPEkn4xmrZs,16444
59
+ taskiq_llm_queue/results/s3_backend.py,sha256=3snDgJhRMjlTPhPNWHVwsQqh4JvdXFFwDxbNCWq0oOA,4104
60
+ taskiq_llm_queue-0.1.0a1.dist-info/METADATA,sha256=OvWaIwuQeqUOaGyJZ0ZUnqiHMu6K_TQARUwzOyxxNX0,18269
61
+ taskiq_llm_queue-0.1.0a1.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
62
+ taskiq_llm_queue-0.1.0a1.dist-info/entry_points.txt,sha256=u9QOO0rKyinfwfcBrnyNu5LdDproymBwFJSSedoSWBg,64
63
+ taskiq_llm_queue-0.1.0a1.dist-info/licenses/LICENSE,sha256=fyt0lY9XpMIg0h9QEgiY3twFAGBqEiyCBB_9C83IpIc,1085
64
+ taskiq_llm_queue-0.1.0a1.dist-info/licenses/NOTICE,sha256=QBk0_Gzqpez_MheapoFec71cpSSfrPDKQMoK4x1JgYQ,794
65
+ taskiq_llm_queue-0.1.0a1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ llm-queue = taskiq_llm_queue.cli:console_main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The taskiq-llm-queue authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,15 @@
1
+ taskiq-llm-queue
2
+ Copyright (c) 2026 The taskiq-llm-queue authors. Licensed under the MIT License.
3
+
4
+ The SQS broker's design and feature set build on two MIT-licensed projects. This package is
5
+ an independent implementation; any code adapted from them keeps its original notice below.
6
+
7
+ taskiq-aio-sqs — https://github.com/vonsteer/taskiq-aio-sqs
8
+ Copyright (c) 2022-2024 Jesse Constante. MIT License.
9
+ Ideas used: aiobotocore-based broker, visibility heartbeat, message metadata,
10
+ extended (S3) messages, batching, fair queues.
11
+
12
+ taskiq-sqs — https://github.com/taskiq-python/taskiq-sqs
13
+ Copyright (c) 2026 Mike Shultz and Taskiq team. MIT License.
14
+ Ideas used: multi-queue structure, queue declaration, message expiry,
15
+ batch flush on shutdown, S3 offload middleware and result backend.