From e5ce0200c8b6eede58f58e23c15e49a1949362de Mon Sep 17 00:00:00 2001 From: vikingowl Date: Fri, 3 Apr 2026 18:53:15 +0200 Subject: [PATCH] =?UTF-8?q?feat!:=20sync=20with=20Python=20SDK=20v2.3.0=20?= =?UTF-8?q?=E2=80=94=20workflow=20registration=20model=20+=20remove=20work?= =?UTF-8?q?ers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CodeDefinition, SignalDefinition, QueryDefinition, UpdateDefinition types for workflow interface metadata. Update Registration struct with DeploymentID, Definition, and CompatibleWithChatAssistant fields. Deprecate TaskQueue in favor of DeploymentID. BREAKING CHANGE: Remove GetWorkflowWorkerInfo and workflow.WorkerInfo — the /v1/workflows/workers/whoami endpoint was removed upstream. --- CHANGELOG.md | 31 +++++++ README.md | 6 +- mistral.go | 2 +- workflow/definition.go | 36 ++++++++ workflow/definition_test.go | 166 ++++++++++++++++++++++++++++++++++++ workflow/registration.go | 15 ++-- workflows_workers.go | 16 ---- workflows_workers_test.go | 35 -------- 8 files changed, 243 insertions(+), 64 deletions(-) create mode 100644 workflow/definition.go create mode 100644 workflow/definition_test.go delete mode 100644 workflows_workers.go delete mode 100644 workflows_workers_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3997a7a..b5c94e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ +## v1.3.0 — 2026-04-03 + +Upstream sync with Python SDK v2.3.0. Updates workflow registration model +to reflect the managed deployment architecture and removes the deprecated +workers endpoint. + +### Added + +- **`workflow.CodeDefinition`** — workflow interface metadata type with + input/output schemas, signal/query/update handler definitions, + determinism flag, and execution timeout. +- **`workflow.SignalDefinition`**, **`QueryDefinition`**, + **`UpdateDefinition`** — handler descriptor types. +- **`Registration.Definition`** — code definition field on workflow + registrations. +- **`Registration.DeploymentID`** — replaces the worker/task-queue model + with managed deployment references. +- **`Registration.CompatibleWithChatAssistant`** — flag for chat assistant + compatibility. + +### Deprecated + +- **`Registration.TaskQueue`** — use `DeploymentID` instead. Will be + removed in a future release. + +### Removed (breaking) + +- **`GetWorkflowWorkerInfo`** — the `/v1/workflows/workers/whoami` endpoint + was removed upstream. +- **`workflow.WorkerInfo`** — type no longer exists in the API. + ## v1.2.1 — 2026-04-03 Move module path to `github.com/VikingOwl91/mistral-go-sdk` for public diff --git a/README.md b/README.md index b4483d0..bf18fb8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The most complete Go client for the [Mistral AI API](https://docs.mistral.ai/). **Zero dependencies.** The entire SDK — including tests — uses only the Go standard library. No `go.sum`, no transitive dependency tree to audit, no version conflicts, no supply chain risk. -**Full API coverage.** 166 methods across every Mistral endpoint — including Workflows, Connectors, Audio Speech/Voices, Conversations, Agents CRUD, Libraries, OCR, Observability, Fine-tuning, and Batch Jobs. No other Go SDK covers Workflows, Conversations, Connectors, or Observability. +**Full API coverage.** 165 methods across every Mistral endpoint — including Workflows, Connectors, Audio Speech/Voices, Conversations, Agents CRUD, Libraries, OCR, Observability, Fine-tuning, and Batch Jobs. No other Go SDK covers Workflows, Conversations, Connectors, or Observability. **Typed streaming.** A generic pull-based `Stream[T]` iterator — no channels, no goroutines, no leaks. Just `Next()` / `Current()` / `Err()` / `Close()`. @@ -132,7 +132,7 @@ for stream.Next() { ## API Coverage -166 public methods on `Client`, grouped by domain: +165 public methods on `Client`, grouped by domain: | Domain | Methods | |--------|---------| @@ -167,7 +167,6 @@ for stream.Next() { | **Workflows (metrics)** | `GetWorkflowMetrics` | | **Workflows (runs)** | `ListWorkflowRuns`, `GetWorkflowRun`, `GetWorkflowRunHistory` | | **Workflows (schedules)** | `ListWorkflowSchedules`, `ScheduleWorkflow`, `UnscheduleWorkflow` | -| **Workflows (workers)** | `GetWorkflowWorkerInfo` | ## Comparison @@ -241,6 +240,7 @@ as its upstream reference for API surface and type definitions. | SDK Version | Upstream Python SDK | |-------------|---------------------| +| v1.3.0 | v2.3.0 | | v1.2.1 | v2.2.0 | | v1.2.0 | v2.2.0 | | v1.1.0 | v2.1.3 | diff --git a/mistral.go b/mistral.go index 5c86808..1f346bf 100644 --- a/mistral.go +++ b/mistral.go @@ -6,7 +6,7 @@ import ( ) // Version is the SDK version string. -const Version = "1.2.1" +const Version = "1.3.0" const ( defaultBaseURL = "https://api.mistral.ai" diff --git a/workflow/definition.go b/workflow/definition.go new file mode 100644 index 0000000..4e940c2 --- /dev/null +++ b/workflow/definition.go @@ -0,0 +1,36 @@ +package workflow + +// CodeDefinition describes a workflow's code-level interface: its input/output +// schemas, signal/query/update handlers, and execution constraints. +type CodeDefinition struct { + InputSchema map[string]any `json:"input_schema"` + OutputSchema map[string]any `json:"output_schema,omitempty"` + Signals []SignalDefinition `json:"signals,omitempty"` + Queries []QueryDefinition `json:"queries,omitempty"` + Updates []UpdateDefinition `json:"updates,omitempty"` + EnforceDeterminism bool `json:"enforce_determinism,omitempty"` + ExecutionTimeout *float64 `json:"execution_timeout,omitempty"` +} + +// SignalDefinition describes a signal handler on a workflow. +type SignalDefinition struct { + Name string `json:"name"` + InputSchema map[string]any `json:"input_schema"` + Description *string `json:"description,omitempty"` +} + +// QueryDefinition describes a query handler on a workflow. +type QueryDefinition struct { + Name string `json:"name"` + InputSchema map[string]any `json:"input_schema"` + Description *string `json:"description,omitempty"` + OutputSchema map[string]any `json:"output_schema,omitempty"` +} + +// UpdateDefinition describes an update handler on a workflow. +type UpdateDefinition struct { + Name string `json:"name"` + InputSchema map[string]any `json:"input_schema"` + Description *string `json:"description,omitempty"` + OutputSchema map[string]any `json:"output_schema,omitempty"` +} diff --git a/workflow/definition_test.go b/workflow/definition_test.go new file mode 100644 index 0000000..77ac49e --- /dev/null +++ b/workflow/definition_test.go @@ -0,0 +1,166 @@ +package workflow + +import ( + "encoding/json" + "testing" +) + +func TestCodeDefinition_RoundTrip(t *testing.T) { + raw := `{ + "input_schema": {"type": "object", "properties": {"prompt": {"type": "string"}}}, + "output_schema": {"type": "object", "properties": {"result": {"type": "string"}}}, + "signals": [ + {"name": "cancel", "input_schema": {"type": "object"}, "description": "Cancel the workflow"} + ], + "queries": [ + {"name": "status", "input_schema": {"type": "object"}, "description": "Get status", "output_schema": {"type": "string"}} + ], + "updates": [ + {"name": "set_priority", "input_schema": {"type": "object", "properties": {"level": {"type": "integer"}}}, "description": "Set priority", "output_schema": null} + ], + "enforce_determinism": true, + "execution_timeout": 3600.5 + }` + + var def CodeDefinition + if err := json.Unmarshal([]byte(raw), &def); err != nil { + t.Fatal(err) + } + + if def.InputSchema == nil { + t.Fatal("InputSchema is nil") + } + if def.OutputSchema == nil { + t.Fatal("OutputSchema is nil") + } + if len(def.Signals) != 1 { + t.Fatalf("expected 1 signal, got %d", len(def.Signals)) + } + if def.Signals[0].Name != "cancel" { + t.Errorf("signal name = %q, want cancel", def.Signals[0].Name) + } + if def.Signals[0].Description == nil || *def.Signals[0].Description != "Cancel the workflow" { + t.Errorf("signal description wrong") + } + if len(def.Queries) != 1 { + t.Fatalf("expected 1 query, got %d", len(def.Queries)) + } + if def.Queries[0].Name != "status" { + t.Errorf("query name = %q, want status", def.Queries[0].Name) + } + if def.Queries[0].OutputSchema == nil { + t.Error("query OutputSchema is nil, expected non-nil") + } + if len(def.Updates) != 1 { + t.Fatalf("expected 1 update, got %d", len(def.Updates)) + } + if def.Updates[0].Name != "set_priority" { + t.Errorf("update name = %q, want set_priority", def.Updates[0].Name) + } + if def.EnforceDeterminism != true { + t.Error("EnforceDeterminism should be true") + } + if def.ExecutionTimeout == nil || *def.ExecutionTimeout != 3600.5 { + t.Errorf("ExecutionTimeout = %v, want 3600.5", def.ExecutionTimeout) + } + + // Re-marshal and verify round-trip + out, err := json.Marshal(def) + if err != nil { + t.Fatal(err) + } + var def2 CodeDefinition + if err := json.Unmarshal(out, &def2); err != nil { + t.Fatal(err) + } + if len(def2.Signals) != 1 || def2.Signals[0].Name != "cancel" { + t.Error("round-trip failed for signals") + } + if def2.EnforceDeterminism != true { + t.Error("round-trip failed for enforce_determinism") + } +} + +func TestCodeDefinition_MinimalFields(t *testing.T) { + raw := `{"input_schema": {"type": "object"}}` + + var def CodeDefinition + if err := json.Unmarshal([]byte(raw), &def); err != nil { + t.Fatal(err) + } + if def.InputSchema == nil { + t.Fatal("InputSchema is nil") + } + if def.OutputSchema != nil { + t.Errorf("OutputSchema should be nil, got %v", def.OutputSchema) + } + if def.Signals != nil { + t.Errorf("Signals should be nil, got %v", def.Signals) + } + if def.EnforceDeterminism != false { + t.Error("EnforceDeterminism should default to false") + } + if def.ExecutionTimeout != nil { + t.Errorf("ExecutionTimeout should be nil, got %v", def.ExecutionTimeout) + } +} + +func TestRegistration_NewFields(t *testing.T) { + raw := `{ + "id": "reg-1", + "workflow_id": "wf-1", + "task_queue": "legacy-queue", + "deployment_id": "dep-abc", + "compatible_with_chat_assistant": true, + "definition": { + "input_schema": {"type": "object"}, + "enforce_determinism": false + }, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-02T00:00:00Z" + }` + + var reg Registration + if err := json.Unmarshal([]byte(raw), ®); err != nil { + t.Fatal(err) + } + if reg.ID != "reg-1" { + t.Errorf("ID = %q", reg.ID) + } + if reg.DeploymentID == nil || *reg.DeploymentID != "dep-abc" { + t.Errorf("DeploymentID = %v, want dep-abc", reg.DeploymentID) + } + if reg.CompatibleWithChatAssistant != true { + t.Error("CompatibleWithChatAssistant should be true") + } + if reg.Definition == nil { + t.Fatal("Definition is nil") + } + if reg.Definition.InputSchema == nil { + t.Error("Definition.InputSchema is nil") + } + // TaskQueue still works for backward compat + if reg.TaskQueue != "legacy-queue" { + t.Errorf("TaskQueue = %q, want legacy-queue", reg.TaskQueue) + } +} + +func TestRegistration_NullDeploymentID(t *testing.T) { + raw := `{ + "id": "reg-2", + "workflow_id": "wf-2", + "task_queue": "q", + "definition": {"input_schema": {"type": "object"}} + }` + + var reg Registration + if err := json.Unmarshal([]byte(raw), ®); err != nil { + t.Fatal(err) + } + if reg.DeploymentID != nil { + t.Errorf("DeploymentID should be nil, got %v", reg.DeploymentID) + } + if reg.CompatibleWithChatAssistant != false { + t.Error("CompatibleWithChatAssistant should default to false") + } +} diff --git a/workflow/registration.go b/workflow/registration.go index be14848..61eab36 100644 --- a/workflow/registration.go +++ b/workflow/registration.go @@ -2,8 +2,12 @@ package workflow // Registration represents a workflow registration. type Registration struct { - ID string `json:"id"` - WorkflowID string `json:"workflow_id"` + ID string `json:"id"` + WorkflowID string `json:"workflow_id"` + Definition *CodeDefinition `json:"definition,omitempty"` + DeploymentID *string `json:"deployment_id,omitempty"` + CompatibleWithChatAssistant bool `json:"compatible_with_chat_assistant,omitempty"` + // Deprecated: use DeploymentID instead. Will be removed in a future release. TaskQueue string `json:"task_queue"` Workflow *Workflow `json:"workflow,omitempty"` CreatedAt string `json:"created_at"` @@ -35,10 +39,3 @@ type RegistrationGetParams struct { WithWorkflow *bool IncludeShared *bool } - -// WorkerInfo holds information about the current worker. -type WorkerInfo struct { - SchedulerURL string `json:"scheduler_url"` - Namespace string `json:"namespace"` - TLS bool `json:"tls"` -} diff --git a/workflows_workers.go b/workflows_workers.go deleted file mode 100644 index 97d9bb5..0000000 --- a/workflows_workers.go +++ /dev/null @@ -1,16 +0,0 @@ -package mistral - -import ( - "context" - - "github.com/VikingOwl91/mistral-go-sdk/workflow" -) - -// GetWorkflowWorkerInfo retrieves information about the current worker. -func (c *Client) GetWorkflowWorkerInfo(ctx context.Context) (*workflow.WorkerInfo, error) { - var resp workflow.WorkerInfo - if err := c.doJSON(ctx, "GET", "/v1/workflows/workers/whoami", nil, &resp); err != nil { - return nil, err - } - return &resp, nil -} diff --git a/workflows_workers_test.go b/workflows_workers_test.go deleted file mode 100644 index b77b7a2..0000000 --- a/workflows_workers_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package mistral - -import ( - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "testing" -) - -func TestGetWorkflowWorkerInfo_Success(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/v1/workflows/workers/whoami" { - t.Errorf("got path %s", r.URL.Path) - } - json.NewEncoder(w).Encode(map[string]any{ - "scheduler_url": "https://scheduler.mistral.ai", - "namespace": "default", - "tls": true, - }) - })) - defer server.Close() - - client := NewClient("key", WithBaseURL(server.URL)) - info, err := client.GetWorkflowWorkerInfo(context.Background()) - if err != nil { - t.Fatal(err) - } - if info.Namespace != "default" { - t.Errorf("got namespace %q", info.Namespace) - } - if !info.TLS { - t.Error("expected tls=true") - } -}