Skip to content

Commit f2b2356

Browse files
authored
[ai] Reponse serialization, http.Header standard (#516)
1 parent e7e4243 commit f2b2356

File tree

11 files changed

+432
-41
lines changed

11 files changed

+432
-41
lines changed

aisdk/ai/api/embedding_model.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"context"
5+
"net/http"
56
)
67

78
// Embedding is a vector, i.e. an array of numbers.
@@ -60,5 +61,5 @@ type EmbeddingUsage struct {
6061
// EmbeddingRawResponse contains raw response information for debugging.
6162
type EmbeddingRawResponse struct {
6263
// Headers are the response headers.
63-
Headers map[string]string
64+
Headers http.Header
6465
}

aisdk/ai/api/embedding_options.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package api
22

3+
import "net/http"
4+
35
// EmbeddingOption represent the options for generating embeddings.
46
type EmbeddingOption func(*EmbeddingOptions)
57

68
// WithEmbeddingHeaders sets HTTP headers to be sent with the request.
79
// Only applicable for HTTP-based providers.
8-
func WithEmbeddingHeaders(headers map[string]string) EmbeddingOption {
10+
func WithEmbeddingHeaders(headers http.Header) EmbeddingOption {
911
return func(o *EmbeddingOptions) {
1012
o.Headers = headers
1113
}
@@ -15,5 +17,5 @@ func WithEmbeddingHeaders(headers map[string]string) EmbeddingOption {
1517
type EmbeddingOptions struct {
1618
// Headers are additional HTTP headers to be sent with the request.
1719
// Only applicable for HTTP-based providers.
18-
Headers map[string]string
20+
Headers http.Header
1921
}

aisdk/ai/api/image_model.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"context"
5+
"net/http"
56
"time"
67
)
78

@@ -69,5 +70,5 @@ type ImageResponseMetadata struct {
6970
ModelID string
7071

7172
// Headers are the response headers.
72-
Headers map[string]string
73+
Headers http.Header
7374
}

aisdk/ai/api/image_model_call_options.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package api
22

3+
import "net/http"
4+
35
// ImageCallOptions represents the options for generating images.
46
type ImageCallOptions struct {
57
// N is the number of images to generate.
@@ -40,7 +42,7 @@ type ImageCallOptions struct {
4042

4143
// Headers are additional HTTP headers to be sent with the request.
4244
// Only applicable for HTTP-based providers.
43-
Headers map[string]string
45+
Headers http.Header
4446
}
4547

4648
// ImageCallOption is a function that modifies ImageCallOptions.
@@ -106,7 +108,7 @@ func WithImageProviderOptions(options map[string]map[string]any) ImageCallOption
106108

107109
// WithImageHeaders sets additional HTTP headers to be sent with the request.
108110
// Only applicable for HTTP-based providers.
109-
func WithImageHeaders(headers map[string]string) ImageCallOption {
111+
func WithImageHeaders(headers http.Header) ImageCallOption {
110112
return func(o *ImageCallOptions) {
111113
o.Headers = headers
112114
}

aisdk/ai/api/llm_call_options.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package api
33
import (
44
"encoding/json"
55
"fmt"
6+
"net/http"
67

78
jsonschema "github.com/sashabaranov/go-openai/jsonschema"
89
"github.com/tidwall/gjson"
@@ -59,7 +60,7 @@ type CallOptions struct {
5960

6061
// Headers specifies additional HTTP headers to send with the request.
6162
// Only applicable for HTTP-based providers.
62-
Headers map[string]string `json:"headers,omitempty"`
63+
Headers http.Header `json:"headers,omitempty"`
6364

6465
// ProviderMetadata contains additional provider-specific metadata.
6566
// The metadata is passed through to the provider from the AI SDK and enables

aisdk/ai/api/llm_call_options_test.go

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,12 @@ func TestCallOptions_JSON(t *testing.T) {
1414
jsonStr string
1515
}{
1616
{
17-
name: "basic_options",
17+
name: "basic_fields",
1818
jsonStr: `{
19-
"max_output_tokens": 1000,
19+
"max_output_tokens": 1500,
2020
"temperature": 0.7,
21+
"stop_sequences": ["STOP", "END"],
2122
"top_p": 0.9,
22-
"stop_sequences": ["STOP", "END"]
23-
}`,
24-
},
25-
{
26-
name: "complex_options_with_response_format",
27-
jsonStr: `{
28-
"max_output_tokens": 2000,
29-
"temperature": 0.5,
3023
"top_k": 40,
3124
"presence_penalty": 0.1,
3225
"frequency_penalty": 0.2,
@@ -37,8 +30,8 @@ func TestCallOptions_JSON(t *testing.T) {
3730
"description": "A structured response"
3831
},
3932
"headers": {
40-
"X-Custom-Header": "custom-value",
41-
"Authorization": "Bearer token"
33+
"X-Custom-Header": ["custom-value"],
34+
"Authorization": ["Bearer token"]
4235
}
4336
}`,
4437
},
@@ -97,9 +90,9 @@ func TestCallOptions_JSON(t *testing.T) {
9790
"tool_name": "get_weather"
9891
},
9992
"headers": {
100-
"X-API-Key": "test-key",
101-
"User-Agent": "test-agent",
102-
"Content-Type": "application/json"
93+
"X-API-Key": ["test-key"],
94+
"User-Agent": ["test-agent"],
95+
"Content-Type": ["application/json"]
10396
},
10497
"provider_metadata": {
10598
"openai": {
@@ -187,9 +180,9 @@ func BenchmarkCallOptionsUnmarshal(b *testing.B) {
187180
"tool_name": "get_weather"
188181
},
189182
"headers": {
190-
"X-API-Key": "test-key",
191-
"User-Agent": "test-agent",
192-
"Content-Type": "application/json"
183+
"X-API-Key": ["test-key"],
184+
"User-Agent": ["test-agent"],
185+
"Content-Type": ["application/json"]
193186
},
194187
"provider_metadata": {
195188
"openai": {

aisdk/ai/api/llm_language_model.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package api
22

33
import (
44
"context"
5+
"encoding/json"
56
"iter"
67
"net/http"
78
"time"
@@ -86,6 +87,33 @@ type Response struct {
8687

8788
func (r Response) GetProviderMetadata() *ProviderMetadata { return r.ProviderMetadata }
8889

90+
// UnmarshalJSON implements custom JSON unmarshaling for Response
91+
func (r *Response) UnmarshalJSON(data []byte) error {
92+
// Use a temporary struct to unmarshal everything except content
93+
type ResponseAlias Response
94+
temp := struct {
95+
*ResponseAlias
96+
Content []json.RawMessage `json:"content,omitempty"`
97+
}{
98+
ResponseAlias: (*ResponseAlias)(r),
99+
}
100+
101+
if err := json.Unmarshal(data, &temp); err != nil {
102+
return err
103+
}
104+
105+
// Handle content blocks
106+
if temp.Content != nil {
107+
content, err := unmarshalContentBlocks(temp.Content)
108+
if err != nil {
109+
return err
110+
}
111+
r.Content = content
112+
}
113+
114+
return nil
115+
}
116+
89117
// StreamResponse represents the result of a streaming language model call.
90118
type StreamResponse struct {
91119
// Stream is the sequence of events received from the model.
@@ -150,8 +178,8 @@ type ResponseInfo struct {
150178
// ModelID of the model that was used to generate the response, if the provider sends one.
151179
ModelID string `json:"model_id,omitzero"`
152180

153-
// Header contains a map of the HTTP response headers.
154-
Header http.Header
181+
// Headers contains the HTTP response headers.
182+
Headers http.Header `json:"headers,omitzero"`
155183

156184
// Body is the raw HTTP body that was returned by the provider.
157185
// Not provided for streaming responses.

0 commit comments

Comments
 (0)