UNPKG

agents

Version:

A home for your AI agents

259 lines (258 loc) • 9.09 kB
//#region src/models/core/settings.ts /** The gateway id Cloudflare auto-creates on first use. */ const DEFAULT_GATEWAY_ID = "default"; /** Key that per-call options live under in a framework's per-call options bag. */ const PROVIDER_OPTIONS_KEY = "cloudflare"; /** The id legs of a fallback list; model objects are their module's business. */ function idLegs(fallback) { return (fallback ?? []).filter((leg) => typeof leg === "string"); } /** Strips `undefined` values so a later layer never blanks an earlier one. */ function defined(value) { const result = {}; for (const [key, entry] of Object.entries(value)) if (entry !== void 0) result[key] = entry; return result; } function normalizeGateway(gateway) { if (gateway === void 0) return {}; return typeof gateway === "string" ? { id: gateway } : defined(gateway); } /** * Folds one layer's flat gateway sugar into its `gateway` object. The flat key * wins over the nested one, and an explicit `undefined` on either never blanks * a lower layer. */ function layerGateway(options) { if (options === void 0) return {}; return { ...normalizeGateway(options.gateway), ...defined({ byokAlias: options.byokAlias, cacheKey: options.cacheKey, cacheTtl: options.cacheTtl, collectLog: options.collectLog, eventId: options.eventId, id: options.id, metadata: options.metadata, requestTimeoutMs: options.requestTimeoutMs, retries: options.retries, skipCache: options.skipCache, zdr: options.zdr }) }; } /** The flat gateway sugar {@link layerGateway} folds into `gateway`. */ const FLAT_GATEWAY_KEYS = [ "byokAlias", "cacheKey", "cacheTtl", "collectLog", "eventId", "gateway", "id", "metadata", "requestTimeoutMs", "retries", "skipCache", "zdr" ]; /** * One layer's gateway options as a single object, both spellings folded in. * Exported so a module that collapses two option layers into one bag (a * fallback leg inheriting a chain's gateway) can merge them field by field * rather than replacing one object with the other. * * @experimental This surface is experimental and may change. */ function gatewayLayerOf(options) { return layerGateway(options); } /** An option bag without the gateway keys, whichever spelling they used. */ function withoutGatewayKeys(options) { const rest = { ...options }; for (const key of FLAT_GATEWAY_KEYS) delete rest[key]; return defined(rest); } /** * Collapses two option layers into one bag, `override` winning field by field. * * The gateway keys of both are folded into a single `gateway` object, so * neither layer's flat sugar can out-rank the other's nested spelling when the * bag is resolved later, and `metadata` merges rather than replaces — the same * rules {@link resolveOptions} applies across its three layers. * * @experimental This surface is experimental and may change. */ function mergeModelOptions(base, override) { if (base === void 0) return override; if (override === void 0) return base; const layers = [layerGateway(base), layerGateway(override)]; const gateway = Object.assign({}, ...layers); const metadata = Object.assign({}, ...layers.map((layer) => layer.metadata)); const retries = mergeRetries(layers); const headers = { ...base.headers, ...override.headers }; return { ...withoutGatewayKeys(base), ...withoutGatewayKeys(override), ...Object.keys(headers).length > 0 ? { headers } : {}, gateway: { ...gateway, ...Object.keys(metadata).length > 0 ? { metadata } : {}, ...retries === void 0 ? {} : { retries } } }; } /** * Merges the retry policies field by field, so a provider-wide `backoff` still * applies when a call only sets `maxAttempts`. `undefined` on any layer means * "not set" and never blanks a lower one. */ function mergeRetries(layers) { const merged = layers.reduce((result, layer) => ({ ...result, ...layer.retries === void 0 ? {} : defined(layer.retries) }), {}); return Object.keys(merged).length > 0 ? merged : void 0; } /** * Merges the option layers. Precedence is per-call * (`providerOptions.cloudflare`) > per-model (`ai(id, options)`) > * provider (`createAI({ ... })`). * * The provider layer takes the whole settings object as well as a bare * `gateway` value, so `createAI({ binding, id: "prod", cacheTtl: 60 })` reads * the same as `createAI({ binding, gateway: { id: "prod", cacheTtl: 60 } })`. * * @experimental This surface is experimental and may change. */ function resolveOptions(provider, model, call) { const layers = typeof provider === "string" ? [ normalizeGateway(provider), layerGateway(model), layerGateway(call) ] : [ layerGateway(provider), layerGateway(model), layerGateway(call) ]; const gateway = Object.assign({}, ...layers); const metadata = Object.assign({}, ...layers.map((layer) => layer.metadata)); const retries = mergeRetries(layers); return { chatTemplateKwargs: call?.chatTemplateKwargs ?? model?.chatTemplateKwargs, fallback: idLegs(call?.fallback ?? model?.fallback), gateway: { ...gateway, id: gateway.id ?? "default", ...Object.keys(metadata).length > 0 ? { metadata } : {}, ...retries === void 0 ? {} : { retries } }, headers: { ...model?.headers, ...call?.headers }, reasoningEffort: call?.reasoningEffort !== void 0 ? call.reasoningEffort : model?.reasoningEffort, sessionAffinity: call?.sessionAffinity ?? model?.sessionAffinity }; } function asRecord(value) { return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0; } function asStringRecord(value) { const record = asRecord(value); if (record === void 0) return void 0; const result = {}; for (const [key, entry] of Object.entries(record)) if (typeof entry === "string") result[key] = entry; return result; } function asString(value) { return typeof value === "string" ? value : void 0; } function asNumber(value) { return typeof value === "number" ? value : void 0; } function asBoolean(value) { return typeof value === "boolean" ? value : void 0; } function asReasoningEffort(value) { if (value === null) return null; if (value === "low" || value === "medium" || value === "high") return value; } /** * Metadata values the gateway accepts are scalars only; a nested object or * array would be rejected by the binding, so drop those rather than forward * them. */ function asMetadata(value) { const record = asRecord(value); if (record === void 0) return void 0; const result = {}; for (const [key, entry] of Object.entries(record)) if (entry === null || typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean") result[key] = entry; return result; } const BACKOFFS = [ "constant", "linear", "exponential" ]; /** Reads the retry policy field by field; the binding validates it strictly. */ function asRetries(value) { const record = asRecord(value); if (record === void 0) return void 0; const maxAttempts = asNumber(record.maxAttempts); const backoff = asString(record.backoff); const retries = defined({ backoff: BACKOFFS.find((entry) => entry === backoff), maxAttempts: maxAttempts !== void 0 && Number.isInteger(maxAttempts) && maxAttempts >= 1 && maxAttempts <= 5 ? maxAttempts : void 0, retryDelayMs: asNumber(record.retryDelayMs) }); return Object.keys(retries).length > 0 ? retries : void 0; } /** Reads the shared gateway keys out of a loosely-typed record. */ function parseGatewayFields(raw) { return defined({ byokAlias: asString(raw.byokAlias), cacheKey: asString(raw.cacheKey), cacheTtl: asNumber(raw.cacheTtl), collectLog: asBoolean(raw.collectLog), eventId: asString(raw.eventId), id: asString(raw.id), metadata: asMetadata(raw.metadata), requestTimeoutMs: asNumber(raw.requestTimeoutMs), retries: asRetries(raw.retries), skipCache: asBoolean(raw.skipCache), zdr: asBoolean(raw.zdr) }); } /** * Reads per-call options out of a loosely-typed options bag (the value a * framework keeps under {@link PROVIDER_OPTIONS_KEY}). * * The bag is plain JSON, so every field is read defensively: anything of an * unexpected type is dropped rather than thrown, because a malformed * observability hint should never fail a generation. * * @experimental This surface is experimental and may change. */ function parseModelOptions(value) { const raw = asRecord(value); if (raw === void 0) return void 0; const nested = asRecord(raw.gateway); const gateway = typeof raw.gateway === "string" ? raw.gateway : nested !== void 0 ? parseGatewayFields(nested) : void 0; const fallback = Array.isArray(raw.fallback) ? raw.fallback.filter((entry) => typeof entry === "string") : void 0; return defined({ ...parseGatewayFields(raw), chatTemplateKwargs: asRecord(raw.chatTemplateKwargs), fallback, gateway, headers: asStringRecord(raw.headers), reasoningEffort: asReasoningEffort(raw.reasoningEffort), sessionAffinity: asString(raw.sessionAffinity) }); } //#endregion export { parseModelOptions as a, mergeModelOptions as i, PROVIDER_OPTIONS_KEY as n, resolveOptions as o, gatewayLayerOf as r, DEFAULT_GATEWAY_ID as t }; //# sourceMappingURL=settings-Dk2il2Hf.js.map