openai / openai-go

The official Go library for the OpenAI API
Apache License 2.0
452 stars 29 forks source link

How are you supposed to unmarshal JSON payloads? #133

Open joehewett opened 6 hours ago

joehewett commented 6 hours ago

I'm struggling to unmarshal raw JSON payloads into the OpenAI Go types

e.g. consider

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"

    "github.com/openai/openai-go"
    "github.com/openai/openai-go/option"
)

func main() {
    // Create a new request
    f := openai.ChatCompletionNewParams{
        Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
            openai.UserMessage("Say this is a test"),
        }),
        Model: openai.F(openai.ChatModelGPT4o),
    }

    // Marshal and print the request
    jsonRequest, err := json.Marshal(f)
    if err != nil {
        panic(err.Error())
    }
    fmt.Printf("Request marshalled: %s\n", string(jsonRequest))

    // Now unmarshal it and observe error
    var v openai.ChatCompletionNewParams
    err = json.Unmarshal(jsonRequest, &v)
    if err != nil {
        panic(err.Error())
    }
    fmt.Printf("Request unmarshalled: %+v\n", v)
}

You'd think that marshalling to string and then directly unmarshalling would work, but

Request marshalled: {"messages":[{"content":[{"text":"Say this is a test","type":"text"}],"role":"user"}],"model":"gpt-4o"}
Error unmarshalling: json: cannot unmarshal array into Go struct field ChatCompletionNewParams.messages of type param.Field[[]github.com/openai/openai-go.ChatCompletionMessageParamUnion]

Context is that I'm receiving ChatCompletionNewParams object in JSON format, and need to unmarshal it into a bona-fide ChatCompletionNewParams to fling it at the OpenAI client, but I can't because it won't convert due to the param.Fields intermediary types.

Am I missing something here?

joehewett commented 6 hours ago

Equivalent behaviour with the sashabaranov client:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "os"

    "github.com/sashabaranov/go-openai"
)

func main() {
    client := openai.NewClient(os.Getenv("OPENAI_API_KEY"))

    // Create a new request
    f := openai.ChatCompletionRequest{
        Model: openai.GPT4o,
        Messages: []openai.ChatCompletionMessage{
            {
                Role:    openai.ChatMessageRoleUser,
                Content: "Say this is a test",
            },
        },
    }

    // Marshal and print the request
    jsonRequest, err := json.Marshal(f)
    if err != nil {
        panic(err.Error())
    }
    fmt.Printf("Request marshalled: %s\n", string(jsonRequest))

    // Now unmarshal it
    var v openai.ChatCompletionRequest
    err = json.Unmarshal(jsonRequest, &v)
    if err != nil {
        panic(err.Error())
    }
    fmt.Printf("Request unmarshalled: %+v\n", v)

    // Send the request
    chatCompletion, err := client.CreateChatCompletion(context.TODO(), v)
    if err != nil {
        panic(err.Error())
    }

    println(chatCompletion.Choices[0].Message.Content)
}

Produces correct result:

joe@bongometrical:~/repos/play$ go run main.go
Request marshalled: {"model":"gpt-4o","messages":[{"role":"user","content":"Say this is a test"}]}
Request unmarshalled: {Model:gpt-4o Messages:[{Role:user Content:Say this is a test Refusal: MultiContent:[] Name: FunctionCall:<nil> ToolCalls:[] ToolCallID:}] MaxTokens:0 MaxCompletionTokens:0 Temperature:0 TopP:0 N:0 Stream:false Stop:[] PresencePenalty:0 ResponseFormat:<nil> Seed:<nil> FrequencyPenalty:0 LogitBias:map[] LogProbs:false TopLogProbs:0 User: Functions:[] FunctionCall:<nil> Tools:[] ToolChoice:<nil> StreamOptions:<nil> ParallelToolCalls:<nil> Store:false Metadata:map[]}
This is a test. How can I assist you further?