mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 19:29:13 +08:00
feat(ql3): execute copilot diagnosis models
This commit is contained in:
@@ -33,16 +33,9 @@ import {
|
||||
type ModelInvocationPriceQuote,
|
||||
} from '../pricing/pricing';
|
||||
import {
|
||||
isPluginPackagePromptOutputCompletionRepository,
|
||||
type PluginPackagePromptOutputCompletionRepository,
|
||||
} from '../prompt-output/pluginPackagePromptOutputCompletion';
|
||||
import {
|
||||
PluginPackagePromptOutputArtifactConflictError,
|
||||
normalizePluginPackagePromptOutputArtifact,
|
||||
pluginPackagePromptOutputArtifactReference,
|
||||
type PluginPackagePromptOutputArtifact,
|
||||
type PluginPackagePromptOutputArtifactReference,
|
||||
} from '../prompt-output/pluginPackagePromptOutputArtifact';
|
||||
normalizeModelInvocationAtomicSuccess,
|
||||
type ModelInvocationAtomicSuccess,
|
||||
} from './modelInvocationAtomicSuccess';
|
||||
|
||||
const MAX_COORDINATOR_ATTEMPTS = 3;
|
||||
|
||||
@@ -242,32 +235,21 @@ export class DurableModelInvocationCoordinator
|
||||
return this.#admit(record, admission, quote);
|
||||
}
|
||||
|
||||
async recordWithPromptOutputArtifact(
|
||||
async recordWithAtomicSuccess<TReference>(
|
||||
record: Readonly<ModelInvocationAuditRecord>,
|
||||
artifactValue: PluginPackagePromptOutputArtifact,
|
||||
extensionValue: ModelInvocationAtomicSuccess<TReference>,
|
||||
): Promise<
|
||||
Readonly<{
|
||||
status: 'created' | 'existing';
|
||||
reference: Readonly<PluginPackagePromptOutputArtifactReference>;
|
||||
reference: Readonly<TReference>;
|
||||
}>
|
||||
> {
|
||||
const artifact = normalizePluginPackagePromptOutputArtifact(artifactValue);
|
||||
if (
|
||||
record.phase !== 'completed' ||
|
||||
record.requestId !== artifact.invocationId ||
|
||||
record.projectId !== artifact.projectId ||
|
||||
record.runId !== artifact.runId ||
|
||||
record.stepRunId !== artifact.stepRunId ||
|
||||
record.provider !== artifact.provider ||
|
||||
record.model !== artifact.model ||
|
||||
record.outputBytes !== artifact.outputBytes ||
|
||||
!isPluginPackagePromptOutputCompletionRepository(this.repository)
|
||||
) {
|
||||
throw new PluginPackagePromptOutputArtifactConflictError();
|
||||
}
|
||||
const result = await this.#complete(record, artifact);
|
||||
const extension = normalizeModelInvocationAtomicSuccess(extensionValue);
|
||||
if (record.phase !== 'completed') throw extension.conflict();
|
||||
extension.assertAudit(record);
|
||||
const result = await this.#complete(record, extension);
|
||||
if (!result.reference) {
|
||||
throw new PluginPackagePromptOutputArtifactConflictError();
|
||||
throw extension.conflict();
|
||||
}
|
||||
return Object.freeze({
|
||||
status: result.status,
|
||||
@@ -383,22 +365,18 @@ export class DurableModelInvocationCoordinator
|
||||
throw new ModelInvocationConflictError();
|
||||
}
|
||||
|
||||
async #complete(
|
||||
async #complete<TReference = never>(
|
||||
audit: Readonly<ModelInvocationAuditRecord>,
|
||||
artifactValue?: Readonly<PluginPackagePromptOutputArtifact>,
|
||||
extensionValue?: ModelInvocationAtomicSuccess<TReference>,
|
||||
): Promise<
|
||||
Readonly<
|
||||
ModelInvocationAuditDisposition & {
|
||||
reference?: Readonly<PluginPackagePromptOutputArtifactReference>;
|
||||
reference?: Readonly<TReference>;
|
||||
}
|
||||
>
|
||||
> {
|
||||
const artifact = artifactValue
|
||||
? normalizePluginPackagePromptOutputArtifact(artifactValue)
|
||||
: undefined;
|
||||
const artifactRepository = artifact
|
||||
? (this.repository as ModelInvocationRepository &
|
||||
PluginPackagePromptOutputCompletionRepository)
|
||||
const extension = extensionValue
|
||||
? normalizeModelInvocationAtomicSuccess(extensionValue)
|
||||
: undefined;
|
||||
const startValue = await this.repository.findStart(audit.requestId);
|
||||
if (!startValue) throw new ModelInvocationConflictError();
|
||||
@@ -406,16 +384,14 @@ export class DurableModelInvocationCoordinator
|
||||
const existing = await this.repository.findCompletion(audit.requestId);
|
||||
if (existing) {
|
||||
assertCompletionMatchesAudit(existing, start, audit);
|
||||
if (artifact && artifactRepository) {
|
||||
const stored = await artifactRepository.findPromptOutputArtifact(
|
||||
artifact.artifactId,
|
||||
);
|
||||
if (!stored || JSON.stringify(stored) !== JSON.stringify(artifact)) {
|
||||
throw new PluginPackagePromptOutputArtifactConflictError();
|
||||
if (extension) {
|
||||
const stored = await extension.find(this.repository);
|
||||
if (!stored || !extension.matches(stored)) {
|
||||
throw extension.conflict();
|
||||
}
|
||||
return Object.freeze({
|
||||
status: 'existing' as const,
|
||||
reference: pluginPackagePromptOutputArtifactReference(stored),
|
||||
reference: stored,
|
||||
});
|
||||
}
|
||||
return Object.freeze({ status: 'existing' as const });
|
||||
@@ -430,7 +406,7 @@ export class DurableModelInvocationCoordinator
|
||||
) {
|
||||
throw new ModelInvocationConflictError();
|
||||
}
|
||||
const transition = completionTransition(audit, artifact?.artifactId);
|
||||
const transition = completionTransition(audit, extension?.outputRef);
|
||||
const mutationIdentity = createModelInvocationMutationIdentity(
|
||||
audit.requestId,
|
||||
'completion',
|
||||
@@ -464,7 +440,7 @@ export class DurableModelInvocationCoordinator
|
||||
actor: { type: 'executor', id: 'model-gateway' },
|
||||
},
|
||||
),
|
||||
artifact?.artifactId,
|
||||
extension?.outputRef,
|
||||
);
|
||||
try {
|
||||
const pricingAware = isPricingAwareModelInvocationRepository(
|
||||
@@ -479,42 +455,32 @@ export class DurableModelInvocationCoordinator
|
||||
const reservation = quotaAware
|
||||
? await this.repository.findQuotaReservation(audit.requestId)
|
||||
: null;
|
||||
const result =
|
||||
artifactRepository && artifact
|
||||
? await artifactRepository.completeWithPromptOutputArtifact(
|
||||
command,
|
||||
artifact,
|
||||
)
|
||||
: pricingAware && quote
|
||||
if (extension) {
|
||||
const result = await extension.commit(this.repository, command);
|
||||
return Object.freeze({
|
||||
status: result.status,
|
||||
reference: result.reference,
|
||||
});
|
||||
}
|
||||
const result = pricingAware && quote
|
||||
? await this.repository.completeWithPricing(command)
|
||||
: quotaAware && reservation
|
||||
? await this.repository.completeWithQuota(command)
|
||||
: await this.repository.complete(command);
|
||||
return Object.freeze({
|
||||
status: result.status,
|
||||
...(artifact && artifactRepository
|
||||
? {
|
||||
reference: pluginPackagePromptOutputArtifactReference(artifact),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const stored = await this.#completionAfterFailure(
|
||||
start,
|
||||
audit,
|
||||
error,
|
||||
artifact,
|
||||
artifactRepository,
|
||||
extension,
|
||||
);
|
||||
if (stored) {
|
||||
return Object.freeze({
|
||||
status: 'existing' as const,
|
||||
...(artifact
|
||||
? {
|
||||
reference:
|
||||
pluginPackagePromptOutputArtifactReference(artifact),
|
||||
}
|
||||
: {}),
|
||||
...(extension ? { reference: stored.reference } : {}),
|
||||
});
|
||||
}
|
||||
if (
|
||||
@@ -569,31 +535,29 @@ export class DurableModelInvocationCoordinator
|
||||
}
|
||||
}
|
||||
|
||||
async #completionAfterFailure(
|
||||
async #completionAfterFailure<TReference = never>(
|
||||
start: Readonly<ModelInvocationStartRecord>,
|
||||
audit: Readonly<ModelInvocationAuditRecord>,
|
||||
original: unknown,
|
||||
artifact?: Readonly<PluginPackagePromptOutputArtifact>,
|
||||
artifactRepository?: PluginPackagePromptOutputCompletionRepository,
|
||||
): Promise<Readonly<ModelInvocationCompletionRecord> | null> {
|
||||
extension?: ModelInvocationAtomicSuccess<TReference>,
|
||||
): Promise<
|
||||
Readonly<{
|
||||
completion: Readonly<ModelInvocationCompletionRecord>;
|
||||
reference?: Readonly<TReference>;
|
||||
}> | null
|
||||
> {
|
||||
try {
|
||||
const stored = await this.repository.findCompletion(audit.requestId);
|
||||
if (!stored) return null;
|
||||
const completion = assertCompletionMatchesAudit(stored, start, audit);
|
||||
if (artifact) {
|
||||
if (!artifactRepository) throw original;
|
||||
const storedArtifact =
|
||||
await artifactRepository.findPromptOutputArtifact(
|
||||
artifact.artifactId,
|
||||
);
|
||||
if (
|
||||
!storedArtifact ||
|
||||
JSON.stringify(storedArtifact) !== JSON.stringify(artifact)
|
||||
) {
|
||||
if (extension) {
|
||||
const reference = await extension.find(this.repository);
|
||||
if (!reference || !extension.matches(reference)) {
|
||||
throw original;
|
||||
}
|
||||
return Object.freeze({ completion, reference });
|
||||
}
|
||||
return completion;
|
||||
return Object.freeze({ completion });
|
||||
} catch {
|
||||
throw original;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import type {
|
||||
ModelInvocationAuditRecord,
|
||||
ModelInvocationAuditDisposition,
|
||||
} from '../model-gateway/model';
|
||||
import type {
|
||||
ModelInvocationCompletionCommand,
|
||||
ModelInvocationRepository,
|
||||
} from './modelInvocation';
|
||||
|
||||
export interface ModelInvocationAtomicSuccessCommit<TReference> {
|
||||
readonly status: ModelInvocationAuditDisposition['status'];
|
||||
readonly reference: Readonly<TReference>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain-owned extension for one encrypted successful Model output.
|
||||
*
|
||||
* The generic Model coordinator owns the StepRun/usage/pricing protocol while
|
||||
* this extension owns the output identity, exact replay check and dialect
|
||||
* transaction that persists the encrypted output beside that protocol.
|
||||
*/
|
||||
export interface ModelInvocationAtomicSuccess<TReference> {
|
||||
readonly outputRef: string;
|
||||
assertAudit(record: Readonly<ModelInvocationAuditRecord>): void;
|
||||
find(
|
||||
repository: ModelInvocationRepository,
|
||||
): Promise<Readonly<TReference> | null>;
|
||||
matches(reference: Readonly<TReference>): boolean;
|
||||
commit(
|
||||
repository: ModelInvocationRepository,
|
||||
command: Readonly<ModelInvocationCompletionCommand>,
|
||||
): Promise<Readonly<ModelInvocationAtomicSuccessCommit<TReference>>>;
|
||||
conflict(): Error;
|
||||
}
|
||||
|
||||
export function normalizeModelInvocationAtomicSuccess<TReference>(
|
||||
value: ModelInvocationAtomicSuccess<TReference>,
|
||||
): ModelInvocationAtomicSuccess<TReference> {
|
||||
if (
|
||||
!value ||
|
||||
typeof value !== 'object' ||
|
||||
Array.isArray(value) ||
|
||||
typeof value.outputRef !== 'string' ||
|
||||
value.outputRef.length < 1 ||
|
||||
value.outputRef.length > 512 ||
|
||||
typeof value.assertAudit !== 'function' ||
|
||||
typeof value.find !== 'function' ||
|
||||
typeof value.matches !== 'function' ||
|
||||
typeof value.commit !== 'function' ||
|
||||
typeof value.conflict !== 'function'
|
||||
) {
|
||||
throw new TypeError('Model invocation atomic success is invalid');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
+60
-19
@@ -1,4 +1,4 @@
|
||||
import type { PostgresPool } from '@qinglong/runtime-core';
|
||||
import type { PostgresClient, PostgresPool } from '@qinglong/runtime-core';
|
||||
|
||||
import { createModelInvocationPriceSettlement } from '../../pricing/pricing';
|
||||
import { type PluginPackagePromptOutputArtifact } from '../../prompt-output/pluginPackagePromptOutputArtifact';
|
||||
@@ -315,17 +315,36 @@ export async function completeWithPricingOperation(
|
||||
});
|
||||
}
|
||||
|
||||
export async function completeWithPromptOutputArtifactOperation(
|
||||
export interface PostgresModelInvocationAtomicOutputBinding<
|
||||
TArtifact,
|
||||
TReference,
|
||||
> {
|
||||
readonly artifact: Readonly<TArtifact>;
|
||||
readonly reference: Readonly<TReference>;
|
||||
read(client: PostgresClient): Promise<Readonly<TArtifact> | null>;
|
||||
put(client: PostgresClient): Promise<Readonly<TArtifact>>;
|
||||
matches(stored: Readonly<TArtifact>): boolean;
|
||||
}
|
||||
|
||||
export interface CommitPostgresModelInvocationAtomicOutputResult<
|
||||
TArtifact,
|
||||
TReference,
|
||||
> extends CommitModelInvocationResult<ModelInvocationCompletionRecord> {
|
||||
readonly artifact: Readonly<TArtifact>;
|
||||
readonly reference: Readonly<TReference>;
|
||||
}
|
||||
|
||||
export async function completeWithAtomicOutputOperation<TArtifact, TReference>(
|
||||
pool: PostgresPool,
|
||||
commandValue: ModelInvocationCompletionCommand,
|
||||
artifactValue: PluginPackagePromptOutputArtifact,
|
||||
): Promise<Readonly<CommitPluginPackagePromptOutputResult>> {
|
||||
binding: PostgresModelInvocationAtomicOutputBinding<TArtifact, TReference>,
|
||||
): Promise<
|
||||
Readonly<
|
||||
CommitPostgresModelInvocationAtomicOutputResult<TArtifact, TReference>
|
||||
>
|
||||
> {
|
||||
const command = normalizeModelInvocationCompletionCommand(commandValue);
|
||||
const completion = command.completion;
|
||||
const binding = assertPluginPackagePromptOutputCompletionBinding(
|
||||
command,
|
||||
artifactValue,
|
||||
);
|
||||
const expectedUsage = createModelInvocationUsageLedgerRecord(
|
||||
command.start,
|
||||
completion,
|
||||
@@ -377,10 +396,7 @@ export async function completeWithPromptOutputArtifactOperation(
|
||||
if (existing[0]) {
|
||||
const [storedArtifact, usage, priceSettlements, quotaSettlements] =
|
||||
await Promise.all([
|
||||
readPostgresPluginPackagePromptOutputArtifactInTransaction(
|
||||
client,
|
||||
binding.artifact.artifactId,
|
||||
),
|
||||
binding.read(client),
|
||||
usageRows(client, 'usage.invocation_id = $1', [
|
||||
completion.invocationId,
|
||||
]),
|
||||
@@ -395,7 +411,7 @@ export async function completeWithPromptOutputArtifactOperation(
|
||||
if (
|
||||
JSON.stringify(stored) !== JSON.stringify(completion) ||
|
||||
!storedArtifact ||
|
||||
JSON.stringify(storedArtifact) !== JSON.stringify(binding.artifact) ||
|
||||
!binding.matches(storedArtifact) ||
|
||||
usage.length !== (expectedUsage ? 1 : 0) ||
|
||||
(expectedUsage &&
|
||||
JSON.stringify(parseUsage(usage[0]!)) !==
|
||||
@@ -434,12 +450,6 @@ export async function completeWithPromptOutputArtifactOperation(
|
||||
throw new ModelInvocationConflictError();
|
||||
}
|
||||
await assertCurrent(client, command.stepRunMutation, completion.projectId);
|
||||
const artifact = (
|
||||
await putPostgresPluginPackagePromptOutputArtifactInTransaction(
|
||||
client,
|
||||
binding.artifact,
|
||||
)
|
||||
).artifact;
|
||||
await applyMutation(client, command.stepRunMutation);
|
||||
await insertCompletion(client, completion);
|
||||
if (expectedUsage) await insertUsage(client, expectedUsage);
|
||||
@@ -449,6 +459,7 @@ export async function completeWithPromptOutputArtifactOperation(
|
||||
if (expectedQuotaSettlement) {
|
||||
await insertQuotaSettlement(client, expectedQuotaSettlement);
|
||||
}
|
||||
const artifact = await binding.put(client);
|
||||
return Object.freeze({
|
||||
status: 'created' as const,
|
||||
record: completion,
|
||||
@@ -457,3 +468,33 @@ export async function completeWithPromptOutputArtifactOperation(
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function completeWithPromptOutputArtifactOperation(
|
||||
pool: PostgresPool,
|
||||
commandValue: ModelInvocationCompletionCommand,
|
||||
artifactValue: PluginPackagePromptOutputArtifact,
|
||||
): Promise<Readonly<CommitPluginPackagePromptOutputResult>> {
|
||||
const command = normalizeModelInvocationCompletionCommand(commandValue);
|
||||
const binding = assertPluginPackagePromptOutputCompletionBinding(
|
||||
command,
|
||||
artifactValue,
|
||||
);
|
||||
return completeWithAtomicOutputOperation(pool, command, {
|
||||
artifact: binding.artifact,
|
||||
reference: binding.reference,
|
||||
read: (client) =>
|
||||
readPostgresPluginPackagePromptOutputArtifactInTransaction(
|
||||
client,
|
||||
binding.artifact.artifactId,
|
||||
),
|
||||
put: async (client) =>
|
||||
(
|
||||
await putPostgresPluginPackagePromptOutputArtifactInTransaction(
|
||||
client,
|
||||
binding.artifact,
|
||||
)
|
||||
).artifact,
|
||||
matches: (stored) =>
|
||||
JSON.stringify(stored) === JSON.stringify(binding.artifact),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user