Add POST /runs/import for runs executed elsewhere

A history recorded before the move (MLflow experiments) had nowhere to
go, since runs were only made by running a flow. Each entry is stored as
it ended: parameters completed and checked like a submit, cause
"import", empty commit and code digest, metrics and artifacts written
as given, nothing queued or announced. Run.external_id (new migration)
makes a second import a no-op and is an export column so a row joins
back. The request is all or nothing and capped at 200 entries;
Client.import_runs pages a longer history.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgG6TJw5qBZzaLghQSRVrq
This commit is contained in:
2026-09-16 17:01:07 +02:00
co-authored by Claude Opus 5
parent eeaa46d4ae
commit d9630f66f5
12 changed files with 848 additions and 32 deletions
+228
View File
@@ -1456,6 +1456,212 @@ export const HistoryPointSchema = {
description: 'One numeric value a message carried, and when.'
} as const;
export const ImportArtifactSchema = {
properties: {
name: {
type: 'string',
maxLength: 255,
minLength: 1,
title: 'Name'
},
filename: {
anyOf: [
{
type: 'string',
maxLength: 255
},
{
type: 'null'
}
],
title: 'Filename'
},
media_type: {
type: 'string',
maxLength: 128,
title: 'Media Type',
default: 'application/octet-stream'
},
content_base64: {
type: 'string',
title: 'Content Base64'
}
},
type: 'object',
required: ['name', 'content_base64'],
title: 'ImportArtifact'
} as const;
export const ImportCreateSchema = {
properties: {
runs: {
items: {
'$ref': '#/components/schemas/ImportEntry'
},
type: 'array',
title: 'Runs'
}
},
type: 'object',
title: 'ImportCreate'
} as const;
export const ImportEntrySchema = {
properties: {
flow: {
type: 'string',
title: 'Flow'
},
external_id: {
type: 'string',
maxLength: 255,
minLength: 1,
title: 'External Id'
},
params: {
additionalProperties: true,
type: 'object',
title: 'Params'
},
seed: {
anyOf: [
{
type: 'integer'
},
{
type: 'null'
}
],
title: 'Seed'
},
status: {
type: 'string',
enum: ['ok', 'error', 'cancelled'],
title: 'Status',
default: 'ok'
},
created_at: {
type: 'string',
format: 'date-time',
title: 'Created At'
},
started_at: {
anyOf: [
{
type: 'string',
format: 'date-time'
},
{
type: 'null'
}
],
title: 'Started At'
},
finished_at: {
anyOf: [
{
type: 'string',
format: 'date-time'
},
{
type: 'null'
}
],
title: 'Finished At'
},
result: {
additionalProperties: true,
type: 'object',
title: 'Result'
},
metrics: {
items: {
'$ref': '#/components/schemas/ImportMetric'
},
type: 'array',
title: 'Metrics'
},
artifacts: {
items: {
'$ref': '#/components/schemas/ImportArtifact'
},
type: 'array',
title: 'Artifacts'
},
labels: {
items: {
type: 'string'
},
type: 'array',
title: 'Labels'
},
group_id: {
anyOf: [
{
type: 'string',
maxLength: 64
},
{
type: 'null'
}
],
title: 'Group Id'
}
},
type: 'object',
required: ['flow', 'external_id', 'created_at'],
title: 'ImportEntry',
description: 'One run executed somewhere else, as it ended there.'
} as const;
export const ImportMetricSchema = {
properties: {
name: {
type: 'string',
maxLength: 128,
minLength: 1,
title: 'Name'
},
step: {
type: 'integer',
title: 'Step',
default: -1
},
ts: {
type: 'number',
title: 'Ts',
default: 0
},
value: {
type: 'number',
title: 'Value'
}
},
type: 'object',
required: ['name', 'value'],
title: 'ImportMetric'
} as const;
export const ImportedSchema = {
properties: {
external_id: {
type: 'string',
title: 'External Id'
},
id: {
type: 'string',
title: 'Id'
},
created: {
type: 'boolean',
title: 'Created'
}
},
type: 'object',
required: ['external_id', 'id', 'created'],
title: 'Imported'
} as const;
export const LibraryNodeSchema = {
properties: {
name: {
@@ -2879,6 +3085,17 @@ export const RunDetailSchema = {
title: 'Commit',
default: ''
},
external_id: {
anyOf: [
{
type: 'string'
},
{
type: 'null'
}
],
title: 'External Id'
},
seed: {
anyOf: [
{
@@ -4197,6 +4414,17 @@ export const fluksio__api__routes__runs__RunRowSchema = {
title: 'Commit',
default: ''
},
external_id: {
anyOf: [
{
type: 'string'
},
{
type: 'null'
}
],
title: 'External Id'
},
seed: {
anyOf: [
{
File diff suppressed because one or more lines are too long
+57
View File
@@ -475,6 +475,7 @@ export type fluksio__api__routes__runs__RunRow = {
origin_commit?: string;
code_digest?: string;
commit?: string;
external_id?: (string | null);
seed: (number | null);
group_id: (string | null);
draft?: boolean;
@@ -524,6 +525,55 @@ export type HTTPValidationError = {
detail?: Array<ValidationError>;
};
export type ImportArtifact = {
name: string;
filename?: (string | null);
media_type?: string;
content_base64: string;
};
export type ImportCreate = {
runs?: Array<ImportEntry>;
};
export type Imported = {
external_id: string;
id: string;
created: boolean;
};
/**
* One run executed somewhere else, as it ended there.
*/
export type ImportEntry = {
flow: string;
external_id: string;
params?: {
[key: string]: unknown;
};
seed?: (number | null);
status?: 'ok' | 'error' | 'cancelled';
created_at: string;
started_at?: (string | null);
finished_at?: (string | null);
result?: {
[key: string]: unknown;
};
metrics?: Array<ImportMetric>;
artifacts?: Array<ImportArtifact>;
labels?: Array<(string)>;
group_id?: (string | null);
};
export type status = 'ok' | 'error' | 'cancelled';
export type ImportMetric = {
name: string;
step?: number;
ts?: number;
value: number;
};
/**
* A node source shared across flows, and who is using it.
*/
@@ -1024,6 +1074,7 @@ export type RunDetail = {
origin_commit?: string;
code_digest?: string;
commit?: string;
external_id?: (string | null);
seed: (number | null);
group_id: (string | null);
draft?: boolean;
@@ -1839,6 +1890,12 @@ export type RunsCreateSweepData = {
export type RunsCreateSweepResponse = (Array<fluksio__api__routes__runs__RunRow>);
export type RunsImportRunsData = {
requestBody: ImportCreate;
};
export type RunsImportRunsResponse = (Array<Imported>);
export type RunsReadRunsData = {
before?: (string | null);
digest?: (string | null);