Skip to content

Commit 6f645e0

Browse files
authored
Merge pull request #2 from Khan/release/v1.40.5
Release/v1.40.5
2 parents c21e5b8 + 5ded51a commit 6f645e0

31 files changed

+1774
-143
lines changed

.codecov.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
coverage:
2+
ignore:
3+
- "examples/**"
4+
- "internal/test/**"

.github/workflows/pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
go-version: '1.24'
1717
- name: Run vet
1818
run: |
19-
go vet .
19+
go vet -stdversion ./...
2020
- name: Run golangci-lint
2121
uses: golangci/golangci-lint-action@v7
2222
with:

chat.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"encoding/json"
66
"errors"
77
"net/http"
8+
9+
"github.com/sashabaranov/go-openai/jsonschema"
810
)
911

1012
// Chat message role defined by the OpenAI API.
@@ -221,6 +223,31 @@ type ChatCompletionResponseFormatJSONSchema struct {
221223
Strict bool `json:"strict"`
222224
}
223225

226+
func (r *ChatCompletionResponseFormatJSONSchema) UnmarshalJSON(data []byte) error {
227+
type rawJSONSchema struct {
228+
Name string `json:"name"`
229+
Description string `json:"description,omitempty"`
230+
Schema json.RawMessage `json:"schema"`
231+
Strict bool `json:"strict"`
232+
}
233+
var raw rawJSONSchema
234+
if err := json.Unmarshal(data, &raw); err != nil {
235+
return err
236+
}
237+
r.Name = raw.Name
238+
r.Description = raw.Description
239+
r.Strict = raw.Strict
240+
if len(raw.Schema) > 0 && string(raw.Schema) != "null" {
241+
var d jsonschema.Definition
242+
err := json.Unmarshal(raw.Schema, &d)
243+
if err != nil {
244+
return err
245+
}
246+
r.Schema = &d
247+
}
248+
return nil
249+
}
250+
224251
// ChatCompletionRequest represents a request structure for chat completion API.
225252
type ChatCompletionRequest struct {
226253
Model string `json:"model"`
@@ -280,6 +307,8 @@ type ChatCompletionRequest struct {
280307
// Such as think mode for qwen3. "chat_template_kwargs": {"enable_thinking": false}
281308
// https://qwen.readthedocs.io/en/latest/deployment/vllm.html#thinking-non-thinking-modes
282309
ChatTemplateKwargs map[string]any `json:"chat_template_kwargs,omitempty"`
310+
// Specifies the latency tier to use for processing the request.
311+
ServiceTier ServiceTier `json:"service_tier,omitempty"`
283312
}
284313

285314
type StreamOptions struct {
@@ -363,6 +392,15 @@ const (
363392
FinishReasonNull FinishReason = "null"
364393
)
365394

395+
type ServiceTier string
396+
397+
const (
398+
ServiceTierAuto ServiceTier = "auto"
399+
ServiceTierDefault ServiceTier = "default"
400+
ServiceTierFlex ServiceTier = "flex"
401+
ServiceTierPriority ServiceTier = "priority"
402+
)
403+
366404
func (r FinishReason) MarshalJSON() ([]byte, error) {
367405
if r == FinishReasonNull || r == "" {
368406
return []byte("null"), nil
@@ -395,6 +433,7 @@ type ChatCompletionResponse struct {
395433
Usage Usage `json:"usage"`
396434
SystemFingerprint string `json:"system_fingerprint"`
397435
PromptFilterResults []PromptFilterResult `json:"prompt_filter_results,omitempty"`
436+
ServiceTier ServiceTier `json:"service_tier,omitempty"`
398437

399438
httpHeader
400439
}

chat_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,3 +946,142 @@ func TestFinishReason(t *testing.T) {
946946
}
947947
}
948948
}
949+
950+
func TestChatCompletionResponseFormatJSONSchema_UnmarshalJSON(t *testing.T) {
951+
type args struct {
952+
data []byte
953+
}
954+
tests := []struct {
955+
name string
956+
args args
957+
wantErr bool
958+
}{
959+
{
960+
"",
961+
args{
962+
data: []byte(`{
963+
"name": "math_response",
964+
"strict": true,
965+
"schema": {
966+
"type": "object",
967+
"properties": {
968+
"steps": {
969+
"type": "array",
970+
"items": {
971+
"type": "object",
972+
"properties": {
973+
"explanation": { "type": "string" },
974+
"output": { "type": "string" }
975+
},
976+
"required": ["explanation","output"],
977+
"additionalProperties": false
978+
}
979+
},
980+
"final_answer": { "type": "string" }
981+
},
982+
"required": ["steps","final_answer"],
983+
"additionalProperties": false
984+
}
985+
}`),
986+
},
987+
false,
988+
},
989+
{
990+
"",
991+
args{
992+
data: []byte(`{
993+
"name": "math_response",
994+
"strict": true,
995+
"schema": null
996+
}`),
997+
},
998+
false,
999+
},
1000+
{
1001+
"",
1002+
args{
1003+
data: []byte(`[123,456]`),
1004+
},
1005+
true,
1006+
},
1007+
{
1008+
"",
1009+
args{
1010+
data: []byte(`{
1011+
"name": "math_response",
1012+
"strict": true,
1013+
"schema": 123456
1014+
}`),
1015+
},
1016+
true,
1017+
},
1018+
}
1019+
for _, tt := range tests {
1020+
t.Run(tt.name, func(t *testing.T) {
1021+
var r openai.ChatCompletionResponseFormatJSONSchema
1022+
err := r.UnmarshalJSON(tt.args.data)
1023+
if (err != nil) != tt.wantErr {
1024+
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
1025+
}
1026+
})
1027+
}
1028+
}
1029+
1030+
func TestChatCompletionRequest_UnmarshalJSON(t *testing.T) {
1031+
type args struct {
1032+
bs []byte
1033+
}
1034+
tests := []struct {
1035+
name string
1036+
args args
1037+
wantErr bool
1038+
}{
1039+
{
1040+
"",
1041+
args{bs: []byte(`{
1042+
"model": "llama3-1b",
1043+
"messages": [
1044+
{ "role": "system", "content": "You are a helpful math tutor." },
1045+
{ "role": "user", "content": "solve 8x + 31 = 2" }
1046+
],
1047+
"response_format": {
1048+
"type": "json_schema",
1049+
"json_schema": {
1050+
"name": "math_response",
1051+
"strict": true,
1052+
"schema": {
1053+
"type": "object",
1054+
"properties": {
1055+
"steps": {
1056+
"type": "array",
1057+
"items": {
1058+
"type": "object",
1059+
"properties": {
1060+
"explanation": { "type": "string" },
1061+
"output": { "type": "string" }
1062+
},
1063+
"required": ["explanation","output"],
1064+
"additionalProperties": false
1065+
}
1066+
},
1067+
"final_answer": { "type": "string" }
1068+
},
1069+
"required": ["steps","final_answer"],
1070+
"additionalProperties": false
1071+
}
1072+
}
1073+
}
1074+
}`)},
1075+
false,
1076+
},
1077+
}
1078+
for _, tt := range tests {
1079+
t.Run(tt.name, func(t *testing.T) {
1080+
var m openai.ChatCompletionRequest
1081+
err := json.Unmarshal(tt.args.bs, &m)
1082+
if err != nil {
1083+
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
1084+
}
1085+
})
1086+
}
1087+
}

client.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ func NewOrgClient(authToken, org string) *Client {
7171
return NewClientWithConfig(config)
7272
}
7373

74+
// SetBaseURL updates the base URL for the client.
75+
// This allows changing the endpoint after client instantiation.
76+
func (c *Client) SetBaseURL(baseURL string) {
77+
c.config.BaseURL = baseURL
78+
}
79+
80+
// GetBaseURL returns the current base URL for the client.
81+
func (c *Client) GetBaseURL() string {
82+
return c.config.BaseURL
83+
}
84+
7485
type requestOptions struct {
7586
body any
7687
header http.Header
@@ -84,6 +95,20 @@ func withBody(body any) requestOption {
8495
}
8596
}
8697

98+
func withExtraBody(extraBody map[string]any) requestOption {
99+
return func(args *requestOptions) {
100+
// Assert that args.body is a map[string]any.
101+
bodyMap, ok := args.body.(map[string]any)
102+
if ok {
103+
// If it's a map[string]any then only add extraBody
104+
// fields to args.body otherwise keep only fields in request struct.
105+
for key, value := range extraBody {
106+
bodyMap[key] = value
107+
}
108+
}
109+
}
110+
}
111+
87112
func withContentType(contentType string) requestOption {
88113
return func(args *requestOptions) {
89114
args.header.Set("Content-Type", contentType)

client_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,35 @@ func TestClient(t *testing.T) {
3939
}
4040
}
4141

42+
func TestClient_SetBaseURL(t *testing.T) {
43+
const mockToken = "mock token"
44+
client := NewClient(mockToken)
45+
46+
// Test default base URL
47+
defaultBaseURL := client.GetBaseURL()
48+
if defaultBaseURL != openaiAPIURLv1 {
49+
t.Errorf("Expected default BaseURL to be %q, got %q", openaiAPIURLv1, defaultBaseURL)
50+
}
51+
52+
// Test setting a new base URL
53+
const newBaseURL = "https://custom-api.example.com/v1"
54+
client.SetBaseURL(newBaseURL)
55+
56+
// Verify the base URL was updated
57+
updatedBaseURL := client.GetBaseURL()
58+
if updatedBaseURL != newBaseURL {
59+
t.Errorf("Expected BaseURL to be %q after SetBaseURL, got %q", newBaseURL, updatedBaseURL)
60+
}
61+
62+
// Test that the updated base URL is used in fullURL method
63+
suffix := "/chat/completions"
64+
fullURL := client.fullURL(suffix)
65+
expectedFullURL := newBaseURL + suffix
66+
if fullURL != expectedFullURL {
67+
t.Errorf("Expected fullURL to be %q, got %q", expectedFullURL, fullURL)
68+
}
69+
}
70+
4271
func TestSetCommonHeadersAnthropic(t *testing.T) {
4372
config := DefaultAnthropicConfig("mock-token", "")
4473
client := NewClientWithConfig(config)

completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ type CompletionResponse struct {
242242
Created int64 `json:"created"`
243243
Model string `json:"model"`
244244
Choices []CompletionChoice `json:"choices"`
245-
Usage Usage `json:"usage"`
245+
Usage *Usage `json:"usage,omitempty"`
246246

247247
httpHeader
248248
}

completion_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func handleCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
192192
}
193193
inputTokens *= n
194194
completionTokens := completionReq.MaxTokens * len(prompts) * n
195-
res.Usage = openai.Usage{
195+
res.Usage = &openai.Usage{
196196
PromptTokens: inputTokens,
197197
CompletionTokens: completionTokens,
198198
TotalTokens: inputTokens + completionTokens,

0 commit comments

Comments
 (0)