openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
308 lines (307 loc) • 12.3 kB
JavaScript
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js";
import { s as asFiniteNumber } from "./number-coercion-CJQ8TR--.js";
import { r as getChildLogger } from "./logger-Brhy0cvC.js";
import { s as normalizePluginsConfig } from "./config-state-CikNNz7T.js";
import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js";
import { t as getPluginRuntimeGatewayRequestScope } from "./gateway-request-scope-BAEdAUQ6.js";
import "./logging-CvNAh8ZC.js";
import { n as modelKey } from "./model-ref-shared-CqcindZs.js";
import { a as normalizeModelRef } from "./model-selection-normalize-roKDxQ9_.js";
import "./model-selection-CA_65iXi.js";
import { o as normalizeUsage } from "./usage-C67Kbb7n.js";
import { a as resolveModelCostConfig, t as estimateUsageCost } from "./usage-format-DAT-61Hm.js";
//#region src/plugins/runtime/runtime-llm.runtime.ts
const defaultLogger = getChildLogger({ capability: "runtime.llm" });
function toRuntimeLogger(logger) {
return {
debug: (message, meta) => logger.debug?.(meta, message),
info: (message, meta) => logger.info(meta, message),
warn: (message, meta) => logger.warn(meta, message),
error: (message, meta) => logger.error(meta, message)
};
}
function normalizeCaller(caller, fallback) {
const source = caller ?? fallback;
if (!source) return { kind: "unknown" };
return {
kind: source.kind,
...normalizeOptionalString(source.id) ? { id: source.id.trim() } : {},
...normalizeOptionalString(source.name) ? { name: source.name.trim() } : {}
};
}
function resolveTrustedCaller(authority) {
if (authority?.caller?.kind === "context-engine") return normalizeCaller(authority.caller);
const scopedPluginId = normalizeOptionalString(getPluginRuntimeGatewayRequestScope()?.pluginId);
if (scopedPluginId) return {
kind: "plugin",
id: scopedPluginId
};
return normalizeCaller(authority?.caller);
}
function resolveRuntimeConfig(options) {
const cfg = options.getConfig?.();
if (!cfg) throw new Error("Plugin LLM completion requires an injected runtime config scope.");
return cfg;
}
async function resolveAgentId(params) {
const authorityAgentIdRaw = normalizeOptionalString(params.authority?.agentId);
const requestedAgentIdRaw = normalizeOptionalString(params.request.agentId);
const authorityAgentId = authorityAgentIdRaw ? normalizeAgentId(authorityAgentIdRaw) : void 0;
const requestedAgentId = requestedAgentIdRaw ? normalizeAgentId(requestedAgentIdRaw) : void 0;
if (params.authority?.requiresBoundAgent && !authorityAgentId) throw new Error("Plugin LLM completion is not bound to an active session agent.");
if (authorityAgentId) {
if (requestedAgentId && requestedAgentId !== authorityAgentId && !params.allowAgentIdOverride) throw new Error("Plugin LLM completion cannot override the active session agent.");
return authorityAgentId;
}
if (requestedAgentId) {
if (!params.allowAgentIdOverride) throw new Error("Plugin LLM completion cannot override the target agent.");
return requestedAgentId;
}
const { resolveDefaultAgentId } = await import("./agent-scope-Do4dPjL6.js");
return resolveDefaultAgentId(params.cfg);
}
function buildSystemPrompt(params) {
const segments = [normalizeOptionalString(params.systemPrompt), ...params.messages.filter((message) => message.role === "system").map((message) => normalizeOptionalString(message.content))].filter((segment) => Boolean(segment));
return segments.length > 0 ? segments.join("\n\n") : void 0;
}
function buildMessages(params) {
const now = Date.now();
return params.request.messages.filter((message) => message.role !== "system").map((message) => message.role === "user" ? {
role: "user",
content: message.content,
timestamp: now
} : {
role: "assistant",
content: [{
type: "text",
text: message.content
}],
api: params.api,
provider: params.provider,
model: params.model,
usage: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
totalTokens: 0,
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
total: 0
}
},
stopReason: "stop",
timestamp: now
});
}
function readFiniteNonNegativeNumber(value) {
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
}
function readExplicitCostUsd(raw) {
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
const cost = raw.cost;
if (typeof cost === "number") return readFiniteNonNegativeNumber(cost);
if (!cost || typeof cost !== "object" || Array.isArray(cost)) return;
return readFiniteNonNegativeNumber(cost.totalUsd) ?? readFiniteNonNegativeNumber(cost.total);
}
function buildUsage(params) {
const costConfig = resolveModelCostConfig({
provider: params.provider,
model: params.model,
config: params.cfg
});
const costUsd = readExplicitCostUsd(params.rawUsage) ?? estimateUsageCost({
usage: params.normalized,
cost: costConfig
});
return {
...params.normalized?.input !== void 0 ? { inputTokens: params.normalized.input } : {},
...params.normalized?.output !== void 0 ? { outputTokens: params.normalized.output } : {},
...params.normalized?.cacheRead !== void 0 ? { cacheReadTokens: params.normalized.cacheRead } : {},
...params.normalized?.cacheWrite !== void 0 ? { cacheWriteTokens: params.normalized.cacheWrite } : {},
...params.normalized?.total !== void 0 ? { totalTokens: params.normalized.total } : {},
...costUsd !== void 0 ? { costUsd } : {}
};
}
function finiteOption(value) {
return asFiniteNumber(value);
}
function normalizeAllowedModelRef(raw) {
const trimmed = raw.trim();
if (!trimmed) return null;
if (trimmed === "*") return "*";
const slash = trimmed.indexOf("/");
if (slash <= 0 || slash >= trimmed.length - 1) return null;
const provider = trimmed.slice(0, slash).trim();
const model = trimmed.slice(slash + 1).trim();
if (!provider || !model) return null;
const normalized = normalizeModelRef(provider, model);
return modelKey(normalized.provider, normalized.model);
}
function buildPolicyFromEntry(entry) {
const allowedModels = /* @__PURE__ */ new Set();
let allowAnyModel = false;
for (const modelRef of entry.allowedModels ?? []) {
const normalizedModelRef = normalizeAllowedModelRef(modelRef);
if (!normalizedModelRef) continue;
if (normalizedModelRef === "*") {
allowAnyModel = true;
continue;
}
allowedModels.add(normalizedModelRef);
}
return {
allowAgentIdOverride: entry.allowAgentIdOverride === true,
allowModelOverride: entry.allowModelOverride === true,
hasConfiguredAllowedModels: entry.hasAllowedModelsConfig === true,
allowAnyModel,
allowedModels
};
}
function resolvePluginPolicyId(authority, caller) {
const authorityPluginId = normalizeOptionalString(authority?.pluginIdForPolicy);
if (authorityPluginId) return authorityPluginId;
if (caller.kind !== "plugin") return;
return normalizeOptionalString(caller.id);
}
function resolvePluginLlmOverridePolicy(cfg, pluginId) {
if (!pluginId) return;
const entry = normalizePluginsConfig(cfg.plugins).entries[pluginId]?.llm;
return entry ? buildPolicyFromEntry(entry) : void 0;
}
function resolveAuthorityModelPolicy(authority) {
if (authority?.allowAgentIdOverride !== true && authority?.allowModelOverride !== true && authority?.allowedModels === void 0) return;
return buildPolicyFromEntry({
allowAgentIdOverride: authority.allowAgentIdOverride,
allowModelOverride: authority.allowModelOverride,
hasAllowedModelsConfig: authority.allowedModels !== void 0,
allowedModels: authority.allowedModels
});
}
function assertAllowedModelOverride(params) {
let policy;
let policyOwnerPluginId;
if (params.authorityPolicy?.allowModelOverride) policy = params.authorityPolicy;
else if (params.pluginPolicy?.allowModelOverride) {
policy = params.pluginPolicy;
policyOwnerPluginId = params.pluginPolicyId;
}
if (!policy) throw new Error("Plugin LLM completion cannot override the target model.");
if (policy.allowAnyModel) return;
if (policy.hasConfiguredAllowedModels && policy.allowedModels.size === 0) throw new Error("Plugin LLM completion model override allowlist has no valid models.");
if (policy.allowedModels.size === 0) return;
if (!params.resolvedModelRef) throw new Error("Plugin LLM completion model override allowlist requires a resolvable provider/model target.");
if (!policy.allowedModels.has(params.resolvedModelRef)) {
const owner = policyOwnerPluginId ? ` for plugin "${policyOwnerPluginId}"` : "";
throw new Error(`Plugin LLM completion model override "${params.resolvedModelRef}" is not allowlisted${owner}.`);
}
}
/**
* Create the host-owned generic LLM completion runtime for trusted plugin callers.
*/
function createRuntimeLlm(options = {}) {
const logger = options.logger ?? toRuntimeLogger(defaultLogger);
return { complete: async (params) => {
const caller = resolveTrustedCaller(options.authority);
if (options.authority?.allowComplete === false) {
const reason = options.authority.denyReason ?? "capability denied";
logger.warn("plugin llm completion denied", {
caller,
purpose: params.purpose,
reason
});
throw new Error(`Plugin LLM completion denied: ${reason}`);
}
const [{ prepareSimpleCompletionModelForAgent, completeWithPreparedSimpleCompletionModel, resolveSimpleCompletionSelectionForAgent }, cfg] = await Promise.all([import("./simple-completion-runtime-kdAgDxlZ.js"), Promise.resolve(resolveRuntimeConfig(options))]);
const pluginPolicyId = resolvePluginPolicyId(options.authority, caller);
const pluginPolicy = resolvePluginLlmOverridePolicy(cfg, pluginPolicyId);
const authorityPolicy = resolveAuthorityModelPolicy(options.authority);
const preferredProfile = normalizeOptionalString(options.authority?.preferredProfile);
const agentId = await resolveAgentId({
request: params,
cfg,
authority: options.authority,
allowAgentIdOverride: options.authority?.allowAgentIdOverride === false ? false : authorityPolicy?.allowAgentIdOverride === true || pluginPolicy?.allowAgentIdOverride === true
});
const requestedModel = normalizeOptionalString(params.model);
if (requestedModel) {
const selection = resolveSimpleCompletionSelectionForAgent({
cfg,
agentId,
modelRef: requestedModel
});
const normalizedSelection = selection ? normalizeModelRef(selection.provider, selection.modelId) : null;
assertAllowedModelOverride({
resolvedModelRef: normalizedSelection ? modelKey(normalizedSelection.provider, normalizedSelection.model) : null,
pluginPolicyId,
authorityPolicy,
pluginPolicy
});
}
const prepared = await prepareSimpleCompletionModelForAgent({
cfg,
agentId,
modelRef: params.model,
preferredProfile,
allowBundledStaticCatalogFallback: true,
allowMissingApiKeyModes: ["aws-sdk"],
skipAgentDiscovery: true
});
if ("error" in prepared) throw new Error(`Plugin LLM completion failed: ${prepared.error}`);
const context = {
systemPrompt: buildSystemPrompt(params),
messages: buildMessages({
request: params,
provider: prepared.model.provider,
model: prepared.model.id,
api: prepared.model.api
})
};
const result = await completeWithPreparedSimpleCompletionModel({
model: prepared.model,
auth: prepared.auth,
cfg,
context,
options: {
maxTokens: finiteOption(params.maxTokens),
temperature: finiteOption(params.temperature),
signal: params.signal
}
});
const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
const normalizedUsage = normalizeUsage(result.usage);
const usage = buildUsage({
rawUsage: result.usage,
normalized: normalizedUsage,
cfg,
provider: prepared.selection.provider,
model: prepared.selection.modelId
});
logger.info("plugin llm completion", {
caller,
purpose: params.purpose,
sessionKey: options.authority?.sessionKey,
agentId,
provider: prepared.selection.provider,
model: prepared.selection.modelId,
usage
});
return {
text,
provider: prepared.selection.provider,
model: prepared.selection.modelId,
agentId,
usage,
audit: {
caller,
...params.purpose ? { purpose: params.purpose } : {},
...options.authority?.sessionKey ? { sessionKey: options.authority.sessionKey } : {}
}
};
} };
}
//#endregion
export { createRuntimeLlm };