feat(orchestrator): observe Page Data validation
This commit is contained in:
+154
-3
@@ -111,6 +111,43 @@ function messageText(message) {
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function isPageDataWorkflowRequested(userMessage, taskType = null) {
|
||||
const metadata = userMessage?.metadata ?? {};
|
||||
const runMetadata = metadata?.[RUN_METADATA_KEY] ?? metadata?.agentRun ?? {};
|
||||
const normalizedTaskType = String(
|
||||
taskType ?? runMetadata?.taskType ?? metadata?.taskType ?? '',
|
||||
).trim().toLowerCase();
|
||||
const selectedSkill = String(
|
||||
runMetadata?.selectedChatSkill ?? metadata?.selectedChatSkill ?? '',
|
||||
).trim();
|
||||
const displayText = String(metadata?.displayText ?? '').trim()
|
||||
|| deriveUserFacingText(messageText(userMessage));
|
||||
return runMetadata?.pgRequired === true
|
||||
|| runMetadata?.pageDataAiderWorkflow === true
|
||||
|| normalizedTaskType === 'page_data_dev'
|
||||
|| normalizedTaskType === 'page_data_dev_complex'
|
||||
|| selectedSkill === 'page-data-collect'
|
||||
|| isPageDataIntent(displayText);
|
||||
}
|
||||
|
||||
function pageDataFailureCheckId(code) {
|
||||
switch (String(code ?? '').trim().toUpperCase()) {
|
||||
case 'PAGE_DATA_DELIVERY_FAILED':
|
||||
return 'page_data_binding';
|
||||
case 'PAGE_DATA_DELIVERABLE_MISSING':
|
||||
return 'page_data_deliverable';
|
||||
case 'DELIVERABLE_DATA_STORAGE_FORBIDDEN':
|
||||
return 'page_data_storage_policy';
|
||||
case 'REQUIRED_REVIEW_EXECUTOR_MISMATCH':
|
||||
case 'REQUIRED_EXECUTOR_UNAVAILABLE':
|
||||
return 'independent_review';
|
||||
case 'TOOL_GATEWAY_VALIDATION_FAILED':
|
||||
return 'code_validation';
|
||||
default:
|
||||
return 'agent_run_completion';
|
||||
}
|
||||
}
|
||||
|
||||
function buildFreshSessionContext(conversation, { maxMessages = 16, maxChars = 12_000 } = {}) {
|
||||
const visible = [];
|
||||
for (const message of Array.isArray(conversation) ? conversation : []) {
|
||||
@@ -394,6 +431,7 @@ function getRunOptionsFromMessage(userMessage) {
|
||||
? String(runMetadata.reviewExecutor).trim().toLowerCase()
|
||||
: null,
|
||||
pageDataAiderWorkflow: runMetadata?.pageDataAiderWorkflow === true,
|
||||
pgRequired: runMetadata?.pgRequired === true,
|
||||
forceDeepReasoning: runMetadata?.forceDeepReasoning === true || metadata?.forceDeepReasoning === true,
|
||||
validation: normalizeToolGatewayValidation(runMetadata?.validation ?? metadata?.toolGatewayValidation),
|
||||
sessionMessageCount: normalizeSessionMessageCount(
|
||||
@@ -475,6 +513,7 @@ export function createAgentRunGateway({
|
||||
syncUserPagesOnSuccess = null,
|
||||
observePersonalMemoryOnSuccess = null,
|
||||
observeWorkflowRun = null,
|
||||
observeWorkflowValidation = null,
|
||||
isSessionExternallyBusy = null,
|
||||
validateRunDeliverables = null,
|
||||
quiesceSessionOnTerminal = null,
|
||||
@@ -637,9 +676,78 @@ export function createAgentRunGateway({
|
||||
logger: console,
|
||||
});
|
||||
|
||||
const validationDispatcher = createShadowObservationDispatcher({
|
||||
observe: observeWorkflowValidation,
|
||||
maxConcurrent: maxConcurrentShadowObservations,
|
||||
maxQueued: maxQueuedShadowObservations,
|
||||
onCompleted: async (input, result, context) => {
|
||||
if (!result?.observed) {
|
||||
await appendEvent(input.runId, 'workflow_validation_observation_skipped', {
|
||||
reason: result?.reason ?? 'shadow_validation_not_selected',
|
||||
});
|
||||
return;
|
||||
}
|
||||
await appendEvent(input.runId, 'workflow_validation_observation_completed', {
|
||||
engine: result.engine ?? 'langgraph',
|
||||
mode: result.mode ?? 'shadow',
|
||||
configVersion: result.configVersion ?? null,
|
||||
kind: result.validation?.kind ?? 'page-data-delivery',
|
||||
verdict: result.validation?.verdict ?? null,
|
||||
latencyMs: Math.max(0, nowMs() - context.enqueuedAt),
|
||||
});
|
||||
},
|
||||
onFailed: async (input, error, context) => {
|
||||
await appendEvent(input.runId, 'workflow_validation_observation_failed', {
|
||||
code: String(error?.code ?? 'WORKFLOW_VALIDATION_OBSERVATION_FAILED').slice(0, 128),
|
||||
message: String(error instanceof Error ? error.message : error).slice(0, 1000),
|
||||
latencyMs: Math.max(0, nowMs() - context.enqueuedAt),
|
||||
}).catch(() => {});
|
||||
},
|
||||
onSkipped: async (input, context) => {
|
||||
await appendEvent(input.runId, 'workflow_validation_observation_skipped', {
|
||||
reason: context.reason,
|
||||
});
|
||||
},
|
||||
logger: console,
|
||||
});
|
||||
|
||||
function dispatchShadowObservation(input) {
|
||||
if (input.toolMode !== 'code') return;
|
||||
shadowDispatcher.dispatch(input);
|
||||
const pageDataRequired = isPageDataWorkflowRequested(input.userMessage, input.taskType);
|
||||
if (input.toolMode !== 'code' && !pageDataRequired) return;
|
||||
shadowDispatcher.dispatch({
|
||||
...input,
|
||||
taskType: input.taskType || (pageDataRequired ? 'page_data_dev' : null),
|
||||
pageDataRequired,
|
||||
});
|
||||
}
|
||||
|
||||
function dispatchPageDataValidationObservation({
|
||||
row,
|
||||
runId,
|
||||
checks,
|
||||
metrics = null,
|
||||
}) {
|
||||
const userMessage = parseDbJsonColumn(row?.user_message_json, {}) ?? {};
|
||||
const runOptions = getRunOptionsFromMessage(userMessage);
|
||||
if (!isPageDataWorkflowRequested(userMessage, runOptions.taskType)) return false;
|
||||
return validationDispatcher.dispatch({
|
||||
runId,
|
||||
requestId: row?.request_id ?? null,
|
||||
userId: row?.user_id ?? null,
|
||||
workflowName: 'code-run-v1',
|
||||
observation: {
|
||||
idempotencyKey: `${runId}:page-data-delivery:v1`,
|
||||
taskType: runOptions.taskType || 'page_data_dev',
|
||||
required: true,
|
||||
checks,
|
||||
metrics: {
|
||||
pageCount: Number(metrics?.pageCount ?? 0),
|
||||
publicationCount: Number(metrics?.publicationCount ?? 0),
|
||||
},
|
||||
source: 'portal-agent-run',
|
||||
observedAt: nowMs(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function appendRunSnapshot(runId) {
|
||||
@@ -1464,6 +1572,8 @@ export function createAgentRunGateway({
|
||||
}, { expectedStatus: 'running' });
|
||||
return;
|
||||
}
|
||||
const userMessage = parseDbJsonColumn(row.user_message_json, {}) ?? {};
|
||||
const runOptions = getRunOptionsFromMessage(userMessage);
|
||||
assertRequiredImageGenerationCompleted(row, routing, toolEvidence);
|
||||
// `row` was loaded before this worker claimed the run, so its started_at
|
||||
// can still be null. Refresh it before scoping workspace files to the
|
||||
@@ -1490,7 +1600,8 @@ export function createAgentRunGateway({
|
||||
}
|
||||
const runDisplayText = extractRunDisplayText(row);
|
||||
const selectedSkill = selectedRunSkill(row);
|
||||
const pageDataIntent = isPageDataIntent(runDisplayText)
|
||||
const pageDataIntent = isPageDataWorkflowRequested(userMessage, runOptions.taskType)
|
||||
|| isPageDataIntent(runDisplayText)
|
||||
|| selectedSkill === 'page-data-collect'
|
||||
|| routing?.suggestedSkill === 'page-data-collect';
|
||||
const pageGenerationIntent = isPageGenerationIntent(runDisplayText)
|
||||
@@ -1557,6 +1668,35 @@ export function createAgentRunGateway({
|
||||
error_message: null,
|
||||
}, { expectedStatus: 'running' });
|
||||
if (!marked) return false;
|
||||
if (pageDataIntent) {
|
||||
dispatchPageDataValidationObservation({
|
||||
row,
|
||||
runId,
|
||||
checks: [
|
||||
{ id: 'agent_run_completion', status: 'passed' },
|
||||
{
|
||||
id: 'page_data_binding',
|
||||
status: typeof syncUserPagesOnSuccess === 'function' ? 'passed' : 'skipped',
|
||||
},
|
||||
{
|
||||
id: 'page_data_storage_policy',
|
||||
status: typeof validateRunDeliverables === 'function' ? 'passed' : 'skipped',
|
||||
},
|
||||
{
|
||||
id: 'page_data_deliverable',
|
||||
status: requiresPageDeliverable ? 'passed' : 'skipped',
|
||||
},
|
||||
{
|
||||
id: 'independent_review',
|
||||
status: runOptions.reviewExecutor ? 'passed' : 'skipped',
|
||||
},
|
||||
],
|
||||
metrics: {
|
||||
pageCount: deliverables?.pageCount ?? 0,
|
||||
publicationCount: deliverables?.publicationCount ?? 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (typeof observePersonalMemoryOnSuccess === 'function') {
|
||||
await observePersonalMemoryOnSuccess({
|
||||
userId: row.user_id,
|
||||
@@ -1639,6 +1779,17 @@ export function createAgentRunGateway({
|
||||
}
|
||||
: {}),
|
||||
}, { expectedStatus: 'running' });
|
||||
if (!retryable) {
|
||||
dispatchPageDataValidationObservation({
|
||||
row,
|
||||
runId,
|
||||
checks: [{
|
||||
id: pageDataFailureCheckId(err?.code),
|
||||
status: 'failed',
|
||||
codes: [String(err?.code ?? 'AGENT_RUN_FAILED')],
|
||||
}],
|
||||
});
|
||||
}
|
||||
if (retryable && autoDispatch) {
|
||||
setTimeout(() => dispatchRun(runId), retryDelaysMs[nextAttempt - 1]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user