


























@@ -1,6 +1,6 @@
11import {
22createProviderHttpError,
3-readProviderJsonResponse,
3+readProviderJsonObjectResponse,
44} from "openclaw/plugin-sdk/provider-http";
55import type { OpenClawConfig } from "openclaw/plugin-sdk/provider-onboard";
66import {
@@ -84,6 +84,14 @@ type KimiSearchResult = {
8484grounded: boolean;
8585};
868687+function isRecord(value: unknown): value is Record<string, unknown> {
88+return typeof value === "object" && value !== null && !Array.isArray(value);
89+}
90+91+function throwMalformedKimiResponse(): never {
92+throw new Error("Kimi API error: malformed JSON response");
93+}
94+8795function resolveKimiConfig(searchConfig?: SearchConfigRecord): KimiConfig {
8896const kimi = searchConfig?.kimi;
8997return kimi && typeof kimi === "object" && !Array.isArray(kimi) ? (kimi as KimiConfig) : {};
@@ -132,13 +140,30 @@ function extractKimiMessageText(message: KimiMessage | undefined): string | unde
132140}
133141134142function extractKimiCitations(data: KimiSearchResponse): string[] {
135-const citations = (data.search_results ?? [])
136-.map((entry) => entry.url?.trim())
143+const searchResults = data.search_results ?? [];
144+if (!Array.isArray(searchResults)) {
145+throwMalformedKimiResponse();
146+}
147+const citations = searchResults
148+.map((entry) => (isRecord(entry) && typeof entry.url === "string" ? entry.url.trim() : ""))
137149.filter((url): url is string => Boolean(url));
138150139-for (const toolCall of data.choices?.[0]?.message?.tool_calls ?? []) {
140-const rawArguments = toolCall.function?.arguments;
141-if (!rawArguments) {
151+const choices = data.choices ?? [];
152+if (!Array.isArray(choices)) {
153+throwMalformedKimiResponse();
154+}
155+const firstChoice = choices[0];
156+const message = firstChoice && isRecord(firstChoice.message) ? firstChoice.message : undefined;
157+const toolCalls = message?.tool_calls ?? [];
158+if (!Array.isArray(toolCalls)) {
159+throwMalformedKimiResponse();
160+}
161+for (const toolCall of toolCalls) {
162+if (!isRecord(toolCall) || !isRecord(toolCall.function)) {
163+continue;
164+}
165+const rawArguments = toolCall.function.arguments;
166+if (typeof rawArguments !== "string" || !rawArguments) {
142167continue;
143168}
144169try {
@@ -165,11 +190,16 @@ function extractKimiCitations(data: KimiSearchResponse): string[] {
165190}
166191167192function hasKimiSearchResults(data: KimiSearchResponse): boolean {
168-return (data.search_results ?? []).some(
193+const searchResults = data.search_results ?? [];
194+if (!Array.isArray(searchResults)) {
195+throwMalformedKimiResponse();
196+}
197+return searchResults.some(
169198(entry) =>
170-Boolean(normalizeOptionalString(entry.url)) ||
171-Boolean(normalizeOptionalString(entry.title)) ||
172-Boolean(normalizeOptionalString(entry.content)),
199+isRecord(entry) &&
200+(Boolean(normalizeOptionalString(entry.url)) ||
201+Boolean(normalizeOptionalString(entry.title)) ||
202+Boolean(normalizeOptionalString(entry.content))),
173203);
174204}
175205@@ -219,7 +249,13 @@ async function runKimiSearch(params: {
219249throw await createProviderHttpError(res, "Kimi API error");
220250}
221251222-const data = await readProviderJsonResponse<KimiSearchResponse>(res, "Kimi API error");
252+const data = (await readProviderJsonObjectResponse(
253+res,
254+"Kimi API error",
255+)) as KimiSearchResponse;
256+if (!Array.isArray(data.choices)) {
257+throwMalformedKimiResponse();
258+}
223259if (hasKimiSearchResults(data)) {
224260hasGroundingEvidence = true;
225261}
@@ -230,14 +266,23 @@ async function runKimiSearch(params: {
230266hasGroundingEvidence = true;
231267}
232268const choice = data.choices?.[0];
269+if (!isRecord(choice) || !isRecord(choice.message)) {
270+throwMalformedKimiResponse();
271+}
233272const message = choice?.message;
234273const text = extractKimiMessageText(message);
235274const toolCalls = message?.tool_calls ?? [];
275+if (!Array.isArray(toolCalls)) {
276+throwMalformedKimiResponse();
277+}
236278237279if (choice?.finish_reason !== "tool_calls" || toolCalls.length === 0) {
280+if (!text) {
281+throwMalformedKimiResponse();
282+}
238283return {
239284done: true,
240-content: text ?? "No response",
285+content: text,
241286citations: [...collectedCitations],
242287};
243288}
@@ -269,9 +314,12 @@ async function runKimiSearch(params: {
269314});
270315}
271316if (!pushed) {
317+if (!text) {
318+throwMalformedKimiResponse();
319+}
272320return {
273321done: true,
274-content: text ?? "No response",
322+content: text,
275323citations: [...collectedCitations],
276324};
277325}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。