Skip to content
dsh.fish
Bundle

dsh-multiprovider

Provider-neutral multi-account scheduling, affinity, health, and Settings UI for DeepSeek Harness

Source
monotykamary
stars
1 stars
License
MIT
Updated
Updated 13 days ago

Readme

<div align="center">

# 🔀 dsh-multiprovider

**Provider-neutral multi-account scheduling for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)**

_One provider identity, many credentials, explicit leases, and operator-visible health._

[![checks](https://img.shields.io/github/actions/workflow/status/monotykamary/dsh-multiprovider/check.yml?branch=main&style=for-the-badge&label=checks)](https://github.com/monotykamary/dsh-multiprovider/actions/workflows/check.yml)
[![npm](https://img.shields.io/npm/v/dsh-multiprovider?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/dsh-multiprovider)
[![DeepSeek Harness](https://img.shields.io/badge/DeepSeek-Harness-4D6BFE?style=for-the-badge)](https://github.com/deepseek-ai/deepseek-harness)
[![license](https://img.shields.io/badge/license-MIT-f4c430?style=for-the-badge)](LICENSE)

</div>

---

**dsh-multiprovider** lets concrete provider plugins expose several OAuth, API-key, service-account, or custom credentials behind one stable provider/model identity. It owns account selection, affinity, health, cooldowns, and operator preferences. Provider plugins continue to own authentication, credential storage, transport, and provider-specific error interpretation.

The package is a native Cordis service and DSH bundle, not a second model router. Callers lease one account for a complete operation, settle the outcome exactly once, and explicitly reacquire when provider semantics make failover safe.

## Why multiprovider?

| | Capability | What it unlocks |
| :-: | --- | --- |
| 🔁 | **Deterministic selection** | Round-robin, weighted, least-in-flight, and priority policies behind one provider route. |
| 📌 | **Session affinity** | Stable account reuse for related requests while the selected account remains eligible. |
| 🩺 | **Visible health** | Cooldowns, failure classes, and in-flight counts without exposing credential material. |
| 🔐 | **Provider-owned secrets** | Opaque credential references stay on the host and never enter browser snapshots. |
| 🎛️ | **Live preferences** | Operators enable, weight, prioritize, and reset accounts from DSH Settings. |
| 🧩 | **Native composition** | Cordis lifecycle, DSH settings, and the Web host remain the only runtime authorities. |

## How it fits

```mermaid
flowchart LR
  Request[Provider operation] --> Pool[ctx.multiprovider]
  Pool --> Policy[Selection + affinity]
  Policy --> Lease[Account lease]
  Lease --> Provider[Concrete provider plugin]
  Provider --> Outcome[Success / failure / cancel]
  Outcome --> Health[Health + cooldown]
  Health --> Pool
  Pool --> Settings[Accounts settings]
```

The logical provider identity stays unchanged. Internal account IDs and credential references are not model routes and must never be persisted as session provider IDs.

## What it provides

- Dynamic provider and account registration through `ctx.multiprovider`
- Opaque, provider-owned credential references that never enter browser snapshots
- Idempotent account leases with in-flight accounting
- Round-robin, smooth weighted round-robin, least-in-flight, and priority policies
- Optional session/workload affinity
- Per-attempt account exclusions for explicit failover loops
- Normalized rate-limit, quota, authentication, transient, and fatal failure health
- Configurable cooldowns with transient exponential backoff
- Live, durable enable/weight/priority/policy preferences through DSH settings
- A dedicated **Accounts** section in DSH's Settings modal
- Same-origin, loopback-only, secret-free Settings endpoints

The logical provider identity stays unchanged. Internal account IDs and credential references are not model routes and should never be persisted as session provider IDs.

## Install

Requirements: Node.js `^22.19.0 || >=24`, pnpm 11 for this checkout, and DeepSeek Harness `^0.1.1`.

The package is included in the tested DSH distribution. To add it to another profile explicitly:

```sh
pnpm dlx @monotykamary/dsh@latest plugin --profile web add dsh-multiprovider
```

For local development, build this checkout and add its absolute path:

```sh
pnpm install --frozen-lockfile
pnpm run check
pnpm dsh plugin --profile web add link:/absolute/path/to/dsh-multiprovider
```

The bundle patch installs the `multiprovider` service. Concrete provider integrations inject that service and retain ownership of enrollment, refresh, storage, and transport.

## Provider integration

### 1. Register a provider inventory

Credential references are opaque to this package. They can be DSH `CredentialRef` values, credential record keys, provider-owned file handles, or another non-secret locator.

```ts
import type { Context } from '@monotykamary/cordis'
import type {} from 'dsh-multiprovider'

export function installAccounts(ctx: Context) {
  ctx.inject(['multiprovider'], (mctx) => {
    mctx.effect(() => mctx.multiprovider.registerProvider({
      id: 'anthropic',
      label: 'Anthropic',
      managementHint: 'Add and remove keys in the Anthropic provider settings.',
      accounts: async () => [
        {
          id: 'work',
          label: 'Work key',
          authKind: 'api-key',
          credentialRef: { kind: 'record', key: 'anthropic/work' },
          weight: 3,
          metadata: { organization: 'Work' },
        },
        {
          id: 'personal',
          label: 'Personal key',
          authKind: 'api-key',
          credentialRef: { kind: 'record', key: 'anthropic/personal' },
        },
      ],
      classifyFailure: (error) => {
        const status = (error as { status?: number }).status
        if (status === 429) return { kind: 'rate-limit', retryable: true }
        if (status === 401 || status === 403) return { kind: 'auth', retryable: true }
        if (status !== undefined && status >= 500) return { kind: 'transient', retryable: true }
        return { kind: 'fatal', retryable: false }
      },
    }), 'anthropic: multiprovider accounts')
  })
}
```

Do not put API keys, access tokens, refresh tokens, or raw provider diagnostics in `metadata`, labels, account IDs, or credential references. A reference must be a locator, not the secret itself.

### 2. Lease an account for the complete operation

```ts
const attempted = new Set<string>()

for (;;) {
  const lease = await ctx.multiprovider.acquire<MyCredentialRef>({
    providerId: 'anthropic',
    affinityKey: session.id,
    excludeAccountIds: attempted,
  })
  attempted.add(lease.accountId)

  try {
    const credential = await resolveCredential(lease.credentialRef)
    const result = await runCompleteProviderOperation(credential)
    lease.release({ status: 'success' })
    return result
  } catch (error) {
    const disposition = lease.release({ status: 'failure', error })
    if (!disposition?.retryable || !isSafeToReplay(error)) throw error
    // Reacquiring with excludeAccountIds selects another eligible account.
  }
}
```

For streaming LLM requests, hold the lease until the stream has completed or failed—not merely until an async iterable is created. Never replay automatically after user-visible output has started unless the provider integration can prove replay is safe.

Related search, image, usage, and tool operations should pass the same session/workload affinity key when one is available.

## Settings UI

The browser plugin contributes a standalone `settings.section` named **Accounts**. It shows:

- registered provider pools and account auth kinds
- health, cooldown, failure, and in-flight status
- pool selection policy and session affinity
- per-account enablement, weight, and priority
- provider-supplied account-management guidance
- an operator action to clear automatic cooldown/failure health

Secrets are never returned by `GET /plugins/dsh-multiprovider/state`. Mutation endpoints enforce loopback and same-origin checks, capped JSON bodies, method allowlists, no-store responses, and strict field validation.

## Ownership boundary

| Layer | Owns |
| --- | --- |
| `dsh-multiprovider` | Account pools, leases, selection, affinity, health, cooldowns, failover primitives, operator policy UI |
| Provider plugin | OAuth/API-key enrollment, credential persistence and refresh, transport, complete stream lifetime, error classification |
| DSH core | Request/session lifecycle, credential and authorization services, provider/model identity |

## Current scope

Health, leases, and affinity are process-local. Preferences are durable through DSH settings. This version does not queue for account capacity, impose per-account concurrency limits, or perform hidden retries. Those are deliberate future extensions; explicit reacquisition keeps replay safety in the provider integration where protocol semantics are known.

## Development and release

```sh
pnpm install --frozen-lockfile
pnpm run check
pnpm pack --dry-run
```

`pnpm run check` type-checks the host and browser faces, runs the complete Vitest suite, and builds the ESM service plus browser client bundle. `prepack` repeats that check before npm creates a release payload.

## Relationship to the other projects

- [**DeepSeek Harness**](https://github.com/deepseek-ai/deepseek-harness) owns provider/model identity, request and session lifecycle, settings persistence, the Web host, and client composition.
- [**dsh-codex**](https://github.com/monotykamary/dsh-codex) is a concrete integration: it leases Codex accounts while retaining OAuth, token refresh, and response-stream ownership.
- [**dsh-fabric**](https://github.com/monotykamary/dsh-fabric), [**dsh-fovea**](https://github.com/monotykamary/dsh-fovea), [**dsh-factory**](https://github.com/monotykamary/dsh-factory), and [**dsh-tool-repair**](https://github.com/monotykamary/dsh-tool-repair) are the other external bundles pinned by the tested DSH distribution.

## License

MIT © [Tom Nguyen](https://github.com/monotykamary).

Install

dsh plugin --profile web add github:monotykamary/dsh-multiprovider

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.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source