Skip to content

feat(providers): support DwarfStar (ds4) via openai-compatible backend strategy#1349

Merged
Aaronontheweb merged 16 commits into
netclaw-dev:devfrom
GiuseppePatane:feat/ds4-provider
Jun 25, 2026
Merged

feat(providers): support DwarfStar (ds4) via openai-compatible backend strategy#1349
Aaronontheweb merged 16 commits into
netclaw-dev:devfrom
GiuseppePatane:feat/ds4-provider

Conversation

@GiuseppePatane

@GiuseppePatane GiuseppePatane commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds support for DwarfStar (ds4) — antirez's native C inference engine for DeepSeek V4 Flash/PRO — through the existing openai-compatible provider type, with a dedicated backend strategy for context-window detection.

ds4 speaks the same protocol as every other OpenAI-compatible backend: same chat client, same endpoints, endpoint-only auth (ds4-server enforces no token), native tool_calls. The only real difference is metadata shape — /v1/models emits OpenRouter-shaped context_length / top_provider.context_length, fields neither the vLLM strategy (max_model_len) nor the llama.cpp strategy (meta.n_ctx / /props) reads. Without dedicated parsing, the context window — ds4's defining feature, long contexts backed by compressed KV cache on SSD — would never be auto-detected and compaction would size against a default.

Note on branch history: the first iteration added ds4 as a dedicated provider type (descriptor + plugin + resolver + DI wiring + TUI picker row). Review concluded the capability resolver's strategy chain is the seam built for exactly this case, so the provider type was removed and replaced by a strategy. The removal and the replacement are separate commits for reviewability.

Changes

  • Ds4BackendStrategy in the openai-compatible resolver chain (vLLM → ds4 → llama.cpp → generic)
    • Matches on owned_by: "ds4.c" — exact vendor marker verified against ds4_server.c, mirroring the vLLM owned_by check
    • Reads context_length, falling back to top_provider.context_length
    • Reports text-in/text-out modalities (ds4 is text-only, and its model ids are not HuggingFace ids, so downstream resolvers have nothing to fill in)
    • Ordered before llama.cpp so a proxy answering /props in front of ds4 cannot steal the match
  • Descriptor probe fallback: OpenAiCompatibleDescriptor.TryReadContextWindow now also reads the OpenRouter-shaped fields, so the model picker shows the context window for any backend emitting them
  • Discoverability: picker row display name is now llama.cpp / vLLM / ds4
  • Docs & skills: README, SPEC-008, docs/spec/configuration.md example (Type: "openai-compatible", endpoint http://127.0.0.1:8000), netclaw-operations skill row updated + version bump per the System Skills Sync Rule

Unrelated CI fix riding along

  • Transitive pin MessagePack 2.5.301 (GHSA-hv8m-jj95-wg3x, pulled at 2.5.192 by Aspire 13.4.x in the samples). The advisory was published mid-PR and breaks restore on every branch under warnings-as-errors — worth cherry-picking to dev independently of this PR.

Testing

  • Ds4BackendStrategyTests: match on owned_by, non-match for other vendors/missing model, top-level and nested context fields, missing-context → null
  • Resolver dispatch test: ds4 shape wins over llama.cpp even with a /props response present
  • Descriptor probe theory rows for both OpenRouter-shaped fields
  • Build clean (0 errors / 0 warnings), Netclaw.Daemon.Tests 698/698, Netclaw.Cli.Tests 790/790, dotnet slopwatch analyze 0 issues
  • Smoke: provider-add tape green (SMOKE_RID=osx-arm64). init-wizard tape fails only on a host-environment leak (the wizard detects the developer machine's ~/.claude/skills and shows an extra External Skills screen) — unrelated to this change; provider/endpoint/model steps pass.

Usage

# ds4-server running on its default port
netclaw provider add ds4 openai-compatible --endpoint http://127.0.0.1:8000
# model ids: deepseek-v4-flash / deepseek-v4-pro — context window auto-detected

…provider

The `ds4` DeepSeek V4 local engine uses an OpenAI-compatible API but emits
OpenRouter-shaped model metadata (e.g., `context_length` fields), which
requires a dedicated `IProviderDescriptor` and `IModelCapabilityResolver`
to correctly detect capabilities.

This change introduces `ds4` as a first-class LLM provider, enabling
Netclaw to leverage the local DeepSeek V4 engine with accurate context
window detection and chat client integration. It includes:
- `ds4` provider descriptor with default endpoint and auth.
- Capability resolver for `ds4`'s model listing.
- CLI display and daemon service integration.
- Documentation in `SKILL.md`.
…provider

The `ds4` DeepSeek V4 local engine uses an OpenAI-compatible API but emits
OpenRouter-shaped model metadata (e.g., `context_length` fields), which
requires a dedicated `IProviderDescriptor` and `IModelCapabilityResolver`
to correctly detect capabilities.

This change introduces `ds4` as a first-class LLM provider, enabling
Netclaw to leverage the local DeepSeek V4 engine with accurate context
window detection and chat client integration. It includes:
- `ds4` provider configuration with a default local endpoint.
- Capability resolver integration for `ds4`'s model listing in the CLI doctor.
- Updated documentation in the configuration spec, provider abstraction spec, and `README.md`.
Adding the ds4 provider type inserts a "DwarfStar (ds4)" row between
anthropic and github-copilot in the alphabetical-by-TypeKey provider
list. The init-wizard, init-wizard-reverse-proxy, provider-add, and
wizard-screens tapes navigated to Ollama with `Down 2` (ollama at
index 2); it now sits at index 3, so they landed on github-copilot
(OAuth, no endpoint step) and timed out waiting for the endpoint
screen. Bump those to `Down 3`.

Also refresh the provider-manager-empty screenshot baseline (captured
from CI) to include the new ds4 row.
@Aaronontheweb

Copy link
Copy Markdown
Collaborator

The ds4 plugin uses the same chat client, same endpoints, same auth, and same tool calling as openai-compatible — the only difference is which JSON field name holds the context window in the /v1/models response. Can this just be a new backend strategy on the existing openai-compatible provider instead of a whole new provider type?

ds4 duplicates the openai-compatible provider end to end: same chat client
(OpenAiCompatibleChatClient), same endpoints, same EndpointOnlyAuth, same
native tool calling. The only real difference — which JSON field of
/v1/models holds the context window — belongs in the capability resolver's
backend strategy chain, not in a parallel provider type with its own
descriptor, plugin, DI wiring, doctor probe, and TUI picker row.

Removes the Ds4 descriptor/resolver/plugin, their registrations, the
doctor wiring, and reverts the TUI picker row and smoke tapes to dev.
The replacement Ds4BackendStrategy lands in the follow-up commit.
…end strategy

Adds Ds4BackendStrategy to the openai-compatible capability resolver chain.
ds4 is recognized by owned_by "ds4.c" on the /v1/models entry (mirroring
the vLLM owned_by marker) and its OpenRouter-shaped context window is read
from context_length / top_provider.context_length. Ordered before llama.cpp
so a proxy answering /props in front of ds4 cannot steal the match.
Modalities are reported Text/Text: ds4 serves text-only DeepSeek V4 models
and its model ids are not HuggingFace ids, so downstream resolvers have
nothing to fill in.

The descriptor probe gains the same context_length fallback so the model
picker shows the context window for any OpenRouter-shaped backend.

ds4 setup now goes through the openai-compatible provider type
(endpoint http://127.0.0.1:8000); docs and the netclaw-operations skill
updated accordingly.
Users scanning the provider picker had no signal that DwarfStar (ds4) is
supported after its dedicated provider type was folded into
openai-compatible.
Aspire 13.4.x pulls MessagePack 2.5.192 into the Demo.AppHost samples; the
newly published advisory (LZ4 decompression AccessViolationException on bad
input) trips NU1903 under warnings-as-errors and breaks restore in CI.
Transitive pinning is already enabled, so a central PackageVersion entry on
the first patched release is enough. Remove once the Aspire line ships a
patched MessagePack.
@GiuseppePatane GiuseppePatane changed the title feat(providers): add DwarfStar (ds4) DeepSeek V4 local engine as LLM provider feat(providers): support DwarfStar (ds4) via openai-compatible backend strategy Jun 12, 2026
@GiuseppePatane

Copy link
Copy Markdown
Contributor Author

The ds4 plugin uses the same chat client, same endpoints, same auth, and same tool calling as openai-compatible — the only difference is which JSON field name holds the context window in the /v1/models response. Can this just be a new backend strategy on the existing openai-compatible provider instead of a whole new provider type?

Yes i agree. Removed my previous implementation and integrated ds4 as a strategy within the existing OpenAI-compatible provider. The only addition is a visual distinction, where ds4 is listed among the compatible providers in the UI.

GiuseppePatane and others added 5 commits June 23, 2026 12:48
…rsion

The ds4 provider PR introduced a duplicate MessagePack package version:
2.5.301 (correct, Aspire pin) alongside 3.1.7 (incorrect, stale entry
left behind during reorganization).

NuGet treats duplicate PackageVersion items as errors, blocking CI.
Removing the stale 3.1.7 entry and its comment block.
@Aaronontheweb Aaronontheweb added the providers Provider integrations and capability detection across OpenAI-compatible backends. label Jun 25, 2026
@Aaronontheweb

Aaronontheweb commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Cleanup pushed

Pushed two follow-up commits to GiuseppePatane:feat/ds4-provider:

  • fe8743b5 fix(providers): clean up ds4 openai-compatible support
  • 216bb63c test(smoke): approve ds4 provider screenshots

What changed

  • Kept the MessagePack transitive pin on 3.1.7, avoiding a downgrade from dev.
  • Renamed the OpenAI-compatible display label to OpenAI-compatible (llama.cpp / vLLM / DwarfStar ds4).
  • Ordered the ds4 backend strategy before vLLM so exact owned_by: "ds4.c" metadata wins over broad max_model_len heuristics.
  • Added a regression test for ds4 model metadata that also includes max_model_len.
  • Updated TUI assertions and approved screenshot baselines for the new provider label.
  • Updated netclaw-operations skill guidance and bumped its version.
  • Synced configuration/provider docs and OpenSpec specs with the actual provider behavior.

Local validation

  • dotnet test Netclaw.slnx passed.
  • Targeted provider strategy tests passed, including the ds4 precedence regression.
  • Targeted TUI display-name tests passed.
  • dotnet slopwatch analyze --hook passed.
  • pwsh ./scripts/Add-FileHeaders.ps1 -Verify passed.
  • git diff --check passed.
  • Screenshot baseline updates compare at AE=0 against the CI-generated actual PNGs.

Full dotnet slopwatch analyze still reports only pre-existing unrelated SW004 warnings in untouched TUI tests.

Smoke notes

Native smoke ran locally after installing smoke-only tools and setting VHS_NO_SANDBOX=1. All scenarios and all tapes passed except init-wizard; that remaining failure is the existing model-selection mismatch where the final chat footer renders Ready | all-minilm:latest while the tape expects Ready | qwen2:0.5b, after config has already been written successfully.

CI status

All PR checks are passing on 216bb63c, including Screenshot Regression and Native Smoke on Linux and macOS.

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

big change I made here was making DS4 stand out in the TUI

@Aaronontheweb Aaronontheweb merged commit 3212fbb into netclaw-dev:dev Jun 25, 2026
15 checks passed
@Aaronontheweb Aaronontheweb mentioned this pull request Jun 25, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

providers Provider integrations and capability detection across OpenAI-compatible backends.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants