mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-01 23:21:42 +00:00
fix(moonshot): forward abort signal through Kimi web search provider (#105125)
This commit is contained in:
committed by
GitHub
parent
81e392b7ff
commit
ebb301c32d
@@ -216,6 +216,7 @@ async function runKimiSearch(params: {
|
||||
baseUrl: string;
|
||||
model: string;
|
||||
timeoutSeconds: number;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<KimiSearchResult> {
|
||||
const endpoint = `${params.baseUrl.trim().replace(/\/$/, "")}/chat/completions`;
|
||||
const messages: Array<Record<string, unknown>> = [{ role: "user", content: params.query }];
|
||||
@@ -227,6 +228,7 @@ async function runKimiSearch(params: {
|
||||
{
|
||||
url: endpoint,
|
||||
timeoutSeconds: params.timeoutSeconds,
|
||||
signal: params.signal,
|
||||
init: {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -341,6 +343,7 @@ async function runKimiSearch(params: {
|
||||
export async function executeKimiWebSearchProviderTool(
|
||||
ctx: { config?: OpenClawConfig; searchConfig?: SearchConfigRecord },
|
||||
args: Record<string, unknown>,
|
||||
opts?: { signal?: AbortSignal },
|
||||
): Promise<Record<string, unknown>> {
|
||||
const searchConfig = mergeScopedSearchConfig(
|
||||
ctx.searchConfig,
|
||||
@@ -392,6 +395,7 @@ export async function executeKimiWebSearchProviderTool(
|
||||
baseUrl,
|
||||
model,
|
||||
timeoutSeconds: resolveSearchTimeoutSeconds(searchConfig),
|
||||
signal: opts?.signal,
|
||||
});
|
||||
if (!result.grounded) {
|
||||
return {
|
||||
|
||||
@@ -1,31 +1,12 @@
|
||||
// Moonshot tests cover kimi web search provider plugin behavior.
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/provider-onboard";
|
||||
import { withEnvAsync } from "openclaw/plugin-sdk/test-env";
|
||||
import { withEnv, withEnvAsync } from "openclaw/plugin-sdk/test-env";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { testing } from "../test-api.js";
|
||||
import { createKimiWebSearchProvider } from "./kimi-web-search-provider.js";
|
||||
|
||||
const kimiApiKeyEnv = ["KIMI_API", "KEY"].join("_");
|
||||
|
||||
function withEnv(overrides: Record<string, string>, run: () => void): void {
|
||||
const previous = new Map<string, string | undefined>();
|
||||
for (const [key, value] of Object.entries(overrides)) {
|
||||
previous.set(key, process.env[key]);
|
||||
process.env[key] = value;
|
||||
}
|
||||
try {
|
||||
run();
|
||||
} finally {
|
||||
for (const [key, value] of previous) {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function jsonResponse(body: unknown): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
status: 200,
|
||||
@@ -286,6 +267,42 @@ describe("kimi web search provider", () => {
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("forwards the execution abort signal to an in-flight Kimi search", async () => {
|
||||
const fetchMock = vi.fn(
|
||||
(_url: string, init?: RequestInit) =>
|
||||
new Promise<Response>((_resolve, reject) => {
|
||||
init?.signal?.addEventListener(
|
||||
"abort",
|
||||
() => reject(new Error(String(init.signal?.reason ?? "Aborted"))),
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
await withEnvAsync({ KIMI_API_KEY: "kimi-test-key" }, async () => {
|
||||
const controller = new AbortController();
|
||||
const tool = createKimiWebSearchProvider().createTool({ config: {}, searchConfig: {} });
|
||||
if (!tool) {
|
||||
throw new Error("Expected tool definition");
|
||||
}
|
||||
|
||||
const search = tool.execute(
|
||||
{ query: "unique Kimi abort regression" },
|
||||
{
|
||||
signal: controller.signal,
|
||||
},
|
||||
);
|
||||
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledOnce());
|
||||
controller.abort(new Error("Kimi search cancelled"));
|
||||
|
||||
await expect(search).rejects.toThrow("Kimi search cancelled");
|
||||
expect(fetchMock.mock.calls[0]?.[1]?.signal?.aborted).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("uses config apiKey when provided", () => {
|
||||
expect(testing.resolveKimiApiKey({ apiKey: "kimi-test-key" })).toBe("kimi-test-key");
|
||||
});
|
||||
|
||||
@@ -60,9 +60,11 @@ export function createKimiWebSearchProvider(): WebSearchProviderPlugin {
|
||||
description:
|
||||
"Search the web using Kimi by Moonshot. Returns AI-synthesized answers with citations from native $web_search.",
|
||||
parameters: KimiSearchSchema,
|
||||
execute: async (args) => {
|
||||
execute: async (args, context) => {
|
||||
const { executeKimiWebSearchProviderTool } = await loadKimiWebSearchProviderRuntime();
|
||||
return await executeKimiWebSearchProviderTool(ctx, args);
|
||||
return await executeKimiWebSearchProviderTool(ctx, args, {
|
||||
signal: context?.signal,
|
||||
});
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user