mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 02:27:44 +08:00
feat(ql3): expose copilot diagnosis read model
This commit is contained in:
@@ -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,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
+57
@@ -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');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user