@things-factory/integration-mlflow 9.1.19 → 10.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,235 +0,0 @@
1
- import { Field, ObjectType, InputType, Int } from 'type-graphql'
2
-
3
- // ============================================================================
4
- // Experiment Types
5
- // ============================================================================
6
-
7
- @ObjectType({ description: 'MLflow Experiment' })
8
- export class MLflowExperiment {
9
- @Field({ description: 'Experiment ID' })
10
- experimentId: string
11
-
12
- @Field({ description: 'Experiment name' })
13
- name: string
14
-
15
- @Field({ description: 'Artifact location' })
16
- artifactLocation: string
17
-
18
- @Field({ description: 'Lifecycle stage' })
19
- lifecycleStage: string
20
-
21
- @Field(type => String, { nullable: true, description: 'Tags as JSON string' })
22
- tags?: string
23
- }
24
-
25
- @InputType({ description: 'Input for creating MLflow experiment' })
26
- export class CreateExperimentInput {
27
- @Field({ description: 'Experiment name' })
28
- name: string
29
-
30
- @Field({ nullable: true, description: 'Artifact location' })
31
- artifactLocation?: string
32
-
33
- @Field({ nullable: true, description: 'Tags as JSON string' })
34
- tags?: string
35
- }
36
-
37
- // ============================================================================
38
- // Run Types
39
- // ============================================================================
40
-
41
- @ObjectType({ description: 'MLflow Run' })
42
- export class MLflowRun {
43
- @Field({ description: 'Run ID' })
44
- runId: string
45
-
46
- @Field({ description: 'Experiment ID' })
47
- experimentId: string
48
-
49
- @Field({ description: 'Run status' })
50
- status: string
51
-
52
- @Field(type => Int, { description: 'Start time (Unix timestamp)' })
53
- startTime: number
54
-
55
- @Field(type => Int, { nullable: true, description: 'End time (Unix timestamp)' })
56
- endTime?: number
57
-
58
- @Field({ description: 'Artifact URI' })
59
- artifactUri: string
60
-
61
- @Field(type => String, { nullable: true, description: 'Metrics as JSON string' })
62
- metrics?: string
63
-
64
- @Field(type => String, { nullable: true, description: 'Params as JSON string' })
65
- params?: string
66
-
67
- @Field(type => String, { nullable: true, description: 'Tags as JSON string' })
68
- tags?: string
69
- }
70
-
71
- @InputType({ description: 'Input for creating MLflow run' })
72
- export class CreateRunInput {
73
- @Field({ description: 'Experiment ID' })
74
- experimentId: string
75
-
76
- @Field(type => Int, { nullable: true, description: 'Start time' })
77
- startTime?: number
78
-
79
- @Field({ nullable: true, description: 'Tags as JSON string' })
80
- tags?: string
81
- }
82
-
83
- @InputType({ description: 'Input for logging metric' })
84
- export class LogMetricInput {
85
- @Field({ description: 'Run ID' })
86
- runId: string
87
-
88
- @Field({ description: 'Metric key' })
89
- key: string
90
-
91
- @Field({ description: 'Metric value' })
92
- value: number
93
-
94
- @Field(type => Int, { nullable: true, description: 'Timestamp' })
95
- timestamp?: number
96
-
97
- @Field(type => Int, { nullable: true, description: 'Step' })
98
- step?: number
99
- }
100
-
101
- @InputType({ description: 'Input for logging parameter' })
102
- export class LogParamInput {
103
- @Field({ description: 'Run ID' })
104
- runId: string
105
-
106
- @Field({ description: 'Parameter key' })
107
- key: string
108
-
109
- @Field({ description: 'Parameter value' })
110
- value: string
111
- }
112
-
113
- @InputType({ description: 'Input for updating run' })
114
- export class UpdateRunInput {
115
- @Field({ description: 'Run ID' })
116
- runId: string
117
-
118
- @Field({ nullable: true, description: 'Status' })
119
- status?: string
120
-
121
- @Field(type => Int, { nullable: true, description: 'End time' })
122
- endTime?: number
123
- }
124
-
125
- // ============================================================================
126
- // Model Registry Types
127
- // ============================================================================
128
-
129
- @ObjectType({ description: 'MLflow Registered Model' })
130
- export class MLflowRegisteredModel {
131
- @Field({ description: 'Model name' })
132
- name: string
133
-
134
- @Field(type => Int, { description: 'Creation timestamp' })
135
- creationTimestamp: number
136
-
137
- @Field(type => Int, { description: 'Last updated timestamp' })
138
- lastUpdatedTimestamp: number
139
-
140
- @Field({ nullable: true, description: 'Model description' })
141
- description?: string
142
-
143
- @Field(type => String, { nullable: true, description: 'Tags as JSON string' })
144
- tags?: string
145
-
146
- @Field(type => String, { nullable: true, description: 'Latest versions as JSON string' })
147
- latestVersions?: string
148
- }
149
-
150
- @InputType({ description: 'Input for creating registered model' })
151
- export class CreateRegisteredModelInput {
152
- @Field({ description: 'Model name' })
153
- name: string
154
-
155
- @Field({ nullable: true, description: 'Model description' })
156
- description?: string
157
-
158
- @Field({ nullable: true, description: 'Tags as JSON string' })
159
- tags?: string
160
- }
161
-
162
- @ObjectType({ description: 'MLflow Model Version' })
163
- export class MLflowModelVersion {
164
- @Field({ description: 'Model name' })
165
- name: string
166
-
167
- @Field({ description: 'Version number' })
168
- version: string
169
-
170
- @Field(type => Int, { description: 'Creation timestamp' })
171
- creationTimestamp: number
172
-
173
- @Field({ description: 'Current stage' })
174
- currentStage: string
175
-
176
- @Field({ description: 'Model source' })
177
- source: string
178
-
179
- @Field({ description: 'Version status' })
180
- status: string
181
-
182
- @Field({ nullable: true, description: 'Run ID' })
183
- runId?: string
184
-
185
- @Field({ nullable: true, description: 'Description' })
186
- description?: string
187
- }
188
-
189
- @InputType({ description: 'Input for creating model version' })
190
- export class CreateModelVersionInput {
191
- @Field({ description: 'Model name' })
192
- name: string
193
-
194
- @Field({ description: 'Model source URI' })
195
- source: string
196
-
197
- @Field({ nullable: true, description: 'Run ID' })
198
- runId?: string
199
-
200
- @Field({ nullable: true, description: 'Description' })
201
- description?: string
202
-
203
- @Field({ nullable: true, description: 'Tags as JSON string' })
204
- tags?: string
205
- }
206
-
207
- @InputType({ description: 'Input for transitioning model version stage' })
208
- export class TransitionStageInput {
209
- @Field({ description: 'Model name' })
210
- name: string
211
-
212
- @Field({ description: 'Version number' })
213
- version: string
214
-
215
- @Field({ description: 'Target stage (None, Staging, Production, Archived)' })
216
- stage: string
217
-
218
- @Field({ nullable: true, description: 'Archive existing versions in target stage' })
219
- archiveExistingVersions?: boolean
220
- }
221
-
222
- @InputType({ description: 'Input for searching runs' })
223
- export class SearchRunsInput {
224
- @Field(type => [String], { description: 'Experiment IDs to search' })
225
- experimentIds: string[]
226
-
227
- @Field({ nullable: true, description: 'Filter string' })
228
- filter?: string
229
-
230
- @Field(type => Int, { nullable: true, description: 'Max results' })
231
- maxResults?: number
232
-
233
- @Field(type => [String], { nullable: true, description: 'Order by' })
234
- orderBy?: string[]
235
- }
@@ -1,375 +0,0 @@
1
- /**
2
- * MLflow REST API Client
3
- *
4
- * Thin wrapper around MLflow REST API for Things-Factory integration
5
- * Reference: https://mlflow.org/docs/latest/rest-api.html
6
- */
7
-
8
- import { config } from '@things-factory/env'
9
-
10
- const MLFLOW_TRACKING_URI = config.get('mlflowTrackingUri', process.env.MLFLOW_TRACKING_URI || 'http://localhost:5000')
11
-
12
- interface MLflowResponse<T> {
13
- data?: T
14
- error?: string
15
- }
16
-
17
- class MLflowApiClient {
18
- private baseUrl: string
19
-
20
- constructor(baseUrl: string = MLFLOW_TRACKING_URI) {
21
- this.baseUrl = baseUrl.replace(/\/$/, '') // Remove trailing slash
22
- }
23
-
24
- /**
25
- * Generic HTTP request wrapper
26
- */
27
- private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
28
- const url = `${this.baseUrl}${endpoint}`
29
-
30
- const defaultHeaders = {
31
- 'Content-Type': 'application/json'
32
- }
33
-
34
- try {
35
- const response = await fetch(url, {
36
- ...options,
37
- headers: {
38
- ...defaultHeaders,
39
- ...options.headers
40
- }
41
- })
42
-
43
- if (!response.ok) {
44
- const errorText = await response.text()
45
- throw new Error(`MLflow API error: ${response.status} - ${errorText}`)
46
- }
47
-
48
- return await response.json()
49
- } catch (error) {
50
- console.error(`MLflow API request failed: ${endpoint}`, error)
51
- throw error
52
- }
53
- }
54
-
55
- // ========================================================================
56
- // Experiments API
57
- // ========================================================================
58
-
59
- /**
60
- * Create a new experiment
61
- */
62
- async createExperiment(params: {
63
- name: string
64
- artifact_location?: string
65
- tags?: Array<{ key: string; value: string }>
66
- }): Promise<{ experiment_id: string }> {
67
- return this.request('/api/2.0/mlflow/experiments/create', {
68
- method: 'POST',
69
- body: JSON.stringify(params)
70
- })
71
- }
72
-
73
- /**
74
- * Get experiment by ID
75
- */
76
- async getExperiment(experimentId: string): Promise<{
77
- experiment: {
78
- experiment_id: string
79
- name: string
80
- artifact_location: string
81
- lifecycle_stage: string
82
- tags?: Array<{ key: string; value: string }>
83
- }
84
- }> {
85
- return this.request(`/api/2.0/mlflow/experiments/get?experiment_id=${experimentId}`)
86
- }
87
-
88
- /**
89
- * Search experiments
90
- */
91
- async searchExperiments(params?: { max_results?: number; filter?: string; order_by?: string[] }): Promise<{
92
- experiments: Array<{
93
- experiment_id: string
94
- name: string
95
- artifact_location: string
96
- lifecycle_stage: string
97
- tags?: Array<{ key: string; value: string }>
98
- }>
99
- }> {
100
- const queryParams = new URLSearchParams()
101
- if (params?.max_results) queryParams.append('max_results', String(params.max_results))
102
- if (params?.filter) queryParams.append('filter', params.filter)
103
-
104
- return this.request(`/api/2.0/mlflow/experiments/search?${queryParams}`)
105
- }
106
-
107
- /**
108
- * Delete experiment
109
- */
110
- async deleteExperiment(experimentId: string): Promise<void> {
111
- return this.request('/api/2.0/mlflow/experiments/delete', {
112
- method: 'POST',
113
- body: JSON.stringify({ experiment_id: experimentId })
114
- })
115
- }
116
-
117
- // ========================================================================
118
- // Runs API
119
- // ========================================================================
120
-
121
- /**
122
- * Create a new run
123
- */
124
- async createRun(params: {
125
- experiment_id: string
126
- start_time?: number
127
- tags?: Array<{ key: string; value: string }>
128
- }): Promise<{
129
- run: {
130
- info: {
131
- run_id: string
132
- experiment_id: string
133
- status: string
134
- start_time: number
135
- artifact_uri: string
136
- }
137
- data: any
138
- }
139
- }> {
140
- return this.request('/api/2.0/mlflow/runs/create', {
141
- method: 'POST',
142
- body: JSON.stringify(params)
143
- })
144
- }
145
-
146
- /**
147
- * Get run by ID
148
- */
149
- async getRun(runId: string): Promise<{
150
- run: {
151
- info: {
152
- run_id: string
153
- experiment_id: string
154
- status: string
155
- start_time: number
156
- end_time?: number
157
- artifact_uri: string
158
- }
159
- data: {
160
- metrics: Array<{ key: string; value: number; timestamp: number; step: number }>
161
- params: Array<{ key: string; value: string }>
162
- tags: Array<{ key: string; value: string }>
163
- }
164
- }
165
- }> {
166
- return this.request(`/api/2.0/mlflow/runs/get?run_id=${runId}`)
167
- }
168
-
169
- /**
170
- * Search runs
171
- */
172
- async searchRuns(params: {
173
- experiment_ids: string[]
174
- filter?: string
175
- max_results?: number
176
- order_by?: string[]
177
- }): Promise<{
178
- runs: Array<{
179
- info: {
180
- run_id: string
181
- experiment_id: string
182
- status: string
183
- start_time: number
184
- end_time?: number
185
- artifact_uri: string
186
- }
187
- data: any
188
- }>
189
- }> {
190
- return this.request('/api/2.0/mlflow/runs/search', {
191
- method: 'POST',
192
- body: JSON.stringify(params)
193
- })
194
- }
195
-
196
- /**
197
- * Log metric for a run
198
- */
199
- async logMetric(params: {
200
- run_id: string
201
- key: string
202
- value: number
203
- timestamp?: number
204
- step?: number
205
- }): Promise<void> {
206
- return this.request('/api/2.0/mlflow/runs/log-metric', {
207
- method: 'POST',
208
- body: JSON.stringify(params)
209
- })
210
- }
211
-
212
- /**
213
- * Log parameter for a run
214
- */
215
- async logParam(params: { run_id: string; key: string; value: string }): Promise<void> {
216
- return this.request('/api/2.0/mlflow/runs/log-parameter', {
217
- method: 'POST',
218
- body: JSON.stringify(params)
219
- })
220
- }
221
-
222
- /**
223
- * Update run status
224
- */
225
- async updateRun(params: {
226
- run_id: string
227
- status?: 'RUNNING' | 'SCHEDULED' | 'FINISHED' | 'FAILED' | 'KILLED'
228
- end_time?: number
229
- }): Promise<void> {
230
- return this.request('/api/2.0/mlflow/runs/update', {
231
- method: 'POST',
232
- body: JSON.stringify(params)
233
- })
234
- }
235
-
236
- // ========================================================================
237
- // Model Registry API
238
- // ========================================================================
239
-
240
- /**
241
- * Create registered model
242
- */
243
- async createRegisteredModel(params: {
244
- name: string
245
- tags?: Array<{ key: string; value: string }>
246
- description?: string
247
- }): Promise<{
248
- registered_model: {
249
- name: string
250
- creation_timestamp: number
251
- last_updated_timestamp: number
252
- }
253
- }> {
254
- return this.request('/api/2.0/mlflow/registered-models/create', {
255
- method: 'POST',
256
- body: JSON.stringify(params)
257
- })
258
- }
259
-
260
- /**
261
- * Get registered model
262
- */
263
- async getRegisteredModel(name: string): Promise<{
264
- registered_model: {
265
- name: string
266
- creation_timestamp: number
267
- last_updated_timestamp: number
268
- description?: string
269
- tags?: Array<{ key: string; value: string }>
270
- latest_versions?: any[]
271
- }
272
- }> {
273
- return this.request(`/api/2.0/mlflow/registered-models/get?name=${encodeURIComponent(name)}`)
274
- }
275
-
276
- /**
277
- * Search registered models
278
- */
279
- async searchRegisteredModels(params?: { filter?: string; max_results?: number; order_by?: string[] }): Promise<{
280
- registered_models: Array<{
281
- name: string
282
- creation_timestamp: number
283
- last_updated_timestamp: number
284
- description?: string
285
- latest_versions?: any[]
286
- }>
287
- }> {
288
- const queryParams = new URLSearchParams()
289
- if (params?.filter) queryParams.append('filter', params.filter)
290
- if (params?.max_results) queryParams.append('max_results', String(params.max_results))
291
-
292
- return this.request(`/api/2.0/mlflow/registered-models/search?${queryParams}`)
293
- }
294
-
295
- /**
296
- * Create model version
297
- */
298
- async createModelVersion(params: {
299
- name: string
300
- source: string
301
- run_id?: string
302
- tags?: Array<{ key: string; value: string }>
303
- description?: string
304
- }): Promise<{
305
- model_version: {
306
- name: string
307
- version: string
308
- creation_timestamp: number
309
- last_updated_timestamp: number
310
- source: string
311
- status: string
312
- }
313
- }> {
314
- return this.request('/api/2.0/mlflow/model-versions/create', {
315
- method: 'POST',
316
- body: JSON.stringify(params)
317
- })
318
- }
319
-
320
- /**
321
- * Get model version
322
- */
323
- async getModelVersion(params: { name: string; version: string }): Promise<{
324
- model_version: {
325
- name: string
326
- version: string
327
- creation_timestamp: number
328
- current_stage: string
329
- source: string
330
- status: string
331
- run_id?: string
332
- }
333
- }> {
334
- return this.request(
335
- `/api/2.0/mlflow/model-versions/get?name=${encodeURIComponent(params.name)}&version=${params.version}`
336
- )
337
- }
338
-
339
- /**
340
- * Transition model version stage
341
- */
342
- async transitionModelVersionStage(params: {
343
- name: string
344
- version: string
345
- stage: 'None' | 'Staging' | 'Production' | 'Archived'
346
- archive_existing_versions?: boolean
347
- }): Promise<{
348
- model_version: {
349
- name: string
350
- version: string
351
- current_stage: string
352
- }
353
- }> {
354
- return this.request('/api/2.0/mlflow/model-versions/transition-stage', {
355
- method: 'POST',
356
- body: JSON.stringify(params)
357
- })
358
- }
359
-
360
- /**
361
- * Delete model version
362
- */
363
- async deleteModelVersion(params: { name: string; version: string }): Promise<void> {
364
- return this.request('/api/2.0/mlflow/model-versions/delete', {
365
- method: 'DELETE',
366
- body: JSON.stringify(params)
367
- })
368
- }
369
- }
370
-
371
- // Export singleton instance
372
- export const mlflowApi = new MLflowApiClient()
373
-
374
- // Export class for testing or custom instances
375
- export { MLflowApiClient }