mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 20:20:42 +00:00
fix: make conversation labels work with Codex
This commit is contained in:
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const completeSimple = vi.hoisted(() => vi.fn());
|
||||
const getRuntimeAuthForModel = vi.hoisted(() => vi.fn());
|
||||
const logVerbose = vi.hoisted(() => vi.fn());
|
||||
const requireApiKey = vi.hoisted(() => vi.fn());
|
||||
const resolveDefaultModelForAgent = vi.hoisted(() => vi.fn());
|
||||
const resolveModelAsync = vi.hoisted(() => vi.fn());
|
||||
@@ -18,6 +19,8 @@ vi.mock("@mariozechner/pi-ai", async () => {
|
||||
|
||||
vi.mock("../../agents/model-auth.js", () => ({ requireApiKey }));
|
||||
|
||||
vi.mock("../../globals.js", () => ({ logVerbose }));
|
||||
|
||||
vi.mock("../../agents/model-selection.js", () => ({
|
||||
resolveDefaultModelForAgent,
|
||||
}));
|
||||
@@ -40,6 +43,7 @@ describe("generateConversationLabel", () => {
|
||||
beforeEach(() => {
|
||||
completeSimple.mockReset();
|
||||
getRuntimeAuthForModel.mockReset();
|
||||
logVerbose.mockReset();
|
||||
requireApiKey.mockReset();
|
||||
resolveDefaultModelForAgent.mockReset();
|
||||
resolveModelAsync.mockReset();
|
||||
@@ -88,4 +92,70 @@ describe("generateConversationLabel", () => {
|
||||
cfg: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("passes the label prompt as systemPrompt and the user text as message content", async () => {
|
||||
await generateConversationLabel({
|
||||
userMessage: "Need help with invoices",
|
||||
prompt: "Generate a label",
|
||||
cfg: {},
|
||||
});
|
||||
|
||||
expect(completeSimple).toHaveBeenCalledWith(
|
||||
{ provider: "openai" },
|
||||
{
|
||||
systemPrompt: "Generate a label",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: "Need help with invoices",
|
||||
timestamp: expect.any(Number),
|
||||
},
|
||||
],
|
||||
},
|
||||
expect.objectContaining({
|
||||
apiKey: "resolved-key",
|
||||
maxTokens: 100,
|
||||
temperature: 0.3,
|
||||
signal: expect.any(AbortSignal),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("omits temperature for Codex Responses simple completions", async () => {
|
||||
resolveDefaultModelForAgent.mockReturnValue({ provider: "openai-codex", model: "gpt-5.5" });
|
||||
resolveModelAsync.mockResolvedValue({
|
||||
model: { provider: "openai-codex", api: "openai-codex-responses" },
|
||||
authStorage: {},
|
||||
modelRegistry: {},
|
||||
});
|
||||
|
||||
await generateConversationLabel({
|
||||
userMessage: "тест создания топика-треда",
|
||||
prompt: "Generate a label",
|
||||
cfg: {},
|
||||
});
|
||||
|
||||
expect(completeSimple.mock.calls[0]?.[2]).toEqual(
|
||||
expect.not.objectContaining({ temperature: expect.anything() }),
|
||||
);
|
||||
});
|
||||
|
||||
it("logs completion errors instead of treating them as empty labels", async () => {
|
||||
completeSimple.mockResolvedValue({
|
||||
content: [],
|
||||
stopReason: "error",
|
||||
errorMessage: "Codex error: Instructions are required",
|
||||
});
|
||||
|
||||
const label = await generateConversationLabel({
|
||||
userMessage: "Need help with invoices",
|
||||
prompt: "Generate a label",
|
||||
cfg: {},
|
||||
});
|
||||
|
||||
expect(label).toBeNull();
|
||||
expect(logVerbose).toHaveBeenCalledWith(
|
||||
"conversation-label-generator: completion failed: Codex error: Instructions are required",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,6 +23,20 @@ function isTextContentBlock(block: { type: string }): block is TextContent {
|
||||
return block.type === "text";
|
||||
}
|
||||
|
||||
function isCodexSimpleCompletionModel(model: { api?: string; provider?: string }): boolean {
|
||||
return model.provider === "openai-codex" || model.api === "openai-codex-responses";
|
||||
}
|
||||
|
||||
function extractSimpleCompletionError(result: {
|
||||
stopReason?: string;
|
||||
errorMessage?: string;
|
||||
}): string | null {
|
||||
if (result.stopReason !== "error") {
|
||||
return null;
|
||||
}
|
||||
return result.errorMessage?.trim() || "unknown error";
|
||||
}
|
||||
|
||||
export async function generateConversationLabel(
|
||||
params: ConversationLabelParams,
|
||||
): Promise<string | null> {
|
||||
@@ -58,10 +72,11 @@ export async function generateConversationLabel(
|
||||
const result = await completeSimple(
|
||||
completionModel,
|
||||
{
|
||||
systemPrompt: prompt,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: `${prompt}\n\n${userMessage}`,
|
||||
content: userMessage,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
],
|
||||
@@ -69,10 +84,15 @@ export async function generateConversationLabel(
|
||||
{
|
||||
apiKey,
|
||||
maxTokens: 100,
|
||||
temperature: 0.3,
|
||||
...(isCodexSimpleCompletionModel(completionModel) ? {} : { temperature: 0.3 }),
|
||||
signal: controller.signal,
|
||||
},
|
||||
);
|
||||
const errorMessage = extractSimpleCompletionError(result);
|
||||
if (errorMessage) {
|
||||
logVerbose(`conversation-label-generator: completion failed: ${errorMessage}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const text = result.content
|
||||
.filter(isTextContentBlock)
|
||||
|
||||
Reference in New Issue
Block a user