Merge pull request #2692 from pikasTech/fix/workbench-user-submit-clock
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Failed
Pipelines as Code CI / hwlab-nc01-v03-ci-poll- Failed
透传 Workbench 用户消息发送时间
This commit is contained in:
@@ -1071,6 +1071,11 @@ function rejectRemovedResourceWorkspaceFiles(value) {
|
||||
throw adapterError("legacy_workspace_files_removed", "workspaceFiles/resourceWorkspaceFiles are removed; use AgentRun ResourceBundleRef kind=gitbundle with bundles[]");
|
||||
}
|
||||
|
||||
function validIsoTimestamp(value) {
|
||||
const timestamp = firstNonEmpty(value);
|
||||
return timestamp && Number.isFinite(Date.parse(timestamp)) ? new Date(Date.parse(timestamp)).toISOString() : null;
|
||||
}
|
||||
|
||||
function adapterError(code, message, details = {}) {
|
||||
return Object.assign(new Error(message), {
|
||||
code,
|
||||
@@ -1092,6 +1097,7 @@ function buildAgentRunCommandInput({ params, traceId, backendProfile, sessionId,
|
||||
...(promptEvidence?.prompt ? { promptMetadata: promptEvidence.prompt } : {}),
|
||||
traceId,
|
||||
userMessageId: agentRunUserMessageId(params, traceId),
|
||||
submittedAt: validIsoTimestamp(params.submittedAt),
|
||||
projectId: firstNonEmpty(params.projectId) || null,
|
||||
conversationId: safeConversationId(params.conversationId) || null,
|
||||
sessionId: sessionId ?? scopedAgentRunSessionIdForParams(params, traceId),
|
||||
@@ -1121,6 +1127,7 @@ function buildAgentRunSteerCommandInput({ params, traceId, steerTraceId, mapped
|
||||
...(promptEvidence?.prompt ? { promptMetadata: promptEvidence.prompt } : {}),
|
||||
traceId: steerTraceId,
|
||||
userMessageId: agentRunUserMessageId(params, steerTraceId),
|
||||
submittedAt: validIsoTimestamp(params.submittedAt),
|
||||
targetTraceId: traceId,
|
||||
targetCommandId: mapped.agentRun.commandId,
|
||||
conversationId: safeConversationId(mapped.conversationId ?? params.conversationId) || null,
|
||||
|
||||
@@ -168,6 +168,10 @@ test("projects only explicit AgentRun lifecycle timestamps", () => {
|
||||
}, { source: "hwlab-test" });
|
||||
assert.equal(withoutFacts.event.startedAt, null);
|
||||
assert.equal(withoutFacts.event.finishedAt, null);
|
||||
assert.equal(withoutFacts.event.createdAt, "2026-07-09T10:00:05.000Z");
|
||||
assert.equal(withoutFacts.event.submittedAt, null);
|
||||
assert.equal(withoutFacts.event.timingClock, "event-created-at-fallback");
|
||||
assert.equal(withoutFacts.event.blocking, false);
|
||||
});
|
||||
|
||||
test("projects AgentRun assistant_message Kafka event into HWLAB trace event", () => {
|
||||
@@ -336,6 +340,7 @@ test("projects AgentRun user_message into a stable HWLAB user event", () => {
|
||||
traceId: "trc_user_message",
|
||||
targetTraceId: "trc_user_message_target",
|
||||
userMessageId: "msg_user_message_user",
|
||||
submittedAt: "2026-07-10T09:59:55.000Z",
|
||||
text: "replay this input",
|
||||
source: "agentrun-turn-command-payload",
|
||||
valuesPrinted: false
|
||||
@@ -352,6 +357,10 @@ test("projects AgentRun user_message into a stable HWLAB user event", () => {
|
||||
assert.equal(projected.event.userMessageId, "msg_user_message_user");
|
||||
assert.equal(projected.event.messageId, "msg_user_message_user");
|
||||
assert.equal(projected.event.targetTraceId, "trc_user_message_target");
|
||||
assert.equal(projected.event.createdAt, "2026-07-10T09:59:55.000Z");
|
||||
assert.equal(projected.event.submittedAt, "2026-07-10T09:59:55.000Z");
|
||||
assert.equal(projected.event.timingClock, "user-submitted-at");
|
||||
assert.equal(projected.event.blocking, false);
|
||||
assert.equal(projected.event.text, "replay this input");
|
||||
assert.equal(projected.event.sourceEventId, "evt_user_message");
|
||||
assert.equal(projected.sourceEvent.partition, 1);
|
||||
|
||||
@@ -1516,8 +1516,10 @@ function mapAgentRunSourceEventToHwlabEvent({ input, sourceEvent, payload, run,
|
||||
if (type === "user_message") {
|
||||
const userMessageId = firstText(payload.userMessageId);
|
||||
const userText = textPayload(payload, "user message");
|
||||
const submittedAt = timestampValue(payload.submittedAt);
|
||||
return {
|
||||
...base,
|
||||
createdAt: submittedAt || base.createdAt,
|
||||
type: "user",
|
||||
eventType: "user",
|
||||
status: "sent",
|
||||
@@ -1529,6 +1531,9 @@ function mapAgentRunSourceEventToHwlabEvent({ input, sourceEvent, payload, run,
|
||||
userMessageId,
|
||||
targetTraceId: firstText(payload.targetTraceId),
|
||||
userSource: firstText(payload.source),
|
||||
submittedAt,
|
||||
timingClock: submittedAt ? "user-submitted-at" : "event-created-at-fallback",
|
||||
blocking: false,
|
||||
terminal: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ test("L1 AgentRun application dispatches gpt.pika without polling or projecting
|
||||
stateFile,
|
||||
env: agentRunEnvironment(),
|
||||
fetchImpl: fakeAgentRunManager(calls),
|
||||
now: () => "2026-07-20T01:13:12.232Z",
|
||||
});
|
||||
|
||||
const created = await app.createSession({ actor: { id: "usr_native", role: "user" }, params: { providerProfile: "gpt.pika" } });
|
||||
@@ -29,7 +30,7 @@ test("L1 AgentRun application dispatches gpt.pika without polling or projecting
|
||||
actor: { id: "usr_native", role: "user" },
|
||||
traceId: "trc_l1_gpt_pika_smoke",
|
||||
sessionId: session.sessionId,
|
||||
params: { message: "hi", providerProfile: "gpt.pika" },
|
||||
params: { message: "hi", submittedAt: "2026-07-20T01:13:07.699Z", providerProfile: "gpt.pika" },
|
||||
};
|
||||
const admitted = await app.admitTurn(input);
|
||||
const result = await app.dispatchTurn(admitted);
|
||||
@@ -48,6 +49,7 @@ test("L1 AgentRun application dispatches gpt.pika without polling or projecting
|
||||
const state = JSON.parse(await readFile(stateFile, "utf8"));
|
||||
expect(state.turns[input.traceId]).toMatchObject({
|
||||
status: "running",
|
||||
createdAt: "2026-07-20T01:13:07.699Z",
|
||||
terminal: false,
|
||||
runId: "run_l1_gpt_pika",
|
||||
commandId: "cmd_l1_gpt_pika",
|
||||
@@ -94,6 +96,9 @@ function agentRunEnvironment(): Record<string, string> {
|
||||
AGENTRUN_API_KEY: "test-agentrun-key",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "NC01",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_GPT_PIKA_SECRET_NAME: "provider-gpt-pika",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_NAME: "agentrun-test-tool-github-pr",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_UNIDESK_SSH_TOOL_SECRET_NAME: "agentrun-test-tool-unidesk-ssh",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_REPO_URL: "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git",
|
||||
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "gpt.pika",
|
||||
@@ -126,6 +131,7 @@ function fakeAgentRunManager(calls: Array<{ method: string; path: string; author
|
||||
}
|
||||
if (method === "POST" && url.pathname === "/api/v1/runs/run_l1_gpt_pika/commands") {
|
||||
expect(body.payload.prompt).toBe("hi");
|
||||
expect(body.payload.submittedAt).toBe("2026-07-20T01:13:07.699Z");
|
||||
return send({
|
||||
id: "cmd_l1_gpt_pika",
|
||||
runId: "run_l1_gpt_pika",
|
||||
|
||||
@@ -76,7 +76,7 @@ export function createNativeAgentRunWorkbenchApplication(options: {
|
||||
async admitTurn(input) {
|
||||
return mutate((state) => {
|
||||
const session = ownedSession(state, input.sessionId, input.actor.id);
|
||||
const timestamp = now();
|
||||
const timestamp = validTimestamp(input.params.submittedAt) ?? now();
|
||||
const message = requiredText(input.params.message, "message");
|
||||
state.turns[input.traceId] = {
|
||||
traceId: input.traceId,
|
||||
@@ -93,7 +93,7 @@ export function createNativeAgentRunWorkbenchApplication(options: {
|
||||
session.status = "running";
|
||||
session.lastTraceId = input.traceId;
|
||||
session.updatedAt = timestamp;
|
||||
return { ...input, params: { ...input.params, message, providerProfile: text(input.params.providerProfile) || session.providerProfile } };
|
||||
return { ...input, params: { ...input.params, message, submittedAt: timestamp, providerProfile: text(input.params.providerProfile) || session.providerProfile } };
|
||||
});
|
||||
},
|
||||
async dispatchTurn(input: WorkbenchTurnInput) {
|
||||
@@ -163,6 +163,7 @@ async function load(stateFile: string): Promise<NativeState> {
|
||||
}
|
||||
function object(value: unknown) { return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, Record<string, any>> : {}; }
|
||||
function text(value: unknown) { return String(value ?? "").trim(); }
|
||||
function validTimestamp(value: unknown) { const timestamp = text(value); return timestamp && Number.isFinite(Date.parse(timestamp)) ? new Date(Date.parse(timestamp)).toISOString() : null; }
|
||||
function requiredText(value: unknown, field: string) { const result = text(value); if (!result) throw codedError("invalid_input", `${field} is required`); return result; }
|
||||
function validOptionalId(value: unknown, prefix: string) { const id = text(value); if (!id) return null; if (!id.startsWith(prefix)) throw codedError("invalid_input", `${prefix.slice(0, -1)} id is invalid`); return id; }
|
||||
function ownedSession(state: NativeState, sessionId: string, ownerUserId: string) { const session = state.sessions[sessionId]; if (!session || session.ownerUserId !== ownerUserId) throw codedError("session_not_found", "session is not visible to the actor"); return session; }
|
||||
|
||||
@@ -545,7 +545,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
|
||||
chatPending.value = true;
|
||||
rememberRecentDraft(value);
|
||||
recordActivity("submit");
|
||||
const payload = { message: value, prompt: value, sessionId, threadId: providerThreadId, traceId, userMessageId, providerProfile: providerProfile.value, gatewayShellTimeoutMs: gatewayShellTimeoutMs.value, targetTraceId: composer.value.targetTraceId, steerTraceId };
|
||||
const payload = { message: value, prompt: value, submittedAt, sessionId, threadId: providerThreadId, traceId, userMessageId, providerProfile: providerProfile.value, gatewayShellTimeoutMs: gatewayShellTimeoutMs.value, targetTraceId: composer.value.targetTraceId, steerTraceId };
|
||||
const response = steerMode
|
||||
? await workbenchColadaMutations.steerAgentMessage({ payload, timeoutMs: codeAgentTimeoutMs.value, activityRef: () => activityRef.value, sessionId, traceId })
|
||||
: await workbenchColadaMutations.submitAgentMessage({ payload, timeoutMs: codeAgentTimeoutMs.value, activityRef: () => activityRef.value, sessionId, traceId });
|
||||
|
||||
Reference in New Issue
Block a user