From f3e992d0ff9d0237e9fd42181cac15a399631265 Mon Sep 17 00:00:00 2001 From: Peter Paul Schaffrath Date: Wed, 10 Jun 2026 11:57:39 +0200 Subject: [PATCH 1/4] feat(mexp): add model experiments wait handler --- services/modelexperiments/CHANGELOG.md | 4 + services/modelexperiments/VERSION | 2 +- services/modelexperiments/go.mod | 5 +- services/modelexperiments/v1api/wait/wait.go | 96 +++++ .../modelexperiments/v1api/wait/wait_test.go | 327 ++++++++++++++++++ 5 files changed, 432 insertions(+), 2 deletions(-) create mode 100644 services/modelexperiments/v1api/wait/wait.go create mode 100644 services/modelexperiments/v1api/wait/wait_test.go diff --git a/services/modelexperiments/CHANGELOG.md b/services/modelexperiments/CHANGELOG.md index 4794a642e..b77c0b1d8 100644 --- a/services/modelexperiments/CHANGELOG.md +++ b/services/modelexperiments/CHANGELOG.md @@ -1,2 +1,6 @@ +## v0.2.0 + +- **New**: STACKIT Model Experiments module wait handler added. + ## v0.1.0 - **New**: API for STACKIT modelexperiments \ No newline at end of file diff --git a/services/modelexperiments/VERSION b/services/modelexperiments/VERSION index 9ff151c5b..81fd7ba08 100644 --- a/services/modelexperiments/VERSION +++ b/services/modelexperiments/VERSION @@ -1 +1 @@ -v0.1.0 \ No newline at end of file +v0.2.0 \ No newline at end of file diff --git a/services/modelexperiments/go.mod b/services/modelexperiments/go.mod index 6ea1a4c6f..b62e53f0e 100644 --- a/services/modelexperiments/go.mod +++ b/services/modelexperiments/go.mod @@ -2,7 +2,10 @@ module github.com/stackitcloud/stackit-sdk-go/services/modelexperiments go 1.25 -require github.com/stackitcloud/stackit-sdk-go/core v0.26.0 +require ( + github.com/google/go-cmp v0.7.0 + github.com/stackitcloud/stackit-sdk-go/core v0.26.0 +) require ( github.com/golang-jwt/jwt/v5 v5.3.1 // indirect diff --git a/services/modelexperiments/v1api/wait/wait.go b/services/modelexperiments/v1api/wait/wait.go new file mode 100644 index 000000000..4541684a3 --- /dev/null +++ b/services/modelexperiments/v1api/wait/wait.go @@ -0,0 +1,96 @@ +package wait + +import ( + "context" + "errors" + "net/http" + "time" + + "github.com/stackitcloud/stackit-sdk-go/core/wait" + modelexperiments "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api" +) + +const ( + INSTANCESTATE_ACTIVE = "active" + INSTANCESTATE_IMPAIRED = "impaired" + + TOKENSTATE_ACTIVE = "active" +) + +// CreateMExpInstanceWaitHandler will wait for creation of Model Experiments instance +func CreateMExpInstanceWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceResponse, modelexperiments.InstanceState]{ + FetchInstance: a.GetInstance(ctx, projectId, region, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceResponse) (modelexperiments.InstanceState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Instance.State, nil + }, + ActiveState: []modelexperiments.InstanceState{modelexperiments.INSTANCESTATE_ACTIVE}, + ErrorState: []modelexperiments.InstanceState{modelexperiments.INSTANCESTATE_IMPAIRED}, + } + handler := wait.New(waitConfig.Wait()) + + handler.SetTimeout(10 * time.Minute) + + return handler +} + +// DeleteMExpInstanceWaitHandler will wait for deletion of Model Experiments instance +func DeleteMExpInstanceWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceResponse, modelexperiments.InstanceState]{ + FetchInstance: a.GetInstance(ctx, projectId, region, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceResponse) (modelexperiments.InstanceState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Instance.State, nil + }, + DeleteHttpErrorStatusCodes: []int{http.StatusNotFound}, + } + handler := wait.New(waitConfig.Wait()) + + handler.SetTimeout(10 * time.Minute) + + return handler +} + +// CreateMExpTokenWait Handler will wait for creation of Model Experiments instance token +func CreateMExpInstanceTokenWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId, tokenId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceTokenResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceTokenResponse, modelexperiments.TokenState]{ + FetchInstance: a.GetInstanceToken(ctx, projectId, region, tokenId, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceTokenResponse) (modelexperiments.TokenState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Token.State, nil + }, + ActiveState: []modelexperiments.TokenState{modelexperiments.TOKENSTATE_ACTIVE}, + ErrorState: []modelexperiments.TokenState{}, + } + handler := wait.New(waitConfig.Wait()) + + handler.SetTimeout(10 * time.Minute) + + return handler +} + +// DeleteMExpInstanceTokenWaitHandler will wait for deletion of Model Experiments instance token +func DeleteMExpInstanceTokenWaitHandler(ctx context.Context, a modelexperiments.DefaultAPI, region, projectId, instanceId, tokenId string) *wait.AsyncActionHandler[modelexperiments.GetInstanceTokenResponse] { + waitConfig := wait.WaiterHelper[modelexperiments.GetInstanceTokenResponse, modelexperiments.TokenState]{ + FetchInstance: a.GetInstanceToken(ctx, projectId, region, tokenId, instanceId).Execute, + GetState: func(response *modelexperiments.GetInstanceTokenResponse) (modelexperiments.TokenState, error) { + if response == nil { + return "", errors.New("empty response") + } + return response.Token.State, nil + }, + DeleteHttpErrorStatusCodes: []int{http.StatusNotFound}, + } + handler := wait.New(waitConfig.Wait()) + + handler.SetTimeout(10 * time.Minute) + + return handler +} diff --git a/services/modelexperiments/v1api/wait/wait_test.go b/services/modelexperiments/v1api/wait/wait_test.go new file mode 100644 index 000000000..9d2c61972 --- /dev/null +++ b/services/modelexperiments/v1api/wait/wait_test.go @@ -0,0 +1,327 @@ +package wait + +import ( + "context" + "testing" + "testing/synctest" + "time" + + "github.com/google/go-cmp/cmp" + + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + modelexperiments "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api" +) + +type mockSettings struct { + getFails bool + statusCode int + resourceState string +} + +func newAPIMock(settings *mockSettings) modelexperiments.DefaultAPI { + return &modelexperiments.DefaultAPIServiceMock{ + GetInstanceExecuteMock: utils.Ptr(func(_ modelexperiments.ApiGetInstanceRequest) (*modelexperiments.GetInstanceResponse, error) { + if settings.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: settings.statusCode, + } + } + + return &modelexperiments.GetInstanceResponse{ + Instance: modelexperiments.Instance{ + State: modelexperiments.InstanceState(settings.resourceState), + }, + }, nil + }), + GetInstanceTokenExecuteMock: utils.Ptr(func(_ modelexperiments.ApiGetInstanceTokenRequest) (*modelexperiments.GetInstanceTokenResponse, error) { + if settings.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: settings.statusCode, + } + } + + return &modelexperiments.GetInstanceTokenResponse{ + Token: modelexperiments.TokenMetadata{ + State: modelexperiments.TokenState(settings.resourceState), + }, + }, nil + }), + } +} + +func TestCreateMExpInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "create_succeeded", + getFails: false, + statusCode: 202, + resourceState: string(modelexperiments.INSTANCESTATE_ACTIVE), + wantErr: false, + wantResp: true, + }, + { + desc: "get_fails", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + { + desc: "not_found", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: true, + wantResp: false, + }, + { + desc: "impaired", + getFails: false, + statusCode: 200, + resourceState: string(modelexperiments.INSTANCESTATE_IMPAIRED), + wantErr: true, + wantResp: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceResponse{ + Instance: modelexperiments.Instance{ + State: modelexperiments.InstanceState(tt.resourceState), + }, + } + } + + handler := CreateMExpInstanceWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestDeleteMExpInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "delete_succeeded", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: false, + wantResp: false, + }, + { + desc: "delete_in_progress", + getFails: false, + statusCode: 200, + resourceState: string(modelexperiments.INSTANCESTATE_DELETING), + wantErr: true, // Should timeout since delete is not complete + wantResp: false, + }, + { + desc: "get_fails_with_other_error", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceResponse{ + Instance: modelexperiments.Instance{ + State: modelexperiments.InstanceState(tt.resourceState), + }, + } + } + + handler := DeleteMExpInstanceWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestCreateMExpInstanceTokenWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "create_succeeded", + getFails: false, + statusCode: 202, + resourceState: string(modelexperiments.TOKENSTATE_ACTIVE), + wantErr: false, + wantResp: true, + }, + { + desc: "get_fails", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + { + desc: "not_found", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceTokenResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceTokenResponse{ + Token: modelexperiments.TokenMetadata{ + State: modelexperiments.TokenState(tt.resourceState), + }, + } + } + + handler := CreateMExpInstanceTokenWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId", "tid") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestDeleteMExpInstanceTokenWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + statusCode int + resourceState string + wantErr bool + wantResp bool + }{ + { + desc: "delete_succeeded", + getFails: true, + statusCode: 404, + resourceState: "", + wantErr: false, + wantResp: false, + }, + { + desc: "delete_in_progress", + getFails: false, + statusCode: 200, + resourceState: string(modelexperiments.TOKENSTATE_DELETING), + wantErr: true, // Should timeout since delete is not complete + wantResp: false, + }, + { + desc: "get_fails_with_other_error", + getFails: true, + statusCode: 500, + resourceState: "", + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(&mockSettings{ + getFails: tt.getFails, + statusCode: tt.statusCode, + resourceState: tt.resourceState, + }) + + var wantRes *modelexperiments.GetInstanceTokenResponse + if tt.wantResp { + wantRes = &modelexperiments.GetInstanceTokenResponse{ + Token: modelexperiments.TokenMetadata{ + State: modelexperiments.TokenState(tt.resourceState), + }, + } + } + + handler := DeleteMExpInstanceTokenWaitHandler(context.Background(), apiClient, "region", "pid", "instanceId", "tid") + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} From 557a03ed0b9b801de1a85111bcb838b2b769c4ec Mon Sep 17 00:00:00 2001 From: Peter Paul Schaffrath Date: Wed, 10 Jun 2026 13:18:31 +0200 Subject: [PATCH 2/4] feat(mexp): add example and update changelog --- CHANGELOG.md | 2 + examples/modelexperiments/go.mod | 16 ++++++ examples/modelexperiments/go.sum | 8 +++ examples/modelexperiments/modelexperiments.go | 53 +++++++++++++++++++ services/modelexperiments/CHANGELOG.md | 1 - 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 examples/modelexperiments/go.mod create mode 100644 examples/modelexperiments/go.sum create mode 100644 examples/modelexperiments/modelexperiments.go diff --git a/CHANGELOG.md b/CHANGELOG.md index be03af0fe..baa965319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -279,6 +279,8 @@ - [v0.30.0](services/mariadb/CHANGELOG.md#v0300) - **Feature:** Introduce enums for various attributes - `modelexperiments`: + - [v.0.2.0](services/modelexperiments/CHANGELOG.md#v020) + - **New**: STACKIT Model Experiments module wait handler added. - [v0.1.0](services/modelexperiments/CHANGELOG.md#v010) - **New**: API for STACKIT modelexperiments - `modelserving`: diff --git a/examples/modelexperiments/go.mod b/examples/modelexperiments/go.mod new file mode 100644 index 000000000..5d045f282 --- /dev/null +++ b/examples/modelexperiments/go.mod @@ -0,0 +1,16 @@ +module github.com/stackitcloud/stackit-sdk-go/examples/modelexperiments + +go 1.25 + +// This is not needed in production. This is only here to point the golangci linter to the local version instead of the last release on GitHub. +replace github.com/stackitcloud/stackit-sdk-go/services/modelexperiments => ../../services/modelexperiments + +require ( + github.com/stackitcloud/stackit-sdk-go/core v0.26.0 + github.com/stackitcloud/stackit-sdk-go/services/modelexperiments v0.2.0 +) + +require ( + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect +) diff --git a/examples/modelexperiments/go.sum b/examples/modelexperiments/go.sum new file mode 100644 index 000000000..3712a0c87 --- /dev/null +++ b/examples/modelexperiments/go.sum @@ -0,0 +1,8 @@ +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10cz4l0KM2L6hqYBH2QA= +github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA= diff --git a/examples/modelexperiments/modelexperiments.go b/examples/modelexperiments/modelexperiments.go new file mode 100644 index 000000000..c5e603f87 --- /dev/null +++ b/examples/modelexperiments/modelexperiments.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + "fmt" + "os" + + modelexperiments "github.com/stackitcloud/stackit-sdk-go/services/modelexperiments/v1api" +) + +func main() { + projectId := "PROJECT_ID" // the uuid of your STACKIT project + region := "eu01" + instanceName := "instance" + description := "description" + + modelexperimentsClient, err := modelexperiments.NewAPIClient() + if err != nil { + fmt.Fprintf(os.Stderr, "Creating API client: %v\n", err) + os.Exit(1) + } + + // Create a instance + createInstancePayload := modelexperiments.CreateInstancePayload{ + Name: instanceName, + Description: &description, + } + createInstanceResp, err := modelexperimentsClient.DefaultAPI.CreateInstance(context.Background(), projectId, region).CreateInstancePayload(createInstancePayload).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `CreateInstance`: %v\n", err) + } else { + fmt.Printf("Created instance with instance id \"%s\".\n", createInstanceResp.Instance.Id) + } + + // Get the created instance + getInstanceResp, err := modelexperimentsClient.DefaultAPI.GetInstance(context.Background(), projectId, region, createInstanceResp.Instance.Id).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `GetInstance`: %v\n", err) + } else { + fmt.Printf("Retrieved instance: %+v\n", getInstanceResp.Instance) + } + + // Create a token for the instance + createTokenPayload := modelexperiments.CreateInstanceTokenPayload{ + Name: "token-name", + } + createTokenResp, err := modelexperimentsClient.DefaultAPI.CreateInstanceToken(context.Background(), projectId, region, createInstanceResp.Instance.Id).CreateInstanceTokenPayload(createTokenPayload).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `CreateToken`: %v\n", err) + } else { + fmt.Printf("Created token: %+v\n", createTokenResp.Token) + } +} diff --git a/services/modelexperiments/CHANGELOG.md b/services/modelexperiments/CHANGELOG.md index b77c0b1d8..55a0c150e 100644 --- a/services/modelexperiments/CHANGELOG.md +++ b/services/modelexperiments/CHANGELOG.md @@ -1,5 +1,4 @@ ## v0.2.0 - - **New**: STACKIT Model Experiments module wait handler added. ## v0.1.0 From adfed35c2100ecf7fd69f3fed51fe0347edcc3e2 Mon Sep 17 00:00:00 2001 From: Peter Paul Schaffrath Date: Wed, 10 Jun 2026 20:51:46 +0200 Subject: [PATCH 3/4] feat(mexp): fix linting --- examples/modelexperiments/go.mod | 4 ++-- go.work | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/modelexperiments/go.mod b/examples/modelexperiments/go.mod index 5d045f282..d3350d45c 100644 --- a/examples/modelexperiments/go.mod +++ b/examples/modelexperiments/go.mod @@ -6,8 +6,8 @@ go 1.25 replace github.com/stackitcloud/stackit-sdk-go/services/modelexperiments => ../../services/modelexperiments require ( - github.com/stackitcloud/stackit-sdk-go/core v0.26.0 - github.com/stackitcloud/stackit-sdk-go/services/modelexperiments v0.2.0 + github.com/stackitcloud/stackit-sdk-go/core v0.26.0 + github.com/stackitcloud/stackit-sdk-go/services/modelexperiments v0.2.0 ) require ( diff --git a/go.work b/go.work index 9d64b69f9..7c0013e6d 100644 --- a/go.work +++ b/go.work @@ -20,6 +20,7 @@ use ( ./examples/logs ./examples/mariadb ./examples/middleware + ./examples/modelexperiments ./examples/mongodbflex ./examples/objectstorage ./examples/observability From 6cffbe60a2d8dceac65ee9d62e713df48d371df5 Mon Sep 17 00:00:00 2001 From: Peter Paul Schaffrath Date: Wed, 10 Jun 2026 21:04:34 +0200 Subject: [PATCH 4/4] feat(mexp): tidying dependencies --- examples/modelexperiments/go.mod | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/modelexperiments/go.mod b/examples/modelexperiments/go.mod index d3350d45c..7b5fb9fbf 100644 --- a/examples/modelexperiments/go.mod +++ b/examples/modelexperiments/go.mod @@ -5,12 +5,10 @@ go 1.25 // This is not needed in production. This is only here to point the golangci linter to the local version instead of the last release on GitHub. replace github.com/stackitcloud/stackit-sdk-go/services/modelexperiments => ../../services/modelexperiments -require ( - github.com/stackitcloud/stackit-sdk-go/core v0.26.0 - github.com/stackitcloud/stackit-sdk-go/services/modelexperiments v0.2.0 -) +require github.com/stackitcloud/stackit-sdk-go/services/modelexperiments v0.2.0 require ( github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/stackitcloud/stackit-sdk-go/core v0.26.0 // indirect )