fix: preserve context window during resume preset refresh (#1353)
Co-authored-by: Letta Code <noreply@letta.com>
This commit is contained in:
@@ -182,21 +182,32 @@ function buildModelSettings(
|
|||||||
* @param agentId - The agent ID
|
* @param agentId - The agent ID
|
||||||
* @param modelHandle - The model handle (e.g., "anthropic/claude-sonnet-4-5-20250929")
|
* @param modelHandle - The model handle (e.g., "anthropic/claude-sonnet-4-5-20250929")
|
||||||
* @param updateArgs - Additional config args (context_window, reasoning_effort, enable_reasoner, etc.)
|
* @param updateArgs - Additional config args (context_window, reasoning_effort, enable_reasoner, etc.)
|
||||||
* @param preserveParallelToolCalls - If true, preserves the parallel_tool_calls setting when updating the model
|
* @param options - Optional update behavior overrides
|
||||||
* @returns The updated agent state from the server (includes llm_config and model_settings)
|
* @returns The updated agent state from the server (includes llm_config and model_settings)
|
||||||
*/
|
*/
|
||||||
|
export interface UpdateAgentLLMConfigOptions {
|
||||||
|
preserveContextWindow?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateAgentLLMConfig(
|
export async function updateAgentLLMConfig(
|
||||||
agentId: string,
|
agentId: string,
|
||||||
modelHandle: string,
|
modelHandle: string,
|
||||||
updateArgs?: Record<string, unknown>,
|
updateArgs?: Record<string, unknown>,
|
||||||
|
options?: UpdateAgentLLMConfigOptions,
|
||||||
): Promise<AgentState> {
|
): Promise<AgentState> {
|
||||||
const client = await getClient();
|
const client = await getClient();
|
||||||
|
|
||||||
const modelSettings = buildModelSettings(modelHandle, updateArgs);
|
const modelSettings = buildModelSettings(modelHandle, updateArgs);
|
||||||
// First try updateArgs, then fall back to API-cached context window for BYOK models
|
const explicitContextWindow = updateArgs?.context_window as
|
||||||
|
| number
|
||||||
|
| undefined;
|
||||||
|
const shouldPreserveContextWindow = options?.preserveContextWindow === true;
|
||||||
|
// Resume refresh updates should not implicitly reset context window.
|
||||||
const contextWindow =
|
const contextWindow =
|
||||||
(updateArgs?.context_window as number | undefined) ??
|
explicitContextWindow ??
|
||||||
(await getModelContextWindow(modelHandle));
|
(!shouldPreserveContextWindow
|
||||||
|
? await getModelContextWindow(modelHandle)
|
||||||
|
: undefined);
|
||||||
const hasModelSettings = Object.keys(modelSettings).length > 0;
|
const hasModelSettings = Object.keys(modelSettings).length > 0;
|
||||||
|
|
||||||
// MiniMax doesn't have a dedicated ModelSettings class, so model_settings
|
// MiniMax doesn't have a dedicated ModelSettings class, so model_settings
|
||||||
|
|||||||
@@ -918,6 +918,7 @@ export async function handleHeadlessCommand(
|
|||||||
agent.id,
|
agent.id,
|
||||||
presetRefresh.modelHandle,
|
presetRefresh.modelHandle,
|
||||||
resumeRefreshUpdateArgs,
|
resumeRefreshUpdateArgs,
|
||||||
|
{ preserveContextWindow: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1761,6 +1761,7 @@ async function main(): Promise<void> {
|
|||||||
agent.id,
|
agent.id,
|
||||||
presetRefresh.modelHandle,
|
presetRefresh.modelHandle,
|
||||||
resumeRefreshUpdateArgs,
|
resumeRefreshUpdateArgs,
|
||||||
|
{ preserveContextWindow: true },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ describe("model preset refresh wiring", () => {
|
|||||||
expect(source).toContain("getModelInfoForLlmConfig(modelHandle");
|
expect(source).toContain("getModelInfoForLlmConfig(modelHandle");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("modify.ts keeps direct updateArgs-driven model update flow", () => {
|
test("modify.ts supports preserving context window during resume refresh", () => {
|
||||||
const path = fileURLToPath(
|
const path = fileURLToPath(
|
||||||
new URL("../../agent/modify.ts", import.meta.url),
|
new URL("../../agent/modify.ts", import.meta.url),
|
||||||
);
|
);
|
||||||
@@ -32,7 +32,14 @@ describe("model preset refresh wiring", () => {
|
|||||||
expect(updateSegment).toContain(
|
expect(updateSegment).toContain(
|
||||||
"buildModelSettings(modelHandle, updateArgs)",
|
"buildModelSettings(modelHandle, updateArgs)",
|
||||||
);
|
);
|
||||||
|
expect(source).toContain("export interface UpdateAgentLLMConfigOptions");
|
||||||
|
expect(updateSegment).toContain("options?: UpdateAgentLLMConfigOptions");
|
||||||
|
expect(updateSegment).toContain("shouldPreserveContextWindow");
|
||||||
expect(updateSegment).toContain("getModelContextWindow(modelHandle)");
|
expect(updateSegment).toContain("getModelContextWindow(modelHandle)");
|
||||||
|
expect(updateSegment).toContain("options?.preserveContextWindow === true");
|
||||||
|
expect(updateSegment).not.toContain(
|
||||||
|
"(updateArgs?.context_window as number | undefined) ??\n (await getModelContextWindow(modelHandle));",
|
||||||
|
);
|
||||||
expect(updateSegment).not.toContain(
|
expect(updateSegment).not.toContain(
|
||||||
"const currentAgent = await client.agents.retrieve(",
|
"const currentAgent = await client.agents.retrieve(",
|
||||||
);
|
);
|
||||||
@@ -175,6 +182,9 @@ describe("model preset refresh wiring", () => {
|
|||||||
expect(source).toContain("needsUpdate");
|
expect(source).toContain("needsUpdate");
|
||||||
expect(source).toContain("await updateAgentLLMConfig(");
|
expect(source).toContain("await updateAgentLLMConfig(");
|
||||||
expect(source).toContain("presetRefresh.modelHandle");
|
expect(source).toContain("presetRefresh.modelHandle");
|
||||||
|
expect(source).toMatch(
|
||||||
|
/resumeRefreshUpdateArgs,\s*\{\s*preserveContextWindow:\s*true\s*\},/,
|
||||||
|
);
|
||||||
expect(source).not.toContain(
|
expect(source).not.toContain(
|
||||||
"await updateAgentLLMConfig(\n agent.id,\n presetRefresh.modelHandle,\n presetRefresh.updateArgs,",
|
"await updateAgentLLMConfig(\n agent.id,\n presetRefresh.modelHandle,\n presetRefresh.updateArgs,",
|
||||||
);
|
);
|
||||||
@@ -194,6 +204,9 @@ describe("model preset refresh wiring", () => {
|
|||||||
expect(source).toContain("needsUpdate");
|
expect(source).toContain("needsUpdate");
|
||||||
expect(source).toContain("await updateAgentLLMConfig(");
|
expect(source).toContain("await updateAgentLLMConfig(");
|
||||||
expect(source).toContain("presetRefresh.modelHandle");
|
expect(source).toContain("presetRefresh.modelHandle");
|
||||||
|
expect(source).toMatch(
|
||||||
|
/resumeRefreshUpdateArgs,\s*\{\s*preserveContextWindow:\s*true\s*\},/,
|
||||||
|
);
|
||||||
expect(source).not.toContain(
|
expect(source).not.toContain(
|
||||||
"await updateAgentLLMConfig(\n agent.id,\n presetRefresh.modelHandle,\n presetRefresh.updateArgs,",
|
"await updateAgentLLMConfig(\n agent.id,\n presetRefresh.modelHandle,\n presetRefresh.updateArgs,",
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user