🕐 feat: Add promptCacheTtl model parameter for 1h/5m cache duration (#13835)
Some checks failed
Publish `librechat-data-provider` to NPM / pack (push) Waiting to run
Publish `librechat-data-provider` to NPM / publish-npm (push) Blocked by required conditions
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Waiting to run
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Waiting to run
GitNexus Index / index (push) Waiting to run
GitNexus Index / post-index (push) Blocked by required conditions
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Waiting to run
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Blocked by required conditions
Sync Helm Chart Tags / Ignore non-main push (push) Waiting to run
Sync Helm Chart Tags / Sync chart tags (push) Waiting to run
Publish `@librechat/data-schemas` to NPM / pack (push) Has been cancelled
Publish `@librechat/data-schemas` to NPM / publish-npm (push) Has been cancelled

* 🕐 feat: Add promptCacheTtl model parameter for 1h/5m cache duration

Adds a user-configurable `promptCacheTtl` parameter (dropdown: 5m | 1h)
alongside the existing `promptCache` toggle for Anthropic, Bedrock, and
OpenRouter endpoints. Default is undefined so the agents SDK applies its
own default (1h), letting users opt down to the legacy 5m TTL.

- data-provider: schema, parameterSettings dropdown, types, bedrock picks
- data-schemas: convo/preset types + mongoose defaults
- api: thread promptCacheTtl into anthropic + openai(OpenRouter) llmConfig
- i18n: en translation keys for label/description/default placeholder
- tests: anthropic llm.spec coverage for set + unset cases

* 🔧 fix: Tie Bedrock promptCacheTtl to promptCache + thread OpenRouter TTL params (Codex review)

- bedrock.ts: clear promptCacheTtl whenever promptCache is off/unsupported,
  so an unsupported 1h is never sent on a non-caching Bedrock request
- openai/llm.ts: resolve promptCacheTtl through the same defaultParams/
  addParams/dropParams machinery as promptCache (via promptCacheTtlValue)
  so OpenRouter custom endpoints can configure/override/drop it
- tests: bedrock TTL-tied-to-promptCache cases; OpenRouter TTL default/add/drop

* 🎨 style: Sort imports in openai/llm.spec.ts (CI sort-imports)

*  test: Prove OpenRouter TTL-only selection honors promptCache default (Codex review)

OPENROUTER_DEFAULT_PARAMS injects promptCache:true into defaultParams, so a
TTL-only dropdown selection (promptCacheTtl set, promptCache switch untouched)
still resolves caching on and forwards the TTL. Add regression tests via the
real getOpenAIConfig entry point: TTL-only -> promptCache+TTL both set;
explicit promptCache:false -> both dropped.

* 🔖 chore: Bump librechat-data-provider to 0.8.506

* 🔧 fix: Drop Anthropic promptCacheTtl when promptCache is dropped (Codex review)

dropParams: ['promptCache'] deleted requestOptions.promptCache but left
promptCacheTtl behind, so the admin opt-out path could still carry a TTL
on a request with caching disabled. Clear the TTL alongside promptCache.
This commit is contained in:
Danny Avila 2026-06-18 16:36:43 -04:00 committed by GitHub
parent 6a63531eb4
commit 268fcbb78d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 263 additions and 7 deletions

View file

@ -226,6 +226,7 @@
"com_endpoint_anthropic_effort": "Controls how much computational effort Claude applies. Lower effort saves tokens and reduces latency; higher effort produces more thorough responses. 'Max' enables the deepest reasoning on supported adaptive-thinking models.",
"com_endpoint_anthropic_maxoutputtokens": "Maximum number of tokens that can be generated in the response. Specify a lower value for shorter responses and a higher value for longer responses. Note: models may stop before reaching this maximum.",
"com_endpoint_anthropic_prompt_cache": "Prompt caching allows reusing large context or instructions across API calls, reducing costs and latency",
"com_endpoint_anthropic_prompt_cache_ttl": "How long a cached prompt prefix stays warm between requests. Leave unset to use the provider default (1 hour). The 1-hour cache keeps the prefix warm across longer gaps at a higher one-time write cost; choose 5 minutes for the legacy behavior.",
"com_endpoint_anthropic_temp": "Ranges from 0 to 1. Use temp closer to 0 for analytical / multiple choice, and closer to 1 for creative and generative tasks. We recommend altering this or Top P but not both.",
"com_endpoint_anthropic_thinking": "Enables internal reasoning for supported Claude models. For newer models (Opus 4.6+), uses adaptive thinking controlled by the Effort parameter. For legacy models, requires \"Thinking Budget\" to be set and lower than \"Max Output Tokens\".",
"com_endpoint_anthropic_thinking_budget": "Determines the max number of tokens Claude is allowed use for its internal reasoning process. Larger budgets can improve response quality by enabling more thorough analysis for complex problems, although Claude may not use the entire budget allocated, especially at ranges above 32K. This setting must be lower than \"Max Output Tokens.\"",
@ -336,6 +337,8 @@
"com_endpoint_presets": "presets",
"com_endpoint_presets_clear_warning": "Are you sure you want to clear all presets? This is irreversible.",
"com_endpoint_prompt_cache": "Use Prompt Caching",
"com_endpoint_prompt_cache_ttl": "Prompt Cache Duration",
"com_endpoint_prompt_cache_ttl_default": "Default (1 hour)",
"com_endpoint_prompt_prefix": "Custom Instructions",
"com_endpoint_prompt_prefix_assistants": "Additional Instructions",
"com_endpoint_prompt_prefix_assistants_placeholder": "Set additional instructions or context on top of the Assistant's main instructions. Ignored if empty.",

2
package-lock.json generated
View file

@ -44177,7 +44177,7 @@
},
"packages/data-provider": {
"name": "librechat-data-provider",
"version": "0.8.505",
"version": "0.8.506",
"license": "ISC",
"dependencies": {
"axios": "^1.16.0",

View file

@ -356,6 +356,46 @@ describe('getLLMConfig', () => {
expect(result.llmConfig.promptCache).toBe(true);
});
it('should pass promptCacheTtl to llmConfig when configured', () => {
const result = getLLMConfig('test-api-key', {
modelOptions: {
model: 'claude-3-5-sonnet',
promptCache: true,
promptCacheTtl: '1h',
},
});
expect(result.llmConfig.promptCache).toBe(true);
expect((result.llmConfig as Record<string, unknown>).promptCacheTtl).toBe('1h');
});
it('should omit promptCacheTtl when unset so the agents SDK applies its default', () => {
const result = getLLMConfig('test-api-key', {
modelOptions: {
model: 'claude-3-5-sonnet',
promptCache: true,
},
});
expect(result.llmConfig.promptCache).toBe(true);
expect((result.llmConfig as Record<string, unknown>).promptCacheTtl).toBeUndefined();
});
it('should drop promptCacheTtl when promptCache is dropped via dropParams', () => {
const result = getLLMConfig('test-api-key', {
modelOptions: {
model: 'claude-3-5-sonnet',
promptCache: true,
promptCacheTtl: '1h',
},
dropParams: ['promptCache'],
});
/** A dropped cache must not leave an orphaned TTL on the request. */
expect(result.llmConfig.promptCache).toBeUndefined();
expect((result.llmConfig as Record<string, unknown>).promptCacheTtl).toBeUndefined();
});
it('should handle thinking and thinkingBudget options', () => {
const result = getLLMConfig('test-api-key', {
modelOptions: {

View file

@ -114,6 +114,8 @@ function getLLMConfig(
const systemOptions = {
thinking: options.modelOptions?.thinking ?? anthropicSettings.thinking.default,
promptCache: options.modelOptions?.promptCache ?? anthropicSettings.promptCache.default,
promptCacheTtl:
options.modelOptions?.promptCacheTtl ?? anthropicSettings.promptCacheTtl.default,
thinkingBudget:
options.modelOptions?.thinkingBudget ?? anthropicSettings.thinkingBudget.default,
effort: options.modelOptions?.effort ?? anthropicSettings.effort.default,
@ -126,6 +128,7 @@ function getLLMConfig(
if (options.modelOptions) {
delete options.modelOptions.thinking;
delete options.modelOptions.promptCache;
delete options.modelOptions.promptCacheTtl;
delete options.modelOptions.thinkingBudget;
delete options.modelOptions.effort;
delete options.modelOptions.thinkingDisplay;
@ -222,6 +225,10 @@ function getLLMConfig(
/** Pass promptCache boolean for downstream cache_control application */
if (supportsCacheControl) {
(requestOptions as Record<string, unknown>).promptCache = true;
/** Pass an explicit TTL when configured; otherwise the agents SDK defaults to 1h */
if (systemOptions.promptCacheTtl != null) {
(requestOptions as Record<string, unknown>).promptCacheTtl = systemOptions.promptCacheTtl;
}
}
const headers = getClaudeHeaders(requestOptions.model ?? '', supportsCacheControl);
@ -297,6 +304,11 @@ function getLLMConfig(
delete (requestOptions.invocationKwargs as Record<string, unknown>)[param];
}
});
/** A TTL is meaningless without caching — drop it alongside promptCache. */
if (options.dropParams.includes('promptCache')) {
delete (requestOptions as Record<string, unknown>).promptCacheTtl;
}
}
if (shouldOmitSamplingParameters) {

View file

@ -920,6 +920,46 @@ describe('getOpenAIConfig', () => {
expect(result.provider).toBe('openrouter');
});
it('should honor a TTL-only selection for OpenRouter (promptCache defaulted on)', () => {
/**
* Reproduces selecting only the promptCacheTtl dropdown without toggling
* the promptCache switch: modelOptions carries the TTL but not promptCache.
* OPENROUTER_DEFAULT_PARAMS still resolves caching on, so the TTL survives.
*/
const result = getOpenAIConfig(
mockApiKey,
{
modelOptions: {
model: 'anthropic/claude-sonnet-4.6',
promptCacheTtl: '1h',
} as Record<string, unknown>,
},
'openrouter',
);
expect(result.llmConfig.promptCache).toBe(true);
expect((result.llmConfig as Record<string, unknown>).promptCacheTtl).toBe('1h');
expect(result.provider).toBe('openrouter');
});
it('should drop the OpenRouter TTL when promptCache is explicitly disabled', () => {
const result = getOpenAIConfig(
mockApiKey,
{
modelOptions: {
model: 'anthropic/claude-sonnet-4.6',
promptCache: false,
promptCacheTtl: '1h',
} as Record<string, unknown>,
},
'openrouter',
);
expect(result.llmConfig.promptCache).toBeUndefined();
expect((result.llmConfig as Record<string, unknown>).promptCacheTtl).toBeUndefined();
expect(result.provider).toBe('openrouter');
});
it('should handle web_search with OpenRouter using plugins format', () => {
const modelOptions = {
model: 'gpt-4',

View file

@ -5,8 +5,8 @@ import {
ReasoningSummary,
ReasoningParameterFormat,
} from 'librechat-data-provider';
import { getOpenAILLMConfig, extractDefaultParams, applyDefaultParams } from './llm';
import type * as t from '~/types';
import { getOpenAILLMConfig, extractDefaultParams, applyDefaultParams } from './llm';
describe('getOpenAILLMConfig', () => {
describe('Basic Configuration', () => {
@ -1100,6 +1100,44 @@ describe('getOpenAILLMConfig', () => {
expect(dropped.llmConfig).not.toHaveProperty('promptCache');
});
it('should resolve OpenRouter promptCacheTtl default/add/drop params', () => {
const fromDefault = getOpenAILLMConfig({
apiKey: 'test-api-key',
streaming: true,
useOpenRouter: true,
defaultParams: { promptCache: true, promptCacheTtl: '1h' },
modelOptions: {
model: 'anthropic/claude-sonnet-4.6',
},
});
const overridden = getOpenAILLMConfig({
apiKey: 'test-api-key',
streaming: true,
useOpenRouter: true,
defaultParams: { promptCache: true, promptCacheTtl: '1h' },
addParams: { promptCacheTtl: '5m' },
modelOptions: {
model: 'anthropic/claude-sonnet-4.6',
},
});
const dropped = getOpenAILLMConfig({
apiKey: 'test-api-key',
streaming: true,
useOpenRouter: true,
defaultParams: { promptCache: true, promptCacheTtl: '1h' },
dropParams: ['promptCacheTtl'],
modelOptions: {
model: 'anthropic/claude-sonnet-4.6',
},
});
expect((fromDefault.llmConfig as Record<string, unknown>).promptCacheTtl).toBe('1h');
expect((overridden.llmConfig as Record<string, unknown>).promptCacheTtl).toBe('5m');
/** promptCache stays on, but the TTL is dropped so the SDK applies its default */
expect(dropped.llmConfig).toHaveProperty('promptCache', true);
expect((dropped.llmConfig as Record<string, unknown>).promptCacheTtl).toBeUndefined();
});
it('should set includeReasoningContent for DeepSeek models via OpenRouter', () => {
const result = getOpenAILLMConfig({
apiKey: 'test-api-key',

View file

@ -454,10 +454,13 @@ export function getOpenAILLMConfig({
verbosity,
web_search,
promptCache,
promptCacheTtl,
frequency_penalty,
presence_penalty,
...modelOptions
} = cleanedModelOptions as Partial<t.OpenAIParameters & { promptCache?: boolean }>;
} = cleanedModelOptions as Partial<
t.OpenAIParameters & { promptCache?: boolean; promptCacheTtl?: '5m' | '1h' }
>;
const llmConfig = Object.assign(
{
@ -488,6 +491,7 @@ export function getOpenAILLMConfig({
let enableWebSearch = web_search;
let enablePromptCache = promptCache;
let promptCacheTtlValue = promptCacheTtl;
/** Apply defaultParams first - only if fields are undefined */
if (defaultParams && typeof defaultParams === 'object') {
@ -504,6 +508,12 @@ export function getOpenAILLMConfig({
}
continue;
}
if (key === 'promptCacheTtl') {
if (promptCacheTtlValue === undefined && (value === '5m' || value === '1h')) {
promptCacheTtlValue = value;
}
continue;
}
if (key === 'reasoning_effort') {
if (!reasoningEffort && typeof value === 'string') {
reasoningEffort = value as OpenAILLMConfig['reasoning_effort'];
@ -555,6 +565,12 @@ export function getOpenAILLMConfig({
}
continue;
}
if (key === 'promptCacheTtl') {
if (value === '5m' || value === '1h') {
promptCacheTtlValue = value;
}
continue;
}
if (key === 'reasoning_effort') {
if (typeof value === 'string' || value == null) {
reasoningEffort = value as OpenAILLMConfig['reasoning_effort'];
@ -617,6 +633,9 @@ export function getOpenAILLMConfig({
if (dropParams && dropParams.includes('promptCache')) {
enablePromptCache = false;
}
if (dropParams && dropParams.includes('promptCacheTtl')) {
promptCacheTtlValue = undefined;
}
if (useOpenRouter && enableWebSearch) {
/** OpenRouter expects web search as a plugins parameter */
@ -629,6 +648,10 @@ export function getOpenAILLMConfig({
}
if (useOpenRouter && enablePromptCache === true) {
llmConfig.promptCache = true;
/** Pass an explicit TTL when configured; otherwise the agents SDK defaults to 1h */
if (promptCacheTtlValue != null) {
llmConfig.promptCacheTtl = promptCacheTtlValue;
}
}
if (!useOpenRouter) {

View file

@ -33,6 +33,7 @@ export type OAIClientOptions = Omit<OpenAIClientOptions, 'verbosity'> & {
/** Replays `reasoning_content` on tool-bearing turns (DeepSeek thinking-mode, #13366). */
includeReasoningContent?: boolean;
promptCache?: boolean;
promptCacheTtl?: '5m' | '1h';
_lc_stream_delay?: number;
verbosity?: string | null;
};

View file

@ -1,6 +1,6 @@
{
"name": "librechat-data-provider",
"version": "0.8.505",
"version": "0.8.506",
"description": "data services for librechat apps",
"main": "dist/index.js",
"module": "dist/index.mjs",

View file

@ -1271,4 +1271,34 @@ describe('bedrockInputParser', () => {
expect(amrf.reasoning_config).toBeUndefined();
});
});
describe('promptCacheTtl tied to promptCache', () => {
test('preserves promptCacheTtl when caching is enabled (claude default)', () => {
const result = bedrockInputParser.parse({
model: 'anthropic.claude-opus-4-6-v1',
promptCacheTtl: '1h',
}) as Record<string, unknown>;
expect(result.promptCache).toBe(true);
expect(result.promptCacheTtl).toBe('1h');
});
test('clears promptCacheTtl when promptCache is explicitly disabled', () => {
const result = bedrockInputParser.parse({
model: 'anthropic.claude-opus-4-6-v1',
promptCache: false,
promptCacheTtl: '1h',
}) as Record<string, unknown>;
expect(result.promptCacheTtl).toBeUndefined();
});
test('clears promptCache and promptCacheTtl for non-caching models', () => {
const result = bedrockInputParser.parse({
model: 'meta.llama3-1-70b-instruct-v1:0',
promptCache: true,
promptCacheTtl: '1h',
}) as Record<string, unknown>;
expect(result.promptCache).toBeUndefined();
expect(result.promptCacheTtl).toBeUndefined();
});
});
});

View file

@ -256,6 +256,7 @@ export const bedrockInputSchema = s.tConversationSchema
thinkingDisplay: true,
reasoning_effort: true,
promptCache: true,
promptCacheTtl: true,
/* Catch-all fields */
topK: true,
additionalModelRequestFields: true,
@ -312,6 +313,7 @@ export const bedrockInputParser = s.tConversationSchema
thinkingDisplay: true,
reasoning_effort: true,
promptCache: true,
promptCacheTtl: true,
/* Catch-all fields */
topK: true,
additionalModelRequestFields: true,
@ -336,6 +338,7 @@ export const bedrockInputParser = s.tConversationSchema
'topP',
'stop',
'promptCache',
'promptCacheTtl',
];
const additionalFields: Record<string, unknown> = {};
@ -503,6 +506,15 @@ export const bedrockInputParser = s.tConversationSchema
typedData.promptCache = undefined;
}
/**
* A cache TTL is meaningless without caching tie it to promptCache. When
* caching is off or unsupported for the model (cleared above), drop the TTL
* so an unsupported `1h` is never sent on a non-caching Bedrock request.
*/
if (typedData.promptCache !== true) {
typedData.promptCacheTtl = undefined;
}
if (Object.keys(additionalFields).length > 0) {
typedData.additionalModelRequestFields = {
...((typedData.additionalModelRequestFields as Record<string, unknown> | undefined) || {}),

View file

@ -406,6 +406,22 @@ const anthropic: Record<string, SettingDefinition> = {
showDefault: false,
columnSpan: 2,
},
promptCacheTtl: {
key: 'promptCacheTtl',
label: 'com_endpoint_prompt_cache_ttl',
labelCode: true,
description: 'com_endpoint_anthropic_prompt_cache_ttl',
descriptionCode: true,
type: 'enum',
default: anthropicSettings.promptCacheTtl.default,
options: ['5m', '1h'],
component: 'dropdown',
optionType: 'conversation',
showDefault: false,
placeholder: 'com_endpoint_prompt_cache_ttl_default',
placeholderCode: true,
columnSpan: 2,
},
thinking: {
key: 'thinking',
label: 'com_endpoint_thinking',
@ -552,6 +568,22 @@ const bedrock: Record<string, SettingDefinition> = {
showDefault: false,
columnSpan: 2,
},
promptCacheTtl: {
key: 'promptCacheTtl',
label: 'com_endpoint_prompt_cache_ttl',
labelCode: true,
description: 'com_endpoint_anthropic_prompt_cache_ttl',
descriptionCode: true,
type: 'enum',
default: undefined,
options: ['5m', '1h'],
component: 'dropdown',
optionType: 'conversation',
showDefault: false,
placeholder: 'com_endpoint_prompt_cache_ttl_default',
placeholderCode: true,
columnSpan: 2,
},
reasoning_effort: {
key: 'reasoning_effort',
label: 'com_endpoint_reasoning_effort',
@ -792,7 +824,11 @@ const openAI: SettingsConfiguration = [
librechat.fileTokenLimit,
];
const openRouter: SettingsConfiguration = [...openAI, anthropic.promptCache];
const openRouter: SettingsConfiguration = [
...openAI,
anthropic.promptCache,
anthropic.promptCacheTtl,
];
const openAICol1: SettingsConfiguration = [
baseDefinitions.model as SettingDefinition,
@ -829,6 +865,7 @@ const anthropicConfig: SettingsConfiguration = [
anthropic.topK,
librechat.resendFiles,
anthropic.promptCache,
anthropic.promptCacheTtl,
anthropic.thinking,
anthropic.thinkingBudget,
anthropic.effort,
@ -851,6 +888,7 @@ const anthropicCol2: SettingsConfiguration = [
anthropic.topK,
librechat.resendFiles,
anthropic.promptCache,
anthropic.promptCacheTtl,
anthropic.thinking,
anthropic.thinkingBudget,
anthropic.effort,
@ -871,6 +909,7 @@ const bedrockAnthropic: SettingsConfiguration = [
librechat.resendFiles,
bedrock.region,
bedrock.promptCache,
bedrock.promptCacheTtl,
anthropic.thinking,
anthropic.thinkingBudget,
anthropic.effort,
@ -911,6 +950,7 @@ const bedrockGeneral: SettingsConfiguration = [
librechat.resendFiles,
bedrock.region,
bedrock.promptCache,
bedrock.promptCacheTtl,
librechat.fileTokenLimit,
];
@ -930,6 +970,7 @@ const bedrockAnthropicCol2: SettingsConfiguration = [
librechat.resendFiles,
bedrock.region,
bedrock.promptCache,
bedrock.promptCacheTtl,
anthropic.thinking,
anthropic.thinkingBudget,
anthropic.effort,
@ -982,6 +1023,7 @@ const bedrockGeneralCol2: SettingsConfiguration = [
librechat.resendFiles,
bedrock.region,
bedrock.promptCache,
bedrock.promptCacheTtl,
librechat.fileTokenLimit,
];
@ -1092,7 +1134,7 @@ export const presetSettings: Record<
[EModelEndpoint.custom]: openAIColumns,
[Providers.OPENROUTER]: {
col1: openAICol1,
col2: [...openAICol2, anthropic.promptCache],
col2: [...openAICol2, anthropic.promptCache, anthropic.promptCacheTtl],
},
[EModelEndpoint.anthropic]: {
col1: anthropicCol1,

View file

@ -520,6 +520,9 @@ export const anthropicSettings = {
promptCache: {
default: true as const,
},
promptCacheTtl: {
default: undefined,
},
thinking: {
default: true as const,
},
@ -909,6 +912,7 @@ export const tConversationSchema = z.object({
max_tokens: coerceNumber.optional(),
/* Anthropic */
promptCache: z.boolean().optional(),
promptCacheTtl: z.enum(['5m', '1h']).optional(),
system: z.string().optional(),
thinking: z.boolean().optional(),
thinkingBudget: coerceNumber.optional(),
@ -1062,6 +1066,7 @@ export const tQueryParamsSchema = tConversationSchema
maxOutputTokens: true,
/** @endpoints anthropic */
promptCache: true,
promptCacheTtl: true,
thinking: true,
thinkingBudget: true,
thinkingLevel: true,
@ -1368,7 +1373,7 @@ export const openAISchema = openAIBaseSchema
.catch(() => ({}));
export const openRouterSchema = openAIBaseSchema
.merge(tConversationSchema.pick({ promptCache: true }))
.merge(tConversationSchema.pick({ promptCache: true, promptCacheTtl: true }))
.transform((obj: Partial<TConversation>) => removeNullishValues(obj, true))
.catch(() => ({}));
@ -1403,6 +1408,7 @@ export const anthropicBaseSchema = tConversationSchema.pick({
topK: true,
resendFiles: true,
promptCache: true,
promptCacheTtl: true,
thinking: true,
thinkingBudget: true,
effort: true,

View file

@ -53,6 +53,7 @@ export type TEndpointOption = Pick<
| 'additionalModelRequestFields'
// Anthropic-specific
| 'promptCache'
| 'promptCacheTtl'
| 'thinking'
| 'thinkingBudget'
| 'thinkingLevel'

View file

@ -87,6 +87,9 @@ export const conversationPreset: {
promptCache: {
type: BooleanConstructor;
};
promptCacheTtl: {
type: StringConstructor;
};
thinking: {
type: BooleanConstructor;
};
@ -256,6 +259,9 @@ export const conversationPreset: {
promptCache: {
type: Boolean,
},
promptCacheTtl: {
type: String,
},
thinking: {
type: Boolean,
},

View file

@ -28,6 +28,7 @@ export interface IPreset extends Document {
file_ids?: string[];
resendImages?: boolean;
promptCache?: boolean;
promptCacheTtl?: '5m' | '1h';
thinking?: boolean;
thinkingBudget?: number;
effort?: string;

View file

@ -27,6 +27,7 @@ export interface IConversation extends Document {
file_ids?: string[];
resendImages?: boolean;
promptCache?: boolean;
promptCacheTtl?: '5m' | '1h';
thinking?: boolean;
thinkingBudget?: number;
effort?: string;