mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 22:04:31 +00:00
refactor(plugin-sdk): share auth, routing, and stream/account helpers
This commit is contained in:
@@ -128,6 +128,16 @@ export async function waitForExecApprovalDecision(id: string): Promise<string |
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveRegisteredExecApprovalDecision(params: {
|
||||
approvalId: string;
|
||||
preResolvedDecision: string | null | undefined;
|
||||
}): Promise<string | null> {
|
||||
if (params.preResolvedDecision !== undefined) {
|
||||
return params.preResolvedDecision ?? null;
|
||||
}
|
||||
return await waitForExecApprovalDecision(params.approvalId);
|
||||
}
|
||||
|
||||
export async function requestExecApprovalDecision(
|
||||
params: RequestExecApprovalDecisionParams,
|
||||
): Promise<string | null> {
|
||||
|
||||
@@ -19,9 +19,9 @@ import { logInfo } from "../logger.js";
|
||||
import { markBackgrounded, tail } from "./bash-process-registry.js";
|
||||
import {
|
||||
buildExecApprovalRequesterContext,
|
||||
resolveRegisteredExecApprovalDecision,
|
||||
buildExecApprovalTurnSourceContext,
|
||||
registerExecApprovalRequestForHostOrThrow,
|
||||
waitForExecApprovalDecision,
|
||||
} from "./bash-tools.exec-approval-request.js";
|
||||
import {
|
||||
DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
@@ -172,13 +172,12 @@ export async function processGatewayAllowlist(
|
||||
preResolvedDecision = registration.finalDecision;
|
||||
|
||||
void (async () => {
|
||||
let decision: string | null = preResolvedDecision ?? null;
|
||||
let decision: string | null = null;
|
||||
try {
|
||||
// Some gateways may return a final decision inline during registration.
|
||||
// Only call waitDecision when registration did not already carry one.
|
||||
if (preResolvedDecision === undefined) {
|
||||
decision = await waitForExecApprovalDecision(approvalId);
|
||||
}
|
||||
decision = await resolveRegisteredExecApprovalDecision({
|
||||
approvalId,
|
||||
preResolvedDecision,
|
||||
});
|
||||
} catch {
|
||||
emitExecSystemEvent(
|
||||
`Exec denied (gateway id=${approvalId}, approval-request-failed): ${params.command}`,
|
||||
|
||||
@@ -17,9 +17,9 @@ import { parsePreparedSystemRunPayload } from "../infra/system-run-approval-cont
|
||||
import { logInfo } from "../logger.js";
|
||||
import {
|
||||
buildExecApprovalRequesterContext,
|
||||
resolveRegisteredExecApprovalDecision,
|
||||
buildExecApprovalTurnSourceContext,
|
||||
registerExecApprovalRequestForHostOrThrow,
|
||||
waitForExecApprovalDecision,
|
||||
} from "./bash-tools.exec-approval-request.js";
|
||||
import {
|
||||
DEFAULT_APPROVAL_TIMEOUT_MS,
|
||||
@@ -243,13 +243,12 @@ export async function executeNodeHostCommand(
|
||||
preResolvedDecision = registration.finalDecision;
|
||||
|
||||
void (async () => {
|
||||
let decision: string | null = preResolvedDecision ?? null;
|
||||
let decision: string | null = null;
|
||||
try {
|
||||
// Some gateways may return a final decision inline during registration.
|
||||
// Only call waitDecision when registration did not already carry one.
|
||||
if (preResolvedDecision === undefined) {
|
||||
decision = await waitForExecApprovalDecision(approvalId);
|
||||
}
|
||||
decision = await resolveRegisteredExecApprovalDecision({
|
||||
approvalId,
|
||||
preResolvedDecision,
|
||||
});
|
||||
} catch {
|
||||
emitExecSystemEvent(
|
||||
`Exec denied (node=${nodeId} id=${approvalId}, approval-request-failed): ${params.command}`,
|
||||
|
||||
@@ -6,11 +6,14 @@ import type {
|
||||
TextContent,
|
||||
ToolCall,
|
||||
Tool,
|
||||
Usage,
|
||||
} from "@mariozechner/pi-ai";
|
||||
import { createAssistantMessageEventStream } from "@mariozechner/pi-ai";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { buildStreamErrorAssistantMessage } from "./stream-message-shared.js";
|
||||
import {
|
||||
buildAssistantMessage as buildStreamAssistantMessage,
|
||||
buildStreamErrorAssistantMessage,
|
||||
buildUsageWithNoCost,
|
||||
} from "./stream-message-shared.js";
|
||||
|
||||
const log = createSubsystemLogger("ollama-stream");
|
||||
|
||||
@@ -343,25 +346,15 @@ export function buildAssistantMessage(
|
||||
const hasToolCalls = toolCalls && toolCalls.length > 0;
|
||||
const stopReason: StopReason = hasToolCalls ? "toolUse" : "stop";
|
||||
|
||||
const usage: Usage = {
|
||||
input: response.prompt_eval_count ?? 0,
|
||||
output: response.eval_count ?? 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: (response.prompt_eval_count ?? 0) + (response.eval_count ?? 0),
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
};
|
||||
|
||||
return {
|
||||
role: "assistant",
|
||||
return buildStreamAssistantMessage({
|
||||
model: modelInfo,
|
||||
content,
|
||||
stopReason,
|
||||
api: modelInfo.api,
|
||||
provider: modelInfo.provider,
|
||||
model: modelInfo.id,
|
||||
usage,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
usage: buildUsageWithNoCost({
|
||||
input: response.prompt_eval_count ?? 0,
|
||||
output: response.eval_count ?? 0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// ── NDJSON streaming parser ─────────────────────────────────────────────────
|
||||
|
||||
@@ -30,7 +30,6 @@ import type {
|
||||
StopReason,
|
||||
TextContent,
|
||||
ToolCall,
|
||||
Usage,
|
||||
} from "@mariozechner/pi-ai";
|
||||
import { createAssistantMessageEventStream, streamSimple } from "@mariozechner/pi-ai";
|
||||
import {
|
||||
@@ -43,7 +42,9 @@ import {
|
||||
} from "./openai-ws-connection.js";
|
||||
import { log } from "./pi-embedded-runner/logger.js";
|
||||
import {
|
||||
buildAssistantMessage,
|
||||
buildAssistantMessageWithZeroUsage,
|
||||
buildUsageWithNoCost,
|
||||
buildStreamErrorAssistantMessage,
|
||||
} from "./stream-message-shared.js";
|
||||
|
||||
@@ -298,25 +299,16 @@ export function buildAssistantMessageFromResponse(
|
||||
const hasToolCalls = content.some((c) => c.type === "toolCall");
|
||||
const stopReason: StopReason = hasToolCalls ? "toolUse" : "stop";
|
||||
|
||||
const usage: Usage = {
|
||||
input: response.usage?.input_tokens ?? 0,
|
||||
output: response.usage?.output_tokens ?? 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
totalTokens: response.usage?.total_tokens ?? 0,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
};
|
||||
|
||||
return {
|
||||
role: "assistant",
|
||||
return buildAssistantMessage({
|
||||
model: modelInfo,
|
||||
content,
|
||||
stopReason,
|
||||
api: modelInfo.api,
|
||||
provider: modelInfo.provider,
|
||||
model: modelInfo.id,
|
||||
usage,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
usage: buildUsageWithNoCost({
|
||||
input: response.usage?.input_tokens ?? 0,
|
||||
output: response.usage?.output_tokens ?? 0,
|
||||
totalTokens: response.usage?.total_tokens ?? 0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -17,10 +17,32 @@ export function buildZeroUsage(): Usage {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAssistantMessageWithZeroUsage(params: {
|
||||
export function buildUsageWithNoCost(params: {
|
||||
input?: number;
|
||||
output?: number;
|
||||
cacheRead?: number;
|
||||
cacheWrite?: number;
|
||||
totalTokens?: number;
|
||||
}): Usage {
|
||||
const input = params.input ?? 0;
|
||||
const output = params.output ?? 0;
|
||||
const cacheRead = params.cacheRead ?? 0;
|
||||
const cacheWrite = params.cacheWrite ?? 0;
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
cacheRead,
|
||||
cacheWrite,
|
||||
totalTokens: params.totalTokens ?? input + output,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAssistantMessage(params: {
|
||||
model: StreamModelDescriptor;
|
||||
content: AssistantMessage["content"];
|
||||
stopReason: StopReason;
|
||||
usage: Usage;
|
||||
timestamp?: number;
|
||||
}): AssistantMessage {
|
||||
return {
|
||||
@@ -30,11 +52,26 @@ export function buildAssistantMessageWithZeroUsage(params: {
|
||||
api: params.model.api,
|
||||
provider: params.model.provider,
|
||||
model: params.model.id,
|
||||
usage: buildZeroUsage(),
|
||||
usage: params.usage,
|
||||
timestamp: params.timestamp ?? Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAssistantMessageWithZeroUsage(params: {
|
||||
model: StreamModelDescriptor;
|
||||
content: AssistantMessage["content"];
|
||||
stopReason: StopReason;
|
||||
timestamp?: number;
|
||||
}): AssistantMessage {
|
||||
return buildAssistantMessage({
|
||||
model: params.model,
|
||||
content: params.content,
|
||||
stopReason: params.stopReason,
|
||||
usage: buildZeroUsage(),
|
||||
timestamp: params.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildStreamErrorAssistantMessage(params: {
|
||||
model: StreamModelDescriptor;
|
||||
errorMessage: string;
|
||||
|
||||
Reference in New Issue
Block a user