feat(ql3): expose copilot diagnosis read model

This commit is contained in:
whyour
2026-08-16 00:41:34 +08:00
parent 774af4a4c9
commit f77b19e615
16 changed files with 2526 additions and 48 deletions
@@ -31,6 +31,10 @@ import {
type ClusterCopilotFailureDiagnosisProjection,
type CreateProductionClusterCopilotFailureDiagnosisOptions,
} from './copilot/failureDiagnosisComposition';
import {
createProductionClusterCopilotFailureDiagnosisReadService,
type CreateProductionClusterCopilotFailureDiagnosisReadServiceOptions,
} from './copilot/failureDiagnosisReadComposition';
export interface EnabledProductionClusterAiConfig {
readonly enabled: true;
@@ -54,6 +58,11 @@ export interface ProductionClusterAiControlApplicationOptions {
readonly createCopilot?: (
options: CreateProductionClusterCopilotFailureDiagnosisOptions,
) => Promise<Readonly<CopilotFailureDiagnosisApplicationService>>;
readonly createCopilotRead?: (
options: CreateProductionClusterCopilotFailureDiagnosisReadServiceOptions,
) => ReturnType<
typeof createProductionClusterCopilotFailureDiagnosisReadService
>;
readonly openAiDatabase?: ReturnType<typeof createPostgresDatabaseOpener>;
}
@@ -241,7 +250,9 @@ export async function startProductionClusterAiControlApplication(
Array.isArray(options) ||
typeof options.audit !== 'function'
) {
throw new TypeError('Production Cluster AI application options are invalid');
throw new TypeError(
'Production Cluster AI application options are invalid',
);
}
const startControl =
options.startControl ?? startProductionClusterControlApplication;
@@ -249,14 +260,20 @@ export async function startProductionClusterAiControlApplication(
options.bootstrapPrompt ?? bootstrapPostgresPluginPackagePromptApplication;
const createCopilot =
options.createCopilot ?? createProductionClusterCopilotFailureDiagnosis;
const createCopilotRead =
options.createCopilotRead ??
createProductionClusterCopilotFailureDiagnosisReadService;
if (
typeof startControl !== 'function' ||
typeof bootstrapPrompt !== 'function' ||
typeof createCopilot !== 'function' ||
typeof createCopilotRead !== 'function' ||
(options.openAiDatabase !== undefined &&
typeof options.openAiDatabase !== 'function')
) {
throw new TypeError('Production Cluster AI application factories are invalid');
throw new TypeError(
'Production Cluster AI application factories are invalid',
);
}
const copilotArtifactStore = options.control.workerIngress?.artifactStore;
if (
@@ -303,18 +320,27 @@ export async function startProductionClusterAiControlApplication(
let copilotApplication:
| Readonly<CopilotFailureDiagnosisApplicationService>
| undefined;
let copilotReadApplication:
| ReturnType<
typeof createProductionClusterCopilotFailureDiagnosisReadService
>
| undefined;
let copilotSuccessfulCompletion:
| CopilotFailureDiagnosisModelCompletionCoordinator
| undefined;
let stopPromise: Promise<ClusterControlStopResult> | undefined;
let promptOutputPolicy: ProjectPolicyEngine | undefined;
const promptOutputReadAuthorizer = Object.freeze({
async authorize(request: Readonly<{
principal: Parameters<ProjectPolicyEngine['authorize']>[0];
projectId: string;
}>) {
async authorize(
request: Readonly<{
principal: Parameters<ProjectPolicyEngine['authorize']>[0];
projectId: string;
}>,
) {
if (!aiDatabase) {
throw new Error('Cluster AI database is unavailable during output read');
throw new Error(
'Cluster AI database is unavailable during output read',
);
}
promptOutputPolicy ??= new ProjectPolicyEngine(
new PostgresProjectPolicyRepository(aiDatabase.pool),
@@ -361,7 +387,9 @@ export async function startProductionClusterAiControlApplication(
},
async loadProviders() {
if (!aiDatabase) {
throw new Error('Cluster AI database is unavailable during provider load');
throw new Error(
'Cluster AI database is unavailable during provider load',
);
}
const credentialStorage = new PostgresModelProviderCredentialReader(
aiDatabase.pool,
@@ -409,7 +437,11 @@ export async function startProductionClusterAiControlApplication(
throw new Error('Cluster AI Prompt application did not activate');
}
if (preparedCopilot !== undefined) {
if (!copilotSuccessfulCompletion || !aiDatabase || !copilotArtifactStore) {
if (
!copilotSuccessfulCompletion ||
!aiDatabase ||
!copilotArtifactStore
) {
throw new Error('Cluster Copilot shared authorities did not activate');
}
copilotApplication = await createCopilot({
@@ -419,6 +451,10 @@ export async function startProductionClusterAiControlApplication(
successfulCompletion: copilotSuccessfulCompletion,
artifactStore: copilotArtifactStore,
});
copilotReadApplication = createCopilotRead({
pool: aiDatabase.pool,
prepared: preparedCopilot,
});
}
controlApplication = await startControl({
...options.control,
@@ -445,11 +481,13 @@ export async function startProductionClusterAiControlApplication(
capability: promptApplication.promptExecutionOutputs,
},
}),
...(copilotApplication === undefined
...(copilotApplication === undefined ||
copilotReadApplication === undefined
? {}
: {
copilotFailureDiagnosis: {
capability: copilotApplication,
readCapability: copilotReadApplication,
},
}),
});
@@ -0,0 +1,57 @@
import { CopilotFailureDiagnosisReadService } from '@qinglong/ai/failure-diagnosis-read-model';
import { PostgresCopilotFailureDiagnosisAdmissionRepository } from '@qinglong/ai/postgres-failure-diagnosis-admission-storage';
import { PostgresCopilotFailureDiagnosisModelRepository } from '@qinglong/ai/postgres-failure-diagnosis-model-execution-storage';
import { PostgresCopilotFailureDiagnosisPreModelTerminalizationRepository } from '@qinglong/ai/failure-diagnosis-pre-model-terminalization';
import {
PostgresProjectPolicyRepository,
type QingLongPostgresPool,
} from '@qinglong/cluster-postgres/runtime';
import { ProjectPolicyEngine } from '@qinglong/runtime-core/project-policy';
import type { PreparedClusterCopilotFailureDiagnosisProjection } from './failureDiagnosisComposition';
export interface CreateProductionClusterCopilotFailureDiagnosisReadServiceOptions {
readonly pool: QingLongPostgresPool;
readonly prepared: PreparedClusterCopilotFailureDiagnosisProjection;
}
/**
* Reuses the execution Pool, durable repositories and projected output keys;
* creating this service owns no connection, listener or background lifecycle.
*/
export function createProductionClusterCopilotFailureDiagnosisReadService(
options: CreateProductionClusterCopilotFailureDiagnosisReadServiceOptions,
): Readonly<CopilotFailureDiagnosisReadService> {
if (
!options ||
typeof options !== 'object' ||
Array.isArray(options) ||
typeof options.pool?.query !== 'function' ||
typeof options.pool?.connect !== 'function' ||
typeof options.prepared?.outputKeys?.resolve !== 'function'
) {
throw new TypeError(
'Production Cluster Copilot failure diagnosis read dependencies are invalid',
);
}
const admissions = new PostgresCopilotFailureDiagnosisAdmissionRepository(
options.pool,
);
const models = new PostgresCopilotFailureDiagnosisModelRepository(
options.pool,
);
const terminalizations =
new PostgresCopilotFailureDiagnosisPreModelTerminalizationRepository(
options.pool,
);
return new CopilotFailureDiagnosisReadService({
admissions,
terminalizations,
finalizations: models,
models,
authorizer: new ProjectPolicyEngine(
new PostgresProjectPolicyRepository(options.pool),
),
keys: options.prepared.outputKeys,
});
}
@@ -65,6 +65,12 @@ import {
createClusterControlCopilotFailureDiagnosisRoute,
type ClusterCopilotFailureDiagnosisCapability,
} from '../copilot/failure-diagnosis/failureDiagnosisRoute';
import {
createClusterControlCopilotFailureDiagnosisInspectionRoute,
createClusterControlCopilotFailureDiagnosisOutputReadRoute,
type ClusterCopilotFailureDiagnosisInspectionCapability,
type ClusterCopilotFailureDiagnosisOutputReadCapability,
} from '../copilot/failure-diagnosis/failureDiagnosisReadRoutes';
export const PRODUCTION_CLUSTER_CONTROL_ROUTE_OPERATIONS = Object.freeze([
'task.get',
@@ -92,8 +98,14 @@ export const PRODUCTION_CLUSTER_CONTROL_OPTIONAL_ROUTE_OPERATIONS =
'prompt.execution.output.read',
'prompt.output.read',
'copilot.failure_diagnosis.execute',
'copilot.failure_diagnosis.read',
'copilot.failure_diagnosis.output.read',
] as const);
export interface ClusterCopilotFailureDiagnosisReadCapability
extends ClusterCopilotFailureDiagnosisInspectionCapability,
ClusterCopilotFailureDiagnosisOutputReadCapability {}
export interface ProductionClusterControlAssemblyOptions {
readonly createEventId?: ClusterRunCancellationEventIdFactory;
readonly promptCatalog?: Readonly<{
@@ -116,6 +128,7 @@ export interface ProductionClusterControlAssemblyOptions {
}>;
readonly copilotFailureDiagnosis?: Readonly<{
readonly capability: ClusterCopilotFailureDiagnosisCapability;
readonly readCapability?: ClusterCopilotFailureDiagnosisReadCapability;
}>;
readonly workerIngress?: Readonly<{
readonly config: EnabledClusterWorkerIngressConfig;
@@ -164,6 +177,7 @@ export interface ProductionClusterControlApplicationOptions
}>;
readonly copilotFailureDiagnosis?: Readonly<{
readonly capability: ClusterCopilotFailureDiagnosisCapability;
readonly readCapability?: ClusterCopilotFailureDiagnosisReadCapability;
}>;
readonly workerIngress?: ProductionClusterWorkerIngressOptions;
}
@@ -279,6 +293,16 @@ export function createProductionClusterControlApplicationStack(
options.copilotFailureDiagnosis.capability,
),
]),
...(options.copilotFailureDiagnosis?.readCapability === undefined
? []
: [
createClusterControlCopilotFailureDiagnosisInspectionRoute(
options.copilotFailureDiagnosis.readCapability,
),
createClusterControlCopilotFailureDiagnosisOutputReadRoute(
options.copilotFailureDiagnosis.readCapability,
),
]),
];
const routes = createClusterControlRouteRegistry(routeDefinitions);
const expectedRouteCount =
@@ -288,7 +312,8 @@ export function createProductionClusterControlApplicationStack(
(options.promptExecutionInspection === undefined ? 0 : 1) +
(options.promptOutputRead === undefined ? 0 : 1) +
(options.promptExecutionOutputRead === undefined ? 0 : 1) +
(options.copilotFailureDiagnosis === undefined ? 0 : 1);
(options.copilotFailureDiagnosis === undefined ? 0 : 1) +
(options.copilotFailureDiagnosis?.readCapability === undefined ? 0 : 2);
if (routes.size !== expectedRouteCount) {
throw new Error('Production cluster-control route allowlist is incomplete');
}
@@ -0,0 +1,436 @@
// Cluster Copilot exposes separate low-sensitive status and protected output reads.
import type { SecurityPrincipal } from '@qinglong/runtime-core/security';
import type { ClusterControlAdmissionResponse } from '../../transport/httpSurface';
import type {
ClusterControlAuthorizedOperationRequest,
ClusterControlRouteDefinition,
ClusterControlRouteParameters,
} from '../../transport/routeRegistry';
export const CLUSTER_COPILOT_FAILURE_DIAGNOSIS_INSPECTION_RESPONSE_SCHEMA =
'qinglong/cluster-copilot-failure-diagnosis-inspection-response@v1' as const;
export const CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_READ_RESPONSE_SCHEMA =
'qinglong/cluster-copilot-failure-diagnosis-output-read-response@v1' as const;
export const CLUSTER_CONTROL_COPILOT_FAILURE_DIAGNOSIS_INSPECTION_ROUTE =
Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}/copilot/failure-diagnoses/{requestId}',
operationId: 'copilot.failure_diagnosis.read',
permission: 'run.read',
projectParameter: 'projectId',
});
export const CLUSTER_CONTROL_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_READ_ROUTE =
Object.freeze({
method: 'GET' as const,
path: '/api/v3/projects/{projectId}/runs/{runId}/copilot/failure-diagnoses/{requestId}/output',
operationId: 'copilot.failure_diagnosis.output.read',
permission: 'artifact.read',
projectParameter: 'projectId',
});
interface ClusterCopilotFailureDiagnosisReadCommand {
readonly principal: Readonly<SecurityPrincipal>;
readonly projectId: string;
readonly sourceRunId: string;
readonly requestId: string;
}
export interface ClusterCopilotFailureDiagnosisInspectionCapability {
inspect(
command: Readonly<ClusterCopilotFailureDiagnosisReadCommand>,
): Promise<unknown>;
}
export interface ClusterCopilotFailureDiagnosisOutputReadCapability {
readOutput(
command: Readonly<ClusterCopilotFailureDiagnosisReadCommand>,
): Promise<unknown>;
}
const IDENTITY = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$/;
const RUN_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,35}$/;
const DIGEST = /^[0-9a-f]{64}$/;
const FINISH_REASONS = new Set([
'stop',
'length',
'content_filter',
'tool_call',
'unknown',
]);
const OUTCOMES = new Set(['succeeded', 'failed', 'timed_out', 'cancelled']);
const STAGES = new Set(['model', 'tool', 'log', 'deadline', 'cancellation']);
const REASONS = new Set([
'tool_failed',
'tool_timed_out',
'log_not_found',
'log_pending',
'log_missing',
'log_retired',
'tool_budget_exhausted',
'deadline_exceeded',
'cancellation_requested',
]);
function response(
statusCode: number,
body: Readonly<Record<string, unknown>>,
): ClusterControlAdmissionResponse {
return Object.freeze({ statusCode, body: Object.freeze(body) });
}
function exactRecord(
value: unknown,
required: readonly string[],
optional: readonly string[] = [],
): Record<string, unknown> | null {
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
const record = value as Record<string, unknown>;
const actual = Object.keys(record).sort();
const expected = [
...required,
...optional.filter((key) => key in record),
].sort();
return actual.length === expected.length &&
actual.every((key, index) => key === expected[index])
? record
: null;
}
function nonNegativeInteger(value: unknown): value is number {
return Number.isSafeInteger(value) && (value as number) >= 0;
}
function target(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
): Readonly<ClusterCopilotFailureDiagnosisReadCommand> | null {
if (
authorized.request.body !== null ||
authorized.projectId === null ||
typeof parameters.runId !== 'string' ||
!RUN_ID.test(parameters.runId) ||
typeof parameters.requestId !== 'string' ||
!IDENTITY.test(parameters.requestId)
) {
return null;
}
return Object.freeze({
principal: authorized.principal,
projectId: authorized.projectId,
sourceRunId: parameters.runId,
requestId: parameters.requestId,
});
}
function exactTarget(
value: Record<string, unknown>,
expected: Readonly<ClusterCopilotFailureDiagnosisReadCommand>,
): boolean {
return (
value.projectId === expected.projectId &&
value.sourceRunId === expected.sourceRunId &&
value.requestId === expected.requestId
);
}
function notFound(
value: unknown,
expected: Readonly<ClusterCopilotFailureDiagnosisReadCommand>,
schema: string,
): boolean {
const candidate = exactRecord(value, [
'projectId',
'requestId',
'schema',
'sourceRunId',
'status',
]);
return (
!!candidate &&
candidate.schema === schema &&
candidate.status === 'not_found' &&
exactTarget(candidate, expected)
);
}
function usageView(value: unknown): Readonly<Record<string, unknown>> | null {
const usage = exactRecord(value, [
'costMicros',
'currency',
'inputTokens',
'outputTokens',
'totalTokens',
]);
if (
!usage ||
!nonNegativeInteger(usage.inputTokens) ||
!nonNegativeInteger(usage.outputTokens) ||
!nonNegativeInteger(usage.totalTokens) ||
usage.totalTokens !== usage.inputTokens + usage.outputTokens ||
!(
(usage.currency === null && usage.costMicros === null) ||
(usage.currency === 'USD' && nonNegativeInteger(usage.costMicros))
)
) {
return null;
}
return Object.freeze({ ...usage });
}
function inspectionView(
value: unknown,
expected: Readonly<ClusterCopilotFailureDiagnosisReadCommand>,
): Readonly<Record<string, unknown>> | null {
const candidate = exactRecord(value, [
'admittedAtMs',
'diagnosisRunId',
'finalizedAtMs',
'outcome',
'outputAvailable',
'projectId',
'reason',
'requestId',
'schema',
'sourceRunId',
'stage',
'status',
'usage',
]);
if (
!candidate ||
candidate.schema !==
'qinglong/copilot-failure-diagnosis-inspection-result@v1' ||
!exactTarget(candidate, expected) ||
typeof candidate.diagnosisRunId !== 'string' ||
!RUN_ID.test(candidate.diagnosisRunId) ||
!nonNegativeInteger(candidate.admittedAtMs) ||
typeof candidate.outputAvailable !== 'boolean'
) {
return null;
}
if (candidate.status === 'running') {
if (
candidate.outcome !== null ||
candidate.stage !== null ||
candidate.reason !== null ||
candidate.outputAvailable !== false ||
candidate.finalizedAtMs !== null ||
candidate.usage !== null
) {
return null;
}
} else if (candidate.status === 'terminal') {
if (
typeof candidate.outcome !== 'string' ||
!OUTCOMES.has(candidate.outcome) ||
typeof candidate.stage !== 'string' ||
!STAGES.has(candidate.stage) ||
!nonNegativeInteger(candidate.finalizedAtMs) ||
candidate.finalizedAtMs < candidate.admittedAtMs ||
(candidate.stage === 'model') !== (candidate.reason === null) ||
(candidate.reason !== null &&
(typeof candidate.reason !== 'string' ||
!REASONS.has(candidate.reason))) ||
candidate.outputAvailable !==
(candidate.stage === 'model' && candidate.outcome === 'succeeded')
) {
return null;
}
if (candidate.usage !== null && !usageView(candidate.usage)) return null;
} else {
return null;
}
return Object.freeze({
schema: CLUSTER_COPILOT_FAILURE_DIAGNOSIS_INSPECTION_RESPONSE_SCHEMA,
status: candidate.status,
projectId: expected.projectId,
sourceRunId: expected.sourceRunId,
requestId: expected.requestId,
diagnosisRunId: candidate.diagnosisRunId,
outcome: candidate.outcome,
stage: candidate.stage,
reason: candidate.reason,
outputAvailable: candidate.outputAvailable,
admittedAtMs: candidate.admittedAtMs,
finalizedAtMs: candidate.finalizedAtMs,
usage: candidate.usage === null ? null : usageView(candidate.usage),
});
}
function outputView(
value: unknown,
expected: Readonly<ClusterCopilotFailureDiagnosisReadCommand>,
): Readonly<Record<string, unknown>> | null {
const candidate = exactRecord(value, [
'diagnosisRunId',
'projectId',
'reference',
'requestId',
'result',
'schema',
'sourceRunId',
'status',
]);
const reference = candidate
? exactRecord(candidate.reference, [
'artifactDigest',
'artifactId',
'contentDigest',
'outputBytes',
'sealedAtMs',
])
: null;
const result = candidate
? exactRecord(candidate.result, ['finishReason', 'text', 'usage'])
: null;
const usage = result
? exactRecord(
result.usage,
['inputTokens', 'outputTokens', 'totalTokens'],
['costMicros'],
)
: null;
if (
!candidate ||
!reference ||
!result ||
!usage ||
candidate.schema !==
'qinglong/copilot-failure-diagnosis-output-read-result@v1' ||
candidate.status !== 'available' ||
!exactTarget(candidate, expected) ||
typeof candidate.diagnosisRunId !== 'string' ||
!RUN_ID.test(candidate.diagnosisRunId) ||
typeof reference.artifactId !== 'string' ||
!IDENTITY.test(reference.artifactId) ||
typeof reference.artifactDigest !== 'string' ||
!DIGEST.test(reference.artifactDigest) ||
typeof reference.contentDigest !== 'string' ||
!DIGEST.test(reference.contentDigest) ||
!nonNegativeInteger(reference.outputBytes) ||
reference.outputBytes > 1024 * 1024 ||
!nonNegativeInteger(reference.sealedAtMs) ||
typeof result.text !== 'string' ||
Buffer.byteLength(result.text, 'utf8') !== reference.outputBytes ||
typeof result.finishReason !== 'string' ||
!FINISH_REASONS.has(result.finishReason) ||
!nonNegativeInteger(usage.inputTokens) ||
!nonNegativeInteger(usage.outputTokens) ||
!nonNegativeInteger(usage.totalTokens) ||
usage.totalTokens !== usage.inputTokens + usage.outputTokens ||
(usage.costMicros !== undefined && !nonNegativeInteger(usage.costMicros))
) {
return null;
}
return Object.freeze({
schema: CLUSTER_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_READ_RESPONSE_SCHEMA,
status: 'available',
projectId: expected.projectId,
sourceRunId: expected.sourceRunId,
requestId: expected.requestId,
diagnosisRunId: candidate.diagnosisRunId,
reference: Object.freeze({ ...reference }),
result: Object.freeze({
text: result.text,
finishReason: result.finishReason,
usage: Object.freeze({ ...usage }),
}),
});
}
export function createClusterControlCopilotFailureDiagnosisInspectionRoute(
capability: ClusterCopilotFailureDiagnosisInspectionCapability,
): Readonly<ClusterControlRouteDefinition> {
if (!capability || typeof capability.inspect !== 'function') {
throw new TypeError(
'Cluster-control Copilot diagnosis inspection capability is invalid',
);
}
return Object.freeze({
...CLUSTER_CONTROL_COPILOT_FAILURE_DIAGNOSIS_INSPECTION_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
const command = target(authorized, parameters);
if (!command) {
return response(400, {
code: 'invalid_copilot_failure_diagnosis_read_request',
});
}
try {
const result = await capability.inspect(command);
if (
notFound(
result,
command,
'qinglong/copilot-failure-diagnosis-inspection-result@v1',
)
) {
return response(404, { code: 'copilot_failure_diagnosis_not_found' });
}
const view = inspectionView(result, command);
return view
? response(200, view)
: response(503, {
code: 'copilot_failure_diagnosis_read_unavailable',
});
} catch {
return response(503, {
code: 'copilot_failure_diagnosis_read_unavailable',
});
}
},
});
}
export function createClusterControlCopilotFailureDiagnosisOutputReadRoute(
capability: ClusterCopilotFailureDiagnosisOutputReadCapability,
): Readonly<ClusterControlRouteDefinition> {
if (!capability || typeof capability.readOutput !== 'function') {
throw new TypeError(
'Cluster-control Copilot diagnosis output read capability is invalid',
);
}
return Object.freeze({
...CLUSTER_CONTROL_COPILOT_FAILURE_DIAGNOSIS_OUTPUT_READ_ROUTE,
async handle(
authorized: ClusterControlAuthorizedOperationRequest,
parameters: ClusterControlRouteParameters,
) {
const command = target(authorized, parameters);
if (!command) {
return response(400, {
code: 'invalid_copilot_failure_diagnosis_output_read_request',
});
}
try {
const result = await capability.readOutput(command);
if (
notFound(
result,
command,
'qinglong/copilot-failure-diagnosis-output-read-result@v1',
)
) {
return response(404, {
code: 'copilot_failure_diagnosis_output_not_found',
});
}
const view = outputView(result, command);
return view
? response(200, view)
: response(503, {
code: 'copilot_failure_diagnosis_output_read_unavailable',
});
} catch {
return response(503, {
code: 'copilot_failure_diagnosis_output_read_unavailable',
});
}
},
});
}