mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:00:50 +00:00
fix(voice-call): pin response model sessions
This commit is contained in:
@@ -4,6 +4,8 @@ import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js";
|
||||
import { generateVoiceResponse } from "./response-generator.js";
|
||||
|
||||
function createAgentRuntime(payloads: Array<Record<string, unknown>>) {
|
||||
const sessionStore: Record<string, { sessionId: string; updatedAt: number }> = {};
|
||||
const saveSessionStore = vi.fn(async () => {});
|
||||
const runEmbeddedPiAgent = vi.fn(async () => ({
|
||||
payloads,
|
||||
meta: { durationMs: 12, aborted: false },
|
||||
@@ -23,13 +25,13 @@ function createAgentRuntime(payloads: Array<Record<string, unknown>>) {
|
||||
runEmbeddedPiAgent,
|
||||
session: {
|
||||
resolveStorePath: () => "/tmp/openclaw/sessions.json",
|
||||
loadSessionStore: () => ({}),
|
||||
saveSessionStore: async () => {},
|
||||
loadSessionStore: () => sessionStore,
|
||||
saveSessionStore,
|
||||
resolveSessionFilePath: () => "/tmp/openclaw/sessions/session.jsonl",
|
||||
},
|
||||
} as unknown as CoreAgentDeps;
|
||||
|
||||
return { runtime, runEmbeddedPiAgent };
|
||||
return { runtime, runEmbeddedPiAgent, saveSessionStore, sessionStore };
|
||||
}
|
||||
|
||||
function requireEmbeddedAgentArgs(runEmbeddedPiAgent: ReturnType<typeof vi.fn>) {
|
||||
@@ -126,4 +128,39 @@ describe("generateVoiceResponse", () => {
|
||||
|
||||
expect(result.text).toBe("Absolutely. Tell me what you want to do next.");
|
||||
});
|
||||
|
||||
it("pins the voice session to responseModel before running the embedded agent", async () => {
|
||||
const { runtime, runEmbeddedPiAgent, saveSessionStore, sessionStore } = createAgentRuntime([
|
||||
{ text: '{"spoken":"Pinned model works."}' },
|
||||
]);
|
||||
const voiceConfig = VoiceCallConfigSchema.parse({
|
||||
responseModel: "openai/gpt-4.1-nano",
|
||||
responseTimeoutMs: 5000,
|
||||
});
|
||||
|
||||
const result = await generateVoiceResponse({
|
||||
voiceConfig,
|
||||
coreConfig: {} as CoreConfig,
|
||||
agentRuntime: runtime,
|
||||
callId: "call-123",
|
||||
from: "+15550001111",
|
||||
transcript: [{ speaker: "user", text: "hello there" }],
|
||||
userMessage: "hello there",
|
||||
});
|
||||
|
||||
expect(result.text).toBe("Pinned model works.");
|
||||
expect(sessionStore["voice:15550001111"]).toMatchObject({
|
||||
providerOverride: "openai",
|
||||
modelOverride: "gpt-4.1-nano",
|
||||
modelOverrideSource: "auto",
|
||||
});
|
||||
expect(saveSessionStore).toHaveBeenCalledWith("/tmp/openclaw/sessions.json", sessionStore);
|
||||
expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
provider: "openai",
|
||||
model: "gpt-4.1-nano",
|
||||
sessionKey: "voice:15550001111",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import crypto from "node:crypto";
|
||||
import { applyModelOverrideToSessionEntry } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { SessionEntry } from "../api.js";
|
||||
import type { VoiceCallConfig } from "./config.js";
|
||||
@@ -202,6 +203,7 @@ export async function generateVoiceResponse(
|
||||
const sessionStore = agentRuntime.session.loadSessionStore(storePath);
|
||||
const now = Date.now();
|
||||
let sessionEntry = sessionStore[sessionKey] as SessionEntry | undefined;
|
||||
let sessionEntryUpdated = false;
|
||||
|
||||
if (!sessionEntry) {
|
||||
sessionEntry = {
|
||||
@@ -209,16 +211,29 @@ export async function generateVoiceResponse(
|
||||
updatedAt: now,
|
||||
};
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await agentRuntime.session.saveSessionStore(storePath, sessionStore);
|
||||
sessionEntryUpdated = true;
|
||||
}
|
||||
|
||||
const sessionId = sessionEntry.sessionId;
|
||||
const sessionFile = agentRuntime.session.resolveSessionFilePath(sessionId, sessionEntry, {
|
||||
agentId,
|
||||
});
|
||||
|
||||
// Resolve model from config
|
||||
const { provider, model } = resolveVoiceResponseModel({ voiceConfig, agentRuntime });
|
||||
if (voiceConfig.responseModel) {
|
||||
sessionEntryUpdated =
|
||||
applyModelOverrideToSessionEntry({
|
||||
entry: sessionEntry,
|
||||
selection: { provider, model },
|
||||
selectionSource: "auto",
|
||||
}).updated || sessionEntryUpdated;
|
||||
}
|
||||
|
||||
if (sessionEntryUpdated) {
|
||||
await agentRuntime.session.saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
|
||||
const sessionFile = agentRuntime.session.resolveSessionFilePath(sessionId, sessionEntry, {
|
||||
agentId,
|
||||
});
|
||||
|
||||
// Resolve thinking level
|
||||
const thinkLevel = agentRuntime.resolveThinkingDefault({ cfg, provider, model });
|
||||
|
||||
Reference in New Issue
Block a user