mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 09:41:37 +00:00
fix(memory): reject malformed direct and batch embedding vectors (#117200)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
This commit is contained in:
committed by
GitHub
parent
1b3572d08b
commit
618f3298c7
@@ -173,6 +173,39 @@ describe("applyEmbeddingBatchOutputLine", () => {
|
||||
expect(byCustomId.get("req-1")).toEqual([0.1, 0.2]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "non-array", embedding: "poison" },
|
||||
{ name: "array-like object", embedding: { length: 1 } },
|
||||
{ name: "boolean", embedding: false },
|
||||
{ name: "string coordinate", embedding: ["poison"] },
|
||||
{ name: "null coordinate", embedding: [null] },
|
||||
{ name: "NaN coordinate", embedding: [Number.NaN] },
|
||||
{ name: "positive infinity coordinate", embedding: [Number.POSITIVE_INFINITY] },
|
||||
{ name: "negative infinity coordinate", embedding: [Number.NEGATIVE_INFINITY] },
|
||||
])("rejects a $name instead of storing it as a valid vector", ({ embedding }) => {
|
||||
const remaining = new Set(["req-invalid"]);
|
||||
const errors: string[] = [];
|
||||
const byCustomId = new Map<string, number[]>();
|
||||
|
||||
applyEmbeddingBatchOutputLine({
|
||||
line: {
|
||||
custom_id: "req-invalid",
|
||||
response: {
|
||||
status_code: 200,
|
||||
body: { data: [{ embedding: embedding as unknown as number[] }] },
|
||||
},
|
||||
},
|
||||
remaining,
|
||||
errors,
|
||||
byCustomId,
|
||||
});
|
||||
|
||||
expect(remaining.has("req-invalid")).toBe(false);
|
||||
expect(errors).toHaveLength(1);
|
||||
expect(errors[0]).toMatch(/^req-invalid: (?:empty|invalid) embedding$/);
|
||||
expect(byCustomId.size).toBe(0);
|
||||
});
|
||||
|
||||
it("records provider error from line.error", () => {
|
||||
const remaining = new Set(["req-2"]);
|
||||
const errors: string[] = [];
|
||||
|
||||
@@ -174,8 +174,8 @@ export function applyEmbeddingBatchOutputLine(params: {
|
||||
const data =
|
||||
response?.body && typeof response.body === "object" ? (response.body.data ?? []) : [];
|
||||
const embedding = data[0]?.embedding ?? [];
|
||||
if (embedding.length === 0) {
|
||||
params.errors.push(`${customId}: empty embedding`);
|
||||
if (!Array.isArray(embedding) || embedding.length === 0 || !embedding.every(Number.isFinite)) {
|
||||
params.errors.push(`${customId}: ${embedding?.length ? "invalid" : "empty"} embedding`);
|
||||
return;
|
||||
}
|
||||
params.byCustomId.set(customId, embedding);
|
||||
|
||||
@@ -124,6 +124,39 @@ describe("fetchRemoteEmbeddingVectors", () => {
|
||||
).rejects.toThrow("embedding fetch failed: malformed JSON response");
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "query", input: ["query"], data: [{ embedding: [] }] },
|
||||
{
|
||||
name: "document batch",
|
||||
input: ["first", "second"],
|
||||
data: [{ embedding: [0.1] }, { embedding: [] }],
|
||||
},
|
||||
])("rejects empty vectors in a $name response", async ({ input, data }) => {
|
||||
postJsonMock.mockImplementationOnce(async (params) => await params.parse({ data }));
|
||||
|
||||
await expect(
|
||||
fetchRemoteEmbeddingVectors({
|
||||
url: "https://memory.example/v1/embeddings",
|
||||
headers: {},
|
||||
body: { input },
|
||||
errorPrefix: "embedding fetch failed",
|
||||
}),
|
||||
).rejects.toThrow("embedding fetch failed: malformed JSON response");
|
||||
});
|
||||
|
||||
it("preserves an empty response for an empty submitted input batch", async () => {
|
||||
postJsonMock.mockImplementationOnce(async (params) => await params.parse({ data: [] }));
|
||||
|
||||
await expect(
|
||||
fetchRemoteEmbeddingVectors({
|
||||
url: "https://memory.example/v1/embeddings",
|
||||
headers: {},
|
||||
body: { input: [] },
|
||||
errorPrefix: "embedding fetch failed",
|
||||
}),
|
||||
).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("rejects wrong nested embedding vector types", async () => {
|
||||
postJsonMock.mockImplementationOnce(async (params) => {
|
||||
return await params.parse({ data: [{ embedding: [0.1, "bad"] }] });
|
||||
|
||||
@@ -18,7 +18,7 @@ function malformedEmbeddingResponse(errorPrefix: string): Error {
|
||||
|
||||
/** Validate and return one finite embedding vector. */
|
||||
function readEmbeddingVector(value: unknown, errorPrefix: string): number[] {
|
||||
if (!Array.isArray(value)) {
|
||||
if (!Array.isArray(value) || value.length === 0) {
|
||||
throw malformedEmbeddingResponse(errorPrefix);
|
||||
}
|
||||
for (const entry of value) {
|
||||
|
||||
Reference in New Issue
Block a user