Skip to content

Commit 905001d

Browse files
committed
fix(run): allow caller-supplied taskUUID instead of always generating
The API supports caller-provided task identifiers but the CLI rejected taskUUID as a reserved field. Demote taskUUID from Protected to auto-only so it is still excluded from completions and validation but no longer blocked as a key=value argument. If the caller supplies an invalid UUID, Run returns an error before submitting. Closes RUN-10706
1 parent cbe0e78 commit 905001d

4 files changed

Lines changed: 79 additions & 7 deletions

File tree

internal/api/client.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,18 @@ func (c *Client) Run(ctx context.Context, model string, args []string, opts RunO
222222
payload[fieldDeliveryMethod] = deliveryMethod
223223
}
224224

225-
// Inject system fields.
226-
taskUUID := uuid.New()
225+
// Inject system fields. Use a caller-supplied taskUUID if provided, otherwise generate.
226+
var taskUUID uuid.UUID
227+
if raw, ok := payload[fieldTaskUUID]; ok {
228+
s, _ := raw.(string)
229+
parsed, err := uuid.Parse(s)
230+
if err != nil {
231+
return nil, fmt.Errorf("taskUUID: invalid UUID %q: %w", s, err)
232+
}
233+
taskUUID = parsed
234+
} else {
235+
taskUUID = uuid.New()
236+
}
227237
payload[fieldTaskType] = taskType
228238
payload[fieldTaskUUID] = taskUUID
229239

internal/api/run_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,66 @@ func TestClientRun_RawArgs_InvalidKV(t *testing.T) {
401401
t.Errorf("expected 0 transport calls, got %d", mock.callCount)
402402
}
403403
}
404+
405+
// TestClientRun_UserProvidedTaskUUID: a caller-supplied taskUUID must be forwarded
406+
// to the API unchanged instead of being overwritten with a new UUID.
407+
func TestClientRun_UserProvidedTaskUUID(t *testing.T) {
408+
const wantUUID = "d58cbacc-bed6-413e-9e4a-b118e4d84035"
409+
410+
srv := inferenceSchemaServer(t, requestSchemaWithTaskType("imageInference", "sync"))
411+
412+
mock := &mockTransport{
413+
responses: []mockResponse{
414+
{data: []json.RawMessage{successItem(t, nil)}},
415+
},
416+
}
417+
418+
c := NewClient(mock, slog.Default())
419+
c.schemaBaseURLOverride = srv.URL + "/"
420+
421+
_, err := c.Run(context.Background(), testModelAIR, []string{"taskUUID=" + wantUUID}, RunOptions{})
422+
if err != nil {
423+
t.Fatalf("unexpected error: %v", err)
424+
}
425+
426+
if len(mock.captured) == 0 {
427+
t.Fatal("no transport calls captured")
428+
}
429+
taskBytes, err := json.Marshal(mock.captured[0][0])
430+
if err != nil {
431+
t.Fatalf("marshal captured task: %v", err)
432+
}
433+
var payload map[string]any
434+
if err := json.Unmarshal(taskBytes, &payload); err != nil {
435+
t.Fatalf("unmarshal captured task: %v", err)
436+
}
437+
got, ok := payload["taskUUID"]
438+
if !ok {
439+
t.Fatal("taskUUID not present in submitted payload")
440+
}
441+
if s, _ := got.(string); s != wantUUID {
442+
t.Errorf("taskUUID: got %q, want %q", s, wantUUID)
443+
}
444+
}
445+
446+
// TestClientRun_UserProvidedTaskUUID_Invalid: an invalid UUID value must return
447+
// an error before any transport call is made.
448+
func TestClientRun_UserProvidedTaskUUID_Invalid(t *testing.T) {
449+
srv := inferenceSchemaServer(t, requestSchemaWithTaskType("imageInference", "sync"))
450+
451+
mock := &mockTransport{}
452+
453+
c := NewClient(mock, slog.Default())
454+
c.schemaBaseURLOverride = srv.URL + "/"
455+
456+
_, err := c.Run(context.Background(), testModelAIR, []string{"taskUUID=not-a-uuid"}, RunOptions{})
457+
if err == nil {
458+
t.Fatal("expected error for invalid taskUUID, got nil")
459+
}
460+
if !strings.Contains(err.Error(), "taskUUID") {
461+
t.Errorf("expected error to mention %q, got: %v", "taskUUID", err)
462+
}
463+
if mock.callCount != 0 {
464+
t.Errorf("expected 0 transport calls, got %d", mock.callCount)
465+
}
466+
}

internal/schema/schema.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ var ManagedFields = map[string]ManagedField{
106106
Hint: "use the --task-type flag instead",
107107
},
108108
"taskUUID": {
109-
Protected: true,
110-
Hint: "this field is system-generated and cannot be set manually",
109+
Protected: false,
111110
},
112111
"deliveryMethod": {
113112
Protected: false,

internal/schema/schema_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,10 @@ func TestIsProtected_ModelRejected(t *testing.T) {
629629
}
630630
}
631631

632-
func TestIsProtected_TaskUUIDRejected(t *testing.T) {
632+
func TestIsProtected_TaskUUIDAllowed(t *testing.T) {
633633
_, blocked := schema.IsProtected("taskUUID")
634-
if !blocked {
635-
t.Error("expected taskUUID to be protected")
634+
if blocked {
635+
t.Error("taskUUID must not be protected — callers may supply their own task identifier")
636636
}
637637
}
638638

0 commit comments

Comments
 (0)