264 lines
8.4 KiB
TypeScript
264 lines
8.4 KiB
TypeScript
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
|
export default function (pi: ExtensionAPI) {
|
|
// ── Anthropic via Langdock ──────────────────────────────────────────────
|
|
pi.registerProvider("anthropic", {
|
|
baseUrl: "https://api.langdock.com/anthropic/eu",
|
|
apiKey: "$LANGDOCK_API_KEY",
|
|
api: "anthropic-messages",
|
|
models: [
|
|
{
|
|
id: "claude-opus-4-8-default",
|
|
name: "Opus 4.8",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: 200000,
|
|
maxTokens: 32000,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: { forceAdaptiveThinking: true },
|
|
},
|
|
{
|
|
id: "claude-opus-5-default",
|
|
name: "Opus 5",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: 200000,
|
|
maxTokens: 32000,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: { forceAdaptiveThinking: true },
|
|
},
|
|
{
|
|
id: "claude-sonnet-5-default",
|
|
name: "Sonnet 5",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: 200000,
|
|
maxTokens: 16384,
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: { forceAdaptiveThinking: true },
|
|
},
|
|
],
|
|
});
|
|
|
|
// ── OpenAI via Langdock (Responses API) ─────────────────────────────────
|
|
// Chat Completions is unusable for agent work: Langdock rejects the combo pi
|
|
// needs with "400 Function tools with reasoning_effort are not supported for
|
|
// <model> in /v1/chat/completions. To use function tools, use /v1/responses
|
|
// or set reasoning_effort to 'none'."
|
|
// Docs: https://docs.langdock.com/en/developer/completion-api/openai-responses
|
|
//
|
|
// Verified against api.langdock.com/openai/eu/v1/responses:
|
|
// ✓ SSE streaming, strict function tools, parallel calls, tool-result replay
|
|
// ✓ image input, developer role, store:false (pi always sends it)
|
|
// ✗ reasoning effort "minimal" (upstream rejects it for these models)
|
|
// ✗ Lark/regex grammar tools → only `type: "function"` is allowed
|
|
// ✗ max_output_tokens above the per-model cap (silently clamped)
|
|
// • no encrypted reasoning is returned, but replaying reasoning items in
|
|
// `input` on follow-up turns is accepted
|
|
const openaiCompat = {
|
|
// strict JSON-schema function tools are accepted
|
|
supportsStrictMode: true,
|
|
// Langdock: 'Unsupported tool type "custom". Only function tools are allowed.'
|
|
supportsOpenAIGrammarTools: false,
|
|
// skip the underscore-containing `session_id` header (proxies drop it),
|
|
// keep x-client-request-id for session affinity
|
|
sessionAffinityFormat: "openai-nosession",
|
|
} as const;
|
|
// NOTE: provider-level `compat` is ignored for extension-registered models,
|
|
// so it has to be repeated on every model entry below.
|
|
|
|
// Langdock caps OpenAI models at 60k tokens/minute (Claude gets 200k), and a
|
|
// request larger than the budget is rejected outright — so the usable window
|
|
// is far below the model's real context size. 60k here makes pi auto-compact
|
|
// at ~44k (contextWindow - reserveTokens) and stay under the gate.
|
|
// Raise this if your workspace admin lifts the per-model TPM limit.
|
|
const openaiContextWindow = 60000;
|
|
|
|
pi.registerProvider("openai", {
|
|
baseUrl: "https://api.langdock.com/openai/eu/v1",
|
|
apiKey: "$LANGDOCK_API_KEY",
|
|
api: "openai-responses",
|
|
compat: openaiCompat,
|
|
models: [
|
|
{
|
|
id: "gpt-5.6-sol",
|
|
name: "GPT-5.6 Sol",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: openaiContextWindow,
|
|
maxTokens: 16000,
|
|
thinkingLevelMap: {
|
|
off: "none",
|
|
minimal: null,
|
|
low: "low",
|
|
medium: "medium",
|
|
high: "high",
|
|
xhigh: "xhigh",
|
|
max: "max",
|
|
},
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: openaiCompat,
|
|
},
|
|
{
|
|
id: "gpt-5.6-terra",
|
|
name: "GPT-5.6 Terra",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: openaiContextWindow,
|
|
maxTokens: 16000,
|
|
thinkingLevelMap: {
|
|
off: "none",
|
|
minimal: null,
|
|
low: "low",
|
|
medium: "medium",
|
|
high: "high",
|
|
xhigh: "xhigh",
|
|
max: "max",
|
|
},
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: openaiCompat,
|
|
},
|
|
{
|
|
id: "gpt-5.6-luna",
|
|
name: "GPT-5.6 Luna",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: openaiContextWindow,
|
|
maxTokens: 16000,
|
|
thinkingLevelMap: {
|
|
off: "none",
|
|
minimal: null,
|
|
low: "low",
|
|
medium: "medium",
|
|
high: "high",
|
|
xhigh: "xhigh",
|
|
max: "max",
|
|
},
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: openaiCompat,
|
|
},
|
|
{
|
|
id: "gpt-5.5",
|
|
name: "GPT-5.5",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: openaiContextWindow,
|
|
maxTokens: 16000,
|
|
// "max" is rejected for this model
|
|
thinkingLevelMap: {
|
|
off: "none",
|
|
minimal: null,
|
|
low: "low",
|
|
medium: "medium",
|
|
high: "high",
|
|
xhigh: "xhigh",
|
|
max: null,
|
|
},
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: openaiCompat,
|
|
},
|
|
{
|
|
id: "gpt-5.4-mini",
|
|
name: "GPT-5.4 Mini",
|
|
reasoning: true,
|
|
input: ["text", "image"],
|
|
contextWindow: openaiContextWindow,
|
|
maxTokens: 32000,
|
|
thinkingLevelMap: {
|
|
off: "none",
|
|
minimal: null,
|
|
low: "low",
|
|
medium: "medium",
|
|
high: "high",
|
|
xhigh: "xhigh",
|
|
max: null,
|
|
},
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
compat: openaiCompat,
|
|
},
|
|
],
|
|
});
|
|
|
|
// ── Google via Langdock ─────────────────────────────────────────────────
|
|
// pi.registerProvider("google", {
|
|
// baseUrl: "https://api.langdock.com/google/eu/v1beta",
|
|
// apiKey: "$LANGDOCK_API_KEY",
|
|
// api: "google-generative-ai",
|
|
// models: [
|
|
// {
|
|
// id: "models/gemini-3.5-flash",
|
|
// name: "Gemini 3.5 Flash",
|
|
// reasoning: true,
|
|
// input: ["text", "image"],
|
|
// contextWindow: 1048576,
|
|
// maxTokens: 8192,
|
|
// cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
// },
|
|
// {
|
|
// id: "models/gemini-2.5-flash",
|
|
// name: "Gemini 2.5 Flash",
|
|
// reasoning: true,
|
|
// input: ["text", "image"],
|
|
// contextWindow: 1048576,
|
|
// maxTokens: 8192,
|
|
// cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
// },
|
|
// {
|
|
// id: "models/gemini-2.5-pro",
|
|
// name: "Gemini 2.5 Pro",
|
|
// reasoning: true,
|
|
// input: ["text", "image"],
|
|
// contextWindow: 2097152,
|
|
// maxTokens: 65536,
|
|
// cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
// },
|
|
// ],
|
|
// });
|
|
|
|
// ── Hide unwanted built-in providers ────────────────────────────────────
|
|
const keep = new Set([
|
|
"openrouter",
|
|
]);
|
|
|
|
const hide = [
|
|
"ant-ling",
|
|
"azure-openai",
|
|
"azure-openai-responses",
|
|
"deepseek",
|
|
"nvidia-nim",
|
|
"google-vertex",
|
|
"amazon-bedrock",
|
|
"google",
|
|
"mistral",
|
|
"groq",
|
|
"cerebras",
|
|
"cloudflare-ai-gateway",
|
|
"cloudflare-workers-ai",
|
|
"xai",
|
|
"vercel-ai-gateway",
|
|
"zai-coding-plan-global",
|
|
"zai-coding-plan-china",
|
|
"opencode-zen",
|
|
"opencode-go",
|
|
"huggingface",
|
|
"fireworks",
|
|
"together-ai",
|
|
"kimi-for-coding",
|
|
"minimax",
|
|
"xiaomi-mimo",
|
|
"xiaomi-mimo-china",
|
|
"xiaomi-mimo-amsterdam",
|
|
"xiaomi-mimo-singapore",
|
|
];
|
|
|
|
for (const provider of hide) {
|
|
if (!keep.has(provider)) {
|
|
try {
|
|
pi.unregisterProvider(provider);
|
|
} catch {
|
|
// provider may not exist in this build
|
|
}
|
|
}
|
|
}
|
|
}
|