feat: API-Runtime 版本兼容协议 (API-AI-072)
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 46s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 46s
- pollJobs: 记录/更新 RuntimeInstance (capabilities + heartbeat) - submitResult: 校验 schemaVersion 匹配 job.outputSchemaVersion - heartbeat: 首次调用设置 startedAt - 错误码: RESULT_SCHEMA_UNSUPPORTED + RUNTIME_VERSION_INCOMPATIBLE Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
00ac32a103
commit
012e26b950
@ -35,6 +35,24 @@ export class RuntimeInternalService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register / update RuntimeInstance with capabilities
|
||||||
|
if (supSnapshot.length > 0 || supOutput.length > 0) {
|
||||||
|
await this.prisma.runtimeInstance.upsert({
|
||||||
|
where: { runtimeInstanceId },
|
||||||
|
create: {
|
||||||
|
runtimeInstanceId,
|
||||||
|
status: 'active',
|
||||||
|
lastHeartbeatAt: new Date(),
|
||||||
|
capabilities: capabilities as any,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
status: 'active',
|
||||||
|
lastHeartbeatAt: new Date(),
|
||||||
|
capabilities: capabilities as any,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Post-filter by snapshotVersion if needed (requires snapshot join)
|
// Post-filter by snapshotVersion if needed (requires snapshot join)
|
||||||
if (supSnapshot.length > 0) {
|
if (supSnapshot.length > 0) {
|
||||||
const snapshotIds = [...new Set(jobs.map(j => j.snapshotId).filter(Boolean))];
|
const snapshotIds = [...new Set(jobs.map(j => j.snapshotId).filter(Boolean))];
|
||||||
@ -92,14 +110,14 @@ export class RuntimeInternalService {
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
const lockUntil = new Date(now.getTime() + 60_000);
|
const lockUntil = new Date(now.getTime() + 60_000);
|
||||||
|
|
||||||
// First heartbeat: locked → running; subsequent heartbeats: extend lockUntil
|
// First heartbeat: locked → running + set startedAt; subsequent: extend lockUntil
|
||||||
const result = await this.prisma.aiRuntimeJob.updateMany({
|
const result = await this.prisma.aiRuntimeJob.updateMany({
|
||||||
where: {
|
where: {
|
||||||
id: jobId,
|
id: jobId,
|
||||||
lockedBy: runtimeInstanceId,
|
lockedBy: runtimeInstanceId,
|
||||||
status: { in: ['locked', 'running'] },
|
status: { in: ['locked', 'running'] },
|
||||||
},
|
},
|
||||||
data: { lockUntil, status: 'running' },
|
data: { lockUntil, status: 'running', startedAt: new Date() },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.count === 0) {
|
if (result.count === 0) {
|
||||||
@ -176,6 +194,14 @@ export class RuntimeInternalService {
|
|||||||
throw new ConflictException({ errorCode: 'JOB_NOT_ACTIVE', message: `Job is ${job.status}, cannot accept result` });
|
throw new ConflictException({ errorCode: 'JOB_NOT_ACTIVE', message: `Job is ${job.status}, cannot accept result` });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate schema version compatibility
|
||||||
|
if (job.outputSchemaVersion && dto.schemaVersion !== job.outputSchemaVersion) {
|
||||||
|
throw new BadRequestException({
|
||||||
|
errorCode: 'RESULT_SCHEMA_UNSUPPORTED',
|
||||||
|
message: `Result schemaVersion ${dto.schemaVersion} does not match job outputSchemaVersion ${job.outputSchemaVersion}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const resultIdempotencyKey = `${jobId}:${dto.attemptNo}:${dto.outputHash ?? ''}`;
|
const resultIdempotencyKey = `${jobId}:${dto.attemptNo}:${dto.outputHash ?? ''}`;
|
||||||
|
|
||||||
// Check duplicate
|
// Check duplicate
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user