1
1
"""
2
- Models Node as a central element in a project's pipeline
2
+ Models Node as a central element in a project's pipeline
3
3
"""
4
4
5
5
from typing import Annotated , Any , TypeAlias , Union
17
17
StringConstraints ,
18
18
field_validator ,
19
19
)
20
+ from pydantic .config import JsonDict
20
21
21
22
from .basic_types import EnvVarKey , KeyIDStr
22
23
from .projects_access import AccessEnum
71
72
72
73
73
74
class NodeState (BaseModel ):
74
- modified : bool = Field (
75
- default = True , description = "true if the node's outputs need to be re-computed"
76
- )
77
- dependencies : set [NodeID ] = Field (
78
- default_factory = set ,
79
- description = "contains the node inputs dependencies if they need to be computed first" ,
80
- )
81
- current_status : RunningState = Field (
82
- default = RunningState .NOT_STARTED ,
83
- description = "the node's current state" ,
84
- alias = "currentStatus" ,
85
- )
86
- progress : float | None = Field (
87
- default = 0 ,
88
- ge = 0.0 ,
89
- le = 1.0 ,
90
- description = "current progress of the task if available (None if not started or not a computational task)" ,
91
- )
75
+ modified : Annotated [
76
+ bool ,
77
+ Field (
78
+ description = "true if the node's outputs need to be re-computed" ,
79
+ ),
80
+ ] = True
81
+
82
+ dependencies : Annotated [
83
+ set [NodeID ],
84
+ Field (
85
+ default_factory = set ,
86
+ description = "contains the node inputs dependencies if they need to be computed first" ,
87
+ ),
88
+ ] = DEFAULT_FACTORY
89
+
90
+ current_status : Annotated [
91
+ RunningState ,
92
+ Field (
93
+ description = "the node's current state" ,
94
+ alias = "currentStatus" ,
95
+ ),
96
+ ] = RunningState .NOT_STARTED
97
+
98
+ progress : Annotated [
99
+ float | None ,
100
+ Field (
101
+ ge = 0.0 ,
102
+ le = 1.0 ,
103
+ description = "current progress of the task if available (None if not started or not a computational task)" ,
104
+ ),
105
+ ] = 0
106
+
92
107
model_config = ConfigDict (
93
108
extra = "forbid" ,
109
+ populate_by_name = True ,
94
110
json_schema_extra = {
95
111
"examples" : [
96
112
{
@@ -113,24 +129,35 @@ class NodeState(BaseModel):
113
129
)
114
130
115
131
132
+ def _convert_old_enum_name (v ) -> RunningState :
133
+ if v == "FAILURE" :
134
+ return RunningState .FAILED
135
+ return RunningState (v )
136
+
137
+
116
138
class Node (BaseModel ):
117
- key : ServiceKey = Field (
118
- ...,
119
- description = "distinctive name for the node based on the docker registry path" ,
120
- examples = [
121
- "simcore/services/comp/itis/sleeper" ,
122
- "simcore/services/dynamic/3dviewer" ,
123
- "simcore/services/frontend/file-picker" ,
124
- ],
125
- )
126
- version : ServiceVersion = Field (
127
- ...,
128
- description = "semantic version number of the node" ,
129
- examples = ["1.0.0" , "0.0.1" ],
130
- )
131
- label : str = Field (
132
- ..., description = "The short name of the node" , examples = ["JupyterLab" ]
133
- )
139
+ key : Annotated [
140
+ ServiceKey ,
141
+ Field (
142
+ description = "distinctive name for the node based on the docker registry path" ,
143
+ examples = [
144
+ "simcore/services/comp/itis/sleeper" ,
145
+ "simcore/services/dynamic/3dviewer" ,
146
+ "simcore/services/frontend/file-picker" ,
147
+ ],
148
+ ),
149
+ ]
150
+ version : Annotated [
151
+ ServiceVersion ,
152
+ Field (
153
+ description = "semantic version number of the node" ,
154
+ examples = ["1.0.0" , "0.0.1" ],
155
+ ),
156
+ ]
157
+ label : Annotated [
158
+ str ,
159
+ Field (description = "The short name of the node" , examples = ["JupyterLab" ]),
160
+ ]
134
161
progress : Annotated [
135
162
float | None ,
136
163
Field (
@@ -204,9 +231,9 @@ class Node(BaseModel):
204
231
Field (default_factory = dict , description = "values of output properties" ),
205
232
] = DEFAULT_FACTORY
206
233
207
- output_node : Annotated [
208
- bool | None , Field ( deprecated = True , alias = "outputNode" )
209
- ] = None
234
+ output_node : Annotated [bool | None , Field ( deprecated = True , alias = "outputNode" )] = (
235
+ None
236
+ )
210
237
211
238
output_nodes : Annotated [
212
239
list [NodeID ] | None ,
@@ -255,24 +282,109 @@ def _convert_empty_str_to_none(cls, v):
255
282
return None
256
283
return v
257
284
258
- @classmethod
259
- def _convert_old_enum_name (cls , v ) -> RunningState :
260
- if v == "FAILURE" :
261
- return RunningState .FAILED
262
- return RunningState (v )
263
-
264
285
@field_validator ("state" , mode = "before" )
265
286
@classmethod
266
287
def _convert_from_enum (cls , v ):
267
288
if isinstance (v , str ):
289
+
268
290
# the old version of state was a enum of RunningState
269
- running_state_value = cls . _convert_old_enum_name (v )
270
- return NodeState (currentStatus = running_state_value )
291
+ running_state_value = _convert_old_enum_name (v )
292
+ return NodeState (current_status = running_state_value )
271
293
return v
272
294
295
+ @staticmethod
296
+ def _update_json_schema_extra (schema : JsonDict ) -> None :
297
+ schema .update (
298
+ {
299
+ "examples" : [
300
+ # Minimal example with only required fields
301
+ {
302
+ "key" : "simcore/services/comp/no_ports" ,
303
+ "version" : "1.0.0" ,
304
+ "label" : "Sleep" ,
305
+ },
306
+ # Complete example with optional fields
307
+ {
308
+ "key" : "simcore/services/comp/only_inputs" ,
309
+ "version" : "1.0.0" ,
310
+ "label" : "Only INputs" ,
311
+ "inputs" : {
312
+ "input_1" : 1 ,
313
+ "input_2" : 2 ,
314
+ "input_3" : 3 ,
315
+ },
316
+ },
317
+ # Complete example with optional fields
318
+ {
319
+ "key" : "simcore/services/comp/only_outputs" ,
320
+ "version" : "1.0.0" ,
321
+ "label" : "Only Outputs" ,
322
+ "outputs" : {
323
+ "output_1" : 1 ,
324
+ "output_2" : 2 ,
325
+ "output_3" : 3 ,
326
+ },
327
+ },
328
+ # Example with all possible input and output types
329
+ {
330
+ "key" : "simcore/services/comp/itis/all-types" ,
331
+ "version" : "1.0.0" ,
332
+ "label" : "All Types Demo" ,
333
+ "inputs" : {
334
+ "boolean_input" : True ,
335
+ "integer_input" : 42 ,
336
+ "float_input" : 3.14159 ,
337
+ "string_input" : "text value" ,
338
+ "json_input" : {"key" : "value" , "nested" : {"data" : 123 }},
339
+ "port_link_input" : {
340
+ "nodeUuid" : "f2700a54-adcf-45d4-9881-01ec30fd75a2" ,
341
+ "output" : "out_1" ,
342
+ },
343
+ "simcore_file_link" : {
344
+ "store" : "simcore.s3" ,
345
+ "path" : "123e4567-e89b-12d3-a456-426614174000/test.csv" ,
346
+ },
347
+ "datcore_file_link" : {
348
+ "store" : "datcore" ,
349
+ "dataset" : "N:dataset:123" ,
350
+ "path" : "path/to/file.txt" ,
351
+ },
352
+ "download_link" : {
353
+ "downloadLink" : "https://example.com/downloadable/file.txt"
354
+ },
355
+ "array_input" : [1 , 2 , 3 , 4 , 5 ],
356
+ "object_input" : {"name" : "test" , "value" : 42 },
357
+ },
358
+ "outputs" : {
359
+ "boolean_output" : False ,
360
+ "integer_output" : 100 ,
361
+ "float_output" : 2.71828 ,
362
+ "string_output" : "result text" ,
363
+ "json_output" : {"status" : "success" , "data" : [1 , 2 , 3 ]},
364
+ "simcore_file_output" : {
365
+ "store" : "simcore.s3" ,
366
+ "path" : "987e6543-e21b-12d3-a456-426614174000/result.csv" ,
367
+ },
368
+ "datcore_file_output" : {
369
+ "store" : "datcore" ,
370
+ "dataset" : "N:dataset:456" ,
371
+ "path" : "results/output.txt" ,
372
+ },
373
+ "download_link_output" : {
374
+ "downloadLink" : "https://example.com/results/download.txt"
375
+ },
376
+ "array_output" : ["a" , "b" , "c" , "d" ],
377
+ "object_output" : {"status" : "complete" , "count" : 42 },
378
+ },
379
+ },
380
+ ],
381
+ }
382
+ )
383
+
273
384
model_config = ConfigDict (
274
385
extra = "forbid" ,
275
386
populate_by_name = True ,
387
+ json_schema_extra = _update_json_schema_extra ,
276
388
)
277
389
278
390
0 commit comments