/** * Thinking effort configuration for mapping pi's ThinkingLevel to Claude CLI --effort flags. * * Maps pi's reasoning levels (minimal/low/medium/high/xhigh) to the CLI's effort * levels (low/medium/high/max). Opus models get an elevated mapping where medium * becomes high and high becomes max, leveraging their superior reasoning capability. * * IMPORTANT: The CLI does NOT support --thinking-budget. Only --effort is supported. */ import type { ThinkingLevel, ThinkingBudgets } from "@mariozechner/pi-ai"; /** CLI effort levels accepted by the --effort flag */ export type CliEffortLevel = "low" | "medium" | "high" | "max"; /** * Standard model mapping: pi ThinkingLevel -> CLI effort. * Non-Opus models never receive "max" (would cause CLI error). */ const STANDARD_EFFORT_MAP: Record = { minimal: "low", low: "low", medium: "medium", high: "high", xhigh: "high", // non-Opus: silently downgrade (max not supported) }; /** * Opus model mapping: shifted up for elevated reasoning. * Opus models get max capability at high/xhigh levels. */ const OPUS_EFFORT_MAP: Record = { minimal: "low", low: "low", medium: "high", // shifted: standard high high: "max", // shifted: maximum capability xhigh: "max", // Opus gets max }; /** * Detect whether a model ID refers to an Opus model. * Uses includes('opus') for forward-compatibility with future Opus versions. * * @param modelId - The model identifier string * @returns true if the model is an Opus variant */ export function isOpusModel(modelId: string): boolean { return modelId.includes("opus"); } /** * Map pi's ThinkingLevel to a CLI effort string. * * When reasoning is undefined, returns undefined so the --effort flag is omitted * entirely, letting the CLI use its default behavior. When thinkingBudgets are * provided, a console.warn is logged because the CLI only supports effort levels, * not token budgets. * * @param reasoning - Pi's thinking level (undefined = omit flag) * @param modelId - Model ID for Opus detection * @param thinkingBudgets - Custom budgets (logged as unsupported, not applied) * @returns CLI effort level string, or undefined if flag should be omitted */ export function mapThinkingEffort( reasoning?: ThinkingLevel, modelId?: string, thinkingBudgets?: ThinkingBudgets, ): CliEffortLevel | undefined { if (reasoning === undefined) { return undefined; // omit --effort flag entirely } if (thinkingBudgets && Object.keys(thinkingBudgets).length > 0) { console.warn( "[pi-claude-cli] Custom thinkingBudgets are not supported with CLI subprocess. " + "The CLI uses --effort levels instead of token budgets. Budgets will be ignored.", ); } const isOpus = modelId ? isOpusModel(modelId) : false; const map = isOpus ? OPUS_EFFORT_MAP : STANDARD_EFFORT_MAP; return map[reasoning]; }