openclaw/extensions/xai/src/code-execution-shared.ts

130 lines
3.8 KiB
TypeScript

import { normalizeXaiModelId } from "openclaw/plugin-sdk/provider-models";
import { postTrustedWebToolsJson } from "openclaw/plugin-sdk/provider-web-search";
import { extractXaiWebSearchContent, type XaiWebSearchResponse } from "./web-search-shared.js";
export const XAI_CODE_EXECUTION_ENDPOINT = "https://api.x.ai/v1/responses";
export const XAI_DEFAULT_CODE_EXECUTION_MODEL = "grok-4-1-fast";
export type XaiCodeExecutionConfig = {
apiKey?: unknown;
model?: unknown;
maxTurns?: unknown;
};
export type XaiCodeExecutionResponse = XaiWebSearchResponse & {
output?: Array<{
type?: string;
}>;
};
export type XaiCodeExecutionResult = {
content: string;
citations: string[];
usedCodeExecution: boolean;
outputTypes: string[];
};
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
export function resolveXaiCodeExecutionConfig(
config?: Record<string, unknown>,
): XaiCodeExecutionConfig {
return isRecord(config) ? (config as XaiCodeExecutionConfig) : {};
}
export function resolveXaiCodeExecutionModel(config?: Record<string, unknown>): string {
const resolved = resolveXaiCodeExecutionConfig(config);
return typeof resolved.model === "string" && resolved.model.trim()
? normalizeXaiModelId(resolved.model.trim())
: XAI_DEFAULT_CODE_EXECUTION_MODEL;
}
export function resolveXaiCodeExecutionMaxTurns(
config?: Record<string, unknown>,
): number | undefined {
const raw = resolveXaiCodeExecutionConfig(config).maxTurns;
if (typeof raw !== "number" || !Number.isFinite(raw)) {
return undefined;
}
const normalized = Math.trunc(raw);
return normalized > 0 ? normalized : undefined;
}
export function buildXaiCodeExecutionPayload(params: {
task: string;
model: string;
tookMs: number;
content: string;
citations: string[];
usedCodeExecution: boolean;
outputTypes: string[];
}): Record<string, unknown> {
return {
task: params.task,
provider: "xai",
model: params.model,
tookMs: params.tookMs,
content: params.content,
citations: params.citations,
usedCodeExecution: params.usedCodeExecution,
outputTypes: params.outputTypes,
};
}
export async function requestXaiCodeExecution(params: {
apiKey: string;
model: string;
timeoutSeconds: number;
maxTurns?: number;
task: string;
}): Promise<XaiCodeExecutionResult> {
return await postTrustedWebToolsJson(
{
url: XAI_CODE_EXECUTION_ENDPOINT,
timeoutSeconds: params.timeoutSeconds,
apiKey: params.apiKey,
body: {
model: params.model,
input: [{ role: "user", content: params.task }],
tools: [{ type: "code_interpreter" }],
...(params.maxTurns ? { max_turns: params.maxTurns } : {}),
},
errorLabel: "xAI",
},
async (response) => {
const data = (await response.json()) as XaiCodeExecutionResponse;
const { text, annotationCitations } = extractXaiWebSearchContent(data);
const outputTypes = Array.isArray(data.output)
? [
...new Set(
data.output
.map((entry) => entry?.type)
.filter((value): value is string => Boolean(value)),
),
]
: [];
const citations =
Array.isArray(data.citations) && data.citations.length > 0
? data.citations
: annotationCitations;
return {
content: text ?? "No response",
citations,
usedCodeExecution: outputTypes.includes("code_interpreter_call"),
outputTypes,
};
},
);
}
export const __testing = {
buildXaiCodeExecutionPayload,
requestXaiCodeExecution,
resolveXaiCodeExecutionConfig,
resolveXaiCodeExecutionMaxTurns,
resolveXaiCodeExecutionModel,
XAI_DEFAULT_CODE_EXECUTION_MODEL,
} as const;