From 7be398366d05aef2a51818ab9aaed8d43899f3a9 Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Thu, 13 Nov 2025 00:31:57 +0000 Subject: [PATCH 1/2] ## Go SDK Changes Detected: * `Livepeer.Generate.LiveVideoToVideo()`: * `request.Request` **Changed** * `response` **Changed** --- .gitignore | 5 + .speakeasy/gen.lock | 66 +- .speakeasy/gen.yaml | 21 +- .speakeasy/workflow.lock | 16 +- README.md | 83 +- RELEASES.md | 12 +- USAGE.md | 11 +- codeSamples.yaml | 340 +++- .../components/livevideotovideoparams.md | 5 +- .../components/livevideotovideoresponse.md | 4 +- docs/sdks/generate/README.md | 121 +- docs/sdks/livepeer/README.md | 7 - generate.go | 349 ++-- go.mod | 10 +- go.sum | 12 +- internal/config/sdkconfiguration.go | 33 + internal/hooks/hooks.go | 17 +- internal/utils/form.go | 29 +- internal/utils/headers.go | 12 + internal/utils/json.go | 348 +++- internal/utils/pathparams.go | 15 +- internal/utils/queryparams.go | 38 +- internal/utils/requestbody.go | 54 +- internal/utils/retries.go | 52 +- internal/utils/utils.go | 43 +- livepeer.go | 52 +- models/components/apierror.go | 6 +- models/components/audioresponse.go | 6 +- models/components/bodygenaudiototext.go | 32 +- models/components/bodygenimagetoimage.go | 86 +- models/components/bodygenimagetotext.go | 32 +- models/components/bodygenimagetovideo.go | 74 +- models/components/bodygensegmentanything2.go | 68 +- models/components/bodygenupscale.go | 50 +- models/components/chunk.go | 12 +- models/components/httpmetadata.go | 12 +- models/components/imageresponse.go | 6 +- models/components/imagetotextresponse.go | 6 +- models/components/livevideotovideoparams.go | 65 +- models/components/livevideotovideoresponse.go | 44 +- models/components/llmchoice.go | 26 +- models/components/llmmessage.go | 12 +- models/components/llmrequest.go | 44 +- models/components/llmresponse.go | 30 +- models/components/llmtokenusage.go | 18 +- models/components/masksresponse.go | 18 +- models/components/media.go | 18 +- models/components/mediaurl.go | 6 +- models/components/security.go | 6 +- models/components/textresponse.go | 12 +- models/components/texttoimageparams.go | 68 +- models/components/texttospeechparams.go | 20 +- models/components/validationerror.go | 26 +- models/components/videoresponse.go | 6 +- models/operations/genaudiototext.go | 12 +- models/operations/genimagetoimage.go | 12 +- models/operations/genimagetotext.go | 12 +- models/operations/genimagetovideo.go | 12 +- models/operations/genlivevideotovideo.go | 12 +- models/operations/genllm.go | 12 +- models/operations/gensegmentanything2.go | 12 +- models/operations/gentexttoimage.go | 12 +- models/operations/gentexttospeech.go | 12 +- models/operations/genupscale.go | 12 +- optionalnullable/optionalnullable.go | 233 +++ optionalnullable/optionalnullable_test.go | 1806 +++++++++++++++++ types/decimal.go | 20 - 67 files changed, 3751 insertions(+), 992 deletions(-) delete mode 100644 docs/sdks/livepeer/README.md create mode 100644 internal/config/sdkconfiguration.go create mode 100644 optionalnullable/optionalnullable.go create mode 100644 optionalnullable/optionalnullable_test.go delete mode 100644 types/decimal.go diff --git a/.gitignore b/.gitignore index fde225b..158feec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +.DS_Store +**/.speakeasy/temp/ +**/.speakeasy/logs/ +.env +.env.local .speakeasy/reports # .gitignore # IDEs diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index d1c65f0..289f678 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,31 +1,31 @@ lockVersion: 2.0.0 id: 98621ff3-f200-4a5c-9c69-bc1fe845e39c management: - docChecksum: 6657dd3e876a909472f364dc1fe34d72 - docVersion: 0.0.0 - speakeasyVersion: 1.480.0 - generationVersion: 2.499.0 - releaseVersion: 0.7.1 - configChecksum: 283c20636f8ea078f683fa83624f7710 + docChecksum: ccf6085748e452cf3e6a2d9f287f99de + docVersion: 0.13.11 + speakeasyVersion: 1.656.1 + generationVersion: 2.753.1 + releaseVersion: 0.8.0 + configChecksum: 038b1f3814a40d4e08e61b73b1672edb repoURL: https://github.com/livepeer/livepeer-ai-go.git installationURL: https://github.com/livepeer/livepeer-ai-go features: go: additionalDependencies: 0.1.0 - constsAndDefaults: 0.1.6 - core: 3.6.11 + constsAndDefaults: 0.1.12 + core: 3.13.2 defaultEnabledRetries: 0.2.0 envVarSecurityUsage: 0.3.2 - globalSecurity: 2.82.10 + globalSecurity: 2.82.12 globalSecurityCallbacks: 0.1.0 globalSecurityFlattening: 0.1.0 - globalServerURLs: 2.82.2 + globalServerURLs: 2.83.0 intellisenseMarkdownSupport: 0.1.0 nameOverrides: 2.81.2 responseFormat: 0.1.2 - retries: 2.83.2 - sdkHooks: 0.1.0 - unions: 2.85.10 + retries: 2.84.2 + sdkHooks: 0.2.0 + unions: 2.87.1 uploadStreams: 0.1.0 generatedFiles: - .gitattributes @@ -120,9 +120,9 @@ generatedFiles: - docs/models/sdkerrors/httperror.md - docs/models/sdkerrors/httpvalidationerror.md - docs/sdks/generate/README.md - - docs/sdks/livepeer/README.md - generate.go - go.mod + - internal/config/sdkconfiguration.go - internal/hooks/hooks.go - internal/utils/contenttype.go - internal/utils/env.go @@ -138,11 +138,12 @@ generatedFiles: - livepeer.go - models/operations/options.go - models/sdkerrors/sdkerror.go + - optionalnullable/optionalnullable.go + - optionalnullable/optionalnullable_test.go - retry/config.go - types/bigint.go - types/date.go - types/datetime.go - - types/decimal.go - types/pointers.go examples: genTextToImage: @@ -151,7 +152,7 @@ examples: application/json: {"model_id": "", "loras": "", "prompt": "", "height": 576, "width": 1024, "guidance_scale": 7.5, "negative_prompt": "", "safety_check": true, "num_inference_steps": 50, "num_images_per_prompt": 1} responses: "200": - application/json: {"images": [{"url": "https://hateful-cruelty.name", "seed": 857392, "nsfw": true}]} + application/json: {"images": []} "400": application/json: {"detail": {"msg": ""}} "422": @@ -161,10 +162,10 @@ examples: genImageToImage: speakeasy-default-gen-image-to-image: requestBody: - multipart/form-data: {"prompt": "", "image": {"": "x-file: example.file"}, "model_id": "", "loras": "", "strength": 0.8, "guidance_scale": 7.5, "image_guidance_scale": 1.5, "negative_prompt": "", "safety_check": true, "num_inference_steps": 100, "num_images_per_prompt": 1} + multipart/form-data: {"prompt": "", "image": "x-file: example.file", "model_id": "", "loras": "", "strength": 0.8, "guidance_scale": 7.5, "image_guidance_scale": 1.5, "negative_prompt": "", "safety_check": true, "num_inference_steps": 100, "num_images_per_prompt": 1} responses: "200": - application/json: {"images": [{"url": "https://selfish-operating.name/", "seed": 976514, "nsfw": false}]} + application/json: {"images": []} "400": application/json: {"detail": {"msg": ""}} "422": @@ -174,10 +175,10 @@ examples: genImageToVideo: speakeasy-default-gen-image-to-video: requestBody: - multipart/form-data: {"image": {"": "x-file: example.file"}, "model_id": "", "height": 576, "width": 1024, "fps": 6, "motion_bucket_id": 127, "noise_aug_strength": 0.02, "safety_check": true, "num_inference_steps": 25} + multipart/form-data: {"image": "x-file: example.file", "model_id": "", "height": 576, "width": 1024, "fps": 6, "motion_bucket_id": 127, "noise_aug_strength": 0.02, "safety_check": true, "num_inference_steps": 25} responses: "200": - application/json: {"images": [{"url": "https://low-handover.name/", "seed": 87160, "nsfw": true}]} + application/json: {"images": []} "400": application/json: {"detail": {"msg": ""}} "422": @@ -187,10 +188,10 @@ examples: genUpscale: speakeasy-default-gen-upscale: requestBody: - multipart/form-data: {"prompt": "", "image": {"": "x-file: example.file"}, "model_id": "", "safety_check": true, "num_inference_steps": 75} + multipart/form-data: {"prompt": "", "image": "x-file: example.file", "model_id": "", "safety_check": true, "num_inference_steps": 75} responses: "200": - application/json: {"images": [{"url": "https://bogus-typewriter.net", "seed": 311567, "nsfw": false}]} + application/json: {"images": []} "400": application/json: {"detail": {"msg": ""}} "422": @@ -200,10 +201,10 @@ examples: genAudioToText: speakeasy-default-gen-audio-to-text: requestBody: - multipart/form-data: {"audio": {"": "x-file: example.file"}, "model_id": "", "return_timestamps": "true"} + multipart/form-data: {"audio": "x-file: example.file", "model_id": "", "return_timestamps": "true"} responses: "200": - application/json: {"text": "", "chunks": [{"timestamp": ["", ""], "text": ""}, {"timestamp": [], "text": ""}]} + application/json: {"text": "", "chunks": []} "400": application/json: {"detail": {"msg": ""}} "422": @@ -213,7 +214,7 @@ examples: genSegmentAnything2: speakeasy-default-gen-segment-anything2: requestBody: - multipart/form-data: {"image": {"": "x-file: example.file"}, "model_id": "", "multimask_output": true, "return_logits": true, "normalize_coords": true} + multipart/form-data: {"image": "x-file: example.file", "model_id": "", "multimask_output": true, "return_logits": true, "normalize_coords": true} responses: "200": application/json: {"masks": "", "scores": "", "logits": ""} @@ -226,10 +227,10 @@ examples: genLLM: speakeasy-default-gen-LLM: requestBody: - application/json: {"messages": [], "model": "", "temperature": 0.7, "max_tokens": 256, "top_p": 1, "top_k": -1, "stream": false} + application/json: {"messages": [{"role": "", "content": ""}], "model": "", "temperature": 0.7, "max_tokens": 256, "top_p": 1, "top_k": -1, "stream": false} responses: "200": - application/json: {"id": "", "model": "Expedition", "created": 755586, "usage": {"prompt_tokens": 348799, "completion_tokens": 332397, "total_tokens": 528534}, "choices": []} + application/json: {"id": "", "model": "Explorer", "created": 166063, "usage": {"prompt_tokens": 213097, "completion_tokens": 185693, "total_tokens": 530500}, "choices": []} "400": application/json: {"detail": {"msg": ""}} "422": @@ -239,7 +240,7 @@ examples: genImageToText: speakeasy-default-gen-image-to-text: requestBody: - multipart/form-data: {"image": {"": "x-file: example.file"}, "prompt": "", "model_id": ""} + multipart/form-data: {"image": "x-file: example.file", "prompt": "", "model_id": ""} responses: "200": application/json: {"text": ""} @@ -252,10 +253,10 @@ examples: genLiveVideoToVideo: speakeasy-default-gen-live-video-to-video: requestBody: - application/json: {"subscribe_url": "https://soulful-lava.org/", "publish_url": "https://vain-tabletop.biz", "control_url": "", "events_url": "", "model_id": ""} + application/json: {"subscribe_url": "https://soulful-finding.biz", "publish_url": "https://monumental-representation.biz/", "control_url": "", "events_url": "", "model_id": "", "gateway_request_id": "", "manifest_id": "", "stream_id": ""} responses: "200": - application/json: {"subscribe_url": "https://vain-kiss.name", "publish_url": "https://frail-duffel.com", "control_url": "", "events_url": ""} + application/json: {"subscribe_url": "https://any-expense.com/", "publish_url": "https://early-abacus.org", "control_url": "", "events_url": "", "request_id": "", "manifest_id": ""} "400": application/json: {"detail": {"msg": ""}} "422": @@ -268,12 +269,13 @@ examples: application/json: {"model_id": "", "text": "", "description": "A male speaker delivers a slightly expressive and animated speech with a moderate speed and pitch."} responses: "200": - application/json: {"audio": {"url": "https://accurate-parsnip.net/"}} + application/json: {"audio": {"url": "https://slushy-elevation.net/"}} "400": application/json: {"detail": {"msg": ""}} "422": application/json: {} "500": application/json: {"detail": {"msg": ""}} -examplesVersion: 1.0.0 +examplesVersion: 1.0.2 generatedTests: {} +releaseNotes: "## Go SDK Changes Detected:\n* `Livepeer.Generate.LiveVideoToVideo()`: \n * `request.Request` **Changed**\n * `response` **Changed**\n" diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 0a67405..691c5dd 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -4,18 +4,31 @@ generation: maintainOpenAPIOrder: true usageSnippets: optionalPropertyRendering: withExample + sdkInitStyle: constructor useClassNamesForArrayFields: true fixes: nameResolutionDec2023: true + nameResolutionFeb2025: false parameterOrderingFeb2024: true requestResponseComponentNamesFeb2024: true + securityFeb2025: false + sharedErrorComponentsApr2025: false auth: oAuth2ClientCredentialsEnabled: true oAuth2PasswordEnabled: false + hoistGlobalSecurity: true + schemas: + allOfMergeStrategy: shallowMerge + requestBodyFieldName: "" + tests: + generateTests: true + generateNewTests: false + skipResponseBodyAssertions: false go: - version: 0.7.1 + version: 0.8.0 additionalDependencies: {} allowUnknownFieldsInWeakUnions: false + baseErrorName: LivepeerError clientServerStatusCodesAsErrors: true defaultErrorName: SDKError flattenGlobalSecurity: true @@ -27,9 +40,15 @@ go: operations: models/operations shared: models/components webhooks: models/webhooks + includeEmptyObjects: false inputModelSuffix: input maxMethodParams: 4 methodArguments: require-security-and-request + modulePath: "" + nullableOptionalWrapper: false outputModelSuffix: output packageName: github.com/livepeer/livepeer-ai-go + respectRequiredFields: false responseFormat: envelope-http + sdkPackageName: "" + unionDeserializationStrategy: left-to-right diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index e5e61f6..f07ae92 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,13 +1,13 @@ -speakeasyVersion: 1.480.0 +speakeasyVersion: 1.656.1 sources: livepeerai-OAS: sourceNamespace: livepeerai-oas - sourceRevisionDigest: sha256:17df08f6003f51fd012286a1a4579ddd9ce9a377318644fc1c8bc9059fc6bd91 - sourceBlobDigest: sha256:7c0df39c1c5f0ffe580b63f5dc6cb0f81181f5361a80f63f41314cf81d0f3bf4 + sourceRevisionDigest: sha256:8e99fdc55b750db963079293d391dec5168c325a849ee9a52ef1dda1952b8917 + sourceBlobDigest: sha256:3268e3640001fb0f0582a9328448f3235d0fe88448fa7a1249a4524ebdd452c8 tags: - latest - - speakeasy-sdk-regen-1736900777 - - 0.0.0 + - speakeasy-sdk-regen-1739406403 + - 0.13.11 targets: livepeerai: source: livepeerai-OAS @@ -17,10 +17,10 @@ targets: livepeerai-go: source: livepeerai-OAS sourceNamespace: livepeerai-oas - sourceRevisionDigest: sha256:17df08f6003f51fd012286a1a4579ddd9ce9a377318644fc1c8bc9059fc6bd91 - sourceBlobDigest: sha256:7c0df39c1c5f0ffe580b63f5dc6cb0f81181f5361a80f63f41314cf81d0f3bf4 + sourceRevisionDigest: sha256:8e99fdc55b750db963079293d391dec5168c325a849ee9a52ef1dda1952b8917 + sourceBlobDigest: sha256:3268e3640001fb0f0582a9328448f3235d0fe88448fa7a1249a4524ebdd452c8 codeSamplesNamespace: code-samples-go-livepeerai-go - codeSamplesRevisionDigest: sha256:d919c3d9ec0aa7e1faf0cbc60f807b5ae75ce44626762362e3b8f1bf29488b54 + codeSamplesRevisionDigest: sha256:267119d169de9b1de80b9cf8c71c6032d66bf4763c42812850adcabe32be70af workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/README.md b/README.md index 3b598e9..e263776 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,7 @@ func main() { ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }, operations.WithRetries( retry.Config{ Strategy: "backoff", @@ -93,16 +84,7 @@ func main() { ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }) if err != nil { log.Fatal(err) @@ -153,16 +135,7 @@ func main() { ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }) if err != nil { @@ -202,10 +175,10 @@ func main() { You can override the default server globally using the `WithServerIndex(serverIndex int)` option when initializing the SDK client instance. The selected server will then be used as the default on the operations that use it. This table lists the indexes associated with the available servers: -| # | Server | -| --- | ------------------------------------------- | -| 0 | `https://dream-gateway.livepeer.cloud` | -| 1 | `https://livepeer.studio/api/beta/generate` | +| # | Server | Description | +| --- | ------------------------------------------- | -------------------------------- | +| 0 | `https://dream-gateway.livepeer.cloud` | Livepeer Cloud Community Gateway | +| 1 | `https://livepeer.studio/api/beta/generate` | Livepeer Studio Gateway | #### Example @@ -223,21 +196,12 @@ func main() { ctx := context.Background() s := livepeeraigo.New( - livepeeraigo.WithServerIndex(1), + livepeeraigo.WithServerIndex(0), livepeeraigo.WithSecurity(""), ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }) if err != nil { log.Fatal(err) @@ -266,21 +230,12 @@ func main() { ctx := context.Background() s := livepeeraigo.New( - livepeeraigo.WithServerURL("https://dream-gateway.livepeer.cloud"), + livepeeraigo.WithServerURL("https://livepeer.studio/api/beta/generate"), livepeeraigo.WithSecurity(""), ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }) if err != nil { log.Fatal(err) @@ -310,12 +265,13 @@ The built-in `net/http` client satisfies this interface and a default client bas import ( "net/http" "time" - "github.com/myorg/your-go-sdk" + + "github.com/livepeer/livepeer-ai-go" ) var ( httpClient = &http.Client{Timeout: 30 * time.Second} - sdkClient = sdk.New(sdk.WithClient(httpClient)) + sdkClient = livepeeraigo.New(livepeeraigo.WithClient(httpClient)) ) ``` @@ -352,16 +308,7 @@ func main() { ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }) if err != nil { log.Fatal(err) diff --git a/RELEASES.md b/RELEASES.md index 0cc3d05..4573136 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -128,4 +128,14 @@ Based on: ### Generated - [go v0.7.1] . ### Releases -- [Go v0.7.1] https://github.com/livepeer/livepeer-ai-go/releases/tag/v0.7.1 - . \ No newline at end of file +- [Go v0.7.1] https://github.com/livepeer/livepeer-ai-go/releases/tag/v0.7.1 - . + +## 2025-11-13 00:30:55 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.656.1 (2.753.1) https://github.com/speakeasy-api/speakeasy +### Generated +- [go v0.8.0] . +### Releases +- [Go v0.8.0] https://github.com/livepeer/livepeer-ai-go/releases/tag/v0.8.0 - . \ No newline at end of file diff --git a/USAGE.md b/USAGE.md index 3b7baa0..d249705 100644 --- a/USAGE.md +++ b/USAGE.md @@ -17,16 +17,7 @@ func main() { ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), + Prompt: "", }) if err != nil { log.Fatal(err) diff --git a/codeSamples.yaml b/codeSamples.yaml index f994156..fc13dc7 100644 --- a/codeSamples.yaml +++ b/codeSamples.yaml @@ -8,58 +8,378 @@ actions: x-codeSamples: - lang: go label: genAudioToText - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"os\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n content, fileErr := os.Open(\"example.file\")\n if fileErr != nil {\n panic(fileErr)\n }\n\n\n res, err := s.Generate.AudioToText(ctx, components.BodyGenAudioToText{\n Audio: components.Audio{\n FileName: \"example.file\",\n Content: content,\n },\n ModelID: livepeeraigo.String(\"\"),\n ReturnTimestamps: livepeeraigo.String(\"true\"),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.TextResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "os" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + example, fileErr := os.Open("example.file") + if fileErr != nil { + panic(fileErr) + } + + res, err := s.Generate.AudioToText(ctx, components.BodyGenAudioToText{ + Audio: components.Audio{ + FileName: "example.file", + Content: example, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.TextResponse != nil { + // handle response + } + } - target: $["paths"]["/image-to-image"]["post"] update: x-codeSamples: - lang: go label: genImageToImage - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"os\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n content, fileErr := os.Open(\"example.file\")\n if fileErr != nil {\n panic(fileErr)\n }\n\n\n res, err := s.Generate.ImageToImage(ctx, components.BodyGenImageToImage{\n Prompt: \"\",\n Image: components.Image{\n FileName: \"example.file\",\n Content: content,\n },\n ModelID: livepeeraigo.String(\"\"),\n Loras: livepeeraigo.String(\"\"),\n Strength: livepeeraigo.Float64(0.8),\n GuidanceScale: livepeeraigo.Float64(7.5),\n ImageGuidanceScale: livepeeraigo.Float64(1.5),\n NegativePrompt: livepeeraigo.String(\"\"),\n SafetyCheck: livepeeraigo.Bool(true),\n NumInferenceSteps: livepeeraigo.Int64(100),\n NumImagesPerPrompt: livepeeraigo.Int64(1),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.ImageResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "os" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + example, fileErr := os.Open("example.file") + if fileErr != nil { + panic(fileErr) + } + + res, err := s.Generate.ImageToImage(ctx, components.BodyGenImageToImage{ + Prompt: "", + Image: components.Image{ + FileName: "example.file", + Content: example, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.ImageResponse != nil { + // handle response + } + } - target: $["paths"]["/image-to-text"]["post"] update: x-codeSamples: - lang: go label: genImageToText - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"os\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n content, fileErr := os.Open(\"example.file\")\n if fileErr != nil {\n panic(fileErr)\n }\n\n\n res, err := s.Generate.ImageToText(ctx, components.BodyGenImageToText{\n Image: components.BodyGenImageToTextImage{\n FileName: \"example.file\",\n Content: content,\n },\n Prompt: livepeeraigo.String(\"\"),\n ModelID: livepeeraigo.String(\"\"),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.ImageToTextResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "os" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + example, fileErr := os.Open("example.file") + if fileErr != nil { + panic(fileErr) + } + + res, err := s.Generate.ImageToText(ctx, components.BodyGenImageToText{ + Image: components.BodyGenImageToTextImage{ + FileName: "example.file", + Content: example, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.ImageToTextResponse != nil { + // handle response + } + } - target: $["paths"]["/image-to-video"]["post"] update: x-codeSamples: - lang: go label: genImageToVideo - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"os\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n content, fileErr := os.Open(\"example.file\")\n if fileErr != nil {\n panic(fileErr)\n }\n\n\n res, err := s.Generate.ImageToVideo(ctx, components.BodyGenImageToVideo{\n Image: components.BodyGenImageToVideoImage{\n FileName: \"example.file\",\n Content: content,\n },\n ModelID: livepeeraigo.String(\"\"),\n Height: livepeeraigo.Int64(576),\n Width: livepeeraigo.Int64(1024),\n Fps: livepeeraigo.Int64(6),\n MotionBucketID: livepeeraigo.Int64(127),\n NoiseAugStrength: livepeeraigo.Float64(0.02),\n SafetyCheck: livepeeraigo.Bool(true),\n NumInferenceSteps: livepeeraigo.Int64(25),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.VideoResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "os" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + example, fileErr := os.Open("example.file") + if fileErr != nil { + panic(fileErr) + } + + res, err := s.Generate.ImageToVideo(ctx, components.BodyGenImageToVideo{ + Image: components.BodyGenImageToVideoImage{ + FileName: "example.file", + Content: example, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.VideoResponse != nil { + // handle response + } + } - target: $["paths"]["/live-video-to-video"]["post"] update: x-codeSamples: - lang: go label: genLiveVideoToVideo - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n res, err := s.Generate.LiveVideoToVideo(ctx, components.LiveVideoToVideoParams{\n SubscribeURL: \"https://soulful-lava.org/\",\n PublishURL: \"https://vain-tabletop.biz\",\n ControlURL: livepeeraigo.String(\"\"),\n EventsURL: livepeeraigo.String(\"\"),\n ModelID: livepeeraigo.String(\"\"),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.LiveVideoToVideoResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + res, err := s.Generate.LiveVideoToVideo(ctx, components.LiveVideoToVideoParams{ + SubscribeURL: "https://soulful-finding.biz", + PublishURL: "https://monumental-representation.biz/", + }) + if err != nil { + log.Fatal(err) + } + if res.LiveVideoToVideoResponse != nil { + // handle response + } + } - target: $["paths"]["/llm"]["post"] update: x-codeSamples: - lang: go label: genLLM - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n res, err := s.Generate.Llm(ctx, components.LLMRequest{\n Messages: []components.LLMMessage{\n\n },\n Model: livepeeraigo.String(\"\"),\n Temperature: livepeeraigo.Float64(0.7),\n MaxTokens: livepeeraigo.Int64(256),\n TopP: livepeeraigo.Float64(1),\n TopK: livepeeraigo.Int64(-1),\n Stream: livepeeraigo.Bool(false),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.LLMResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + res, err := s.Generate.Llm(ctx, components.LLMRequest{ + Messages: []components.LLMMessage{ + components.LLMMessage{ + Role: "", + Content: "", + }, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.LLMResponse != nil { + // handle response + } + } - target: $["paths"]["/segment-anything-2"]["post"] update: x-codeSamples: - lang: go label: genSegmentAnything2 - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"os\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n content, fileErr := os.Open(\"example.file\")\n if fileErr != nil {\n panic(fileErr)\n }\n\n\n res, err := s.Generate.SegmentAnything2(ctx, components.BodyGenSegmentAnything2{\n Image: components.BodyGenSegmentAnything2Image{\n FileName: \"example.file\",\n Content: content,\n },\n ModelID: livepeeraigo.String(\"\"),\n MultimaskOutput: livepeeraigo.Bool(true),\n ReturnLogits: livepeeraigo.Bool(true),\n NormalizeCoords: livepeeraigo.Bool(true),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.MasksResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "os" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + example, fileErr := os.Open("example.file") + if fileErr != nil { + panic(fileErr) + } + + res, err := s.Generate.SegmentAnything2(ctx, components.BodyGenSegmentAnything2{ + Image: components.BodyGenSegmentAnything2Image{ + FileName: "example.file", + Content: example, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.MasksResponse != nil { + // handle response + } + } - target: $["paths"]["/text-to-image"]["post"] update: x-codeSamples: - lang: go label: genTextToImage - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{\n ModelID: livepeeraigo.String(\"\"),\n Loras: livepeeraigo.String(\"\"),\n Prompt: \"\",\n Height: livepeeraigo.Int64(576),\n Width: livepeeraigo.Int64(1024),\n GuidanceScale: livepeeraigo.Float64(7.5),\n NegativePrompt: livepeeraigo.String(\"\"),\n SafetyCheck: livepeeraigo.Bool(true),\n NumInferenceSteps: livepeeraigo.Int64(50),\n NumImagesPerPrompt: livepeeraigo.Int64(1),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.ImageResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ + Prompt: "", + }) + if err != nil { + log.Fatal(err) + } + if res.ImageResponse != nil { + // handle response + } + } - target: $["paths"]["/text-to-speech"]["post"] update: x-codeSamples: - lang: go label: genTextToSpeech - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n res, err := s.Generate.TextToSpeech(ctx, components.TextToSpeechParams{\n ModelID: livepeeraigo.String(\"\"),\n Text: livepeeraigo.String(\"\"),\n Description: livepeeraigo.String(\"A male speaker delivers a slightly expressive and animated speech with a moderate speed and pitch.\"),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.AudioResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + res, err := s.Generate.TextToSpeech(ctx, components.TextToSpeechParams{}) + if err != nil { + log.Fatal(err) + } + if res.AudioResponse != nil { + // handle response + } + } - target: $["paths"]["/upscale"]["post"] update: x-codeSamples: - lang: go label: genUpscale - source: "package main\n\nimport(\n\t\"context\"\n\tlivepeeraigo \"github.com/livepeer/livepeer-ai-go\"\n\t\"os\"\n\t\"github.com/livepeer/livepeer-ai-go/models/components\"\n\t\"log\"\n)\n\nfunc main() {\n ctx := context.Background()\n \n s := livepeeraigo.New(\n livepeeraigo.WithSecurity(\"\"),\n )\n\n content, fileErr := os.Open(\"example.file\")\n if fileErr != nil {\n panic(fileErr)\n }\n\n\n res, err := s.Generate.Upscale(ctx, components.BodyGenUpscale{\n Prompt: \"\",\n Image: components.BodyGenUpscaleImage{\n FileName: \"example.file\",\n Content: content,\n },\n ModelID: livepeeraigo.String(\"\"),\n SafetyCheck: livepeeraigo.Bool(true),\n NumInferenceSteps: livepeeraigo.Int64(75),\n })\n if err != nil {\n log.Fatal(err)\n }\n if res.ImageResponse != nil {\n // handle response\n }\n}" + source: |- + package main + + import( + "context" + livepeeraigo "github.com/livepeer/livepeer-ai-go" + "os" + "github.com/livepeer/livepeer-ai-go/models/components" + "log" + ) + + func main() { + ctx := context.Background() + + s := livepeeraigo.New( + livepeeraigo.WithSecurity(""), + ) + + example, fileErr := os.Open("example.file") + if fileErr != nil { + panic(fileErr) + } + + res, err := s.Generate.Upscale(ctx, components.BodyGenUpscale{ + Prompt: "", + Image: components.BodyGenUpscaleImage{ + FileName: "example.file", + Content: example, + }, + }) + if err != nil { + log.Fatal(err) + } + if res.ImageResponse != nil { + // handle response + } + } diff --git a/docs/models/components/livevideotovideoparams.md b/docs/models/components/livevideotovideoparams.md index 67b5b70..8b8e9d7 100644 --- a/docs/models/components/livevideotovideoparams.md +++ b/docs/models/components/livevideotovideoparams.md @@ -10,4 +10,7 @@ | `ControlURL` | **string* | :heavy_minus_sign: | URL for subscribing via Trickle protocol for updates in the live video-to-video generation params. | | `EventsURL` | **string* | :heavy_minus_sign: | URL for publishing events via Trickle protocol for pipeline status and logs. | | `ModelID` | **string* | :heavy_minus_sign: | Name of the pipeline to run in the live video to video job. Notice that this is named model_id for consistency with other routes, but it does not refer to a Hugging Face model ID. The exact model(s) depends on the pipeline implementation and might be configurable via the `params` argument. | -| `Params` | [*components.Params](../../models/components/params.md) | :heavy_minus_sign: | Initial parameters for the pipeline. | \ No newline at end of file +| `Params` | [*components.Params](../../models/components/params.md) | :heavy_minus_sign: | Initial parameters for the pipeline. | +| `GatewayRequestID` | **string* | :heavy_minus_sign: | The ID of the Gateway request (for logging purposes). | +| `ManifestID` | **string* | :heavy_minus_sign: | The manifest ID from the orchestrator (for logging purposes). | +| `StreamID` | **string* | :heavy_minus_sign: | The Stream ID (for logging purposes). | \ No newline at end of file diff --git a/docs/models/components/livevideotovideoresponse.md b/docs/models/components/livevideotovideoresponse.md index 4264b72..c463d5b 100644 --- a/docs/models/components/livevideotovideoresponse.md +++ b/docs/models/components/livevideotovideoresponse.md @@ -10,4 +10,6 @@ Response model for live video-to-video generation. | `SubscribeURL` | *string* | :heavy_check_mark: | Source URL of the incoming stream to subscribe to | | `PublishURL` | *string* | :heavy_check_mark: | Destination URL of the outgoing stream to publish to | | `ControlURL` | **string* | :heavy_minus_sign: | URL for updating the live video-to-video generation | -| `EventsURL` | **string* | :heavy_minus_sign: | URL for subscribing to events for pipeline status and logs | \ No newline at end of file +| `EventsURL` | **string* | :heavy_minus_sign: | URL for subscribing to events for pipeline status and logs | +| `RequestID` | **string* | :heavy_minus_sign: | The ID generated for this request | +| `ManifestID` | **string* | :heavy_minus_sign: | Orchestrator manifest ID for this request | \ No newline at end of file diff --git a/docs/sdks/generate/README.md b/docs/sdks/generate/README.md index f3d64f0..5616d78 100644 --- a/docs/sdks/generate/README.md +++ b/docs/sdks/generate/README.md @@ -22,6 +22,7 @@ Generate images from text prompts. ### Example Usage + ```go package main @@ -34,22 +35,13 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) res, err := s.Generate.TextToImage(ctx, components.TextToImageParams{ - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), Prompt: "", - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - GuidanceScale: livepeeraigo.Float64(7.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(50), - NumImagesPerPrompt: livepeeraigo.Int64(1), }) if err != nil { log.Fatal(err) @@ -87,6 +79,7 @@ Apply image transformations to a provided image. ### Example Usage + ```go package main @@ -100,32 +93,22 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - content, fileErr := os.Open("example.file") + example, fileErr := os.Open("example.file") if fileErr != nil { panic(fileErr) } - res, err := s.Generate.ImageToImage(ctx, components.BodyGenImageToImage{ Prompt: "", Image: components.Image{ FileName: "example.file", - Content: content, + Content: example, }, - ModelID: livepeeraigo.String(""), - Loras: livepeeraigo.String(""), - Strength: livepeeraigo.Float64(0.8), - GuidanceScale: livepeeraigo.Float64(7.5), - ImageGuidanceScale: livepeeraigo.Float64(1.5), - NegativePrompt: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(100), - NumImagesPerPrompt: livepeeraigo.Int64(1), }) if err != nil { log.Fatal(err) @@ -163,6 +146,7 @@ Generate a video from a provided image. ### Example Usage + ```go package main @@ -176,30 +160,21 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - content, fileErr := os.Open("example.file") + example, fileErr := os.Open("example.file") if fileErr != nil { panic(fileErr) } - res, err := s.Generate.ImageToVideo(ctx, components.BodyGenImageToVideo{ Image: components.BodyGenImageToVideoImage{ FileName: "example.file", - Content: content, + Content: example, }, - ModelID: livepeeraigo.String(""), - Height: livepeeraigo.Int64(576), - Width: livepeeraigo.Int64(1024), - Fps: livepeeraigo.Int64(6), - MotionBucketID: livepeeraigo.Int64(127), - NoiseAugStrength: livepeeraigo.Float64(0.02), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(25), }) if err != nil { log.Fatal(err) @@ -237,6 +212,7 @@ Upscale an image by increasing its resolution. ### Example Usage + ```go package main @@ -250,26 +226,22 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - content, fileErr := os.Open("example.file") + example, fileErr := os.Open("example.file") if fileErr != nil { panic(fileErr) } - res, err := s.Generate.Upscale(ctx, components.BodyGenUpscale{ Prompt: "", Image: components.BodyGenUpscaleImage{ FileName: "example.file", - Content: content, + Content: example, }, - ModelID: livepeeraigo.String(""), - SafetyCheck: livepeeraigo.Bool(true), - NumInferenceSteps: livepeeraigo.Int64(75), }) if err != nil { log.Fatal(err) @@ -307,6 +279,7 @@ Transcribe audio files to text. ### Example Usage + ```go package main @@ -320,24 +293,21 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - content, fileErr := os.Open("example.file") + example, fileErr := os.Open("example.file") if fileErr != nil { panic(fileErr) } - res, err := s.Generate.AudioToText(ctx, components.BodyGenAudioToText{ Audio: components.Audio{ FileName: "example.file", - Content: content, + Content: example, }, - ModelID: livepeeraigo.String(""), - ReturnTimestamps: livepeeraigo.String("true"), }) if err != nil { log.Fatal(err) @@ -375,6 +345,7 @@ Segment objects in an image. ### Example Usage + ```go package main @@ -388,26 +359,21 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - content, fileErr := os.Open("example.file") + example, fileErr := os.Open("example.file") if fileErr != nil { panic(fileErr) } - res, err := s.Generate.SegmentAnything2(ctx, components.BodyGenSegmentAnything2{ Image: components.BodyGenSegmentAnything2Image{ FileName: "example.file", - Content: content, + Content: example, }, - ModelID: livepeeraigo.String(""), - MultimaskOutput: livepeeraigo.Bool(true), - ReturnLogits: livepeeraigo.Bool(true), - NormalizeCoords: livepeeraigo.Bool(true), }) if err != nil { log.Fatal(err) @@ -445,6 +411,7 @@ Generate text using a language model. ### Example Usage + ```go package main @@ -457,21 +424,18 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) res, err := s.Generate.Llm(ctx, components.LLMRequest{ Messages: []components.LLMMessage{ - + components.LLMMessage{ + Role: "", + Content: "", + }, }, - Model: livepeeraigo.String(""), - Temperature: livepeeraigo.Float64(0.7), - MaxTokens: livepeeraigo.Int64(256), - TopP: livepeeraigo.Float64(1), - TopK: livepeeraigo.Int64(-1), - Stream: livepeeraigo.Bool(false), }) if err != nil { log.Fatal(err) @@ -509,6 +473,7 @@ Transform image files to text. ### Example Usage + ```go package main @@ -522,24 +487,21 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - content, fileErr := os.Open("example.file") + example, fileErr := os.Open("example.file") if fileErr != nil { panic(fileErr) } - res, err := s.Generate.ImageToText(ctx, components.BodyGenImageToText{ Image: components.BodyGenImageToTextImage{ FileName: "example.file", - Content: content, + Content: example, }, - Prompt: livepeeraigo.String(""), - ModelID: livepeeraigo.String(""), }) if err != nil { log.Fatal(err) @@ -577,6 +539,7 @@ Apply transformations to a live video streamed to the returned endpoints. ### Example Usage + ```go package main @@ -589,17 +552,14 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) res, err := s.Generate.LiveVideoToVideo(ctx, components.LiveVideoToVideoParams{ - SubscribeURL: "https://soulful-lava.org/", - PublishURL: "https://vain-tabletop.biz", - ControlURL: livepeeraigo.String(""), - EventsURL: livepeeraigo.String(""), - ModelID: livepeeraigo.String(""), + SubscribeURL: "https://soulful-finding.biz", + PublishURL: "https://monumental-representation.biz/", }) if err != nil { log.Fatal(err) @@ -637,6 +597,7 @@ Generate a text-to-speech audio file based on the provided text input and speake ### Example Usage + ```go package main @@ -649,16 +610,12 @@ import( func main() { ctx := context.Background() - + s := livepeeraigo.New( livepeeraigo.WithSecurity(""), ) - res, err := s.Generate.TextToSpeech(ctx, components.TextToSpeechParams{ - ModelID: livepeeraigo.String(""), - Text: livepeeraigo.String(""), - Description: livepeeraigo.String("A male speaker delivers a slightly expressive and animated speech with a moderate speed and pitch."), - }) + res, err := s.Generate.TextToSpeech(ctx, components.TextToSpeechParams{}) if err != nil { log.Fatal(err) } diff --git a/docs/sdks/livepeer/README.md b/docs/sdks/livepeer/README.md deleted file mode 100644 index b9ade0c..0000000 --- a/docs/sdks/livepeer/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Livepeer SDK - -## Overview - -Livepeer AI Runner: An application to run AI pipelines - -### Available Operations diff --git a/generate.go b/generate.go index 26596a5..6976bfb 100644 --- a/generate.go +++ b/generate.go @@ -6,6 +6,7 @@ import ( "bytes" "context" "fmt" + "github.com/livepeer/livepeer-ai-go/internal/config" "github.com/livepeer/livepeer-ai-go/internal/hooks" "github.com/livepeer/livepeer-ai-go/internal/utils" "github.com/livepeer/livepeer-ai-go/models/components" @@ -17,25 +18,22 @@ import ( ) type Generate struct { - sdkConfiguration sdkConfiguration + rootSDK *Livepeer + sdkConfiguration config.SDKConfiguration + hooks *hooks.Hooks } -func newGenerate(sdkConfig sdkConfiguration) *Generate { +func newGenerate(rootSDK *Livepeer, sdkConfig config.SDKConfiguration, hooks *hooks.Hooks) *Generate { return &Generate{ + rootSDK: rootSDK, sdkConfiguration: sdkConfig, + hooks: hooks, } } // TextToImage - Text To Image // Generate images from text prompts. func (s *Generate) TextToImage(ctx context.Context, request components.TextToImageParams, opts ...operations.Option) (*operations.GenTextToImageResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genTextToImage", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -59,6 +57,15 @@ func (s *Generate) TextToImage(ctx context.Context, request components.TextToIma return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genTextToImage", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "json", `request:"mediaType=application/json"`) if err != nil { return nil, err @@ -113,15 +120,17 @@ func (s *Generate) TextToImage(ctx context.Context, request components.TextToIma "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -138,7 +147,7 @@ func (s *Generate) TextToImage(ctx context.Context, request components.TextToIma err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -146,13 +155,13 @@ func (s *Generate) TextToImage(ctx context.Context, request components.TextToIma if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -165,17 +174,17 @@ func (s *Generate) TextToImage(ctx context.Context, request components.TextToIma err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -303,13 +312,6 @@ func (s *Generate) TextToImage(ctx context.Context, request components.TextToIma // ImageToImage - Image To Image // Apply image transformations to a provided image. func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenImageToImage, opts ...operations.Option) (*operations.GenImageToImageResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genImageToImage", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -333,6 +335,15 @@ func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenI return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genImageToImage", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "multipart", `request:"mediaType=multipart/form-data"`) if err != nil { return nil, err @@ -387,15 +398,17 @@ func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenI "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -412,7 +425,7 @@ func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenI err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -420,13 +433,13 @@ func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenI if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -439,17 +452,17 @@ func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenI err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -577,13 +590,6 @@ func (s *Generate) ImageToImage(ctx context.Context, request components.BodyGenI // ImageToVideo - Image To Video // Generate a video from a provided image. func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenImageToVideo, opts ...operations.Option) (*operations.GenImageToVideoResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genImageToVideo", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -607,6 +613,15 @@ func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenI return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genImageToVideo", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "multipart", `request:"mediaType=multipart/form-data"`) if err != nil { return nil, err @@ -661,15 +676,17 @@ func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenI "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -686,7 +703,7 @@ func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenI err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -694,13 +711,13 @@ func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenI if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -713,17 +730,17 @@ func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenI err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -851,13 +868,6 @@ func (s *Generate) ImageToVideo(ctx context.Context, request components.BodyGenI // Upscale // Upscale an image by increasing its resolution. func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscale, opts ...operations.Option) (*operations.GenUpscaleResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genUpscale", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -881,6 +891,15 @@ func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscal return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genUpscale", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "multipart", `request:"mediaType=multipart/form-data"`) if err != nil { return nil, err @@ -935,15 +954,17 @@ func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscal "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -960,7 +981,7 @@ func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscal err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -968,13 +989,13 @@ func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscal if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -987,17 +1008,17 @@ func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscal err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -1125,13 +1146,6 @@ func (s *Generate) Upscale(ctx context.Context, request components.BodyGenUpscal // AudioToText - Audio To Text // Transcribe audio files to text. func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAudioToText, opts ...operations.Option) (*operations.GenAudioToTextResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genAudioToText", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -1155,6 +1169,15 @@ func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAu return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genAudioToText", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "multipart", `request:"mediaType=multipart/form-data"`) if err != nil { return nil, err @@ -1209,15 +1232,17 @@ func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAu "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -1234,7 +1259,7 @@ func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAu err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -1242,13 +1267,13 @@ func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAu if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -1261,17 +1286,17 @@ func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAu err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "413", "415", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -1403,13 +1428,6 @@ func (s *Generate) AudioToText(ctx context.Context, request components.BodyGenAu // SegmentAnything2 - Segment Anything 2 // Segment objects in an image. func (s *Generate) SegmentAnything2(ctx context.Context, request components.BodyGenSegmentAnything2, opts ...operations.Option) (*operations.GenSegmentAnything2Response, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genSegmentAnything2", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -1433,6 +1451,15 @@ func (s *Generate) SegmentAnything2(ctx context.Context, request components.Body return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genSegmentAnything2", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "multipart", `request:"mediaType=multipart/form-data"`) if err != nil { return nil, err @@ -1487,15 +1514,17 @@ func (s *Generate) SegmentAnything2(ctx context.Context, request components.Body "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -1512,7 +1541,7 @@ func (s *Generate) SegmentAnything2(ctx context.Context, request components.Body err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -1520,13 +1549,13 @@ func (s *Generate) SegmentAnything2(ctx context.Context, request components.Body if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -1539,17 +1568,17 @@ func (s *Generate) SegmentAnything2(ctx context.Context, request components.Body err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -1677,13 +1706,6 @@ func (s *Generate) SegmentAnything2(ctx context.Context, request components.Body // Llm - LLM // Generate text using a language model. func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts ...operations.Option) (*operations.GenLLMResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genLLM", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -1707,6 +1729,15 @@ func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genLLM", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "json", `request:"mediaType=application/json"`) if err != nil { return nil, err @@ -1761,15 +1792,17 @@ func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -1786,7 +1819,7 @@ func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -1794,13 +1827,13 @@ func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -1813,17 +1846,17 @@ func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -1951,13 +1984,6 @@ func (s *Generate) Llm(ctx context.Context, request components.LLMRequest, opts // ImageToText - Image To Text // Transform image files to text. func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenImageToText, opts ...operations.Option) (*operations.GenImageToTextResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genImageToText", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -1981,6 +2007,15 @@ func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenIm return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genImageToText", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "multipart", `request:"mediaType=multipart/form-data"`) if err != nil { return nil, err @@ -2035,15 +2070,17 @@ func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenIm "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -2060,7 +2097,7 @@ func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenIm err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -2068,13 +2105,13 @@ func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenIm if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -2087,17 +2124,17 @@ func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenIm err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "413", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -2227,13 +2264,6 @@ func (s *Generate) ImageToText(ctx context.Context, request components.BodyGenIm // LiveVideoToVideo - Live Video To Video // Apply transformations to a live video streamed to the returned endpoints. func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.LiveVideoToVideoParams, opts ...operations.Option) (*operations.GenLiveVideoToVideoResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genLiveVideoToVideo", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -2257,6 +2287,15 @@ func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.Live return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genLiveVideoToVideo", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "json", `request:"mediaType=application/json"`) if err != nil { return nil, err @@ -2311,15 +2350,17 @@ func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.Live "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -2336,7 +2377,7 @@ func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.Live err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -2344,13 +2385,13 @@ func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.Live if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -2363,17 +2404,17 @@ func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.Live err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } @@ -2501,13 +2542,6 @@ func (s *Generate) LiveVideoToVideo(ctx context.Context, request components.Live // TextToSpeech - Text To Speech // Generate a text-to-speech audio file based on the provided text input and speaker description. func (s *Generate) TextToSpeech(ctx context.Context, request components.TextToSpeechParams, opts ...operations.Option) (*operations.GenTextToSpeechResponse, error) { - hookCtx := hooks.HookContext{ - Context: ctx, - OperationID: "genTextToSpeech", - OAuth2Scopes: []string{}, - SecuritySource: s.sdkConfiguration.Security, - } - o := operations.Options{} supportedOptions := []string{ operations.SupportedOptionRetries, @@ -2531,6 +2565,15 @@ func (s *Generate) TextToSpeech(ctx context.Context, request components.TextToSp return nil, fmt.Errorf("error generating URL: %w", err) } + hookCtx := hooks.HookContext{ + SDK: s.rootSDK, + SDKConfiguration: s.sdkConfiguration, + BaseURL: baseURL, + Context: ctx, + OperationID: "genTextToSpeech", + OAuth2Scopes: nil, + SecuritySource: s.sdkConfiguration.Security, + } bodyReader, reqContentType, err := utils.SerializeRequestBody(ctx, request, false, false, "Request", "json", `request:"mediaType=application/json"`) if err != nil { return nil, err @@ -2585,15 +2628,17 @@ func (s *Generate) TextToSpeech(ctx context.Context, request components.TextToSp "504", }, }, func() (*http.Response, error) { - if req.Body != nil { + if req.Body != nil && req.Body != http.NoBody && req.GetBody != nil { copyBody, err := req.GetBody() + if err != nil { return nil, err } + req.Body = copyBody } - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { if retry.IsPermanentError(err) || retry.IsTemporaryError(err) { return nil, err @@ -2610,7 +2655,7 @@ func (s *Generate) TextToSpeech(ctx context.Context, request components.TextToSp err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) } return httpRes, err }) @@ -2618,13 +2663,13 @@ func (s *Generate) TextToSpeech(ctx context.Context, request components.TextToSp if err != nil { return nil, err } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } } } else { - req, err = s.sdkConfiguration.Hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) + req, err = s.hooks.BeforeRequest(hooks.BeforeRequestContext{HookContext: hookCtx}, req) if err != nil { return nil, err } @@ -2637,17 +2682,17 @@ func (s *Generate) TextToSpeech(ctx context.Context, request components.TextToSp err = fmt.Errorf("error sending request: no response") } - _, err = s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) + _, err = s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, nil, err) return nil, err } else if utils.MatchStatusCodes([]string{"400", "401", "422", "4XX", "500", "5XX"}, httpRes.StatusCode) { - _httpRes, err := s.sdkConfiguration.Hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) + _httpRes, err := s.hooks.AfterError(hooks.AfterErrorContext{HookContext: hookCtx}, httpRes, nil) if err != nil { return nil, err } else if _httpRes != nil { httpRes = _httpRes } } else { - httpRes, err = s.sdkConfiguration.Hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) + httpRes, err = s.hooks.AfterSuccess(hooks.AfterSuccessContext{HookContext: hookCtx}, httpRes) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index 7598fb3..6e2bb0d 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,11 @@ module github.com/livepeer/livepeer-ai-go -go 1.20 +go 1.22 -require github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 +require github.com/stretchr/testify v1.11.1 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index c5a75f1..c4c1710 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,10 @@ -github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 h1:S92OBrGuLLZsyM5ybUzgc/mPjIYk2AZqufieooe98uw= -github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05/go.mod h1:M9R1FoZ3y//hwwnJtO51ypFGwm8ZfpxPT/ZLtO1mcgQ= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/config/sdkconfiguration.go b/internal/config/sdkconfiguration.go new file mode 100644 index 0000000..c0491c7 --- /dev/null +++ b/internal/config/sdkconfiguration.go @@ -0,0 +1,33 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package config + +import ( + "context" + "github.com/livepeer/livepeer-ai-go/retry" + "net/http" + "time" +) + +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +type SDKConfiguration struct { + Client HTTPClient + Security func(context.Context) (interface{}, error) + ServerURL string + ServerIndex int + ServerList []string + UserAgent string + RetryConfig *retry.Config + Timeout *time.Duration +} + +func (c *SDKConfiguration) GetServerDetails() (string, map[string]string) { + if c.ServerURL != "" { + return c.ServerURL, nil + } + + return c.ServerList[c.ServerIndex], nil +} diff --git a/internal/hooks/hooks.go b/internal/hooks/hooks.go index 8ea491a..942b42b 100644 --- a/internal/hooks/hooks.go +++ b/internal/hooks/hooks.go @@ -5,6 +5,7 @@ package hooks import ( "context" "errors" + "github.com/livepeer/livepeer-ai-go/internal/config" "net/http" ) @@ -24,10 +25,13 @@ type HTTPClient interface { } type HookContext struct { - Context context.Context - OperationID string - OAuth2Scopes []string - SecuritySource func(context.Context) (interface{}, error) + SDK any + SDKConfiguration config.SDKConfiguration + BaseURL string + Context context.Context + OperationID string + OAuth2Scopes []string + SecuritySource func(context.Context) (interface{}, error) } type BeforeRequestContext struct { @@ -70,6 +74,11 @@ type Hooks struct { afterErrorHook []afterErrorHook } +var _ sdkInitHook = (*Hooks)(nil) +var _ beforeRequestHook = (*Hooks)(nil) +var _ afterSuccessHook = (*Hooks)(nil) +var _ afterErrorHook = (*Hooks)(nil) + func New() *Hooks { h := &Hooks{ sdkInitHooks: []sdkInitHook{}, diff --git a/internal/utils/form.go b/internal/utils/form.go index 9e33358..64bbd08 100644 --- a/internal/utils/form.go +++ b/internal/utils/form.go @@ -10,16 +10,19 @@ import ( "strings" "time" - "github.com/ericlagergren/decimal" - + "github.com/livepeer/livepeer-ai-go/optionalnullable" "github.com/livepeer/livepeer-ai-go/types" ) -func populateForm(paramName string, explode bool, objType reflect.Type, objValue reflect.Value, delimiter string, getFieldName func(reflect.StructField) string) url.Values { +func populateForm(paramName string, explode bool, objType reflect.Type, objValue reflect.Value, delimiter string, defaultValue *string, getFieldName func(reflect.StructField) string) url.Values { formValues := url.Values{} if isNil(objType, objValue) { + if defaultValue != nil { + formValues.Add(paramName, *defaultValue) + } + return formValues } @@ -37,8 +40,6 @@ func populateForm(paramName string, explode bool, objType reflect.Type, objValue formValues.Add(paramName, valToString(objValue.Interface())) case big.Int: formValues.Add(paramName, valToString(objValue.Interface())) - case decimal.Big: - formValues.Add(paramName, valToString(objValue.Interface())) default: var items []string @@ -60,7 +61,13 @@ func populateForm(paramName string, explode bool, objType reflect.Type, objValue } if explode { - formValues.Add(fieldName, valToString(valType.Interface())) + if valType.Kind() == reflect.Slice || valType.Kind() == reflect.Array { + for i := 0; i < valType.Len(); i++ { + formValues.Add(fieldName, valToString(valType.Index(i).Interface())) + } + } else { + formValues.Add(fieldName, valToString(valType.Interface())) + } } else { items = append(items, fmt.Sprintf("%s%s%s", fieldName, delimiter, valToString(valType.Interface()))) } @@ -71,6 +78,16 @@ func populateForm(paramName string, explode bool, objType reflect.Type, objValue } } case reflect.Map: + // check if optionalnullable.OptionalNullable[T] + if nullableValue, ok := optionalnullable.AsOptionalNullable(objValue); ok { + // Handle optionalnullable.OptionalNullable[T] using GetUntyped method + if value, isSet := nullableValue.GetUntyped(); isSet && value != nil { + formValues.Add(paramName, valToString(value)) + } + // If not set or explicitly null, skip adding to form + return formValues + } + items := []string{} iter := objValue.MapRange() diff --git a/internal/utils/headers.go b/internal/utils/headers.go index a07608b..3e6d4da 100644 --- a/internal/utils/headers.go +++ b/internal/utils/headers.go @@ -8,6 +8,8 @@ import ( "net/http" "reflect" "strings" + + "github.com/livepeer/livepeer-ai-go/optionalnullable" ) func PopulateHeaders(_ context.Context, req *http.Request, headers interface{}, globals interface{}) { @@ -98,6 +100,16 @@ func serializeHeader(objType reflect.Type, objValue reflect.Value, explode bool) return strings.Join(items, ",") case reflect.Map: + // check if optionalnullable.OptionalNullable[T] + if nullableValue, ok := optionalnullable.AsOptionalNullable(objValue); ok { + // Handle optionalnullable.OptionalNullable[T] using GetUntyped method + if value, isSet := nullableValue.GetUntyped(); isSet && value != nil { + return valToString(value) + } + // If not set or explicitly null, return empty string + return "" + } + items := []string{} iter := objValue.MapRange() diff --git a/internal/utils/json.go b/internal/utils/json.go index 0eb3953..de39457 100644 --- a/internal/utils/json.go +++ b/internal/utils/json.go @@ -5,6 +5,7 @@ package utils import ( "bytes" "encoding/json" + "errors" "fmt" "math/big" "reflect" @@ -14,8 +15,6 @@ import ( "unsafe" "github.com/livepeer/livepeer-ai-go/types" - - "github.com/ericlagergren/decimal" ) func MarshalJSON(v interface{}, tag reflect.StructTag, topLevel bool) ([]byte, error) { @@ -40,21 +39,30 @@ func MarshalJSON(v interface{}, tag reflect.StructTag, topLevel bool) ([]byte, e fieldName := field.Name omitEmpty := false + omitZero := false jsonTag := field.Tag.Get("json") if jsonTag != "" { for _, tag := range strings.Split(jsonTag, ",") { if tag == "omitempty" { omitEmpty = true + } else if tag == "omitzero" { + omitZero = true } else { fieldName = tag } } } - if isNil(field.Type, fieldVal) && field.Tag.Get("const") == "" { - if omitEmpty { + if (omitEmpty || omitZero) && field.Tag.Get("const") == "" { + // Both omitempty and omitzero skip zero values (including nil) + if isNil(field.Type, fieldVal) { + continue + } + + if omitZero && fieldVal.IsZero() { continue } + } if !field.IsExported() && field.Tag.Get("const") == "" { @@ -114,9 +122,9 @@ func MarshalJSON(v interface{}, tag reflect.StructTag, topLevel bool) ([]byte, e } } -func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool, disallowUnknownFields bool) error { +func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool, requiredFields []string) error { if reflect.TypeOf(v).Kind() != reflect.Ptr { - return fmt.Errorf("v must be a pointer") + return errors.New("v must be a pointer") } typ, val := dereferencePointers(reflect.TypeOf(v), reflect.ValueOf(v)) @@ -124,19 +132,25 @@ func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool switch { case isModelType(typ): if topLevel || bytes.Equal(b, []byte("null")) { - d := json.NewDecoder(bytes.NewReader(b)) - if disallowUnknownFields { - d.DisallowUnknownFields() - } - return d.Decode(v) + return json.Unmarshal(b, v) } - var unmarhsaled map[string]json.RawMessage + var unmarshaled map[string]json.RawMessage - if err := json.Unmarshal(b, &unmarhsaled); err != nil { + if err := json.Unmarshal(b, &unmarshaled); err != nil { return err } + missingFields := []string{} + for _, requiredField := range requiredFields { + if _, ok := unmarshaled[requiredField]; !ok { + missingFields = append(missingFields, requiredField) + } + } + if len(missingFields) > 0 { + return fmt.Errorf("missing required fields: %s", strings.Join(missingFields, ", ")) + } + var additionalPropertiesField *reflect.StructField var additionalPropertiesValue *reflect.Value @@ -149,7 +163,7 @@ func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool jsonTag := field.Tag.Get("json") if jsonTag != "" { for _, tag := range strings.Split(jsonTag, ",") { - if tag != "omitempty" { + if tag != "omitempty" && tag != "omitzero" { fieldName = tag } } @@ -163,7 +177,7 @@ func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool // If we receive a value for a const field ignore it but mark it as unmarshaled if field.Tag.Get("const") != "" { - if r, ok := unmarhsaled[fieldName]; ok { + if r, ok := unmarshaled[fieldName]; ok { val := string(r) if strings.HasPrefix(val, `"`) && strings.HasSuffix(val, `"`) { @@ -178,40 +192,36 @@ func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool return fmt.Errorf("const field `%s` does not match expected value `%s` got `%s`", fieldName, constValue, val) } - delete(unmarhsaled, fieldName) + delete(unmarshaled, fieldName) } } else if !field.IsExported() { continue } - value, ok := unmarhsaled[fieldName] + value, ok := unmarshaled[fieldName] if !ok { - defaultTag := field.Tag.Get("default") - if defaultTag != "" { + defaultTag, defaultOk := field.Tag.Lookup("default") + if defaultOk { value = handleDefaultConstValue(defaultTag, fieldVal.Interface(), field.Tag) ok = true } } else { - delete(unmarhsaled, fieldName) + delete(unmarshaled, fieldName) } if ok { - if err := unmarshalValue(value, fieldVal, field.Tag, disallowUnknownFields); err != nil { + if err := unmarshalValue(value, fieldVal, field.Tag); err != nil { return err } } } - keys := make([]string, 0, len(unmarhsaled)) - for k := range unmarhsaled { + keys := make([]string, 0, len(unmarshaled)) + for k := range unmarshaled { keys = append(keys, k) } if len(keys) > 0 { - if disallowUnknownFields && (additionalPropertiesField == nil || additionalPropertiesValue == nil) { - return fmt.Errorf("unknown fields: %v", keys) - } - if additionalPropertiesField != nil && additionalPropertiesValue != nil { typeOfMap := additionalPropertiesField.Type if additionalPropertiesValue.Type().Kind() == reflect.Interface { @@ -222,10 +232,10 @@ func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool mapValue := reflect.MakeMap(typeOfMap) - for key, value := range unmarhsaled { + for key, value := range unmarshaled { val := reflect.New(typeOfMap.Elem()) - if err := unmarshalValue(value, val, additionalPropertiesField.Tag, disallowUnknownFields); err != nil { + if err := unmarshalValue(value, val, additionalPropertiesField.Tag); err != nil { return err } @@ -244,7 +254,7 @@ func UnmarshalJSON(b []byte, v interface{}, tag reflect.StructTag, topLevel bool } } default: - return unmarshalValue(b, reflect.ValueOf(v), tag, disallowUnknownFields) + return unmarshalValue(b, reflect.ValueOf(v), tag) } return nil @@ -257,8 +267,8 @@ func marshalValue(v interface{}, tag reflect.StructTag) (json.RawMessage, error) } if isNil(reflect.TypeOf(v), reflect.ValueOf(v)) { - defaultTag := tag.Get("default") - if defaultTag != "" { + defaultTag, ok := tag.Lookup("default") + if ok { return handleDefaultConstValue(defaultTag, v, tag), nil } @@ -284,6 +294,11 @@ func marshalValue(v interface{}, tag reflect.StructTag) (json.RawMessage, error) return []byte("null"), nil } + // Check if the map implements json.Marshaler (like optionalnullable.OptionalNullable[T]) + if marshaler, ok := val.Interface().(json.Marshaler); ok { + return marshaler.MarshalJSON() + } + out := map[string]json.RawMessage{} for _, key := range val.MapKeys() { @@ -337,17 +352,6 @@ func marshalValue(v interface{}, tag reflect.StructTag) (json.RawMessage, error) b := val.Interface().(big.Int) return []byte(fmt.Sprintf(`"%s"`, (&b).String())), nil } - case reflect.TypeOf(decimal.Big{}): - format := tag.Get("decimal") - if format == "number" { - b := val.Interface().(decimal.Big) - f, ok := (&b).Float64() - if ok { - return []byte(b.String()), nil - } - - return []byte(fmt.Sprintf(`%f`, f)), nil - } } } @@ -378,11 +382,6 @@ func handleDefaultConstValue(tagValue string, val interface{}, tag reflect.Struc if format == "string" { return []byte(fmt.Sprintf(`"%s"`, tagValue)) } - case reflect.TypeOf(decimal.Big{}): - decimalTag := tag.Get("decimal") - if decimalTag != "number" { - return []byte(fmt.Sprintf(`"%s"`, tagValue)) - } case reflect.TypeOf(types.Date{}): return []byte(fmt.Sprintf(`"%s"`, tagValue)) default: @@ -394,7 +393,7 @@ func handleDefaultConstValue(tagValue string, val interface{}, tag reflect.Struc return []byte(tagValue) } -func unmarshalValue(value json.RawMessage, v reflect.Value, tag reflect.StructTag, disallowUnknownFields bool) error { +func unmarshalValue(value json.RawMessage, v reflect.Value, tag reflect.StructTag) error { if bytes.Equal(value, []byte("null")) { if v.CanAddr() { return json.Unmarshal(value, v.Addr().Interface()) @@ -466,18 +465,18 @@ func unmarshalValue(value json.RawMessage, v reflect.Value, tag reflect.StructTa } } - var unmarhsaled map[string]json.RawMessage + var unmarshaled map[string]json.RawMessage - if err := json.Unmarshal(value, &unmarhsaled); err != nil { + if err := json.Unmarshal(value, &unmarshaled); err != nil { return err } m := reflect.MakeMap(typ) - for k, value := range unmarhsaled { + for k, value := range unmarshaled { itemVal := reflect.New(typ.Elem()) - if err := unmarshalValue(value, itemVal, tag, disallowUnknownFields); err != nil { + if err := unmarshalValue(value, itemVal, tag); err != nil { return err } @@ -498,7 +497,7 @@ func unmarshalValue(value json.RawMessage, v reflect.Value, tag reflect.StructTa for index, value := range unmarshaled { itemVal := reflect.New(typ.Elem()) - if err := unmarshalValue(value, itemVal, tag, disallowUnknownFields); err != nil { + if err := unmarshalValue(value, itemVal, tag); err != nil { return err } @@ -563,27 +562,6 @@ func unmarshalValue(value json.RawMessage, v reflect.Value, tag reflect.StructTa v.Set(reflect.ValueOf(b)) return nil - case reflect.TypeOf(decimal.Big{}): - var d *decimal.Big - format := tag.Get("decimal") - if format == "number" { - var ok bool - d, ok = new(decimal.Big).SetString(string(value)) - if !ok { - return fmt.Errorf("failed to parse number as decimal.Big") - } - } else { - if err := json.Unmarshal(value, &d); err != nil { - return err - } - } - - if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Ptr { - v = v.Elem() - } - - v.Set(reflect.ValueOf(d)) - return nil case reflect.TypeOf(types.Date{}): var s string @@ -616,11 +594,7 @@ func unmarshalValue(value json.RawMessage, v reflect.Value, tag reflect.StructTa val = v.Interface() } - d := json.NewDecoder(bytes.NewReader(value)) - if disallowUnknownFields { - d.DisallowUnknownFields() - } - return d.Decode(val) + return json.Unmarshal(value, val) } func dereferencePointers(typ reflect.Type, val reflect.Value) (reflect.Type, reflect.Value) { @@ -652,8 +626,6 @@ func isComplexValueType(typ reflect.Type) bool { fallthrough case reflect.TypeOf(big.Int{}): fallthrough - case reflect.TypeOf(decimal.Big{}): - fallthrough case reflect.TypeOf(types.Date{}): return true } @@ -673,3 +645,215 @@ func isModelType(typ reflect.Type) bool { return false } + +// CalculateJSONSize returns the byte size of the JSON representation of a value. +// This is used to determine which union type variant has the most data. +func CalculateJSONSize(v interface{}) int { + data, err := json.Marshal(v) + if err != nil { + return 0 + } + return len(data) +} + +// UnionCandidate represents a candidate type during union deserialization +type UnionCandidate struct { + FieldCount int + Size int + Type any // The union type enum value + Value any // The unmarshaled value +} + +// CountFields recursively counts the number of valid (non-nil, non-zero) fields set in a value. +// This is used as the primary discriminator for union types, with JSON size as a tiebreaker. +func CountFields(v interface{}) int { + if v == nil { + return 0 + } + + typ := reflect.TypeOf(v) + val := reflect.ValueOf(v) + + // Dereference pointers + for typ.Kind() == reflect.Ptr { + if val.IsNil() { + return 0 + } + typ = typ.Elem() + val = val.Elem() + } + + return countFieldsRecursive(typ, val) +} + +// PickBestCandidate selects the best union type candidate using a multi-stage filtering approach: +// 1. If multiple candidates, filter by field count (keep only those with max field count) +// 2. If still multiple, filter by JSON size (keep only those with max size) +// 3. Return the first remaining candidate +func PickBestCandidate(candidates []UnionCandidate) *UnionCandidate { + if len(candidates) == 0 { + return nil + } + + if len(candidates) == 1 { + return &candidates[0] + } + + // Filter by field count if we have multiple candidates + if len(candidates) > 1 { + maxFieldCount := -1 + for i := range candidates { + candidates[i].FieldCount = CountFields(candidates[i].Value) + if candidates[i].FieldCount > maxFieldCount { + maxFieldCount = candidates[i].FieldCount + } + } + + // Keep only candidates with maximum field count + filtered := make([]UnionCandidate, 0, len(candidates)) + for _, c := range candidates { + if c.FieldCount == maxFieldCount { + filtered = append(filtered, c) + } + } + candidates = filtered + } + + if len(candidates) == 1 { + return &candidates[0] + } + + // Filter by JSON size if we still have multiple candidates + if len(candidates) > 1 { + maxSize := -1 + for i := range candidates { + candidates[i].Size = CalculateJSONSize(candidates[i].Value) + if candidates[i].Size > maxSize { + maxSize = candidates[i].Size + } + } + + // Keep only candidates with maximum size + filtered := make([]UnionCandidate, 0, len(candidates)) + for _, c := range candidates { + if c.Size == maxSize { + filtered = append(filtered, c) + } + } + candidates = filtered + } + + // Pick the first remaining candidate + return &candidates[0] +} + +func countFieldsRecursive(typ reflect.Type, val reflect.Value) int { + count := 0 + + switch typ.Kind() { + case reflect.Struct: + // Handle special types + switch typ { + case reflect.TypeOf(time.Time{}): + if !val.Interface().(time.Time).IsZero() { + return 1 + } + return 0 + case reflect.TypeOf(big.Int{}): + b := val.Interface().(big.Int) + if b.Sign() != 0 { + return 1 + } + return 0 + case reflect.TypeOf(types.Date{}): + // Date is always counted if it exists + return 1 + } + + // For regular structs, count non-zero fields + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + fieldVal := val.Field(i) + + // Skip unexported fields and const fields + if !field.IsExported() || field.Tag.Get("const") != "" { + continue + } + + // Skip fields tagged with json:"-" + jsonTag := field.Tag.Get("json") + if jsonTag == "-" { + continue + } + + fieldTyp := field.Type + // Dereference pointer types for the field + for fieldTyp.Kind() == reflect.Ptr { + if fieldVal.IsNil() { + break + } + fieldTyp = fieldTyp.Elem() + fieldVal = fieldVal.Elem() + } + + if !isNil(field.Type, val.Field(i)) { + count += countFieldsRecursive(fieldTyp, fieldVal) + } + } + + case reflect.Slice, reflect.Array: + if val.IsNil() || val.Len() == 0 { + return 0 + } + // Count each array/slice element + for i := 0; i < val.Len(); i++ { + itemVal := val.Index(i) + itemTyp := itemVal.Type() + + // Dereference pointer types + for itemTyp.Kind() == reflect.Ptr { + if itemVal.IsNil() { + break + } + itemTyp = itemTyp.Elem() + itemVal = itemVal.Elem() + } + + if !isNil(itemTyp, itemVal) { + count += countFieldsRecursive(itemTyp, itemVal) + } + } + + case reflect.String: + if val.String() != "" { + count = 1 + } + + case reflect.Bool: + // Bools always count as a field (even if false) + count = 1 + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if val.Int() != 0 { + count = 1 + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if val.Uint() != 0 { + count = 1 + } + + case reflect.Float32, reflect.Float64: + if val.Float() != 0 { + count = 1 + } + + default: + // For any other type, if it's not zero, count it as 1 + if !val.IsZero() { + count = 1 + } + } + + return count +} diff --git a/internal/utils/pathparams.go b/internal/utils/pathparams.go index e3d585e..b87d17b 100644 --- a/internal/utils/pathparams.go +++ b/internal/utils/pathparams.go @@ -11,8 +11,7 @@ import ( "strings" "time" - "github.com/ericlagergren/decimal" - + "github.com/livepeer/livepeer-ai-go/optionalnullable" "github.com/livepeer/livepeer-ai-go/types" ) @@ -114,6 +113,16 @@ func getSimplePathParams(parentName string, objType reflect.Type, objValue refle } pathParams[parentName] = strings.Join(ppVals, ",") case reflect.Map: + // check if optionalnullable.OptionalNullable[T] + if nullableValue, ok := optionalnullable.AsOptionalNullable(objValue); ok { + // Handle optionalnullable.OptionalNullable[T] using GetUntyped method + if value, isSet := nullableValue.GetUntyped(); isSet && value != nil { + pathParams[parentName] = valToString(value) + } + // If not set or explicitly null, return nil (skip parameter) + return pathParams + } + if objValue.Len() == 0 { return nil } @@ -135,8 +144,6 @@ func getSimplePathParams(parentName string, objType reflect.Type, objValue refle pathParams[parentName] = valToString(objValue.Interface()) case big.Int: pathParams[parentName] = valToString(objValue.Interface()) - case decimal.Big: - pathParams[parentName] = valToString(objValue.Interface()) default: var ppVals []string for i := 0; i < objType.NumField(); i++ { diff --git a/internal/utils/queryparams.go b/internal/utils/queryparams.go index 0039cea..350cd21 100644 --- a/internal/utils/queryparams.go +++ b/internal/utils/queryparams.go @@ -12,8 +12,7 @@ import ( "reflect" "time" - "github.com/ericlagergren/decimal" - + "github.com/livepeer/livepeer-ai-go/optionalnullable" "github.com/livepeer/livepeer-ai-go/types" ) @@ -43,10 +42,13 @@ func PopulateQueryParams(_ context.Context, req *http.Request, queryParams inter } func populateQueryParams(queryParams interface{}, globals interface{}, values url.Values, skipFields []string) ([]string, error) { - queryParamsStructType, queryParamsValType := dereferencePointers(reflect.TypeOf(queryParams), reflect.ValueOf(queryParams)) + queryParamsVal := reflect.ValueOf(queryParams) + if queryParamsVal.Kind() == reflect.Pointer && queryParamsVal.IsNil() { + return nil, nil + } + queryParamsStructType, queryParamsValType := dereferencePointers(reflect.TypeOf(queryParams), queryParamsVal) globalsAlreadyPopulated := []string{} - for i := 0; i < queryParamsStructType.NumField(); i++ { fieldType := queryParamsStructType.Field(i) valType := queryParamsValType.Field(i) @@ -65,6 +67,14 @@ func populateQueryParams(queryParams interface{}, globals interface{}, values ur continue } + constValue := parseConstTag(fieldType) + if constValue != nil { + values.Add(qpTag.ParamName, *constValue) + continue + } + + defaultValue := parseDefaultTag(fieldType) + if globals != nil { var globalFound bool fieldType, valType, globalFound = populateFromGlobals(fieldType, valType, queryParamTagKey, globals) @@ -91,14 +101,14 @@ func populateQueryParams(queryParams interface{}, globals interface{}, values ur } } case "form": - vals := populateFormParams(qpTag, fieldType.Type, valType, ",") + vals := populateFormParams(qpTag, fieldType.Type, valType, ",", defaultValue) for k, v := range vals { for _, vv := range v { values.Add(k, vv) } } case "pipeDelimited": - vals := populateFormParams(qpTag, fieldType.Type, valType, "|") + vals := populateFormParams(qpTag, fieldType.Type, valType, "|", defaultValue) for k, v := range vals { for _, vv := range v { values.Add(k, vv) @@ -149,6 +159,16 @@ func populateDeepObjectParams(tag *paramTag, objType reflect.Type, objValue refl switch objValue.Kind() { case reflect.Map: + // check if optionalnullable.OptionalNullable[T] + if nullableValue, ok := optionalnullable.AsOptionalNullable(objValue); ok { + // Handle optionalnullable.OptionalNullable[T] using GetUntyped method + if value, isSet := nullableValue.GetUntyped(); isSet && value != nil { + values.Add(tag.ParamName, valToString(value)) + } + // If not set or explicitly null, skip adding to values + return values + } + populateDeepObjectParamsMap(values, tag.ParamName, objValue) case reflect.Struct: populateDeepObjectParamsStruct(values, tag.ParamName, objValue) @@ -227,7 +247,7 @@ func populateDeepObjectParamsStruct(qsValues url.Values, priorScope string, stru populateDeepObjectParamsMap(qsValues, scope, fieldValue) case reflect.Struct: switch fieldValue.Type() { - case reflect.TypeOf(big.Int{}), reflect.TypeOf(decimal.Big{}), reflect.TypeOf(time.Time{}), reflect.TypeOf(types.Date{}): + case reflect.TypeOf(big.Int{}), reflect.TypeOf(time.Time{}), reflect.TypeOf(types.Date{}): qsValues.Add(scope, valToString(fieldValue.Interface())) continue @@ -240,8 +260,8 @@ func populateDeepObjectParamsStruct(qsValues url.Values, priorScope string, stru } } -func populateFormParams(tag *paramTag, objType reflect.Type, objValue reflect.Value, delimiter string) url.Values { - return populateForm(tag.ParamName, tag.Explode, objType, objValue, delimiter, func(fieldType reflect.StructField) string { +func populateFormParams(tag *paramTag, objType reflect.Type, objValue reflect.Value, delimiter string, defaultValue *string) url.Values { + return populateForm(tag.ParamName, tag.Explode, objType, objValue, delimiter, defaultValue, func(fieldType reflect.StructField) string { qpTag := parseQueryParamTag(fieldType) if qpTag == nil { return "" diff --git a/internal/utils/requestbody.go b/internal/utils/requestbody.go index 78479c6..4f207a6 100644 --- a/internal/utils/requestbody.go +++ b/internal/utils/requestbody.go @@ -7,10 +7,15 @@ import ( "context" "fmt" "io" + "mime" "mime/multipart" + "net/textproto" "net/url" + "path/filepath" "reflect" "regexp" + + "github.com/livepeer/livepeer-ai-go/optionalnullable" ) const ( @@ -166,9 +171,21 @@ func encodeMultipartFormData(w io.Writer, data interface{}) (string, error) { tag := parseMultipartFormTag(field) if tag.File { - if err := encodeMultipartFormDataFile(writer, tag.Name, fieldType, valType); err != nil { - writer.Close() - return "", err + switch fieldType.Kind() { + case reflect.Slice, reflect.Array: + for i := 0; i < valType.Len(); i++ { + arrayVal := valType.Index(i) + + if err := encodeMultipartFormDataFile(writer, tag.Name+"[]", arrayVal.Type(), arrayVal); err != nil { + writer.Close() + return "", err + } + } + default: + if err := encodeMultipartFormDataFile(writer, tag.Name, fieldType, valType); err != nil { + writer.Close() + return "", err + } } } else if tag.JSON { jw, err := writer.CreateFormField(tag.Name) @@ -243,7 +260,18 @@ func encodeMultipartFormDataFile(w *multipart.Writer, fieldName string, fieldTyp return fmt.Errorf("invalid multipart/form-data file") } - fw, err := w.CreateFormFile(fieldName, fileName) + // Detect content type based on file extension + contentType := mime.TypeByExtension(filepath.Ext(fileName)) + if contentType == "" { + contentType = "application/octet-stream" + } + + // Create multipart header with proper content type + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, fieldName, fileName)) + h.Set("Content-Type", contentType) + + fw, err := w.CreatePart(h) if err != nil { return err } @@ -251,6 +279,11 @@ func encodeMultipartFormDataFile(w *multipart.Writer, fieldName string, fieldTyp return err } + // Reset seek position to 0 if the reader supports seeking + if seeker, ok := reader.(io.Seeker); ok { + _, _ = seeker.Seek(0, io.SeekStart) + } + return nil } @@ -292,7 +325,7 @@ func encodeFormData(fieldName string, w io.Writer, data interface{}) error { switch tag.Style { // TODO: support other styles case "form": - values := populateForm(tag.Name, tag.Explode, fieldType, valType, ",", func(sf reflect.StructField) string { + values := populateForm(tag.Name, tag.Explode, fieldType, valType, ",", nil, func(sf reflect.StructField) string { tag := parseFormTag(field) if tag == nil { return "" @@ -309,6 +342,17 @@ func encodeFormData(fieldName string, w io.Writer, data interface{}) error { } } case reflect.Map: + // check if optionalnullable.OptionalNullable[T] + if nullableValue, ok := optionalnullable.AsOptionalNullable(requestValType); ok { + // Handle optionalnullable.OptionalNullable[T] using GetUntyped method + if value, isSet := nullableValue.GetUntyped(); isSet && value != nil { + dataValues.Set(fieldName, valToString(value)) + } + // If not set or explicitly null, skip adding to form + break + } + + // Handle regular map for _, k := range requestValType.MapKeys() { v := requestValType.MapIndex(k) dataValues.Set(fmt.Sprintf("%v", k.Interface()), valToString(v.Interface())) diff --git a/internal/utils/retries.go b/internal/utils/retries.go index d50cb6e..7334ab6 100644 --- a/internal/utils/retries.go +++ b/internal/utils/retries.go @@ -7,10 +7,12 @@ import ( "errors" "fmt" "github.com/livepeer/livepeer-ai-go/retry" + "io" "math" "math/rand" "net/http" "net/url" + "slices" "strconv" "strings" "time" @@ -27,6 +29,17 @@ type Retries struct { StatusCodes []string } +var ( + // IETF RFC 7231 4.2 safe and idempotent HTTP methods for connection retries + idempotentHTTPMethods = []string{ + http.MethodDelete, + http.MethodGet, + http.MethodHead, + http.MethodOptions, + http.MethodPut, + } +) + func Retry(ctx context.Context, r Retries, operation func() (*http.Response, error)) (*http.Response, error) { switch r.Config.Strategy { case "backoff": @@ -49,11 +62,48 @@ func Retry(ctx context.Context, r Retries, operation func() (*http.Response, err res, err := operation() if err != nil { + if !r.Config.RetryConnectionErrors { + return retry.Permanent(err) + } + + var httpMethod string + + // Use http.Request method if available + if res != nil && res.Request != nil { + httpMethod = res.Request.Method + } + + isIdempotentHTTPMethod := slices.Contains(idempotentHTTPMethods, httpMethod) urlError := new(url.Error) + if errors.As(err, &urlError) { - if (urlError.Temporary() || urlError.Timeout()) && r.Config.RetryConnectionErrors { + if urlError.Temporary() || urlError.Timeout() { return err } + + // In certain error cases, the http.Request may not have + // been populated, so use url.Error.Op which only has its + // first character capitalized from the original request + // HTTP method. + if httpMethod == "" { + httpMethod = strings.ToUpper(urlError.Op) + } + + isIdempotentHTTPMethod = slices.Contains(idempotentHTTPMethods, httpMethod) + + // Connection closed + if errors.Is(urlError.Err, io.EOF) && isIdempotentHTTPMethod { + return err + } + } + + // syscall detection is not available on every platform, so + // fallback to best effort string detection. + isBrokenPipeError := strings.Contains(err.Error(), "broken pipe") + isConnectionResetError := strings.Contains(err.Error(), "connection reset") + + if (isBrokenPipeError || isConnectionResetError) && isIdempotentHTTPMethod { + return err } return retry.Permanent(err) diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 031a71a..0415b53 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -14,8 +14,6 @@ import ( "strconv" "strings" "time" - - "github.com/ericlagergren/decimal" ) const ( @@ -40,8 +38,8 @@ func UnmarshalJsonFromResponseBody(body io.Reader, out interface{}, tag string) if err != nil { return fmt.Errorf("error reading response body: %w", err) } - if err := UnmarshalJSON(data, out, reflect.StructTag(tag), true, false); err != nil { - return fmt.Errorf("error unmarshalling json response body: %w", err) + if err := UnmarshalJSON(data, out, reflect.StructTag(tag), true, nil); err != nil { + return fmt.Errorf("error unmarshaling json response body: %w", err) } return nil @@ -96,6 +94,26 @@ func AsSecuritySource(security interface{}) func(context.Context) (interface{}, } } +func parseConstTag(field reflect.StructField) *string { + value := field.Tag.Get("const") + + if value == "" { + return nil + } + + return &value +} + +func parseDefaultTag(field reflect.StructField) *string { + value := field.Tag.Get("default") + + if value == "" { + return nil + } + + return &value +} + func parseStructTag(tagKey string, field reflect.StructField) map[string]string { tag := field.Tag.Get(tagKey) if tag == "" { @@ -163,8 +181,6 @@ func valToString(val interface{}) string { return v.Format(time.RFC3339Nano) case big.Int: return v.String() - case decimal.Big: - return v.String() default: return fmt.Sprintf("%v", v) } @@ -225,6 +241,21 @@ func isNil(typ reflect.Type, val reflect.Value) bool { return false } +func isEmptyContainer(typ reflect.Type, val reflect.Value) bool { + if isNil(typ, val) { + return true + } + + switch typ.Kind() { + case reflect.Slice, reflect.Array: + return val.Len() == 0 + case reflect.Map: + return val.Len() == 0 + default: + return false + } +} + func contains(arr []string, str string) bool { for _, a := range arr { if a == str { diff --git a/livepeer.go b/livepeer.go index 8f4e56f..c0a06c3 100644 --- a/livepeer.go +++ b/livepeer.go @@ -2,9 +2,12 @@ package livepeeraigo +// Generated from OpenAPI doc version 0.13.11 and generator version 2.753.1 + import ( "context" "fmt" + "github.com/livepeer/livepeer-ai-go/internal/config" "github.com/livepeer/livepeer-ai-go/internal/hooks" "github.com/livepeer/livepeer-ai-go/internal/utils" "github.com/livepeer/livepeer-ai-go/models/components" @@ -21,7 +24,7 @@ var ServerList = []string{ "https://livepeer.studio/api/beta/generate", } -// HTTPClient provides an interface for suplying the SDK with a custom HTTP client +// HTTPClient provides an interface for supplying the SDK with a custom HTTP client type HTTPClient interface { Do(req *http.Request) (*http.Response, error) } @@ -47,34 +50,13 @@ func Float64(f float64) *float64 { return &f } // Pointer provides a helper function to return a pointer to a type func Pointer[T any](v T) *T { return &v } -type sdkConfiguration struct { - Client HTTPClient - Security func(context.Context) (interface{}, error) - ServerURL string - ServerIndex int - Language string - OpenAPIDocVersion string - SDKVersion string - GenVersion string - UserAgent string - RetryConfig *retry.Config - Hooks *hooks.Hooks - Timeout *time.Duration -} - -func (c *sdkConfiguration) GetServerDetails() (string, map[string]string) { - if c.ServerURL != "" { - return c.ServerURL, nil - } - - return ServerList[c.ServerIndex], nil -} - // Livepeer AI Runner: An application to run AI pipelines type Livepeer struct { - Generate *Generate + SDKVersion string + Generate *Generate - sdkConfiguration sdkConfiguration + sdkConfiguration config.SDKConfiguration + hooks *hooks.Hooks } type SDKOption func(*Livepeer) @@ -148,14 +130,12 @@ func WithTimeout(timeout time.Duration) SDKOption { // New creates a new instance of the SDK with the provided options func New(opts ...SDKOption) *Livepeer { sdk := &Livepeer{ - sdkConfiguration: sdkConfiguration{ - Language: "go", - OpenAPIDocVersion: "0.0.0", - SDKVersion: "0.7.1", - GenVersion: "2.499.0", - UserAgent: "speakeasy-sdk/go 0.7.1 2.499.0 0.0.0 github.com/livepeer/livepeer-ai-go", - Hooks: hooks.New(), + SDKVersion: "0.8.0", + sdkConfiguration: config.SDKConfiguration{ + UserAgent: "speakeasy-sdk/go 0.8.0 2.753.1 0.13.11 github.com/livepeer/livepeer-ai-go", + ServerList: ServerList, }, + hooks: hooks.New(), } for _, opt := range opts { opt(sdk) @@ -168,12 +148,12 @@ func New(opts ...SDKOption) *Livepeer { currentServerURL, _ := sdk.sdkConfiguration.GetServerDetails() serverURL := currentServerURL - serverURL, sdk.sdkConfiguration.Client = sdk.sdkConfiguration.Hooks.SDKInit(currentServerURL, sdk.sdkConfiguration.Client) - if serverURL != currentServerURL { + serverURL, sdk.sdkConfiguration.Client = sdk.hooks.SDKInit(currentServerURL, sdk.sdkConfiguration.Client) + if currentServerURL != serverURL { sdk.sdkConfiguration.ServerURL = serverURL } - sdk.Generate = newGenerate(sdk.sdkConfiguration) + sdk.Generate = newGenerate(sdk, sdk.sdkConfiguration, sdk.hooks) return sdk } diff --git a/models/components/apierror.go b/models/components/apierror.go index 652b153..9953417 100644 --- a/models/components/apierror.go +++ b/models/components/apierror.go @@ -8,9 +8,9 @@ type APIError struct { Msg string `json:"msg"` } -func (o *APIError) GetMsg() string { - if o == nil { +func (a *APIError) GetMsg() string { + if a == nil { return "" } - return o.Msg + return a.Msg } diff --git a/models/components/audioresponse.go b/models/components/audioresponse.go index ecb89f9..e982fec 100644 --- a/models/components/audioresponse.go +++ b/models/components/audioresponse.go @@ -8,9 +8,9 @@ type AudioResponse struct { Audio MediaURL `json:"audio"` } -func (o *AudioResponse) GetAudio() MediaURL { - if o == nil { +func (a *AudioResponse) GetAudio() MediaURL { + if a == nil { return MediaURL{} } - return o.Audio + return a.Audio } diff --git a/models/components/bodygenaudiototext.go b/models/components/bodygenaudiototext.go index 49b9528..4c65bb0 100644 --- a/models/components/bodygenaudiototext.go +++ b/models/components/bodygenaudiototext.go @@ -12,18 +12,18 @@ type Audio struct { Content any `multipartForm:"content"` } -func (o *Audio) GetFileName() string { - if o == nil { +func (a *Audio) GetFileName() string { + if a == nil { return "" } - return o.FileName + return a.FileName } -func (o *Audio) GetContent() any { - if o == nil { +func (a *Audio) GetContent() any { + if a == nil { return nil } - return o.Content + return a.Content } type BodyGenAudioToText struct { @@ -40,29 +40,29 @@ func (b BodyGenAudioToText) MarshalJSON() ([]byte, error) { } func (b *BodyGenAudioToText) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &b, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &b, "", false, []string{"audio"}); err != nil { return err } return nil } -func (o *BodyGenAudioToText) GetAudio() Audio { - if o == nil { +func (b *BodyGenAudioToText) GetAudio() Audio { + if b == nil { return Audio{} } - return o.Audio + return b.Audio } -func (o *BodyGenAudioToText) GetModelID() *string { - if o == nil { +func (b *BodyGenAudioToText) GetModelID() *string { + if b == nil { return nil } - return o.ModelID + return b.ModelID } -func (o *BodyGenAudioToText) GetReturnTimestamps() *string { - if o == nil { +func (b *BodyGenAudioToText) GetReturnTimestamps() *string { + if b == nil { return nil } - return o.ReturnTimestamps + return b.ReturnTimestamps } diff --git a/models/components/bodygenimagetoimage.go b/models/components/bodygenimagetoimage.go index 942e6ec..e207c5c 100644 --- a/models/components/bodygenimagetoimage.go +++ b/models/components/bodygenimagetoimage.go @@ -12,18 +12,18 @@ type Image struct { Content any `multipartForm:"content"` } -func (o *Image) GetFileName() string { - if o == nil { +func (i *Image) GetFileName() string { + if i == nil { return "" } - return o.FileName + return i.FileName } -func (o *Image) GetContent() any { - if o == nil { +func (i *Image) GetContent() any { + if i == nil { return nil } - return o.Content + return i.Content } type BodyGenImageToImage struct { @@ -58,92 +58,92 @@ func (b BodyGenImageToImage) MarshalJSON() ([]byte, error) { } func (b *BodyGenImageToImage) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &b, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &b, "", false, []string{"prompt", "image"}); err != nil { return err } return nil } -func (o *BodyGenImageToImage) GetPrompt() string { - if o == nil { +func (b *BodyGenImageToImage) GetPrompt() string { + if b == nil { return "" } - return o.Prompt + return b.Prompt } -func (o *BodyGenImageToImage) GetImage() Image { - if o == nil { +func (b *BodyGenImageToImage) GetImage() Image { + if b == nil { return Image{} } - return o.Image + return b.Image } -func (o *BodyGenImageToImage) GetModelID() *string { - if o == nil { +func (b *BodyGenImageToImage) GetModelID() *string { + if b == nil { return nil } - return o.ModelID + return b.ModelID } -func (o *BodyGenImageToImage) GetLoras() *string { - if o == nil { +func (b *BodyGenImageToImage) GetLoras() *string { + if b == nil { return nil } - return o.Loras + return b.Loras } -func (o *BodyGenImageToImage) GetStrength() *float64 { - if o == nil { +func (b *BodyGenImageToImage) GetStrength() *float64 { + if b == nil { return nil } - return o.Strength + return b.Strength } -func (o *BodyGenImageToImage) GetGuidanceScale() *float64 { - if o == nil { +func (b *BodyGenImageToImage) GetGuidanceScale() *float64 { + if b == nil { return nil } - return o.GuidanceScale + return b.GuidanceScale } -func (o *BodyGenImageToImage) GetImageGuidanceScale() *float64 { - if o == nil { +func (b *BodyGenImageToImage) GetImageGuidanceScale() *float64 { + if b == nil { return nil } - return o.ImageGuidanceScale + return b.ImageGuidanceScale } -func (o *BodyGenImageToImage) GetNegativePrompt() *string { - if o == nil { +func (b *BodyGenImageToImage) GetNegativePrompt() *string { + if b == nil { return nil } - return o.NegativePrompt + return b.NegativePrompt } -func (o *BodyGenImageToImage) GetSafetyCheck() *bool { - if o == nil { +func (b *BodyGenImageToImage) GetSafetyCheck() *bool { + if b == nil { return nil } - return o.SafetyCheck + return b.SafetyCheck } -func (o *BodyGenImageToImage) GetSeed() *int64 { - if o == nil { +func (b *BodyGenImageToImage) GetSeed() *int64 { + if b == nil { return nil } - return o.Seed + return b.Seed } -func (o *BodyGenImageToImage) GetNumInferenceSteps() *int64 { - if o == nil { +func (b *BodyGenImageToImage) GetNumInferenceSteps() *int64 { + if b == nil { return nil } - return o.NumInferenceSteps + return b.NumInferenceSteps } -func (o *BodyGenImageToImage) GetNumImagesPerPrompt() *int64 { - if o == nil { +func (b *BodyGenImageToImage) GetNumImagesPerPrompt() *int64 { + if b == nil { return nil } - return o.NumImagesPerPrompt + return b.NumImagesPerPrompt } diff --git a/models/components/bodygenimagetotext.go b/models/components/bodygenimagetotext.go index 4473e62..9a5ccb5 100644 --- a/models/components/bodygenimagetotext.go +++ b/models/components/bodygenimagetotext.go @@ -12,18 +12,18 @@ type BodyGenImageToTextImage struct { Content any `multipartForm:"content"` } -func (o *BodyGenImageToTextImage) GetFileName() string { - if o == nil { +func (b *BodyGenImageToTextImage) GetFileName() string { + if b == nil { return "" } - return o.FileName + return b.FileName } -func (o *BodyGenImageToTextImage) GetContent() any { - if o == nil { +func (b *BodyGenImageToTextImage) GetContent() any { + if b == nil { return nil } - return o.Content + return b.Content } type BodyGenImageToText struct { @@ -40,29 +40,29 @@ func (b BodyGenImageToText) MarshalJSON() ([]byte, error) { } func (b *BodyGenImageToText) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &b, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &b, "", false, []string{"image"}); err != nil { return err } return nil } -func (o *BodyGenImageToText) GetImage() BodyGenImageToTextImage { - if o == nil { +func (b *BodyGenImageToText) GetImage() BodyGenImageToTextImage { + if b == nil { return BodyGenImageToTextImage{} } - return o.Image + return b.Image } -func (o *BodyGenImageToText) GetPrompt() *string { - if o == nil { +func (b *BodyGenImageToText) GetPrompt() *string { + if b == nil { return nil } - return o.Prompt + return b.Prompt } -func (o *BodyGenImageToText) GetModelID() *string { - if o == nil { +func (b *BodyGenImageToText) GetModelID() *string { + if b == nil { return nil } - return o.ModelID + return b.ModelID } diff --git a/models/components/bodygenimagetovideo.go b/models/components/bodygenimagetovideo.go index 07bc220..c556c42 100644 --- a/models/components/bodygenimagetovideo.go +++ b/models/components/bodygenimagetovideo.go @@ -12,18 +12,18 @@ type BodyGenImageToVideoImage struct { Content any `multipartForm:"content"` } -func (o *BodyGenImageToVideoImage) GetFileName() string { - if o == nil { +func (b *BodyGenImageToVideoImage) GetFileName() string { + if b == nil { return "" } - return o.FileName + return b.FileName } -func (o *BodyGenImageToVideoImage) GetContent() any { - if o == nil { +func (b *BodyGenImageToVideoImage) GetContent() any { + if b == nil { return nil } - return o.Content + return b.Content } type BodyGenImageToVideo struct { @@ -54,78 +54,78 @@ func (b BodyGenImageToVideo) MarshalJSON() ([]byte, error) { } func (b *BodyGenImageToVideo) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &b, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &b, "", false, []string{"image"}); err != nil { return err } return nil } -func (o *BodyGenImageToVideo) GetImage() BodyGenImageToVideoImage { - if o == nil { +func (b *BodyGenImageToVideo) GetImage() BodyGenImageToVideoImage { + if b == nil { return BodyGenImageToVideoImage{} } - return o.Image + return b.Image } -func (o *BodyGenImageToVideo) GetModelID() *string { - if o == nil { +func (b *BodyGenImageToVideo) GetModelID() *string { + if b == nil { return nil } - return o.ModelID + return b.ModelID } -func (o *BodyGenImageToVideo) GetHeight() *int64 { - if o == nil { +func (b *BodyGenImageToVideo) GetHeight() *int64 { + if b == nil { return nil } - return o.Height + return b.Height } -func (o *BodyGenImageToVideo) GetWidth() *int64 { - if o == nil { +func (b *BodyGenImageToVideo) GetWidth() *int64 { + if b == nil { return nil } - return o.Width + return b.Width } -func (o *BodyGenImageToVideo) GetFps() *int64 { - if o == nil { +func (b *BodyGenImageToVideo) GetFps() *int64 { + if b == nil { return nil } - return o.Fps + return b.Fps } -func (o *BodyGenImageToVideo) GetMotionBucketID() *int64 { - if o == nil { +func (b *BodyGenImageToVideo) GetMotionBucketID() *int64 { + if b == nil { return nil } - return o.MotionBucketID + return b.MotionBucketID } -func (o *BodyGenImageToVideo) GetNoiseAugStrength() *float64 { - if o == nil { +func (b *BodyGenImageToVideo) GetNoiseAugStrength() *float64 { + if b == nil { return nil } - return o.NoiseAugStrength + return b.NoiseAugStrength } -func (o *BodyGenImageToVideo) GetSafetyCheck() *bool { - if o == nil { +func (b *BodyGenImageToVideo) GetSafetyCheck() *bool { + if b == nil { return nil } - return o.SafetyCheck + return b.SafetyCheck } -func (o *BodyGenImageToVideo) GetSeed() *int64 { - if o == nil { +func (b *BodyGenImageToVideo) GetSeed() *int64 { + if b == nil { return nil } - return o.Seed + return b.Seed } -func (o *BodyGenImageToVideo) GetNumInferenceSteps() *int64 { - if o == nil { +func (b *BodyGenImageToVideo) GetNumInferenceSteps() *int64 { + if b == nil { return nil } - return o.NumInferenceSteps + return b.NumInferenceSteps } diff --git a/models/components/bodygensegmentanything2.go b/models/components/bodygensegmentanything2.go index 36d5c60..4489d4c 100644 --- a/models/components/bodygensegmentanything2.go +++ b/models/components/bodygensegmentanything2.go @@ -12,18 +12,18 @@ type BodyGenSegmentAnything2Image struct { Content any `multipartForm:"content"` } -func (o *BodyGenSegmentAnything2Image) GetFileName() string { - if o == nil { +func (b *BodyGenSegmentAnything2Image) GetFileName() string { + if b == nil { return "" } - return o.FileName + return b.FileName } -func (o *BodyGenSegmentAnything2Image) GetContent() any { - if o == nil { +func (b *BodyGenSegmentAnything2Image) GetContent() any { + if b == nil { return nil } - return o.Content + return b.Content } type BodyGenSegmentAnything2 struct { @@ -52,71 +52,71 @@ func (b BodyGenSegmentAnything2) MarshalJSON() ([]byte, error) { } func (b *BodyGenSegmentAnything2) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &b, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &b, "", false, []string{"image"}); err != nil { return err } return nil } -func (o *BodyGenSegmentAnything2) GetImage() BodyGenSegmentAnything2Image { - if o == nil { +func (b *BodyGenSegmentAnything2) GetImage() BodyGenSegmentAnything2Image { + if b == nil { return BodyGenSegmentAnything2Image{} } - return o.Image + return b.Image } -func (o *BodyGenSegmentAnything2) GetModelID() *string { - if o == nil { +func (b *BodyGenSegmentAnything2) GetModelID() *string { + if b == nil { return nil } - return o.ModelID + return b.ModelID } -func (o *BodyGenSegmentAnything2) GetPointCoords() *string { - if o == nil { +func (b *BodyGenSegmentAnything2) GetPointCoords() *string { + if b == nil { return nil } - return o.PointCoords + return b.PointCoords } -func (o *BodyGenSegmentAnything2) GetPointLabels() *string { - if o == nil { +func (b *BodyGenSegmentAnything2) GetPointLabels() *string { + if b == nil { return nil } - return o.PointLabels + return b.PointLabels } -func (o *BodyGenSegmentAnything2) GetBox() *string { - if o == nil { +func (b *BodyGenSegmentAnything2) GetBox() *string { + if b == nil { return nil } - return o.Box + return b.Box } -func (o *BodyGenSegmentAnything2) GetMaskInput() *string { - if o == nil { +func (b *BodyGenSegmentAnything2) GetMaskInput() *string { + if b == nil { return nil } - return o.MaskInput + return b.MaskInput } -func (o *BodyGenSegmentAnything2) GetMultimaskOutput() *bool { - if o == nil { +func (b *BodyGenSegmentAnything2) GetMultimaskOutput() *bool { + if b == nil { return nil } - return o.MultimaskOutput + return b.MultimaskOutput } -func (o *BodyGenSegmentAnything2) GetReturnLogits() *bool { - if o == nil { +func (b *BodyGenSegmentAnything2) GetReturnLogits() *bool { + if b == nil { return nil } - return o.ReturnLogits + return b.ReturnLogits } -func (o *BodyGenSegmentAnything2) GetNormalizeCoords() *bool { - if o == nil { +func (b *BodyGenSegmentAnything2) GetNormalizeCoords() *bool { + if b == nil { return nil } - return o.NormalizeCoords + return b.NormalizeCoords } diff --git a/models/components/bodygenupscale.go b/models/components/bodygenupscale.go index 6a1ce6c..098a518 100644 --- a/models/components/bodygenupscale.go +++ b/models/components/bodygenupscale.go @@ -12,18 +12,18 @@ type BodyGenUpscaleImage struct { Content any `multipartForm:"content"` } -func (o *BodyGenUpscaleImage) GetFileName() string { - if o == nil { +func (b *BodyGenUpscaleImage) GetFileName() string { + if b == nil { return "" } - return o.FileName + return b.FileName } -func (o *BodyGenUpscaleImage) GetContent() any { - if o == nil { +func (b *BodyGenUpscaleImage) GetContent() any { + if b == nil { return nil } - return o.Content + return b.Content } type BodyGenUpscale struct { @@ -46,50 +46,50 @@ func (b BodyGenUpscale) MarshalJSON() ([]byte, error) { } func (b *BodyGenUpscale) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &b, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &b, "", false, []string{"prompt", "image"}); err != nil { return err } return nil } -func (o *BodyGenUpscale) GetPrompt() string { - if o == nil { +func (b *BodyGenUpscale) GetPrompt() string { + if b == nil { return "" } - return o.Prompt + return b.Prompt } -func (o *BodyGenUpscale) GetImage() BodyGenUpscaleImage { - if o == nil { +func (b *BodyGenUpscale) GetImage() BodyGenUpscaleImage { + if b == nil { return BodyGenUpscaleImage{} } - return o.Image + return b.Image } -func (o *BodyGenUpscale) GetModelID() *string { - if o == nil { +func (b *BodyGenUpscale) GetModelID() *string { + if b == nil { return nil } - return o.ModelID + return b.ModelID } -func (o *BodyGenUpscale) GetSafetyCheck() *bool { - if o == nil { +func (b *BodyGenUpscale) GetSafetyCheck() *bool { + if b == nil { return nil } - return o.SafetyCheck + return b.SafetyCheck } -func (o *BodyGenUpscale) GetSeed() *int64 { - if o == nil { +func (b *BodyGenUpscale) GetSeed() *int64 { + if b == nil { return nil } - return o.Seed + return b.Seed } -func (o *BodyGenUpscale) GetNumInferenceSteps() *int64 { - if o == nil { +func (b *BodyGenUpscale) GetNumInferenceSteps() *int64 { + if b == nil { return nil } - return o.NumInferenceSteps + return b.NumInferenceSteps } diff --git a/models/components/chunk.go b/models/components/chunk.go index fb85d67..70aad94 100644 --- a/models/components/chunk.go +++ b/models/components/chunk.go @@ -10,16 +10,16 @@ type Chunk struct { Text string `json:"text"` } -func (o *Chunk) GetTimestamp() []any { - if o == nil { +func (c *Chunk) GetTimestamp() []any { + if c == nil { return []any{} } - return o.Timestamp + return c.Timestamp } -func (o *Chunk) GetText() string { - if o == nil { +func (c *Chunk) GetText() string { + if c == nil { return "" } - return o.Text + return c.Text } diff --git a/models/components/httpmetadata.go b/models/components/httpmetadata.go index e18bdc0..148ec47 100644 --- a/models/components/httpmetadata.go +++ b/models/components/httpmetadata.go @@ -13,16 +13,16 @@ type HTTPMetadata struct { Request *http.Request `json:"-"` } -func (o *HTTPMetadata) GetResponse() *http.Response { - if o == nil { +func (h *HTTPMetadata) GetResponse() *http.Response { + if h == nil { return nil } - return o.Response + return h.Response } -func (o *HTTPMetadata) GetRequest() *http.Request { - if o == nil { +func (h *HTTPMetadata) GetRequest() *http.Request { + if h == nil { return nil } - return o.Request + return h.Request } diff --git a/models/components/imageresponse.go b/models/components/imageresponse.go index 6630673..2f6cff0 100644 --- a/models/components/imageresponse.go +++ b/models/components/imageresponse.go @@ -8,9 +8,9 @@ type ImageResponse struct { Images []Media `json:"images"` } -func (o *ImageResponse) GetImages() []Media { - if o == nil { +func (i *ImageResponse) GetImages() []Media { + if i == nil { return []Media{} } - return o.Images + return i.Images } diff --git a/models/components/imagetotextresponse.go b/models/components/imagetotextresponse.go index c3d5635..8496dad 100644 --- a/models/components/imagetotextresponse.go +++ b/models/components/imagetotextresponse.go @@ -8,9 +8,9 @@ type ImageToTextResponse struct { Text string `json:"text"` } -func (o *ImageToTextResponse) GetText() string { - if o == nil { +func (i *ImageToTextResponse) GetText() string { + if i == nil { return "" } - return o.Text + return i.Text } diff --git a/models/components/livevideotovideoparams.go b/models/components/livevideotovideoparams.go index 2d65a8e..0d306ef 100644 --- a/models/components/livevideotovideoparams.go +++ b/models/components/livevideotovideoparams.go @@ -23,6 +23,12 @@ type LiveVideoToVideoParams struct { ModelID *string `default:"" json:"model_id"` // Initial parameters for the pipeline. Params *Params `json:"params,omitempty"` + // The ID of the Gateway request (for logging purposes). + GatewayRequestID *string `default:"" json:"gateway_request_id"` + // The manifest ID from the orchestrator (for logging purposes). + ManifestID *string `default:"" json:"manifest_id"` + // The Stream ID (for logging purposes). + StreamID *string `default:"" json:"stream_id"` } func (l LiveVideoToVideoParams) MarshalJSON() ([]byte, error) { @@ -30,50 +36,71 @@ func (l LiveVideoToVideoParams) MarshalJSON() ([]byte, error) { } func (l *LiveVideoToVideoParams) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &l, "", false, []string{"subscribe_url", "publish_url"}); err != nil { return err } return nil } -func (o *LiveVideoToVideoParams) GetSubscribeURL() string { - if o == nil { +func (l *LiveVideoToVideoParams) GetSubscribeURL() string { + if l == nil { return "" } - return o.SubscribeURL + return l.SubscribeURL } -func (o *LiveVideoToVideoParams) GetPublishURL() string { - if o == nil { +func (l *LiveVideoToVideoParams) GetPublishURL() string { + if l == nil { return "" } - return o.PublishURL + return l.PublishURL } -func (o *LiveVideoToVideoParams) GetControlURL() *string { - if o == nil { +func (l *LiveVideoToVideoParams) GetControlURL() *string { + if l == nil { return nil } - return o.ControlURL + return l.ControlURL } -func (o *LiveVideoToVideoParams) GetEventsURL() *string { - if o == nil { +func (l *LiveVideoToVideoParams) GetEventsURL() *string { + if l == nil { return nil } - return o.EventsURL + return l.EventsURL } -func (o *LiveVideoToVideoParams) GetModelID() *string { - if o == nil { +func (l *LiveVideoToVideoParams) GetModelID() *string { + if l == nil { return nil } - return o.ModelID + return l.ModelID } -func (o *LiveVideoToVideoParams) GetParams() *Params { - if o == nil { +func (l *LiveVideoToVideoParams) GetParams() *Params { + if l == nil { return nil } - return o.Params + return l.Params +} + +func (l *LiveVideoToVideoParams) GetGatewayRequestID() *string { + if l == nil { + return nil + } + return l.GatewayRequestID +} + +func (l *LiveVideoToVideoParams) GetManifestID() *string { + if l == nil { + return nil + } + return l.ManifestID +} + +func (l *LiveVideoToVideoParams) GetStreamID() *string { + if l == nil { + return nil + } + return l.StreamID } diff --git a/models/components/livevideotovideoresponse.go b/models/components/livevideotovideoresponse.go index bda30d5..5ceb480 100644 --- a/models/components/livevideotovideoresponse.go +++ b/models/components/livevideotovideoresponse.go @@ -16,6 +16,10 @@ type LiveVideoToVideoResponse struct { ControlURL *string `default:"" json:"control_url"` // URL for subscribing to events for pipeline status and logs EventsURL *string `default:"" json:"events_url"` + // The ID generated for this request + RequestID *string `default:"" json:"request_id"` + // Orchestrator manifest ID for this request + ManifestID *string `default:"" json:"manifest_id"` } func (l LiveVideoToVideoResponse) MarshalJSON() ([]byte, error) { @@ -23,36 +27,50 @@ func (l LiveVideoToVideoResponse) MarshalJSON() ([]byte, error) { } func (l *LiveVideoToVideoResponse) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &l, "", false, []string{"subscribe_url", "publish_url"}); err != nil { return err } return nil } -func (o *LiveVideoToVideoResponse) GetSubscribeURL() string { - if o == nil { +func (l *LiveVideoToVideoResponse) GetSubscribeURL() string { + if l == nil { return "" } - return o.SubscribeURL + return l.SubscribeURL } -func (o *LiveVideoToVideoResponse) GetPublishURL() string { - if o == nil { +func (l *LiveVideoToVideoResponse) GetPublishURL() string { + if l == nil { return "" } - return o.PublishURL + return l.PublishURL } -func (o *LiveVideoToVideoResponse) GetControlURL() *string { - if o == nil { +func (l *LiveVideoToVideoResponse) GetControlURL() *string { + if l == nil { return nil } - return o.ControlURL + return l.ControlURL } -func (o *LiveVideoToVideoResponse) GetEventsURL() *string { - if o == nil { +func (l *LiveVideoToVideoResponse) GetEventsURL() *string { + if l == nil { return nil } - return o.EventsURL + return l.EventsURL +} + +func (l *LiveVideoToVideoResponse) GetRequestID() *string { + if l == nil { + return nil + } + return l.RequestID +} + +func (l *LiveVideoToVideoResponse) GetManifestID() *string { + if l == nil { + return nil + } + return l.ManifestID } diff --git a/models/components/llmchoice.go b/models/components/llmchoice.go index 4579630..0569529 100644 --- a/models/components/llmchoice.go +++ b/models/components/llmchoice.go @@ -18,36 +18,36 @@ func (l LLMChoice) MarshalJSON() ([]byte, error) { } func (l *LLMChoice) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &l, "", false, []string{"index"}); err != nil { return err } return nil } -func (o *LLMChoice) GetIndex() int64 { - if o == nil { +func (l *LLMChoice) GetIndex() int64 { + if l == nil { return 0 } - return o.Index + return l.Index } -func (o *LLMChoice) GetFinishReason() *string { - if o == nil { +func (l *LLMChoice) GetFinishReason() *string { + if l == nil { return nil } - return o.FinishReason + return l.FinishReason } -func (o *LLMChoice) GetDelta() *LLMMessage { - if o == nil { +func (l *LLMChoice) GetDelta() *LLMMessage { + if l == nil { return nil } - return o.Delta + return l.Delta } -func (o *LLMChoice) GetMessage() *LLMMessage { - if o == nil { +func (l *LLMChoice) GetMessage() *LLMMessage { + if l == nil { return nil } - return o.Message + return l.Message } diff --git a/models/components/llmmessage.go b/models/components/llmmessage.go index 96d69d0..a5bb2c2 100644 --- a/models/components/llmmessage.go +++ b/models/components/llmmessage.go @@ -7,16 +7,16 @@ type LLMMessage struct { Content string `json:"content"` } -func (o *LLMMessage) GetRole() string { - if o == nil { +func (l *LLMMessage) GetRole() string { + if l == nil { return "" } - return o.Role + return l.Role } -func (o *LLMMessage) GetContent() string { - if o == nil { +func (l *LLMMessage) GetContent() string { + if l == nil { return "" } - return o.Content + return l.Content } diff --git a/models/components/llmrequest.go b/models/components/llmrequest.go index 2ab4da0..51167ea 100644 --- a/models/components/llmrequest.go +++ b/models/components/llmrequest.go @@ -21,57 +21,57 @@ func (l LLMRequest) MarshalJSON() ([]byte, error) { } func (l *LLMRequest) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &l, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &l, "", false, []string{"messages"}); err != nil { return err } return nil } -func (o *LLMRequest) GetMessages() []LLMMessage { - if o == nil { +func (l *LLMRequest) GetMessages() []LLMMessage { + if l == nil { return []LLMMessage{} } - return o.Messages + return l.Messages } -func (o *LLMRequest) GetModel() *string { - if o == nil { +func (l *LLMRequest) GetModel() *string { + if l == nil { return nil } - return o.Model + return l.Model } -func (o *LLMRequest) GetTemperature() *float64 { - if o == nil { +func (l *LLMRequest) GetTemperature() *float64 { + if l == nil { return nil } - return o.Temperature + return l.Temperature } -func (o *LLMRequest) GetMaxTokens() *int64 { - if o == nil { +func (l *LLMRequest) GetMaxTokens() *int64 { + if l == nil { return nil } - return o.MaxTokens + return l.MaxTokens } -func (o *LLMRequest) GetTopP() *float64 { - if o == nil { +func (l *LLMRequest) GetTopP() *float64 { + if l == nil { return nil } - return o.TopP + return l.TopP } -func (o *LLMRequest) GetTopK() *int64 { - if o == nil { +func (l *LLMRequest) GetTopK() *int64 { + if l == nil { return nil } - return o.TopK + return l.TopK } -func (o *LLMRequest) GetStream() *bool { - if o == nil { +func (l *LLMRequest) GetStream() *bool { + if l == nil { return nil } - return o.Stream + return l.Stream } diff --git a/models/components/llmresponse.go b/models/components/llmresponse.go index 1d33b08..18a7baa 100644 --- a/models/components/llmresponse.go +++ b/models/components/llmresponse.go @@ -10,37 +10,37 @@ type LLMResponse struct { Choices []LLMChoice `json:"choices"` } -func (o *LLMResponse) GetID() string { - if o == nil { +func (l *LLMResponse) GetID() string { + if l == nil { return "" } - return o.ID + return l.ID } -func (o *LLMResponse) GetModel() string { - if o == nil { +func (l *LLMResponse) GetModel() string { + if l == nil { return "" } - return o.Model + return l.Model } -func (o *LLMResponse) GetCreated() int64 { - if o == nil { +func (l *LLMResponse) GetCreated() int64 { + if l == nil { return 0 } - return o.Created + return l.Created } -func (o *LLMResponse) GetUsage() LLMTokenUsage { - if o == nil { +func (l *LLMResponse) GetUsage() LLMTokenUsage { + if l == nil { return LLMTokenUsage{} } - return o.Usage + return l.Usage } -func (o *LLMResponse) GetChoices() []LLMChoice { - if o == nil { +func (l *LLMResponse) GetChoices() []LLMChoice { + if l == nil { return []LLMChoice{} } - return o.Choices + return l.Choices } diff --git a/models/components/llmtokenusage.go b/models/components/llmtokenusage.go index 55826fe..e01e9fc 100644 --- a/models/components/llmtokenusage.go +++ b/models/components/llmtokenusage.go @@ -8,23 +8,23 @@ type LLMTokenUsage struct { TotalTokens int64 `json:"total_tokens"` } -func (o *LLMTokenUsage) GetPromptTokens() int64 { - if o == nil { +func (l *LLMTokenUsage) GetPromptTokens() int64 { + if l == nil { return 0 } - return o.PromptTokens + return l.PromptTokens } -func (o *LLMTokenUsage) GetCompletionTokens() int64 { - if o == nil { +func (l *LLMTokenUsage) GetCompletionTokens() int64 { + if l == nil { return 0 } - return o.CompletionTokens + return l.CompletionTokens } -func (o *LLMTokenUsage) GetTotalTokens() int64 { - if o == nil { +func (l *LLMTokenUsage) GetTotalTokens() int64 { + if l == nil { return 0 } - return o.TotalTokens + return l.TotalTokens } diff --git a/models/components/masksresponse.go b/models/components/masksresponse.go index 62dc852..42efc6c 100644 --- a/models/components/masksresponse.go +++ b/models/components/masksresponse.go @@ -12,23 +12,23 @@ type MasksResponse struct { Logits string `json:"logits"` } -func (o *MasksResponse) GetMasks() string { - if o == nil { +func (m *MasksResponse) GetMasks() string { + if m == nil { return "" } - return o.Masks + return m.Masks } -func (o *MasksResponse) GetScores() string { - if o == nil { +func (m *MasksResponse) GetScores() string { + if m == nil { return "" } - return o.Scores + return m.Scores } -func (o *MasksResponse) GetLogits() string { - if o == nil { +func (m *MasksResponse) GetLogits() string { + if m == nil { return "" } - return o.Logits + return m.Logits } diff --git a/models/components/media.go b/models/components/media.go index 564327e..7cde9c3 100644 --- a/models/components/media.go +++ b/models/components/media.go @@ -12,23 +12,23 @@ type Media struct { Nsfw bool `json:"nsfw"` } -func (o *Media) GetURL() string { - if o == nil { +func (m *Media) GetURL() string { + if m == nil { return "" } - return o.URL + return m.URL } -func (o *Media) GetSeed() int64 { - if o == nil { +func (m *Media) GetSeed() int64 { + if m == nil { return 0 } - return o.Seed + return m.Seed } -func (o *Media) GetNsfw() bool { - if o == nil { +func (m *Media) GetNsfw() bool { + if m == nil { return false } - return o.Nsfw + return m.Nsfw } diff --git a/models/components/mediaurl.go b/models/components/mediaurl.go index 6755932..bcc4438 100644 --- a/models/components/mediaurl.go +++ b/models/components/mediaurl.go @@ -8,9 +8,9 @@ type MediaURL struct { URL string `json:"url"` } -func (o *MediaURL) GetURL() string { - if o == nil { +func (m *MediaURL) GetURL() string { + if m == nil { return "" } - return o.URL + return m.URL } diff --git a/models/components/security.go b/models/components/security.go index 42d6588..5767e20 100644 --- a/models/components/security.go +++ b/models/components/security.go @@ -6,9 +6,9 @@ type Security struct { HTTPBearer string `security:"scheme,type=http,subtype=bearer,name=Authorization"` } -func (o *Security) GetHTTPBearer() string { - if o == nil { +func (s *Security) GetHTTPBearer() string { + if s == nil { return "" } - return o.HTTPBearer + return s.HTTPBearer } diff --git a/models/components/textresponse.go b/models/components/textresponse.go index af32181..bbf6326 100644 --- a/models/components/textresponse.go +++ b/models/components/textresponse.go @@ -10,16 +10,16 @@ type TextResponse struct { Chunks []Chunk `json:"chunks"` } -func (o *TextResponse) GetText() string { - if o == nil { +func (t *TextResponse) GetText() string { + if t == nil { return "" } - return o.Text + return t.Text } -func (o *TextResponse) GetChunks() []Chunk { - if o == nil { +func (t *TextResponse) GetChunks() []Chunk { + if t == nil { return []Chunk{} } - return o.Chunks + return t.Chunks } diff --git a/models/components/texttoimageparams.go b/models/components/texttoimageparams.go index 206b3c6..6661322 100644 --- a/models/components/texttoimageparams.go +++ b/models/components/texttoimageparams.go @@ -36,85 +36,85 @@ func (t TextToImageParams) MarshalJSON() ([]byte, error) { } func (t *TextToImageParams) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &t, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &t, "", false, []string{"prompt"}); err != nil { return err } return nil } -func (o *TextToImageParams) GetModelID() *string { - if o == nil { +func (t *TextToImageParams) GetModelID() *string { + if t == nil { return nil } - return o.ModelID + return t.ModelID } -func (o *TextToImageParams) GetLoras() *string { - if o == nil { +func (t *TextToImageParams) GetLoras() *string { + if t == nil { return nil } - return o.Loras + return t.Loras } -func (o *TextToImageParams) GetPrompt() string { - if o == nil { +func (t *TextToImageParams) GetPrompt() string { + if t == nil { return "" } - return o.Prompt + return t.Prompt } -func (o *TextToImageParams) GetHeight() *int64 { - if o == nil { +func (t *TextToImageParams) GetHeight() *int64 { + if t == nil { return nil } - return o.Height + return t.Height } -func (o *TextToImageParams) GetWidth() *int64 { - if o == nil { +func (t *TextToImageParams) GetWidth() *int64 { + if t == nil { return nil } - return o.Width + return t.Width } -func (o *TextToImageParams) GetGuidanceScale() *float64 { - if o == nil { +func (t *TextToImageParams) GetGuidanceScale() *float64 { + if t == nil { return nil } - return o.GuidanceScale + return t.GuidanceScale } -func (o *TextToImageParams) GetNegativePrompt() *string { - if o == nil { +func (t *TextToImageParams) GetNegativePrompt() *string { + if t == nil { return nil } - return o.NegativePrompt + return t.NegativePrompt } -func (o *TextToImageParams) GetSafetyCheck() *bool { - if o == nil { +func (t *TextToImageParams) GetSafetyCheck() *bool { + if t == nil { return nil } - return o.SafetyCheck + return t.SafetyCheck } -func (o *TextToImageParams) GetSeed() *int64 { - if o == nil { +func (t *TextToImageParams) GetSeed() *int64 { + if t == nil { return nil } - return o.Seed + return t.Seed } -func (o *TextToImageParams) GetNumInferenceSteps() *int64 { - if o == nil { +func (t *TextToImageParams) GetNumInferenceSteps() *int64 { + if t == nil { return nil } - return o.NumInferenceSteps + return t.NumInferenceSteps } -func (o *TextToImageParams) GetNumImagesPerPrompt() *int64 { - if o == nil { +func (t *TextToImageParams) GetNumImagesPerPrompt() *int64 { + if t == nil { return nil } - return o.NumImagesPerPrompt + return t.NumImagesPerPrompt } diff --git a/models/components/texttospeechparams.go b/models/components/texttospeechparams.go index f401d7d..3cb9169 100644 --- a/models/components/texttospeechparams.go +++ b/models/components/texttospeechparams.go @@ -20,29 +20,29 @@ func (t TextToSpeechParams) MarshalJSON() ([]byte, error) { } func (t *TextToSpeechParams) UnmarshalJSON(data []byte) error { - if err := utils.UnmarshalJSON(data, &t, "", false, false); err != nil { + if err := utils.UnmarshalJSON(data, &t, "", false, nil); err != nil { return err } return nil } -func (o *TextToSpeechParams) GetModelID() *string { - if o == nil { +func (t *TextToSpeechParams) GetModelID() *string { + if t == nil { return nil } - return o.ModelID + return t.ModelID } -func (o *TextToSpeechParams) GetText() *string { - if o == nil { +func (t *TextToSpeechParams) GetText() *string { + if t == nil { return nil } - return o.Text + return t.Text } -func (o *TextToSpeechParams) GetDescription() *string { - if o == nil { +func (t *TextToSpeechParams) GetDescription() *string { + if t == nil { return nil } - return o.Description + return t.Description } diff --git a/models/components/validationerror.go b/models/components/validationerror.go index 5155868..ff346dd 100644 --- a/models/components/validationerror.go +++ b/models/components/validationerror.go @@ -16,8 +16,8 @@ const ( ) type Loc struct { - Str *string `queryParam:"inline"` - Integer *int64 `queryParam:"inline"` + Str *string `queryParam:"inline,name=loc"` + Integer *int64 `queryParam:"inline,name=loc"` Type LocType } @@ -43,14 +43,14 @@ func CreateLocInteger(integer int64) Loc { func (u *Loc) UnmarshalJSON(data []byte) error { var str string = "" - if err := utils.UnmarshalJSON(data, &str, "", true, true); err == nil { + if err := utils.UnmarshalJSON(data, &str, "", true, nil); err == nil { u.Str = &str u.Type = LocTypeStr return nil } var integer int64 = int64(0) - if err := utils.UnmarshalJSON(data, &integer, "", true, true); err == nil { + if err := utils.UnmarshalJSON(data, &integer, "", true, nil); err == nil { u.Integer = &integer u.Type = LocTypeInteger return nil @@ -77,23 +77,23 @@ type ValidationError struct { Type string `json:"type"` } -func (o *ValidationError) GetLoc() []Loc { - if o == nil { +func (v *ValidationError) GetLoc() []Loc { + if v == nil { return []Loc{} } - return o.Loc + return v.Loc } -func (o *ValidationError) GetMsg() string { - if o == nil { +func (v *ValidationError) GetMsg() string { + if v == nil { return "" } - return o.Msg + return v.Msg } -func (o *ValidationError) GetType() string { - if o == nil { +func (v *ValidationError) GetType() string { + if v == nil { return "" } - return o.Type + return v.Type } diff --git a/models/components/videoresponse.go b/models/components/videoresponse.go index aec85dd..26e2cdd 100644 --- a/models/components/videoresponse.go +++ b/models/components/videoresponse.go @@ -8,9 +8,9 @@ type VideoResponse struct { Images []Media `json:"images"` } -func (o *VideoResponse) GetImages() []Media { - if o == nil { +func (v *VideoResponse) GetImages() []Media { + if v == nil { return []Media{} } - return o.Images + return v.Images } diff --git a/models/operations/genaudiototext.go b/models/operations/genaudiototext.go index 2959484..3c17450 100644 --- a/models/operations/genaudiototext.go +++ b/models/operations/genaudiototext.go @@ -12,16 +12,16 @@ type GenAudioToTextResponse struct { TextResponse *components.TextResponse } -func (o *GenAudioToTextResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenAudioToTextResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenAudioToTextResponse) GetTextResponse() *components.TextResponse { - if o == nil { +func (g *GenAudioToTextResponse) GetTextResponse() *components.TextResponse { + if g == nil { return nil } - return o.TextResponse + return g.TextResponse } diff --git a/models/operations/genimagetoimage.go b/models/operations/genimagetoimage.go index 9f14d4a..c912a0c 100644 --- a/models/operations/genimagetoimage.go +++ b/models/operations/genimagetoimage.go @@ -12,16 +12,16 @@ type GenImageToImageResponse struct { ImageResponse *components.ImageResponse } -func (o *GenImageToImageResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenImageToImageResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenImageToImageResponse) GetImageResponse() *components.ImageResponse { - if o == nil { +func (g *GenImageToImageResponse) GetImageResponse() *components.ImageResponse { + if g == nil { return nil } - return o.ImageResponse + return g.ImageResponse } diff --git a/models/operations/genimagetotext.go b/models/operations/genimagetotext.go index fdf1e07..ea7fe3c 100644 --- a/models/operations/genimagetotext.go +++ b/models/operations/genimagetotext.go @@ -12,16 +12,16 @@ type GenImageToTextResponse struct { ImageToTextResponse *components.ImageToTextResponse } -func (o *GenImageToTextResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenImageToTextResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenImageToTextResponse) GetImageToTextResponse() *components.ImageToTextResponse { - if o == nil { +func (g *GenImageToTextResponse) GetImageToTextResponse() *components.ImageToTextResponse { + if g == nil { return nil } - return o.ImageToTextResponse + return g.ImageToTextResponse } diff --git a/models/operations/genimagetovideo.go b/models/operations/genimagetovideo.go index 88205f1..9b33d3e 100644 --- a/models/operations/genimagetovideo.go +++ b/models/operations/genimagetovideo.go @@ -12,16 +12,16 @@ type GenImageToVideoResponse struct { VideoResponse *components.VideoResponse } -func (o *GenImageToVideoResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenImageToVideoResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenImageToVideoResponse) GetVideoResponse() *components.VideoResponse { - if o == nil { +func (g *GenImageToVideoResponse) GetVideoResponse() *components.VideoResponse { + if g == nil { return nil } - return o.VideoResponse + return g.VideoResponse } diff --git a/models/operations/genlivevideotovideo.go b/models/operations/genlivevideotovideo.go index 6bb8204..4b39a78 100644 --- a/models/operations/genlivevideotovideo.go +++ b/models/operations/genlivevideotovideo.go @@ -12,16 +12,16 @@ type GenLiveVideoToVideoResponse struct { LiveVideoToVideoResponse *components.LiveVideoToVideoResponse } -func (o *GenLiveVideoToVideoResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenLiveVideoToVideoResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenLiveVideoToVideoResponse) GetLiveVideoToVideoResponse() *components.LiveVideoToVideoResponse { - if o == nil { +func (g *GenLiveVideoToVideoResponse) GetLiveVideoToVideoResponse() *components.LiveVideoToVideoResponse { + if g == nil { return nil } - return o.LiveVideoToVideoResponse + return g.LiveVideoToVideoResponse } diff --git a/models/operations/genllm.go b/models/operations/genllm.go index 81a9140..eced8af 100644 --- a/models/operations/genllm.go +++ b/models/operations/genllm.go @@ -12,16 +12,16 @@ type GenLLMResponse struct { LLMResponse *components.LLMResponse } -func (o *GenLLMResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenLLMResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenLLMResponse) GetLLMResponse() *components.LLMResponse { - if o == nil { +func (g *GenLLMResponse) GetLLMResponse() *components.LLMResponse { + if g == nil { return nil } - return o.LLMResponse + return g.LLMResponse } diff --git a/models/operations/gensegmentanything2.go b/models/operations/gensegmentanything2.go index daca761..e892cdd 100644 --- a/models/operations/gensegmentanything2.go +++ b/models/operations/gensegmentanything2.go @@ -12,16 +12,16 @@ type GenSegmentAnything2Response struct { MasksResponse *components.MasksResponse } -func (o *GenSegmentAnything2Response) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenSegmentAnything2Response) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenSegmentAnything2Response) GetMasksResponse() *components.MasksResponse { - if o == nil { +func (g *GenSegmentAnything2Response) GetMasksResponse() *components.MasksResponse { + if g == nil { return nil } - return o.MasksResponse + return g.MasksResponse } diff --git a/models/operations/gentexttoimage.go b/models/operations/gentexttoimage.go index 899e034..5ddb83f 100644 --- a/models/operations/gentexttoimage.go +++ b/models/operations/gentexttoimage.go @@ -12,16 +12,16 @@ type GenTextToImageResponse struct { ImageResponse *components.ImageResponse } -func (o *GenTextToImageResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenTextToImageResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenTextToImageResponse) GetImageResponse() *components.ImageResponse { - if o == nil { +func (g *GenTextToImageResponse) GetImageResponse() *components.ImageResponse { + if g == nil { return nil } - return o.ImageResponse + return g.ImageResponse } diff --git a/models/operations/gentexttospeech.go b/models/operations/gentexttospeech.go index 2d1e9e7..94e53d8 100644 --- a/models/operations/gentexttospeech.go +++ b/models/operations/gentexttospeech.go @@ -12,16 +12,16 @@ type GenTextToSpeechResponse struct { AudioResponse *components.AudioResponse } -func (o *GenTextToSpeechResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenTextToSpeechResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenTextToSpeechResponse) GetAudioResponse() *components.AudioResponse { - if o == nil { +func (g *GenTextToSpeechResponse) GetAudioResponse() *components.AudioResponse { + if g == nil { return nil } - return o.AudioResponse + return g.AudioResponse } diff --git a/models/operations/genupscale.go b/models/operations/genupscale.go index 2da7f52..1b0746f 100644 --- a/models/operations/genupscale.go +++ b/models/operations/genupscale.go @@ -12,16 +12,16 @@ type GenUpscaleResponse struct { ImageResponse *components.ImageResponse } -func (o *GenUpscaleResponse) GetHTTPMeta() components.HTTPMetadata { - if o == nil { +func (g *GenUpscaleResponse) GetHTTPMeta() components.HTTPMetadata { + if g == nil { return components.HTTPMetadata{} } - return o.HTTPMeta + return g.HTTPMeta } -func (o *GenUpscaleResponse) GetImageResponse() *components.ImageResponse { - if o == nil { +func (g *GenUpscaleResponse) GetImageResponse() *components.ImageResponse { + if g == nil { return nil } - return o.ImageResponse + return g.ImageResponse } diff --git a/optionalnullable/optionalnullable.go b/optionalnullable/optionalnullable.go new file mode 100644 index 0000000..c6739be --- /dev/null +++ b/optionalnullable/optionalnullable.go @@ -0,0 +1,233 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package optionalnullable + +import ( + "bytes" + "encoding/json" + "reflect" +) + +// OptionalNullable represents a field that can distinguish between three states: +// 1. Set to a value: field is present with a non-nil value +// 2. Set to nil: field is present but explicitly set to null in JSON +// 3. Unset: field is omitted from JSON entirely +// +// This type is designed to work with JSON marshaling/unmarshaling and supports +// the `omitempty` struct tag to properly omit unset fields from JSON output. +// +// Usage: +// +// type User struct { +// Name OptionalNullable[string] `json:"name,omitempty"` +// Age OptionalNullable[int] `json:"age,omitempty"` +// Tags OptionalNullable[[]string] `json:"tags,omitempty"` +// } +// +// // Set to value +// name := "John" +// user.Name = From(&name) +// +// // Set to nil (will appear as "name": null in JSON) +// user.Name = From[string](nil) +// +// // Leave unset (will be omitted from JSON with omitempty) +// user := User{} +// +// WARNING: Do NOT use *OptionalNullable[T] as a field type. Always use OptionalNullable[T] directly. +// Using *OptionalNullable[T] will break the omitempty behavior and JSON marshaling. +// +// The type is implemented as a map[bool]*T where: +// - nil map represents unset state +// - Map with true key represents set state (value may be nil) +type OptionalNullable[T any] map[bool]*T + +// From creates a new OptionalNullable with the given value. +// Pass nil to create a OptionalNullable that is set to null. +// Pass a pointer to a value to create a OptionalNullable with that value. +// +// Examples: +// +// hello := "hello" +// From(&hello) // set to "hello" +// From[string](nil) // set to null +func From[T any](value *T) OptionalNullable[T] { + return map[bool]*T{ + true: value, + } +} + +// IsNull returns true if the OptionalNullable is explicitly set to nil. +// Returns false if the OptionalNullable is unset or has a value. +// +// Note: This differs from traditional null checks because unset fields +// return false, not true. Use IsSet() to check if a field was provided. +func (n OptionalNullable[T]) IsNull() bool { + v, ok := n[true] + return ok && v == nil +} + +// IsSet returns true if the OptionalNullable has been explicitly set (to either a value or nil). +// Returns false if the OptionalNullable is unset (omitted from JSON). +// +// This is the key method for distinguishing between: +// - Set to nil: IsSet() = true, IsNull() = true +// - Unset: IsSet() = false, IsNull() = false +func (n OptionalNullable[T]) IsSet() bool { + _, ok := n[true] + return ok +} + +// Get returns the internal pointer and whether the field was set. +// +// Return values: +// - (ptr, true): field was set (ptr may be nil if set to null) +// - (nil, false): field was unset/omitted +// +// This method provides direct access to the internal pointer representation. +func (n OptionalNullable[T]) Get() (*T, bool) { + v, ok := n[true] + return v, ok +} + +// GetOrZero returns the value and whether it was set. +// +// Return values: +// - (value, true): field was set to a non-nil value +// - (zero, true): field was explicitly set to nil +// - (zero, false): field was unset/omitted +// +// Examples: +// +// val, ok := nullable.GetOrZero() +// if !ok { +// // Field was unset/omitted +// } else if nullable.IsNull() { +// // Field was explicitly set to null +// } else { +// // Field has a value: val +// } +func (n OptionalNullable[T]) GetOrZero() (T, bool) { + var zero T + + if v, ok := n[true]; ok { + if v == nil { + return zero, true + } + return *v, true + } + return zero, false +} + +// GetUntyped returns the value as interface{} and whether it was set. +// This is useful for reflection-based code that needs to work with the value +// without knowing the specific type T. +// +// Return values: +// - (value, true): field was set to a non-nil value +// - (nil, true): field was explicitly set to nil +// - (nil, false): field was unset/omitted +func (n OptionalNullable[T]) GetUntyped() (interface{}, bool) { + if v, ok := n[true]; ok { + if v == nil { + return nil, true + } + return *v, true + } + return nil, false +} + +// Set sets the OptionalNullable to the given value pointer. +// Pass nil to set the field to null. +// Pass a pointer to a value to set the field to that value. +// +// Examples: +// +// nullable.Set(ptrFrom("hello")) // set to "hello" +// nullable.Set(nil) // set to null +func (n *OptionalNullable[T]) Set(value *T) { + *n = map[bool]*T{ + true: value, + } +} + +// Unset removes the value, making the field unset/omitted. +// After calling Unset(), IsSet() will return false and the field +// will be omitted from JSON output when using omitempty. +func (n *OptionalNullable[T]) Unset() { + *n = map[bool]*T{} +} + +// MarshalJSON implements json.Marshaler. +// +// Behavior: +// - Unset fields: omitted from JSON when struct field has omitempty tag +// - Null fields: serialized as "null" +// - Value fields: serialized as the actual value +// +// The omitempty behavior works because an empty map is considered +// a zero value by Go's JSON package. +func (n OptionalNullable[T]) MarshalJSON() ([]byte, error) { + if n.IsNull() { + return []byte("null"), nil + } + + return json.Marshal(n[true]) +} + +// UnmarshalJSON implements json.Unmarshaler. +// +// Behavior: +// - "null" in JSON: sets the field to null (IsSet=true, IsNull=true) +// - Any other value: sets the field to that value (IsSet=true, IsNull=false) +// - Missing from JSON: field remains unset (IsSet=false, IsNull=false) +func (n *OptionalNullable[T]) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, []byte("null")) { + n.Set(nil) + return nil + } + var v T + if err := json.Unmarshal(data, &v); err != nil { + return err + } + n.Set(&v) + return nil +} + +// NullableInterface defines the interface that all OptionalNullable[T] types implement. +// This interface provides untyped access to optional nullable values for reflection-based code. +type OptionalNullableInterface interface { + GetUntyped() (interface{}, bool) +} + +// AsOptionalNullable attempts to convert a reflect.Value to a OptionalNullableInterface. +// This is a helper function for reflection-based code that needs to check +// if a value implements the optional nullable interface pattern. +// +// Returns: +// - (nullable, true): if the value implements OptionalNullableInterface +// - (nil, false): if the value does not implement OptionalNullableInterface +// +// Example usage: +// +// if nullable, ok := AsOptionalNullable(reflectValue); ok { +// if value, isSet := nullable.GetUntyped(); isSet { +// // Handle the nullable value +// } +// } +func AsOptionalNullable(v reflect.Value) (OptionalNullableInterface, bool) { + // Check if the value can be converted to an interface first + if !v.CanInterface() { + return nil, false + } + + // Check if the underlying value is a nil map (unset nullable) + if v.Kind() == reflect.Map && v.IsNil() { + return nil, false + } + + if nullable, ok := v.Interface().(OptionalNullableInterface); ok { + return nullable, true + } + return nil, false +} diff --git a/optionalnullable/optionalnullable_test.go b/optionalnullable/optionalnullable_test.go new file mode 100644 index 0000000..e6e5a01 --- /dev/null +++ b/optionalnullable/optionalnullable_test.go @@ -0,0 +1,1806 @@ +// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. + +package optionalnullable + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Test helper function to create pointers from values +func ptrFrom[T any](value T) *T { + return &value +} + +// Test helper types for comprehensive testing +type TestStruct struct { + Name string `json:"name"` + Age int `json:"age"` +} + +type TestContainer struct { + StringField OptionalNullable[string] `json:"string_field,omitempty"` + IntField OptionalNullable[int] `json:"int_field,omitempty"` + SliceField OptionalNullable[[]string] `json:"slice_field,omitempty"` + StructField OptionalNullable[TestStruct] `json:"struct_field,omitempty"` +} + +// TestNewNullable tests the From constructor +func TestNewNullable(t *testing.T) { + t.Parallel() + t.Run("with string value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "test", got) + }) + + t.Run("with nil pointer", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "", got) // zero value for string + }) + + t.Run("with int value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom(42)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 42, got) + }) + + t.Run("with slice value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom([]string{"a", "b", "c"})) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b", "c"}, got) + }) + + t.Run("with empty slice", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom([]string{})) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{}, got) + }) + + t.Run("with struct value", func(t *testing.T) { + t.Parallel() + val := TestStruct{Name: "John", Age: 30} + nullable := From(&val) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + v, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, val, v) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, TestStruct{Name: "John", Age: 30}, got) + }) +} + +// TestNewNullableUnset tests the NewNullableUnset constructor +func TestNewNullableUnset(t *testing.T) { + t.Parallel() + t.Run("string type", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // Unset is not null + + got, ok := nullable.GetOrZero() + assert.False(t, ok) + assert.Equal(t, "", got) // zero value for string + }) + + t.Run("int type", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[int] + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // Unset is not null + + got, ok := nullable.GetOrZero() + assert.False(t, ok) + assert.Equal(t, 0, got) // zero value for int + }) + + t.Run("slice type", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[[]string] + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // Unset is not null + + got, ok := nullable.GetOrZero() + assert.False(t, ok) + assert.Nil(t, got) // zero value for slice is nil + }) +} + +// TestIsNull tests the IsNull method +func TestIsNull(t *testing.T) { + t.Parallel() + t.Run("with value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + assert.False(t, nullable.IsNull()) + }) + + t.Run("with nil pointer", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + assert.True(t, nullable.IsNull()) + }) + + t.Run("unset", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + assert.False(t, nullable.IsNull()) + }) +} + +// TestIsSet tests the IsSet method +func TestIsSet(t *testing.T) { + t.Parallel() + t.Run("with value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + assert.True(t, nullable.IsSet()) + }) + + t.Run("with nil pointer", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + assert.True(t, nullable.IsSet()) + }) + + t.Run("unset", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + assert.False(t, nullable.IsSet()) + }) +} + +// TestGet tests the Get method +func TestGet(t *testing.T) { + t.Parallel() + t.Run("with string value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "test", got) + }) + + t.Run("with nil pointer", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "", got) // zero value + }) + + t.Run("unset", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + + got, ok := nullable.GetOrZero() + assert.False(t, ok) + assert.Equal(t, "", got) // zero value + }) + + t.Run("with slice value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom([]string{"a", "b"})) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b"}, got) + }) + + t.Run("with nil slice pointer", func(t *testing.T) { + t.Parallel() + nullable := From[[]string](nil) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Nil(t, got) // zero value for slice is nil + }) +} + +// TestPointer tests the Pointer method +func TestPointer(t *testing.T) { + t.Parallel() + t.Run("with value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + + ptr, ok := nullable.Get() + assert.True(t, ok) + assert.NotNil(t, ptr) + assert.Equal(t, "test", *ptr) + }) + + t.Run("with nil pointer", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + + ptr, ok := nullable.Get() + assert.True(t, ok) + assert.Nil(t, ptr) + }) + + t.Run("unset", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + + ptr, ok := nullable.Get() + assert.False(t, ok) + assert.Nil(t, ptr) + }) +} + +// TestSet tests the Set method +func TestSet(t *testing.T) { + t.Parallel() + t.Run("set string value", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + + // Initially unset + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // Unset is not null + + // Set a value + nullable.Set(ptrFrom("test")) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "test", got) + }) + + t.Run("set int value", func(t *testing.T) { + t.Parallel() + nullable := OptionalNullable[int]{} + + nullable.Set(ptrFrom(42)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 42, got) + }) + + t.Run("set slice value", func(t *testing.T) { + t.Parallel() + nullable := OptionalNullable[[]string]{} + + slice := []string{"a", "b"} + nullable.Set(ptrFrom(slice)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b"}, got) + }) + + t.Run("set empty slice", func(t *testing.T) { + t.Parallel() + nullable := OptionalNullable[[]string]{} + + slice := []string{} + nullable.Set(ptrFrom(slice)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{}, got) + }) + + t.Run("overwrite existing value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("original")) + + // Verify original value + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "original", got) + + // Set new value + nullable.Set(ptrFrom("new")) + + got, ok = nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "new", got) + }) +} + +// TestUnset tests the Unset method +func TestUnset(t *testing.T) { + t.Parallel() + t.Run("unset from value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + + // Initially set + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + // Unset + nullable.Unset() + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // After unset is not null + // Value is now internal to the map implementation + + got, ok := nullable.GetOrZero() + assert.False(t, ok) + assert.Equal(t, "", got) // zero value + }) + + t.Run("unset from nil", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + + // Initially set to nil + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) // Set to nil should be null + + // Unset + nullable.Unset() + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // After unset is not null + }) + + t.Run("unset already unset", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + + // Initially unset + assert.False(t, nullable.IsSet()) + + // Unset again + nullable.Unset() + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) // Empty map is not null + }) +} + +// TestMarshalJSON tests JSON marshaling +func TestMarshalJSON(t *testing.T) { + t.Parallel() + t.Run("marshal string value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `"test"`, string(data)) + }) + + t.Run("marshal int value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom(42)) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `42`, string(data)) + }) + + t.Run("marshal nil value", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `null`, string(data)) + }) + + t.Run("marshal slice value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom([]string{"a", "b", "c"})) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `["a","b","c"]`, string(data)) + }) + + t.Run("marshal empty slice", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom([]string{})) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `[]`, string(data)) + }) + + t.Run("marshal struct value", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom(TestStruct{Name: "John", Age: 30})) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `{"name":"John","age":30}`, string(data)) + }) + + // Note: Unset values are not tested here because the current implementation + // doesn't handle unset fields in marshaling (see TODO in the code) +} + +// TestUnmarshalJSON tests JSON unmarshaling +func TestUnmarshalJSON(t *testing.T) { + t.Parallel() + t.Run("unmarshal string value", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + err := json.Unmarshal([]byte(`"test"`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "test", got) + }) + + t.Run("unmarshal int value", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[int] + err := json.Unmarshal([]byte(`42`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 42, got) + }) + + t.Run("unmarshal null value", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + err := json.Unmarshal([]byte(`null`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "", got) // zero value + }) + + t.Run("unmarshal slice value", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[[]string] + err := json.Unmarshal([]byte(`["a","b","c"]`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b", "c"}, got) + }) + + t.Run("unmarshal empty slice", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[[]string] + err := json.Unmarshal([]byte(`[]`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{}, got) + }) + + t.Run("unmarshal struct value", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[TestStruct] + err := json.Unmarshal([]byte(`{"name":"John","age":30}`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, TestStruct{Name: "John", Age: 30}, got) + }) + + t.Run("unmarshal invalid JSON", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + err := json.Unmarshal([]byte(`invalid`), &nullable) + assert.Error(t, err) + + // Ensure the nullable remains unset after error + assert.False(t, nullable.IsSet()) + }) + + t.Run("unmarshal invalid JSON for int", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[int] + err := json.Unmarshal([]byte(`"not_a_number"`), &nullable) + assert.Error(t, err) + + // Ensure the nullable remains unset after error + assert.False(t, nullable.IsSet()) + }) + + t.Run("unmarshal malformed JSON", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[TestStruct] + err := json.Unmarshal([]byte(`{invalid json`), &nullable) + assert.Error(t, err) + + // Ensure the nullable remains unset after error + assert.False(t, nullable.IsSet()) + }) +} + +// TestJSONRoundTrip tests marshaling and unmarshaling together +func TestJSONRoundTrip(t *testing.T) { + t.Parallel() + t.Run("string value round trip", func(t *testing.T) { + t.Parallel() + nullable1 := From(ptrFrom("test value")) + + // Marshal + data, err := json.Marshal(nullable1) + require.NoError(t, err) + + // Unmarshal + var nullable2 OptionalNullable[string] + err = json.Unmarshal(data, &nullable2) + require.NoError(t, err) + + // Compare + assert.Equal(t, nullable1.IsSet(), nullable2.IsSet()) + assert.Equal(t, nullable1.IsNull(), nullable2.IsNull()) + + got1, ok1 := nullable1.GetOrZero() + got2, ok2 := nullable2.GetOrZero() + assert.Equal(t, ok1, ok2) + assert.Equal(t, got1, got2) + }) + + t.Run("nil value round trip", func(t *testing.T) { + t.Parallel() + nullable1 := From[string](nil) + + // Marshal + data, err := json.Marshal(nullable1) + require.NoError(t, err) + + // Unmarshal + var nullable2 OptionalNullable[string] + err = json.Unmarshal(data, &nullable2) + require.NoError(t, err) + + // Compare + assert.Equal(t, nullable1.IsSet(), nullable2.IsSet()) + assert.Equal(t, nullable1.IsNull(), nullable2.IsNull()) + + got1, ok1 := nullable1.GetOrZero() + got2, ok2 := nullable2.GetOrZero() + assert.Equal(t, ok1, ok2) + assert.Equal(t, got1, got2) + }) + + t.Run("slice round trip", func(t *testing.T) { + t.Parallel() + nullable1 := From(ptrFrom([]string{"a", "b", "c"})) + + // Marshal + data, err := json.Marshal(nullable1) + require.NoError(t, err) + + // Unmarshal + var nullable2 OptionalNullable[[]string] + err = json.Unmarshal(data, &nullable2) + require.NoError(t, err) + + // Compare + assert.Equal(t, nullable1.IsSet(), nullable2.IsSet()) + assert.Equal(t, nullable1.IsNull(), nullable2.IsNull()) + + got1, ok1 := nullable1.GetOrZero() + got2, ok2 := nullable2.GetOrZero() + assert.Equal(t, ok1, ok2) + assert.Equal(t, got1, got2) + }) +} + +// TestJSONToJSONRoundTrip tests starting with JSON and ensuring we can serialize back to the same JSON +func TestJSONToJSONRoundTrip(t *testing.T) { + t.Parallel() + t.Run("string value JSON round trip", func(t *testing.T) { + t.Parallel() + originalJSON := `"hello world"` + + // Unmarshal from JSON + var nullable OptionalNullable[string] + err := json.Unmarshal([]byte(originalJSON), &nullable) + require.NoError(t, err) + + // Verify state + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "hello world", got) + + // Marshal back to JSON + resultJSON, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, originalJSON, string(resultJSON)) + }) + + t.Run("null value JSON round trip", func(t *testing.T) { + t.Parallel() + originalJSON := `null` + + // Unmarshal from JSON + var nullable OptionalNullable[string] + err := json.Unmarshal([]byte(originalJSON), &nullable) + require.NoError(t, err) + + // Verify state + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "", got) // zero value + + // Marshal back to JSON + resultJSON, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, originalJSON, string(resultJSON)) + }) + + t.Run("int value JSON round trip", func(t *testing.T) { + t.Parallel() + originalJSON := `42` + + // Unmarshal from JSON + var nullable OptionalNullable[int] + err := json.Unmarshal([]byte(originalJSON), &nullable) + require.NoError(t, err) + + // Verify state + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 42, got) + + // Marshal back to JSON + resultJSON, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, originalJSON, string(resultJSON)) + }) + + t.Run("slice value JSON round trip", func(t *testing.T) { + t.Parallel() + originalJSON := `["a","b","c"]` + + // Unmarshal from JSON + var nullable OptionalNullable[[]string] + err := json.Unmarshal([]byte(originalJSON), &nullable) + require.NoError(t, err) + + // Verify state + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b", "c"}, got) + + // Marshal back to JSON + resultJSON, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, originalJSON, string(resultJSON)) + }) + + t.Run("empty slice JSON round trip", func(t *testing.T) { + t.Parallel() + originalJSON := `[]` + + // Unmarshal from JSON + var nullable OptionalNullable[[]string] + err := json.Unmarshal([]byte(originalJSON), &nullable) + require.NoError(t, err) + + // Verify state + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{}, got) + + // Marshal back to JSON + resultJSON, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, originalJSON, string(resultJSON)) + }) + + t.Run("struct value JSON round trip", func(t *testing.T) { + t.Parallel() + originalJSON := `{"name":"Alice","age":25}` + + // Unmarshal from JSON + var nullable OptionalNullable[TestStruct] + err := json.Unmarshal([]byte(originalJSON), &nullable) + require.NoError(t, err) + + // Verify state + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, TestStruct{Name: "Alice", Age: 25}, got) + + // Marshal back to JSON + resultJSON, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, originalJSON, string(resultJSON)) + }) +} + +// TestContainerStates tests comprehensive state detection and serialization with TestContainer +func TestContainerStates(t *testing.T) { + t.Parallel() + t.Run("all fields set to values", func(t *testing.T) { + t.Parallel() + container := TestContainer{ + StringField: From(ptrFrom("hello")), + IntField: From(ptrFrom(42)), + SliceField: From(ptrFrom([]string{"a", "b"})), + StructField: From(ptrFrom(TestStruct{Name: "John", Age: 30})), + } + + // Verify all fields are set and not null + assert.True(t, container.StringField.IsSet()) + assert.False(t, container.StringField.IsNull()) + assert.True(t, container.IntField.IsSet()) + assert.False(t, container.IntField.IsNull()) + assert.True(t, container.SliceField.IsSet()) + assert.False(t, container.SliceField.IsNull()) + assert.True(t, container.StructField.IsSet()) + assert.False(t, container.StructField.IsNull()) + + // Verify values + stringVal, ok := container.StringField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "hello", stringVal) + + intVal, ok := container.IntField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 42, intVal) + + sliceVal, ok := container.SliceField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b"}, sliceVal) + + structVal, ok := container.StructField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, TestStruct{Name: "John", Age: 30}, structVal) + + // Test JSON serialization + data, err := json.Marshal(container) + require.NoError(t, err) + + var result map[string]interface{} + err = json.Unmarshal(data, &result) + require.NoError(t, err) + + assert.Equal(t, "hello", result["string_field"]) + assert.Equal(t, float64(42), result["int_field"]) // JSON numbers are float64 + assert.Equal(t, []interface{}{"a", "b"}, result["slice_field"]) + structResult := result["struct_field"].(map[string]interface{}) + assert.Equal(t, "John", structResult["name"]) + assert.Equal(t, float64(30), structResult["age"]) + }) + + t.Run("all fields set to nil", func(t *testing.T) { + t.Parallel() + container := TestContainer{ + StringField: From[string](nil), + IntField: From[int](nil), + SliceField: From[[]string](nil), + StructField: From[TestStruct](nil), + } + + // Verify all fields are set but null + assert.True(t, container.StringField.IsSet()) + assert.True(t, container.StringField.IsNull()) + assert.True(t, container.IntField.IsSet()) + assert.True(t, container.IntField.IsNull()) + assert.True(t, container.SliceField.IsSet()) + assert.True(t, container.SliceField.IsNull()) + assert.True(t, container.StructField.IsSet()) + assert.True(t, container.StructField.IsNull()) + + // Verify GetOrZero() behavior for nil values + stringVal, ok := container.StringField.GetOrZero() + assert.True(t, ok) // set to nil still returns true + assert.Equal(t, "", stringVal) // zero value + + intVal, ok := container.IntField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 0, intVal) // zero value + + sliceVal, ok := container.SliceField.GetOrZero() + assert.True(t, ok) + assert.Nil(t, sliceVal) // zero value for slice is nil + + structVal, ok := container.StructField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, TestStruct{}, structVal) // zero value + + // Test JSON serialization - all should be null + data, err := json.Marshal(container) + require.NoError(t, err) + + var result map[string]interface{} + err = json.Unmarshal(data, &result) + require.NoError(t, err) + + assert.Nil(t, result["string_field"]) + assert.Nil(t, result["int_field"]) + assert.Nil(t, result["slice_field"]) + assert.Nil(t, result["struct_field"]) + }) + + t.Run("all fields unset", func(t *testing.T) { + t.Parallel() + container := TestContainer{} + + // Verify all fields are unset + assert.False(t, container.StringField.IsSet()) + assert.False(t, container.StringField.IsNull()) // unset is not null in new implementation + assert.False(t, container.IntField.IsSet()) + assert.False(t, container.IntField.IsNull()) + assert.False(t, container.SliceField.IsSet()) + assert.False(t, container.SliceField.IsNull()) + assert.False(t, container.StructField.IsSet()) + assert.False(t, container.StructField.IsNull()) + + // Verify GetOrZero() behavior for unset values + stringVal, ok := container.StringField.GetOrZero() + assert.False(t, ok) // unset returns false + assert.Equal(t, "", stringVal) // zero value + + intVal, ok := container.IntField.GetOrZero() + assert.False(t, ok) + assert.Equal(t, 0, intVal) // zero value + + sliceVal, ok := container.SliceField.GetOrZero() + assert.False(t, ok) + assert.Nil(t, sliceVal) // zero value + + structVal, ok := container.StructField.GetOrZero() + assert.False(t, ok) + assert.Equal(t, TestStruct{}, structVal) // zero value + + // Test JSON serialization - unset fields should be omitted due to omitempty + data, err := json.Marshal(container) + require.NoError(t, err) + + var result map[string]interface{} + err = json.Unmarshal(data, &result) + require.NoError(t, err) + + // With omitempty, unset fields should not appear in JSON + assert.NotContains(t, result, "string_field") + assert.NotContains(t, result, "int_field") + assert.NotContains(t, result, "slice_field") + assert.NotContains(t, result, "struct_field") + }) + + t.Run("slice field states: nil vs unset vs empty vs set", func(t *testing.T) { + t.Parallel() + // Test all possible slice states + nilSlice := TestContainer{ + SliceField: From[[]string](nil), // explicitly set to nil + } + unsetSlice := TestContainer{} // unset + emptySlice := TestContainer{ + SliceField: From(ptrFrom([]string{})), // empty slice + } + setSlice := TestContainer{ + SliceField: From(ptrFrom([]string{"a", "b"})), // slice with values + } + + // Verify nil slice + assert.True(t, nilSlice.SliceField.IsSet()) + assert.True(t, nilSlice.SliceField.IsNull()) + val, ok := nilSlice.SliceField.GetOrZero() + assert.True(t, ok) + assert.Nil(t, val) + + // Verify unset slice + assert.False(t, unsetSlice.SliceField.IsSet()) + assert.False(t, unsetSlice.SliceField.IsNull()) // Unset is not null + val, ok = unsetSlice.SliceField.GetOrZero() + assert.False(t, ok) + assert.Nil(t, val) + + // Verify empty slice + assert.True(t, emptySlice.SliceField.IsSet()) + assert.False(t, emptySlice.SliceField.IsNull()) + val, ok = emptySlice.SliceField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{}, val) + + // Verify set slice + assert.True(t, setSlice.SliceField.IsSet()) + assert.False(t, setSlice.SliceField.IsNull()) + val, ok = setSlice.SliceField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, []string{"a", "b"}, val) + + // Test JSON serialization for each state + nilData, err := json.Marshal(nilSlice) + require.NoError(t, err) + assert.Contains(t, string(nilData), `"slice_field":null`) + + unsetData, err := json.Marshal(unsetSlice) + require.NoError(t, err) + assert.NotContains(t, string(unsetData), "slice_field") // omitted due to omitempty + + emptyData, err := json.Marshal(emptySlice) + require.NoError(t, err) + assert.Contains(t, string(emptyData), `"slice_field":[]`) + + setData, err := json.Marshal(setSlice) + require.NoError(t, err) + assert.Contains(t, string(setData), `"slice_field":["a","b"]`) + }) + + t.Run("mixed states container", func(t *testing.T) { + t.Parallel() + container := TestContainer{ + StringField: From(ptrFrom("hello")), // set to value + IntField: From[int](nil), // set to nil + StructField: From(ptrFrom(TestStruct{Name: "Alice", Age: 25})), // set to value + } + + // Verify states + assert.True(t, container.StringField.IsSet()) + assert.False(t, container.StringField.IsNull()) + + assert.True(t, container.IntField.IsSet()) + assert.True(t, container.IntField.IsNull()) + + assert.False(t, container.SliceField.IsSet()) + assert.False(t, container.SliceField.IsNull()) // Unset is not null + + assert.True(t, container.StructField.IsSet()) + assert.False(t, container.StructField.IsNull()) + + // Test JSON serialization + data, err := json.Marshal(container) + require.NoError(t, err) + + var result map[string]interface{} + err = json.Unmarshal(data, &result) + require.NoError(t, err) + + assert.Equal(t, "hello", result["string_field"]) + assert.Nil(t, result["int_field"]) + assert.NotContains(t, result, "slice_field") // unset, so omitted + structResult := result["struct_field"].(map[string]interface{}) + assert.Equal(t, "Alice", structResult["name"]) + assert.Equal(t, float64(25), structResult["age"]) + }) + + t.Run("JSON unmarshaling preserves states", func(t *testing.T) { + t.Parallel() + // JSON with some fields missing, some null, some with values + jsonData := `{ + "string_field": "test", + "int_field": null, + "struct_field": {"name": "Bob", "age": 35} + }` + + var container TestContainer + err := json.Unmarshal([]byte(jsonData), &container) + require.NoError(t, err) + + // string_field: present with value + assert.True(t, container.StringField.IsSet()) + assert.False(t, container.StringField.IsNull()) + stringVal, ok := container.StringField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "test", stringVal) + + // int_field: present but null + assert.True(t, container.IntField.IsSet()) + assert.True(t, container.IntField.IsNull()) + intVal, ok := container.IntField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 0, intVal) // zero value + + // slice_field: missing from JSON, should remain unset + assert.False(t, container.SliceField.IsSet()) + assert.False(t, container.SliceField.IsNull()) // Unset is not null + sliceVal, ok := container.SliceField.GetOrZero() + assert.False(t, ok) + assert.Nil(t, sliceVal) + + // struct_field: present with value + assert.True(t, container.StructField.IsSet()) + assert.False(t, container.StructField.IsNull()) + structVal, ok := container.StructField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, TestStruct{Name: "Bob", Age: 35}, structVal) + }) +} + +// TestNilVsUnsetDistinction tests the key feature of distinguishing nil from unset +func TestNilVsUnsetDistinction(t *testing.T) { + t.Parallel() + t.Run("explicit nil vs unset", func(t *testing.T) { + t.Parallel() + // Explicitly set to nil + explicitNil := From[string](nil) + + // Unset + var unset OptionalNullable[string] + + // Both are null, but only one is set + assert.True(t, explicitNil.IsNull()) + assert.True(t, explicitNil.IsSet()) + + assert.False(t, unset.IsNull()) // Unset is not null + assert.False(t, unset.IsSet()) + + // Get behavior differs + got1, ok1 := explicitNil.GetOrZero() + got2, ok2 := unset.GetOrZero() + + assert.True(t, ok1) // explicitly set to nil returns true + assert.False(t, ok2) // unset returns false + assert.Equal(t, "", got1) // both return zero value + assert.Equal(t, "", got2) + + // Get behavior differs + ptr1, ok1 := explicitNil.Get() + ptr2, ok2 := unset.Get() + + assert.True(t, ok1) // explicitly set to nil returns true + assert.False(t, ok2) // unset returns false + assert.Nil(t, ptr1) // both return nil pointer + assert.Nil(t, ptr2) + }) + + t.Run("empty slice vs nil slice vs unset", func(t *testing.T) { + t.Parallel() + // Empty slice + emptyNullable := From(ptrFrom([]string{})) + + // Nil slice + nilNullable := From[[]string](nil) + + // Unset + var unsetNullable OptionalNullable[[]string] + + // All have different characteristics + assert.True(t, emptyNullable.IsSet()) + assert.False(t, emptyNullable.IsNull()) + + assert.True(t, nilNullable.IsSet()) + assert.True(t, nilNullable.IsNull()) + + assert.False(t, unsetNullable.IsSet()) + assert.False(t, unsetNullable.IsNull()) // Unset is not null + + // Get behavior + got1, ok1 := emptyNullable.GetOrZero() + got2, ok2 := nilNullable.GetOrZero() + got3, ok3 := unsetNullable.GetOrZero() + + assert.True(t, ok1) + assert.Equal(t, []string{}, got1) + + assert.True(t, ok2) + assert.Nil(t, got2) + + assert.False(t, ok3) + assert.Nil(t, got3) + }) +} + +// TestJSONOmitEmpty tests behavior with omitempty tag +func TestJSONOmitEmpty(t *testing.T) { + t.Parallel() + t.Run("marshal with omitempty", func(t *testing.T) { + t.Parallel() + // Test container with various nullable states + container := TestContainer{ + StringField: From(ptrFrom("test")), + IntField: From(ptrFrom(42)), + StructField: From[TestStruct](nil), // explicitly nil + } + + data, err := json.Marshal(container) + require.NoError(t, err) + + // Parse back to verify structure + var result map[string]interface{} + err = json.Unmarshal(data, &result) + require.NoError(t, err) + + // Should contain set fields + assert.Contains(t, result, "string_field") + assert.Contains(t, result, "int_field") + assert.Contains(t, result, "struct_field") + + // Should not contain unset field (due to omitempty) + // Note: This depends on how the marshaling handles unset fields + // The current implementation doesn't handle this case properly (see TODO) + }) + + t.Run("unmarshal missing fields", func(t *testing.T) { + t.Parallel() + // JSON with some fields missing + jsonData := `{"string_field": "test", "int_field": null}` + + var container TestContainer + err := json.Unmarshal([]byte(jsonData), &container) + require.NoError(t, err) + + // Present fields should be set + assert.True(t, container.StringField.IsSet()) + assert.False(t, container.StringField.IsNull()) + got, ok := container.StringField.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "test", got) + + // Null field should be set to nil + assert.True(t, container.IntField.IsSet()) + assert.True(t, container.IntField.IsNull()) + + // Missing fields should remain unset + assert.False(t, container.SliceField.IsSet()) + assert.False(t, container.StructField.IsSet()) + }) +} + +// TestEdgeCases tests various edge cases +func TestEdgeCases(t *testing.T) { + t.Parallel() + t.Run("zero values", func(t *testing.T) { + t.Parallel() + // Test with zero values that are not nil + intNullable := From(ptrFrom(0)) + stringNullable := From(ptrFrom("")) + + assert.True(t, intNullable.IsSet()) + assert.False(t, intNullable.IsNull()) + got, ok := intNullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, 0, got) + + assert.True(t, stringNullable.IsSet()) + assert.False(t, stringNullable.IsNull()) + got2, ok2 := stringNullable.GetOrZero() + assert.True(t, ok2) + assert.Equal(t, "", got2) + }) + + t.Run("pointer to pointer", func(t *testing.T) { + t.Parallel() + // Test with pointer to pointer type + inner := "test" + nullable := From(ptrFrom(&inner)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, &inner, got) + assert.Equal(t, "test", *got) + }) + + t.Run("complex struct", func(t *testing.T) { + t.Parallel() + complexStruct := struct { + Name string + Values []int + Metadata map[string]string + }{ + Name: "complex", + Values: []int{1, 2, 3}, + Metadata: map[string]string{ + "key1": "value1", + "key2": "value2", + }, + } + + nullable := From(ptrFrom(complexStruct)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, complexStruct, got) + }) +} + +// TestDoublePointers tests comprehensive double pointer scenarios +func TestDoublePointers(t *testing.T) { + t.Parallel() + + t.Run("string double pointer with value", func(t *testing.T) { + t.Parallel() + inner := "hello world" + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Equal(t, &inner, got) + assert.Equal(t, "hello world", *got) + }) + + t.Run("int double pointer with value", func(t *testing.T) { + t.Parallel() + inner := 42 + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Equal(t, &inner, got) + assert.Equal(t, 42, *got) + }) + + t.Run("double pointer to nil", func(t *testing.T) { + t.Parallel() + var ptr *string = nil + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Nil(t, got) + }) + + t.Run("nil double pointer", func(t *testing.T) { + t.Parallel() + nullable := From[*string](nil) + + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Nil(t, got) // zero value for **string is nil + }) + + t.Run("unset double pointer", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[*string] + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.False(t, ok) + assert.Nil(t, got) // zero value for **string is nil + }) + + t.Run("double pointer modification", func(t *testing.T) { + t.Parallel() + inner := "original" + ptr := &inner + nullable := From(ptrFrom(ptr)) + + // Verify original value + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "original", *got) + + // Modify through double pointer + *got = "modified" + assert.Equal(t, "modified", inner) + assert.Equal(t, "modified", *got) + }) + + t.Run("double pointer to struct", func(t *testing.T) { + t.Parallel() + inner := TestStruct{Name: "Alice", Age: 30} + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Equal(t, TestStruct{Name: "Alice", Age: 30}, *got) + + // Modify through double pointer + (*got).Name = "Bob" + assert.Equal(t, "Bob", inner.Name) + assert.Equal(t, "Bob", (*got).Name) + }) + + t.Run("double pointer to slice", func(t *testing.T) { + t.Parallel() + inner := []string{"a", "b", "c"} + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Equal(t, []string{"a", "b", "c"}, *got) + + // Modify through double pointer + *got = append(*got, "d") + assert.Equal(t, []string{"a", "b", "c", "d"}, inner) + assert.Equal(t, []string{"a", "b", "c", "d"}, *got) + }) + + t.Run("double pointer to empty slice", func(t *testing.T) { + t.Parallel() + inner := []string{} + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Equal(t, []string{}, *got) + }) + + t.Run("double pointer to nil slice", func(t *testing.T) { + t.Parallel() + var inner []string = nil + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr, got) + assert.Nil(t, *got) + }) + + t.Run("double pointer JSON marshaling", func(t *testing.T) { + t.Parallel() + inner := "json test" + ptr := &inner + nullable := From(ptrFrom(ptr)) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `"json test"`, string(data)) + }) + + t.Run("double pointer JSON unmarshaling", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[*string] + err := json.Unmarshal([]byte(`"json test"`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.NotNil(t, got) + assert.Equal(t, "json test", *got) + }) + + t.Run("double pointer JSON null marshaling", func(t *testing.T) { + t.Parallel() + nullable := From[*string](nil) + + data, err := json.Marshal(nullable) + require.NoError(t, err) + assert.Equal(t, `null`, string(data)) + }) + + t.Run("double pointer JSON null unmarshaling", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[*string] + err := json.Unmarshal([]byte(`null`), &nullable) + require.NoError(t, err) + + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Nil(t, got) + }) + + t.Run("double pointer round trip", func(t *testing.T) { + t.Parallel() + inner := "round trip test" + ptr := &inner + nullable1 := From(ptrFrom(ptr)) + + // Marshal + data, err := json.Marshal(nullable1) + require.NoError(t, err) + + // Unmarshal + var nullable2 OptionalNullable[*string] + err = json.Unmarshal(data, &nullable2) + require.NoError(t, err) + + // Compare states + assert.Equal(t, nullable1.IsSet(), nullable2.IsSet()) + assert.Equal(t, nullable1.IsNull(), nullable2.IsNull()) + + got1, ok1 := nullable1.GetOrZero() + got2, ok2 := nullable2.GetOrZero() + assert.Equal(t, ok1, ok2) + + // Values should be equal + assert.Equal(t, *got1, *got2) + }) + + t.Run("triple pointer", func(t *testing.T) { + t.Parallel() + inner := "triple" + ptr1 := &inner + ptr2 := &ptr1 + nullable := From(ptrFrom(ptr2)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, ptr2, got) + assert.Equal(t, ptr1, *got) + assert.Equal(t, "triple", **got) + }) + + t.Run("double pointer set and unset", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[*string] + + // Initially unset + assert.False(t, nullable.IsSet()) + + // Set to double pointer + inner := "set test" + ptr := &inner + nullable.Set(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "set test", *got) + + // Set to nil + nullable.Set(nil) + + assert.True(t, nullable.IsSet()) + assert.True(t, nullable.IsNull()) + + got, ok = nullable.GetOrZero() + assert.True(t, ok) + assert.Nil(t, got) + + // Unset + nullable.Unset() + + assert.False(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok = nullable.GetOrZero() + assert.False(t, ok) + assert.Nil(t, got) + }) + + t.Run("double pointer Get method", func(t *testing.T) { + t.Parallel() + inner := "get test" + ptr := &inner + nullable := From(ptrFrom(ptr)) + + // Test Get method + gotPtr, ok := nullable.Get() + assert.True(t, ok) + assert.NotNil(t, gotPtr) + assert.Equal(t, ptr, *gotPtr) + assert.Equal(t, "get test", **gotPtr) + + // Test with nil + nilNullable := From[*string](nil) + gotPtr, ok = nilNullable.Get() + assert.True(t, ok) + assert.Nil(t, gotPtr) + + // Test with unset + var unsetNullable OptionalNullable[*string] + gotPtr, ok = unsetNullable.Get() + assert.False(t, ok) + assert.Nil(t, gotPtr) + }) + + t.Run("double pointer zero values", func(t *testing.T) { + t.Parallel() + // Test with zero value string + inner := "" + ptr := &inner + nullable := From(ptrFrom(ptr)) + + assert.True(t, nullable.IsSet()) + assert.False(t, nullable.IsNull()) + + got, ok := nullable.GetOrZero() + assert.True(t, ok) + assert.Equal(t, "", *got) + + // Test with zero value int + innerInt := 0 + ptrInt := &innerInt + nullableInt := From(ptrFrom(ptrInt)) + + assert.True(t, nullableInt.IsSet()) + assert.False(t, nullableInt.IsNull()) + + gotInt, okInt := nullableInt.GetOrZero() + assert.True(t, okInt) + assert.Equal(t, 0, *gotInt) + }) +} + +// TestAsOptionalNullable tests the AsOptionalNullable helper function +func TestAsOptionalNullable(t *testing.T) { + t.Parallel() + + t.Run("with nullable string", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, "test", value) + }) + + t.Run("with nullable int", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom(42)) + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, 42, value) + }) + + t.Run("with nullable nil", func(t *testing.T) { + t.Parallel() + nullable := From[string](nil) + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Nil(t, value) + }) + + t.Run("with unset nullable", func(t *testing.T) { + t.Parallel() + var nullable OptionalNullable[string] + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.False(t, ok) + assert.Nil(t, result) + }) + + t.Run("with non-nullable string", func(t *testing.T) { + t.Parallel() + regularString := "not nullable" + reflectValue := reflect.ValueOf(regularString) + + result, ok := AsOptionalNullable(reflectValue) + assert.False(t, ok) + assert.Nil(t, result) + }) + + t.Run("with non-nullable int", func(t *testing.T) { + t.Parallel() + regularInt := 42 + reflectValue := reflect.ValueOf(regularInt) + + result, ok := AsOptionalNullable(reflectValue) + assert.False(t, ok) + assert.Nil(t, result) + }) + + t.Run("with non-nullable map", func(t *testing.T) { + t.Parallel() + regularMap := map[string]int{"key": 42} + reflectValue := reflect.ValueOf(regularMap) + + result, ok := AsOptionalNullable(reflectValue) + assert.False(t, ok) + assert.Nil(t, result) + }) + + t.Run("with non-nullable struct", func(t *testing.T) { + t.Parallel() + regularStruct := TestStruct{Name: "test", Age: 30} + reflectValue := reflect.ValueOf(regularStruct) + + result, ok := AsOptionalNullable(reflectValue) + assert.False(t, ok) + assert.Nil(t, result) + }) + + t.Run("with nullable double pointer", func(t *testing.T) { + t.Parallel() + inner := "test" + ptr := &inner + nullable := From(ptrFrom(ptr)) + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, ptr, value) + assert.Equal(t, "test", *value.(*string)) + }) + + t.Run("with nullable slice", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom([]string{"a", "b", "c"})) + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, []string{"a", "b", "c"}, value) + }) + + t.Run("with nullable struct", func(t *testing.T) { + t.Parallel() + testStruct := TestStruct{Name: "Alice", Age: 25} + nullable := From(ptrFrom(testStruct)) + reflectValue := reflect.ValueOf(nullable) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, testStruct, value) + }) + + t.Run("with pointer to nullable", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + ptrToNullable := &nullable + reflectValue := reflect.ValueOf(ptrToNullable) + + // This should work since the pointer to nullable still contains a nullable + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, "test", value) + }) + + t.Run("with interface containing nullable", func(t *testing.T) { + t.Parallel() + nullable := From(ptrFrom("test")) + var iface interface{} = nullable + reflectValue := reflect.ValueOf(iface) + + result, ok := AsOptionalNullable(reflectValue) + assert.True(t, ok) + assert.NotNil(t, result) + + value, isSet := result.GetUntyped() + assert.True(t, isSet) + assert.Equal(t, "test", value) + }) +} diff --git a/types/decimal.go b/types/decimal.go deleted file mode 100644 index d8429bc..0000000 --- a/types/decimal.go +++ /dev/null @@ -1,20 +0,0 @@ -// Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT. - -package types - -import ( - "fmt" - - "github.com/ericlagergren/decimal" -) - -// MustNewDecimalFromString returns an instance of Decimal from a string -// Avoid using this function in production code. -func MustNewDecimalFromString(s string) *decimal.Big { - d, ok := new(decimal.Big).SetString(s) - if !ok { - panic(fmt.Errorf("failed to parse string as decimal.Big")) - } - - return d -} From 01ec00203ebe0909e750aa9f3e891fb7c36e14cb Mon Sep 17 00:00:00 2001 From: "speakeasy-github[bot]" <128539517+speakeasy-github[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 00:32:00 +0000 Subject: [PATCH 2/2] empty commit to trigger [run-tests] workflow