From a39b1c0de21abfe1102d65d26595a12c8c001838 Mon Sep 17 00:00:00 2001 From: portuu3 <> Date: Tue, 14 Jul 2026 11:46:40 +0200 Subject: [PATCH 1/4] Add a POST /job/unknown-manifest endpoint that accepts an arbitrary JSON manifest --- .../src/modules/job/job.controller.spec.ts | 48 ++++++- .../server/src/modules/job/job.controller.ts | 37 ++++++ .../server/src/modules/job/job.dto.ts | 7 ++ .../src/modules/job/job.service.spec.ts | 117 ++++++++++++++++++ .../server/src/modules/job/job.service.ts | 37 ++++++ 5 files changed, 245 insertions(+), 1 deletion(-) diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.spec.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.spec.ts index 5778fa133d..d294f939e7 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.spec.ts @@ -23,7 +23,11 @@ import { } from '../manifest/fixtures'; import { MutexManagerService } from '../mutex/mutex-manager.service'; import { JobController } from './job.controller'; -import { JobManifestDto, JobQuickLaunchDto } from './job.dto'; +import { + JobManifestDto, + JobQuickLaunchDto, + JobUnknownManifestDto, +} from './job.dto'; import { JobService } from './job.service'; describe('JobController', () => { @@ -31,6 +35,7 @@ describe('JobController', () => { const mockJobService = { createJob: jest.fn(), + createJobWithUnknownManifest: jest.fn(), }; const mockMutexManagerService = { @@ -316,4 +321,45 @@ describe('JobController', () => { expect(mockJobService.createJob).not.toHaveBeenCalled(); }); }); + + describe('createJobWithUnknownManifest', () => { + it('should create a job with an unknown manifest and return job ID', async () => { + const jobUnknownManifestDto: JobUnknownManifestDto = { + chainId: ChainId.POLYGON_AMOY, + requestType: FortuneJobType.FORTUNE, + manifest: { + custom: faker.string.sample(), + nested: { + value: faker.number.int(), + }, + }, + paymentCurrency: PaymentCurrency.USD, + paymentAmount: faker.number.int({ min: 100, max: 1000 }), + escrowFundToken: EscrowFundToken.HMT, + }; + + mockMutexManagerService.runExclusive.mockImplementationOnce( + async (_id, _timeout, fn) => { + return await fn(); + }, + ); + mockJobService.createJobWithUnknownManifest.mockResolvedValueOnce(1); + + const result = await jobController.createJobWithUnknownManifest( + jobUnknownManifestDto, + mockRequest, + ); + + expect(result).toBe(1); + expect(mockJobService.createJobWithUnknownManifest).toHaveBeenCalledWith( + mockRequest.user, + jobUnknownManifestDto, + ); + expect(mockMutexManagerService.runExclusive).toHaveBeenCalledWith( + `user${mockRequest.user.id}`, + MUTEX_TIMEOUT, + expect.any(Function), + ); + }); + }); }); diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index abb5dbdf0f..19f525ee97 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -33,6 +33,7 @@ import { JobListDto, JobManifestDto, JobQuickLaunchDto, + JobUnknownManifestDto, } from './job.dto'; import { JobService } from './job.service'; @@ -128,6 +129,42 @@ export class JobController { ); } + @ApiOperation({ + summary: 'Create a job with an unknown manifest', + description: + 'Endpoint to create a job using a manifest JSON body without validating its format.', + }) + @ApiBody({ type: JobUnknownManifestDto }) + @ApiResponse({ + status: 201, + description: 'ID of the created job.', + type: Number, + }) + @ApiResponse({ + status: 400, + description: 'Bad Request. Invalid input parameters.', + }) + @ApiResponse({ + status: 401, + description: 'Unauthorized. Missing or invalid credentials.', + }) + @Post('/unknown-manifest') + public async createJobWithUnknownManifest( + @Body() data: JobUnknownManifestDto, + @Request() req: RequestWithUser, + ): Promise { + return await this.mutexManagerService.runExclusive( + `user${req.user.id}`, + MUTEX_TIMEOUT, + async () => { + return await this.jobService.createJobWithUnknownManifest( + req.user, + data, + ); + }, + ); + } + @ApiOperation({ summary: 'Get a list of jobs', description: diff --git a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts index 2fd27bb044..3e193930fd 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.dto.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.dto.ts @@ -111,6 +111,13 @@ export class JobManifestDto extends JobDto { public manifest: ManifestDto; } +export class JobUnknownManifestDto extends JobDto { + @ApiProperty({ type: Object }) + @IsObject() + @IsNotEmpty() + public manifest: Record; +} + export class StorageDataDto { @ApiProperty({ enum: StorageProviders }) @IsEnumCaseInsensitive(StorageProviders) diff --git a/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts b/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts index b9bb5312a8..b347647a80 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts @@ -61,6 +61,7 @@ import { GetJobsDto, JobManifestDto, JobQuickLaunchDto, + JobUnknownManifestDto, } from './job.dto'; import { JobRepository } from './job.repository'; import { JobService } from './job.service'; @@ -137,6 +138,122 @@ describe('JobService', () => { afterEach(() => { jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + describe('createJobWithUnknownManifest', () => { + it('should upload an unknown manifest and create a job without format validation', async () => { + const userMock = createUser({ whitelist: new WhitelistEntity() }); + const dto: JobUnknownManifestDto = { + chainId: ChainId.POLYGON_AMOY, + requestType: FortuneJobType.FORTUNE, + manifest: { + arbitrary: faker.string.sample(), + schema: { + value: faker.number.int(), + }, + }, + reputationOracle: faker.finance.ethereumAddress(), + exchangeOracle: faker.finance.ethereumAddress(), + recordingOracle: faker.finance.ethereumAddress(), + paymentCurrency: PaymentCurrency.USD, + paymentAmount: faker.number.int({ min: 100, max: 1000 }), + escrowFundToken: EscrowFundToken.HMT, + }; + const uploadedFile = { + url: faker.internet.url(), + hash: faker.string.uuid(), + }; + const jobId = faker.number.int({ min: 1 }); + const createJobSpy = jest + .spyOn(jobService, 'createJob') + .mockResolvedValueOnce(jobId); + + mockManifestService.uploadManifest.mockResolvedValueOnce(uploadedFile); + + const result = await jobService.createJobWithUnknownManifest( + userMock, + dto, + ); + + expect(result).toBe(jobId); + expect(mockWeb3Service.validateChainId).toHaveBeenCalledWith(dto.chainId); + expect(mockManifestService.validateManifest).not.toHaveBeenCalled(); + expect(mockManifestService.uploadManifest).toHaveBeenCalledWith( + dto.chainId, + dto.manifest, + [dto.exchangeOracle, dto.reputationOracle, dto.recordingOracle], + ); + expect(createJobSpy).toHaveBeenCalledWith( + userMock, + dto.requestType, + expect.objectContaining({ + chainId: dto.chainId, + requestType: dto.requestType, + reputationOracle: dto.reputationOracle, + exchangeOracle: dto.exchangeOracle, + recordingOracle: dto.recordingOracle, + paymentCurrency: dto.paymentCurrency, + paymentAmount: dto.paymentAmount, + escrowFundToken: dto.escrowFundToken, + manifestUrl: uploadedFile.url, + manifestHash: uploadedFile.hash, + }), + ); + expect(createJobSpy.mock.calls[0][2]).not.toHaveProperty('manifest'); + }); + + it('should use default oracles when creating a job with an unknown manifest', async () => { + const userMock = createUser({ whitelist: new WhitelistEntity() }); + const dto: JobUnknownManifestDto = { + chainId: ChainId.POLYGON_AMOY, + requestType: HCaptchaJobType.HCAPTCHA, + manifest: { + custom: faker.string.sample(), + }, + paymentCurrency: PaymentCurrency.USD, + paymentAmount: faker.number.int({ min: 100, max: 1000 }), + escrowFundToken: EscrowFundToken.HMT, + }; + const uploadedFile = { + url: faker.internet.url(), + hash: faker.string.uuid(), + }; + const createJobSpy = jest + .spyOn(jobService, 'createJob') + .mockResolvedValueOnce(faker.number.int({ min: 1 })); + + mockManifestService.uploadManifest.mockResolvedValueOnce(uploadedFile); + + await jobService.createJobWithUnknownManifest(userMock, dto); + + expect(mockManifestService.uploadManifest).toHaveBeenCalledWith( + dto.chainId, + dto.manifest, + [ + mockWeb3ConfigService.hCaptchaOracleAddress, + mockWeb3ConfigService.hCaptchaOracleAddress, + mockWeb3ConfigService.hCaptchaOracleAddress, + ], + ); + expect(createJobSpy).toHaveBeenCalledWith( + userMock, + dto.requestType, + expect.objectContaining({ + chainId: dto.chainId, + requestType: dto.requestType, + reputationOracle: mockWeb3ConfigService.hCaptchaOracleAddress, + exchangeOracle: mockWeb3ConfigService.hCaptchaOracleAddress, + recordingOracle: mockWeb3ConfigService.hCaptchaOracleAddress, + paymentCurrency: dto.paymentCurrency, + paymentAmount: dto.paymentAmount, + escrowFundToken: dto.escrowFundToken, + manifestUrl: uploadedFile.url, + manifestHash: uploadedFile.hash, + }), + ); + expect(createJobSpy.mock.calls[0][2]).not.toHaveProperty('manifest'); + }); }); describe('createJob', () => { diff --git a/packages/apps/job-launcher/server/src/modules/job/job.service.ts b/packages/apps/job-launcher/server/src/modules/job/job.service.ts index 2c967136b3..5e05787098 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.service.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.service.ts @@ -75,6 +75,8 @@ import { GetJobsDto, JobDetailsDto, JobListDto, + JobQuickLaunchDto, + JobUnknownManifestDto, } from './job.dto'; import { JobEntity } from './job.entity'; import { JobRepository } from './job.repository'; @@ -271,6 +273,41 @@ export class JobService { return jobEntity.id; } + public async createJobWithUnknownManifest( + user: UserEntity, + dto: JobUnknownManifestDto, + ): Promise { + const { chainId, requestType, manifest } = dto; + let { reputationOracle, exchangeOracle, recordingOracle } = dto; + + this.web3Service.validateChainId(chainId); + + if (!reputationOracle || !exchangeOracle || !recordingOracle) { + const defaultOracles = await this.getDefaultOracles(chainId, requestType); + reputationOracle = reputationOracle ?? defaultOracles.reputationOracle; + exchangeOracle = exchangeOracle ?? defaultOracles.exchangeOracle; + recordingOracle = recordingOracle ?? defaultOracles.recordingOracle; + } + + const { url, hash } = await this.manifestService.uploadManifest( + chainId, + manifest, + [exchangeOracle, reputationOracle, recordingOracle], + ); + + const { manifest: _manifest, ...jobDto } = dto; + const quickLaunchDto: JobQuickLaunchDto = { + ...jobDto, + reputationOracle, + exchangeOracle, + recordingOracle, + manifestUrl: url, + manifestHash: hash, + }; + + return this.createJob(user, dto.requestType, quickLaunchDto); + } + private async getDefaultOracles( chainId: ChainId, requestType: JobRequestType, From d6d6768307530a9b1547c08d627e5ceddb264492 Mon Sep 17 00:00:00 2001 From: portuu3 <> Date: Tue, 14 Jul 2026 13:32:00 +0200 Subject: [PATCH 2/4] add cvat types --- .../job-launcher/client/src/types/index.ts | 2 + .../server/src/common/constants/index.ts | 2 + .../server/src/common/enums/job.ts | 18 +++--- .../server/src/common/utils/storage.ts | 2 + .../migrations/1784028614177-addCvatTypes.ts | 60 +++++++++++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts diff --git a/packages/apps/job-launcher/client/src/types/index.ts b/packages/apps/job-launcher/client/src/types/index.ts index 9e240856be..69cc95ab0e 100644 --- a/packages/apps/job-launcher/client/src/types/index.ts +++ b/packages/apps/job-launcher/client/src/types/index.ts @@ -107,11 +107,13 @@ export enum JobType { } export enum CvatJobType { + IMAGE_LABEL_BINARY = 'image_label_binary', IMAGE_POINTS = 'image_points', IMAGE_POLYGONS = 'image_polygons', IMAGE_BOXES = 'image_boxes', IMAGE_BOXES_FROM_POINTS = 'image_boxes_from_points', IMAGE_SKELETONS_FROM_BOXES = 'image_skeletons_from_boxes', + AUDIO_TRANSCRIPTION = 'audio_transcription', } export type FortuneRequest = { diff --git a/packages/apps/job-launcher/server/src/common/constants/index.ts b/packages/apps/job-launcher/server/src/common/constants/index.ts index 7bbb8b9a5e..21c84f7e33 100644 --- a/packages/apps/job-launcher/server/src/common/constants/index.ts +++ b/packages/apps/job-launcher/server/src/common/constants/index.ts @@ -26,11 +26,13 @@ export const SENDGRID_API_KEY_DISABLED = 'sendgrid-disabled'; export const HEADER_SIGNATURE_KEY = 'human-signature'; export const CVAT_JOB_TYPES = [ + CvatJobType.IMAGE_LABEL_BINARY, CvatJobType.IMAGE_POLYGONS, CvatJobType.IMAGE_BOXES, CvatJobType.IMAGE_POINTS, CvatJobType.IMAGE_BOXES_FROM_POINTS, CvatJobType.IMAGE_SKELETONS_FROM_BOXES, + CvatJobType.AUDIO_TRANSCRIPTION, ]; export const CANCEL_JOB_STATUSES = [ diff --git a/packages/apps/job-launcher/server/src/common/enums/job.ts b/packages/apps/job-launcher/server/src/common/enums/job.ts index db16799d11..ec47eb6ab3 100644 --- a/packages/apps/job-launcher/server/src/common/enums/job.ts +++ b/packages/apps/job-launcher/server/src/common/enums/job.ts @@ -32,11 +32,13 @@ export enum HCaptchaJobType { } export enum CvatJobType { + IMAGE_LABEL_BINARY = 'image_label_binary', IMAGE_POINTS = 'image_points', IMAGE_POLYGONS = 'image_polygons', IMAGE_BOXES = 'image_boxes', IMAGE_BOXES_FROM_POINTS = 'image_boxes_from_points', IMAGE_SKELETONS_FROM_BOXES = 'image_skeletons_from_boxes', + AUDIO_TRANSCRIPTION = 'audio_transcription', } /** @deprecated Audino jobs are no longer supported. */ @@ -45,12 +47,14 @@ export enum AudinoJobType { AUDIO_ATTRIBUTE_ANNOTATION = 'audio_attribute_annotation', } -export const JobType = [ - ...Object.values(CvatJobType), - ...Object.values(FortuneJobType), - ...Object.values(HCaptchaJobType), - ...Object.values(AudinoJobType), -]; +export const JobType = Array.from( + new Set([ + ...Object.values(CvatJobType), + ...Object.values(FortuneJobType), + ...Object.values(HCaptchaJobType), + ...Object.values(AudinoJobType), + ]), +); export type JobRequestType = CvatJobType | FortuneJobType | HCaptchaJobType; @@ -62,7 +66,7 @@ export enum JobCaptchaRequestType { IMAGE_LABEL_BINARY = 'image_label_binary', IMAGE_LABEL_MULTIPLE_CHOICE = 'image_label_multiple_choice', IMAGE_LABEL_AREA_SELECT = 'image_label_area_select', - TEXT_FREEE_NTRY = 'text_free_entry', + TEXT_FREE_ENTRY = 'text_free_entry', } export enum JobCaptchaShapeType { diff --git a/packages/apps/job-launcher/server/src/common/utils/storage.ts b/packages/apps/job-launcher/server/src/common/utils/storage.ts index fbe86382ec..5953ab98d5 100644 --- a/packages/apps/job-launcher/server/src/common/utils/storage.ts +++ b/packages/apps/job-launcher/server/src/common/utils/storage.ts @@ -31,11 +31,13 @@ export function generateBucketUrl( if ( ( [ + CvatJobType.IMAGE_LABEL_BINARY, CvatJobType.IMAGE_POLYGONS, CvatJobType.IMAGE_BOXES, CvatJobType.IMAGE_POINTS, CvatJobType.IMAGE_BOXES_FROM_POINTS, CvatJobType.IMAGE_SKELETONS_FROM_BOXES, + CvatJobType.AUDIO_TRANSCRIPTION, ] as JobRequestType[] ).includes(jobType) && storageData.provider != StorageProviders.AWS && diff --git a/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts b/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts new file mode 100644 index 0000000000..71c6c8b8cb --- /dev/null +++ b/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts @@ -0,0 +1,60 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddCvatTypes1784028614177 implements MigrationInterface { + name = 'AddCvatTypes1784028614177'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TYPE "hmt"."jobs_request_type_enum" + RENAME TO "jobs_request_type_enum_old" + `); + await queryRunner.query(` + CREATE TYPE "hmt"."jobs_request_type_enum" AS ENUM( + 'image_label_binary', + 'image_points', + 'image_polygons', + 'image_boxes', + 'image_boxes_from_points', + 'image_skeletons_from_boxes', + 'audio_transcription', + 'fortune', + 'hcaptcha', + 'audio_attribute_annotation' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."jobs" + ALTER COLUMN "request_type" TYPE "hmt"."jobs_request_type_enum" USING "request_type"::"text"::"hmt"."jobs_request_type_enum" + `); + await queryRunner.query(` + DROP TYPE "hmt"."jobs_request_type_enum_old" + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "hmt"."jobs_request_type_enum_old" AS ENUM( + 'image_points', + 'image_polygons', + 'image_boxes', + 'image_boxes_from_points', + 'image_skeletons_from_boxes', + 'fortune', + 'hcaptcha', + 'audio_transcription', + 'audio_attribute_annotation' + ) + `); + await queryRunner.query(` + ALTER TABLE "hmt"."jobs" + ALTER COLUMN "request_type" TYPE "hmt"."jobs_request_type_enum_old" USING "request_type"::"text"::"hmt"."jobs_request_type_enum_old" + `); + await queryRunner.query(` + DROP TYPE "hmt"."jobs_request_type_enum" + `); + await queryRunner.query(` + ALTER TYPE "hmt"."jobs_request_type_enum_old" + RENAME TO "jobs_request_type_enum" + `); + } +} From 45ab5adb776cde244167d341dbe3a4730b5de4ec Mon Sep 17 00:00:00 2001 From: portuu3 <> Date: Wed, 15 Jul 2026 15:01:44 +0200 Subject: [PATCH 3/4] Remove IMAGE_LABEL_BINARY and rename endpoint --- .../apps/job-launcher/server/src/common/constants/index.ts | 1 - packages/apps/job-launcher/server/src/common/enums/job.ts | 2 -- packages/apps/job-launcher/server/src/common/utils/storage.ts | 1 - .../src/database/migrations/1784028614177-addCvatTypes.ts | 1 - .../job-launcher/server/src/modules/job/job.controller.ts | 2 +- .../server/src/modules/manifest/manifest.service.spec.ts | 4 ++-- 6 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/apps/job-launcher/server/src/common/constants/index.ts b/packages/apps/job-launcher/server/src/common/constants/index.ts index 21c84f7e33..aa4b361175 100644 --- a/packages/apps/job-launcher/server/src/common/constants/index.ts +++ b/packages/apps/job-launcher/server/src/common/constants/index.ts @@ -26,7 +26,6 @@ export const SENDGRID_API_KEY_DISABLED = 'sendgrid-disabled'; export const HEADER_SIGNATURE_KEY = 'human-signature'; export const CVAT_JOB_TYPES = [ - CvatJobType.IMAGE_LABEL_BINARY, CvatJobType.IMAGE_POLYGONS, CvatJobType.IMAGE_BOXES, CvatJobType.IMAGE_POINTS, diff --git a/packages/apps/job-launcher/server/src/common/enums/job.ts b/packages/apps/job-launcher/server/src/common/enums/job.ts index ec47eb6ab3..bc8119a981 100644 --- a/packages/apps/job-launcher/server/src/common/enums/job.ts +++ b/packages/apps/job-launcher/server/src/common/enums/job.ts @@ -32,7 +32,6 @@ export enum HCaptchaJobType { } export enum CvatJobType { - IMAGE_LABEL_BINARY = 'image_label_binary', IMAGE_POINTS = 'image_points', IMAGE_POLYGONS = 'image_polygons', IMAGE_BOXES = 'image_boxes', @@ -63,7 +62,6 @@ export enum JobCaptchaMode { } export enum JobCaptchaRequestType { - IMAGE_LABEL_BINARY = 'image_label_binary', IMAGE_LABEL_MULTIPLE_CHOICE = 'image_label_multiple_choice', IMAGE_LABEL_AREA_SELECT = 'image_label_area_select', TEXT_FREE_ENTRY = 'text_free_entry', diff --git a/packages/apps/job-launcher/server/src/common/utils/storage.ts b/packages/apps/job-launcher/server/src/common/utils/storage.ts index 5953ab98d5..b5e829b30b 100644 --- a/packages/apps/job-launcher/server/src/common/utils/storage.ts +++ b/packages/apps/job-launcher/server/src/common/utils/storage.ts @@ -31,7 +31,6 @@ export function generateBucketUrl( if ( ( [ - CvatJobType.IMAGE_LABEL_BINARY, CvatJobType.IMAGE_POLYGONS, CvatJobType.IMAGE_BOXES, CvatJobType.IMAGE_POINTS, diff --git a/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts b/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts index 71c6c8b8cb..5558b6bf54 100644 --- a/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts +++ b/packages/apps/job-launcher/server/src/database/migrations/1784028614177-addCvatTypes.ts @@ -10,7 +10,6 @@ export class AddCvatTypes1784028614177 implements MigrationInterface { `); await queryRunner.query(` CREATE TYPE "hmt"."jobs_request_type_enum" AS ENUM( - 'image_label_binary', 'image_points', 'image_polygons', 'image_boxes', diff --git a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts index 19f525ee97..1fda2dbe6b 100644 --- a/packages/apps/job-launcher/server/src/modules/job/job.controller.ts +++ b/packages/apps/job-launcher/server/src/modules/job/job.controller.ts @@ -148,7 +148,7 @@ export class JobController { status: 401, description: 'Unauthorized. Missing or invalid credentials.', }) - @Post('/unknown-manifest') + @Post('/manifest-quick-launch') public async createJobWithUnknownManifest( @Body() data: JobUnknownManifestDto, @Request() req: RequestWithUser, diff --git a/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts b/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts index 413e893a0a..5fa407a6d2 100644 --- a/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts +++ b/packages/apps/job-launcher/server/src/modules/manifest/manifest.service.spec.ts @@ -64,7 +64,7 @@ describe('ManifestService', () => { it('should validate an hcaptcha manifest successfully', async () => { const manifest = { job_mode: faker.lorem.word(), - request_type: JobCaptchaRequestType.IMAGE_LABEL_BINARY, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, request_config: {}, requester_accuracy_target: faker.number.float({ min: 0.5, @@ -112,7 +112,7 @@ describe('ManifestService', () => { it('should throw when a required hcaptcha property is missing', async () => { const manifest = { job_mode: faker.lorem.word(), - request_type: JobCaptchaRequestType.IMAGE_LABEL_BINARY, + request_type: JobCaptchaRequestType.IMAGE_LABEL_AREA_SELECT, request_config: {}, requester_accuracy_target: faker.number.float({ min: 0.5, From 1194d16e01f665ab64fa28479dd94defd0e06d42 Mon Sep 17 00:00:00 2001 From: portuu3 <> Date: Thu, 16 Jul 2026 09:03:29 +0200 Subject: [PATCH 4/4] remove image_label_binary --- packages/apps/job-launcher/client/src/types/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/apps/job-launcher/client/src/types/index.ts b/packages/apps/job-launcher/client/src/types/index.ts index 69cc95ab0e..0f59474dab 100644 --- a/packages/apps/job-launcher/client/src/types/index.ts +++ b/packages/apps/job-launcher/client/src/types/index.ts @@ -107,7 +107,6 @@ export enum JobType { } export enum CvatJobType { - IMAGE_LABEL_BINARY = 'image_label_binary', IMAGE_POINTS = 'image_points', IMAGE_POLYGONS = 'image_polygons', IMAGE_BOXES = 'image_boxes',