Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/update-laguna-limits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"opencode-provider-poolside": patch
---

Update Laguna model limits to the verified Poolside metadata and preserve those limits when live discovery reports stale values.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- **Live model discovery** — fetches the current model catalog from the Poolside API at startup, with sensible Laguna fallbacks when the API is unreachable.
- **OpenAI-compatible** — uses [`@ai-sdk/openai-compatible`](https://ai-sdk.dev/providers/open-source-providers/openai-compatible) under the hood, so streaming, tool calls, and JSON mode work out of the box.
- **API key management** — built-in auth hook for storing and loading your Poolside API key via OpenCode's `/connect` command.
- **Reasoning support** — Laguna models expose `none`, `minimal`, `low`, `medium`, `high`, and `xhigh` reasoning effort levels through OpenCode's variant picker.
- **Reasoning support** — Laguna M.1 and XS 2.1 expose `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`; Laguna S 2.1 exposes its supported `none` and `xhigh` modes through OpenCode's variant picker.
- **Zero config** — add the plugin to your `opencode.json` and you're ready to go.

## Quick start
Expand Down Expand Up @@ -73,11 +73,13 @@ Or set a default model in your config:

## Available models

| Model | Description | Reasoning |
|---|---|---|
| `poolside/laguna-m.1` | Poolside's most capable coding agent model | ✅ |
| `poolside/laguna-xs-2.1` | Second-generation coding agent model (XS) | ✅ |
| `poolside/laguna-s-2.1` | Second-generation coding agent model (S) | ✅ |
| Model | Context | Max output | Reasoning |
|---|---:|---:|---|
| `poolside/laguna-m.1` | 262,144 | 32,768 | `none`–`xhigh` |
| `poolside/laguna-xs-2.1` | 262,144 | 32,768 | `none`–`xhigh` |
| `poolside/laguna-s-2.1` | 1,048,576 | 131,072 | `none`, `xhigh` |

The static catalog uses these verified Poolside limits. Authenticated model discovery enriches the catalog with live capabilities, while preserving these limits when the Poolside `/models` response is incomplete or stale.

## Configuration

Expand Down Expand Up @@ -125,7 +127,7 @@ export POOLSIDE_API_KEY="your-api-key"
## How it works

1. **Config hook** — On startup, the plugin registers the `poolside` provider with `@ai-sdk/openai-compatible`, sets the base URL and environment variable, and populates the model catalog.
2. **Model discovery** — If `POOLSIDE_API_KEY` is available, the plugin fetches the live model list from `https://inference.poolside.ai/v1/models`. If the API is unreachable, it falls back to a static catalog of known Laguna models.
2. **Model discovery** — If `POOLSIDE_API_KEY` is available, the plugin fetches the live model list from `https://inference.poolside.ai/v1/models`. If the API is unreachable, it falls back to a static catalog of known Laguna models; verified limits remain in place if the endpoint reports stale values.
3. **Model aliases** — OpenCode exposes models as `poolside/laguna-*` while sending Poolside's required `poolside/laguna-*` upstream IDs without duplicating the provider prefix.
4. **Auth hook** — The plugin provides an API key auth method so you can manage your key with OpenCode's `/connect poolside` command.

Expand Down
6 changes: 4 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ export const MAX_ONLY_VARIANTS = {
const ZERO_COST = { input: 0, output: 0, cache_read: 0, cache_write: 0 } as const;

/**
* Default context window and output token limits for Poolside models.
* Default context window and output token limits for Laguna M.1 and XS 2.1.
*/
const DEFAULT_CONTEXT_WINDOW = 262_144;
const DEFAULT_MAX_TOKENS = 32_768;
const LAGUNA_S_CONTEXT_WINDOW = 1_048_576;
const LAGUNA_S_MAX_TOKENS = 131_072;

/**
* Current Poolside Platform models, available before authenticated refresh.
Expand Down Expand Up @@ -130,7 +132,7 @@ export const FALLBACK_MODELS = [
reasoning: true,
tool_call: true,
cost: ZERO_COST,
limit: { context: DEFAULT_CONTEXT_WINDOW, output: DEFAULT_MAX_TOKENS },
limit: { context: LAGUNA_S_CONTEXT_WINDOW, output: LAGUNA_S_MAX_TOKENS },
variants: MAX_ONLY_VARIANTS,
},
];
17 changes: 9 additions & 8 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,16 @@ export function parseModelsResponse(
fallback?.cost.cache_write ?? 0
),
},
// The verified Laguna catalog is authoritative for known models.
// Poolside's /models endpoint can expose stale limits for Laguna S
// 2.1, so live metadata must not shrink its advertised capacity.
limit: {
context: positiveNumber(
item.context_length,
fallback?.limit.context ?? 262_144
),
output: positiveNumber(
item.max_completion_tokens,
fallback?.limit.output ?? 32_768
),
context:
fallback?.limit.context ??
positiveNumber(item.context_length, 262_144),
output:
fallback?.limit.output ??
positiveNumber(item.max_completion_tokens, 32_768),
},
variants,
modalities: {
Expand Down
29 changes: 29 additions & 0 deletions test/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,24 @@ describe("parseModelsResponse", () => {
assert.equal(result[1]!.id, "poolside/laguna-xs-2.1");
assert.equal(result[2]!.id, "poolside/laguna-s-2.1");
});

test("preserves verified Laguna limits when the API reports stale values", () => {
const payload: PoolsideModelsResponse = {
data: [
{
id: "poolside/laguna-s-2.1",
context_length: 262_144,
max_completion_tokens: 32_768,
},
],
};
const result = parseModelsResponse(payload);

assert.deepEqual(result[0]!.limit, {
context: 1_048_576,
output: 131_072,
});
});
});

describe("modelsToConfigMap", () => {
Expand Down Expand Up @@ -242,4 +260,15 @@ describe("FALLBACK_MODELS", () => {
assert.ok(Object.keys(model.variants).length > 0);
}
});

test("uses the verified context and output limits", () => {
const limits = Object.fromEntries(
FALLBACK_MODELS.map((model) => [model.id, model.limit])
);
assert.deepEqual(limits, {
"poolside/laguna-m.1": { context: 262_144, output: 32_768 },
"poolside/laguna-xs-2.1": { context: 262_144, output: 32_768 },
"poolside/laguna-s-2.1": { context: 1_048_576, output: 131_072 },
});
});
});