Özel Providers
Extensions özel model sağlayıcılarını pi.registerProvider() aracılığıyla kaydedebilir. Bu şunları sağlar:
- Proxy'ler - İstekleri kurumsal proxy'ler veya API ağ geçitleri aracılığıyla yönlendirin
- Özel uç noktalar - Şirket içinde barındırılan veya özel model dağıtımlarını kullanın
- OAuth/SSO - Kurumsal sağlayıcılar için kimlik doğrulama akışları ekleyin
- Özel API'ler - Standart olmayan LLM API'ler için akış uygulayın
Örnek Extensions
Bu eksiksiz sağlayıcı örneklerine bakın:
İçindekiler
- Example Extensions
- Quick Reference
- Override Existing Provider
- Register New Provider
- Unregister Provider
- OAuth Support
- Custom Streaming API
- Context Overflow Errors
- Testing Your Implementation
- Config Reference
- Model Definition Reference
Hızlı Referans
Extensions tam bir pi-ai Provider kaydedebilir veya eski sağlayıcı yapılandırma formunu kullanabilir. Özel kimlik doğrulama, filtreleme, yenileme veya akış davranışı gerektiğinde eksiksiz bir sağlayıcıyı tercih edin. Pi, kayıtlı yerel sağlayıcıların üzerindeki models.json geçersiz kılmaları oluşturur.
import { createProvider, openAICompletionsApi } from "@earendil-works/pi-ai";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default function (pi: ExtensionAPI) {
pi.registerProvider(createProvider({
id: "native-local",
name: "Native Local",
baseUrl: "http://localhost:8080/v1",
auth: {
apiKey: {
name: "Local server API key",
async login(interaction) {
return {
type: "api_key",
key: await interaction.prompt({ type: "secret", message: "API key" })
};
},
async resolve({ credential }) {
return credential?.key
? { auth: { apiKey: credential.key }, source: "stored API key" }
: undefined;
}
}
},
models: [],
api: openAICompletionsApi()
}));
// Legacy provider-config form:
// Override baseUrl for existing provider
pi.registerProvider("anthropic", {
baseUrl: "https://proxy.example.com"
});
// Register new provider with models
pi.registerProvider("my-provider", {
name: "My Provider",
baseUrl: "https://api.example.com",
apiKey: "$MY_API_KEY",
api: "openai-completions",
models: [
{
id: "my-model",
name: "My Model",
reasoning: false,
input: ["text", "image"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 4096
}
]
});
}Uzantı fabrikası da async olabilir. Dinamik model keşfi için, modelleri session_start yerine fabrikaya getirin ve kaydedin. pi, başlatma devam etmeden önce fabrikayı bekler, böylece sağlayıcı etkileşimli başlatma sırasında ve pi --list-models'ye hazır olur.
Mevcut Sağlayıcıyı Geçersiz Kıl
En basit kullanım durumu: Mevcut bir sağlayıcıyı bir proxy aracılığıyla yeniden yönlendirmek.
// All Anthropic requests now go through your proxy
pi.registerProvider("anthropic", {
baseUrl: "https://proxy.example.com"
});
// Add custom headers to OpenAI requests
pi.registerProvider("openai", {
headers: {
"X-Custom-Header": "value"
}
});
// Both baseUrl and headers
pi.registerProvider("google", {
baseUrl: "https://ai-gateway.corp.com/google",
headers: {
"X-Corp-Auth": "$CORP_AUTH_TOKEN" // env var or literal
}
});Yalnızca baseUrl ve/veya headers sağlandığında (models yok), o sağlayıcı için mevcut tüm modeller yeni uç noktayla korunur.
Yeni Sağlayıcıyı Kaydedin
Tamamen yeni bir sağlayıcı eklemek için gerekli yapılandırmayla birlikte models belirtin.
Model listesi uzak bir uç noktadan geliyorsa eşzamansız bir uzantı fabrikası kullanın:
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
export default async function (pi: ExtensionAPI) {
const response = await fetch("http://localhost:1234/v1/models");
const payload = (await response.json()) as {
data: Array<{
id: string;
name?: string;
context_window?: number;
max_tokens?: number;
}>;
};
pi.registerProvider("local-openai", {
baseUrl: "http://localhost:1234/v1",
apiKey: "$LOCAL_OPENAI_API_KEY",
api: "openai-completions",
models: payload.data.map((model) => ({
id: model.id,
name: model.name ?? model.id,
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: model.context_window ?? 128000,
maxTokens: model.max_tokens ?? 4096,
})),
});
}Bu, getirilen modelleri başlatma tamamlanmadan önce kaydeder.
pi.registerProvider("my-llm", {
baseUrl: "https://api.my-llm.com/v1",
apiKey: "$MY_LLM_API_KEY", // env var reference
api: "openai-completions", // which streaming API to use
models: [
{
id: "my-llm-large",
name: "My LLM Large",
reasoning: true, // supports extended thinking
input: ["text", "image"],
cost: {
input: 3.0, // $/million tokens
output: 15.0,
cacheRead: 0.3,
cacheWrite: 3.75
},
contextWindow: 200000,
maxTokens: 16384
}
]
});models sağlandığında, o sağlayıcı için mevcut tüm modellerin yerini alır.
apiKey ve özel başlık değerleri, models.json ile aynı yapılandırma değeri sözdizimini kullanır: !command başlangıçta tüm değer için bir komut yürütür, $ENV_VAR ve ${ENV_VAR} ortam değişkenlerini enterpolasyona tabi tutar, $ bir değişmez değer ``apiKeyve özel başlık değerleri,models.jsonile aynı yapılandırma değeri sözdizimini kullanır:!commandbaşlangıçta tüm değer için bir komut yürütür,$ENV_VARve${ENV_VAR}ortam değişkenlerini enterpolasyona tabi tutar,$bir değişmez değer ve$!bir değişmez değer yayar!`.
Sağlayıcının Kaydını İptal Et
Daha önce pi.registerProvider(name,...) aracılığıyla kaydedilen bir sağlayıcıyı kaldırmak için pi.unregisterProvider(name) tuşunu kullanın:
// Register
pi.registerProvider("my-llm", {
baseUrl: "https://api.my-llm.com/v1",
apiKey: "$MY_LLM_API_KEY",
api: "openai-completions",
models: [
{
id: "my-llm-large",
name: "My LLM Large",
reasoning: true,
input: ["text", "image"],
cost: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite: 3.75 },
contextWindow: 200000,
maxTokens: 16384
}
]
});
// Later, remove it
pi.unregisterProvider("my-llm");Kayıt silme işlemi, sağlayıcının dinamik modellerini, API key geri dönüşünü, OAuth sağlayıcı kaydını ve özel akış işleyicisi kayıtlarını kaldırır. Geçersiz kılınan tüm yerleşik modeller veya sağlayıcı davranışları geri yüklenir.
İlk dahili yükleme aşamasından sonra yapılan çağrılar hemen uygulanır, dolayısıyla /reload gerekli değildir.
API Türler
api alanı hangi akış uygulamasının kullanılacağını belirler:
| API | Şunun için kullanın: |
|---|---|
anthropic-messages |
Antropik Claude API ve uyumlular |
openai-completions |
OpenAI Sohbet Tamamlamaları API ve uyumlular |
openai-responses |
OpenAI Yanıtları API |
azure-openai-responses |
Azure OpenAI Yanıtları API |
openai-codex-responses |
OpenAI Kodeksi Yanıtları API |
mistral-conversations |
Yerel Mistral Sohbet Tamamlamaları akışı |
google-generative-ai |
Google Üretken Yapay Zeka API |
google-vertex |
Google Vertex AI API |
bedrock-converse-stream |
Amazon Bedrock Converse API |
Çoğu OpenAI uyumlu sağlayıcı openai-completions ile çalışır. Modele özgü düşünme düzeyleri için model düzeyi thinkingLevelMap'yi ve sağlayıcı tuhaflıkları için compat'yi kullanın. xhigh ve max düzeyleri isteğe bağlıdır, boş olmayan harita girişleri gerektirir ve desteklenmeyen deliklerle ayrılabilir:
models: [{
id: "custom-model",
// ...
reasoning: true,
thinkingLevelMap: { // map pi levels to provider values; null hides unsupported levels
minimal: null,
low: null,
medium: null,
high: "default",
xhigh: null,
max: "max"
},
compat: {
supportsDeveloperRole: false, // use "system" instead of "developer"
supportsReasoningEffort: true,
maxTokensField: "max_tokens", // instead of "max_completion_tokens"
requiresToolResultName: true, // tool results need name field
thinkingFormat: "qwen", // top-level enable_thinking: true
cacheControlFormat: "anthropic" // Anthropic-style cache_control markers
}
}]OpenRouter tarzı reasoning: { effort } kontrolleri için openrouter kullanın. Birlikte tarzı reasoning: { enabled } kontrolleri için together kullanın; supportsReasoningEffort ile reasoning_effort'yi de gönderir. chat_template_kwargs.enable_thinking okuyan ve preserve_thinking'ye ihtiyaç duyan yerel Qwen uyumlu sunucular için qwen-chat-template kullanın.
Sistem isteminde, son araç tanımında ve son kullanıcı, asistan veya araç sonucu metin içeriğinde cache_control aracılığıyla Antropik tarzda bilgi istemi önbelleğe almayı ortaya çıkaran OpenAI uyumlu sağlayıcılar için cacheControlFormat: "anthropic" kullanın.
api: "anthropic-messages" kullanan Antropik uyumlu sağlayıcılar için, yukarı akış modeli uyarlamalı düşünme gerektiren modellere veya sağlayıcılara (thinking.type: "adaptive" artı output_config.effort) compat.forceAdaptiveThinking: true değerini ayarlayın. Yerleşik uyarlanabilir Claude modelleri bunu otomatik olarak ayarlar. compat.allowEmptySignature: true'yi yalnızca boş düşünme imzaları yayan ve tekrar oynatıldığında signature: "" bekleyen sağlayıcılar için ayarlayın.
Geçiş notu: Mistral
openai-completions'denmistral-conversations'ye taşındı. Yerel Mistral modelleri içinmistral-conversationskullanın. Mistral uyumlu/özel uç noktaları kasıtlı olarakopenai-completionsüzerinden yönlendiriyorsanız,compatişaretlerini gerektiği gibi açıkça ayarlayın.
Kimlik Doğrulama Başlığı
Sağlayıcınız Authorization: Bearer <key> bekliyor ancak standart API kullanmıyorsa authHeader: true değerini ayarlayın:
pi.registerProvider("custom-api", {
baseUrl: "https://api.example.com",
apiKey: "$MY_API_KEY",
authHeader: true, // adds Authorization: Bearer header
api: "openai-completions",
models: [...]
});Anahtar her istek için çözümlenir. Açık bir istek Authorization başlığı, oluşturulan değere göre önceliklidir.
OAuth Destek
/login ile entegre olan OAuth/SSO kimlik doğrulamasını ekleyin:
import type { OAuthCredentials, OAuthLoginCallbacks } from "@earendil-works/pi-ai";
pi.registerProvider("corporate-ai", {
baseUrl: "https://ai.corp.com/v1",
api: "openai-responses",
models: [...],
oauth: {
name: "Corporate AI (SSO)",
async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
const method = await callbacks.onSelect({
message: "Select login method:",
options: [
{ id: "browser", label: "Browser OAuth" },
{ id: "device", label: "Device code" }
]
});
if (!method) throw new Error("Login cancelled");
let code: string;
if (method === "device") {
callbacks.onDeviceCode({
userCode: "ABCD-1234",
verificationUri: "https://sso.corp.com/device",
intervalSeconds: 5,
expiresInSeconds: 900
});
code = await pollDeviceCodeUntilComplete();
} else {
callbacks.onAuth({ url: "https://sso.corp.com/authorize?..." });
code = await callbacks.onPrompt({ message: "Enter SSO code:" });
}
// Exchange for tokens (your implementation)
const tokens = await exchangeCodeForTokens(code);
return {
refresh: tokens.refreshToken,
access: tokens.accessToken,
expires: Date.now() + tokens.expiresIn * 1000
};
},
async refreshToken(credentials: OAuthCredentials, signal: AbortSignal): Promise<OAuthCredentials> {
const tokens = await refreshAccessToken(credentials.refresh, signal);
return {
refresh: tokens.refreshToken ?? credentials.refresh,
access: tokens.accessToken,
expires: Date.now() + tokens.expiresIn * 1000
};
},
getApiKey(credentials: OAuthCredentials): string {
return credentials.access;
}
}
});Kayıt olduktan sonra kullanıcılar /login corporate-ai aracılığıyla kimlik doğrulaması yapabilir.
OAuthOturum AçmaGeri Aramalar
callbacks nesnesi, sağlayıcının sahip olduğu akış için kullanıcı arayüzünden bağımsız etkileşimler sağlar:
interface OAuthLoginCallbacks {
// Open URL in browser (for OAuth redirects)
onAuth(params: { url: string }): void;
// Show device code (for device authorization flow)
onDeviceCode(params: {
userCode: string;
verificationUri: string;
intervalSeconds?: number;
expiresInSeconds?: number;
}): void;
// Show transient progress
onProgress?(message: string): void;
// Prompt user for input (for manual token entry)
onPrompt(params: { message: string }): Promise<string>;
// Show an interactive selector, e.g. to choose browser OAuth vs device code
onSelect(params: {
message: string;
options: { id: string; label: string }[];
}): Promise<string | undefined>;
}OAuthKimlik Bilgileri
Kimlik bilgileri ~/.pi/agent/auth.json'de kalıcıdır:
interface OAuthCredentials {
refresh: string; // Refresh token (for refreshToken())
access: string; // Access token (returned by getApiKey())
expires: number; // Expiration timestamp in milliseconds
}Özel Yayın API
Standart olmayan API'lere sahip sağlayıcılar için streamSimple'yi uygulayın. Kendi uygulamanızı yazmadan önce mevcut sağlayıcı uygulamalarını inceleyin:
Referans uygulamalar:
- anthropic.ts - Antropik Mesajlar API
- mistral.ts - Mistral Konuşmalar API
- openai-completions.ts - OpenAI Sohbet Tamamlamaları
- openai-responses.ts - OpenAI Yanıtları API
- google.ts - Google Üretken Yapay Zeka
- amazon-bedrock.ts - AWS Ana Kayası
Akış Deseni
Tüm sağlayıcılar aynı modeli izler:
import {
type AssistantMessage,
type AssistantMessageEventStream,
type Context,
type Model,
type SimpleStreamOptions,
calculateCost,
createAssistantMessageEventStream,
} from "@earendil-works/pi-ai";
function streamMyProvider(
model: Model<any>,
context: Context,
options?: SimpleStreamOptions
): AssistantMessageEventStream {
const stream = createAssistantMessageEventStream();
(async () => {
// Initialize output message
const output: AssistantMessage = {
role: "assistant",
content: [],
api: model.api,
provider: model.provider,
model: model.id,
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
},
stopReason: "pending",
timestamp: Date.now(),
};
try {
// Push start event
stream.push({ type: "start", partial: output });
// Make API request and process response...
// Push content events as they arrive and set stopReason from the terminal event.
if (output.stopReason === "pending") {
throw new Error("Provider stream ended without a stop reason");
}
if (output.stopReason === "error" || output.stopReason === "aborted") {
throw new Error(output.errorMessage || "An unknown error occurred");
}
// Push done event
stream.push({
type: "done",
reason: output.stopReason,
message: output
});
stream.end();
} catch (error) {
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
output.errorMessage = error instanceof Error ? error.message : String(error);
stream.push({ type: "error", reason: output.stopReason, error: output });
stream.end();
}
})();
return stream;
}Etkinlik Türleri
Olayları stream.push() aracılığıyla şu sırayla aktarın:
{ type: "start", partial: output }- Yayın başladıİçerik etkinlikleri (tekrarlanabilir, her blok için
contentIndexparça):{ type: "text_start", contentIndex, partial }- Metin bloğu başlatıldı{ type: "text_delta", contentIndex, delta, partial }- Metin öbeği{ type: "text_end", contentIndex, content, partial }- Metin bloğu sona erdi{ type: "thinking_start", contentIndex, partial }- Düşünmeye başlandı{ type: "thinking_delta", contentIndex, delta, partial }- Düşünme öbeği{ type: "thinking_end", contentIndex, content, partial }- Düşünme sona erdi{ type: "toolcall_start", contentIndex, partial }- Araç çağrısı başlatıldı{ type: "toolcall_delta", contentIndex, delta, partial }- Araç çağrısı JSON öbeği{ type: "toolcall_end", contentIndex, toolCall, partial }- Araç çağrısı sona erdi
{ type: "done", reason, message }veya{ type: "error", reason, error }- Yayın sona erdi
Her etkinlikteki partial alanı mevcut AssistantMessage durumunu içerir. Verileri aldıkça output.content'yi güncelleyin, ardından partial olarak output'yi ekleyin.
İçerik Blokları
output.content'ye ulaştıkça içerik blokları ekleyin:
// Text block
output.content.push({ type: "text", text: "" });
stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
// As text arrives
const block = output.content[contentIndex];
if (block.type === "text") {
block.text += delta;
stream.push({ type: "text_delta", contentIndex, delta, partial: output });
}
// When block completes
stream.push({ type: "text_end", contentIndex, content: block.text, partial: output });Araç Çağrıları
Araç çağrıları JSON biriktirmeyi ve ayrıştırmayı gerektirir:
// Start tool call
output.content.push({
type: "toolCall",
id: toolCallId,
name: toolName,
arguments: {}
});
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
// Accumulate JSON
let partialJson = "";
partialJson += jsonDelta;
try {
block.arguments = JSON.parse(partialJson);
} catch {}
stream.push({ type: "toolcall_delta", contentIndex, delta: jsonDelta, partial: output });
// Complete
stream.push({
type: "toolcall_end",
contentIndex,
toolCall: { type: "toolCall", id, name, arguments: block.arguments },
partial: output
});Kullanım ve Maliyet
API yanıtından kullanımı güncelleyin ve maliyeti hesaplayın:
output.usage.input = response.usage.input_tokens;
output.usage.output = response.usage.output_tokens;
output.usage.cacheRead = response.usage.cache_read_tokens ?? 0;
output.usage.cacheWrite = response.usage.cache_write_tokens ?? 0;
output.usage.totalTokens = output.usage.input + output.usage.output +
output.usage.cacheRead + output.usage.cacheWrite;
calculateCost(model, output.usage);Bağlam Taşması Hataları
Bir istek modelin bağlam penceresini aştığında pi, konuşmayı sıkıştırıp yeniden deneyerek otomatik olarak kurtarılabilir. Bu kurtarma yalnızca Pi'nin arızayı bir taşma olarak algılaması durumunda devreye girer.
Algılama, sonlandırılan asistan mesajı üzerinde çalışır:
stopReason === "error"errorMessagepi'nin bilinen taşma modellerinden biriyle eşleşir (bkz.packages/ai/src/utils/overflow.ts)
Sağlayıcınız pi'nin tanımadığı bir mesajla taşma hataları döndürürse, sağlayıcıyı kaydeden aynı uzantıdan hatayı normalleştirin. Asistan mesajını, errorMessage pi'nin tanıdığı bir ifadeyle başlayacak şekilde yeniden yazmak için bir message_end işleyicisi kullanın. Genel geri dönüş context_length_exceeded en güvenli seçimdir.
const MY_PROVIDER_OVERFLOW_PATTERN = /your provider's overflow phrase/i;
export default function (pi: ExtensionAPI) {
pi.registerProvider("my-provider", { /* ... */ });
pi.on("message_end", (event, ctx) => {
const message = event.message;
if (message.role !== "assistant") return;
if (message.stopReason !== "error") return;
if (
message.provider !== "my-provider" &&
ctx.model?.provider !== "my-provider"
)
return;
const errorMessage = message.errorMessage ?? "";
if (errorMessage.includes("context_length_exceeded")) return;
if (!MY_PROVIDER_OVERFLOW_PATTERN.test(errorMessage)) return;
return {
message: {
...message,
errorMessage: `context_length_exceeded: ${errorMessage}`,
},
};
});
}message_end, pi'nin otomatik sıkıştırma için asistan mesajını izlemesinden önce çalışır, dolayısıyla yeniden yazılan errorMessage, pi'nin kontrol ettiği şeydir. Bunu yerine getirdiğimizde pi şunları yapacaktır:
errorMessage'den taşmayı tespit edin.- Başarısız olan asistan mesajını canlı bağlamdan bırakın.
- Sıkıştırmayı çalıştırın.
- İsteği bir kez yeniden deneyin.
Yeniden yazmayı dikkatli bir şekilde koruyun:
- Bunu sağlayıcınızın kapsamına alın (
message.providervectx.model?.provider), böylece diğer sağlayıcılardan gelen ilgisiz hatalara dokunulmaz. - Pi'nin genel taşma modellerini değil, sağlayıcıya özel bir modeli eşleştirin. Hız sınırı veya azaltma hatalarının (
rate limit,too many requests) yeniden yazılması, pi'nin normal geri çekme ile yeniden deneme yolu yerine yanlışlıkla sıkıştırmayı tetikler. errorMessagezatencontext_length_exceedediçerdiğinde, işleyicinin önemsiz olması durumunda atlayın.
Kayıt
Akış işlevinizi kaydedin:
pi.registerProvider("my-provider", {
baseUrl: "https://api.example.com",
apiKey: "$MY_API_KEY",
api: "my-custom-api",
models: [...],
streamSimple: streamMyProvider
});Uygulamanızı Test Etme
Sağlayıcınızı, yerleşik sağlayıcılar tarafından kullanılan aynı test paketleriyle karşılaştırarak test edin. Bu test dosyalarını packages/ai/test/ adresinden kopyalayıp uyarlayın:
| Test | Amaç |
|---|---|
stream.test.ts |
Temel akış, metin çıkışı |
tokens.test.ts |
Jeton sayımı ve kullanımı |
abort.test.ts |
AbortSinyal yönetimi |
empty.test.ts |
Boş/minimum yanıtlar |
context-overflow.test.ts |
Bağlam penceresi sınırları |
image-limits.test.ts |
Görüntü girişi yönetimi |
unicode-surrogate.test.ts |
Unicode uç durumları |
tool-call-without-result.test.ts |
Araç çağrısı uç durumları |
image-tool-result.test.ts |
Araç sonuçlarındaki resimler |
total-tokens.test.ts |
Toplam jeton hesaplaması |
cross-provider-handoff.test.ts |
Sağlayıcılar arasında bağlam aktarımı |
Uyumluluğu doğrulamak için sağlayıcınız/model çiftlerinizle testler yapın.
Yapılandırma Referansı
interface ProviderConfig {
/** Display name for the provider in UI such as /login. */
name?: string;
/** API endpoint URL. Required when defining models. */
baseUrl?: string;
/** API key literal, env interpolation ($ENV_VAR or ${ENV_VAR}), or !command. Required when defining models (unless oauth). */
apiKey?: string;
/** API type for streaming. Required at provider or model level when defining models. */
api?: Api;
/** Custom streaming implementation for non-standard APIs. */
streamSimple?: (
model: Model<Api>,
context: Context,
options?: SimpleStreamOptions
) => AssistantMessageEventStream;
/** Custom headers to include in requests. Values use the same resolution syntax as apiKey. */
headers?: Record<string, string>;
/** If true, adds Authorization: Bearer header with the resolved API key. */
authHeader?: boolean;
/** Models to register. If provided, replaces all existing models for this provider. */
models?: ProviderModelConfig[];
/** OAuth provider for /login support. */
oauth?: {
name: string;
login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
refreshToken(credentials: OAuthCredentials, signal: AbortSignal): Promise<OAuthCredentials>;
getApiKey(credentials: OAuthCredentials): string;
};
}Model Tanımı Referansı
interface ProviderModelConfig {
/** Model ID (e.g., "claude-sonnet-4-20250514"). */
id: string;
/** Display name (e.g., "Claude 4 Sonnet"). */
name: string;
/** API type override for this specific model. */
api?: Api;
/** API endpoint URL override for this specific model. */
baseUrl?: string;
/** Whether the model supports extended thinking. */
reasoning: boolean;
/** Maps pi thinking levels to provider/model-specific values; null marks a level unsupported. */
thinkingLevelMap?: Partial<Record<"off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max", string | null>>;
/** Supported input types. */
input: ("text" | "image")[];
/** Cost per million tokens (for usage tracking). */
cost: {
input: number;
output: number;
cacheRead: number;
cacheWrite: number;
};
/** Maximum context window size in tokens. */
contextWindow: number;
/** Maximum output tokens. */
maxTokens: number;
/** Custom headers for this specific model. */
headers?: Record<string, string>;
/** Compatibility settings for the selected API. */
compat?: {
// openai-completions
supportsStore?: boolean;
supportsDeveloperRole?: boolean;
supportsReasoningEffort?: boolean;
supportsUsageInStreaming?: boolean;
supportsFinishReason?: boolean;
supportsStrictMode?: boolean;
supportsOpenAIGrammarTools?: boolean; // openai-completions/openai-responses; false falls back to normal function tools
maxTokensField?: "max_completion_tokens" | "max_tokens";
requiresToolResultName?: boolean;
requiresAssistantAfterToolResult?: boolean;
requiresThinkingAsText?: boolean;
requiresReasoningContentOnAssistantMessages?: boolean;
thinkingFormat?: "openai" | "openrouter" | "deepseek" | "together" | "baseten" | "zai" | "qwen" | "chat-template" | "qwen-chat-template" | "string-thinking" | "ant-ling";
chatTemplateKwargs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort"; omitWhenOff?: boolean }>;
chatTemplateArgs?: Record<string, string | number | boolean | null | { "$var": "thinking.enabled" | "thinking.effort"; omitWhenOff?: boolean }>;
cacheControlFormat?: "anthropic";
sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter";
sendSessionAffinityHeaders?: boolean;
// anthropic-messages
supportsEagerToolInputStreaming?: boolean;
supportsLongCacheRetention?: boolean;
sendSessionAffinityHeaders?: boolean;
supportsCacheControlOnTools?: boolean;
forceAdaptiveThinking?: boolean;
allowEmptySignature?: boolean;
supportsStrictTools?: boolean;
};
}openrouter reasoning: { effort } gönderir. deepseek etkinleştirildiğinde thinking: { type: "enabled" | "disabled" } ve reasoning_effort gönderir. together, reasoning: { enabled }'yi ve ayrıca supportsReasoningEffort etkinleştirildiğinde reasoning_effort'yi gönderir. qwen DashScope tarzı üst düzey enable_thinking içindir. chat_template_kwargs.enable_thinking okuyan ve preserve_thinking'ye ihtiyaç duyan yerel Qwen uyumlu sunucular için qwen-chat-template kullanın. Yapılandırılabilir chat_template_kwargs için chat-template kullanın, örneğin chatTemplateKwargs: { "thinking": { "$var": "thinking.enabled" } } ile vLLM'nin arkasında DeepSeek V3.x. Sağlayıcı chat_template_args'nin altında geçiş değerleri beklediğinde ve isteğe bağlı olarak üst düzey reasoning_effort'yi desteklediğinde thinkingFormat: "baseten"'yi chatTemplateArgs ile birlikte kullanın.
cacheControlFormat: "anthropic" sistem istemine, son araç tanımına ve son kullanıcı, asistan veya araç sonucu metin içeriğine Antropik stil cache_control işaretleyicileri uygular.