# SDK reference

Import SDK functions and types from `@canopyx/provider`:

| Import                       | Contents                                                  | Runs in        |
| ---------------------------- | --------------------------------------------------------- | -------------- |
| `@canopyx/provider`          | Configuration, connector, engine adapters, strategies     | Bun            |
| `@canopyx/provider/protocol` | Wire schemas and types                                    | Bun            |
| `@canopyx/provider/strategy` | `StrategySpec` schema, `evaluateStrategy` and trace types | Bun or browser |
| `@canopyx/provider/template` | `renderProviderConfig`, which writes `provider.ts`        | Bun or browser |

## `defineProvider`

```ts
function defineProvider<M extends EngineSignals>(
  config: ProviderConfig<M>,
): ProviderConfig<M> & { readonly url: string };
```

Fills in the default Canopy WebSocket URL and preserves the engine metric type in callbacks. It does not connect or check the engine. Export this configuration as the default export when using the CLI.

## Starter generation

`renderProviderConfig({ adapter, baseUrl, offeringId, providerUrl, spec })` returns editable
TypeScript. `spec` must pass `StrategySpec`, including an explicit `admission.maxConcurrent`.

## Price interpolation

`interpolatePrice({ floor, ceiling, position, span })` returns a decimal rate between two prices.
`position` and `span` are safe integers with `0 <= position <= span` and `span > 0`.
`multiplyPrice({ price, multiplierBps, floor? })` scales a rate by 0 to 1,000,000 basis points,
with an optional minimum. `dividePrice({ price, divisorBps })` recovers a base rate using a
positive divisor of at most 1,000,000 basis points. These helpers use integer microdollars,
round up, and cap the result at `999999.999999`.

## `evaluateStrategy`

```ts
function evaluateStrategy(input: {
  strategy: StrategySpec;
  context: StrategyContext;
  rfq?: { estimate: RfqRequest["estimate"] };
  now: number;
  utilisation?: number;
}): StrategyEvaluation;
```

The pure request evaluator for starter policies. Generated starter code implements the same rules; locally edited callbacks can differ. `context` takes `metrics` (any of `runningRequests`, `waitingRequests`, `kvCacheUsageRatio`, `slotUsageRatio`, or `undefined` when unavailable), `activeRequests`, `pendingQuotes` and `idleCachedSessions`; a `PolicyContext` satisfies it. `now` is epoch milliseconds. `utilisation`, from 0 to 1, replaces computed utilisation for pricing only.

Returns `decision` (`{ accept: true, terms }` or `{ accept: false }`), `declineReason`, `standing` terms, `utilisationBps`, `loadBps` and `trace`, a list of `{ step, outcome, message, values }` explaining each step. It reads no clock and uses no randomness.

## `scalePrice`

```ts
function scalePrice(options: { price: string; multiplier: number }): string;
```

Multiplies a decimal USD rate per million tokens using exact integer arithmetic. The price must match the [price format](/providers/policies#price-format), and `multiplier` must be a positive safe integer. Returns six fractional digits. Throws for invalid input or a result above `999999.999999`.

```ts
scalePrice({ price: "0.35", multiplier: 3 }); // "1.050000"
```

## `engineApiKey`

```ts
function engineApiKey(): string | undefined;
```

Returns the engine API key from the systemd credential `canopy-engine-key`, the file named by `CANOPY_ENGINE_API_KEY_FILE`, or `CANOPY_ENGINE_API_KEY`, in that order. Use it for an engine adapter's `apiKey` so the key stays out of `provider.ts`.

## `startProvider`

```ts
function startProvider<M extends EngineSignals>(config: ProviderConfig<M>): Promise<ProviderClient>;
```

Connects, resolves the engine model ID from the saved Canopy mapping, validates the engine, and restores session state. A live session resolves after Canopy acknowledges the first standing-price update; a staging session resolves after Canopy acknowledges the first connector report. Rejects when startup fails. After startup, the client polls metrics and reconnects automatically.

`ProviderClient.close(): Promise<void>` shuts down the connector and its runtime. Repeated calls share the same promise. `ProviderClient.mode()` returns `"staging"` or `"live"`. See [Lifecycle and caching](/providers/lifecycle) for shutdown responsibilities.

## `checkEngine`

```ts
function checkEngine<M extends EngineSignals>(input: {
  engine: EngineAdapter<M>;
  upstreamModelId?: string;
  timeoutMs?: number;
}): Promise<EngineCheck<M>>;
```

Checks health, discovers a single model, and validates metrics without connecting to Canopy. For a multi-model engine, supply `upstreamModelId` for this diagnostic. `timeoutMs` defaults to `3000`.

Returns `{ model, models, metrics, collectedAt, capacity, version }`. `models` lists every engine model ID; `capacity` (`maxSequences`, `kvCacheTokens`, `slots`) and `version` appear where the engine reports them. A failed check rejects with an error whose `stage` is `health`, `models` or `metrics`.

## Engine adapters

```ts
function vllm(
  options: EngineOptions & { labels?: Readonly<Record<string, string>> },
): EngineAdapter<VllmMetrics>;
function llamaCpp(
  options: EngineOptions & { router?: boolean; slots?: number },
): EngineAdapter<LlamaCppMetrics>;
function sglang(
  options: EngineOptions & { labels?: Readonly<Record<string, string>> },
): EngineAdapter<SglangMetrics>;
function ollama(options: EngineOptions): EngineAdapter<LimitedMetrics>;
function mlx(options: EngineOptions): EngineAdapter<LimitedMetrics>;
function tabbyApi(options: EngineOptions): EngineAdapter<LimitedMetrics>;
```

`EngineOptions` requires `baseUrl`; `apiKey` and `metricsUrl` are optional. See [Engines and metrics](/providers/engines) for supported versions and fields.

`EngineAdapter<M>` contains `kind`, `support` (`"supported"` or `"limited"`), `options`, and optionally `healthPath`, `parse`, `metricsQuery` and `discover`. Limited adapters have no `parse` and report empty metrics. `M` extends `EngineSignals`, whose fields are all optional; `EngineLoad` requires `runningRequests` and `waitingRequests`.

## `ProviderConfig`

| Property              | Required | Description                                                                                    |
| --------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `url`                 | No       | Defaults to `wss://api.canopyx.ai/providers/ws`; override for local or self-hosted Canopy      |
| `offeringId`          | Yes      | Offering ID from your Canopy setup, such as `alice/qwen3-32b-fp8`                              |
| `engine`              | Yes      | Engine adapter                                                                                 |
| `standingPrice`       | Yes      | `(context: PolicyContext<M>) => QuoteTerms \| Promise<QuoteTerms>`                             |
| `quote`               | Yes      | `(context: PolicyContext<M> & { rfq: RfqRequest }) => QuoteDecision \| Promise<QuoteDecision>` |
| `token`               | No       | Connector credential. See [Credentials](/providers/installer#credentials) for the defaults     |
| `host`                | No       | Name shown in the connector console; defaults to the machine's host name                       |
| `verbose`             | No       | Log metrics, prices, RFQs and quotes as they change                                            |
| `cleanCache`          | No       | Reset cache accounting after physical cache loss; defaults to `false`                          |
| `onAward`             | No       | Award observer with an abort signal                                                            |
| `onQuoteRelease`      | No       | Pending quote release observer                                                                 |
| `onExecutionRelease`  | No       | Execution release observer                                                                     |
| `onCommitment`        | No       | Commitment update and restoration observer                                                     |
| `onCommitmentRelease` | No       | Commitment release observer                                                                    |
| `onError`             | No       | Synchronous `(error: Error) => void` reporter                                                  |

`PolicyContext<M>` contains `metrics`, `collectedAt`, `activeRequests`, `idleCachedSessions` and `pendingQuotes`. The [custom pricing guide](/providers/policies) describes each field. The [lifecycle guide](/providers/lifecycle#observe-lifecycle-events) describes callback arguments.

### Timing options

All timing options are positive integers in milliseconds.

| Property                | Default | Description                                                                                                    |
| ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
| `pollIntervalMs`        | `1000`  | Delay between health and metrics collection cycles                                                             |
| `maxMetricsAgeMs`       | `5000`  | Maximum usable metrics age                                                                                     |
| `requestTimeoutMs`      | `3000`  | Collection and ordinary callback timeout                                                                       |
| `connectTimeoutMs`      | `10000` | Startup wait including mapping resolution, engine validation, state restoration, and the first acknowledgement |
| `priceUpdateIntervalMs` | `30000` | Delay between standing-price publications                                                                      |

RFQ callbacks use the remaining time until `rfq.deadline`, rather than the ordinary callback timeout.

## Exported types

The root package exports:

* Configuration and pricing: `ProviderConfig`, `PolicyContext`, `QuoteDecision`.
* Strategies: `StrategySpec` (schema and type), `EvaluateStrategyInput`, `StrategyContext`, `StrategyDecision`, `StrategyDeclineReason`, `StrategyEvaluation`, `StrategyTraceStep`.
* Engines: `EngineSignals`, `EngineLoad`, `EngineOptions`, `EngineAdapter`, `EngineCapacity`, `VllmMetrics`, `LlamaCppMetrics`, `SglangMetrics`, `LimitedMetrics`, `EngineCheck`.
* Client: `ProviderClient`, `SDK_VERSION`.
* Protocol values: `QuoteTerms`, `RfqRequest`, `QuoteAward`, `QuoteRelease`, `ExecutionRelease`, `Commitment`, `CommitmentRelease`.

The `/protocol` import exposes runtime schemas as well as types, including `Envelope`, `SessionState`, `Welcome`, `SessionMode`, `SessionModeUpdate`, `ConnectorReport`, `PriceUpdatePayload`, `RfqQuotePayload` and `EnrollmentCode`. It also exports `PROTOCOL_VERSION`, currently `1`. See the [WebSocket reference](/providers/protocol) for the full message contract.

## CLI

The installer puts the CLI at `~/.canopy-provider/bin/canopy-provider`; in your own project, run it with `bunx canopy-provider`. Commands without a `./provider.ts` path use the install in `$CANOPY_PROVIDER_HOME`, or `~/.canopy-provider`.

| Command                                                                                                            | Purpose                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| `enroll CODE [--app-url URL] [--name NAME] [--dir DIR] [--offering ID] [--engine KIND] [--engine-url URL] [--yes]` | Exchange an enrollment code and write an install                                                                 |
| `start [./provider.ts] [--dir DIR] [--verbose]`                                                                    | Run the connector until SIGINT or SIGTERM                                                                        |
| `doctor [./provider.ts] [--dir DIR] [--app-url URL]`                                                               | Check the credential and account status, clock, offering mapping, engine, WebSocket and Canopy's endpoint checks |
| `check [./provider.ts] [--dir DIR] [--model upstream-id]`                                                          | Check the engine without contacting Canopy                                                                       |
| `service install --system\|--user\|--launchd [--dir DIR]`                                                          | Install and start a service. `service uninstall` and `service status` manage it                                  |
| `init [./provider.ts] [--app-url URL]`                                                                             | Write `provider.ts` for a project of your own, using a connector token                                           |
| `uninstall [--dir DIR] [--app-url URL]`                                                                            | Remove the service and revoke the connector credential                                                           |

`init` reads `CANOPY_PROVIDER_TOKEN` or prompts for a token from **Create token** on the Install page. It confirms the engine serves the registered model ID, refuses to overwrite an existing file, and saves the token in an ignored `.env`. `check` and `start` load the file's default-exported configuration. See [Install the connector](/providers/installer) for the installed layout and services.
