Compare commits

..

85 Commits

Author SHA1 Message Date
rltakashige fd17a3bc09 vllm loads! 2026-03-27 12:02:36 +00:00
rltakashige 7ca9edaf42 wohohoh 2026-03-25 10:35:46 +00:00
Evan d1f13f21a1 time to fiddle 2026-03-24 11:54:10 +00:00
Evan a6cdc93d3c disable 2026-03-24 11:54:04 +00:00
Evan a9c7b1c68a urgk 2026-03-24 11:54:04 +00:00
Evan 6355f3d8fb man i gotta commit 2026-03-23 16:00:57 +00:00
Evan 8cd6191c70 gotta move the cache soon 2026-03-21 22:22:25 +00:00
Evan 532a8f0b07 we got it to BUILD 2026-03-20 15:38:16 +00:00
Evan 581a1fcd79 move chunks to core 2026-03-19 16:18:40 +00:00
Evan ac7194dd90 mupdate 2026-03-19 16:01:35 +00:00
Evan f538b44211 shmovin 2026-03-19 14:51:08 +00:00
Evan a3ce437fd4 first 2026-03-19 11:10:58 +00:00
Ryuichi Leo Takashige be731d3a85 Merge main 2026-03-17 19:05:55 +00:00
Ryuichi Leo Takashige 655185cfe7 Address comments 4 - defer to the warmup into the exo batch generator and vllm batch engine and don't store model on the generators. 2026-03-17 19:00:12 +00:00
Ryuichi Leo Takashige 1dd9c28842 Address comments 3, mainly refactors 2026-03-17 18:31:41 +00:00
Ryuichi Leo Takashige cacd26e63c Type error lol 2026-03-17 18:00:39 +00:00
Ryuichi Leo Takashige 6a3eb2f37d close() 2026-03-17 17:31:15 +00:00
Ryuichi Leo Takashige e1df77bc4c No more future annotations 2026-03-17 17:07:32 +00:00
ciaranbor a6519ba006 Update mflux to 0.16.9 (#1751)
Prevents malformed output from Qwen-Image
2026-03-17 16:58:23 +00:00
Ryuichi Leo Takashige e78e53df6e Address comments 2 including vllm capability in state 2026-03-17 16:55:08 +00:00
Ryuichi Leo Takashige c70d9006e8 Address comments including task id interface 2026-03-17 15:31:55 +00:00
Ryuichi Leo Takashige 72cd8552ae Merge branch 'main' into leo/dgx-spark-integrations 2026-03-17 13:58:49 +00:00
rltakashige b713889f73 Fix exo bench again again (#1750)
Mb premature auto merge
2026-03-17 13:47:24 +00:00
rltakashige 6ee673147d Fix exo bench prefill and decode tps (#1749) 2026-03-17 13:36:44 +00:00
ciaranbor ff4d20eed9 Fix image models through dashboard (#1746)
## Motivation

Image generation/editing from the dashboard was broken. ChatForm
bypassed the parent's model-launch logic, so image requests fell through
to text chat.

## Changes

- Moved image routing logic from ChatForm.svelte to +page.svelte
(routeMessage())
- ChatForm now always delegates to parent via onAutoSend (made required)
- Fixed missing updateActiveConversation() call on message retry

## Why It Works

All sends now go through the parent's launch-then-route path, so image
models get launched before the request is dispatched to the correct
endpoint.
2026-03-17 11:22:01 +00:00
Ryuichi Leo Takashige 8cd1308336 Tidy pass 1 2026-03-17 00:38:16 +00:00
Ryuichi Leo Takashige 04dcdbd127 Merge main 2026-03-16 23:01:48 +00:00
Ryuichi Leo Takashige ec5d62f935 Strip vllm generator 2026-03-16 22:11:35 +00:00
Ryuichi Leo Takashige e96f084051 Distributed callbacks 2026-03-16 21:17:14 +00:00
Ryuichi Leo Takashige dc68ddbac0 Prompt formatting 2026-03-16 20:37:38 +00:00
Ryuichi Leo Takashige 073f8c1690 add batching 2026-03-16 19:25:45 +00:00
Ryuichi Leo Takashige 3c29d0dd4c test prefix caching 2026-03-16 16:43:51 +00:00
rltakashige 594ed99734 new uv lock for fastsafetensors 2026-03-13 17:08:49 +00:00
Evan Quiney 7ed4639540 use structured concurrency in download coordinator (#1722)
identical to #1721 but a little safer imo. 


## manual testing
ran a few downloads, cancelled non-master, cancelled master -- no errors
reported.
2026-03-13 14:55:37 +00:00
Mazin Sharaf 29d4165fe2 Add step logo condition to FamilyLogos component (#1676)
## Motivation

<!-- Why is this change needed? What problem does it solve? -->
<!-- If it fixes an open issue, please link to the issue here -->
This was to fix a small issue which was that the StepFun logo was not
included in the sidebar, and I also noticed it from an open issue:
- #1662 

## Changes

<!-- Describe what you changed in detail -->
I added a condition to the FamilyLogos where if the family is "step"
then the logo will be included in the sidebar.

## Test Plan
Open exo, go choose a model, and scroll down the sidebar until you see
the step logo, which should be there.

### Manual Testing
<!-- Hardware: (e.g., MacBook Pro M1 Max 32GB, Mac Mini M2 16GB,
connected via Thunderbolt 4) -->
<!-- What you did: -->
<!-- - -->
Check for the step logo in the sidebar.
2026-03-13 13:31:46 +00:00
Ryuichi Leo Takashige e9e23e556e Have loading progress 2026-03-13 12:58:34 +00:00
ecohash-co 12af7c9586 fix: shield macmon cleanup from cancellation to prevent orphaned process (#1714)
## Summary

Fixes a bug where macmon metrics (GPU usage, temperature, power, RAM)
freeze permanently in the dashboard while non-macmon metrics (disk
usage) continue updating normally.

**Root cause: asyncio subprocess pipe transport flow control stall**

Observed on a 2-node Mac Studio M3 Ultra cluster (Python 3.13.12, anyio
4.11.0, macOS with kqueue) running EXO.app for ~36 hours. Diagnostics
confirmed:

1. **Only macmon monitoring is stuck** — disk metrics from the same
InfoGatherer continue updating, proving the Worker, EventRouter, and API
pipelines are healthy
2. **macmon IS producing data** — its stdout pipe is full at exactly
65536 bytes (64KB OS buffer), and macmon is blocked on write at 0% CPU
3. **The pipe read-end FD is still open** — the exo process holds it,
but asyncio isn't reading from it
4. **The stale GPU value (0.21) is wrong** — should be ~1.0 (matching
the other node under identical load)

This is consistent with asyncio's subprocess pipe transport getting
stuck in flow control: `pause_reading()` is called when the internal
buffer exceeds the high-water mark, but `resume_reading()` is never
called, permanently deregistering the FD from kqueue. The `receive()`
coroutine waits forever for data asyncio will never deliver.

```
$ lsof -p <macmon_pid>
macmon  74691  FD=1  PIPE  SIZE=65536  ->0x5ae78ecf376ccd0e  # full pipe

$ lsof -p <exo_pid> | grep <pipe_id>
exo     74689  FD=47  PIPE  SIZE=65536  ->0xd129da474c1340f7  # read end held open, not consumed
```

Note: anyio 4.11's `Process.aclose()` already uses
`CancelScope(shield=True)` for cleanup during cancellation — this is NOT
an election cleanup issue (confirmed by @ciaranbor's testing).

**Fix:** Replace the `async for` iteration with an explicit `receive()`
inside `fail_after()`. If macmon produces no output for 10× its
configured interval (minimum 30s), `TimeoutError` fires, `open_process`
cleanup kills macmon and tears down the stale transport, and the loop
restarts with a fresh subprocess and fresh asyncio transport.

## Test plan

- [x] All 246 existing tests pass
- [ ] Verify macmon restarts after simulated pipe stall (e.g., `SIGSTOP`
macmon, wait for timeout, confirm restart and metrics resume)
- [ ] Long-running soak test on multi-node cluster to confirm the fix
prevents recurrence
2026-03-13 12:54:30 +00:00
ciaranbor f28b2fd037 Extract mlx revision from uv lock (#1715)
## Motivation

The MLX version and git revision in nix/mlx.nix were hardcoded and had
to be manually kept in sync with uv.lock

## Changes

- flake.nix: Extract MLX git rev from uv.lock's source.git URL and pass
as uvLockMlxRev
- nix/mlx.nix: Use uvLockMlxVersion and uvLockMlxRev instead of
hardcoded values; remove version mismatch assertion

## Why It Works

uv.lock is already the source of truth — now Nix reads both version and
rev from it directly. The pinned fetchFromGitHub hash still guards
against unexpected changes.
2026-03-13 12:34:54 +00:00
Ryuichi Leo Takashige 169ea2a5e8 Fix GPT OSS by not retokenizing prompts 2026-03-12 18:02:43 +00:00
Ryuichi Leo Takashige 4a7901c548 Fix patches 2026-03-12 17:42:02 +00:00
Ryuichi Leo Takashige 7bb5cb4fc7 Allow memory profiling to be unstable 2026-03-12 17:34:53 +00:00
Ryuichi Leo Takashige 493e342f83 Skip impossible shardings 2026-03-12 17:33:12 +00:00
Ryuichi Leo Takashige 283b1809c9 Move VLLM runner into VLLM engine 2026-03-12 17:20:30 +00:00
Ryuichi Leo Takashige 35030119e3 ExoBench and ExoEval for CUDA 2026-03-12 17:09:51 +00:00
rltakashige 585dfe3549 Merge branch 'main' into leo/dgx-spark-integrations 2026-03-12 15:41:29 +00:00
Ryuichi Leo Takashige 5d7a005a13 Destroy process group on Keyboard Interrupt 2026-03-12 15:33:32 +00:00
Ryuichi Leo Takashige 87b7c5ef8b Pass CI 2026-03-12 15:24:49 +00:00
Ryuichi Leo Takashige 957ebbd21f Set max token length as max context length if no max tokens set 2026-03-12 15:15:39 +00:00
Mustafa Alp Yılmaz ea18a62581 fix: guard against ZeroDivisionError in mlx_lm stats (#1707)
## Problem

Running short completions (like `max_tokens=1` health check probes) can
finish so fast that `mlx_lm`'s internal `generation_time` rounds to
zero. When that happens, `BatchGenerator.stats()` in
`mlx_lm/generate.py` divides `generation_tokens / generation_time` and
throws a `ZeroDivisionError`, which kills the runner process.

EXO already handles this on its side — lines 289-293 in
`batch_generate.py` guard the TPS calculation with `if
generation_elapsed > 0`. But the call to `self._exo_gen.stats()` on line
294 goes into mlx_lm's *separate* timing code, which doesn't have the
same guard. Two different timers, only one is protected.

In my case, this was triggered by health check probes (content: `"a"`,
`max_tokens=1`). The generation completed in sub-microsecond time,
`generation_time` was exactly `0`, and the runner crashed. Since the
health check command stays in the queue and retries after recovery, it
created an infinite crash loop — every ~15 seconds the runner would load
the model, get the same health check, and die again.

## Fix

Wrap `self._exo_gen.stats()` in a `try/except ZeroDivisionError`. If it
throws, set `mlx_stats` to `None` and fall back to `0.0` for
`prompt_tps`. The only fields EXO reads from `mlx_stats` are
`prompt_tps` and `prompt_time` — losing them on a sub-microsecond
generation has no practical impact.

## Traceback

```
File "batch_generate.py", line 294, in step
    mlx_stats = self._exo_gen.stats()
File "mlx_lm/generate.py", line 1224, in stats
    self._stats.generation_tokens / self._stats.generation_time
ZeroDivisionError: division by zero
```
2026-03-12 14:48:36 +00:00
rltakashige 4b6dd7588f lockgit status 2026-03-12 14:41:33 +00:00
Ryuichi Leo Takashige 3f4f7c9ba6 Only do for aarch64 linux 2026-03-12 13:42:23 +00:00
Ryuichi Leo Takashige 1331465ba0 Add missing runner features 2026-03-12 10:51:37 +00:00
Miguel Cruz 0782d90ec5 fix: show partial download progress on initial dashboard load (#1706)
## Summary

- Dashboard now shows partial download progress for models that were
partially downloaded in a previous session, instead of showing 0%
- Both `getModelDownloadStatus()` and `getInstanceDownloadStatus()` now
handle `DownloadPending` entries that carry non-zero
`downloaded`/`total` bytes

Fixes #1042

## Root cause

When exo restarts with partially downloaded models, the
`DownloadCoordinator` emits `DownloadPending` events (because
`downloaded_this_session` is 0, even though real bytes exist on disk).
The main page dashboard only checked for `DownloadOngoing` entries, so
these partially downloaded models showed as 0%.

The dedicated `/downloads` page already handled this correctly — it
renders `DownloadPending` entries with a progress bar when `downloaded >
0`. This fix brings the same behavior to the main page.

## Changes

For both `getModelDownloadStatus()` and `getInstanceDownloadStatus()` in
`+page.svelte`:
- Accept `DownloadPending` in addition to `DownloadOngoing`
- For `DownloadPending` entries with `downloaded > 0` or `total > 0`,
synthesize a `DownloadProgress` object from the top-level fields (with
`speed: 0` and `etaMs: 0` since no active download is in progress)
- Skip `DownloadPending` entries where both `downloaded` and `total` are
0 (truly pending, not yet started)

## Test plan

- [ ] Partially download a model, quit exo, relaunch — dashboard should
show partial progress instead of 0%
- [ ] Fully downloaded models still show as complete
- [ ] Active downloads still show real-time progress with speed/ETA
- [ ] Models never downloaded show as not started (not falsely showing
progress)
- [ ] Dashboard builds without errors (`cd dashboard && npm run build`)

---------

Co-authored-by: Evan <evanev7@gmail.com>
2026-03-12 10:39:34 +00:00
Ryuichi Leo Takashige 8f94727f14 Ignore missing modules if type stubs exist 2026-03-12 00:20:28 +00:00
Ryuichi Leo Takashige 9ee23ee0d3 Make vllm inference runner closer to the normal inference runner 2026-03-11 23:41:34 +00:00
Ryuichi Leo Takashige f75d36cbe0 Fix cache patch 2026-03-11 22:28:45 +00:00
Ryuichi Leo Takashige 2683ac7b61 Add Torch typings 2026-03-11 21:44:10 +00:00
Ryuichi Leo Takashige 404b9769ac Download models without model.safetensors.index 2026-03-11 21:32:07 +00:00
Ryuichi Leo Takashige 3e097f7243 only download a single copy of the model. 2026-03-11 20:57:29 +00:00
Ryuichi Leo Takashige 0c8615f25c Only import VLLM once.. 2026-03-11 20:49:19 +00:00
Ryuichi Leo Takashige ba35a4ba13 Move VLLM into the runner and add type stubs 2026-03-11 19:15:55 +00:00
Ryuichi Leo Takashige 9a83fa6cdf Patch VLLM to load multiple models dynamically 2026-03-11 18:17:59 +00:00
Ryuichi Leo Takashige 659c1bc737 Progress: Run EXO-CUDA through nix! 2026-03-11 18:17:59 +00:00
Ryuichi Leo Takashige 34df811b92 Vibe coding design baby 2026-03-11 18:17:59 +00:00
Ryuichi Leo Takashige ca5870a2e8 Some Linux Laptop/Desktop detection and goodbye penguin 2026-03-11 18:17:59 +00:00
Ryuichi Leo Takashige 6be6ea5fd2 Fix placement preview 2026-03-11 18:17:59 +00:00
Ryuichi Leo Takashige 5e9d27b753 Show Sparks and Linux in topology 2026-03-11 18:17:59 +00:00
Ryuichi Leo Takashige cfc8f09004 Fast direct USB connectivity 2026-03-11 18:17:59 +00:00
rltakashige f221a6c85c Normalise Responses API tool call format (#1704)
## Motivation

The responses API often does not provide tool args nested under a
"function" field. Since we follow the chat completions format of tools
in the backend (for MLX chat templates), we need to normalise to this
format.

## Test Plan

### Manual Testing
Works on n8n!
<img width="3442" height="2076" alt="image"
src="https://github.com/user-attachments/assets/9e11d679-0102-4d83-9a8e-b0a7a5898708"
/>
2026-03-11 18:10:12 +00:00
Mustafa Alp Yılmaz 2994b41089 fix: validate num_key_value_heads in tensor sharding placement (#1669)
## Problem

Models with fewer KV heads than nodes crash during tensor parallelism.
For example, Qwen3.5 MoE models have only 2 KV heads — trying to shard
across 4 nodes produces empty tensors and a reshape error at runtime.

The placement system already validates `hidden_size % num_nodes == 0`
but doesn't check KV heads, so it creates configurations that look valid
but blow up when the worker tries to split the attention heads.

Affected models include Qwen3.5-35B-A3B, Qwen3.5-122B-A10B,
Qwen3.5-397B-A17B, Qwen3-Next-80B-A3B, and Qwen3-Coder-Next (all have 2
KV heads).

## Changes

**Placement validation** (`src/exo/master/placement.py`):
- Combined KV heads divisibility check with the existing hidden_size
filter in a single pass
- Cycles where `num_key_value_heads % len(cycle) != 0` are now excluded
for tensor sharding
- Error message includes both constraints when no valid cycle is found

**Model card schema** (`src/exo/shared/models/model_cards.py`):
- Added optional `num_key_value_heads` field to `ModelCard` and
`ConfigData`
- Extracted from HuggingFace `config.json` (handles both top-level and
`text_config` nesting)
- Passed through in `fetch_from_hf()` for dynamically fetched cards

**All 68 inference model cards**
(`resources/inference_model_cards/*.toml`):
- Populated `num_key_value_heads` from each model's HuggingFace config

**Utility script** (`scripts/fetch_kv_heads.py`):
- Fetches `num_key_value_heads` from HuggingFace and updates TOML cards
- `--missing`: only fills in cards that don't have the field yet
- `--all`: re-fetches and overwrites everything
- Uses tomlkit for safe TOML editing and ThreadPoolExecutor for parallel
fetches

## Behavior

- Instance previews no longer show tensor options for models that can't
split their KV heads across the cluster size
- `place_instance()` rejects with a clear error instead of crash-looping
- Pipeline parallelism is unaffected
- 2-node tensor still works for 2-KV-head models (2 ÷ 2 = 1)
- Field is optional — existing custom cards without it continue to work
(validation is skipped when `None`)
2026-03-11 13:46:33 +00:00
Mustafa Alp Yılmaz 38f0c09175 fix: use StreamingDetokenizer in batch generator to fix emoji/UTF-8 corruption (#1691)
## Problem

Emojis and other multi-byte UTF-8 characters are rendered as `\ufffd`
(Unicode Replacement Character) in batch streaming responses.

Byte-level BPE tokenizers (like Qwen's) can split multi-byte UTF-8
characters (e.g. a 4-byte emoji) across multiple tokens. The batch
generator decodes each token independently with
`tokenizer.decode([token_id])`, which produces U+FFFD for partial byte
sequences.

The sequential generator doesn't have this problem — it uses
`stream_generate()` from mlx_lm, which internally uses
`StreamingDetokenizer` to buffer incomplete bytes.

## Fix

Use `StreamingDetokenizer` in the batch generator, matching the
sequential path:

- Each `_EngineTask` gets its own `StreamingDetokenizer` instance
- `add_token()` buffers tokens, holding back incomplete UTF-8 byte
sequences until they form valid characters
- `last_segment` returns only complete, valid text
- `finalize()` flushes any remaining buffered bytes when generation
completes

Empty text segments during buffering are harmless — they're already
handled correctly by the downstream streaming pipeline.

---------

Co-authored-by: Ryuichi Leo Takashige <leo@exolabs.net>
2026-03-10 16:10:22 +00:00
ciaranbor f36fd56c38 Include power usage in bench responses (#1692)
## Motivation

Add power/energy usage tracking to /bench API responses.

## Changes

- New PowerSampler class that periodically samples sys_power from each
node during inference
- New PowerUsage / NodePowerStats API types
- Integrated into both bench chat completion and bench image generation
endpoints

## Why It Works

Runs concurrently via anyio.create_task_group, reads from existing
state.node_system heartbeat data — no new plumbing needed. Energy =
avg_power × elapsed_time.

## Test Plan

### Manual Testing

Run a /bench request and verify power_usage appears in the response.

### Automated Testing

7 async tests covering single/multi-node, energy math, dynamic power
changes, empty state, and lifecycle.
2026-03-10 16:02:55 +00:00
rltakashige 82c54dd6d6 Add support for Nemotron sharding (#1693)
### Automated Testing
tested logits match
2026-03-10 15:51:07 +00:00
ciaranbor 3536161f15 Fix stale state.runners (#1684)
## Motivation

When a runner shuts down, there's no mechanism to remove it from
state.runners, causing stale state to accumulate.

## Changes

- apply.py: apply_runner_status_updated now handles RunnerShutdown
status by removing the runner from state instead of adding/updating it.
Removed the separate apply_runner_deleted function entirely.
- events.py: Removed the RunnerDeleted event type — runner cleanup is
now handled via RunnerStatusUpdated with a RunnerShutdown status.
- Tests: New test_apply_runner_deleted.py with 2 tests: verifying
RunnerShutdown removes the runner, and that a normal status update adds
it.

## Why It Works

Instead of a separate RunnerDeleted event and coordinating its emission
from master/placement, runner cleanup is handled naturally through the
existing RunnerStatusUpdated path — a RunnerShutdown status signals
removal. This is simpler and requires no changes to master or placement
logic.

## Test Plan

### Automated Testing

2 new unit tests covering RunnerShutdown removal and normal runner
status addition
2026-03-10 09:32:25 +00:00
ArvidSU a6aa07ed83 feat(dashboard): add mobile drawer support for sidebars (#1677)
## Summary

- Adds mobile-friendly drawer UI for the chat sidebar and model family
sidebar
- Introduces responsive header navigation with mobile-specific toggle
buttons
- Uses slide-in drawer overlay pattern on small screens instead of
collapsible sidebars
- Stores mobile drawer state in app store for consistent state
management

## Testing

Tested on desktop (responsive mode) and mobile viewport widths. Sidebars
now slide in as drawers on small screens with proper z-index layering
and close-on-outside-click behavior.
2026-03-10 04:18:11 +00:00
rltakashige 131ad0ff36 Implement continuous batching (#1642)
## Motivation

Following the changes made in #1632 !
Closes #1020

## Changes

<!-- Describe what you changed in detail -->

## Why It Works

<!-- Explain why your approach solves the problem -->

## Test Plan

### Manual Testing
<!-- Hardware: (e.g., MacBook Pro M1 Max 32GB, Mac Mini M2 16GB,
connected via Thunderbolt 4) -->
<!-- What you did: -->
<!-- - -->

### Automated Testing
<!-- Describe changes to automated tests, or how existing tests cover
this change -->
<!-- - -->

---------

Co-authored-by: Evan Quiney <evanev7@gmail.com>
2026-03-09 15:04:45 +00:00
ciaranbor d01636100a Ciaran/gpt oss tool call finish reason (#1673)
## Motivation

When gpt-oss truncates a response mid-tool-call (e.g. hitting max
tokens), the finish_reason was swallowed because the parser was in
tool-accumulation mode and never yielded it back to the caller. This
caused the API to hang or fail to signal completion.

## Changes

In parse_gpt_oss, when accumulating tool call arguments and a
finish_reason arrives, yield the response (with empty text) so the
finish reason propagates

## Why It Works

The parser now checks for finish_reason on every chunk while inside a
tool call. When the model stops generating (truncation), the finish
signal is forwarded immediately rather than being silently consumed.

## Test Plan

### Automated Testing

Added tests covering truncated tool calls (finish_reason="length") and
plain text truncation
2026-03-06 21:09:20 +00:00
ciaranbor 79c8dbeacd Ciaran/minor download bugs (#1674)
## Motivation

Two minor bugs in the download coordinator: cancelling a download didn't
reset its status to pending, and a single error in
_emit_existing_download_progress would kill the loop permanently.

## Changes

- Cancel emits pending status: When a download is cancelled, emit a
DownloadPending event so the UI/cluster state reflects that the model is
no longer actively downloading.
- Resilient progress emission loop: Move the try/except inside the while
loop so transient errors are logged but the periodic emission continues.

## Why It Works

- The cancel fix ensures the download status is consistent — without it,
a cancelled download would still appear as in-progress.
- The loop fix prevents a single exception from permanently stopping the
60-second periodic progress broadcast.
2026-03-06 17:46:18 +00:00
Evan Quiney 0096159728 up gossipsub limit (#1671)
bump gossipsub limit to 8MB + add warning
this is a bandaid solution for large messages while we figure out the
point to point protocol
2026-03-06 14:20:51 +00:00
Andrei Onel 7a36d3968d docs: Update documentation for v1.0.68 release (#1667)
## Motivation

Updated documentation for v1.0.68 release

## Changes

**docs/api.md:**
- Added documentation for new API endpoints: Claude Messages API
(`/v1/messages`), OpenAI Responses API (`/v1/responses`), and Ollama API
compatibility endpoints
- Documented custom model management endpoints (`POST /models/add`,
`DELETE /models/custom/{model_id}`)
- Added `enable_thinking` parameter documentation for thinking-capable
models (DeepSeek V3.1, Qwen3, GLM-4.7)
- Documented usage statistics in responses (prompt_tokens,
completion_tokens, total_tokens)
- Added streaming event format documentation for all API types
- Updated image generation section with FLUX.1-Kontext-dev support and
new dimensions (1024x1365, 1365x1024)
- Added request cancellation documentation
- Updated complete endpoint summary with all new endpoints
- Added security notes about trust_remote_code being opt-in

**README.md:**
- Updated Features section to highlight multiple API compatibility
options
- Added Environment Variables section documenting all configuration
options (EXO_MODELS_PATH, EXO_OFFLINE, EXO_ENABLE_IMAGE_MODELS,
EXO_LIBP2P_NAMESPACE, EXO_FAST_SYNCH, EXO_TRACING_ENABLED)
- Expanded "Using the API" section with examples for Claude Messages
API, OpenAI Responses API, and Ollama API
- Added custom model loading documentation with security notes
- Updated file locations to include log files and custom model cards
paths

**CONTRIBUTING.md:** 
- Added documentation for TOML model cards format and the API adapter
pattern

**docs/architecture.md:**
- Documented the adapter architecture introduced in PR #1167

Closes #1653

---------

Co-authored-by: askmanu[bot] <192355599+askmanu[bot]@users.noreply.github.com>
Co-authored-by: Evan Quiney <evanev7@gmail.com>
2026-03-06 11:32:46 +00:00
Mustafa Alp Yılmaz eee3432738 feat(mlx): add repetition_penalty and repetition_context_size to chat completions (#1665)
## Problem

Models running through exo can get stuck in repetition loops —
generating the same text over and over until hitting the token limit. It
happens more with quantized models where probability distributions can
become degenerate.

`mlx_lm` has `make_logits_processors(repetition_penalty,
repetition_context_size)` that handles this, but it is never called
anywhere in the pipeline.

## Solution

Wire `repetition_penalty` and `repetition_context_size` through the
stack:

- `ChatCompletionRequest` → accept the params from the client
- `TextGenerationTaskParams` → carry them through the pipeline
- `chat_completions.py` adapter → map to internal params
- `generate.py` → call `make_logits_processors()` and merge into the
`logits_processors` list

When `repetition_penalty` is `None` (default), `make_logits_processors`
returns `[]` so existing behavior is unchanged.

## Usage

```json
{
  "model": "mlx-community/...",
  "messages": [...],
  "repetition_penalty": 1.15,
  "repetition_context_size": 64
}
```
2026-03-06 10:51:25 +00:00
Alex Cheema e8c3a873a6 feat(dashboard): improve model picker feedback and HuggingFace search (#1661)
## Motivation

Fixes #1648. Users adding custom models from HuggingFace got no clear
feedback that the model was successfully added — the model didn't appear
prominently in the list. Additionally, searches were limited to
`mlx-community` models with no fallback.

## Changes

- **Success feedback**: After adding a custom model, a toast
notification appears, the view auto-switches to "All Models", and the
newly added model is scrolled into view with a green highlight that
fades over 4 seconds.
- **HuggingFace search fallback**: The `/models/search` endpoint now
searches `mlx-community` first; if no results are found, it falls back
to searching all of HuggingFace.
- **Inline HF results**: When the main search bar finds no local
matches, HuggingFace search results appear inline with "+ Add" buttons
and a "See all results on Hub" link.
- **Full repo ID for non-mlx models**: Non-mlx-community models now
display the full repo ID (e.g., `meta-llama/Llama-3.1-8B`) instead of
just the short name.

## Why It Works

The toast + scroll + highlight gives immediate, unambiguous feedback
that the model was added and where it lives in the list. The HF search
fallback broadens discoverability while still prioritising mlx-community
models. Inline results in the main search bar mean users don't need to
navigate to the HF tab to discover new models.

## Test Plan

### Manual Testing
- Open model picker → HuggingFace Hub tab → search for a model → click
"+ Add"
- Verify: toast appears, view switches to All Models, model highlighted
with green glow
- Search for a term with no mlx-community results → verify fallback to
full HF search
- Non-mlx-community results show full repo ID (e.g., `org/model-name`)
- On All Models tab, search for a model not in the local list → verify
"From HuggingFace" section appears

### Automated Testing
- Existing tests cover the model catalog and API; no new automated tests
needed for UI behaviour changes

---------

Co-authored-by: Evan <evanev7@gmail.com>
2026-03-06 10:23:08 +00:00
Michael Harrigan afab3095b0 fix: KVPrefixCache Regression (#1668) 2026-03-06 10:11:08 +00:00
ciaranbor b9d40e8e35 Ciaran/re download bug (#1658)
## Motivation

After deleting a model and re-downloading it, the CachedShardDownloader
returns the stale cached path, so ensure_shard short-circuits and no
download actually happens.

## Changes

- Added invalidate(model_id) method to the ShardDownloader ABC and all
implementations
- CachedShardDownloader.invalidate evicts cache entries matching the
model ID and delegates down
- DownloadCoordinator.delete_model calls invalidate after cancelling
active downloads, before deleting files
- Added end-to-end test that downloads, deletes, and re-downloads a
model through the coordinator

## Why It Works

The cache is cleared when a model is deleted, so the next ensure_shard
call performs a fresh download instead of returning the stale path.

## Test Plan

## Automated Testing

New test_re_download_after_delete_completes exercises the full download
→ delete → re-download flow through DownloadCoordinator with
CachedShardDownloader + SingletonShardDownloader wrappers matching
production.
2026-03-05 14:18:17 +00:00
wysie 3a4d635d0c Fix copy code button not working in dashboard (#1659)
Fixes #1657

## Motivation

The copy button on code blocks in the dashboard does nothing when
clicked. This affects all code blocks in assistant responses.

## Root Cause

In `MarkdownContent.svelte`, click event listeners are bound to
`.copy-code-btn` elements via `setupCopyButtons()` inside a Svelte 5
`$effect`. However, the effect fires before the DOM has been updated
with the new HTML, so `querySelectorAll(".copy-code-btn")` finds zero
buttons.

Additionally, during streaming, the `content` prop updates on every
token, causing the entire `{@html processedHtml}` to be re-rendered.
This destroys all previously bound event listeners, even if they were
successfully attached.

## Changes

Replaced the per-button `addEventListener` approach with **event
delegation** — a single click listener on the container element that
catches clicks bubbling up from any `.copy-code-btn` or
`.copy-math-btn`. This:

- Eliminates the timing issue (the listener exists before the buttons
are rendered)
- Survives HTML re-renders during streaming (no need to re-bind)
- Removes the need for `setupCopyButtons()` and the `data-listenerBound`
tracking

## Testing

1. Load any model
2. Prompt it to generate a code block (e.g. "write a hello world in
Python")
3. Click the copy button on the code block
4. Paste — the code is copied correctly
5. Verified the button also works during streaming (before generation
completes)

Co-authored-by: Wysie <wysie@users.noreply.github.com>
2026-03-05 12:41:17 +00:00
593 changed files with 20861 additions and 5839 deletions
+20
View File
@@ -0,0 +1,20 @@
from enum import Enum
class HarmonyEncodingName(Enum):
HARMONY_GPT_OSS = ...
class HarmonyEncoding: ...
class HarmonyError(Exception): ...
class Role(Enum):
ASSISTANT = ...
class StreamableParser:
last_content_delta: str
current_channel: str | None
current_recipient: str | None
def __init__(self, encoding: HarmonyEncoding, role: Role = ...) -> None: ...
def process(self, token_id: int) -> None: ...
def load_harmony_encoding(name: HarmonyEncodingName) -> HarmonyEncoding: ...
+17
View File
@@ -0,0 +1,17 @@
class NvmlMemoryInfo:
used: int
total: int
free: int
class NvmlUtilizationRates:
gpu: int
memory: int
def nvmlInit() -> None: ...
def nvmlShutdown() -> None: ...
def nvmlDeviceGetCount() -> int: ...
def nvmlDeviceGetHandleByIndex(index: int) -> object: ...
def nvmlDeviceGetUtilizationRates(handle: object) -> NvmlUtilizationRates: ...
def nvmlDeviceGetTemperature(handle: object, sensor_type: int) -> int: ...
def nvmlDeviceGetPowerUsage(handle: object) -> int: ...
def nvmlDeviceGetMemoryInfo(handle: object) -> NvmlMemoryInfo: ...
+61
View File
@@ -0,0 +1,61 @@
from typing import Any, Sequence
from torch import backends as backends
from torch import cuda as cuda
from torch import distributed as distributed
__version__: str
class version:
cuda: str
class dtype: ...
bfloat16: dtype
float16: dtype
float32: dtype
int8: dtype
int32: dtype
int64: dtype
long: dtype
float8_e4m3fn: dtype
class Tensor:
shape: Sequence[int]
dtype: dtype
def __getitem__(self, key: Any) -> Tensor: ...
def __setitem__(self, key: Any, value: Any) -> None: ...
def to(self, *args: Any, **kwargs: Any) -> Tensor: ...
def cpu(self) -> Tensor: ...
def detach(self) -> Tensor: ...
def clone(self) -> Tensor: ...
def flatten(self, start_dim: int = 0, end_dim: int = -1) -> Tensor: ...
def view(self, *shape: Any) -> Tensor: ...
def squeeze(self, dim: int = ...) -> Tensor: ...
def unsqueeze(self, dim: int) -> Tensor: ...
def permute(self, *dims: int) -> Tensor: ...
def float(self) -> Tensor: ...
def numpy(self) -> Any: ...
def numel(self) -> int: ...
def nelement(self) -> int: ...
@property
def is_cuda(self) -> bool: ...
@property
def device(self) -> device: ...
def __len__(self) -> int: ...
def data_ptr(self) -> int: ...
def tolist(self) -> Any: ...
def abs(self) -> Tensor: ...
def max(self) -> Tensor: ...
def mean(self) -> Tensor: ...
def sum(self, dim: int = ...) -> Tensor: ...
def item(self) -> float: ...
def tensor(data: Any, dtype: dtype | None = None, device: Any = None) -> Tensor: ...
def zeros(*size: Any, dtype: dtype | None = None, device: Any = None) -> Tensor: ...
def empty(*size: Any, dtype: dtype | None = None, device: Any = None) -> Tensor: ...
def from_numpy(ndarray: Any) -> Tensor: ...
def inference_mode() -> Any: ...
class device:
def __init__(self, type: str, index: int = ...) -> None: ...
@@ -0,0 +1 @@
from torch.backends import cuda as cuda
@@ -0,0 +1 @@
def is_built() -> bool: ...
+10
View File
@@ -0,0 +1,10 @@
class _DeviceProperties:
total_memory: int
def is_available() -> bool: ...
def get_device_name(device: int) -> str: ...
def get_device_properties(device: int) -> _DeviceProperties: ...
def empty_cache() -> None: ...
def mem_get_info() -> tuple[int, int]: ...
def synchronize() -> None: ...
def max_memory_allocated() -> int: ...
@@ -0,0 +1,2 @@
def is_initialized() -> bool: ...
def destroy_process_group() -> None: ...
+1
View File
@@ -0,0 +1 @@
__version__: str
+2
View File
@@ -0,0 +1,2 @@
class ModelConfig:
max_model_len: int
+18
View File
@@ -0,0 +1,18 @@
from dataclasses import dataclass
@dataclass
class EngineArgs:
model: str = ...
served_model_name: str | list[str] | None = ...
tokenizer: str | None = ...
trust_remote_code: bool = ...
dtype: str = ...
seed: int = ...
max_model_len: int | None = ...
gpu_memory_utilization: float = ...
enforce_eager: bool = ...
tensor_parallel_size: int = ...
pipeline_parallel_size: int = ...
quantization: str | None = ...
load_format: str = ...
enable_sleep_mode: bool = ...
+17
View File
@@ -0,0 +1,17 @@
class CompletionOutput:
index: int
text: str
token_ids: list[int]
cumulative_logprob: float | None
logprobs: object | None
finish_reason: str | None
stop_reason: int | str | None
def finished(self) -> bool: ...
class RequestOutput:
request_id: str
prompt: str | None
prompt_token_ids: list[int] | None
outputs: list[CompletionOutput]
finished: bool
+11
View File
@@ -0,0 +1,11 @@
class SamplingParams:
n: int
temperature: float
top_p: float
top_k: int
min_p: float
seed: int | None
stop: str | list[str] | None
max_tokens: int | None
logprobs: int | None
repetition_penalty: float
@@ -0,0 +1,3 @@
from vllm.tokenizers.protocol import TokenizerLike
__all__ = ["TokenizerLike"]
@@ -0,0 +1,15 @@
from typing import Protocol
class TokenizerLike(Protocol):
@property
def eos_token_id(self) -> int: ...
@property
def vocab_size(self) -> int: ...
def encode(self, text: str, add_special_tokens: bool = ...) -> list[int]: ...
def decode(self, ids: list[int] | int, skip_special_tokens: bool = ...) -> str: ...
def apply_chat_template(
self,
messages: list[dict[str, str]],
tools: list[dict[str, object]] | None = ...,
**kwargs: object,
) -> str | list[int]: ...
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,24 @@
from collections.abc import Sequence
from vllm.v1.core.kv_cache_utils import BlockPool, KVCacheBlock
from vllm.v1.kv_cache_interface import KVCacheConfig
class KVCacheBlocks:
blocks: tuple[Sequence[KVCacheBlock], ...]
def __init__(self, blocks: tuple[Sequence[KVCacheBlock], ...]) -> None: ...
def get_block_ids(self) -> tuple[list[int], ...]: ...
class KVCacheManager:
block_pool: BlockPool
kv_cache_config: KVCacheConfig
enable_caching: bool
num_kv_cache_groups: int
coordinator: object
def __init__(self, *args: object, **kwargs: object) -> None: ...
def allocate_slots(
self, request: object, num_new_tokens: int, *args: object, **kwargs: object
) -> KVCacheBlocks | None: ...
def get_computed_blocks(self, request: object) -> tuple[KVCacheBlocks, int]: ...
def create_kv_cache_blocks(
self, blocks: tuple[list[KVCacheBlock], ...]
) -> KVCacheBlocks: ...
@@ -0,0 +1,16 @@
class KVCacheBlock:
block_id: int
ref_cnt: int
def __init__(self, block_id: int) -> None: ...
class FreeKVCacheBlockQueue:
def append_n(self, blocks: list[KVCacheBlock]) -> None: ...
def popleft_n(self, n: int) -> list[KVCacheBlock]: ...
class BlockPool:
blocks: list[KVCacheBlock]
free_block_queue: FreeKVCacheBlockQueue
num_gpu_blocks: int
enable_caching: bool
def get_num_free_blocks(self) -> int: ...
def get_new_blocks(self, num_blocks: int) -> list[KVCacheBlock]: ...
@@ -0,0 +1,22 @@
from vllm.config import ModelConfig
from vllm.engine.arg_utils import EngineArgs
from vllm.outputs import RequestOutput
from vllm.sampling_params import SamplingParams
from vllm.tokenizers import TokenizerLike
class LLMEngine:
tokenizer: TokenizerLike | None
model_config: ModelConfig
@classmethod
def from_engine_args(cls, engine_args: EngineArgs) -> LLMEngine: ...
def add_request(
self,
request_id: str,
prompt: str,
params: SamplingParams,
arrival_time: float | None = ...,
) -> None: ...
def step(self) -> list[RequestOutput]: ...
def has_unfinished_requests(self) -> bool: ...
def get_tokenizer(self) -> TokenizerLike: ...
@@ -0,0 +1,23 @@
from dataclasses import dataclass
@dataclass
class KVCacheSpec:
block_size: int
num_kv_heads: int
head_size: int
@dataclass
class KVCacheGroupSpec:
layer_names: list[str]
kv_cache_spec: KVCacheSpec
@dataclass
class KVCacheTensorSpec:
shared_by: list[str]
size: int
@dataclass
class KVCacheConfig:
num_blocks: int
kv_cache_groups: list[KVCacheGroupSpec]
kv_cache_tensors: list[KVCacheTensorSpec]
+6
View File
@@ -0,0 +1,6 @@
class Request:
request_id: str
prompt_token_ids: list[int] | None
num_prompt_tokens: int
num_computed_tokens: int
num_tokens: int
@@ -0,0 +1 @@
@@ -0,0 +1,24 @@
import torch
class _CompilationConfig:
static_forward_context: dict[str, object]
class _ModelConfig:
hf_config: object
class GPUModelRunner:
kv_caches: list[torch.Tensor]
compilation_config: _CompilationConfig
model_config: _ModelConfig | None
def _allocate_kv_cache_tensors(
self, kv_cache_config: object
) -> dict[str, torch.Tensor]: ...
def initialize_kv_cache_tensors(
self, kv_cache_config: object, kernel_block_sizes: list[int]
) -> dict[str, torch.Tensor]: ...
def _reshape_kv_cache_tensors(
self,
kv_cache_config: object,
raw_tensors: dict[str, torch.Tensor],
kernel_block_sizes: list[int],
) -> dict[str, torch.Tensor]: ...
@@ -0,0 +1,6 @@
from vllm.v1.worker.gpu_model_runner import GPUModelRunner
class Worker:
model_runner: GPUModelRunner
def determine_available_memory(self) -> int: ...
def initialize_from_config(self, kv_cache_config: object) -> None: ...
+1
View File
@@ -0,0 +1 @@
def extract_layer_index(layer_name: str, num_attn_module: int) -> int: ...
-20
View File
@@ -1,20 +0,0 @@
"""
This type stub file was generated by pyright.
"""
from activations import *
from base import *
from containers import *
from convolution import *
from convolution_transpose import *
from distributed import *
from dropout import *
from embedding import *
from linear import *
from normalization import *
from pooling import *
from positional_encoding import *
from quantized import *
from recurrent import *
from transformer import *
from upsample import *
@@ -2,10 +2,11 @@
This type stub file was generated by pyright.
"""
from typing import Protocol
import mlx.core as mx
import PIL.Image
import tqdm
from typing import Protocol
from mflux.models.common.config.config import Config
class BeforeLoopCallback(Protocol):
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import TYPE_CHECKING
from mflux.callbacks.callback import (
AfterLoopCallback,
BeforeLoopCallback,
@@ -2,10 +2,11 @@
This type stub file was generated by pyright.
"""
from typing import TYPE_CHECKING
import mlx.core as mx
import PIL.Image
import tqdm
from typing import TYPE_CHECKING
from mflux.callbacks.callback_registry import CallbackRegistry
from mflux.models.common.config.config import Config
@@ -2,11 +2,12 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from pathlib import Path
from typing import Any
from tqdm import tqdm
import mlx.core as mx
from mflux.models.common.config.model_config import ModelConfig
from tqdm import tqdm
logger = ...
@@ -2,10 +2,11 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from functools import lru_cache
from typing import Literal
import mlx.core as mx
class ModelConfig:
precision: mx.Dtype = ...
def __init__(
@@ -2,10 +2,10 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from pathlib import Path
from typing import TYPE_CHECKING, TypeAlias
from mlx import nn
import mlx.core as mx
from mflux.models.common.vae.tiling_config import TilingConfig
from mflux.models.fibo.latent_creator.fibo_latent_creator import FiboLatentCreator
from mflux.models.flux.latent_creator.flux_latent_creator import FluxLatentCreator
@@ -13,6 +13,7 @@ from mflux.models.qwen.latent_creator.qwen_latent_creator import QwenLatentCreat
from mflux.models.z_image.latent_creator.z_image_latent_creator import (
ZImageLatentCreator,
)
from mlx import nn
if TYPE_CHECKING:
LatentCreatorType: TypeAlias = type[
@@ -2,8 +2,8 @@
This type stub file was generated by pyright.
"""
from mlx import nn
from mflux.models.common.lora.layer.linear_lora_layer import LoRALinear
from mlx import nn
class FusedLoRALinear(nn.Module):
def __init__(
@@ -2,10 +2,11 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
import mlx.nn as nn
from collections.abc import Callable
from dataclasses import dataclass
import mlx.core as mx
import mlx.nn as nn
from mflux.models.common.lora.mapping.lora_mapping import LoRATarget
@dataclass
@@ -2,11 +2,12 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from collections.abc import Callable
from dataclasses import dataclass
from typing import List, Protocol
import mlx.core as mx
@dataclass
class LoRATarget:
model_path: str
@@ -36,4 +36,3 @@ class Rule(NamedTuple):
name: str
check: str
action: QuantizationAction | PathAction | LoraAction | ConfigAction
...
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import TYPE_CHECKING
from mflux.models.common.config.model_config import ModelConfig
if TYPE_CHECKING: ...
@@ -2,9 +2,10 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from abc import ABC, abstractmethod
import mlx.core as mx
class BaseScheduler(ABC):
@property
@abstractmethod
@@ -2,8 +2,9 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from typing import TYPE_CHECKING
import mlx.core as mx
from mflux.models.common.config.config import Config
from mflux.models.common.schedulers.base_scheduler import BaseScheduler
@@ -2,8 +2,9 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from typing import TYPE_CHECKING
import mlx.core as mx
from mflux.models.common.config.config import Config
from mflux.models.common.schedulers.base_scheduler import BaseScheduler
@@ -2,8 +2,9 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from typing import TYPE_CHECKING
import mlx.core as mx
from mflux.models.common.config.config import Config
from mflux.models.common.schedulers.base_scheduler import BaseScheduler
@@ -4,9 +4,10 @@ This type stub file was generated by pyright.
from abc import ABC, abstractmethod
from typing import Protocol, runtime_checkable
from mflux.models.common.tokenizer.tokenizer_output import TokenizerOutput
from PIL import Image
from transformers import PreTrainedTokenizer
from mflux.models.common.tokenizer.tokenizer_output import TokenizerOutput
"""
This type stub file was generated by pyright.
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import TYPE_CHECKING
from mflux.models.common.tokenizer.tokenizer import BaseTokenizer
from mflux.models.common.weights.loading.weight_definition import TokenizerDefinition
@@ -2,9 +2,10 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from dataclasses import dataclass
import mlx.core as mx
"""
This type stub file was generated by pyright.
"""
@@ -2,9 +2,10 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from typing import Callable
import mlx.core as mx
class VAETiler:
@staticmethod
def encode_image_tiled(
@@ -3,8 +3,8 @@ This type stub file was generated by pyright.
"""
import mlx.core as mx
from mlx import nn
from mflux.models.common.vae.tiling_config import TilingConfig
from mlx import nn
class VAEUtil:
@staticmethod
@@ -2,8 +2,9 @@
This type stub file was generated by pyright.
"""
import mlx.nn as nn
from typing import TYPE_CHECKING
import mlx.nn as nn
from mflux.models.common.weights.loading.loaded_weights import LoadedWeights
from mflux.models.common.weights.loading.weight_definition import (
ComponentDefinition,
@@ -2,11 +2,12 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from dataclasses import dataclass
from typing import Callable, List, TYPE_CHECKING, TypeAlias
from mflux.models.common.weights.mapping.weight_mapping import WeightTarget
from typing import TYPE_CHECKING, Callable, List, TypeAlias
import mlx.core as mx
from mflux.models.common.tokenizer.tokenizer import BaseTokenizer
from mflux.models.common.weights.mapping.weight_mapping import WeightTarget
from mflux.models.depth_pro.weights.depth_pro_weight_definition import (
DepthProWeightDefinition,
)
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import TYPE_CHECKING
from mflux.models.common.weights.loading.loaded_weights import LoadedWeights
from mflux.models.common.weights.loading.weight_definition import (
ComponentDefinition,
@@ -2,8 +2,9 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from typing import Dict, List, Optional
import mlx.core as mx
from mflux.models.common.weights.mapping.weight_mapping import WeightTarget
class WeightMapper:
@@ -2,10 +2,11 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from dataclasses import dataclass
from typing import Callable, List, Optional, Protocol
import mlx.core as mx
"""
This type stub file was generated by pyright.
"""
@@ -2,7 +2,8 @@
This type stub file was generated by pyright.
"""
from typing import Any, TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from mflux.models.common.weights.loading.weight_definition import WeightDefinitionType
if TYPE_CHECKING: ...
@@ -2,9 +2,10 @@
This type stub file was generated by pyright.
"""
import mlx.core as mx
from dataclasses import dataclass
from pathlib import Path
import mlx.core as mx
from PIL import Image
@dataclass
@@ -13,7 +14,6 @@ class DepthResult:
depth_array: mx.array
min_depth: float
max_depth: float
...
class DepthPro:
def __init__(self, quantize: int | None = ...) -> None: ...
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import List
from mflux.models.common.weights.loading.weight_definition import (
ComponentDefinition,
TokenizerDefinition,
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import List
from mflux.models.common.weights.mapping.weight_mapping import (
WeightMapping,
WeightTarget,
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import List
from mflux.models.common.weights.loading.weight_definition import (
ComponentDefinition,
TokenizerDefinition,
@@ -3,6 +3,7 @@ This type stub file was generated by pyright.
"""
from typing import List
from mflux.models.common.weights.mapping.weight_mapping import (
WeightMapping,
WeightTarget,

Some files were not shown because too many files have changed in this diff Show More