refactor: add remote runtime adapter
This commit is contained in:
@@ -336,6 +336,24 @@ function parseDispatchExecutionCompletion(rawOutput) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildRemoteExecutionCompletionPayload(task, payload) {
|
||||
return {
|
||||
taskId: task.taskId,
|
||||
status: payload.status === "failed" ? "failed" : "completed",
|
||||
requestId: payload.requestId,
|
||||
replyBody: typeof payload.replyBody === "string" ? payload.replyBody.trim() || undefined : undefined,
|
||||
errorMessage: typeof payload.errorMessage === "string" ? payload.errorMessage.trim() || undefined : undefined,
|
||||
dispatchExecutionId:
|
||||
typeof payload.dispatchExecutionId === "string" ? payload.dispatchExecutionId.trim() || undefined : undefined,
|
||||
targetProjectId:
|
||||
typeof payload.targetProjectId === "string" ? payload.targetProjectId.trim() || undefined : undefined,
|
||||
targetThreadId:
|
||||
typeof payload.targetThreadId === "string" ? payload.targetThreadId.trim() || undefined : undefined,
|
||||
rawThreadReply:
|
||||
typeof payload.rawThreadReply === "string" ? payload.rawThreadReply.trim() || undefined : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async function runMasterAgentTask(config, runtime, task) {
|
||||
const outputFile = join(os.tmpdir(), `${task.taskId}.reply.txt`);
|
||||
const stderrChunks = [];
|
||||
@@ -372,15 +390,18 @@ async function runMasterAgentTask(config, runtime, task) {
|
||||
task.taskType === "dispatch_execution"
|
||||
? parseDispatchExecutionCompletion(replyBody)
|
||||
: null;
|
||||
const completion = await completeMasterAgentTask(config, runtime, {
|
||||
taskId: task.taskId,
|
||||
status: "completed",
|
||||
replyBody: dispatchExecutionCompletion?.replyBody ?? replyBody,
|
||||
dispatchExecutionId: task.dispatchExecutionId,
|
||||
targetProjectId: task.targetProjectId,
|
||||
targetThreadId: task.targetThreadId,
|
||||
rawThreadReply: dispatchExecutionCompletion?.rawThreadReply,
|
||||
});
|
||||
const completion = await completeMasterAgentTask(
|
||||
config,
|
||||
runtime,
|
||||
buildRemoteExecutionCompletionPayload(task, {
|
||||
status: "completed",
|
||||
replyBody: dispatchExecutionCompletion?.replyBody ?? replyBody,
|
||||
dispatchExecutionId: task.dispatchExecutionId,
|
||||
targetProjectId: task.targetProjectId,
|
||||
targetThreadId: task.targetThreadId,
|
||||
rawThreadReply: dispatchExecutionCompletion?.rawThreadReply,
|
||||
}),
|
||||
);
|
||||
runtime.activeMasterTask = {
|
||||
taskId: task.taskId,
|
||||
status: completion.ok ? "completed" : "complete_failed",
|
||||
@@ -403,14 +424,17 @@ async function runMasterAgentTask(config, runtime, task) {
|
||||
completedAt: new Date().toISOString(),
|
||||
detail,
|
||||
};
|
||||
await completeMasterAgentTask(config, runtime, {
|
||||
taskId: task.taskId,
|
||||
status: "failed",
|
||||
errorMessage: detail,
|
||||
dispatchExecutionId: task.dispatchExecutionId,
|
||||
targetProjectId: task.targetProjectId,
|
||||
targetThreadId: task.targetThreadId,
|
||||
}).catch(() => null);
|
||||
await completeMasterAgentTask(
|
||||
config,
|
||||
runtime,
|
||||
buildRemoteExecutionCompletionPayload(task, {
|
||||
status: "failed",
|
||||
errorMessage: detail,
|
||||
dispatchExecutionId: task.dispatchExecutionId,
|
||||
targetProjectId: task.targetProjectId,
|
||||
targetThreadId: task.targetThreadId,
|
||||
}),
|
||||
).catch(() => null);
|
||||
await postAppLog(config, runtime, {
|
||||
projectId: "master-agent",
|
||||
level: "error",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { authorizeDeviceWriteRequest } from "@/lib/boss-device-auth";
|
||||
import { completeMasterAgentTask } from "@/lib/boss-data";
|
||||
import { normalizeRemoteExecutionResult } from "@/lib/execution/remote-runtime-adapter";
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
@@ -30,17 +31,18 @@ export async function POST(
|
||||
const { taskId } = await context.params;
|
||||
|
||||
try {
|
||||
const normalized = normalizeRemoteExecutionResult(body);
|
||||
const task = await completeMasterAgentTask({
|
||||
taskId,
|
||||
deviceId: body.deviceId.trim(),
|
||||
status: body.status === "failed" ? "failed" : "completed",
|
||||
replyBody: body.replyBody,
|
||||
errorMessage: body.errorMessage,
|
||||
requestId: body.requestId,
|
||||
dispatchExecutionId: body.dispatchExecutionId,
|
||||
targetProjectId: body.targetProjectId,
|
||||
targetThreadId: body.targetThreadId,
|
||||
rawThreadReply: body.rawThreadReply,
|
||||
status: normalized.status,
|
||||
replyBody: normalized.replyBody,
|
||||
errorMessage: normalized.errorMessage,
|
||||
requestId: normalized.requestId,
|
||||
dispatchExecutionId: normalized.dispatchExecutionId,
|
||||
targetProjectId: normalized.targetProjectId,
|
||||
targetThreadId: normalized.targetThreadId,
|
||||
rawThreadReply: normalized.rawThreadReply,
|
||||
});
|
||||
return NextResponse.json({ ok: true, task });
|
||||
} catch (error) {
|
||||
|
||||
11
src/lib/execution/backends/boss-native-orchestrator.ts
Normal file
11
src/lib/execution/backends/boss-native-orchestrator.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { OrchestrationBackend } from "@/lib/execution/orchestration-backend";
|
||||
|
||||
export const BOSS_NATIVE_ORCHESTRATOR: OrchestrationBackend = {
|
||||
backendId: "boss-native-orchestrator",
|
||||
async describe() {
|
||||
return {
|
||||
backendId: "boss-native-orchestrator",
|
||||
label: "Boss Native Orchestrator",
|
||||
};
|
||||
},
|
||||
};
|
||||
43
src/lib/execution/remote-runtime-adapter.ts
Normal file
43
src/lib/execution/remote-runtime-adapter.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export interface RemoteExecutionResultInput {
|
||||
status: "completed" | "failed";
|
||||
dispatchExecutionId?: string;
|
||||
targetProjectId?: string;
|
||||
targetThreadId?: string;
|
||||
rawThreadReply?: string;
|
||||
replyBody?: string;
|
||||
errorMessage?: string;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
export interface NormalizedRemoteExecutionResult {
|
||||
status: "completed" | "failed";
|
||||
dispatchExecutionId?: string;
|
||||
targetProjectId?: string;
|
||||
targetThreadId?: string;
|
||||
rawThreadReply?: string;
|
||||
replyBody?: string;
|
||||
errorMessage?: string;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
function trimToDefined(value: string | undefined) {
|
||||
const trimmed = value?.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function normalizeRemoteExecutionResult(
|
||||
input: RemoteExecutionResultInput,
|
||||
): NormalizedRemoteExecutionResult {
|
||||
return {
|
||||
status: input.status === "failed" ? "failed" : "completed",
|
||||
dispatchExecutionId: trimToDefined(input.dispatchExecutionId),
|
||||
targetProjectId: trimToDefined(input.targetProjectId),
|
||||
targetThreadId: trimToDefined(input.targetThreadId),
|
||||
rawThreadReply: trimToDefined(input.rawThreadReply),
|
||||
replyBody: trimToDefined(input.replyBody),
|
||||
errorMessage: trimToDefined(input.errorMessage),
|
||||
requestId: trimToDefined(input.requestId),
|
||||
};
|
||||
}
|
||||
|
||||
export const normalizeRemoteExecutionResultForTesting = normalizeRemoteExecutionResult;
|
||||
39
tests/remote-runtime-adapter.test.ts
Normal file
39
tests/remote-runtime-adapter.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { normalizeRemoteExecutionResultForTesting } from "../src/lib/execution/remote-runtime-adapter.ts";
|
||||
|
||||
test("RemoteRuntimeAdapter 会把 local-agent 回写标准化成统一结果", () => {
|
||||
const normalized = normalizeRemoteExecutionResultForTesting({
|
||||
status: "completed",
|
||||
dispatchExecutionId: "dx-1",
|
||||
targetProjectId: "project-1",
|
||||
targetThreadId: "thread-1",
|
||||
rawThreadReply: " 链路正常 ",
|
||||
replyBody: " 主 Agent 汇总:链路正常 ",
|
||||
});
|
||||
|
||||
assert.equal(normalized.status, "completed");
|
||||
assert.equal(normalized.dispatchExecutionId, "dx-1");
|
||||
assert.equal(normalized.targetProjectId, "project-1");
|
||||
assert.equal(normalized.targetThreadId, "thread-1");
|
||||
assert.equal(normalized.rawThreadReply, "链路正常");
|
||||
assert.equal(normalized.replyBody, "主 Agent 汇总:链路正常");
|
||||
});
|
||||
|
||||
test("RemoteRuntimeAdapter 会忽略空白字段并保留失败状态", () => {
|
||||
const normalized = normalizeRemoteExecutionResultForTesting({
|
||||
status: "failed",
|
||||
dispatchExecutionId: " ",
|
||||
targetProjectId: " project-2 ",
|
||||
targetThreadId: "",
|
||||
rawThreadReply: " ",
|
||||
errorMessage: " MODEL_CALL_FAILED ",
|
||||
});
|
||||
|
||||
assert.equal(normalized.status, "failed");
|
||||
assert.equal(normalized.dispatchExecutionId, undefined);
|
||||
assert.equal(normalized.targetProjectId, "project-2");
|
||||
assert.equal(normalized.targetThreadId, undefined);
|
||||
assert.equal(normalized.rawThreadReply, undefined);
|
||||
assert.equal(normalized.errorMessage, "MODEL_CALL_FAILED");
|
||||
});
|
||||
Reference in New Issue
Block a user