Skip to content
dsh.fish
Bundle

dsh-plugin-model-config

Full model-configuration editor for the DeepSeek Harness web Settings > Models page: every field the deployment's model-config schema declares, not just the handful the shipped UI exposes.

Source
liutian11451-png
License
MIT
Updated
Updated 2 hours ago

Readme

# dsh-plugin-model-config

English | [中文](README.zh.md)

> **AI-generated code.** This package was written by an AI agent (DeepSeek Harness, model `deepseek-flash`) in a human-directed session, and published by the human who ran it. Review it before you install it, and especially before you allow its `prepare` script to run on your machine. See [AI disclosure](#ai-disclosure) for exactly what has and has not been verified.

A model-configuration control for the DeepSeek Harness web **Settings → Models** page: inside each provider's own card, one row per setting, ready to edit.

## What it does

The shipped Models card already shows a provider's route, credential and model catalog. What it has no control for is the rest of the provider's configuration — for the DeepSeek adapter that is thinking mode, reasoning effort, the context-window and token defaults, the four image budgets, the four file and quota limits, and the timeouts.

This plugin adds those settings **as rows inside the provider's own card**: each row is the setting's name on the left and its control on the right, so the block reads like the settings form it sits in.

| Schema node | Control |
|---|---|
| `number` | number box (carrying the schema's `min`, `max`, `step`), e.g. context window |
| `boolean` | Yes/No switch |
| union of literals | select |
| `string` | text box (a credential reference is labelled as one) |

Structural values are left to the card that already owns them: the model catalog, a nested retry policy, and anything the schema declares as a dict or an unmodelled node are document-shaped configuration, not settings rows, and the shipped card already edits the catalog.

Nothing about the setting list is hardcoded. The namespaces come from the deployment's own `llm.listConfigurableProviders()` and the settings come from each namespace's serialized schema, so **a setting added to an adapter — or a whole adapter this plugin has never seen — appears as a row with no change here**.

## Install

```sh
dsh plugin --profile web add dsh-plugin-model-config
```

Substitute your profile name for `web` if you run a different one. The package declares `dsh.bundle`, so `dsh plugin` registers its patch layer in the profile automatically. Restart `dsh web` and reload the page: the build payload is composed at boot, so a reload alone is not enough the first time.

### From a GitHub checkout

```sh
dsh plugin --profile web add github:liutian11451-png/dsh-plugin-model-config
```

A git install fetches sources, not built artifacts, so the package ships a `prepare` script that builds `lib/` from `src/` with plain tsdown — no sibling repository, no type checking. pnpm ≥10 refuses to run a git dependency's `prepare` until you allow it, so the first `add` fails with `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` and prints the exact key to add. For a git install that key names the resolved tarball, not the bare package name:

```yaml
allowBuilds:
  dsh-plugin-model-config@https://codeload.github.com/liutian11451-png/dsh-plugin-model-config/tar.gz/<commit-sha>: true
```

Copy the line pnpm prints verbatim — it carries the commit the install resolved to — then re-run the `add`. Treat that allowance as permission to execute this package's code on your machine at install time, and pin a commit (`github:liutian11451-png/dsh-plugin-model-config#<sha>`) so a later push cannot silently change what runs.

One caveat when moving an existing install: if the plugin was previously installed from a **local directory**, remove that first. A leftover link in the profile's `node_modules` collides with the incoming git install and pnpm fails with an `EPERM` symlink error during import.

To avoid asking users for the allowance altogether, publish to npm or ship a tarball from `pnpm pack` — neither needs build permission.

### Requirements

A settings provider must be mounted (`@deepseek-ai/dsh-settings-file` is part of the standard preset — it is what gives `settings.describe` anything to answer). A deployment whose settings provider is read-only renders the controls disabled and says so.

## How a write works

Each row reads the namespace's **resolved** value (schema defaults → composition base → user layer) for display, and writes only the **user layer**, one path at a time, through `settings.mutate(ns, ops, expectedRevision)`. A row is shown as overridden exactly when the raw user section carries that path — presence, not equality, is the harness's own rule — so **resetting a setting is an `unset`, never a write of the default back**. A write that loses a revision race is reported instead of overwriting.

## Layout

```
package.json                              # dsh.bundle + dsh.client manifests
cordis.patch.yml                          # inserts this package's loader row
src/index.ts                              # node half (inert)
src/client/index.ts                       # browser half: one keyed seat entry per namespace
src/client/fields.ts                      # the schema walker
src/client/ProviderRows.tsx               # the setting rows and the write plumbing
src/client/locales.ts                     # zh/en dictionaries, including the setting names
tsdown.config.ts                          # consumer-side build
smoke.mjs                                 # loader-contract smoke test
```

The node half is deliberately empty. The harness client module registry discovers `dsh.client` packages by walking the host Loader's rows, so the package needs one mounted row as its carrier; that is all the row is for.

The plugin registers into `settings.models.provider-card`, the seat the Models section exposes for exactly this: a card's adapter extension area, dispatched by the card that owns the namespace. That seat is **keyed and matched exactly** (`options.key` against the card's `entryKey`), so the plugin registers one entry per configurable namespace — a single entry would render nothing anywhere. No file of the DeepSeek Harness repository is modified.

## Development

```sh
pnpm install
pnpm build       # emits lib/index.js and lib/client.js
pnpm typecheck
pnpm test        # the walker contract, the rows, and dictionary coverage
node smoke.mjs   # evaluates the built browser half against a stub shell
```

## Known limitations

- **It edits the live settings document, not a file you can diff.** A change lands in the harness settings store, the same one the shipped controls write to. There is no per-change preview and no undo beyond resetting a setting.
- **Structural settings are not rows.** The model catalog, a nested retry policy, `headers`/`queryParams` maps and any unmodelled node are handled by the card that already owns them (the catalog) or not at all. A row is a scalar a person sets with one control; a document-shaped value is not.
- **A value with no dictionary entry shows its schema key.** Every setting the shipped adapters declare has a name; a third-party adapter's new setting renders with its raw key until the dictionary covers it, and the test suite fails in this repository when a fixture adds one.
- **A dict-typed provider family registers one profile per card.** `llm-pi-ai` nests profiles under `providers`; each profile is sliced out for its own card, and the profile's own schema decides which settings appear.
- **Secret-role settings are not round-tripped.** A `role('secret')` value never rides the wire, so no row can show or set it; use the credential surface the adapter documents.
- **Web profile only** — declared `platform: web`; there is no terminal-surface equivalent.

## AI disclosure

This package was **written by an AI agent**, not by a person typing the code. It was produced by DeepSeek Harness running the `deepseek-flash` model in a session directed by its human publisher ([@liutian11451-png](https://github.com/liutian11451-png)), on 2026-09-12. The human chose the feature, approved the approach, and reported the behaviour; the agent read the harness sources to establish the settings, slot, and schema contracts, then wrote the source, tests, build configuration, and documentation in this repository. The same disclosure is recorded in machine-readable form under `aiDisclosure` in `package.json`.

Treat the code accordingly: read it before you install it, and treat the `allowBuilds` permission a git install asks for as permission to run unreviewed generated code on your machine.

### What has been verified

- `pnpm test` — 34 tests: the schema walker's mapping of every node kind in both envelope shapes, the row filter, the rows themselves (control kind per schema type, resolved values, overrides, reset), the write path (op shape, revision, nested profile paths, conflict handling), and dictionary coverage against the schema fixtures
- `pnpm typecheck` — clean
- `node smoke.mjs` — loader-contract assertions against the built artifact, including that `apply` registers one **keyed** `settings.models.provider-card` entry per configurable namespace, which is the wiring the page silently depends on
- The GitHub Actions workflow in this repository runs those four steps on every push
- **The page was driven headlessly and read back** (`node drive-ui.mjs <url>`: Edge over the DevTools protocol, no dependencies). On a live deployment it confirmed the block renders inside the DeepSeek card — the `模型配置` heading, sixteen numbered settings showing their resolved values, and `thinking`/`reasoningEffort` as closed selects — and that editing one number persisted it to the settings document.

Three faults were found this way and fixed:

1. `remote.settings` and `remote.llm` were read without being declared in `inject`, so the whole loader entry failed (`cannot get property "remote.settings" without inject`).
2. The settings schema arrives as a **reference table** (`{ uid, refs }`) whose nested schemas are integer ids; the walker read those ids as nodes, so it found no fields and rendered nothing. Both shapes are now fixtures, and a test asserts they agree.
3. A union member's literal can live in `value` or in `meta.default` depending on how the union was written; reading only the first turned a closed set into a JSON editor.

### What has not been verified

- **No human line-by-line review.** The publisher verified behaviour in the running app, not the source.
- **No security audit.** Nothing here was examined for hostile or careless behaviour beyond what the tests and the described behaviour cover.
- **One machine, one platform.** Development, testing, and live use all happened on Windows with Node 24 and pnpm 11. The CI workflow runs on Linux, so the build is exercised on two platforms, but the UI has only been seen on one.
- **No clean-machine install test.** The publishing machine already had a plugin install in its profile; an install onto a profile that has never held this plugin was not exercised.
- **The boolean switch has no live counterpart on the adapters tested.** None of this deployment's provider schemas declares a boolean, so the Yes/No control is covered by a component test rather than by a rendered page.
- **The `llm-pi-ai` dict-typed layout was reasoned from the settings document.** Its slicing path is covered at the component level, but no live card for it was inspected.

## License

MIT

Install

dsh plugin --profile web add github:liutian11451-png/dsh-plugin-model-config#999b378a81fcaa689f26a1ebd6f668c23da34311

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
Source