import { Resolver, Mutation, Query, Arg, Ctx, Int, Directive } from 'type-graphql' import { labelStudioApi } from '../../utils/label-studio-api-client.js' import { MLBackend, AddMLBackendInput } from '../../types/label-studio-types.js' @Resolver() export class MLBackendService { /** * Get ML backends for a project */ @Query(returns => [MLBackend], { description: 'Get all ML backends connected to a Label Studio project' }) @Directive('@privilege(category: "label-studio", privilege: "query")') async labelStudioMLBackends( @Arg('projectId', type => Int) projectId: number, @Ctx() context: ResolverContext ): Promise { try { const backends = await labelStudioApi.getMLBackends(projectId) return backends.map(backend => ({ id: backend.id, url: backend.url, title: backend.title, isInteractive: backend.is_interactive || false, modelVersion: backend.model_version || 'unknown' })) } catch (error) { console.error(`Failed to fetch ML backends for project ${projectId}:`, error) throw new Error(`Failed to fetch ML backends: ${error.message}`) } } /** * Add ML backend to project */ @Mutation(returns => MLBackend, { description: 'Add ML backend to Label Studio project for predictions and training' }) @Directive('@privilege(category: "label-studio", privilege: "mutation")') async addMLBackendToProject( @Arg('projectId', type => Int) projectId: number, @Arg('input') input: AddMLBackendInput, @Ctx() context: ResolverContext ): Promise { try { const backend = await labelStudioApi.addMLBackend({ project: projectId, url: input.url, title: input.title, is_interactive: input.isInteractive || false }) return { id: backend.id, url: backend.url, title: backend.title, isInteractive: backend.is_interactive || false, modelVersion: backend.model_version || 'unknown' } } catch (error) { console.error(`Failed to add ML backend to project ${projectId}:`, error) throw new Error(`Failed to add ML backend: ${error.message}`) } } /** * Delete ML backend */ @Mutation(returns => Boolean, { description: 'Remove ML backend from project' }) @Directive('@privilege(category: "label-studio", privilege: "mutation")') async deleteMLBackend( @Arg('mlBackendId', type => Int) mlBackendId: number, @Ctx() context: ResolverContext ): Promise { try { await labelStudioApi.deleteMLBackend(mlBackendId) return true } catch (error) { console.error(`Failed to delete ML backend ${mlBackendId}:`, error) throw new Error(`Failed to delete ML backend: ${error.message}`) } } /** * Trigger predictions for tasks */ @Mutation(returns => Boolean, { description: 'Trigger ML predictions for tasks in a project' }) @Directive('@privilege(category: "label-studio", privilege: "mutation")') async triggerLabelStudioPredictions( @Arg('projectId', type => Int) projectId: number, @Ctx() context: ResolverContext, @Arg('taskIds', type => [Int], { nullable: true }) taskIds?: number[] ): Promise { try { await labelStudioApi.triggerPredictions(projectId, taskIds) return true } catch (error) { console.error(`Failed to trigger predictions for project ${projectId}:`, error) throw new Error(`Failed to trigger predictions: ${error.message}`) } } /** * Train ML model */ @Mutation(returns => Boolean, { description: 'Trigger ML model training with current annotations' }) @Directive('@privilege(category: "label-studio", privilege: "mutation")') async trainLabelStudioModel( @Arg('mlBackendId', type => Int) mlBackendId: number, @Ctx() context: ResolverContext ): Promise { try { await labelStudioApi.trainModel(mlBackendId) return true } catch (error) { console.error(`Failed to train model ${mlBackendId}:`, error) throw new Error(`Failed to train model: ${error.message}`) } } }