feat: first cut at openai compatible server
This commit is contained in:
150
PLAN.md
150
PLAN.md
@@ -13,11 +13,11 @@ sessions, tools, and turn orchestration.
|
||||
| Area | Status | Available now | Still open |
|
||||
| --- | --- | --- | --- |
|
||||
| App shell | Implemented | Native multi-window Iced app, Codex-inspired layout, native Application/Edit/Window menus, Preferences panel, Model Manager, and streaming composer | Native app release packaging |
|
||||
| Projects and sessions | Implemented with transcripts | Native folder picker, project-name dialog, create/select/delete projects and sessions, and durable structured chat history | KV payloads, rename/archive, and model binding |
|
||||
| Persistence | Implemented for metadata, preferences, and chat | SQLite in Application Support, Diesel ORM, embedded migrations, constrained defaults, streamed message updates, and reopen tests | Downloaded-artifact metadata and session-runtime migrations |
|
||||
| Model runtime | DeepSeek Flash vertical implemented | Rust-owned mmap/model/session graph, reused Metal kernels, 2K compressed-attention generation, DS4 sampling, lazy background loading, cancellation, and idle unloading | Ratio-4 sparse indexer beyond 2K, DSpark/SSD/steering, GLM/Pro, KV checkpoint persistence, and shared acquisition for HTTP |
|
||||
| Local endpoint | Not started | Nothing listening | OpenAI-compatible HTTP and streaming surfaces |
|
||||
| Agent | Basic local chat implemented | Multi-turn role rendering, structured reasoning disclosure, streamed DeepSeek Flash text, composer send/stop, and durable transcript rehydration | Tools, approvals, compaction, statistics, and KV-backed resume |
|
||||
| Projects and sessions | Implemented with transcripts and KV resume | Native folder picker, project-name dialog, create/select/delete projects and sessions, durable structured chat history, and per-session KV checkpoints | Rename/archive and model binding |
|
||||
| Persistence | Implemented for local chat | SQLite in Application Support, Diesel ORM, embedded migrations, constrained defaults, streamed message updates, reopen tests, context/speed state, and local KV rehydration | Downloaded-artifact metadata and session-runtime migrations; HTTP chats deliberately never enter SQLite |
|
||||
| Model runtime | DeepSeek Flash vertical implemented | Rust-owned mmap/model/session graph, local Metal kernels, full configured sparse context, DS4 sampling, one process-wide UI/HTTP owner, cancellation, idle unloading, prefix continuation, and KV checkpoint save/load | DSpark/SSD/steering and GLM/Pro execution |
|
||||
| Local endpoint | Chat Completions vertical implemented | Automatic configurable localhost listener (port 4000 by default), verified-on-disk model discovery, transient requests, non-streaming and SSE Chat Completions, reasoning, usage, sampling, stops, cancellation, tools, and exact in-process tool replay | GUI status and enable/disable controls, Responses, legacy Completions, Anthropic Messages, full `ds4_server.c` fixture parity, and cross-restart exact tool replay |
|
||||
| Agent | Solid local chat implemented | Multi-turn role rendering, structured reasoning disclosure, Markdown, automatic scrolling, context/speed display, durable transcript rehydration, and KV-backed resume | Tools, approvals, compaction, and richer statistics |
|
||||
| A2UI | Future extension | bDS2 provides the reference structured render-tool contract | Native inline surfaces for local chat after core chat and tools are stable |
|
||||
| Dev brain | Not started | No vault access | Obsidian vault selection, durable access, and agent knowledge/memory tools |
|
||||
| Release | Not started | Development binary builds and tests | `.app` packaging, signing, entitlements, and notarization |
|
||||
@@ -28,8 +28,9 @@ without blocking the UI. Local chat now lazily acquires the Rust DeepSeek Flash
|
||||
executor, streams generated text, supports cancellation and multi-turn role
|
||||
replay, persists reasoning and answers while they stream, rehydrates selected
|
||||
sessions, and unloads model resources after the configured idle timeout. The
|
||||
current Rust graph supports DS4 compressed attention through 2K context; the sparse ratio-4 indexer,
|
||||
KV checkpoints, HTTP service, and agent tools remain open.
|
||||
current Rust graph supports the full configured context with ratio-4 sparse
|
||||
attention and durable local KV checkpoints. The shared HTTP Chat Completions
|
||||
service is available; the remaining protocol routes and agent tools remain open.
|
||||
|
||||
## Phase 0 — project and session shell
|
||||
|
||||
@@ -49,16 +50,16 @@ Exit criterion: restart the app and see the same project/session tree.
|
||||
## Phase 1 — model library and on-demand loading
|
||||
|
||||
Status: **partially implemented**. The typed preference/download/intake path and
|
||||
a DeepSeek Flash Rust/Metal vertical are implemented, including compressed KV
|
||||
through 2K context, DS4 sampling, a single background owner, cancellation, and
|
||||
idle unload. Sparse indexed attention, persistent KV/session sync, other model
|
||||
families, and optional execution modes remain open.
|
||||
a DeepSeek Flash Rust/Metal vertical are implemented, including full configured
|
||||
context, ratio-4 sparse indexed attention, prefix continuation, KV checkpoint
|
||||
save/load, DS4 sampling, cancellation, idle unload, and shared HTTP acquisition.
|
||||
Other model families and optional execution modes remain open.
|
||||
|
||||
1. **Implemented:** the typed model/runtime preference contract described below
|
||||
is complete before chat, the HTTP endpoint, or the engine lifecycle.
|
||||
2. **Partially implemented:** mmap-backed loading, tokenizer/prompt rendering,
|
||||
Rust-owned Flash session creation/evaluation/sampling, and model ownership
|
||||
are complete. Prefix sync plus KV save/load remain.
|
||||
2. **Implemented for DeepSeek Flash:** mmap-backed loading, tokenizer/prompt
|
||||
rendering, Rust-owned session creation/evaluation/sampling, prefix sync, and
|
||||
KV save/load are complete.
|
||||
3. **Implemented:** retain the tested model restrictions from DwarfStar. Main
|
||||
and DSpark artifacts reject invalid GGUF versions, catalog mismatches, and
|
||||
incompatible metadata, tensor shapes, offsets, or quantization before
|
||||
@@ -66,8 +67,11 @@ families, and optional execution modes remain open.
|
||||
4. **Partially implemented:** reuse the existing `.metal` kernels and preserve mmap-backed resident
|
||||
loading plus the explicit SSD-streaming path. Keep Objective-C only at the
|
||||
Metal interop boundary.
|
||||
5. **Implemented for local chat:** keep one engine resident at most, matching DwarfStar's instance-lock and
|
||||
memory assumptions.
|
||||
5. Keep exactly one model resident process-wide. Local chat and every endpoint
|
||||
request must use the same owner and queue; model selection may replace the
|
||||
resident model only after active work reaches a safe boundary. Multiple
|
||||
simultaneously loaded models are out of scope because DS4Server targets
|
||||
models large enough to consume most available memory.
|
||||
6. **Implemented for local chat:** move engine work off the UI thread and support cooperative cancellation at
|
||||
the safe session boundaries already defined by `ds4_session_sync`.
|
||||
|
||||
@@ -92,21 +96,25 @@ to Rust while preserving its semantics and on-disk compatibility:
|
||||
checkpoints, model/quantization/context/payload-ABI compatibility checks,
|
||||
tool-call replay metadata, disk-budget enforcement, hit tracking, and
|
||||
eviction behavior.
|
||||
- Use one process-wide Rust session registry and KV checkpoint store for both
|
||||
local chat and the HTTP server. A remote request bound to an application
|
||||
session resolves to the same logical DS4 session as the GUI and mutations are
|
||||
serialized so two callers never write one live timeline concurrently.
|
||||
Stateless HTTP requests still participate in the same compatible-prefix
|
||||
checkpoint pool, so a prefix produced locally can be reused remotely and
|
||||
vice versa.
|
||||
- Store that shared checkpoint pool under Application Support in
|
||||
`kv-cache/`, using the DS4 key/header/payload rules. Session rows hold only an
|
||||
optional reference to their current checkpoint; the large opaque payload is
|
||||
not copied into separate GUI and HTTP caches.
|
||||
- Sharing does not merge unrelated conversations or expose one session's
|
||||
transcript to another. It shares the DS4 implementation, checkpoint pool, and
|
||||
explicitly bound logical session; each unrelated live conversation keeps its
|
||||
own mutable session state.
|
||||
- Use one process-wide Rust model owner and KV checkpoint implementation for
|
||||
local chat and the HTTP server. Model access is serialized so two callers
|
||||
never mutate the single resident inference graph concurrently.
|
||||
- Application projects and sessions belong exclusively to local chat. HTTP
|
||||
requests are transient and must never create projects, sessions, messages,
|
||||
transcript rows, request-identity mappings, or any other durable chat record.
|
||||
The outside client owns conversation identity and resends the history needed
|
||||
for each request, exactly as `ds4_server.c` expects.
|
||||
- Transient HTTP requests may read and write engine-owned KV cache files for
|
||||
compatible-prefix reuse. Those opaque cache files are the only server-side
|
||||
persistence permitted for external conversations and do not establish an
|
||||
application session or recoverable transcript.
|
||||
- Store engine-owned checkpoints under Application Support in `kv-cache/`.
|
||||
Local chats use their application session ID; HTTP uses content-addressed
|
||||
entries under `kv-cache/http/`. The large opaque payload is never stored in
|
||||
SQLite, and HTTP cache entries do not create application sessions.
|
||||
- Cache sharing does not merge conversations or expose transcript text. It
|
||||
shares only the DS4 implementation and compatible opaque prefix checkpoints;
|
||||
the request body remains the source of truth for external conversation state.
|
||||
- Engine unload must first leave every reusable live timeline in a valid
|
||||
persisted checkpoint. Reload restores a compatible checkpoint immediately;
|
||||
if model identity, quantization, context size, payload ABI, or rendered
|
||||
@@ -140,6 +148,8 @@ to Rust while preserving its semantics and on-disk compatibility:
|
||||
- Let the user enable or disable DSpark for the selected model. Disable the
|
||||
control for models that do not support DSpark.
|
||||
- Let the user configure the inactivity timeout; default to 10 minutes.
|
||||
- Let the user configure the localhost endpoint port; default to 4000 and apply
|
||||
a changed port when preferences are saved.
|
||||
- Persist preferences in the application SQLite database. Changing preferences
|
||||
never loads a model by itself.
|
||||
|
||||
@@ -258,43 +268,58 @@ engine lifecycle.
|
||||
|
||||
## Phase 2 — OpenAI-compatible local endpoint
|
||||
|
||||
Status: **open; not started**.
|
||||
Status: **partially implemented**. The shared single-model runtime,
|
||||
verified-model discovery, configurable localhost port, and the first
|
||||
`ds4_server.c`-compatible Chat Completions vertical are implemented. The
|
||||
remaining protocol routes plus GUI status and enable/disable controls remain
|
||||
open.
|
||||
|
||||
1. Add a localhost-only HTTP service whose lifecycle is independent of the
|
||||
1. **Partially implemented:** add a localhost-only HTTP service whose lifecycle is independent of the
|
||||
engine. It can listen while the model is unloaded and acquires the configured
|
||||
model only for inference. Start and stop it from the GUI; never expose a LAN
|
||||
listener without an explicit setting.
|
||||
2. Implement the DwarfStar compatibility surface in this order:
|
||||
model only for inference. It listens on configurable port 4000 by default;
|
||||
start/stop controls remain open. Never expose a LAN listener without an
|
||||
explicit setting.
|
||||
2. Implement the DwarfStar compatibility surface from `ds4_server.c` in this
|
||||
order, preserving its request parsing, prompt formatting, reasoning fields,
|
||||
streaming events, errors, usage accounting, stop behavior, tool replay, and
|
||||
cancellation semantics rather than inventing an app-specific protocol:
|
||||
- `GET /v1/models`
|
||||
- `POST /v1/chat/completions`
|
||||
- `POST /v1/responses`
|
||||
- `POST /v1/completions`
|
||||
`GET /v1/models` reports the configured model without forcing it to load.
|
||||
`GET /v1/models` scans managed artifacts without forcing a model to load and
|
||||
reports only main model files that are fully downloaded and have a matching
|
||||
on-disk verification marker. Missing, partial, unverified, or checksum-invalid
|
||||
artifacts are never advertised.
|
||||
3. Support streaming SSE, reasoning output, usage accounting, cancellation,
|
||||
sampling parameters, tool schemas, and tool choice.
|
||||
4. Port exact sampled tool-call replay and deterministic canonicalization from
|
||||
`ds4_server.c` so a client's normalized JSON does not destroy KV-prefix
|
||||
reuse.
|
||||
5. Route API conversations through the shared session registry and KV store
|
||||
defined in Phase 1. Responses `conversation` bindings and any other
|
||||
persistent request identity map to the same application session used by the
|
||||
GUI; stateless requests use shared DS4 prefix lookup. Persist/restore only
|
||||
through the engine-owned serialization format. Add resident batching only
|
||||
after single-session and cross-surface correctness are established.
|
||||
5. Route API work through the single process-wide model owner and engine-owned
|
||||
KV store defined in Phase 1. External requests are always stateless at the
|
||||
application layer: never write their messages to SQLite and never bind a
|
||||
Responses `conversation`, request identifier, or client-supplied session key
|
||||
to a local project/session. The client must replay its protocol history;
|
||||
DS4-compatible prefix lookup may accelerate that replay using opaque KV cache
|
||||
files. Add resident batching only if one-model serialized execution is later
|
||||
proven insufficient.
|
||||
6. Display endpoint address, model, active requests, token rates, and errors in
|
||||
the GUI.
|
||||
|
||||
Exit criterion: the upstream DwarfStar server tests pass against the app for
|
||||
non-streaming and streaming Chat Completions and Responses calls, including a
|
||||
multi-turn tool call. A compatible prefix created by local chat must register a
|
||||
remote cache hit, a remote checkpoint must be reusable by local chat, and a
|
||||
GUI/HTTP race on one bound session must serialize without corrupting its token
|
||||
or KV frontier.
|
||||
multi-turn tool call. Verified installed models are listed without loading one;
|
||||
missing/unverified models are absent; local and HTTP generations cannot load two
|
||||
models concurrently; external calls leave no project, session, message, or
|
||||
transcript rows behind; and compatible KV cache files remain reusable.
|
||||
|
||||
## Phase 3 — durable agent sessions
|
||||
|
||||
Status: **partially implemented**. Project/session metadata and structured
|
||||
transcripts are durable; model binding and KV payload persistence remain open.
|
||||
Status: **partially implemented for local chat only**. Project/session metadata,
|
||||
structured transcripts, context statistics, and per-session KV checkpoints are
|
||||
durable. Model binding and archive behavior remain open. This phase never
|
||||
applies to external endpoint conversations.
|
||||
|
||||
Extend each metadata-only session with transcript and shared-checkpoint
|
||||
metadata:
|
||||
@@ -306,14 +331,14 @@ Application Support/DS4Server.rfc1437.de/
|
||||
main.gguf
|
||||
dspark.gguf # only when enabled
|
||||
kv-cache/
|
||||
<ds4-cache-key>.kv
|
||||
<local-session-id>.bin
|
||||
http/<conversation-tag>.bin
|
||||
```
|
||||
|
||||
- **Partially implemented:** migrated message tables persist user text,
|
||||
reasoning state, and assistant output while generation streams. Add the
|
||||
optional current-checkpoint key to SQLite. Keep large engine-owned KV
|
||||
payloads only in the process-wide Phase 1 store, written with atomic
|
||||
replacement; do not introduce separate agent-only or HTTP-only cache formats.
|
||||
- **Implemented:** migrated message tables persist local user text, reasoning
|
||||
state, assistant output, context usage, and generation speed while generation
|
||||
streams. Local session KV payloads remain engine-owned files written with
|
||||
atomic replacement; SQLite never stores opaque tensor data.
|
||||
- Record model identity, context size, rendered token history, title,
|
||||
timestamps, and working directory.
|
||||
- Restore compatible KV immediately. If KV is absent or incompatible, rebuild
|
||||
@@ -365,6 +390,11 @@ ephemeral presentation preferences are the only exception.
|
||||
|
||||
### Native macOS menus (required with chat)
|
||||
|
||||
Status: **partially implemented**. Application, Edit, and Window menus are
|
||||
installed; Cut, Copy, Paste, and Select All now bridge into the focused Iced
|
||||
text field. File/View/Help menus, dynamic enabled state, the remaining Edit
|
||||
actions, and selectable transcript text remain open.
|
||||
|
||||
The application must install and maintain a complete native macOS menu bar;
|
||||
the application-name menu alone is not sufficient. Use platform menu roles,
|
||||
ordering, selectors, and standard key equivalents rather than drawing menus
|
||||
@@ -493,11 +523,13 @@ tests, and release packaging remain open.
|
||||
|
||||
1. Finish the Flash session core as one larger slice: port the ratio-4 indexer
|
||||
and indexed attention for full configured context, then add prefix sync and
|
||||
compatible KV checkpoint save/load.
|
||||
compatible KV checkpoint save/load. **Implemented.**
|
||||
2. Put the OpenAI-compatible endpoint on the same engine lifecycle so local
|
||||
chats and HTTP requests cannot load duplicate engines.
|
||||
3. Add transcript/KV persistence, then connect the existing conversation UI and
|
||||
finally port the agent tools.
|
||||
chats and HTTP requests cannot load duplicate models. Keep HTTP conversation
|
||||
state transient except for opaque KV cache files. **Chat Completions vertical
|
||||
implemented; remaining reference routes are open.**
|
||||
3. Local transcript/KV persistence and the conversation UI are implemented;
|
||||
port the agent tools after the shared endpoint lifecycle is stable.
|
||||
4. Add the opt-in Dev Brain tool after local file/search boundaries and agent
|
||||
approvals are stable.
|
||||
5. Add bDS2-style A2UI render tools and native inline surfaces after the core
|
||||
|
||||
Reference in New Issue
Block a user