openclaw/test/auto-reply.retry.test.ts
Jon Shapiro 92697edb7c fix(venice): add compat settings to prevent HTTP 400 errors
Venice's API doesn't support certain OpenAI-compatible parameters that
Clawdbot sends by default:

- `store`: Venice returns HTTP 400 with no body when this is present
- `developer` role: Not supported by Venice's API

This adds VENICE_COMPAT settings (supportsStore: false,
supportsDeveloperRole: false) to all Venice model definitions, both
from the static catalog and dynamically discovered models.

Fixes issues reported in PR #1666 where users experienced silent
failures (HTTP 400, no body) when using Venice models.

Co-authored-by: jonisjongithub <jonisjongithub@users.noreply.github.com>
Co-authored-by: Clawdbot <bot@clawd.bot>
2026-01-26 17:56:01 -08:00

90 lines
2.3 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
vi.mock("../src/web/media.js", () => ({
loadWebMedia: vi.fn(async () => ({
buffer: Buffer.from("img"),
contentType: "image/jpeg",
kind: "image",
fileName: "img.jpg",
})),
}));
import { defaultRuntime } from "../src/runtime.js";
import { deliverWebReply } from "../src/web/auto-reply.js";
import type { WebInboundMessage } from "../src/web/inbound.js";
const noopLogger = {
info: vi.fn(),
warn: vi.fn(),
};
function makeMsg(): WebInboundMessage {
const reply = vi.fn<
Parameters<WebInboundMessage["reply"]>,
ReturnType<WebInboundMessage["reply"]>
>();
const sendMedia = vi.fn<
Parameters<WebInboundMessage["sendMedia"]>,
ReturnType<WebInboundMessage["sendMedia"]>
>();
const sendComposing = vi.fn<
Parameters<WebInboundMessage["sendComposing"]>,
ReturnType<WebInboundMessage["sendComposing"]>
>();
return {
from: "+10000000000",
conversationId: "+10000000000",
to: "+20000000000",
id: "abc",
body: "hello",
chatType: "direct",
chatId: "chat-1",
sendComposing,
reply,
sendMedia,
};
}
describe("deliverWebReply retry", () => {
it("retries text send on transient failure", async () => {
const msg = makeMsg();
msg.reply.mockRejectedValueOnce(new Error("connection closed"));
msg.reply.mockResolvedValueOnce(undefined);
await expect(
deliverWebReply({
replyResult: { text: "hi" },
msg,
maxMediaBytes: 5_000_000,
replyLogger: noopLogger,
runtime: defaultRuntime,
skipLog: true,
}),
).resolves.toBeUndefined();
expect(msg.reply).toHaveBeenCalledTimes(2);
});
it("retries media send on transient failure", async () => {
const msg = makeMsg();
msg.sendMedia.mockRejectedValueOnce(new Error("socket reset"));
msg.sendMedia.mockResolvedValueOnce(undefined);
await expect(
deliverWebReply({
replyResult: {
text: "caption",
mediaUrl: "http://example.com/img.jpg",
},
msg,
maxMediaBytes: 5_000_000,
replyLogger: noopLogger,
runtime: defaultRuntime,
skipLog: true,
}),
).resolves.toBeUndefined();
expect(msg.sendMedia).toHaveBeenCalledTimes(2);
});
});