Files
DS4Server/PLAN.md
2026-07-24 12:23:50 +02:00

11 KiB

DS4Server implementation plan

Bundle/application identifier: DS4Server.rfc1437.de

The GUI is a Rust/Iced reimplementation of the user-facing model server and coding-agent workflows in ../ds4. The existing engine remains the behavioral 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.

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.
  • Never delete workspace contents when removing app metadata.

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.
  2. Retain the tested model restrictions from DwarfStar; reject unsupported shapes and quantizations before allocating model state.
  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. 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.

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 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

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 ds4_server.c so a client's normalized JSON does not destroy KV-prefix reuse.
  5. Bind API conversations to engine sessions and persist/restore KV payloads using the engine-owned serialization format. Add resident batching only after single-session correctness is established.
  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.

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:

Application Support/DS4Server.rfc1437.de/
  data.sqlite3
  models/<model-id>/
    main.gguf
    dflash.gguf       # only when enabled
  sessions/<session-id>/
    kv.bin
  • Add migrated message/transcript tables to SQLite. Keep only the engine-owned large KV payload in per-session files, written with atomic replacement.
  • Record model identity, context size, rendered token history, title, timestamps, and working directory.
  • Restore compatible KV immediately. If KV is absent or incompatible, rebuild from the transcript and show prefill progress.
  • Add strip/archive behavior equivalent to the native agent: retain transcript while removing the large KV payload.
  • Version formats before the first release and add migrations only when a real format change exists.

Exit criterion: quit during normal use, relaunch, select a session, and resume 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.
  2. Port the native agent turn loop from ds4_agent.c, including model-specific system prompts and DeepSeek DSML/GLM tool-call parsing.
  3. Implement the local tool set with the project directory as its boundary:
    • shell command execution
    • file read and continuation
    • file write
    • anchored edit
    • text/file search
  4. Stream assistant tokens and partial tool calls into Iced while inference and tools run on workers. Preserve cooperative interruption and a valid KV prefix at every stop point.
  5. Require confirmation for commands or writes outside the project boundary, destructive actions, and network tools. Add web search/page visiting only after the local tool loop is stable.
  6. Port context compaction and system-prompt reinjection after ordinary multi-turn operation is correct.

Exit criterion: create a project session, ask the model to inspect and edit a small fixture repository, review each tool result in the GUI, restart, and 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 KV correctness; run live Metal tests only when a supported GGUF is available.
  • Preserve the DwarfStar/llama.cpp notices required by ../ds4/LICENSE for 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.
  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.