add preferences and macOS packaging
This commit is contained in:
120
PLAN.md
120
PLAN.md
@@ -8,11 +8,30 @@ reference: `ds4.c`/`ds4.h` define model and session behavior,
|
||||
`ds4_server.c` defines the compatible API, and `ds4_agent.c` defines agent
|
||||
sessions, tools, and turn orchestration.
|
||||
|
||||
## Phase 0 — project and session shell (implemented)
|
||||
## Current status
|
||||
|
||||
| Area | Status | Available now | Still open |
|
||||
| --- | --- | --- | --- |
|
||||
| App shell | Implemented | Native Iced window, Codex-inspired two-pane layout, dark theme, embedded SVG icons, `Command-,` Preferences panel | Functional chat content and native app packaging |
|
||||
| Projects and sessions | Implemented for metadata | Native folder picker, project-name dialog, create/select/delete projects and sessions | Transcripts, KV payloads, rename/archive, and model binding |
|
||||
| Persistence | Implemented for metadata and preferences | SQLite in Application Support, Diesel ORM, embedded migrations, constrained defaults and CRUD tests | Messages, downloaded-artifact metadata, and session-runtime migrations |
|
||||
| Model runtime | Preferences only | Persisted model choice, DFlash toggle, and idle timeout | Downloads, loading, inference, idle unloading, and Metal integration |
|
||||
| Local endpoint | Not started | Nothing listening | OpenAI-compatible HTTP and streaming surfaces |
|
||||
| Agent | UI shell only | Empty conversation pane and composer placeholder | Messages, generation, tools, approvals, compaction, and cancellation |
|
||||
| Release | Not started | Development binary builds and tests | `.app` packaging, signing, entitlements, and notarization |
|
||||
|
||||
The application currently manages durable project/session metadata and model
|
||||
preferences. It does not yet download or load a model, serve HTTP, save chat
|
||||
messages, or run an agent.
|
||||
|
||||
## Phase 0 — project and session shell
|
||||
|
||||
Status: **implemented**.
|
||||
|
||||
- Show projects in a sidebar with their sessions nested underneath.
|
||||
- Create projects with a native macOS folder picker and a project-name dialog.
|
||||
- Create, select, and delete projects and sessions.
|
||||
- Use a Codex-inspired two-pane layout with embedded, action-specific SVG icons.
|
||||
- Persist projects and sessions in SQLite under
|
||||
`~/Library/Application Support/DS4Server.rfc1437.de/`, using Diesel ORM
|
||||
queries and embedded Diesel migrations.
|
||||
@@ -22,6 +41,9 @@ Exit criterion: restart the app and see the same project/session tree.
|
||||
|
||||
## Phase 1 — model library and on-demand loading
|
||||
|
||||
Status: **partially implemented**. Preferences and their schema are complete;
|
||||
model downloads, loading, inference, and idle unloading remain open.
|
||||
|
||||
1. Port the narrow GGUF loader, tokenizer, prompt renderer, and Metal session
|
||||
API behind a small Rust `Engine` surface modeled on `ds4.h`:
|
||||
`open`, `close`, `create_session`, `sync`, `sample`, and KV save/load.
|
||||
@@ -30,31 +52,76 @@ Exit criterion: restart the app and see the same project/session tree.
|
||||
3. 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.
|
||||
4. Add a model library screen that records GGUF paths and loading options.
|
||||
Load only after the user chooses a model. Expose
|
||||
`Unloaded → Loading → Ready/Failed → Unloading` state and progress in Iced.
|
||||
5. Keep one engine resident initially, matching DwarfStar's instance-lock and
|
||||
memory assumptions. Switching models unloads the current engine first.
|
||||
6. Move engine work off the UI thread and support cooperative cancellation at
|
||||
4. Keep one engine resident at most, matching DwarfStar's instance-lock and
|
||||
memory assumptions.
|
||||
5. Move engine work off the UI thread and support cooperative cancellation at
|
||||
the safe session boundaries already defined by `ds4_session_sync`.
|
||||
|
||||
Persist model references and tuning options in Application Support. For a
|
||||
sandboxed distribution, store security-scoped bookmarks rather than assuming
|
||||
raw paths remain accessible.
|
||||
### Lazy lifecycle
|
||||
|
||||
- Start the application and the local HTTP endpoint with no model loaded.
|
||||
- Acquire the configured model only when generation starts, whether initiated
|
||||
by a local chat or an OpenAI-compatible endpoint request. Concurrent first
|
||||
requests share the same load instead of opening the model more than once.
|
||||
- Expose `Unloaded → Downloading → Loading → Ready → Unloading` plus failure
|
||||
states and progress in the GUI. A request that triggered loading waits for
|
||||
the shared load and receives a clear error if download or initialization
|
||||
fails.
|
||||
- Treat active local generations, endpoint requests, prefills, and tool turns
|
||||
as model activity. Start the idle timer only when all activity has stopped.
|
||||
- Unload the engine after the configured idle timeout. New work cancels a
|
||||
pending unload; work arriving after unload transparently loads the model
|
||||
again. Persisted transcripts and KV files remain available when model memory
|
||||
is released.
|
||||
- Changing the selected model prevents new work from using the old model and
|
||||
unloads it as soon as current work reaches a safe boundary.
|
||||
|
||||
### Preferences (implemented)
|
||||
|
||||
- Provide a Preferences panel from the sidebar and the standard
|
||||
macOS `Command-,` shortcut.
|
||||
- Let the user choose the active model. Default to `deepseek-v4-flash`.
|
||||
- Let the user enable or disable DFlash for the selected model. Disable the
|
||||
control for models that do not support DFlash.
|
||||
- Let the user configure the inactivity timeout; default to 10 minutes.
|
||||
- Persist preferences in the application SQLite database. Changing preferences
|
||||
never loads a model by itself.
|
||||
|
||||
### Targeted downloads and storage
|
||||
|
||||
- Store managed model artifacts under
|
||||
`~/Library/Application Support/DS4Server.rfc1437.de/models/<model-id>/` and
|
||||
mmap/load them from there. Do not place managed models in the app bundle or a
|
||||
project directory.
|
||||
- Download only the selected model's main artifact. Download its DFlash
|
||||
artifact only when DFlash is enabled; never prefetch other catalog models or
|
||||
optional artifacts.
|
||||
- Verify size/checksum before making a download usable, resume partial
|
||||
downloads, and remove failed temporary files without touching a valid model.
|
||||
- When the selected model changes or DFlash is disabled, offer removal of the
|
||||
now-unused artifact; do not delete large existing downloads without explicit
|
||||
confirmation.
|
||||
|
||||
Exit criterion: select a supported DeepSeek V4 or GLM 5.2 GGUF, load it without
|
||||
blocking the UI, run a deterministic prompt, unload it, and pass the matching
|
||||
`../ds4` token-output fixture.
|
||||
blocking the UI only when a chat begins, run a deterministic prompt, unload it
|
||||
after the configured idle timeout, and pass the matching `../ds4` token-output
|
||||
fixture. Repeat through the local endpoint and verify both paths share the same
|
||||
engine lifecycle.
|
||||
|
||||
## Phase 2 — OpenAI-compatible local endpoint
|
||||
|
||||
1. Add a localhost-only HTTP service owned by the loaded engine. Start and stop
|
||||
it from the GUI; never expose a LAN listener without an explicit setting.
|
||||
Status: **open; not started**.
|
||||
|
||||
1. 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:
|
||||
- `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.
|
||||
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
|
||||
@@ -72,11 +139,17 @@ multi-turn tool call.
|
||||
|
||||
## Phase 3 — durable agent sessions
|
||||
|
||||
Status: **partially open**. Project/session metadata rows exist; transcripts,
|
||||
model binding, and KV payload persistence are not implemented.
|
||||
|
||||
Replace each metadata-only session with a directory:
|
||||
|
||||
```text
|
||||
Application Support/DS4Server.rfc1437.de/
|
||||
data.sqlite3
|
||||
models/<model-id>/
|
||||
main.gguf
|
||||
dflash.gguf # only when enabled
|
||||
sessions/<session-id>/
|
||||
kv.bin
|
||||
```
|
||||
@@ -97,6 +170,9 @@ the same conversation without prefill when its KV payload is compatible.
|
||||
|
||||
## Phase 4 — agent chat and tools
|
||||
|
||||
Status: **UI shell only**. The conversation area, composer, and icons are
|
||||
visual placeholders with no message or agent runtime behind them.
|
||||
|
||||
1. Build the classic chat surface: transcript, reasoning disclosure, tool-call
|
||||
cards, composer, queued input, stop button, prefill progress, and generation
|
||||
statistics.
|
||||
@@ -123,6 +199,11 @@ continue from the persisted session.
|
||||
|
||||
## Verification and packaging
|
||||
|
||||
Status: **partially implemented**. Formatting, Clippy, build, and unit-test
|
||||
commit gates are documented in `AGENTS.md`; database migration/CRUD constraints
|
||||
and the preferences shortcut have unit coverage. Model fixtures, integration
|
||||
tests, and release packaging remain open.
|
||||
|
||||
- Keep unit coverage narrow: persistence round trips, format compatibility,
|
||||
request parsing, and tool-boundary checks.
|
||||
- Reuse `../ds4` fixtures for prompt rendering, sampling, server streaming, and
|
||||
@@ -131,3 +212,14 @@ continue from the persisted session.
|
||||
reused or adapted source and kernels.
|
||||
- Produce a signed `.app` with the exact identifier above, then add hardened
|
||||
runtime, entitlements, notarization, and update delivery as release work.
|
||||
|
||||
## Recommended next milestones
|
||||
|
||||
1. Add the targeted download manager and model catalog, storing only selected
|
||||
artifacts under Application Support with resumable, verified downloads.
|
||||
2. Implement the single-engine lifecycle and local generation path: lazy load,
|
||||
shared concurrent acquisition, activity tracking, and idle unload.
|
||||
3. Put the OpenAI-compatible endpoint on that same engine lifecycle so local
|
||||
chats and HTTP requests cannot load duplicate engines.
|
||||
4. Add transcript/KV persistence, then connect the existing conversation UI and
|
||||
finally port the agent tools.
|
||||
|
||||
Reference in New Issue
Block a user