mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(exa): bound search error bodies
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// Exa provider module implements model/runtime integration.
|
||||
import { parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
||||
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
||||
import {
|
||||
buildSearchCacheKey,
|
||||
DEFAULT_SEARCH_COUNT,
|
||||
@@ -28,6 +29,7 @@ const EXA_SEARCH_ENDPOINT = "https://api.exa.ai/search";
|
||||
const EXA_SEARCH_TYPES = ["auto", "neural", "fast", "deep", "deep-reasoning", "instant"] as const;
|
||||
const EXA_FRESHNESS_VALUES = ["day", "week", "month", "year"] as const;
|
||||
const EXA_MAX_SEARCH_COUNT = 100;
|
||||
const EXA_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
||||
|
||||
type ExaConfig = {
|
||||
apiKey?: string;
|
||||
@@ -76,6 +78,10 @@ async function readExaSearchResults(response: Response): Promise<ExaSearchResult
|
||||
}
|
||||
}
|
||||
|
||||
async function readExaErrorDetail(response: Response): Promise<string> {
|
||||
return await readResponseTextLimited(response, EXA_ERROR_BODY_LIMIT_BYTES);
|
||||
}
|
||||
|
||||
function normalizeExaFreshness(value: string | undefined): ExaFreshness | undefined {
|
||||
const trimmed = normalizeOptionalLowercaseString(value);
|
||||
if (!trimmed) {
|
||||
@@ -407,7 +413,7 @@ async function runExaSearch(params: {
|
||||
},
|
||||
async (res) => {
|
||||
if (!res.ok) {
|
||||
const detail = await res.text();
|
||||
const detail = await readExaErrorDetail(res);
|
||||
throw new Error(`Exa API error (${res.status}): ${detail || res.statusText}`);
|
||||
}
|
||||
return readExaSearchResults(res);
|
||||
@@ -607,6 +613,7 @@ export const testing = {
|
||||
resolveExaSearchCount,
|
||||
resolveExaSearchEndpoint,
|
||||
resolveFreshnessStartDate,
|
||||
readExaErrorDetail,
|
||||
readExaSearchResults,
|
||||
} as const;
|
||||
export { testing as __testing };
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
// Exa tests cover exa web search provider plugin behavior.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { testing } from "../test-api.js";
|
||||
import { createExaWebSearchProvider as createContractExaWebSearchProvider } from "../web-search-contract-api.js";
|
||||
import { createExaWebSearchProvider } from "./exa-web-search-provider.js";
|
||||
|
||||
function cancelTrackedResponse(
|
||||
text: string,
|
||||
init: ResponseInit,
|
||||
): {
|
||||
response: Response;
|
||||
wasCanceled: () => boolean;
|
||||
} {
|
||||
let canceled = false;
|
||||
const stream = new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode(text));
|
||||
},
|
||||
cancel() {
|
||||
canceled = true;
|
||||
},
|
||||
});
|
||||
return {
|
||||
response: new Response(stream, init),
|
||||
wasCanceled: () => canceled,
|
||||
};
|
||||
}
|
||||
|
||||
describe("exa web search provider", () => {
|
||||
it("exposes the expected metadata and selection wiring", () => {
|
||||
const provider = createExaWebSearchProvider();
|
||||
@@ -242,4 +264,20 @@ describe("exa web search provider", () => {
|
||||
"Exa API returned malformed JSON",
|
||||
);
|
||||
});
|
||||
|
||||
it("bounds Exa API error bodies without using response.text()", async () => {
|
||||
const tracked = cancelTrackedResponse(`${"exa upstream unavailable ".repeat(1024)}tail`, {
|
||||
status: 503,
|
||||
headers: { "content-type": "text/plain" },
|
||||
});
|
||||
const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
|
||||
|
||||
const detail = await testing.readExaErrorDetail(tracked.response);
|
||||
|
||||
expect(detail).toContain("exa upstream unavailable");
|
||||
expect(detail).not.toContain("tail");
|
||||
expect(await testing.readExaErrorDetail(new Response("short"))).toBe("short");
|
||||
expect(tracked.wasCanceled()).toBe(true);
|
||||
expect(textSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user