UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

263 lines (262 loc) • 14.2 kB
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-6kR-86XC.mjs"; import { t as asBoolean } from "./boolean-cOv0ta_w.mjs"; import { l as resolveRuntimeServiceVersion } from "./version-Dx-YMDJx.mjs"; import { r as normalizeProviderId } from "./provider-id-BIyeXQag.mjs"; import { i as normalizePluginProviderBaseUrl, r as matchesPluginProviderEndpoint } from "./plugin-metadata-provider-facts-DiVyEvyO.mjs"; import { n as loadPluginMetadataSnapshotRuntime, t as getCurrentPluginMetadataSnapshotRequiredRuntime } from "./plugin-metadata-snapshot-required-BbM5NpXy.mjs"; //#region src/agents/provider-attribution.ts function readCompatBoolean(compat, key) { if (!compat || typeof compat !== "object") return; return asBoolean(compat[key]); } const OPENCLAW_ATTRIBUTION_PRODUCT = "OpenClaw"; const OPENCLAW_ATTRIBUTION_ORIGINATOR = "openclaw"; const OPENROUTER_ATTRIBUTION_CATEGORIES = "cli-agent,cloud-agent,programming-app,creative-writing,writing-assistant,general-chat,personal-agent"; const LOCAL_ENDPOINT_HOSTS = /* @__PURE__ */ new Set([ "localhost", "127.0.0.1", "::1", "[::1]" ]); const OPENAI_RESPONSES_APIS = /* @__PURE__ */ new Set([ "openai-responses", "azure-openai-responses", "openai-chatgpt-responses" ]); const OPENAI_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set([ "openai", "azure-openai", "azure-openai-responses" ]); function resolveUrlHostname(value) { const trimmed = normalizeOptionalString(value); if (!trimmed) return; const candidate = /^[a-z0-9.[\]-]+(?::\d+)?(?:[/?#].*)?$/i.test(trimmed) ? `https://${trimmed}` : trimmed; return normalizeOptionalLowercaseString(URL.parse(candidate)?.hostname); } function resolveProviderMetadataOwners(prepared) { const owners = prepared ?? getCurrentPluginMetadataSnapshotRequiredRuntime({ allowWorkspaceScopedSnapshot: true })?.owners ?? loadPluginMetadataSnapshotRuntime({ config: {} }).owners; return { providerEndpoints: owners.providerEndpoints ?? [], providerRequests: owners.providerRequests ?? /* @__PURE__ */ new Map() }; } function resolveManifestProviderRequest(params) { return params.provider ? resolveProviderMetadataOwners(params.providerMetadataOwners).providerRequests.get(params.provider) : void 0; } function buildManifestEndpointResolution(endpoint, host) { const regionSuffix = endpoint.googleVertexRegionHostSuffix; const googleVertexRegion = endpoint.googleVertexRegion ?? (regionSuffix && host.endsWith(regionSuffix) ? host.slice(0, -regionSuffix.length) : void 0); return { endpointClass: endpoint.endpointClass, hostname: host, ...googleVertexRegion ? { googleVertexRegion } : {} }; } function resolveManifestProviderEndpoint(params) { for (const endpoint of resolveProviderMetadataOwners(params.providerMetadataOwners).providerEndpoints) if (matchesPluginProviderEndpoint(endpoint, params)) return buildManifestEndpointResolution(endpoint, params.host); } function isLocalEndpointHost(host) { return LOCAL_ENDPOINT_HOSTS.has(host) || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal"); } /** Classify loopback and private-network endpoint names without loading plugin metadata. */ function isLocalProviderEndpoint(baseUrl) { const host = resolveUrlHostname(baseUrl); return host !== void 0 && isLocalEndpointHost(host); } function resolveProviderEndpoint(baseUrl, providerMetadataOwners) { if (typeof baseUrl !== "string" || !baseUrl.trim()) return { endpointClass: "default" }; const host = resolveUrlHostname(baseUrl); if (!host) return { endpointClass: "invalid" }; const manifestEndpoint = resolveManifestProviderEndpoint({ host, normalizedBaseUrl: normalizePluginProviderBaseUrl(baseUrl), ...providerMetadataOwners ? { providerMetadataOwners } : {} }); if (manifestEndpoint) return manifestEndpoint; if (isLocalEndpointHost(host)) return { endpointClass: "local", hostname: host }; return { endpointClass: "custom", hostname: host }; } function resolveKnownProviderFamily(provider, providerMetadataOwners) { const manifestFamily = resolveManifestProviderRequest({ provider, ...providerMetadataOwners ? { providerMetadataOwners } : {} })?.family; if (manifestFamily) return manifestFamily; return provider && OPENAI_RESPONSES_PROVIDERS.has(provider) ? "openai-family" : provider || "unknown"; } function resolveProviderAttributionPolicy(provider, env = process.env) { const normalized = normalizeProviderId(provider ?? ""); const version = resolveRuntimeServiceVersion(env); const userAgent = `${OPENCLAW_ATTRIBUTION_ORIGINATOR}/${version}`; const policy = { provider: normalized, enabledByDefault: true, verification: "vendor-documented", hook: "request-headers", product: OPENCLAW_ATTRIBUTION_PRODUCT, version }; switch (normalized) { case "openrouter": return { ...policy, docsUrl: "https://openrouter.ai/docs/app-attribution", reviewNote: "Documented app attribution headers. Verified in OpenClaw runtime wrapper.", headers: { "HTTP-Referer": "https://openclaw.ai", "X-OpenRouter-Title": policy.product, "X-OpenRouter-Categories": OPENROUTER_ATTRIBUTION_CATEGORIES } }; case "nvidia": return { ...policy, reviewNote: "NVIDIA NIM billing invoke-origin attribution header. Applied only on verified NVIDIA routes.", headers: { "X-BILLING-INVOKE-ORIGIN": policy.product } }; case "google": return { ...policy, docsUrl: "https://ai.google.dev/gemini-api/docs/partner-integration", reviewNote: "Gemini API partner integration guidance requires x-goog-api-client on partner and library traffic.", headers: { "x-goog-api-client": userAgent } }; case "openai": case "xai": return { ...policy, verification: "vendor-hidden-api-spec", reviewNote: normalized === "openai" ? "OpenAI native traffic supports hidden originator/User-Agent attribution. Verified against the Codex wire contract." : "xAI api.x.ai accepts a standard openclaw User-Agent. Companion originator/version headers mirror the OpenAI attribution shape for consistency; they are not validated against an xAI-specific spec and are expected to be ignored by xAI's OpenAI-compatible surface.", headers: { originator: OPENCLAW_ATTRIBUTION_ORIGINATOR, version, "User-Agent": userAgent } }; case "opencode-go": return { ...policy, docsUrl: "https://opencode.ai/docs/go/", reviewNote: "OpenCode Go requires coding agents to identify themselves with a specific User-Agent.", headers: { "User-Agent": userAgent } }; case "anthropic": case "groq": case "together": case "mistral": { const reviewNotes = { anthropic: "Anthropic JS SDK exposes defaultHeaders, but app attribution is not yet verified.", groq: "Groq JS SDK exposes defaultHeaders, but app attribution is not yet verified.", together: "Together JS SDK exposes defaultHeaders, but app attribution is not yet verified.", mistral: "Mistral JS SDK exposes a custom userAgent option, but app attribution is not yet verified." }; return { ...policy, enabledByDefault: false, verification: "vendor-sdk-hook-only", hook: normalized === "mistral" ? "custom-user-agent" : "default-headers", reviewNote: reviewNotes[normalized] }; } default: return; } } function resolveProviderRequestPolicy(input, env = process.env) { const provider = normalizeProviderId(input.provider ?? ""); const policy = resolveProviderAttributionPolicy(provider, env); const endpointClass = resolveProviderEndpoint(input.baseUrl, input.providerMetadataOwners).endpointClass; const usesConfiguredBaseUrl = endpointClass !== "default"; const usesKnownNativeOpenAIEndpoint = endpointClass === "openai-public" || endpointClass === "openai" || endpointClass === "azure-openai"; const usesVerifiedOpenAIAttributionHost = endpointClass === "openai-public" || endpointClass === "openai"; const usesExplicitProxyLikeEndpoint = usesConfiguredBaseUrl && !usesKnownNativeOpenAIEndpoint; let attributionProvider; if (provider === "openai" && usesVerifiedOpenAIAttributionHost) attributionProvider = "openai"; else if (provider === "openrouter" && policy?.enabledByDefault) { if (endpointClass === "openrouter" || endpointClass === "default") attributionProvider = "openrouter"; } else if (provider === "xai" && policy?.enabledByDefault) { if (endpointClass === "xai-native" || endpointClass === "default") attributionProvider = "xai"; } else if (provider === "opencode-go" && policy?.enabledByDefault && endpointClass === "opencode-go-native") attributionProvider = "opencode-go"; if (!attributionProvider && endpointClass === "nvidia-native") attributionProvider = "nvidia"; if (!attributionProvider && endpointClass === "google-generative-ai") attributionProvider = "google"; const attributionPolicy = attributionProvider ? resolveProviderAttributionPolicy(attributionProvider, env) : void 0; const attributionHeaders = attributionPolicy?.enabledByDefault ? attributionPolicy.headers : void 0; return { provider: provider || void 0, policy: attributionPolicy ?? policy, endpointClass, usesConfiguredBaseUrl, knownProviderFamily: resolveKnownProviderFamily(provider || void 0, input.providerMetadataOwners), attributionProvider, attributionHeaders, allowsHiddenAttribution: attributionProvider !== void 0 && attributionPolicy?.verification === "vendor-hidden-api-spec", usesKnownNativeOpenAIEndpoint, usesKnownNativeOpenAIRoute: endpointClass === "default" ? provider === "openai" : usesKnownNativeOpenAIEndpoint, usesVerifiedOpenAIAttributionHost, usesExplicitProxyLikeEndpoint }; } function resolveProviderRequestCapabilities(input, env = process.env) { const policy = resolveProviderRequestPolicy(input, env); const provider = policy.provider; const api = normalizeOptionalLowercaseString(input.api); const endpointClass = policy.endpointClass; const isKnownNativeEndpoint = endpointClass === "anthropic-public" || endpointClass === "cerebras-native" || endpointClass === "chutes-native" || endpointClass === "deepseek-native" || endpointClass === "github-copilot-native" || endpointClass === "groq-native" || endpointClass === "meta-native" || endpointClass === "mistral-public" || endpointClass === "minimax-native" || endpointClass === "moonshot-native" || endpointClass === "modelstudio-native" || endpointClass === "nvidia-native" || endpointClass === "openai-public" || endpointClass === "openai" || endpointClass === "opencode-native" || endpointClass === "opencode-go-native" || endpointClass === "azure-openai" || endpointClass === "openrouter" || endpointClass === "xai-native" || endpointClass === "xiaomi-native" || endpointClass === "zai-native" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex"; const manifestProviderRequest = resolveManifestProviderRequest({ provider, ...input.providerMetadataOwners ? { providerMetadataOwners: input.providerMetadataOwners } : {} }); const compatibilityFamily = manifestProviderRequest?.compatibilityFamily; const isResponsesApi = api !== void 0 && OPENAI_RESPONSES_APIS.has(api); const promptCacheKeySupport = readCompatBoolean(input.compat, "supportsPromptCacheKey"); const shouldStripResponsesPromptCache = promptCacheKeySupport === true ? false : promptCacheKeySupport === false ? isResponsesApi : isResponsesApi && policy.usesExplicitProxyLikeEndpoint; return { ...policy, isKnownNativeEndpoint, allowsOpenAIServiceTier: provider === "openai" && api === "openai-responses" && endpointClass === "openai-public" || provider === "openai" && (api === "openai-chatgpt-responses" || api === "openai-responses") && endpointClass === "openai", supportsOpenAIReasoningCompatPayload: provider !== void 0 && api !== void 0 && !policy.usesExplicitProxyLikeEndpoint && (provider === "openai" || provider === "azure-openai" || provider === "azure-openai-responses") && (api === "openai-completions" || api === "openai-responses" || api === "openai-chatgpt-responses" || api === "azure-openai-responses"), allowsAnthropicServiceTier: provider === "anthropic" && api === "anthropic-messages" && (endpointClass === "default" || endpointClass === "anthropic-public"), supportsResponsesStoreField: readCompatBoolean(input.compat, "supportsStore") !== false && isResponsesApi, allowsResponsesStore: readCompatBoolean(input.compat, "supportsStore") !== false && provider !== void 0 && isResponsesApi && OPENAI_RESPONSES_PROVIDERS.has(provider) && policy.usesKnownNativeOpenAIEndpoint, shouldStripResponsesPromptCache, supportsNativeStreamingUsageCompat: endpointClass === "moonshot-native" || endpointClass === "modelstudio-native", supportsOpenAICompletionsStreamingUsageCompat: manifestProviderRequest?.openAICompletions?.supportsStreamingUsage === true, compatibilityFamily }; } function describeProviderRequestRoutingPolicy(policy) { if (!policy.attributionProvider) return "none"; switch (policy.policy?.verification) { case "vendor-hidden-api-spec": return "hidden"; case "vendor-documented": return "documented"; case "vendor-sdk-hook-only": return "sdk-hook-only"; default: return "none"; } } function describeProviderRequestRouteClass(policy) { switch (policy.endpointClass) { case "default": case "invalid": case "local": return policy.endpointClass; case "custom": case "openrouter": return "proxy-like"; default: return "native"; } } function describeProviderRequestRoutingSummary(input, env = process.env) { const policy = resolveProviderRequestPolicy(input, env); const api = normalizeOptionalLowercaseString(input.api) ?? "unknown"; const provider = policy.provider ?? "unknown"; const routeClass = describeProviderRequestRouteClass(policy); const routingPolicy = describeProviderRequestRoutingPolicy(policy); return [ `provider=${provider}`, `api=${api}`, `endpoint=${policy.endpointClass}`, `route=${routeClass}`, `policy=${routingPolicy}` ].join(" "); } //#endregion export { resolveProviderRequestPolicy as a, resolveProviderRequestCapabilities as i, isLocalProviderEndpoint as n, resolveProviderEndpoint as r, describeProviderRequestRoutingSummary as t };