openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
192 lines (191 loc) • 10.3 kB
JavaScript
import { p as readStringValue } from "./string-coerce-mnp54Vah.js";
import { r as resolveProviderRequestCapabilities } from "./provider-attribution-GEHCfopl.js";
import { c as shouldOmitEmptyArrayItems } from "./provider-model-compat-DhKi-Qv5.js";
import { t as normalizeToolParameterSchema } from "./agent-tools-parameter-schema-CO-BQPfo.js";
//#region src/shared/openai-responses-stream-compat.ts
const OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE = "output_text";
const AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE = "text";
const AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE = "response.text.delta";
function isResponsesTextContentPartType(type) {
return type === "output_text" || type === "text";
}
function isResponsesTextDeltaEventType(type) {
return type === "response.output_text.delta" || type === "response.text.delta";
}
function isAzureResponsesTextDeltaEventType(type) {
return type === AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE;
}
function isAzureResponsesTextDeltaEvent(event) {
return isAzureResponsesTextDeltaEventType(event.type) && typeof event.delta === "string";
}
//#endregion
//#region src/agents/openai-strict-tool-setting.ts
/**
* Strict tool-schema default resolution for native OpenAI-compatible routes.
*
* Compatible providers can support strict schemas without inheriting OpenAI's required default.
*/
const optionalString = readStringValue;
function resolvesToNativeOpenAIStrictTools(model, transport) {
const capabilities = resolveProviderRequestCapabilities({
provider: optionalString(model.provider),
api: optionalString(model.api),
baseUrl: optionalString(model.baseUrl),
capability: "llm",
transport,
modelId: optionalString(model.id),
compat: model.compat
});
if (!capabilities.usesKnownNativeOpenAIRoute) return false;
return capabilities.provider === "openai" || capabilities.provider === "azure-openai" || capabilities.provider === "azure-openai-responses";
}
/** Resolve the strict-tool setting for one OpenAI-compatible model/transport. */
function resolveOpenAIStrictToolSetting(model, options) {
if (resolvesToNativeOpenAIStrictTools(model, options?.transport ?? "stream")) return true;
if (options?.supportsStrictMode) return false;
}
//#endregion
//#region src/agents/openai-tool-schema.ts
const MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA = 8;
let strictOpenAISchemaCache = /* @__PURE__ */ new WeakMap();
function resolveToolSchemaModelCompat(compat) {
if (!compat) return;
const unsupportedToolSchemaKeywords = Array.isArray(compat.unsupportedToolSchemaKeywords) ? compat.unsupportedToolSchemaKeywords.filter((keyword) => typeof keyword === "string") : [];
if (unsupportedToolSchemaKeywords.length === 0 && compat.omitEmptyArrayItems !== true) return;
return {
...unsupportedToolSchemaKeywords.length > 0 ? { unsupportedToolSchemaKeywords } : {},
...compat.omitEmptyArrayItems === true ? { omitEmptyArrayItems: true } : {}
};
}
function resolveStrictOpenAISchemaCacheKey(modelCompat) {
const compat = resolveToolSchemaModelCompat(modelCompat);
return JSON.stringify([[...compat?.unsupportedToolSchemaKeywords ?? []].toSorted(), shouldOmitEmptyArrayItems(compat)]);
}
function readCachedStrictOpenAISchema(schema, key) {
return strictOpenAISchemaCache.get(schema)?.find((entry) => entry.key === key)?.value;
}
function rememberStrictOpenAISchema(schema, key, value) {
const entries = strictOpenAISchemaCache.get(schema) ?? [];
strictOpenAISchemaCache.set(schema, [{
key,
value
}, ...entries.filter((entry) => entry.key !== key)].slice(0, MAX_STRICT_SCHEMA_CACHE_ENTRIES_PER_SCHEMA));
return value;
}
/** Normalizes a tool parameter schema into the OpenAI strict JSON-schema subset. */
function normalizeStrictOpenAIJsonSchema(schema, modelCompat) {
const schemaInput = schema ?? {};
if (!schemaInput || typeof schemaInput !== "object") return normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0);
const cacheKey = resolveStrictOpenAISchemaCacheKey(modelCompat);
const cached = readCachedStrictOpenAISchema(schemaInput, cacheKey);
if (cached !== void 0) return cached;
return rememberStrictOpenAISchema(schemaInput, cacheKey, normalizeStrictOpenAIJsonSchemaRecursive(normalizeToolParameterSchema(schemaInput, { modelCompat: resolveToolSchemaModelCompat(modelCompat) }), 0));
}
function normalizeStrictOpenAIJsonSchemaRecursive(schema, depth) {
if (Array.isArray(schema)) {
let changed = false;
const normalized = schema.map((entry) => {
const next = normalizeStrictOpenAIJsonSchemaRecursive(entry, depth);
changed ||= next !== entry;
return next;
});
return changed ? normalized : schema;
}
if (!schema || typeof schema !== "object") return schema;
const record = schema;
let changed = false;
const normalized = {};
for (const [key, value] of Object.entries(record)) {
const next = normalizeStrictOpenAIJsonSchemaRecursive(value, key === "properties" ? depth : depth + 1);
normalized[key] = next;
changed ||= next !== value;
}
if (normalized.type === "object") {
const properties = normalized.properties && typeof normalized.properties === "object" && !Array.isArray(normalized.properties) ? normalized.properties : void 0;
if (properties && Object.keys(properties).length === 0 && !Array.isArray(normalized.required)) {
normalized.required = [];
changed = true;
}
if (depth === 0 && !("additionalProperties" in normalized)) {
normalized.additionalProperties = false;
changed = true;
}
}
return changed ? normalized : schema;
}
/** Normalizes tool parameters using strict OpenAI rules only when strict mode is active. */
function normalizeOpenAIStrictToolParameters(schema, strict, modelCompat) {
const toolSchemaCompat = resolveToolSchemaModelCompat(modelCompat);
if (!strict) return normalizeToolParameterSchema(schema ?? {}, { modelCompat: toolSchemaCompat });
return normalizeStrictOpenAIJsonSchema(schema, toolSchemaCompat);
}
/** Returns whether a schema already satisfies OpenAI strict tool-schema constraints. */
function isStrictOpenAIJsonSchemaCompatible(schema) {
return isStrictOpenAIJsonSchemaCompatibleRecursive(normalizeStrictOpenAIJsonSchema(schema));
}
/** Returns strict-schema violation paths for each incompatible tool definition. */
function findOpenAIStrictToolSchemaDiagnostics(tools) {
return tools.flatMap((tool, toolIndex) => {
const violations = findStrictOpenAIJsonSchemaViolations(normalizeStrictOpenAIJsonSchema(tool.parameters), `${typeof tool.name === "string" && tool.name ? tool.name : `tool[${toolIndex}]`}.parameters`);
if (violations.length === 0) return [];
return [{
toolIndex,
...typeof tool.name === "string" && tool.name ? { toolName: tool.name } : {},
violations
}];
});
}
function isStrictOpenAIJsonSchemaCompatibleRecursive(schema) {
if (Array.isArray(schema)) return schema.every((entry) => isStrictOpenAIJsonSchemaCompatibleRecursive(entry));
if (!schema || typeof schema !== "object") return true;
const record = schema;
if ("anyOf" in record || "oneOf" in record || "allOf" in record) return false;
if (Array.isArray(record.type)) return false;
if (record.type === "object" && record.additionalProperties !== false) return false;
if (record.type === "object") {
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
if (!required) return false;
const requiredSet = new Set(required);
if (Object.keys(properties).some((key) => !requiredSet.has(key))) return false;
}
return Object.entries(record).every(([key, entry]) => {
if (key === "properties" && entry && typeof entry === "object" && !Array.isArray(entry)) return Object.values(entry).every((value) => isStrictOpenAIJsonSchemaCompatibleRecursive(value));
return isStrictOpenAIJsonSchemaCompatibleRecursive(entry);
});
}
function findStrictOpenAIJsonSchemaViolations(schema, path) {
if (Array.isArray(schema)) return schema.flatMap((entry, index) => findStrictOpenAIJsonSchemaViolations(entry, `${path}[${index}]`));
if (!schema || typeof schema !== "object") return [];
const record = schema;
const violations = [];
for (const key of [
"anyOf",
"oneOf",
"allOf"
]) if (key in record) violations.push(`${path}.${key}`);
if (Array.isArray(record.type)) violations.push(`${path}.type`);
if (record.type === "object") {
if (record.additionalProperties !== false) violations.push(`${path}.additionalProperties`);
const properties = record.properties && typeof record.properties === "object" && !Array.isArray(record.properties) ? record.properties : {};
const required = Array.isArray(record.required) ? record.required.filter((entry) => typeof entry === "string") : void 0;
if (!required) violations.push(`${path}.required`);
else {
const requiredSet = new Set(required);
for (const key of Object.keys(properties)) if (!requiredSet.has(key)) violations.push(`${path}.required.${key}`);
}
}
if (record.properties && typeof record.properties === "object" && !Array.isArray(record.properties)) for (const [key, value] of Object.entries(record.properties)) violations.push(...findStrictOpenAIJsonSchemaViolations(value, `${path}.properties.${key}`));
for (const [key, value] of Object.entries(record)) {
if (key === "properties") continue;
if (value && typeof value === "object") violations.push(...findStrictOpenAIJsonSchemaViolations(value, `${path}.${key}`));
}
return violations;
}
/** Resolves the strict flag to advertise for a tool inventory after compatibility checks. */
function resolveOpenAIStrictToolFlagForInventory(tools, strict) {
if (strict !== true) return strict === false ? false : void 0;
return tools.every((tool) => isStrictOpenAIJsonSchemaCompatible(tool.parameters));
}
//#endregion
export { AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as a, isResponsesTextContentPartType as c, resolveOpenAIStrictToolSetting as i, isResponsesTextDeltaEventType as l, normalizeOpenAIStrictToolParameters as n, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as o, resolveOpenAIStrictToolFlagForInventory as r, isAzureResponsesTextDeltaEvent as s, findOpenAIStrictToolSchemaDiagnostics as t };