import type { DagsterLaunchRunParams, DagsterLaunchRunResponse } from '@/tools/dagster/types' import { dagsterGraphqlUrl, dagsterRequestHeaders, dagsterUnionErrorMessage, parseDagsterGraphqlResponse, } from '@/tools/dagster/utils' import type { ToolConfig } from '@/tools/types' interface LaunchRunResult { type: string run?: { runId: string } message?: string /** Present when type === 'InvalidStepError' */ invalidStepKey?: string /** Present when type === 'InvalidOutputError' */ stepKey?: string invalidOutputName?: string /** Present when type === 'RunConfigValidationInvalid' */ errors?: Array<{ message: string }> } function buildLaunchRunMutation(hasConfig: boolean, hasTags: boolean) { const varDefs = [ '$repositoryLocationName: String!', '$repositoryName: String!', '$jobName: String!', ] if (hasConfig) varDefs.push('$runConfigData: RunConfigData') if (hasTags) varDefs.push('$tags: [ExecutionTag!]') const execParams = [ `selector: { repositoryLocationName: $repositoryLocationName repositoryName: $repositoryName jobName: $jobName }`, ] if (hasConfig) execParams.push('runConfigData: $runConfigData') if (hasTags) execParams.push('executionMetadata: { tags: $tags }') return ` mutation LaunchRun(${varDefs.join(', ')}) { launchRun( executionParams: { ${execParams.join('\n ')} } ) { type: __typename ... on LaunchRunSuccess { run { runId } } ... on InvalidStepError { __typename invalidStepKey } ... on InvalidOutputError { __typename stepKey invalidOutputName } ... on RunConfigValidationInvalid { errors { message } } ... on PipelineNotFoundError { message } ... on RunConflict { message } ... on UnauthorizedError { message } ... on InvalidSubsetError { message } ... on PresetNotFoundError { message } ... on ConflictingExecutionParamsError { message } ... on NoModeProvidedError { message } ... on PythonError { message } } } ` } export const launchRunTool: ToolConfig = { id: 'dagster_launch_run', name: 'Dagster Launch Run', description: 'Launch a job run on a Dagster instance.', version: '1.0.0', params: { host: { type: 'string', required: true, visibility: 'user-only', description: 'Dagster host URL (e.g., https://myorg.dagster.cloud/prod or http://localhost:3000)', }, apiKey: { type: 'string', required: false, visibility: 'user-only', description: 'Dagster+ API token (leave blank for OSS / self-hosted)', }, repositoryLocationName: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Repository location (code location) name', }, repositoryName: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Repository name within the code location', }, jobName: { type: 'string', required: true, visibility: 'user-or-llm', description: 'Name of the job to launch', }, runConfigJson: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Run configuration as a JSON object (optional)', }, tags: { type: 'string', required: false, visibility: 'user-or-llm', description: 'Tags as a JSON array of {key, value} objects (optional)', }, }, request: { url: (params) => dagsterGraphqlUrl(params.host), method: 'POST', headers: (params) => dagsterRequestHeaders(params), body: (params) => { const variables: Record = { repositoryLocationName: params.repositoryLocationName, repositoryName: params.repositoryName, jobName: params.jobName, } let hasConfig = false if (params.runConfigJson) { try { variables.runConfigData = JSON.parse(params.runConfigJson) hasConfig = true } catch { throw new Error('Invalid JSON in runConfigJson') } } let hasTags = false if (params.tags) { try { variables.tags = JSON.parse(params.tags) hasTags = true } catch { throw new Error('Invalid JSON in tags') } } return { query: buildLaunchRunMutation(hasConfig, hasTags), variables, } }, }, transformResponse: async (response: Response) => { const data = await parseDagsterGraphqlResponse<{ launchRun?: unknown }>(response) const result = data.data?.launchRun as LaunchRunResult | undefined if (!result) throw new Error('Unexpected response from Dagster') if (result.type === 'LaunchRunSuccess' && result.run) { return { success: true, output: { runId: result.run.runId }, } } if (result.type === 'InvalidStepError' && result.invalidStepKey) { throw new Error(`InvalidStepError: invalid step key "${result.invalidStepKey}"`) } if (result.type === 'InvalidOutputError' && result.stepKey) { throw new Error( `InvalidOutputError: invalid output "${result.invalidOutputName ?? 'unknown'}" on step "${result.stepKey}"` ) } if (result.type === 'RunConfigValidationInvalid' && result.errors?.length) { throw new Error( `RunConfigValidationInvalid: ${result.errors.map((e) => e.message).join('; ')}` ) } throw new Error(`${result.type}: ${dagsterUnionErrorMessage(result, 'Launch run failed')}`) }, outputs: { runId: { type: 'string', description: 'The globally unique ID of the launched run', }, }, }