diff --git a/.stats.yml b/.stats.yml index 82b73d82..f4960fb0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,2 @@ -configured_endpoints: 68 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-dfb00c627f58e5180af7a9b29ed2f2aa0764a3b9daa6a32a1cc45bc8e48dfe15.yml +configured_endpoints: 73 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-4aa6ee65ba9efc789e05e6a5ef0883b2cadf06def8efd863dbf75e9e233067e1.yml diff --git a/api.md b/api.md index 306b7933..304da154 100644 --- a/api.md +++ b/api.md @@ -64,13 +64,25 @@ Response Types: - openai.ChatCompletion - openai.ChatCompletionAudio - openai.ChatCompletionChunk +- openai.ChatCompletionDeleted - openai.ChatCompletionMessage - openai.ChatCompletionMessageToolCall +- openai.ChatCompletionStoreMessage - openai.ChatCompletionTokenLogprob Methods: - client.Chat.Completions.New(ctx context.Context, body openai.ChatCompletionNewParams) (openai.ChatCompletion, error) +- client.Chat.Completions.Get(ctx context.Context, completionID string) (openai.ChatCompletion, error) +- client.Chat.Completions.Update(ctx context.Context, completionID string, body openai.ChatCompletionUpdateParams) (openai.ChatCompletion, error) +- client.Chat.Completions.List(ctx context.Context, query openai.ChatCompletionListParams) (pagination.CursorPage[openai.ChatCompletion], error) +- client.Chat.Completions.Delete(ctx context.Context, completionID string) (openai.ChatCompletionDeleted, error) + +### Messages + +Methods: + +- client.Chat.Completions.Messages.List(ctx context.Context, completionID string, query openai.ChatCompletionMessageListParams) (pagination.CursorPage[openai.ChatCompletionStoreMessage], error) # Embeddings diff --git a/chatcompletion.go b/chatcompletion.go index c20a29b6..78f8cb36 100644 --- a/chatcompletion.go +++ b/chatcompletion.go @@ -4,12 +4,17 @@ package openai import ( "context" + "errors" + "fmt" "net/http" + "net/url" "github.com/openai/openai-go/internal/apijson" + "github.com/openai/openai-go/internal/apiquery" "github.com/openai/openai-go/internal/param" "github.com/openai/openai-go/internal/requestconfig" "github.com/openai/openai-go/option" + "github.com/openai/openai-go/packages/pagination" "github.com/openai/openai-go/packages/ssestream" "github.com/openai/openai-go/shared" ) @@ -21,7 +26,8 @@ import ( // automatically. You should not instantiate this service directly, and instead use // the [NewChatCompletionService] method instead. type ChatCompletionService struct { - Options []option.RequestOption + Options []option.RequestOption + Messages *ChatCompletionMessageService } // NewChatCompletionService generates a new service that applies the given options @@ -30,6 +36,7 @@ type ChatCompletionService struct { func NewChatCompletionService(opts ...option.RequestOption) (r *ChatCompletionService) { r = &ChatCompletionService{} r.Options = opts + r.Messages = NewChatCompletionMessageService(opts...) return } @@ -72,6 +79,71 @@ func (r *ChatCompletionService) NewStreaming(ctx context.Context, body ChatCompl return ssestream.NewStream[ChatCompletionChunk](ssestream.NewDecoder(raw), err) } +// Get a stored chat completion. Only chat completions that have been created with +// the `store` parameter set to `true` will be returned. +func (r *ChatCompletionService) Get(ctx context.Context, completionID string, opts ...option.RequestOption) (res *ChatCompletion, err error) { + opts = append(r.Options[:], opts...) + if completionID == "" { + err = errors.New("missing required completion_id parameter") + return + } + path := fmt.Sprintf("chat/completions/%s", completionID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodGet, path, nil, &res, opts...) + return +} + +// Modify a stored chat completion. Only chat completions that have been created +// with the `store` parameter set to `true` can be modified. Currently, the only +// supported modification is to update the `metadata` field. +func (r *ChatCompletionService) Update(ctx context.Context, completionID string, body ChatCompletionUpdateParams, opts ...option.RequestOption) (res *ChatCompletion, err error) { + opts = append(r.Options[:], opts...) + if completionID == "" { + err = errors.New("missing required completion_id parameter") + return + } + path := fmt.Sprintf("chat/completions/%s", completionID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) + return +} + +// List stored chat completions. Only chat completions that have been stored with +// the `store` parameter set to `true` will be returned. +func (r *ChatCompletionService) List(ctx context.Context, query ChatCompletionListParams, opts ...option.RequestOption) (res *pagination.CursorPage[ChatCompletion], err error) { + var raw *http.Response + opts = append(r.Options[:], opts...) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + path := "chat/completions" + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodGet, path, query, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// List stored chat completions. Only chat completions that have been stored with +// the `store` parameter set to `true` will be returned. +func (r *ChatCompletionService) ListAutoPaging(ctx context.Context, query ChatCompletionListParams, opts ...option.RequestOption) *pagination.CursorPageAutoPager[ChatCompletion] { + return pagination.NewCursorPageAutoPager(r.List(ctx, query, opts...)) +} + +// Delete a stored chat completion. Only chat completions that have been created +// with the `store` parameter set to `true` can be deleted. +func (r *ChatCompletionService) Delete(ctx context.Context, completionID string, opts ...option.RequestOption) (res *ChatCompletionDeleted, err error) { + opts = append(r.Options[:], opts...) + if completionID == "" { + err = errors.New("missing required completion_id parameter") + return + } + path := fmt.Sprintf("chat/completions/%s", completionID) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodDelete, path, nil, &res, opts...) + return +} + // Represents a chat completion response returned by model, based on the provided // input. type ChatCompletion struct { @@ -1001,6 +1073,49 @@ func (r ChatCompletionContentPartTextType) IsKnown() bool { return false } +type ChatCompletionDeleted struct { + // The ID of the chat completion that was deleted. + ID string `json:"id,required"` + // Whether the chat completion was deleted. + Deleted bool `json:"deleted,required"` + // The type of object being deleted. + Object ChatCompletionDeletedObject `json:"object,required"` + JSON chatCompletionDeletedJSON `json:"-"` +} + +// chatCompletionDeletedJSON contains the JSON metadata for the struct +// [ChatCompletionDeleted] +type chatCompletionDeletedJSON struct { + ID apijson.Field + Deleted apijson.Field + Object apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *ChatCompletionDeleted) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r chatCompletionDeletedJSON) RawJSON() string { + return r.raw +} + +// The type of object being deleted. +type ChatCompletionDeletedObject string + +const ( + ChatCompletionDeletedObjectChatCompletionDeleted ChatCompletionDeletedObject = "chat.completion.deleted" +) + +func (r ChatCompletionDeletedObject) IsKnown() bool { + switch r { + case ChatCompletionDeletedObjectChatCompletionDeleted: + return true + } + return false +} + // Developer-provided instructions that the model should follow, regardless of // messages sent by the user. With o1 models and newer, `developer` messages // replace the previous `system` messages. @@ -1433,6 +1548,30 @@ func (r ChatCompletionReasoningEffort) IsKnown() bool { return false } +// A chat completion message generated by the model. +type ChatCompletionStoreMessage struct { + // The identifier of the chat message. + ID string `json:"id,required"` + JSON chatCompletionStoreMessageJSON `json:"-"` + ChatCompletionMessage +} + +// chatCompletionStoreMessageJSON contains the JSON metadata for the struct +// [ChatCompletionStoreMessage] +type chatCompletionStoreMessageJSON struct { + ID apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *ChatCompletionStoreMessage) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r chatCompletionStoreMessageJSON) RawJSON() string { + return r.raw +} + // Options for streaming response. Only set this when you set `stream: true`. type ChatCompletionStreamOptionsParam struct { // If set, an additional chunk will be streamed before the `data: [DONE]` message. @@ -2037,3 +2176,59 @@ type ChatCompletionNewParamsStopUnion interface { type ChatCompletionNewParamsStopArray []string func (r ChatCompletionNewParamsStopArray) ImplementsChatCompletionNewParamsStopUnion() {} + +type ChatCompletionUpdateParams struct { + // Set of 16 key-value pairs that can be attached to an object. This can be useful + // for storing additional information about the object in a structured format, and + // querying for objects via API or the dashboard. + // + // Keys are strings with a maximum length of 64 characters. Values are strings with + // a maximum length of 512 characters. + Metadata param.Field[shared.MetadataParam] `json:"metadata,required"` +} + +func (r ChatCompletionUpdateParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + +type ChatCompletionListParams struct { + // Identifier for the last chat completion from the previous pagination request. + After param.Field[string] `query:"after"` + // Number of chat completions to retrieve. + Limit param.Field[int64] `query:"limit"` + // A list of metadata keys to filter the chat completions by. Example: + // + // `metadata[key1]=value1&metadata[key2]=value2` + Metadata param.Field[shared.MetadataParam] `query:"metadata"` + // The model used to generate the chat completions. + Model param.Field[string] `query:"model"` + // Sort order for chat completions by timestamp. Use `asc` for ascending order or + // `desc` for descending order. Defaults to `asc`. + Order param.Field[ChatCompletionListParamsOrder] `query:"order"` +} + +// URLQuery serializes [ChatCompletionListParams]'s query parameters as +// `url.Values`. +func (r ChatCompletionListParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +// Sort order for chat completions by timestamp. Use `asc` for ascending order or +// `desc` for descending order. Defaults to `asc`. +type ChatCompletionListParamsOrder string + +const ( + ChatCompletionListParamsOrderAsc ChatCompletionListParamsOrder = "asc" + ChatCompletionListParamsOrderDesc ChatCompletionListParamsOrder = "desc" +) + +func (r ChatCompletionListParamsOrder) IsKnown() bool { + switch r { + case ChatCompletionListParamsOrderAsc, ChatCompletionListParamsOrderDesc: + return true + } + return false +} diff --git a/chatcompletion_test.go b/chatcompletion_test.go index 2248171e..1fb57aa8 100644 --- a/chatcompletion_test.go +++ b/chatcompletion_test.go @@ -99,3 +99,107 @@ func TestChatCompletionNewWithOptionalParams(t *testing.T) { t.Fatalf("err should be nil: %s", err.Error()) } } + +func TestChatCompletionGet(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := openai.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Chat.Completions.Get(context.TODO(), "completion_id") + if err != nil { + var apierr *openai.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestChatCompletionUpdate(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := openai.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Chat.Completions.Update( + context.TODO(), + "completion_id", + openai.ChatCompletionUpdateParams{ + Metadata: openai.F(shared.MetadataParam{ + "foo": "string", + }), + }, + ) + if err != nil { + var apierr *openai.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestChatCompletionListWithOptionalParams(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := openai.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Chat.Completions.List(context.TODO(), openai.ChatCompletionListParams{ + After: openai.F("after"), + Limit: openai.F(int64(0)), + Metadata: openai.F(shared.MetadataParam{ + "foo": "string", + }), + Model: openai.F("model"), + Order: openai.F(openai.ChatCompletionListParamsOrderAsc), + }) + if err != nil { + var apierr *openai.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} + +func TestChatCompletionDelete(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := openai.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Chat.Completions.Delete(context.TODO(), "completion_id") + if err != nil { + var apierr *openai.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/chatcompletionmessage.go b/chatcompletionmessage.go new file mode 100644 index 00000000..7228ac3e --- /dev/null +++ b/chatcompletionmessage.go @@ -0,0 +1,101 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package openai + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/url" + + "github.com/openai/openai-go/internal/apiquery" + "github.com/openai/openai-go/internal/param" + "github.com/openai/openai-go/internal/requestconfig" + "github.com/openai/openai-go/option" + "github.com/openai/openai-go/packages/pagination" +) + +// ChatCompletionMessageService contains methods and other services that help with +// interacting with the openai API. +// +// Note, unlike clients, this service does not read variables from the environment +// automatically. You should not instantiate this service directly, and instead use +// the [NewChatCompletionMessageService] method instead. +type ChatCompletionMessageService struct { + Options []option.RequestOption +} + +// NewChatCompletionMessageService generates a new service that applies the given +// options to each request. These options are applied after the parent client's +// options (if there is one), and before any request-specific options. +func NewChatCompletionMessageService(opts ...option.RequestOption) (r *ChatCompletionMessageService) { + r = &ChatCompletionMessageService{} + r.Options = opts + return +} + +// Get the messages in a stored chat completion. Only chat completions that have +// been created with the `store` parameter set to `true` will be returned. +func (r *ChatCompletionMessageService) List(ctx context.Context, completionID string, query ChatCompletionMessageListParams, opts ...option.RequestOption) (res *pagination.CursorPage[ChatCompletionStoreMessage], err error) { + var raw *http.Response + opts = append(r.Options[:], opts...) + opts = append([]option.RequestOption{option.WithResponseInto(&raw)}, opts...) + if completionID == "" { + err = errors.New("missing required completion_id parameter") + return + } + path := fmt.Sprintf("chat/completions/%s/messages", completionID) + cfg, err := requestconfig.NewRequestConfig(ctx, http.MethodGet, path, query, &res, opts...) + if err != nil { + return nil, err + } + err = cfg.Execute() + if err != nil { + return nil, err + } + res.SetPageConfig(cfg, raw) + return res, nil +} + +// Get the messages in a stored chat completion. Only chat completions that have +// been created with the `store` parameter set to `true` will be returned. +func (r *ChatCompletionMessageService) ListAutoPaging(ctx context.Context, completionID string, query ChatCompletionMessageListParams, opts ...option.RequestOption) *pagination.CursorPageAutoPager[ChatCompletionStoreMessage] { + return pagination.NewCursorPageAutoPager(r.List(ctx, completionID, query, opts...)) +} + +type ChatCompletionMessageListParams struct { + // Identifier for the last message from the previous pagination request. + After param.Field[string] `query:"after"` + // Number of messages to retrieve. + Limit param.Field[int64] `query:"limit"` + // Sort order for messages by timestamp. Use `asc` for ascending order or `desc` + // for descending order. Defaults to `asc`. + Order param.Field[ChatCompletionMessageListParamsOrder] `query:"order"` +} + +// URLQuery serializes [ChatCompletionMessageListParams]'s query parameters as +// `url.Values`. +func (r ChatCompletionMessageListParams) URLQuery() (v url.Values) { + return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{ + ArrayFormat: apiquery.ArrayQueryFormatBrackets, + NestedFormat: apiquery.NestedQueryFormatBrackets, + }) +} + +// Sort order for messages by timestamp. Use `asc` for ascending order or `desc` +// for descending order. Defaults to `asc`. +type ChatCompletionMessageListParamsOrder string + +const ( + ChatCompletionMessageListParamsOrderAsc ChatCompletionMessageListParamsOrder = "asc" + ChatCompletionMessageListParamsOrderDesc ChatCompletionMessageListParamsOrder = "desc" +) + +func (r ChatCompletionMessageListParamsOrder) IsKnown() bool { + switch r { + case ChatCompletionMessageListParamsOrderAsc, ChatCompletionMessageListParamsOrderDesc: + return true + } + return false +} diff --git a/chatcompletionmessage_test.go b/chatcompletionmessage_test.go new file mode 100644 index 00000000..23e2129a --- /dev/null +++ b/chatcompletionmessage_test.go @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +package openai_test + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/openai/openai-go" + "github.com/openai/openai-go/internal/testutil" + "github.com/openai/openai-go/option" +) + +func TestChatCompletionMessageListWithOptionalParams(t *testing.T) { + baseURL := "http://localhost:4010" + if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { + baseURL = envURL + } + if !testutil.CheckTestServer(t, baseURL) { + return + } + client := openai.NewClient( + option.WithBaseURL(baseURL), + option.WithAPIKey("My API Key"), + ) + _, err := client.Chat.Completions.Messages.List( + context.TODO(), + "completion_id", + openai.ChatCompletionMessageListParams{ + After: openai.F("after"), + Limit: openai.F(int64(0)), + Order: openai.F(openai.ChatCompletionMessageListParamsOrderAsc), + }, + ) + if err != nil { + var apierr *openai.Error + if errors.As(err, &apierr) { + t.Log(string(apierr.DumpRequest(true))) + } + t.Fatalf("err should be nil: %s", err.Error()) + } +} diff --git a/moderation.go b/moderation.go index f60670cb..a8409f48 100644 --- a/moderation.go +++ b/moderation.go @@ -90,11 +90,11 @@ type ModerationCategories struct { // Content that includes instructions or advice that facilitate the planning or // execution of wrongdoing, or that gives advice or instruction on how to commit // illicit acts. For example, "how to shoplift" would fit this category. - Illicit bool `json:"illicit,required"` + Illicit bool `json:"illicit,required,nullable"` // Content that includes instructions or advice that facilitate the planning or // execution of wrongdoing that also includes violence, or that gives advice or // instruction on the procurement of any weapon. - IllicitViolent bool `json:"illicit/violent,required"` + IllicitViolent bool `json:"illicit/violent,required,nullable"` // Content that promotes, encourages, or depicts acts of self-harm, such as // suicide, cutting, and eating disorders. SelfHarm bool `json:"self-harm,required"`