test: tighten mistral media and onboarding coverage
This commit is contained in:
@@ -5,7 +5,9 @@ import { resolveMemorySearchConfig } from "./memory-search.js";
|
||||
const asConfig = (cfg: OpenClawConfig): OpenClawConfig => cfg;
|
||||
|
||||
describe("memory search config", () => {
|
||||
function configWithDefaultProvider(provider: "openai" | "local" | "gemini"): OpenClawConfig {
|
||||
function configWithDefaultProvider(
|
||||
provider: "openai" | "local" | "gemini" | "mistral",
|
||||
): OpenClawConfig {
|
||||
return asConfig({
|
||||
agents: {
|
||||
defaults: {
|
||||
@@ -147,6 +149,13 @@ describe("memory search config", () => {
|
||||
expectDefaultRemoteBatch(resolved);
|
||||
});
|
||||
|
||||
it("includes remote defaults and model default for mistral without overrides", () => {
|
||||
const cfg = configWithDefaultProvider("mistral");
|
||||
const resolved = resolveMemorySearchConfig(cfg, "main");
|
||||
expectDefaultRemoteBatch(resolved);
|
||||
expect(resolved?.model).toBe("mistral-embed");
|
||||
});
|
||||
|
||||
it("defaults session delta thresholds", () => {
|
||||
const cfg = asConfig({
|
||||
agents: {
|
||||
|
||||
@@ -14,7 +14,12 @@ vi.mock("../../commands/auth-choice-options.js", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/onboard-provider-auth-flags.js", () => ({
|
||||
ONBOARD_PROVIDER_AUTH_FLAGS: [] as Array<{ cliOption: string; description: string }>,
|
||||
ONBOARD_PROVIDER_AUTH_FLAGS: [
|
||||
{
|
||||
cliOption: "--mistral-api-key <key>",
|
||||
description: "Mistral API key",
|
||||
},
|
||||
] as Array<{ cliOption: string; description: string }>,
|
||||
}));
|
||||
|
||||
vi.mock("../../commands/onboard.js", () => ({
|
||||
@@ -103,6 +108,16 @@ describe("registerOnboardCommand", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("parses --mistral-api-key and forwards mistralApiKey", async () => {
|
||||
await runCli(["onboard", "--mistral-api-key", "sk-mistral-test"]);
|
||||
expect(onboardCommandMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
mistralApiKey: "sk-mistral-test",
|
||||
}),
|
||||
runtime,
|
||||
);
|
||||
});
|
||||
|
||||
it("reports errors via runtime on onboard command failures", async () => {
|
||||
onboardCommandMock.mockRejectedValueOnce(new Error("onboard failed"));
|
||||
|
||||
|
||||
@@ -45,4 +45,56 @@ describe("live tool probe utils", () => {
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("retries when tool output is empty and attempts remain", () => {
|
||||
expect(
|
||||
shouldRetryToolReadProbe({
|
||||
text: " ",
|
||||
nonceA: "nonce-a",
|
||||
nonceB: "nonce-b",
|
||||
provider: "openai",
|
||||
attempt: 0,
|
||||
maxAttempts: 3,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("retries when output still looks like tool/function scaffolding", () => {
|
||||
expect(
|
||||
shouldRetryToolReadProbe({
|
||||
text: "Use tool function read[] now.",
|
||||
nonceA: "nonce-a",
|
||||
nonceB: "nonce-b",
|
||||
provider: "openai",
|
||||
attempt: 0,
|
||||
maxAttempts: 3,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("retries mistral nonce marker echoes without parsed nonce values", () => {
|
||||
expect(
|
||||
shouldRetryToolReadProbe({
|
||||
text: "nonceA= nonceB=",
|
||||
nonceA: "nonce-a",
|
||||
nonceB: "nonce-b",
|
||||
provider: "mistral",
|
||||
attempt: 0,
|
||||
maxAttempts: 3,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("does not retry nonce marker echoes for non-mistral providers", () => {
|
||||
expect(
|
||||
shouldRetryToolReadProbe({
|
||||
text: "nonceA= nonceB=",
|
||||
nonceA: "nonce-a",
|
||||
nonceB: "nonce-b",
|
||||
provider: "openai",
|
||||
attempt: 0,
|
||||
maxAttempts: 3,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -109,47 +109,69 @@ describe("runCapability auto audio entries", () => {
|
||||
});
|
||||
|
||||
it("uses mistral when only mistral key is configured", async () => {
|
||||
const priorEnv: Record<string, string | undefined> = {
|
||||
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
|
||||
GROQ_API_KEY: process.env.GROQ_API_KEY,
|
||||
DEEPGRAM_API_KEY: process.env.DEEPGRAM_API_KEY,
|
||||
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
|
||||
MISTRAL_API_KEY: process.env.MISTRAL_API_KEY,
|
||||
};
|
||||
delete process.env.OPENAI_API_KEY;
|
||||
delete process.env.GROQ_API_KEY;
|
||||
delete process.env.DEEPGRAM_API_KEY;
|
||||
delete process.env.GEMINI_API_KEY;
|
||||
process.env.MISTRAL_API_KEY = "mistral-test-key";
|
||||
let runResult: Awaited<ReturnType<typeof runCapability>> | undefined;
|
||||
await withAudioFixture("openclaw-auto-audio-mistral", async ({ ctx, media, cache }) => {
|
||||
const providerRegistry = buildProviderRegistry({
|
||||
openai: {
|
||||
id: "openai",
|
||||
capabilities: ["audio"],
|
||||
transcribeAudio: async () => ({ text: "openai", model: "gpt-4o-mini-transcribe" }),
|
||||
},
|
||||
mistral: {
|
||||
id: "mistral",
|
||||
capabilities: ["audio"],
|
||||
transcribeAudio: async (req) => ({ text: "mistral", model: req.model ?? "unknown" }),
|
||||
},
|
||||
});
|
||||
const cfg = {
|
||||
models: {
|
||||
providers: {
|
||||
mistral: {
|
||||
apiKey: "mistral-test-key",
|
||||
models: [],
|
||||
try {
|
||||
await withAudioFixture("openclaw-auto-audio-mistral", async ({ ctx, media, cache }) => {
|
||||
const providerRegistry = buildProviderRegistry({
|
||||
openai: {
|
||||
id: "openai",
|
||||
capabilities: ["audio"],
|
||||
transcribeAudio: async () => ({ text: "openai", model: "gpt-4o-mini-transcribe" }),
|
||||
},
|
||||
mistral: {
|
||||
id: "mistral",
|
||||
capabilities: ["audio"],
|
||||
transcribeAudio: async (req) => ({ text: "mistral", model: req.model ?? "unknown" }),
|
||||
},
|
||||
});
|
||||
const cfg = {
|
||||
models: {
|
||||
providers: {
|
||||
mistral: {
|
||||
apiKey: "mistral-test-key",
|
||||
models: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
tools: {
|
||||
media: {
|
||||
audio: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as OpenClawConfig;
|
||||
} as unknown as OpenClawConfig;
|
||||
|
||||
runResult = await runCapability({
|
||||
capability: "audio",
|
||||
cfg,
|
||||
ctx,
|
||||
attachments: cache,
|
||||
media,
|
||||
providerRegistry,
|
||||
runResult = await runCapability({
|
||||
capability: "audio",
|
||||
cfg,
|
||||
ctx,
|
||||
attachments: cache,
|
||||
media,
|
||||
providerRegistry,
|
||||
});
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
for (const [key, value] of Object.entries(priorEnv)) {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!runResult) {
|
||||
throw new Error("Expected auto audio mistral result");
|
||||
}
|
||||
|
||||
19
src/memory/embeddings-mistral.test.ts
Normal file
19
src/memory/embeddings-mistral.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_MISTRAL_EMBEDDING_MODEL, normalizeMistralModel } from "./embeddings-mistral.js";
|
||||
|
||||
describe("normalizeMistralModel", () => {
|
||||
it("returns the default model for empty values", () => {
|
||||
expect(normalizeMistralModel("")).toBe(DEFAULT_MISTRAL_EMBEDDING_MODEL);
|
||||
expect(normalizeMistralModel(" ")).toBe(DEFAULT_MISTRAL_EMBEDDING_MODEL);
|
||||
});
|
||||
|
||||
it("strips the mistral/ prefix", () => {
|
||||
expect(normalizeMistralModel("mistral/mistral-embed")).toBe("mistral-embed");
|
||||
expect(normalizeMistralModel(" mistral/custom-embed ")).toBe("custom-embed");
|
||||
});
|
||||
|
||||
it("keeps explicit non-prefixed models", () => {
|
||||
expect(normalizeMistralModel("mistral-embed")).toBe("mistral-embed");
|
||||
expect(normalizeMistralModel("custom-embed-v2")).toBe("custom-embed-v2");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user