{"version":3,"file":"simple-options.d.ts","sourceRoot":"","sources":["../../src/providers/simple-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,GAAG,EACH,OAAO,EAEP,KAAK,EACL,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,aAAa,EACb,MAAM,aAAa,CAAC;AAwBrB,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAW9D;AAQD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAMnF;AAED,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,CAAC,EAAE,mBAAmB,EAC7B,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,OAAO,GACf,aAAa,CAaf;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,GAAG,SAAS,CAE7G;AAED,wBAAgB,0BAA0B,CACzC,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,aAAa,EAC7B,aAAa,CAAC,EAAE,eAAe,GAC7B;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAwB/C","sourcesContent":["import type {\n\tApi,\n\tContext,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tStreamOptions,\n\tThinkingBudgets,\n\tThinkingLevel,\n} from \"../types.js\";\n\n// Rough token estimate (~4 chars/token). Deliberately approximate: this only\n// feeds the output-budget subtraction below, which is clamped by a safety\n// margin and a floor, so small errors are harmless. Not a billing figure.\nfunction estimateStringTokens(text: string): number {\n\treturn Math.ceil(text.length / 4);\n}\n\nfunction estimateMessageTokens(message: Message): number {\n\tlet chars = 0;\n\tif (typeof message.content === \"string\") {\n\t\tchars += message.content.length;\n\t} else {\n\t\tfor (const block of message.content) {\n\t\t\tif (block.type === \"text\") chars += block.text.length;\n\t\t\telse if (block.type === \"thinking\") chars += block.thinking.length;\n\t\t\telse if (block.type === \"toolCall\") chars += JSON.stringify(block.arguments).length + block.name.length;\n\t\t\telse if (block.type === \"image\") chars += 1600; // ~image token approximation\n\t\t}\n\t}\n\treturn Math.ceil(chars / 4);\n}\n\n/** Approximate input token count for a request context (system + messages). */\nexport function estimateContextTokens(context: Context): number {\n\tlet tokens = context.systemPrompt ? estimateStringTokens(context.systemPrompt) : 0;\n\tfor (const message of context.messages) {\n\t\ttokens += estimateMessageTokens(message);\n\t}\n\tif (context.tools?.length) {\n\t\tfor (const tool of context.tools) {\n\t\t\ttokens += estimateStringTokens(JSON.stringify(tool));\n\t\t}\n\t}\n\treturn tokens;\n}\n\n// When we can't see the context we fall back to the model's own output ceiling\n// rather than an arbitrary constant. Minimum output we always leave room for\n// even when context is nearly full, and slack to absorb estimate error.\nconst MIN_OUTPUT_TOKENS = 1024;\nconst INPUT_ESTIMATE_MARGIN_TOKENS = 4096;\n\n/**\n * Resolve the per-request output token budget.\n *\n * The model's own `maxTokens` is the ceiling. When a request context is\n * available and the model advertises a context window, we additionally cap so\n * `input + output` fits the window (a hard API requirement for Anthropic,\n * Bedrock and Gemini), leaving a small margin for estimate error and never\n * dropping below MIN_OUTPUT_TOKENS. This replaces a flat 32k clamp that both\n * throttled large-output models and ignored the window constraint entirely.\n */\nexport function resolveMaxOutputTokens(model: Model<Api>, context?: Context): number {\n\tconst ceiling = model.maxTokens;\n\tif (!context || !model.contextWindow) return ceiling;\n\tconst inputEstimate = estimateContextTokens(context) + INPUT_ESTIMATE_MARGIN_TOKENS;\n\tconst remaining = model.contextWindow - inputEstimate;\n\treturn Math.max(MIN_OUTPUT_TOKENS, Math.min(ceiling, remaining));\n}\n\nexport function buildBaseOptions(\n\tmodel: Model<Api>,\n\toptions?: SimpleStreamOptions,\n\tapiKey?: string,\n\tcontext?: Context,\n): StreamOptions {\n\treturn {\n\t\ttemperature: options?.temperature,\n\t\tmaxTokens: options?.maxTokens || resolveMaxOutputTokens(model, context),\n\t\tsignal: options?.signal,\n\t\tapiKey: apiKey || options?.apiKey,\n\t\tcacheRetention: options?.cacheRetention,\n\t\tsessionId: options?.sessionId,\n\t\theaders: options?.headers,\n\t\tonPayload: options?.onPayload,\n\t\tmaxRetryDelayMs: options?.maxRetryDelayMs,\n\t\tmetadata: options?.metadata,\n\t};\n}\n\nexport function clampReasoning(effort: ThinkingLevel | undefined): Exclude<ThinkingLevel, \"xhigh\"> | undefined {\n\treturn effort === \"xhigh\" ? \"high\" : effort;\n}\n\nexport function adjustMaxTokensForThinking(\n\tbaseMaxTokens: number,\n\tmodelMaxTokens: number,\n\treasoningLevel: ThinkingLevel,\n\tcustomBudgets?: ThinkingBudgets,\n): { maxTokens: number; thinkingBudget: number } {\n\tconst defaultBudgets: ThinkingBudgets = {\n\t\tminimal: 1024,\n\t\tlow: 2048,\n\t\tmedium: 8192,\n\t\thigh: 16384,\n\t};\n\tconst budgets = { ...defaultBudgets, ...customBudgets };\n\n\tconst minOutputTokens = 1024;\n\tconst level = clampReasoning(reasoningLevel)!;\n\tlet thinkingBudget = budgets[level]!;\n\t// `baseMaxTokens` already comes from resolveMaxOutputTokens: the model ceiling\n\t// capped so input + output fits the context window. Anthropic counts thinking\n\t// tokens *within* max_tokens, so the budget is carved from this cap — never\n\t// added on top (which would push max_tokens past the window and 400 the\n\t// request when context is nearly full).\n\tconst maxTokens = Math.min(baseMaxTokens, modelMaxTokens);\n\n\tif (thinkingBudget > maxTokens - minOutputTokens) {\n\t\tthinkingBudget = Math.max(0, maxTokens - minOutputTokens);\n\t}\n\n\treturn { maxTokens, thinkingBudget };\n}\n"]}