Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/consts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export const NEW_API_BASE_URL = "https://recoup-api.vercel.app";
export const RECOUP_API_KEY = process.env.RECOUP_API_KEY;
export const CODING_AGENT_ACCOUNT_ID = "ccbed42f-4d91-4834-b954-2a64a77d8665";
export const OPENCLAW_DEFAULT_MODEL = "vercel-ai-gateway/anthropic/claude-sonnet-4.6";
export const OPENCLAW_DEFAULT_MODEL = "vercel-ai-gateway/anthropic/claude-sonnet-4.6";
// Scheduled customer tasks run headless with no user to catch a hallucinated report.
// Sonnet 5 respects the data-grounding rule (refuses to fabricate) where the prior
// default (haiku-4.5) did not — verified on the OneRPM/Apache benchmark (recoupable/chat#1833).
export const DEFAULT_TASK_MODEL = "anthropic/claude-sonnet-5";
49 changes: 49 additions & 0 deletions src/tasks/__tests__/customerPromptTask.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, it, expect, vi, beforeEach } from "vitest";

const mockRun = vi.hoisted(() => vi.fn());
vi.mock("@trigger.dev/sdk/v3", () => ({
logger: { log: vi.fn(), warn: vi.fn(), error: vi.fn() },
tags: { add: vi.fn() },
schedules: {
task: (config: { run: unknown }) => {
mockRun.mockImplementation(config.run as (...args: unknown[]) => unknown);
return config;
},
},
}));

const mockFetchTask = vi.fn();
vi.mock("../../recoup/fetchTask", () => ({ fetchTask: (...a: unknown[]) => mockFetchTask(...a) }));
const mockGetTaskRoomId = vi.fn();
vi.mock("../../chats/getTaskRoomId", () => ({ getTaskRoomId: (...a: unknown[]) => mockGetTaskRoomId(...a) }));
const mockGenerateChat = vi.fn();
vi.mock("../../recoup/generateChat", () => ({ generateChat: (...a: unknown[]) => mockGenerateChat(...a) }));

// real chatSchema is used
import "../customerPromptTask";

const payload = { timestamp: new Date(0), timezone: "UTC", externalId: "ext-1" };

beforeEach(() => {
vi.clearAllMocks();
mockGetTaskRoomId.mockResolvedValue("room-1");
mockGenerateChat.mockResolvedValue(undefined);
});

describe("customerPromptTask model default", () => {
it("defaults the model to anthropic/claude-sonnet-5 when the task config omits it", async () => {
mockFetchTask.mockResolvedValue({ accountId: "acc-1", artistId: "art-1", prompt: "do a thing" });
await mockRun(payload);
expect(mockGenerateChat).toHaveBeenCalledWith(
expect.objectContaining({ model: "anthropic/claude-sonnet-5", accountId: "acc-1" }),
);
});

it("forwards an explicit task model unchanged", async () => {
mockFetchTask.mockResolvedValue({ accountId: "acc-1", model: "anthropic/claude-opus-4-8", prompt: "x" });
await mockRun(payload);
expect(mockGenerateChat).toHaveBeenCalledWith(
expect.objectContaining({ model: "anthropic/claude-opus-4-8" }),
);
});
});
3 changes: 2 additions & 1 deletion src/tasks/customerPromptTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getTaskRoomId } from "../chats/getTaskRoomId";
import { fetchTask } from "../recoup/fetchTask";
import { generateChat } from "../recoup/generateChat";
import { chatSchema, type ChatConfig } from "../schemas/chatSchema";
import { DEFAULT_TASK_MODEL } from "../consts";

type TaskPayload = {
// Provided automatically by Trigger.dev schedules
Expand Down Expand Up @@ -47,7 +48,7 @@ export const customerPromptTask = schedules.task({

const accountId = taskConfig?.accountId;
const artistId = taskConfig?.artistId;
const model = taskConfig?.model;
const model = taskConfig?.model ?? DEFAULT_TASK_MODEL;
const prompt =
taskConfig?.prompt ??
"Draft a friendly check-in message for our customers.";
Expand Down
Loading