Detailed changes
@@ -82,6 +82,14 @@ func (m *mockLanguageModel) Model() string {
return "mock-model"
}
+func (m *mockLanguageModel) GenerateObject(ctx context.Context, call ObjectCall) (*ObjectResponse, error) {
+ return nil, fmt.Errorf("mock GenerateObject not implemented")
+}
+
+func (m *mockLanguageModel) StreamObject(ctx context.Context, call ObjectCall) (ObjectStreamResponse, error) {
+ return nil, fmt.Errorf("mock StreamObject not implemented")
+}
+
// Test result.content - comprehensive content types (matches TS test)
func TestAgent_Generate_ResultContent_AllTypes(t *testing.T) {
t.Parallel()
@@ -1,6 +1,7 @@
package fantasy
import (
+ "errors"
"fmt"
"net/http"
"strings"
@@ -74,3 +75,30 @@ func (e RetryError) Unwrap() error {
func ErrorTitleForStatusCode(statusCode int) string {
return strings.ToLower(http.StatusText(statusCode))
}
+
+// NoObjectGeneratedError is returned when object generation fails
+// due to parsing errors, validation errors, or model failures.
+type NoObjectGeneratedError struct {
+ RawText string
+ ParseError error
+ ValidationError error
+ Usage Usage
+ FinishReason FinishReason
+}
+
+// Error implements the error interface.
+func (e *NoObjectGeneratedError) Error() string {
+ if e.ValidationError != nil {
+ return fmt.Sprintf("object validation failed: %v", e.ValidationError)
+ }
+ if e.ParseError != nil {
+ return fmt.Sprintf("failed to parse object: %v", e.ParseError)
+ }
+ return "failed to generate object"
+}
+
+// IsNoObjectGeneratedError checks if an error is of type NoObjectGeneratedError.
+func IsNoObjectGeneratedError(err error) bool {
+ var target *NoObjectGeneratedError
+ return errors.As(err, &target)
+}
@@ -0,0 +1,26 @@
+module structured-outputs
+
+go 1.25.2
+
+replace charm.land/fantasy => ../..
+
+require charm.land/fantasy v0.0.0-00010101000000-000000000000
+
+require (
+ github.com/RealAlexandreAI/json-repair v0.0.14 // indirect
+ github.com/charmbracelet/x/exp/slice v0.0.0-20250904123553-b4e2667e5ad5 // indirect
+ github.com/charmbracelet/x/json v0.2.0 // indirect
+ github.com/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3 // indirect
+ github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
+ github.com/goccy/go-yaml v1.18.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/kaptinlin/go-i18n v0.2.0 // indirect
+ github.com/kaptinlin/jsonschema v0.5.2 // indirect
+ github.com/kaptinlin/messageformat-go v0.4.5 // indirect
+ github.com/openai/openai-go/v2 v2.7.1 // indirect
+ github.com/tidwall/gjson v1.18.0 // indirect
+ github.com/tidwall/match v1.1.1 // indirect
+ github.com/tidwall/pretty v1.2.1 // indirect
+ github.com/tidwall/sjson v1.2.5 // indirect
+ golang.org/x/text v0.29.0 // indirect
+)
@@ -0,0 +1,44 @@
+github.com/RealAlexandreAI/json-repair v0.0.14 h1:4kTqotVonDVTio5n2yweRUELVcNe2x518wl0bCsw0t0=
+github.com/RealAlexandreAI/json-repair v0.0.14/go.mod h1:GKJi5borR78O8c7HCVbgqjhoiVibZ6hJldxbc6dGrAI=
+github.com/charmbracelet/x/exp/slice v0.0.0-20250904123553-b4e2667e5ad5 h1:DTSZxdV9qQagD4iGcAt9RgaRBZtJl01bfKgdLzUzUPI=
+github.com/charmbracelet/x/exp/slice v0.0.0-20250904123553-b4e2667e5ad5/go.mod h1:vI5nDVMWi6veaYH+0Fmvpbe/+cv/iJfMntdh+N0+Tms=
+github.com/charmbracelet/x/json v0.2.0 h1:DqB+ZGx2h+Z+1s98HOuOyli+i97wsFQIxP2ZQANTPrQ=
+github.com/charmbracelet/x/json v0.2.0/go.mod h1:opFIflx2YgXgi49xVUu8gEQ21teFAxyMwvOiZhIvWNM=
+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/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3 h1:02WINGfSX5w0Mn+F28UyRoSt9uvMhKguwWMlOAh6U/0=
+github.com/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3/go.mod h1:uNVvRXArCGbZ508SxYYTC5v1JWoz2voff5pm25jU1Ok=
+github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
+github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/kaptinlin/go-i18n v0.2.0 h1:8iwjAERQbCVF78c3HxC4MxUDxDRFvQVQlMDvlsO43hU=
+github.com/kaptinlin/go-i18n v0.2.0/go.mod h1:gRHEMrTHtQLsAFwulPbJG71TwHjXxkagn88O8FI8FuA=
+github.com/kaptinlin/jsonschema v0.5.2 h1:ipUBEv1/RnT+ErwdqXZ3Xtwkwp6uqp/Q9lFILrwhUfc=
+github.com/kaptinlin/jsonschema v0.5.2/go.mod h1:HuWb90460GwFxRe0i9Ni3Z7YXwkjpqjeccWTB9gTZZE=
+github.com/kaptinlin/messageformat-go v0.4.5 h1:Y1CTf38O6lKKXX/UZTwb2Xw7c6DPk7kjQEHPJW6qxTI=
+github.com/kaptinlin/messageformat-go v0.4.5/go.mod h1:r0PH7FsxJX8jS/n6LAYZon5w3X+yfCLUrquqYd2H7ks=
+github.com/openai/openai-go/v2 v2.7.1 h1:/tfvTJhfv7hTSL8mWwc5VL4WLLSDL5yn9VqVykdu9r8=
+github.com/openai/openai-go/v2 v2.7.1/go.mod h1:jrJs23apqJKKbT+pqtFgNKpRju/KP9zpUTZhz3GElQE=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+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=
+github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
+golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
+golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -0,0 +1,99 @@
+package main
+
+// This example demonstrates how to get structured, type-safe outputs from an
+// LLM. Here we're getting a recipe with validated fields that we can use
+// directly in our code.
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "charm.land/fantasy"
+ "charm.land/fantasy/object"
+ "charm.land/fantasy/providers/openai"
+)
+
+// Here's what we want the LLM to fill out. The struct tags tell the model
+// what each field is for.
+type Recipe struct {
+ Name string `json:"name" description:"The name of the recipe"`
+ Ingredients []string `json:"ingredients" description:"List of ingredients needed"`
+ Steps []string `json:"steps" description:"Step-by-step cooking instructions"`
+ PrepTime int `json:"prep_time" description:"Preparation time in minutes"`
+}
+
+func main() {
+ // We'll use OpenAI for this one.
+ apiKey := os.Getenv("OPENAI_API_KEY")
+ if apiKey == "" {
+ fmt.Println("Please set OPENAI_API_KEY environment variable")
+ os.Exit(1)
+ }
+
+ ctx := context.Background()
+
+ // Set up the provider.
+ provider, err := openai.New(openai.WithAPIKey(apiKey))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Whoops: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Pick the model.
+ model, err := provider.LanguageModel(ctx, "gpt-4o-mini")
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Dang: %v\n", err)
+ os.Exit(1)
+ }
+
+ fmt.Println("\nšŖ Generating a recipe...")
+
+ // Ask for a structured recipe. The model will return a Recipe struct
+ // that's been validated against our schema.
+ result, err := object.Generate[Recipe](ctx, model, fantasy.ObjectCall{
+ Prompt: fantasy.Prompt{
+ fantasy.NewUserMessage("Give me a recipe for chocolate chip cookies"),
+ },
+ })
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Oof: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Now we have a type-safe Recipe we can use directly.
+ fmt.Printf("Recipe: %s\n", result.Object.Name)
+ fmt.Printf("Prep time: %d minutes\n", result.Object.PrepTime)
+ fmt.Printf("Ingredients: %d\n", len(result.Object.Ingredients))
+ for i, ing := range result.Object.Ingredients {
+ fmt.Printf(" %d. %s\n", i+1, ing)
+ }
+ fmt.Printf("Steps: %d\n", len(result.Object.Steps))
+ for i, step := range result.Object.Steps {
+ fmt.Printf(" %d. %s\n", i+1, step)
+ }
+ fmt.Printf("\nTokens used: %d\n\n", result.Usage.TotalTokens)
+
+ // Want to see progressive updates as the object builds? Use streaming!
+ fmt.Println("š Now let's try streaming...")
+
+ stream, err := object.Stream[Recipe](ctx, model, fantasy.ObjectCall{
+ Prompt: fantasy.Prompt{
+ fantasy.NewUserMessage("Give me a recipe for banana bread"),
+ },
+ })
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Oof: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Watch the recipe build in real-time!
+ updateCount := 0
+ for partial := range stream.PartialObjectStream() {
+ updateCount++
+ fmt.Printf(" Update %d: %s (%d ingredients, %d steps)\n",
+ updateCount, partial.Name, len(partial.Ingredients), len(partial.Steps))
+ }
+
+ fmt.Println()
+}
@@ -1,9 +1,10 @@
module charm.land/fantasy
-go 1.24.5
+go 1.25
require (
cloud.google.com/go/auth v0.17.0
+ github.com/RealAlexandreAI/json-repair v0.0.14
github.com/aws/aws-sdk-go-v2 v1.39.6
github.com/aws/smithy-go v1.23.2
github.com/charmbracelet/anthropic-sdk-go v0.0.0-20251024181547-21d6f3d9a904
@@ -12,6 +13,7 @@ require (
github.com/go-viper/mapstructure/v2 v2.4.0
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
+ github.com/kaptinlin/jsonschema v0.5.2
github.com/openai/openai-go/v2 v2.7.1
github.com/stretchr/testify v1.11.1
go.yaml.in/yaml/v4 v4.0.0-rc.3
@@ -40,13 +42,17 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/goccy/go-yaml v1.18.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
+ github.com/kaptinlin/go-i18n v0.2.0 // indirect
+ github.com/kaptinlin/messageformat-go v0.4.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
@@ -60,9 +66,9 @@ require (
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/net v0.43.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.35.0 // indirect
- golang.org/x/text v0.28.0 // indirect
+ golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.12.0 // indirect
google.golang.org/api v0.239.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
@@ -14,6 +14,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xP
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/RealAlexandreAI/json-repair v0.0.14 h1:4kTqotVonDVTio5n2yweRUELVcNe2x518wl0bCsw0t0=
+github.com/RealAlexandreAI/json-repair v0.0.14/go.mod h1:GKJi5borR78O8c7HCVbgqjhoiVibZ6hJldxbc6dGrAI=
github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk=
github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
@@ -52,6 +54,8 @@ 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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3 h1:02WINGfSX5w0Mn+F28UyRoSt9uvMhKguwWMlOAh6U/0=
+github.com/go-json-experiment/json v0.0.0-20250910080747-cc2cfa0554c3/go.mod h1:uNVvRXArCGbZ508SxYYTC5v1JWoz2voff5pm25jU1Ok=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -59,6 +63,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@@ -77,6 +83,12 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/kaptinlin/go-i18n v0.2.0 h1:8iwjAERQbCVF78c3HxC4MxUDxDRFvQVQlMDvlsO43hU=
+github.com/kaptinlin/go-i18n v0.2.0/go.mod h1:gRHEMrTHtQLsAFwulPbJG71TwHjXxkagn88O8FI8FuA=
+github.com/kaptinlin/jsonschema v0.5.2 h1:ipUBEv1/RnT+ErwdqXZ3Xtwkwp6uqp/Q9lFILrwhUfc=
+github.com/kaptinlin/jsonschema v0.5.2/go.mod h1:HuWb90460GwFxRe0i9Ni3Z7YXwkjpqjeccWTB9gTZZE=
+github.com/kaptinlin/messageformat-go v0.4.5 h1:Y1CTf38O6lKKXX/UZTwb2Xw7c6DPk7kjQEHPJW6qxTI=
+github.com/kaptinlin/messageformat-go v0.4.5/go.mod h1:r0PH7FsxJX8jS/n6LAYZon5w3X+yfCLUrquqYd2H7ks=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -85,6 +97,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/openai/openai-go/v2 v2.7.1 h1:/tfvTJhfv7hTSL8mWwc5VL4WLLSDL5yn9VqVykdu9r8=
github.com/openai/openai-go/v2 v2.7.1/go.mod h1:jrJs23apqJKKbT+pqtFgNKpRju/KP9zpUTZhz3GElQE=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -127,12 +141,12 @@ golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
+golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
+golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
google.golang.org/api v0.239.0 h1:2hZKUnFZEy81eugPs4e2XzIJ5SOwQg0G82bpXD65Puo=
@@ -219,7 +219,6 @@ type Call struct {
ProviderOptions ProviderOptions `json:"provider_options"`
}
-// CallWarningType represents the type of call warning.
// CallWarningType represents the type of call warning.
type CallWarningType string
@@ -248,6 +247,9 @@ type LanguageModel interface {
Generate(context.Context, Call) (*Response, error)
Stream(context.Context, Call) (StreamResponse, error)
+ GenerateObject(context.Context, ObjectCall) (*ObjectResponse, error)
+ StreamObject(context.Context, ObjectCall) (ObjectStreamResponse, error)
+
Provider() string
Model() string
}
@@ -0,0 +1,233 @@
+package fantasy
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "iter"
+ "reflect"
+
+ "charm.land/fantasy/schema"
+)
+
+// ObjectMode specifies how structured output should be generated.
+type ObjectMode string
+
+const (
+ // ObjectModeAuto lets the provider choose the best approach.
+ ObjectModeAuto ObjectMode = "auto"
+
+ // ObjectModeJSON forces the use of native JSON mode (if supported).
+ ObjectModeJSON ObjectMode = "json"
+
+ // ObjectModeTool forces the use of tool-based approach.
+ ObjectModeTool ObjectMode = "tool"
+
+ // ObjectModeText uses text generation with schema in prompt (fallback for models without tool/JSON support).
+ ObjectModeText ObjectMode = "text"
+)
+
+// ObjectCall represents a request to generate a structured object.
+type ObjectCall struct {
+ Prompt Prompt
+ Schema Schema
+ SchemaName string
+ SchemaDescription string
+
+ MaxOutputTokens *int64
+ Temperature *float64
+ TopP *float64
+ TopK *int64
+ PresencePenalty *float64
+ FrequencyPenalty *float64
+
+ ProviderOptions ProviderOptions
+
+ RepairText schema.ObjectRepairFunc
+}
+
+// ObjectResponse represents the response from a structured object generation.
+type ObjectResponse struct {
+ Object any
+ RawText string
+ Usage Usage
+ FinishReason FinishReason
+ Warnings []CallWarning
+ ProviderMetadata ProviderMetadata
+}
+
+// ObjectStreamPartType indicates the type of stream part.
+type ObjectStreamPartType string
+
+const (
+ // ObjectStreamPartTypeObject is emitted when a new partial object is available.
+ ObjectStreamPartTypeObject ObjectStreamPartType = "object"
+
+ // ObjectStreamPartTypeTextDelta is emitted for text deltas (if model generates text).
+ ObjectStreamPartTypeTextDelta ObjectStreamPartType = "text-delta"
+
+ // ObjectStreamPartTypeError is emitted when an error occurs.
+ ObjectStreamPartTypeError ObjectStreamPartType = "error"
+
+ // ObjectStreamPartTypeFinish is emitted when streaming completes.
+ ObjectStreamPartTypeFinish ObjectStreamPartType = "finish"
+)
+
+// ObjectStreamPart represents a single chunk in the object stream.
+type ObjectStreamPart struct {
+ Type ObjectStreamPartType
+ Object any
+ Delta string
+ Error error
+ Usage Usage
+ FinishReason FinishReason
+ Warnings []CallWarning
+ ProviderMetadata ProviderMetadata
+}
+
+// ObjectStreamResponse is an iterator over ObjectStreamPart.
+type ObjectStreamResponse = iter.Seq[ObjectStreamPart]
+
+// ObjectResult is a typed result wrapper returned by GenerateObject[T].
+type ObjectResult[T any] struct {
+ Object T
+ RawText string
+ Usage Usage
+ FinishReason FinishReason
+ Warnings []CallWarning
+ ProviderMetadata ProviderMetadata
+}
+
+// StreamObjectResult provides typed access to a streaming object generation result.
+type StreamObjectResult[T any] struct {
+ stream ObjectStreamResponse
+ ctx context.Context
+}
+
+// NewStreamObjectResult creates a typed stream result from an untyped stream.
+func NewStreamObjectResult[T any](ctx context.Context, stream ObjectStreamResponse) *StreamObjectResult[T] {
+ return &StreamObjectResult[T]{
+ stream: stream,
+ ctx: ctx,
+ }
+}
+
+// PartialObjectStream returns an iterator that yields progressively more complete objects.
+// Only emits when the object actually changes (deduplication).
+func (s *StreamObjectResult[T]) PartialObjectStream() iter.Seq[T] {
+ return func(yield func(T) bool) {
+ var lastObject T
+ var hasEmitted bool
+
+ for part := range s.stream {
+ if part.Type == ObjectStreamPartTypeObject && part.Object != nil {
+ var current T
+ if err := unmarshalObject(part.Object, ¤t); err != nil {
+ continue
+ }
+
+ if !hasEmitted || !reflect.DeepEqual(current, lastObject) {
+ if !yield(current) {
+ return
+ }
+ lastObject = current
+ hasEmitted = true
+ }
+ }
+ }
+ }
+}
+
+// TextStream returns an iterator that yields text deltas.
+// Useful if the model generates explanatory text alongside the object.
+func (s *StreamObjectResult[T]) TextStream() iter.Seq[string] {
+ return func(yield func(string) bool) {
+ for part := range s.stream {
+ if part.Type == ObjectStreamPartTypeTextDelta && part.Delta != "" {
+ if !yield(part.Delta) {
+ return
+ }
+ }
+ }
+ }
+}
+
+// FullStream returns an iterator that yields all stream parts including errors and metadata.
+func (s *StreamObjectResult[T]) FullStream() iter.Seq[ObjectStreamPart] {
+ return s.stream
+}
+
+// Object waits for the stream to complete and returns the final object.
+// Returns an error if streaming fails or no valid object was generated.
+func (s *StreamObjectResult[T]) Object() (*ObjectResult[T], error) {
+ var finalObject T
+ var usage Usage
+ var finishReason FinishReason
+ var warnings []CallWarning
+ var providerMetadata ProviderMetadata
+ var rawText string
+ var lastError error
+ hasObject := false
+
+ for part := range s.stream {
+ switch part.Type {
+ case ObjectStreamPartTypeObject:
+ if part.Object != nil {
+ if err := unmarshalObject(part.Object, &finalObject); err == nil {
+ hasObject = true
+ if jsonBytes, err := json.Marshal(part.Object); err == nil {
+ rawText = string(jsonBytes)
+ }
+ }
+ }
+
+ case ObjectStreamPartTypeError:
+ lastError = part.Error
+
+ case ObjectStreamPartTypeFinish:
+ usage = part.Usage
+ finishReason = part.FinishReason
+ if len(part.Warnings) > 0 {
+ warnings = part.Warnings
+ }
+ if len(part.ProviderMetadata) > 0 {
+ providerMetadata = part.ProviderMetadata
+ }
+ }
+ }
+
+ if lastError != nil {
+ return nil, lastError
+ }
+
+ if !hasObject {
+ return nil, &NoObjectGeneratedError{
+ RawText: rawText,
+ ParseError: fmt.Errorf("no valid object generated in stream"),
+ Usage: usage,
+ FinishReason: finishReason,
+ }
+ }
+
+ return &ObjectResult[T]{
+ Object: finalObject,
+ RawText: rawText,
+ Usage: usage,
+ FinishReason: finishReason,
+ Warnings: warnings,
+ ProviderMetadata: providerMetadata,
+ }, nil
+}
+
+func unmarshalObject(obj any, target any) error {
+ jsonBytes, err := json.Marshal(obj)
+ if err != nil {
+ return fmt.Errorf("failed to marshal object: %w", err)
+ }
+
+ if err := json.Unmarshal(jsonBytes, target); err != nil {
+ return fmt.Errorf("failed to unmarshal into target type: %w", err)
+ }
+
+ return nil
+}
@@ -0,0 +1,616 @@
+// Package object provides utilities for generating structured objects with automatic schema generation.
+// It simplifies working with typed structured outputs by handling schema reflection and unmarshaling.
+package object
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "reflect"
+
+ "charm.land/fantasy"
+ "charm.land/fantasy/schema"
+)
+
+// Generate generates a structured object that matches the given type T.
+// The schema is automatically generated from T using reflection.
+//
+// Example:
+//
+// type Recipe struct {
+// Name string `json:"name"`
+// Ingredients []string `json:"ingredients"`
+// }
+//
+// result, err := object.Generate[Recipe](ctx, model, fantasy.ObjectCall{
+// Prompt: fantasy.Prompt{fantasy.NewUserMessage("Generate a lasagna recipe")},
+// })
+func Generate[T any](
+ ctx context.Context,
+ model fantasy.LanguageModel,
+ opts fantasy.ObjectCall,
+) (*fantasy.ObjectResult[T], error) {
+ var zero T
+ s := schema.Generate(reflect.TypeOf(zero))
+ opts.Schema = s
+
+ resp, err := model.GenerateObject(ctx, opts)
+ if err != nil {
+ return nil, err
+ }
+
+ var result T
+ if err := unmarshal(resp.Object, &result); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal to %T: %w", result, err)
+ }
+
+ return &fantasy.ObjectResult[T]{
+ Object: result,
+ RawText: resp.RawText,
+ Usage: resp.Usage,
+ FinishReason: resp.FinishReason,
+ Warnings: resp.Warnings,
+ ProviderMetadata: resp.ProviderMetadata,
+ }, nil
+}
+
+// Stream streams a structured object that matches the given type T.
+// Returns a StreamObjectResult[T] with progressive updates and deduplication.
+//
+// Example:
+//
+// stream, err := object.Stream[Recipe](ctx, model, fantasy.ObjectCall{
+// Prompt: fantasy.Prompt{fantasy.NewUserMessage("Generate a lasagna recipe")},
+// })
+//
+// for partial := range stream.PartialObjectStream() {
+// fmt.Printf("Progress: %s\n", partial.Name)
+// }
+//
+// result, err := stream.Object() // Wait for final result
+func Stream[T any](
+ ctx context.Context,
+ model fantasy.LanguageModel,
+ opts fantasy.ObjectCall,
+) (*fantasy.StreamObjectResult[T], error) {
+ var zero T
+ s := schema.Generate(reflect.TypeOf(zero))
+ opts.Schema = s
+
+ stream, err := model.StreamObject(ctx, opts)
+ if err != nil {
+ return nil, err
+ }
+
+ return fantasy.NewStreamObjectResult[T](ctx, stream), nil
+}
+
+// GenerateWithTool is a helper for providers without native JSON mode.
+// It converts the schema to a tool definition, forces the model to call it,
+// and extracts the tool's input as the structured output.
+func GenerateWithTool(
+ ctx context.Context,
+ model fantasy.LanguageModel,
+ call fantasy.ObjectCall,
+) (*fantasy.ObjectResponse, error) {
+ toolName := call.SchemaName
+ if toolName == "" {
+ toolName = "generate_object"
+ }
+
+ toolDescription := call.SchemaDescription
+ if toolDescription == "" {
+ toolDescription = "Generate a structured object matching the schema"
+ }
+
+ tool := fantasy.FunctionTool{
+ Name: toolName,
+ Description: toolDescription,
+ InputSchema: schema.ToMap(call.Schema),
+ }
+
+ toolChoice := fantasy.SpecificToolChoice(tool.Name)
+ resp, err := model.Generate(ctx, fantasy.Call{
+ Prompt: call.Prompt,
+ Tools: []fantasy.Tool{tool},
+ ToolChoice: &toolChoice,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ TopK: call.TopK,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("tool-based generation failed: %w", err)
+ }
+
+ toolCalls := resp.Content.ToolCalls()
+ if len(toolCalls) == 0 {
+ return nil, &fantasy.NoObjectGeneratedError{
+ RawText: resp.Content.Text(),
+ ParseError: fmt.Errorf("no tool call generated"),
+ Usage: resp.Usage,
+ FinishReason: resp.FinishReason,
+ }
+ }
+
+ toolCall := toolCalls[0]
+
+ var obj any
+ if call.RepairText != nil {
+ obj, err = schema.ParseAndValidateWithRepair(ctx, toolCall.Input, call.Schema, call.RepairText)
+ } else {
+ obj, err = schema.ParseAndValidate(toolCall.Input, call.Schema)
+ }
+
+ if err != nil {
+ if nogErr, ok := err.(*fantasy.NoObjectGeneratedError); ok {
+ nogErr.Usage = resp.Usage
+ nogErr.FinishReason = resp.FinishReason
+ }
+ return nil, err
+ }
+
+ return &fantasy.ObjectResponse{
+ Object: obj,
+ RawText: toolCall.Input,
+ Usage: resp.Usage,
+ FinishReason: resp.FinishReason,
+ Warnings: resp.Warnings,
+ ProviderMetadata: resp.ProviderMetadata,
+ }, nil
+}
+
+// GenerateWithText is a helper for providers without tool or JSON mode support.
+// It adds the schema to the system prompt and parses the text response as JSON.
+// This is a fallback for older models or simple providers.
+func GenerateWithText(
+ ctx context.Context,
+ model fantasy.LanguageModel,
+ call fantasy.ObjectCall,
+) (*fantasy.ObjectResponse, error) {
+ jsonSchemaBytes, err := json.Marshal(call.Schema)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal schema: %w", err)
+ }
+
+ schemaInstruction := fmt.Sprintf(
+ "You must respond with valid JSON that matches this schema: %s\n"+
+ "Respond ONLY with the JSON object, no additional text or explanation.",
+ string(jsonSchemaBytes),
+ )
+
+ enhancedPrompt := make(fantasy.Prompt, 0, len(call.Prompt)+1)
+
+ hasSystem := false
+ for _, msg := range call.Prompt {
+ if msg.Role == fantasy.MessageRoleSystem {
+ hasSystem = true
+ existingText := ""
+ if len(msg.Content) > 0 {
+ if textPart, ok := msg.Content[0].(fantasy.TextPart); ok {
+ existingText = textPart.Text
+ }
+ }
+ enhancedPrompt = append(enhancedPrompt, fantasy.NewSystemMessage(existingText+"\n\n"+schemaInstruction))
+ } else {
+ enhancedPrompt = append(enhancedPrompt, msg)
+ }
+ }
+
+ if !hasSystem {
+ enhancedPrompt = append(fantasy.Prompt{fantasy.NewSystemMessage(schemaInstruction)}, call.Prompt...)
+ }
+
+ resp, err := model.Generate(ctx, fantasy.Call{
+ Prompt: enhancedPrompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ TopK: call.TopK,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("text-based generation failed: %w", err)
+ }
+
+ textContent := resp.Content.Text()
+ if textContent == "" {
+ return nil, &fantasy.NoObjectGeneratedError{
+ RawText: "",
+ ParseError: fmt.Errorf("no text content in response"),
+ Usage: resp.Usage,
+ FinishReason: resp.FinishReason,
+ }
+ }
+
+ var obj any
+ if call.RepairText != nil {
+ obj, err = schema.ParseAndValidateWithRepair(ctx, textContent, call.Schema, call.RepairText)
+ } else {
+ obj, err = schema.ParseAndValidate(textContent, call.Schema)
+ }
+
+ if err != nil {
+ if nogErr, ok := err.(*schema.ParseError); ok {
+ return nil, &fantasy.NoObjectGeneratedError{
+ RawText: nogErr.RawText,
+ ParseError: nogErr.ParseError,
+ ValidationError: nogErr.ValidationError,
+ Usage: resp.Usage,
+ FinishReason: resp.FinishReason,
+ }
+ }
+ return nil, err
+ }
+
+ return &fantasy.ObjectResponse{
+ Object: obj,
+ RawText: textContent,
+ Usage: resp.Usage,
+ FinishReason: resp.FinishReason,
+ Warnings: resp.Warnings,
+ ProviderMetadata: resp.ProviderMetadata,
+ }, nil
+}
+
+// StreamWithTool is a helper for providers without native JSON streaming.
+// It uses streaming tool calls to extract and parse the structured output progressively.
+func StreamWithTool(
+ ctx context.Context,
+ model fantasy.LanguageModel,
+ call fantasy.ObjectCall,
+) (fantasy.ObjectStreamResponse, error) {
+ // Create a tool from the schema
+ toolName := call.SchemaName
+ if toolName == "" {
+ toolName = "generate_object"
+ }
+
+ toolDescription := call.SchemaDescription
+ if toolDescription == "" {
+ toolDescription = "Generate a structured object matching the schema"
+ }
+
+ tool := fantasy.FunctionTool{
+ Name: toolName,
+ Description: toolDescription,
+ InputSchema: schema.ToMap(call.Schema),
+ }
+
+ // Make a streaming Generate call with forced tool choice
+ toolChoice := fantasy.SpecificToolChoice(tool.Name)
+ stream, err := model.Stream(ctx, fantasy.Call{
+ Prompt: call.Prompt,
+ Tools: []fantasy.Tool{tool},
+ ToolChoice: &toolChoice,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ TopK: call.TopK,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("tool-based streaming failed: %w", err)
+ }
+
+ // Convert the text stream to object stream parts
+ return func(yield func(fantasy.ObjectStreamPart) bool) {
+ var accumulated string
+ var lastParsedObject any
+ var usage fantasy.Usage
+ var finishReason fantasy.FinishReason
+ var warnings []fantasy.CallWarning
+ var providerMetadata fantasy.ProviderMetadata
+ var streamErr error
+
+ for part := range stream {
+ switch part.Type {
+ case fantasy.StreamPartTypeTextDelta:
+ accumulated += part.Delta
+
+ obj, state, parseErr := schema.ParsePartialJSON(accumulated)
+
+ if state == schema.ParseStateSuccessful || state == schema.ParseStateRepaired {
+ if err := schema.ValidateAgainstSchema(obj, call.Schema); err == nil {
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+ }
+
+ if state == schema.ParseStateFailed && call.RepairText != nil {
+ repairedText, repairErr := call.RepairText(ctx, accumulated, parseErr)
+ if repairErr == nil {
+ obj2, state2, _ := schema.ParsePartialJSON(repairedText)
+ if (state2 == schema.ParseStateSuccessful || state2 == schema.ParseStateRepaired) &&
+ schema.ValidateAgainstSchema(obj2, call.Schema) == nil {
+ if !reflect.DeepEqual(obj2, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj2,
+ }) {
+ return
+ }
+ lastParsedObject = obj2
+ }
+ }
+ }
+ }
+
+ case fantasy.StreamPartTypeToolInputDelta:
+ accumulated += part.Delta
+
+ obj, state, parseErr := schema.ParsePartialJSON(accumulated)
+ if state == schema.ParseStateSuccessful || state == schema.ParseStateRepaired {
+ if err := schema.ValidateAgainstSchema(obj, call.Schema); err == nil {
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+ }
+
+ if state == schema.ParseStateFailed && call.RepairText != nil {
+ repairedText, repairErr := call.RepairText(ctx, accumulated, parseErr)
+ if repairErr == nil {
+ obj2, state2, _ := schema.ParsePartialJSON(repairedText)
+ if (state2 == schema.ParseStateSuccessful || state2 == schema.ParseStateRepaired) &&
+ schema.ValidateAgainstSchema(obj2, call.Schema) == nil {
+ if !reflect.DeepEqual(obj2, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj2,
+ }) {
+ return
+ }
+ lastParsedObject = obj2
+ }
+ }
+ }
+ }
+
+ case fantasy.StreamPartTypeToolCall:
+ toolInput := part.ToolCallInput
+
+ var obj any
+ var err error
+ if call.RepairText != nil {
+ obj, err = schema.ParseAndValidateWithRepair(ctx, toolInput, call.Schema, call.RepairText)
+ } else {
+ obj, err = schema.ParseAndValidate(toolInput, call.Schema)
+ }
+
+ if err == nil {
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+
+ case fantasy.StreamPartTypeError:
+ streamErr = part.Error
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: part.Error,
+ }) {
+ return
+ }
+
+ case fantasy.StreamPartTypeFinish:
+ usage = part.Usage
+ finishReason = part.FinishReason
+
+ case fantasy.StreamPartTypeWarnings:
+ warnings = part.Warnings
+ }
+
+ if len(part.ProviderMetadata) > 0 {
+ providerMetadata = part.ProviderMetadata
+ }
+ }
+
+ if streamErr == nil && lastParsedObject != nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeFinish,
+ Usage: usage,
+ FinishReason: finishReason,
+ Warnings: warnings,
+ ProviderMetadata: providerMetadata,
+ })
+ } else if streamErr == nil && lastParsedObject == nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: &fantasy.NoObjectGeneratedError{
+ RawText: accumulated,
+ ParseError: fmt.Errorf("no valid object generated in stream"),
+ Usage: usage,
+ FinishReason: finishReason,
+ },
+ })
+ }
+ }, nil
+}
+
+// StreamWithText is a helper for providers without tool or JSON streaming support.
+// It adds the schema to the system prompt and parses the streamed text as JSON progressively.
+func StreamWithText(
+ ctx context.Context,
+ model fantasy.LanguageModel,
+ call fantasy.ObjectCall,
+) (fantasy.ObjectStreamResponse, error) {
+ jsonSchemaMap := schema.ToMap(call.Schema)
+ jsonSchemaBytes, err := json.Marshal(jsonSchemaMap)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal schema: %w", err)
+ }
+
+ schemaInstruction := fmt.Sprintf(
+ "You must respond with valid JSON that matches this schema: %s\n"+
+ "Respond ONLY with the JSON object, no additional text or explanation.",
+ string(jsonSchemaBytes),
+ )
+
+ enhancedPrompt := make(fantasy.Prompt, 0, len(call.Prompt)+1)
+
+ hasSystem := false
+ for _, msg := range call.Prompt {
+ if msg.Role == fantasy.MessageRoleSystem {
+ hasSystem = true
+ existingText := ""
+ if len(msg.Content) > 0 {
+ if textPart, ok := msg.Content[0].(fantasy.TextPart); ok {
+ existingText = textPart.Text
+ }
+ }
+ enhancedPrompt = append(enhancedPrompt, fantasy.NewSystemMessage(existingText+"\n\n"+schemaInstruction))
+ } else {
+ enhancedPrompt = append(enhancedPrompt, msg)
+ }
+ }
+
+ if !hasSystem {
+ enhancedPrompt = append(fantasy.Prompt{fantasy.NewSystemMessage(schemaInstruction)}, call.Prompt...)
+ }
+
+ stream, err := model.Stream(ctx, fantasy.Call{
+ Prompt: enhancedPrompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ TopK: call.TopK,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("text-based streaming failed: %w", err)
+ }
+
+ return func(yield func(fantasy.ObjectStreamPart) bool) {
+ var accumulated string
+ var lastParsedObject any
+ var usage fantasy.Usage
+ var finishReason fantasy.FinishReason
+ var warnings []fantasy.CallWarning
+ var providerMetadata fantasy.ProviderMetadata
+ var streamErr error
+
+ for part := range stream {
+ switch part.Type {
+ case fantasy.StreamPartTypeTextDelta:
+ accumulated += part.Delta
+
+ obj, state, parseErr := schema.ParsePartialJSON(accumulated)
+
+ if state == schema.ParseStateSuccessful || state == schema.ParseStateRepaired {
+ if err := schema.ValidateAgainstSchema(obj, call.Schema); err == nil {
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+ }
+
+ if state == schema.ParseStateFailed && call.RepairText != nil {
+ repairedText, repairErr := call.RepairText(ctx, accumulated, parseErr)
+ if repairErr == nil {
+ obj2, state2, _ := schema.ParsePartialJSON(repairedText)
+ if (state2 == schema.ParseStateSuccessful || state2 == schema.ParseStateRepaired) &&
+ schema.ValidateAgainstSchema(obj2, call.Schema) == nil {
+ if !reflect.DeepEqual(obj2, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj2,
+ }) {
+ return
+ }
+ lastParsedObject = obj2
+ }
+ }
+ }
+ }
+
+ case fantasy.StreamPartTypeError:
+ streamErr = part.Error
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: part.Error,
+ }) {
+ return
+ }
+
+ case fantasy.StreamPartTypeFinish:
+ usage = part.Usage
+ finishReason = part.FinishReason
+
+ case fantasy.StreamPartTypeWarnings:
+ warnings = part.Warnings
+ }
+
+ if len(part.ProviderMetadata) > 0 {
+ providerMetadata = part.ProviderMetadata
+ }
+ }
+
+ if streamErr == nil && lastParsedObject != nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeFinish,
+ Usage: usage,
+ FinishReason: finishReason,
+ Warnings: warnings,
+ ProviderMetadata: providerMetadata,
+ })
+ } else if streamErr == nil && lastParsedObject == nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: &fantasy.NoObjectGeneratedError{
+ RawText: accumulated,
+ ParseError: fmt.Errorf("no valid object generated in stream"),
+ Usage: usage,
+ FinishReason: finishReason,
+ },
+ })
+ }
+ }, nil
+}
+
+func unmarshal(obj any, target any) error {
+ jsonBytes, err := json.Marshal(obj)
+ if err != nil {
+ return fmt.Errorf("failed to marshal object: %w", err)
+ }
+
+ if err := json.Unmarshal(jsonBytes, target); err != nil {
+ return fmt.Errorf("failed to unmarshal into target type: %w", err)
+ }
+
+ return nil
+}
@@ -13,6 +13,7 @@ import (
"strings"
"charm.land/fantasy"
+ "charm.land/fantasy/object"
"github.com/charmbracelet/anthropic-sdk-go"
"github.com/charmbracelet/anthropic-sdk-go/bedrock"
"github.com/charmbracelet/anthropic-sdk-go/option"
@@ -40,6 +41,8 @@ type options struct {
skipAuth bool
useBedrock bool
+
+ objectMode fantasy.ObjectMode
}
type provider struct {
@@ -52,7 +55,8 @@ type Option = func(*options)
// New creates a new Anthropic provider with the given options.
func New(opts ...Option) (fantasy.Provider, error) {
providerOptions := options{
- headers: map[string]string{},
+ headers: map[string]string{},
+ objectMode: fantasy.ObjectModeAuto,
}
for _, o := range opts {
o(&providerOptions)
@@ -120,6 +124,17 @@ func WithHTTPClient(client option.HTTPClient) Option {
}
}
+// WithObjectMode sets the object generation mode.
+func WithObjectMode(om fantasy.ObjectMode) Option {
+ return func(o *options) {
+ // not supported
+ if om == fantasy.ObjectModeJSON {
+ om = fantasy.ObjectModeAuto
+ }
+ o.objectMode = om
+ }
+}
+
func (a *provider) LanguageModel(ctx context.Context, modelID string) (fantasy.LanguageModel, error) {
clientOptions := make([]option.RequestOption, 0, 5+len(a.options.headers))
clientOptions = append(clientOptions, option.WithMaxRetries(0))
@@ -952,3 +967,23 @@ func (a languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.S
}
}, nil
}
+
+// GenerateObject implements fantasy.LanguageModel.
+func (a languageModel) GenerateObject(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ switch a.options.objectMode {
+ case fantasy.ObjectModeText:
+ return object.GenerateWithText(ctx, a, call)
+ default:
+ return object.GenerateWithTool(ctx, a, call)
+ }
+}
+
+// StreamObject implements fantasy.LanguageModel.
+func (a languageModel) StreamObject(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ switch a.options.objectMode {
+ case fantasy.ObjectModeText:
+ return object.StreamWithText(ctx, a, call)
+ default:
+ return object.StreamWithTool(ctx, a, call)
+ }
+}
@@ -8,10 +8,13 @@ import (
"fmt"
"maps"
"net/http"
+ "reflect"
"strings"
"charm.land/fantasy"
+ "charm.land/fantasy/object"
"charm.land/fantasy/providers/anthropic"
+ "charm.land/fantasy/schema"
"cloud.google.com/go/auth"
"github.com/charmbracelet/x/exp/slice"
"github.com/google/uuid"
@@ -39,6 +42,7 @@ type options struct {
location string
skipAuth bool
toolCallIDFunc ToolCallIDFunc
+ objectMode fantasy.ObjectMode
}
// Option defines a function that configures Google provider options.
@@ -128,6 +132,13 @@ func WithToolCallIDFunc(f ToolCallIDFunc) Option {
}
}
+// WithObjectMode sets the object generation mode for the Google provider.
+func WithObjectMode(om fantasy.ObjectMode) Option {
+ return func(o *options) {
+ o.objectMode = om
+ }
+}
+
func (*provider) Name() string {
return Name
}
@@ -137,6 +148,7 @@ type languageModel struct {
modelID string
client *genai.Client
providerOptions options
+ objectMode fantasy.ObjectMode
}
// LanguageModel implements fantasy.Provider.
@@ -182,11 +194,18 @@ func (a *provider) LanguageModel(ctx context.Context, modelID string) (fantasy.L
if err != nil {
return nil, err
}
+
+ objectMode := a.options.objectMode
+ if objectMode == "" {
+ objectMode = fantasy.ObjectModeAuto
+ }
+
return &languageModel{
modelID: modelID,
provider: a.options.name,
providerOptions: a.options,
client: client,
+ objectMode: objectMode,
}, nil
}
@@ -739,7 +758,8 @@ func (g *languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.
}
}
- if resp.UsageMetadata != nil {
+ // we need to make sure that there is actual tokendata
+ if resp.UsageMetadata != nil && resp.UsageMetadata.TotalTokenCount != 0 {
currentUsage := mapUsage(resp.UsageMetadata)
// if first usage chunk
if usage == nil {
@@ -789,6 +809,268 @@ func (g *languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.
}, nil
}
+// GenerateObject implements fantasy.LanguageModel.
+func (g *languageModel) GenerateObject(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ switch g.objectMode {
+ case fantasy.ObjectModeText:
+ return object.GenerateWithText(ctx, g, call)
+ case fantasy.ObjectModeTool:
+ return object.GenerateWithTool(ctx, g, call)
+ default:
+ return g.generateObjectWithJSONMode(ctx, call)
+ }
+}
+
+// StreamObject implements fantasy.LanguageModel.
+func (g *languageModel) StreamObject(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ switch g.objectMode {
+ case fantasy.ObjectModeTool:
+ return object.StreamWithTool(ctx, g, call)
+ case fantasy.ObjectModeText:
+ return object.StreamWithText(ctx, g, call)
+ default:
+ return g.streamObjectWithJSONMode(ctx, call)
+ }
+}
+
+func (g *languageModel) generateObjectWithJSONMode(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ // Convert our Schema to Google's JSON Schema format
+ jsonSchemaMap := schema.ToMap(call.Schema)
+
+ // Build request using prepareParams
+ fantasyCall := fantasy.Call{
+ Prompt: call.Prompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ TopK: call.TopK,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ }
+
+ config, contents, warnings, err := g.prepareParams(fantasyCall)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set ResponseMIMEType and ResponseJsonSchema for structured output
+ config.ResponseMIMEType = "application/json"
+ config.ResponseJsonSchema = jsonSchemaMap
+
+ lastMessage, history, ok := slice.Pop(contents)
+ if !ok {
+ return nil, errors.New("no messages to send")
+ }
+
+ chat, err := g.client.Chats.Create(ctx, g.modelID, config, history)
+ if err != nil {
+ return nil, err
+ }
+
+ response, err := chat.SendMessage(ctx, depointerSlice(lastMessage.Parts)...)
+ if err != nil {
+ return nil, toProviderErr(err)
+ }
+
+ mappedResponse, err := g.mapResponse(response, warnings)
+ if err != nil {
+ return nil, err
+ }
+
+ jsonText := mappedResponse.Content.Text()
+ if jsonText == "" {
+ return nil, &fantasy.NoObjectGeneratedError{
+ RawText: "",
+ ParseError: fmt.Errorf("no text content in response"),
+ Usage: mappedResponse.Usage,
+ FinishReason: mappedResponse.FinishReason,
+ }
+ }
+
+ // Parse and validate
+ var obj any
+ if call.RepairText != nil {
+ obj, err = schema.ParseAndValidateWithRepair(ctx, jsonText, call.Schema, call.RepairText)
+ } else {
+ obj, err = schema.ParseAndValidate(jsonText, call.Schema)
+ }
+
+ if err != nil {
+ // Add usage info to error
+ if nogErr, ok := err.(*fantasy.NoObjectGeneratedError); ok {
+ nogErr.Usage = mappedResponse.Usage
+ nogErr.FinishReason = mappedResponse.FinishReason
+ }
+ return nil, err
+ }
+
+ return &fantasy.ObjectResponse{
+ Object: obj,
+ RawText: jsonText,
+ Usage: mappedResponse.Usage,
+ FinishReason: mappedResponse.FinishReason,
+ Warnings: warnings,
+ ProviderMetadata: mappedResponse.ProviderMetadata,
+ }, nil
+}
+
+func (g *languageModel) streamObjectWithJSONMode(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ // Convert our Schema to Google's JSON Schema format
+ jsonSchemaMap := schema.ToMap(call.Schema)
+
+ // Build request using prepareParams
+ fantasyCall := fantasy.Call{
+ Prompt: call.Prompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ TopK: call.TopK,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ }
+
+ config, contents, warnings, err := g.prepareParams(fantasyCall)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set ResponseMIMEType and ResponseJsonSchema for structured output
+ config.ResponseMIMEType = "application/json"
+ config.ResponseJsonSchema = jsonSchemaMap
+
+ lastMessage, history, ok := slice.Pop(contents)
+ if !ok {
+ return nil, errors.New("no messages to send")
+ }
+
+ chat, err := g.client.Chats.Create(ctx, g.modelID, config, history)
+ if err != nil {
+ return nil, err
+ }
+
+ return func(yield func(fantasy.ObjectStreamPart) bool) {
+ if len(warnings) > 0 {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Warnings: warnings,
+ }) {
+ return
+ }
+ }
+
+ var accumulated string
+ var lastParsedObject any
+ var usage *fantasy.Usage
+ var lastFinishReason fantasy.FinishReason
+ var streamErr error
+
+ for resp, err := range chat.SendMessageStream(ctx, depointerSlice(lastMessage.Parts)...) {
+ if err != nil {
+ streamErr = toProviderErr(err)
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: streamErr,
+ })
+ return
+ }
+
+ if len(resp.Candidates) > 0 && resp.Candidates[0].Content != nil {
+ for _, part := range resp.Candidates[0].Content.Parts {
+ if part.Text != "" && !part.Thought {
+ accumulated += part.Text
+
+ // Try to parse the accumulated text
+ obj, state, parseErr := schema.ParsePartialJSON(accumulated)
+
+ // If we successfully parsed, validate and emit
+ if state == schema.ParseStateSuccessful || state == schema.ParseStateRepaired {
+ if err := schema.ValidateAgainstSchema(obj, call.Schema); err == nil {
+ // Only emit if object is different from last
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+ }
+
+ // If parsing failed and we have a repair function, try it
+ if state == schema.ParseStateFailed && call.RepairText != nil {
+ repairedText, repairErr := call.RepairText(ctx, accumulated, parseErr)
+ if repairErr == nil {
+ obj2, state2, _ := schema.ParsePartialJSON(repairedText)
+ if (state2 == schema.ParseStateSuccessful || state2 == schema.ParseStateRepaired) &&
+ schema.ValidateAgainstSchema(obj2, call.Schema) == nil {
+ if !reflect.DeepEqual(obj2, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj2,
+ }) {
+ return
+ }
+ lastParsedObject = obj2
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // we need to make sure that there is actual tokendata
+ if resp.UsageMetadata != nil && resp.UsageMetadata.TotalTokenCount != 0 {
+ currentUsage := mapUsage(resp.UsageMetadata)
+ if usage == nil {
+ usage = ¤tUsage
+ } else {
+ usage.OutputTokens += currentUsage.OutputTokens
+ usage.ReasoningTokens += currentUsage.ReasoningTokens
+ usage.CacheReadTokens += currentUsage.CacheReadTokens
+ }
+ }
+
+ if len(resp.Candidates) > 0 && resp.Candidates[0].FinishReason != "" {
+ lastFinishReason = mapFinishReason(resp.Candidates[0].FinishReason)
+ }
+ }
+
+ // Final validation and emit
+ if streamErr == nil && lastParsedObject != nil {
+ finishReason := lastFinishReason
+ if finishReason == "" {
+ finishReason = fantasy.FinishReasonStop
+ }
+
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeFinish,
+ Usage: *usage,
+ FinishReason: finishReason,
+ })
+ } else if streamErr == nil && lastParsedObject == nil {
+ // No object was generated
+ finalUsage := fantasy.Usage{}
+ if usage != nil {
+ finalUsage = *usage
+ }
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: &fantasy.NoObjectGeneratedError{
+ RawText: accumulated,
+ ParseError: fmt.Errorf("no valid object generated in stream"),
+ Usage: finalUsage,
+ FinishReason: lastFinishReason,
+ },
+ })
+ }
+ }, nil
+}
+
func toGoogleTools(tools []fantasy.Tool, toolChoice *fantasy.ToolChoice) (googleTools []*genai.FunctionDeclaration, googleToolChoice *genai.ToolConfig, warnings []fantasy.CallWarning) {
for _, tool := range tools {
if tool.GetType() == fantasy.ToolTypeFunction {
@@ -4,10 +4,14 @@ import (
"context"
"encoding/json"
"errors"
+ "fmt"
"io"
+ "reflect"
"strings"
"charm.land/fantasy"
+ "charm.land/fantasy/object"
+ "charm.land/fantasy/schema"
xjson "github.com/charmbracelet/x/json"
"github.com/google/uuid"
"github.com/openai/openai-go/v2"
@@ -19,6 +23,7 @@ type languageModel struct {
provider string
modelID string
client openai.Client
+ objectMode fantasy.ObjectMode
prepareCallFunc LanguageModelPrepareCallFunc
mapFinishReasonFunc LanguageModelMapFinishReasonFunc
extraContentFunc LanguageModelExtraContentFunc
@@ -81,11 +86,23 @@ func WithLanguageModelToPromptFunc(fn LanguageModelToPromptFunc) LanguageModelOp
}
}
+// WithLanguageModelObjectMode sets the object generation mode.
+func WithLanguageModelObjectMode(om fantasy.ObjectMode) LanguageModelOption {
+ return func(l *languageModel) {
+ // not supported
+ if om == fantasy.ObjectModeJSON {
+ om = fantasy.ObjectModeAuto
+ }
+ l.objectMode = om
+ }
+}
+
func newLanguageModel(modelID string, provider string, client openai.Client, opts ...LanguageModelOption) languageModel {
model := languageModel{
modelID: modelID,
provider: provider,
client: client,
+ objectMode: fantasy.ObjectModeAuto,
prepareCallFunc: DefaultPrepareCallFunc,
mapFinishReasonFunc: DefaultMapFinishReasonFunc,
usageFunc: DefaultUsageFunc,
@@ -252,13 +269,12 @@ func (o languageModel) Generate(ctx context.Context, call fantasy.Call) (*fantas
for _, tc := range choice.Message.ToolCalls {
toolCallID := tc.ID
content = append(content, fantasy.ToolCallContent{
- ProviderExecuted: false, // TODO: update when handling other tools
+ ProviderExecuted: false,
ToolCallID: toolCallID,
ToolName: tc.Function.Name,
Input: tc.Function.Arguments,
})
}
- // Handle annotations/citations
for _, annotation := range choice.Message.Annotations {
if annotation.Type == "url_citation" {
content = append(content, fantasy.SourceContent{
@@ -302,7 +318,6 @@ func (o languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.S
isActiveText := false
toolCalls := make(map[int64]streamToolCall)
- // Build provider metadata for streaming
providerMetadata := fantasy.ProviderMetadata{
Name: &ProviderMetadata{},
}
@@ -395,7 +410,6 @@ func (o languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.S
toolCalls[toolCallDelta.Index] = existingToolCall
}
} else {
- // Does not exist
var err error
if toolCallDelta.Type != "function" {
err = &fantasy.Error{Title: "invalid provider response", Message: "expected 'function' type."}
@@ -470,7 +484,6 @@ func (o languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.S
}
}
- // Check for annotations in the delta's raw JSON
for _, choice := range chunk.Choices {
if annotations := parseAnnotationsFromDelta(choice.Delta); len(annotations) > 0 {
for _, annotation := range annotations {
@@ -491,7 +504,6 @@ func (o languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.S
}
err := stream.Err()
if err == nil || errors.Is(err, io.EOF) {
- // finished
if isActiveText {
isActiveText = false
if !yield(fantasy.StreamPart{
@@ -504,10 +516,8 @@ func (o languageModel) Stream(ctx context.Context, call fantasy.Call) (fantasy.S
if len(acc.Choices) > 0 {
choice := acc.Choices[0]
- // Add logprobs if available
providerMetadata = o.streamProviderMetadataFunc(choice, providerMetadata)
- // Handle annotations/citations from accumulated response
for _, annotation := range choice.Message.Annotations {
if annotation.Type == "url_citation" {
if !yield(fantasy.StreamPart{
@@ -585,7 +595,6 @@ func toOpenAiTools(tools []fantasy.Tool, toolChoice *fantasy.ToolChoice) (openAi
continue
}
- // TODO: handle provider tool calls
warnings = append(warnings, fantasy.CallWarning{
Type: fantasy.CallWarningTypeUnsupportedTool,
Tool: tool,
@@ -650,3 +659,277 @@ func parseAnnotationsFromDelta(delta openai.ChatCompletionChunkChoiceDelta) []op
return annotations
}
+
+// GenerateObject implements fantasy.LanguageModel.
+func (o languageModel) GenerateObject(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ switch o.objectMode {
+ case fantasy.ObjectModeText:
+ return object.GenerateWithText(ctx, o, call)
+ case fantasy.ObjectModeTool:
+ return object.GenerateWithTool(ctx, o, call)
+ default:
+ return o.generateObjectWithJSONMode(ctx, call)
+ }
+}
+
+// StreamObject implements fantasy.LanguageModel.
+func (o languageModel) StreamObject(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ switch o.objectMode {
+ case fantasy.ObjectModeTool:
+ return object.StreamWithTool(ctx, o, call)
+ case fantasy.ObjectModeText:
+ return object.StreamWithText(ctx, o, call)
+ default:
+ return o.streamObjectWithJSONMode(ctx, call)
+ }
+}
+
+func (o languageModel) generateObjectWithJSONMode(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ jsonSchemaMap := schema.ToMap(call.Schema)
+
+ addAdditionalPropertiesFalse(jsonSchemaMap)
+
+ schemaName := call.SchemaName
+ if schemaName == "" {
+ schemaName = "response"
+ }
+
+ fantasyCall := fantasy.Call{
+ Prompt: call.Prompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ }
+
+ params, warnings, err := o.prepareParams(fantasyCall)
+ if err != nil {
+ return nil, err
+ }
+
+ params.ResponseFormat = openai.ChatCompletionNewParamsResponseFormatUnion{
+ OfJSONSchema: &shared.ResponseFormatJSONSchemaParam{
+ JSONSchema: shared.ResponseFormatJSONSchemaJSONSchemaParam{
+ Name: schemaName,
+ Description: param.NewOpt(call.SchemaDescription),
+ Schema: jsonSchemaMap,
+ Strict: param.NewOpt(true),
+ },
+ },
+ }
+
+ response, err := o.client.Chat.Completions.New(ctx, *params)
+ if err != nil {
+ return nil, toProviderErr(err)
+ }
+
+ if len(response.Choices) == 0 {
+ usage, _ := o.usageFunc(*response)
+ return nil, &fantasy.NoObjectGeneratedError{
+ RawText: "",
+ ParseError: fmt.Errorf("no choices in response"),
+ Usage: usage,
+ FinishReason: fantasy.FinishReasonUnknown,
+ }
+ }
+
+ choice := response.Choices[0]
+ jsonText := choice.Message.Content
+
+ var obj any
+ if call.RepairText != nil {
+ obj, err = schema.ParseAndValidateWithRepair(ctx, jsonText, call.Schema, call.RepairText)
+ } else {
+ obj, err = schema.ParseAndValidate(jsonText, call.Schema)
+ }
+
+ usage, _ := o.usageFunc(*response)
+ finishReason := o.mapFinishReasonFunc(choice.FinishReason)
+
+ if err != nil {
+ if nogErr, ok := err.(*fantasy.NoObjectGeneratedError); ok {
+ nogErr.Usage = usage
+ nogErr.FinishReason = finishReason
+ }
+ return nil, err
+ }
+
+ return &fantasy.ObjectResponse{
+ Object: obj,
+ RawText: jsonText,
+ Usage: usage,
+ FinishReason: finishReason,
+ Warnings: warnings,
+ }, nil
+}
+
+func (o languageModel) streamObjectWithJSONMode(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ jsonSchemaMap := schema.ToMap(call.Schema)
+
+ addAdditionalPropertiesFalse(jsonSchemaMap)
+
+ schemaName := call.SchemaName
+ if schemaName == "" {
+ schemaName = "response"
+ }
+
+ fantasyCall := fantasy.Call{
+ Prompt: call.Prompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ }
+
+ params, warnings, err := o.prepareParams(fantasyCall)
+ if err != nil {
+ return nil, err
+ }
+
+ params.ResponseFormat = openai.ChatCompletionNewParamsResponseFormatUnion{
+ OfJSONSchema: &shared.ResponseFormatJSONSchemaParam{
+ JSONSchema: shared.ResponseFormatJSONSchemaJSONSchemaParam{
+ Name: schemaName,
+ Description: param.NewOpt(call.SchemaDescription),
+ Schema: jsonSchemaMap,
+ Strict: param.NewOpt(true),
+ },
+ },
+ }
+
+ params.StreamOptions = openai.ChatCompletionStreamOptionsParam{
+ IncludeUsage: openai.Bool(true),
+ }
+
+ stream := o.client.Chat.Completions.NewStreaming(ctx, *params)
+
+ return func(yield func(fantasy.ObjectStreamPart) bool) {
+ if len(warnings) > 0 {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Warnings: warnings,
+ }) {
+ return
+ }
+ }
+
+ var accumulated string
+ var lastParsedObject any
+ var usage fantasy.Usage
+ var finishReason fantasy.FinishReason
+ var providerMetadata fantasy.ProviderMetadata
+ var streamErr error
+
+ for stream.Next() {
+ chunk := stream.Current()
+
+ // Update usage
+ usage, providerMetadata = o.streamUsageFunc(chunk, make(map[string]any), providerMetadata)
+
+ if len(chunk.Choices) == 0 {
+ continue
+ }
+
+ choice := chunk.Choices[0]
+ if choice.FinishReason != "" {
+ finishReason = o.mapFinishReasonFunc(choice.FinishReason)
+ }
+
+ if choice.Delta.Content != "" {
+ accumulated += choice.Delta.Content
+
+ obj, state, parseErr := schema.ParsePartialJSON(accumulated)
+
+ if state == schema.ParseStateSuccessful || state == schema.ParseStateRepaired {
+ if err := schema.ValidateAgainstSchema(obj, call.Schema); err == nil {
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+ }
+
+ if state == schema.ParseStateFailed && call.RepairText != nil {
+ repairedText, repairErr := call.RepairText(ctx, accumulated, parseErr)
+ if repairErr == nil {
+ obj2, state2, _ := schema.ParsePartialJSON(repairedText)
+ if (state2 == schema.ParseStateSuccessful || state2 == schema.ParseStateRepaired) &&
+ schema.ValidateAgainstSchema(obj2, call.Schema) == nil {
+ if !reflect.DeepEqual(obj2, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj2,
+ }) {
+ return
+ }
+ lastParsedObject = obj2
+ }
+ }
+ }
+ }
+ }
+ }
+
+ err := stream.Err()
+ if err != nil && !errors.Is(err, io.EOF) {
+ streamErr = toProviderErr(err)
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: streamErr,
+ })
+ return
+ }
+
+ if lastParsedObject != nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeFinish,
+ Usage: usage,
+ FinishReason: finishReason,
+ ProviderMetadata: providerMetadata,
+ })
+ } else {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: &fantasy.NoObjectGeneratedError{
+ RawText: accumulated,
+ ParseError: fmt.Errorf("no valid object generated in stream"),
+ Usage: usage,
+ FinishReason: finishReason,
+ },
+ })
+ }
+ }, nil
+}
+
+// addAdditionalPropertiesFalse recursively adds "additionalProperties": false to all object schemas.
+// This is required by OpenAI's strict mode for structured outputs.
+func addAdditionalPropertiesFalse(schema map[string]any) {
+ if schema["type"] == "object" {
+ if _, hasAdditional := schema["additionalProperties"]; !hasAdditional {
+ schema["additionalProperties"] = false
+ }
+
+ // Recursively process nested properties
+ if properties, ok := schema["properties"].(map[string]any); ok {
+ for _, propValue := range properties {
+ if propSchema, ok := propValue.(map[string]any); ok {
+ addAdditionalPropertiesFalse(propSchema)
+ }
+ }
+ }
+ }
+
+ // Handle array items
+ if items, ok := schema["items"].(map[string]any); ok {
+ addAdditionalPropertiesFalse(items)
+ }
+}
@@ -32,6 +32,7 @@ type options struct {
headers map[string]string
client option.HTTPClient
sdkOptions []option.RequestOption
+ objectMode fantasy.ObjectMode
languageModelOptions []LanguageModelOption
}
@@ -131,6 +132,17 @@ func WithUseResponsesAPI() Option {
}
}
+// WithObjectMode sets the object generation mode.
+func WithObjectMode(om fantasy.ObjectMode) Option {
+ return func(o *options) {
+ // not supported
+ if om == fantasy.ObjectModeJSON {
+ om = fantasy.ObjectModeAuto
+ }
+ o.objectMode = om
+ }
+}
+
// LanguageModel implements fantasy.Provider.
func (o *provider) LanguageModel(_ context.Context, modelID string) (fantasy.LanguageModel, error) {
openaiClientOptions := make([]option.RequestOption, 0, 5+len(o.options.headers)+len(o.options.sdkOptions))
@@ -156,9 +168,16 @@ func (o *provider) LanguageModel(_ context.Context, modelID string) (fantasy.Lan
client := openai.NewClient(openaiClientOptions...)
if o.options.useResponsesAPI && IsResponsesModel(modelID) {
- return newResponsesLanguageModel(modelID, o.options.name, client), nil
+ // Not supported for responses API
+ objectMode := o.options.objectMode
+ if objectMode == fantasy.ObjectModeJSON {
+ objectMode = fantasy.ObjectModeAuto
+ }
+ return newResponsesLanguageModel(modelID, o.options.name, client, objectMode), nil
}
+ o.options.languageModelOptions = append(o.options.languageModelOptions, WithLanguageModelObjectMode(o.options.objectMode))
+
return newLanguageModel(
modelID,
o.options.name,
@@ -5,9 +5,12 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
+ "reflect"
"strings"
"charm.land/fantasy"
+ "charm.land/fantasy/object"
+ "charm.land/fantasy/schema"
"github.com/google/uuid"
"github.com/openai/openai-go/v2"
"github.com/openai/openai-go/v2/packages/param"
@@ -18,18 +21,20 @@ import (
const topLogprobsMax = 20
type responsesLanguageModel struct {
- provider string
- modelID string
- client openai.Client
+ provider string
+ modelID string
+ client openai.Client
+ objectMode fantasy.ObjectMode
}
// newResponsesLanguageModel implements a responses api model
// INFO: (kujtim) currently we do not support stored parameter we default it to false.
-func newResponsesLanguageModel(modelID string, provider string, client openai.Client) responsesLanguageModel {
+func newResponsesLanguageModel(modelID string, provider string, client openai.Client, objectMode fantasy.ObjectMode) responsesLanguageModel {
return responsesLanguageModel{
- modelID: modelID,
- provider: provider,
- client: client,
+ modelID: modelID,
+ provider: provider,
+ client: client,
+ objectMode: objectMode,
}
}
@@ -1032,3 +1037,293 @@ type ongoingToolCall struct {
type reasoningState struct {
metadata *ResponsesReasoningMetadata
}
+
+// GenerateObject implements fantasy.LanguageModel.
+func (o responsesLanguageModel) GenerateObject(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ switch o.objectMode {
+ case fantasy.ObjectModeText:
+ return object.GenerateWithText(ctx, o, call)
+ case fantasy.ObjectModeTool:
+ return object.GenerateWithTool(ctx, o, call)
+ default:
+ return o.generateObjectWithJSONMode(ctx, call)
+ }
+}
+
+// StreamObject implements fantasy.LanguageModel.
+func (o responsesLanguageModel) StreamObject(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ switch o.objectMode {
+ case fantasy.ObjectModeTool:
+ return object.StreamWithTool(ctx, o, call)
+ case fantasy.ObjectModeText:
+ return object.StreamWithText(ctx, o, call)
+ default:
+ return o.streamObjectWithJSONMode(ctx, call)
+ }
+}
+
+func (o responsesLanguageModel) generateObjectWithJSONMode(ctx context.Context, call fantasy.ObjectCall) (*fantasy.ObjectResponse, error) {
+ // Convert our Schema to OpenAI's JSON Schema format
+ jsonSchemaMap := schema.ToMap(call.Schema)
+
+ // Add additionalProperties: false recursively for strict mode (OpenAI requirement)
+ addAdditionalPropertiesFalse(jsonSchemaMap)
+
+ schemaName := call.SchemaName
+ if schemaName == "" {
+ schemaName = "response"
+ }
+
+ // Build request using prepareParams
+ fantasyCall := fantasy.Call{
+ Prompt: call.Prompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ }
+
+ params, warnings := o.prepareParams(fantasyCall)
+
+ // Add structured output via Text.Format field
+ params.Text = responses.ResponseTextConfigParam{
+ Format: responses.ResponseFormatTextConfigParamOfJSONSchema(schemaName, jsonSchemaMap),
+ }
+
+ // Make request
+ response, err := o.client.Responses.New(ctx, *params)
+ if err != nil {
+ return nil, toProviderErr(err)
+ }
+
+ if response.Error.Message != "" {
+ return nil, &fantasy.Error{
+ Title: "provider error",
+ Message: fmt.Sprintf("%s (code: %s)", response.Error.Message, response.Error.Code),
+ }
+ }
+
+ // Extract JSON text from response
+ var jsonText string
+ for _, outputItem := range response.Output {
+ if outputItem.Type == "message" {
+ for _, contentPart := range outputItem.Content {
+ if contentPart.Type == "output_text" {
+ jsonText = contentPart.Text
+ break
+ }
+ }
+ }
+ }
+
+ if jsonText == "" {
+ usage := fantasy.Usage{
+ InputTokens: response.Usage.InputTokens,
+ OutputTokens: response.Usage.OutputTokens,
+ TotalTokens: response.Usage.InputTokens + response.Usage.OutputTokens,
+ }
+ finishReason := mapResponsesFinishReason(response.IncompleteDetails.Reason, false)
+ return nil, &fantasy.NoObjectGeneratedError{
+ RawText: "",
+ ParseError: fmt.Errorf("no text content in response"),
+ Usage: usage,
+ FinishReason: finishReason,
+ }
+ }
+
+ // Parse and validate
+ var obj any
+ if call.RepairText != nil {
+ obj, err = schema.ParseAndValidateWithRepair(ctx, jsonText, call.Schema, call.RepairText)
+ } else {
+ obj, err = schema.ParseAndValidate(jsonText, call.Schema)
+ }
+
+ usage := fantasy.Usage{
+ InputTokens: response.Usage.InputTokens,
+ OutputTokens: response.Usage.OutputTokens,
+ TotalTokens: response.Usage.InputTokens + response.Usage.OutputTokens,
+ }
+ if response.Usage.OutputTokensDetails.ReasoningTokens != 0 {
+ usage.ReasoningTokens = response.Usage.OutputTokensDetails.ReasoningTokens
+ }
+ if response.Usage.InputTokensDetails.CachedTokens != 0 {
+ usage.CacheReadTokens = response.Usage.InputTokensDetails.CachedTokens
+ }
+
+ finishReason := mapResponsesFinishReason(response.IncompleteDetails.Reason, false)
+
+ if err != nil {
+ // Add usage info to error
+ if nogErr, ok := err.(*fantasy.NoObjectGeneratedError); ok {
+ nogErr.Usage = usage
+ nogErr.FinishReason = finishReason
+ }
+ return nil, err
+ }
+
+ return &fantasy.ObjectResponse{
+ Object: obj,
+ RawText: jsonText,
+ Usage: usage,
+ FinishReason: finishReason,
+ Warnings: warnings,
+ }, nil
+}
+
+func (o responsesLanguageModel) streamObjectWithJSONMode(ctx context.Context, call fantasy.ObjectCall) (fantasy.ObjectStreamResponse, error) {
+ // Convert our Schema to OpenAI's JSON Schema format
+ jsonSchemaMap := schema.ToMap(call.Schema)
+
+ // Add additionalProperties: false recursively for strict mode (OpenAI requirement)
+ addAdditionalPropertiesFalse(jsonSchemaMap)
+
+ schemaName := call.SchemaName
+ if schemaName == "" {
+ schemaName = "response"
+ }
+
+ // Build request using prepareParams
+ fantasyCall := fantasy.Call{
+ Prompt: call.Prompt,
+ MaxOutputTokens: call.MaxOutputTokens,
+ Temperature: call.Temperature,
+ TopP: call.TopP,
+ PresencePenalty: call.PresencePenalty,
+ FrequencyPenalty: call.FrequencyPenalty,
+ ProviderOptions: call.ProviderOptions,
+ }
+
+ params, warnings := o.prepareParams(fantasyCall)
+
+ // Add structured output via Text.Format field
+ params.Text = responses.ResponseTextConfigParam{
+ Format: responses.ResponseFormatTextConfigParamOfJSONSchema(schemaName, jsonSchemaMap),
+ }
+
+ stream := o.client.Responses.NewStreaming(ctx, *params)
+
+ return func(yield func(fantasy.ObjectStreamPart) bool) {
+ if len(warnings) > 0 {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Warnings: warnings,
+ }) {
+ return
+ }
+ }
+
+ var accumulated string
+ var lastParsedObject any
+ var usage fantasy.Usage
+ var finishReason fantasy.FinishReason
+ var streamErr error
+ hasFunctionCall := false
+
+ for stream.Next() {
+ event := stream.Current()
+
+ switch event.Type {
+ case "response.output_text.delta":
+ textDelta := event.AsResponseOutputTextDelta()
+ accumulated += textDelta.Delta
+
+ // Try to parse the accumulated text
+ obj, state, parseErr := schema.ParsePartialJSON(accumulated)
+
+ // If we successfully parsed, validate and emit
+ if state == schema.ParseStateSuccessful || state == schema.ParseStateRepaired {
+ if err := schema.ValidateAgainstSchema(obj, call.Schema); err == nil {
+ // Only emit if object is different from last
+ if !reflect.DeepEqual(obj, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj,
+ }) {
+ return
+ }
+ lastParsedObject = obj
+ }
+ }
+ }
+
+ // If parsing failed and we have a repair function, try it
+ if state == schema.ParseStateFailed && call.RepairText != nil {
+ repairedText, repairErr := call.RepairText(ctx, accumulated, parseErr)
+ if repairErr == nil {
+ obj2, state2, _ := schema.ParsePartialJSON(repairedText)
+ if (state2 == schema.ParseStateSuccessful || state2 == schema.ParseStateRepaired) &&
+ schema.ValidateAgainstSchema(obj2, call.Schema) == nil {
+ if !reflect.DeepEqual(obj2, lastParsedObject) {
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeObject,
+ Object: obj2,
+ }) {
+ return
+ }
+ lastParsedObject = obj2
+ }
+ }
+ }
+ }
+
+ case "response.completed", "response.incomplete":
+ completed := event.AsResponseCompleted()
+ finishReason = mapResponsesFinishReason(completed.Response.IncompleteDetails.Reason, hasFunctionCall)
+ usage = fantasy.Usage{
+ InputTokens: completed.Response.Usage.InputTokens,
+ OutputTokens: completed.Response.Usage.OutputTokens,
+ TotalTokens: completed.Response.Usage.InputTokens + completed.Response.Usage.OutputTokens,
+ }
+ if completed.Response.Usage.OutputTokensDetails.ReasoningTokens != 0 {
+ usage.ReasoningTokens = completed.Response.Usage.OutputTokensDetails.ReasoningTokens
+ }
+ if completed.Response.Usage.InputTokensDetails.CachedTokens != 0 {
+ usage.CacheReadTokens = completed.Response.Usage.InputTokensDetails.CachedTokens
+ }
+
+ case "error":
+ errorEvent := event.AsError()
+ streamErr = fmt.Errorf("response error: %s (code: %s)", errorEvent.Message, errorEvent.Code)
+ if !yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: streamErr,
+ }) {
+ return
+ }
+ return
+ }
+ }
+
+ err := stream.Err()
+ if err != nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: toProviderErr(err),
+ })
+ return
+ }
+
+ // Final validation and emit
+ if streamErr == nil && lastParsedObject != nil {
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeFinish,
+ Usage: usage,
+ FinishReason: finishReason,
+ })
+ } else if streamErr == nil && lastParsedObject == nil {
+ // No object was generated
+ yield(fantasy.ObjectStreamPart{
+ Type: fantasy.ObjectStreamPartTypeError,
+ Error: &fantasy.NoObjectGeneratedError{
+ RawText: accumulated,
+ ParseError: fmt.Errorf("no valid object generated in stream"),
+ Usage: usage,
+ FinishReason: finishReason,
+ },
+ })
+ }
+ }, nil
+}
@@ -11,6 +11,7 @@ type options struct {
openaiOptions []openai.Option
languageModelOptions []openai.LanguageModelOption
sdkOptions []option.RequestOption
+ objectMode fantasy.ObjectMode
}
const (
@@ -33,15 +34,24 @@ func New(opts ...Option) (fantasy.Provider, error) {
openai.WithLanguageModelExtraContentFunc(ExtraContentFunc),
openai.WithLanguageModelToPromptFunc(ToPromptFunc),
},
+ objectMode: fantasy.ObjectModeTool, // Default to tool mode for openai-compat
}
for _, o := range opts {
o(&providerOptions)
}
+ // Handle object mode: convert unsupported modes to tool
+ // OpenAI-compat endpoints don't support native JSON mode, so we use tool or text
+ objectMode := providerOptions.objectMode
+ if objectMode == fantasy.ObjectModeAuto || objectMode == fantasy.ObjectModeJSON {
+ objectMode = fantasy.ObjectModeTool
+ }
+
providerOptions.openaiOptions = append(
providerOptions.openaiOptions,
openai.WithSDKOptions(providerOptions.sdkOptions...),
openai.WithLanguageModelOptions(providerOptions.languageModelOptions...),
+ openai.WithObjectMode(objectMode),
)
return openai.New(providerOptions.openaiOptions...)
}
@@ -87,3 +97,13 @@ func WithSDKOptions(opts ...option.RequestOption) Option {
o.sdkOptions = append(o.sdkOptions, opts...)
}
}
+
+// WithObjectMode sets the object generation mode for the OpenAI-compatible provider.
+// Supported modes: ObjectModeTool, ObjectModeText.
+// ObjectModeAuto and ObjectModeJSON are automatically converted to ObjectModeTool
+// since OpenAI-compatible endpoints typically don't support native JSON mode.
+func WithObjectMode(om fantasy.ObjectMode) Option {
+ return func(o *options) {
+ o.objectMode = om
+ }
+}
@@ -12,6 +12,7 @@ import (
type options struct {
openaiOptions []openai.Option
languageModelOptions []openai.LanguageModelOption
+ objectMode fantasy.ObjectMode
}
const (
@@ -39,12 +40,24 @@ func New(opts ...Option) (fantasy.Provider, error) {
openai.WithLanguageModelExtraContentFunc(languageModelExtraContent),
openai.WithLanguageModelToPromptFunc(languageModelToPrompt),
},
+ objectMode: fantasy.ObjectModeTool, // Default to tool mode for openrouter
}
for _, o := range opts {
o(&providerOptions)
}
- providerOptions.openaiOptions = append(providerOptions.openaiOptions, openai.WithLanguageModelOptions(providerOptions.languageModelOptions...))
+ // Handle object mode: convert unsupported modes to tool
+ // OpenRouter doesn't support native JSON mode, so we use tool or text
+ objectMode := providerOptions.objectMode
+ if objectMode == fantasy.ObjectModeAuto || objectMode == fantasy.ObjectModeJSON {
+ objectMode = fantasy.ObjectModeTool
+ }
+
+ providerOptions.openaiOptions = append(
+ providerOptions.openaiOptions,
+ openai.WithLanguageModelOptions(providerOptions.languageModelOptions...),
+ openai.WithObjectMode(objectMode),
+ )
return openai.New(providerOptions.openaiOptions...)
}
@@ -76,6 +89,16 @@ func WithHTTPClient(client option.HTTPClient) Option {
}
}
+// WithObjectMode sets the object generation mode for the OpenRouter provider.
+// Supported modes: ObjectModeTool, ObjectModeText.
+// ObjectModeAuto and ObjectModeJSON are automatically converted to ObjectModeTool
+// since OpenRouter doesn't support native JSON mode.
+func WithObjectMode(om fantasy.ObjectMode) Option {
+ return func(o *options) {
+ o.objectMode = om
+ }
+}
+
func structToMapJSON(s any) (map[string]any, error) {
var result map[string]any
jsonBytes, err := json.Marshal(s)
@@ -99,6 +99,14 @@ func TestAnthropicThinkingWithCacheControl(t *testing.T) {
testThinking(t, pairs, testAnthropicThinking)
}
+func TestAnthropicObjectGeneration(t *testing.T) {
+ var pairs []builderPair
+ for _, m := range anthropicTestModels {
+ pairs = append(pairs, builderPair{m.name, anthropicBuilder(m.model), nil, nil})
+ }
+ testObjectGeneration(t, pairs)
+}
+
func testAnthropicThinking(t *testing.T, result *fantasy.AgentResult) {
reasoningContentCount := 0
signaturesCount := 0
@@ -55,6 +55,22 @@ func TestGoogleThinking(t *testing.T) {
testThinking(t, pairs, testGoogleThinking)
}
+func TestGoogleObjectGeneration(t *testing.T) {
+ var pairs []builderPair
+ for _, m := range geminiTestModels {
+ pairs = append(pairs, builderPair{m.name, geminiBuilder(m.model), nil, nil})
+ }
+ testObjectGeneration(t, pairs)
+}
+
+func TestGoogleVertexObjectGeneration(t *testing.T) {
+ var pairs []builderPair
+ for _, m := range vertexTestModels {
+ pairs = append(pairs, builderPair{m.name, vertexBuilder(m.model), nil, nil})
+ }
+ testObjectGeneration(t, pairs)
+}
+
func testGoogleThinking(t *testing.T, result *fantasy.AgentResult) {
reasoningContentCount := 0
// Test if we got the signature
@@ -0,0 +1,421 @@
+package providertests
+
+import (
+ "context"
+ "strings"
+ "testing"
+
+ "charm.land/fantasy"
+ "github.com/stretchr/testify/require"
+)
+
+// Object generation tests for providers.
+//
+// These test functions can be used to test structured object generation
+// (GenerateObject and StreamObject) for any provider implementation.
+//
+// Usage example:
+//
+// func TestMyProviderObjectGeneration(t *testing.T) {
+// var pairs []builderPair
+// for _, m := range myTestModels {
+// pairs = append(pairs, builderPair{m.name, myBuilder(m.model), nil, nil})
+// }
+// testObjectGeneration(t, pairs)
+// }
+//
+// The tests cover:
+// - Simple object generation (flat schema with basic types)
+// - Complex object generation (nested objects and arrays)
+// - Streaming object generation (progressive updates)
+// - Object generation with custom repair functions
+
+// testObjectGeneration tests structured object generation for a provider.
+// It includes both non-streaming (GenerateObject) and streaming (StreamObject) tests.
+func testObjectGeneration(t *testing.T, pairs []builderPair) {
+ for _, pair := range pairs {
+ t.Run(pair.name, func(t *testing.T) {
+ testSimpleObject(t, pair)
+ testComplexObject(t, pair)
+ })
+ }
+}
+
+func testSimpleObject(t *testing.T, pair builderPair) {
+ // Define a simple schema for a person object
+ schema := fantasy.Schema{
+ Type: "object",
+ Properties: map[string]*fantasy.Schema{
+ "name": {
+ Type: "string",
+ Description: "The person's name",
+ },
+ "age": {
+ Type: "integer",
+ Description: "The person's age",
+ },
+ "city": {
+ Type: "string",
+ Description: "The city where the person lives",
+ },
+ },
+ Required: []string{"name", "age", "city"},
+ }
+
+ checkResult := func(t *testing.T, obj any, rawText string, usage fantasy.Usage) {
+ require.NotNil(t, obj, "object should not be nil")
+ require.NotEmpty(t, rawText, "raw text should not be empty")
+ require.Greater(t, usage.TotalTokens, int64(0), "usage should be tracked")
+
+ // Validate structure
+ objMap, ok := obj.(map[string]any)
+ require.True(t, ok, "object should be a map")
+ require.Contains(t, objMap, "name")
+ require.Contains(t, objMap, "age")
+ require.Contains(t, objMap, "city")
+
+ // Validate types
+ name, ok := objMap["name"].(string)
+ require.True(t, ok, "name should be a string")
+ require.NotEmpty(t, name, "name should not be empty")
+
+ // Age could be float64 from JSON unmarshaling
+ age, ok := objMap["age"].(float64)
+ require.True(t, ok, "age should be a number")
+ require.Greater(t, age, 0.0, "age should be greater than 0")
+
+ city, ok := objMap["city"].(string)
+ require.True(t, ok, "city should be a string")
+ require.NotEmpty(t, city, "city should not be empty")
+ }
+
+ t.Run("simple object", func(t *testing.T) {
+ r := newRecorder(t)
+
+ languageModel, err := pair.builder(t, r)
+ require.NoError(t, err, "failed to build language model")
+
+ prompt := fantasy.Prompt{
+ fantasy.NewUserMessage("Generate information about a person named Alice who is 30 years old and lives in Paris."),
+ }
+
+ response, err := languageModel.GenerateObject(t.Context(), fantasy.ObjectCall{
+ Prompt: prompt,
+ Schema: schema,
+ SchemaName: "Person",
+ SchemaDescription: "A person with name, age, and city",
+ MaxOutputTokens: fantasy.Opt(int64(4000)),
+ ProviderOptions: pair.providerOptions,
+ })
+ require.NoError(t, err, "failed to generate object")
+ require.NotNil(t, response, "response should not be nil")
+ checkResult(t, response.Object, response.RawText, response.Usage)
+ })
+
+ t.Run("simple object streaming", func(t *testing.T) {
+ r := newRecorder(t)
+
+ languageModel, err := pair.builder(t, r)
+ require.NoError(t, err, "failed to build language model")
+
+ prompt := fantasy.Prompt{
+ fantasy.NewUserMessage("Generate information about a person named Alice who is 30 years old and lives in Paris."),
+ }
+
+ stream, err := languageModel.StreamObject(t.Context(), fantasy.ObjectCall{
+ Prompt: prompt,
+ Schema: schema,
+ SchemaName: "Person",
+ SchemaDescription: "A person with name, age, and city",
+ MaxOutputTokens: fantasy.Opt(int64(4000)),
+ ProviderOptions: pair.providerOptions,
+ })
+ require.NoError(t, err, "failed to create object stream")
+ require.NotNil(t, stream, "stream should not be nil")
+
+ var lastObject any
+ var rawText string
+ var usage fantasy.Usage
+ var finishReason fantasy.FinishReason
+ objectCount := 0
+
+ for part := range stream {
+ switch part.Type {
+ case fantasy.ObjectStreamPartTypeObject:
+ lastObject = part.Object
+ objectCount++
+ case fantasy.ObjectStreamPartTypeTextDelta:
+ rawText += part.Delta
+ case fantasy.ObjectStreamPartTypeFinish:
+ usage = part.Usage
+ finishReason = part.FinishReason
+ case fantasy.ObjectStreamPartTypeError:
+ t.Fatalf("stream error: %v", part.Error)
+ }
+ }
+
+ require.NotNil(t, lastObject, "should have received at least one object")
+ require.Greater(t, objectCount, 0, "should have received object updates")
+ require.NotEqual(t, fantasy.FinishReasonUnknown, finishReason, "should have a finish reason")
+
+ // Validate object structure without requiring rawText (may be empty in tool-based mode)
+ require.NotNil(t, lastObject, "object should not be nil")
+ require.Greater(t, usage.TotalTokens, int64(0), "usage should be tracked")
+
+ // Validate structure
+ objMap, ok := lastObject.(map[string]any)
+ require.True(t, ok, "object should be a map")
+ require.Contains(t, objMap, "name")
+ require.Contains(t, objMap, "age")
+ require.Contains(t, objMap, "city")
+
+ // Validate types
+ name, ok := objMap["name"].(string)
+ require.True(t, ok, "name should be a string")
+ require.NotEmpty(t, name, "name should not be empty")
+
+ // Age could be float64 from JSON unmarshaling
+ age, ok := objMap["age"].(float64)
+ require.True(t, ok, "age should be a number")
+ require.Greater(t, age, 0.0, "age should be greater than 0")
+
+ city, ok := objMap["city"].(string)
+ require.True(t, ok, "city should be a string")
+ require.NotEmpty(t, city, "city should not be empty")
+ })
+}
+
+func testComplexObject(t *testing.T, pair builderPair) {
+ // Define a more complex schema with nested objects and arrays
+ schema := fantasy.Schema{
+ Type: "object",
+ Properties: map[string]*fantasy.Schema{
+ "title": {
+ Type: "string",
+ Description: "The book title",
+ },
+ "author": {
+ Type: "object",
+ Properties: map[string]*fantasy.Schema{
+ "name": {
+ Type: "string",
+ Description: "Author's name",
+ },
+ "nationality": {
+ Type: "string",
+ Description: "Author's nationality",
+ },
+ },
+ Required: []string{"name", "nationality"},
+ },
+ "genres": {
+ Type: "array",
+ Items: &fantasy.Schema{
+ Type: "string",
+ },
+ Description: "List of genres",
+ },
+ "published_year": {
+ Type: "integer",
+ Description: "Year the book was published",
+ },
+ },
+ Required: []string{"title", "author", "genres", "published_year"},
+ }
+
+ checkResult := func(t *testing.T, obj any, rawText string, usage fantasy.Usage) {
+ require.NotNil(t, obj, "object should not be nil")
+ require.NotEmpty(t, rawText, "raw text should not be empty")
+ require.Greater(t, usage.TotalTokens, int64(0), "usage should be tracked")
+
+ // Validate structure
+ objMap, ok := obj.(map[string]any)
+ require.True(t, ok, "object should be a map")
+ require.Contains(t, objMap, "title")
+ require.Contains(t, objMap, "author")
+ require.Contains(t, objMap, "genres")
+ require.Contains(t, objMap, "published_year")
+
+ // Validate title
+ title, ok := objMap["title"].(string)
+ require.True(t, ok, "title should be a string")
+ require.True(t, strings.Contains(strings.ToLower(title), "rings"), "title should contain 'rings'")
+
+ // Validate nested author object
+ author, ok := objMap["author"].(map[string]any)
+ require.True(t, ok, "author should be an object")
+ require.Contains(t, author, "name")
+ require.Contains(t, author, "nationality")
+
+ // Validate genres array
+ genres, ok := objMap["genres"].([]any)
+ require.True(t, ok, "genres should be an array")
+ require.Greater(t, len(genres), 0, "genres should have at least one item")
+ for _, genre := range genres {
+ _, ok := genre.(string)
+ require.True(t, ok, "each genre should be a string")
+ }
+
+ // Validate published_year
+ year, ok := objMap["published_year"].(float64)
+ require.True(t, ok, "published_year should be a number")
+ require.Greater(t, year, 1900.0, "published_year should be after 1900")
+ }
+
+ t.Run("complex object", func(t *testing.T) {
+ r := newRecorder(t)
+
+ languageModel, err := pair.builder(t, r)
+ require.NoError(t, err, "failed to build language model")
+
+ prompt := fantasy.Prompt{
+ fantasy.NewUserMessage("Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."),
+ }
+
+ response, err := languageModel.GenerateObject(t.Context(), fantasy.ObjectCall{
+ Prompt: prompt,
+ Schema: schema,
+ SchemaName: "Book",
+ SchemaDescription: "A book with title, author, genres, and publication year",
+ MaxOutputTokens: fantasy.Opt(int64(4000)),
+ ProviderOptions: pair.providerOptions,
+ })
+ require.NoError(t, err, "failed to generate object")
+ require.NotNil(t, response, "response should not be nil")
+ checkResult(t, response.Object, response.RawText, response.Usage)
+ })
+
+ t.Run("complex object streaming", func(t *testing.T) {
+ r := newRecorder(t)
+
+ languageModel, err := pair.builder(t, r)
+ require.NoError(t, err, "failed to build language model")
+
+ prompt := fantasy.Prompt{
+ fantasy.NewUserMessage("Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."),
+ }
+
+ stream, err := languageModel.StreamObject(t.Context(), fantasy.ObjectCall{
+ Prompt: prompt,
+ Schema: schema,
+ SchemaName: "Book",
+ SchemaDescription: "A book with title, author, genres, and publication year",
+ MaxOutputTokens: fantasy.Opt(int64(4000)),
+ ProviderOptions: pair.providerOptions,
+ })
+ require.NoError(t, err, "failed to create object stream")
+ require.NotNil(t, stream, "stream should not be nil")
+
+ var lastObject any
+ var rawText string
+ var usage fantasy.Usage
+ var finishReason fantasy.FinishReason
+ objectCount := 0
+
+ for part := range stream {
+ switch part.Type {
+ case fantasy.ObjectStreamPartTypeObject:
+ lastObject = part.Object
+ objectCount++
+ case fantasy.ObjectStreamPartTypeTextDelta:
+ rawText += part.Delta
+ case fantasy.ObjectStreamPartTypeFinish:
+ usage = part.Usage
+ finishReason = part.FinishReason
+ case fantasy.ObjectStreamPartTypeError:
+ t.Fatalf("stream error: %v", part.Error)
+ }
+ }
+
+ require.NotNil(t, lastObject, "should have received at least one object")
+ require.Greater(t, objectCount, 0, "should have received object updates")
+ require.NotEqual(t, fantasy.FinishReasonUnknown, finishReason, "should have a finish reason")
+
+ // Validate object structure without requiring rawText (may be empty in tool-based mode)
+ require.NotNil(t, lastObject, "object should not be nil")
+ require.Greater(t, usage.TotalTokens, int64(0), "usage should be tracked")
+
+ // Validate structure
+ objMap, ok := lastObject.(map[string]any)
+ require.True(t, ok, "object should be a map")
+ require.Contains(t, objMap, "title")
+ require.Contains(t, objMap, "author")
+ require.Contains(t, objMap, "genres")
+ require.Contains(t, objMap, "published_year")
+
+ // Validate title
+ title, ok := objMap["title"].(string)
+ require.True(t, ok, "title should be a string")
+ require.True(t, strings.Contains(strings.ToLower(title), "rings"), "title should contain 'rings'")
+
+ // Validate nested author object
+ author, ok := objMap["author"].(map[string]any)
+ require.True(t, ok, "author should be an object")
+ require.Contains(t, author, "name")
+ require.Contains(t, author, "nationality")
+
+ // Validate genres array
+ genres, ok := objMap["genres"].([]any)
+ require.True(t, ok, "genres should be an array")
+ require.Greater(t, len(genres), 0, "genres should have at least one item")
+ for _, genre := range genres {
+ _, ok := genre.(string)
+ require.True(t, ok, "each genre should be a string")
+ }
+
+ // Validate published_year
+ year, ok := objMap["published_year"].(float64)
+ require.True(t, ok, "published_year should be a number")
+ require.Greater(t, year, 1900.0, "published_year should be after 1900")
+ })
+}
+
+// testObjectWithRepair tests object generation with custom repair functionality.
+func testObjectWithRepair(t *testing.T, pairs []builderPair) {
+ for _, pair := range pairs {
+ t.Run(pair.name, func(t *testing.T) {
+ t.Run("object with repair", func(t *testing.T) {
+ r := newRecorder(t)
+
+ languageModel, err := pair.builder(t, r)
+ require.NoError(t, err, "failed to build language model")
+
+ minVal := 1.0
+ schema := fantasy.Schema{
+ Type: "object",
+ Properties: map[string]*fantasy.Schema{
+ "count": {
+ Type: "integer",
+ Description: "A count that must be positive",
+ Minimum: &minVal,
+ },
+ },
+ Required: []string{"count"},
+ }
+
+ prompt := fantasy.Prompt{
+ fantasy.NewUserMessage("Return a count of 5"),
+ }
+
+ repairFunc := func(ctx context.Context, text string, err error) (string, error) {
+ // Simple repair: if the JSON is malformed, try to fix it
+ // This is a placeholder - real repair would be more sophisticated
+ return text, nil
+ }
+
+ response, err := languageModel.GenerateObject(t.Context(), fantasy.ObjectCall{
+ Prompt: prompt,
+ Schema: schema,
+ SchemaName: "Count",
+ SchemaDescription: "A simple count object",
+ MaxOutputTokens: fantasy.Opt(int64(4000)),
+ RepairText: repairFunc,
+ ProviderOptions: pair.providerOptions,
+ })
+ require.NoError(t, err, "failed to generate object")
+ require.NotNil(t, response, "response should not be nil")
+ require.NotNil(t, response.Object, "object should not be nil")
+ })
+ })
+ }
+}
@@ -53,6 +53,14 @@ func TestOpenAIResponsesWithSummaryThinking(t *testing.T) {
testThinking(t, pairs, testOpenAIResponsesThinkingWithSummaryThinking)
}
+func TestOpenAIResponsesObjectGeneration(t *testing.T) {
+ var pairs []builderPair
+ for _, m := range openaiTestModels {
+ pairs = append(pairs, builderPair{m.name, openAIReasoningBuilder(m.model), nil, nil})
+ }
+ testObjectGeneration(t, pairs)
+}
+
func testOpenAIResponsesThinkingWithSummaryThinking(t *testing.T, result *fantasy.AgentResult) {
reasoningContentCount := 0
encryptedData := 0
@@ -25,6 +25,14 @@ func TestOpenAICommon(t *testing.T) {
testCommon(t, pairs)
}
+func TestOpenAIObjectGeneration(t *testing.T) {
+ var pairs []builderPair
+ for _, m := range openaiTestModels {
+ pairs = append(pairs, builderPair{m.name, openAIBuilder(m.model), nil, nil})
+ }
+ testObjectGeneration(t, pairs)
+}
+
func openAIBuilder(model string) builderFunc {
return func(t *testing.T, r *recorder.Recorder) (fantasy.LanguageModel, error) {
provider, err := openai.New(
@@ -23,6 +23,14 @@ func TestOpenAICompatibleCommon(t *testing.T) {
})
}
+func TestOpenAICompatObjectGeneration(t *testing.T) {
+ testObjectGeneration(t, []builderPair{
+ {"xai-grok-4-fast", builderXAIGrok4Fast, nil, nil},
+ {"xai-grok-code-fast", builderXAIGrokCodeFast, nil, nil},
+ {"zai-glm-4.5", builderZAIGLM45, nil, nil},
+ })
+}
+
func TestOpenAICompatibleThinking(t *testing.T) {
opts := fantasy.ProviderOptions{
openaicompat.Name: &openaicompat.ProviderOptions{
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 973
+ host: ""
+ body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"text"}],"role":"user"}],"model":"claude-sonnet-4-20250514","tool_choice":{"name":"Book","disable_parallel_tool_use":false,"type":"tool"},"tools":[{"input_schema":{"properties":{"author":{"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"name":"Book","description":"A book with title, author, genres, and publication year"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - Anthropic/Go 1.14.0
+ url: https://api.anthropic.com/v1/messages
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"model":"claude-sonnet-4-20250514","id":"msg_01UkSBWK7ReA1Jc9w3ZSKrNb","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01C9Z3xQvtkmqxD7fyJ4BHN5","name":"Book","input":{"title":"The Lord of the Rings","author":{"name":"J.R.R. Tolkien","nationality":"British"},"genres":["Fantasy","Adventure","Epic Fantasy","High Fantasy"],"published_year":1954}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":549,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":124,"service_tier":"standard"}}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 3.668454583s
@@ -0,0 +1,152 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 987
+ host: ""
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 637
+ host: ""
+ body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"text"}],"role":"user"}],"model":"claude-sonnet-4-20250514","tool_choice":{"name":"Person","disable_parallel_tool_use":false,"type":"tool"},"tools":[{"input_schema":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"name":"Person","description":"A person with name, age, and city"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - Anthropic/Go 1.14.0
+ url: https://api.anthropic.com/v1/messages
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"model":"claude-sonnet-4-20250514","id":"msg_01NeodXcmcw7q1AnbSJ8ShV4","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019sLuNYTpv7VTTJEuGWXzki","name":"Person","input":{"name":"Alice","age":30,"city":"Paris"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":454,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":67,"service_tier":"standard"}}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 2.542812416s
@@ -0,0 +1,89 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 651
+ host: ""
+ body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"text"}],"role":"user"}],"model":"claude-sonnet-4-20250514","tool_choice":{"name":"Person","disable_parallel_tool_use":false,"type":"tool"},"tools":[{"input_schema":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"name":"Person","description":"A person with name, age, and city"}],"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - Anthropic/Go 1.14.0
+ url: https://api.anthropic.com/v1/messages
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: message_start
+ data: {"type":"message_start","message":{"model":"claude-sonnet-4-20250514","id":"msg_01AihXNzoggoeqPeQuWPMYSo","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":454,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":24,"service_tier":"standard"}} }
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_01Midg7qjzUz6pdkS8HcgRY7","name":"Person","input":{}} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""} }
+
+ event: ping
+ data: {"type": "ping"}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"n"} }
+
+ event: ping
+ data: {"type": "ping"}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"ame\":"} }
+
+ event: ping
+ data: {"type": "ping"}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":" \"Alic"} }
+
+ event: ping
+ data: {"type": "ping"}
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"e\""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":", \"age\""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":": 30"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":", \"c"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"ity\": \"Pa"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"ris\"}"} }
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":0 }
+
+ event: message_delta
+ data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":454,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":67} }
+
+ event: message_stop
+ data: {"type":"message_stop" }
+
+ headers:
+ Content-Type:
+ - text/event-stream; charset=utf-8
+ status: 200 OK
+ code: 200
+ duration: 2.144504667s
@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "parts": [
+ {
+ "text": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"fantasy\",\"adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"
+ }
+ ],
+ "role": "model"
+ },
+ "finishReason": "STOP",
+ "index": 0
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 38,
+ "candidatesTokenCount": 41,
+ "totalTokenCount": 218,
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 38
+ }
+ ],
+ "thoughtsTokenCount": 139
+ },
+ "modelVersion": "gemini-2.5-flash",
+ "responseId": "stsRaZqEJ9qO28oPiaHWwQ4"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 1.554350125s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "parts": [
+ {
+ "text": "{\"age\": 30, \"city\": \"Paris\", \"name\": \"Alice\"}"
+ }
+ ],
+ "role": "model"
+ },
+ "finishReason": "STOP",
+ "index": 0
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 20,
+ "candidatesTokenCount": 19,
+ "totalTokenCount": 127,
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 20
+ }
+ ],
+ "thoughtsTokenCount": 88
+ },
+ "modelVersion": "gemini-2.5-flash",
+ "responseId": "r9sRaffqJa-jvdIPwMT46A8"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 1.129109875s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: "data: {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \"{\\\"age\\\": 30, \\\"city\\\": \\\"Paris\\\", \\\"name\\\": \\\"Alice\\\"}\"}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 20,\"candidatesTokenCount\": 19,\"totalTokenCount\": 138,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 20}],\"thoughtsTokenCount\": 99},\"modelVersion\": \"gemini-2.5-flash\",\"responseId\": \"sNsRad21He_hxN8PlPed0Qs\"}\r\n\r\n"
+ headers:
+ Content-Type:
+ - text/event-stream
+ status: 200 OK
+ code: 200
+ duration: 1.556089083s
@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "parts": [
+ {
+ "text": "{\n \"author\": {\n \"name\": \"J.R.R. Tolkien\",\n \"nationality\": \"British\"\n },\n \"genres\": [\n \"Fantasy\",\n \"Adventure\"\n ],\n \"published_year\": 1954,\n \"title\": \"The Lord of the Rings\"\n}"
+ }
+ ],
+ "role": "model"
+ },
+ "finishReason": "STOP",
+ "index": 0
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 38,
+ "candidatesTokenCount": 78,
+ "totalTokenCount": 352,
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 38
+ }
+ ],
+ "thoughtsTokenCount": 236
+ },
+ "modelVersion": "gemini-2.5-pro",
+ "responseId": "v9sRad3jAc_7xs0P77zDwQE"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 4.027611625s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "parts": [
+ {
+ "text": "{\n \"age\": 30,\n \"city\": \"Paris\",\n \"name\": \"Alice\"\n}"
+ }
+ ],
+ "role": "model"
+ },
+ "finishReason": "STOP",
+ "index": 0
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 20,
+ "candidatesTokenCount": 28,
+ "totalTokenCount": 109,
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 20
+ }
+ ],
+ "thoughtsTokenCount": 61
+ },
+ "modelVersion": "gemini-2.5-pro",
+ "responseId": "ttsRacXtC86PvdIP1Zjy8AE"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 2.077859292s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: generativelanguage.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: "data: {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \"{\\n \\\"age\\\": 30,\\n \\\"city\\\":\"}],\"role\": \"model\"},\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 20,\"candidatesTokenCount\": 15,\"totalTokenCount\": 189,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 20}],\"thoughtsTokenCount\": 154},\"modelVersion\": \"gemini-2.5-pro\",\"responseId\": \"udsRabusHJH_xN8PppGAiQ8\"}\r\n\r\ndata: {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \" \\\"Paris\\\",\\n \\\"name\\\": \\\"Alice\\\"\\n}\"}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 20,\"candidatesTokenCount\": 28,\"totalTokenCount\": 202,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 20}],\"thoughtsTokenCount\": 154},\"modelVersion\": \"gemini-2.5-pro\",\"responseId\": \"udsRabusHJH_xN8PppGAiQ8\"}\r\n\r\n"
+ headers:
+ Content-Type:
+ - text/event-stream
+ status: 200 OK
+ code: 200
+ duration: 4.791360833s
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 978
+ host: ""
+ body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"text"}],"role":"user"}],"tool_choice":{"name":"Book","disable_parallel_tool_use":false,"type":"tool"},"tools":[{"input_schema":{"properties":{"author":{"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"name":"Book","description":"A book with title, author, genres, and publication year"}],"anthropic_version":"vertex-2023-10-16"}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - Anthropic/Go 1.14.0
+ url: https://us-east5-aiplatform.googleapis.com/v1/projects/fantasy-playground-472418/locations/us-east5/publishers/anthropic/models/claude-3-7-sonnet@20250219:rawPredict
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"model":"claude-3-7-sonnet-20250219","id":"msg_vrtx_01UAjss8kpXQotMqUWtjP8Vd","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_vrtx_01UqmeUWH5fBpvtZE1jx4knQ","name":"Book","input":{"title":"The Lord of the Rings","author":{"name":"J.R.R. Tolkien","nationality":"British"},"genres":["Fantasy","Adventure","Epic","High fantasy"],"published_year":1954}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":548,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":132}}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 2.867918541s
@@ -0,0 +1,132 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 992
+ host: ""
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 642
+ host: ""
+ body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"text"}],"role":"user"}],"tool_choice":{"name":"Person","disable_parallel_tool_use":false,"type":"tool"},"tools":[{"input_schema":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"name":"Person","description":"A person with name, age, and city"}],"anthropic_version":"vertex-2023-10-16"}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - Anthropic/Go 1.14.0
+ url: https://us-east5-aiplatform.googleapis.com/v1/projects/fantasy-playground-472418/locations/us-east5/publishers/anthropic/models/claude-3-7-sonnet@20250219:rawPredict
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"model":"claude-3-7-sonnet-20250219","id":"msg_vrtx_014VLsXeDXtuC84Nx9V4pBdW","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_vrtx_01TaBaQU6GZpxjApVgvzLfev","name":"Person","input":{"name":"Alice","age":30,"city":"Paris"}}],"stop_reason":"tool_use","stop_sequence":null,"usage":{"input_tokens":453,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":67}}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.818802583s
@@ -0,0 +1,81 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 656
+ host: ""
+ body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"text"}],"role":"user"}],"tool_choice":{"name":"Person","disable_parallel_tool_use":false,"type":"tool"},"tools":[{"input_schema":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"name":"Person","description":"A person with name, age, and city"}],"stream":true,"anthropic_version":"vertex-2023-10-16"}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - Anthropic/Go 1.14.0
+ url: https://us-east5-aiplatform.googleapis.com/v1/projects/fantasy-playground-472418/locations/us-east5/publishers/anthropic/models/claude-3-7-sonnet@20250219:streamRawPredict
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |+
+ event: message_start
+ data: {"type":"message_start","message":{"model":"claude-3-7-sonnet-20250219","id":"msg_vrtx_01XdrdQzn2BFw8pob1JJCRnX","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":453,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":11}}}
+
+ event: ping
+ data: {"type": "ping"}
+
+ event: content_block_start
+ data: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_vrtx_01Y5BDgK5DcxEvwThJWL5Pix","name":"Person","input":{}} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"name\":"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":" \"Alice\""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":", \"age\""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":": 30"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":", \"city\""} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":": \"P"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"ari"} }
+
+ event: content_block_delta
+ data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"s\"}"} }
+
+ event: content_block_stop
+ data: {"type":"content_block_stop","index":0 }
+
+ event: message_delta
+ data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":67} }
+
+ event: message_stop
+ data: {"type":"message_stop" }
+
+ headers:
+ Content-Type:
+ - text/event-stream; charset=utf-8
+ status: 200 OK
+ code: 200
+ duration: 1.471510916s
@@ -0,0 +1,70 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-flash:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "role": "model",
+ "parts": [
+ {
+ "text": "{\n \"title\": \"The Lord of the Rings\",\n \"author\": {\n \"name\": \"J.R.R. Tolkien\",\n \"nationality\": \"British\"\n },\n \"genres\": [\n \"fantasy\",\n \"adventure\"\n ],\n \"published_year\": 1954\n}"
+ }
+ ]
+ },
+ "finishReason": "STOP",
+ "avgLogprobs": -0.45047834322050018
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 37,
+ "candidatesTokenCount": 77,
+ "totalTokenCount": 246,
+ "trafficType": "ON_DEMAND",
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 37
+ }
+ ],
+ "candidatesTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 77
+ }
+ ],
+ "thoughtsTokenCount": 132
+ },
+ "modelVersion": "gemini-2.5-flash",
+ "createTime": "2025-11-10T12:36:25.122147Z",
+ "responseId": "SdwRaaO6B9fzptQPk5W9sQ8"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 1.655407625s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
@@ -0,0 +1,70 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-flash:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "role": "model",
+ "parts": [
+ {
+ "text": "{\"name\": \"Alice\", \"age\": 30, \"city\": \"Paris\"}"
+ }
+ ]
+ },
+ "finishReason": "STOP",
+ "avgLogprobs": -0.57498173964651011
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 19,
+ "candidatesTokenCount": 19,
+ "totalTokenCount": 83,
+ "trafficType": "ON_DEMAND",
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 19
+ }
+ ],
+ "candidatesTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 19
+ }
+ ],
+ "thoughtsTokenCount": 45
+ },
+ "modelVersion": "gemini-2.5-flash",
+ "createTime": "2025-11-10T12:36:22.649878Z",
+ "responseId": "RtwRaZbVJ-zzptQPvOrxqQ8"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 1.886921167s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-flash:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: "data: {\"candidates\": [{\"content\": {\"role\": \"model\",\"parts\": [{\"text\": \"{\\n \\\"name\\\": \\\"Alice\\\",\\n \\\"age\\\": 30,\\n \\\"city\\\": \\\"Paris\\\"\\n}\"}]},\"finishReason\": \"STOP\"}],\"usageMetadata\": {\"promptTokenCount\": 19,\"candidatesTokenCount\": 28,\"totalTokenCount\": 124,\"trafficType\": \"ON_DEMAND\",\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 19}],\"candidatesTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 28}],\"thoughtsTokenCount\": 77},\"modelVersion\": \"gemini-2.5-flash\",\"createTime\": \"2025-11-10T12:36:23.898113Z\",\"responseId\": \"R9wRacHoNpyYw8cPz9-4-QY\"}\r\n\r\n"
+ headers:
+ Content-Type:
+ - text/event-stream
+ status: 200 OK
+ code: 200
+ duration: 1.064075083s
@@ -0,0 +1,70 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-pro:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "role": "model",
+ "parts": [
+ {
+ "text": "{\n \"title\": \"The Lord of the Rings\",\n \"author\": {\n \"name\": \"J.R.R. Tolkien\",\n \"nationality\": \"British\"\n },\n \"genres\": [\n \"Fantasy\",\n \"Adventure\"\n ],\n \"published_year\": 1954\n}"
+ }
+ ]
+ },
+ "finishReason": "STOP",
+ "avgLogprobs": -0.67830568784243106
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 37,
+ "candidatesTokenCount": 77,
+ "totalTokenCount": 514,
+ "trafficType": "ON_DEMAND",
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 37
+ }
+ ],
+ "candidatesTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 77
+ }
+ ],
+ "thoughtsTokenCount": 400
+ },
+ "modelVersion": "gemini-2.5-pro",
+ "createTime": "2025-11-10T12:36:32.031653Z",
+ "responseId": "UNwRaaX3AZ3qptQPg8OiiQU"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 5.257772958s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 817
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about 'The Lord of the Rings' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954)."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"author":{"properties":{"name":{"description":"Author's name","type":"string"},"nationality":{"description":"Author's nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-pro:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
@@ -0,0 +1,70 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-pro:generateContent
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "candidates": [
+ {
+ "content": {
+ "role": "model",
+ "parts": [
+ {
+ "text": "{\n \"name\": \"Alice\",\n \"age\": 30,\n \"city\": \"Paris\"\n}"
+ }
+ ]
+ },
+ "finishReason": "STOP",
+ "avgLogprobs": -0.58463580267769955
+ }
+ ],
+ "usageMetadata": {
+ "promptTokenCount": 19,
+ "candidatesTokenCount": 28,
+ "totalTokenCount": 102,
+ "trafficType": "ON_DEMAND",
+ "promptTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 19
+ }
+ ],
+ "candidatesTokensDetails": [
+ {
+ "modality": "TEXT",
+ "tokenCount": 28
+ }
+ ],
+ "thoughtsTokenCount": 55
+ },
+ "modelVersion": "gemini-2.5-pro",
+ "createTime": "2025-11-10T12:36:28.270555Z",
+ "responseId": "TNwRadvBENLK5OMPoK7I0AI"
+ }
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 1.459651375s
@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 499
+ host: us-east5-aiplatform.googleapis.com
+ body: |
+ {"contents":[{"parts":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris."}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000,"responseJsonSchema":{"properties":{"age":{"description":"The person's age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person's name","type":"string"}},"required":["name","age","city"],"type":"object"},"responseMimeType":"application/json"}}
+ form:
+ alt:
+ - sse
+ headers:
+ Content-Type:
+ - application/json
+ User-Agent:
+ - google-genai-sdk/1.34.0 gl-go/go1.25.0
+ url: https://us-east5-aiplatform.googleapis.com/v1beta1/projects/fantasy-playground-472418/locations/us-east5/publishers/google/models/gemini-2.5-pro:streamGenerateContent?alt=sse
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: "data: {\"candidates\": [{\"content\": {\"role\": \"model\",\"parts\": [{\"text\": \"{\\n \\\"name\\\": \\\"Alice\\\",\\n \\\"age\\\": 30,\\n \\\"city\\\": \\\"Paris\\\"\\n}\"}]},\"finishReason\": \"STOP\"}],\"usageMetadata\": {\"promptTokenCount\": 19,\"candidatesTokenCount\": 28,\"totalTokenCount\": 209,\"trafficType\": \"ON_DEMAND\",\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 19}],\"candidatesTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 28}],\"thoughtsTokenCount\": 162},\"modelVersion\": \"gemini-2.5-pro\",\"createTime\": \"2025-11-10T12:36:29.889263Z\",\"responseId\": \"TdwRaa-jNvWAptQPkJGMwQ8\"}\r\n\r\n"
+ headers:
+ Content-Type:
+ - text/event-stream
+ status: 200 OK
+ code: 200
+ duration: 1.9909835s
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 962
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"grok-4-fast","max_tokens":4000,"tool_choice":{"function":{"name":"Book"},"type":"function"},"tools":[{"function":{"name":"Book","strict":false,"description":"A book with title, author, genres, and publication year","parameters":{"properties":{"author":{"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"function"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.x.ai/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"id":"c507c7e0-9670-a02e-8c70-1df65c243406","object":"chat.completion","created":1762637614,"model":"grok-4-fast-reasoning","choices":[{"index":0,"message":{"role":"assistant","content":"","tool_calls":[{"id":"call_93127190","function":{"name":"Book","arguments":"{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"fantasy\",\"adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"},"type":"function"}],"refusal":null},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":468,"completion_tokens":83,"total_tokens":1122,"prompt_tokens_details":{"text_tokens":468,"audio_tokens":0,"image_tokens":0,"cached_tokens":305},"completion_tokens_details":{"reasoning_tokens":571,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"num_sources_used":0},"system_fingerprint":"fp_bfbe7bd0a2"}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 3.442305458s
@@ -0,0 +1,1158 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1016
+ host: ""
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 626
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"grok-4-fast","max_tokens":4000,"tool_choice":{"function":{"name":"Person"},"type":"function"},"tools":[{"function":{"name":"Person","strict":false,"description":"A person with name, age, and city","parameters":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"function"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.x.ai/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"id":"c741f0c0-b3ad-605e-94e9-aa17f8ad0f1f","object":"chat.completion","created":1762637610,"model":"grok-4-fast-reasoning","choices":[{"index":0,"message":{"role":"assistant","content":"","tool_calls":[{"id":"call_85975414","function":{"name":"Person","arguments":"{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"},"type":"function"}],"refusal":null},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":401,"completion_tokens":46,"total_tokens":787,"prompt_tokens_details":{"text_tokens":401,"audio_tokens":0,"image_tokens":0,"cached_tokens":305},"completion_tokens_details":{"reasoning_tokens":340,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"num_sources_used":0},"system_fingerprint":"fp_bfbe7bd0a2"}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 2.228575458s
@@ -0,0 +1,378 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 680
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"grok-4-fast","max_tokens":4000,"stream_options":{"include_usage":true},"tool_choice":{"function":{"name":"Person"},"type":"function"},"tools":[{"function":{"name":"Person","strict":false,"description":"A person with name, age, and city","parameters":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"function"}],"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.x.ai/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{"role":"assistant"}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637613,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637614,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{"tool_calls":[{"id":"call_85608889","function":{"name":"Person","arguments":"{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"},"index":0,"type":"function"}]}}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637614,"model":"grok-4-fast-reasoning","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: {"id":"a6a4a8e9-1f95-504d-3f2c-0f106447d2d2","object":"chat.completion.chunk","created":1762637614,"model":"grok-4-fast-reasoning","choices":[],"usage":{"prompt_tokens":401,"completion_tokens":46,"total_tokens":616,"prompt_tokens_details":{"text_tokens":401,"audio_tokens":0,"image_tokens":0,"cached_tokens":400},"completion_tokens_details":{"reasoning_tokens":169,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"num_sources_used":0},"system_fingerprint":"fp_bfbe7bd0a2"}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream
+ status: 200 OK
+ code: 200
+ duration: 176.963166ms
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 967
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"grok-code-fast-1","max_tokens":4000,"tool_choice":{"function":{"name":"Book"},"type":"function"},"tools":[{"function":{"name":"Book","strict":false,"description":"A book with title, author, genres, and publication year","parameters":{"properties":{"author":{"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"function"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.x.ai/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"id":"2537f822-595e-1e8e-fe59-59c9761c15a1","object":"chat.completion","created":1762637624,"model":"grok-code-fast-1","choices":[{"index":0,"message":{"role":"assistant","content":"","tool_calls":[{"id":"call_05092142","function":{"name":"Book","arguments":"{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"fantasy\",\"adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"},"type":"function"}],"refusal":null},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":496,"completion_tokens":81,"total_tokens":820,"prompt_tokens_details":{"text_tokens":496,"audio_tokens":0,"image_tokens":0,"cached_tokens":320},"completion_tokens_details":{"reasoning_tokens":243,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"num_sources_used":0},"system_fingerprint":"fp_10f00c862d"}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 2.213765834s
@@ -0,0 +1,320 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1021
+ host: ""
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 631
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"grok-code-fast-1","max_tokens":4000,"tool_choice":{"function":{"name":"Person"},"type":"function"},"tools":[{"function":{"name":"Person","strict":false,"description":"A person with name, age, and city","parameters":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"function"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.x.ai/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"id":"8d3d6258-cbe1-f871-55c0-32bd3487f2d7","object":"chat.completion","created":1762637621,"model":"grok-code-fast-1","choices":[{"index":0,"message":{"role":"assistant","content":"","tool_calls":[{"id":"call_38033184","function":{"name":"Person","arguments":"{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"},"type":"function"}],"refusal":null},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":429,"completion_tokens":44,"total_tokens":630,"prompt_tokens_details":{"text_tokens":429,"audio_tokens":0,"image_tokens":0,"cached_tokens":320},"completion_tokens_details":{"reasoning_tokens":157,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"num_sources_used":0},"system_fingerprint":"fp_10f00c862d"}'
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.615676541s
@@ -0,0 +1,338 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 685
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"grok-code-fast-1","max_tokens":4000,"stream_options":{"include_usage":true},"tool_choice":{"function":{"name":"Person"},"type":"function"},"tools":[{"function":{"name":"Person","strict":false,"description":"A person with name, age, and city","parameters":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"function"}],"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.x.ai/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":"The","role":"assistant"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" user"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" asked"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" to"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" generate"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" information"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" about"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" a"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" person"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" named"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" Alice"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":","}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" "}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":"30"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" years"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" old"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":","}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" living"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" in"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" Paris"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":"."}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" There's"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" a"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" tool"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" called"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" \""}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":"Person"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":"\""}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" that"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" matches"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" this"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" exactly"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":":"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" it"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" has"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" name"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":","}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" age"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":","}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" and"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":" city"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":".\n"}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"tool_calls":[{"id":"call_71440968","function":{"name":"Person","arguments":"{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"},"index":0,"type":"function"}]}}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[{"index":0,"delta":{"reasoning_content":"\n\n## Investigating user request \n- The user asked for information about Alice, a 30-year-old living in Paris."},"finish_reason":"tool_calls"}],"system_fingerprint":"fp_10f00c862d"}
+
+ data: {"id":"8887124f-28ef-3f97-76c9-b348294a5bd5","object":"chat.completion.chunk","created":1762637623,"model":"grok-code-fast-1","choices":[],"usage":{"prompt_tokens":429,"completion_tokens":44,"total_tokens":622,"prompt_tokens_details":{"text_tokens":429,"audio_tokens":0,"image_tokens":0,"cached_tokens":384},"completion_tokens_details":{"reasoning_tokens":149,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0},"num_sources_used":0},"system_fingerprint":"fp_10f00c862d"}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream
+ status: 200 OK
+ code: 200
+ duration: 244.686625ms
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 958
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"glm-4.5","max_tokens":4000,"tool_choice":{"function":{"name":"Book"},"type":"function"},"tools":[{"function":{"name":"Book","strict":false,"description":"A book with title, author, genres, and publication year","parameters":{"properties":{"author":{"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"function"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.z.ai/api/coding/paas/v4/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
@@ -0,0 +1,446 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1012
+ host: ""
@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 622
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"glm-4.5","max_tokens":4000,"tool_choice":{"function":{"name":"Person"},"type":"function"},"tools":[{"function":{"name":"Person","strict":false,"description":"A person with name, age, and city","parameters":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"function"}]}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.z.ai/api/coding/paas/v4/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: '{"choices":[{"finish_reason":"tool_calls","index":0,"message":{"content":"","reasoning_content":"\nThe user is asking me to generate information about a person named Alice who is 30 years old and lives in Paris. Looking at the available function, I have a \"Person\" function that takes three required parameters:\n- name: \"Alice\" \n- age: 30\n- city: \"Paris\"\n\nAll the required parameters are provided, so I can call this function.","role":"assistant","tool_calls":[{"function":{"arguments":"{\"age\": 30, \"city\": \"Paris\", \"name\": \"Alice\"}","name":"Person"},"id":"call_-8178200539482324764","index":0,"type":"function"}]}}],"created":1762637635,"id":"2025110905335254041e69de3b448e","model":"glm-4.5","request_id":"2025110905335254041e69de3b448e","usage":{"completion_tokens":121,"prompt_tokens":230,"prompt_tokens_details":{"cached_tokens":43},"total_tokens":351}}'
+ headers:
+ Content-Type:
+ - application/json; charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 6.174196125s
@@ -0,0 +1,200 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 676
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"glm-4.5","max_tokens":4000,"stream_options":{"include_usage":true},"tool_choice":{"function":{"name":"Person"},"type":"function"},"tools":[{"function":{"name":"Person","strict":false,"description":"A person with name, age, and city","parameters":{"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"function"}],"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.z.ai/api/coding/paas/v4/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"The"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" user"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" wants"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" me"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" to"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" generate"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" information"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" about"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" a"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" person"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" with"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" specific"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" details"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":":\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"-"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" Name"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":":"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" Alice"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"-"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" Age"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":":"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":" "}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"30"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"-"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" City"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":":"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" Paris"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"\n\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"I"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" have"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" a"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" Person"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" function"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" available"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" that"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" takes"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" these"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" exact"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" parameters"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":":\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"-"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" name"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" ("}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"required"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"):"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" \""}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"Alice"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"\"\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"-"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" age"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" ("}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"required"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"):"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":" "}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"30"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","content":"","reasoning_content":"\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"-"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" city"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" ("}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"required"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"):"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" \""}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"Paris"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"\"\n\n"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"All"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" required"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" parameters"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" are"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" provided"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":","}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" so"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" I"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" can"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" make"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" the"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" function"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" call"}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"."}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"delta":{"tool_calls":[{"id":"call_11f61396a06b43ebb9ab7992","index":0,"type":"function","function":{"name":"Person","arguments":"{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"}}]}}]}
+
+ data: {"id":"2025110905335643ffc6855fee49b2","created":1762637636,"model":"glm-4.5","choices":[{"index":0,"finish_reason":"tool_calls","delta":{"role":"assistant","content":""}}],"usage":{"prompt_tokens":230,"completion_tokens":112,"total_tokens":342,"prompt_tokens_details":{"cached_tokens":43}}}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream;charset=UTF-8
+ status: 200 OK
+ code: 200
+ duration: 1.864133208s
@@ -0,0 +1,69 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 968
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"gpt-4o-mini","max_tokens":4000,"response_format":{"json_schema":{"name":"Book","strict":true,"description":"A book with title, author, genres, and publication year","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkh7y0v50HiK3bHkckDQM27mAxaF",
+ "object": "chat.completion",
+ "created": 1762637009,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\",\"Epic\",\"High Fantasy\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 160,
+ "completion_tokens": 43,
+ "total_tokens": 203,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_560af6e559"
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.814804666s
@@ -0,0 +1,126 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1022
+ host: ""
@@ -0,0 +1,69 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 601
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"gpt-4o-mini","max_tokens":4000,"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkh6UpzjVoPIwiP6798RaJh45GaE",
+ "object": "chat.completion",
+ "created": 1762637008,
+ "model": "gpt-4o-mini-2024-07-18",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 99,
+ "completion_tokens": 13,
+ "total_tokens": 112,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_560af6e559"
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 926.519208ms
@@ -0,0 +1,66 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 655
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"gpt-4o-mini","max_tokens":4000,"stream_options":{"include_usage":true},"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"FopRVPvq1"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"vcH0mk1c"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"age"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Yp08y6zs"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"yEwAOmw5"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"30"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"PedSD3PYu"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"I5M18YOR"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"9ReOCb0"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"dRYkzy"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"clnEfR"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"y8uwQU"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"name"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"hoFrRnB"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"f2tFLb"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"Alice"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"MdtNOk"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4NFrFBQr"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"RyhJI"}
+
+ data: {"id":"chatcmpl-CZkh6JIHchpBryosw9Q1zBULiOgQP","object":"chat.completion.chunk","created":1762637008,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[],"usage":{"prompt_tokens":99,"completion_tokens":13,"total_tokens":112,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"a9TEmM6E7"}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream; charset=utf-8
+ status: 200 OK
+ code: 200
+ duration: 611.309416ms
@@ -0,0 +1,69 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 963
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"gpt-4o","max_tokens":4000,"response_format":{"json_schema":{"name":"Book","strict":true,"description":"A book with title, author, genres, and publication year","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkh4icu3jixl4IcoAjWVF1mV1oYu",
+ "object": "chat.completion",
+ "created": 1762637006,
+ "model": "gpt-4o-2024-08-06",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\",\"Epic\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 160,
+ "completion_tokens": 40,
+ "total_tokens": 200,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_b1442291a8"
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.01740425s
@@ -0,0 +1,126 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1017
+ host: ""
@@ -0,0 +1,69 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 596
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"gpt-4o","max_tokens":4000,"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkh2yUHIKUTvXaI7RuJhKssQ3u8J",
+ "object": "chat.completion",
+ "created": 1762637004,
+ "model": "gpt-4o-2024-08-06",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 99,
+ "completion_tokens": 13,
+ "total_tokens": 112,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 0,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": "fp_cbf1785567"
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 866.25175ms
@@ -0,0 +1,66 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 650
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"gpt-4o","max_tokens":4000,"stream_options":{"include_usage":true},"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Q7EWId38WGszkS"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"{\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"WDGuponLquJpz"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"age"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"rCGUFNPg6GmYo"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"\":"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wEfLaS45CzBz3"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"30"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"FXGR1dTIvN4Qp6"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":",\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"KmEPUsJzFtpi5"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"city"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"TxOjAiMNR8i2"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZdagyqxzdDT"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"7AV1wjObQw9"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"\",\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ecbr6MtR7cR"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"name"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"swinVIK7vWIt"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"\":\""},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"V94lL7Iu9CQ"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"Alice"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"tegZ0PohAUA"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"\"}"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"npvxJxBLlAEZV"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"gxtfhzu7ZT"}
+
+ data: {"id":"chatcmpl-CZkh26YobsyDr3tfAEylpsGb7mCKf","object":"chat.completion.chunk","created":1762637004,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":99,"completion_tokens":13,"total_tokens":112,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"mo9vda7oGi3jDi"}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream; charset=utf-8
+ status: 200 OK
+ code: 200
+ duration: 878.195625ms
@@ -0,0 +1,68 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 973
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"gpt-5","max_completion_tokens":4000,"response_format":{"json_schema":{"name":"Book","strict":true,"description":"A book with title, author, genres, and publication year","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkhdPgupBvka4xhGSEPQHw0QQKRf",
+ "object": "chat.completion",
+ "created": 1762637041,
+ "model": "gpt-5-2025-08-07",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 156,
+ "completion_tokens": 370,
+ "total_tokens": 526,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 320,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": null
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 6.754405583s
@@ -0,0 +1,126 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1027
+ host: ""
@@ -0,0 +1,68 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 606
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"gpt-5","max_completion_tokens":4000,"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkhB1eWodsLQ6Gs98f4VUjNlzKV6",
+ "object": "chat.completion",
+ "created": 1762637013,
+ "model": "gpt-5-2025-08-07",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 95,
+ "completion_tokens": 793,
+ "total_tokens": 888,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 768,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": null
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 20.674063542s
@@ -0,0 +1,66 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 660
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"gpt-5","max_completion_tokens":4000,"stream_options":{"include_usage":true},"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"XOkQ64PZpE"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"{\""},"finish_reason":null}],"usage":null,"obfuscation":"Ni84MpFSw"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"age"},"finish_reason":null}],"usage":null,"obfuscation":"HWAfWDi80"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\":"},"finish_reason":null}],"usage":null,"obfuscation":"MgEKDfqen"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"30"},"finish_reason":null}],"usage":null,"obfuscation":"xSUuDmVnPA"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":",\""},"finish_reason":null}],"usage":null,"obfuscation":"rg02xRX4E"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"city"},"finish_reason":null}],"usage":null,"obfuscation":"Otwkppxs"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\":\""},"finish_reason":null}],"usage":null,"obfuscation":"WryBuCt"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Paris"},"finish_reason":null}],"usage":null,"obfuscation":"xHwu9qY"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\",\""},"finish_reason":null}],"usage":null,"obfuscation":"5fVw4lG"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"name"},"finish_reason":null}],"usage":null,"obfuscation":"9Ionk7pl"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\":\""},"finish_reason":null}],"usage":null,"obfuscation":"5vKRCJC"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Alice"},"finish_reason":null}],"usage":null,"obfuscation":"99RRfh5"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\"}"},"finish_reason":null}],"usage":null,"obfuscation":"mpjzsT6aj"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"Y02J6J"}
+
+ data: {"id":"chatcmpl-CZkhWAnMkiLSCMK7u8Hid2v2V0JKT","object":"chat.completion.chunk","created":1762637034,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":95,"completion_tokens":345,"total_tokens":440,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":320,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"oT4O7bI"}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream; charset=utf-8
+ status: 200 OK
+ code: 200
+ duration: 6.848382125s
@@ -0,0 +1,68 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 975
+ host: ""
+ body: '{"messages":[{"content":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","role":"user"}],"model":"o4-mini","max_completion_tokens":4000,"response_format":{"json_schema":{"name":"Book","strict":true,"description":"A book with title, author, genres, and publication year","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZki5CYlesaMKCtCSbinnMUnYGvLR",
+ "object": "chat.completion",
+ "created": 1762637069,
+ "model": "o4-mini-2025-04-16",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 156,
+ "completion_tokens": 187,
+ "total_tokens": 343,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 128,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": null
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 3.853936875s
@@ -0,0 +1,116 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 1029
+ host: ""
@@ -0,0 +1,68 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 608
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"o4-mini","max_completion_tokens":4000,"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |
+ {
+ "id": "chatcmpl-CZkhy2Semw4FSy9i59cUlONdYx0mh",
+ "object": "chat.completion",
+ "created": 1762637062,
+ "model": "o4-mini-2025-04-16",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}",
+ "refusal": null,
+ "annotations": []
+ },
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 95,
+ "completion_tokens": 162,
+ "total_tokens": 257,
+ "prompt_tokens_details": {
+ "cached_tokens": 0,
+ "audio_tokens": 0
+ },
+ "completion_tokens_details": {
+ "reasoning_tokens": 128,
+ "audio_tokens": 0,
+ "accepted_prediction_tokens": 0,
+ "rejected_prediction_tokens": 0
+ }
+ },
+ "service_tier": "default",
+ "system_fingerprint": null
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 3.135977959s
@@ -0,0 +1,66 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 662
+ host: ""
+ body: '{"messages":[{"content":"Generate information about a person named Alice who is 30 years old and lives in Paris.","role":"user"}],"model":"o4-mini","max_completion_tokens":4000,"stream_options":{"include_usage":true},"response_format":{"json_schema":{"name":"Person","strict":true,"description":"A person with name, age, and city","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"}},"type":"json_schema"},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/chat/completions
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"finish_reason":null}],"usage":null,"obfuscation":"1u4UMeKG"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"{\""},"finish_reason":null}],"usage":null,"obfuscation":"Q5UxGax"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"age"},"finish_reason":null}],"usage":null,"obfuscation":"PC0HaNl"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\":"},"finish_reason":null}],"usage":null,"obfuscation":"lsI4acg"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"30"},"finish_reason":null}],"usage":null,"obfuscation":"tcdcmfbK"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":",\""},"finish_reason":null}],"usage":null,"obfuscation":"TD1rpZS"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"city"},"finish_reason":null}],"usage":null,"obfuscation":"IaOcwg"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\":\""},"finish_reason":null}],"usage":null,"obfuscation":"6cUFm"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Paris"},"finish_reason":null}],"usage":null,"obfuscation":"2yZjs"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\",\""},"finish_reason":null}],"usage":null,"obfuscation":"Q37jl"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"name"},"finish_reason":null}],"usage":null,"obfuscation":"kT7Jci"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\":\""},"finish_reason":null}],"usage":null,"obfuscation":"ou5VZ"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Alice"},"finish_reason":null}],"usage":null,"obfuscation":"AMhVx"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"\"}"},"finish_reason":null}],"usage":null,"obfuscation":"ZDFb7mY"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"AQhR"}
+
+ data: {"id":"chatcmpl-CZki1J9qrKifHLYfd83Fec01s4z8D","object":"chat.completion.chunk","created":1762637065,"model":"o4-mini-2025-04-16","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":95,"completion_tokens":290,"total_tokens":385,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":256,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"xEKhD"}
+
+ data: [DONE]
+
+ headers:
+ Content-Type:
+ - text/event-stream; charset=utf-8
+ status: 200 OK
+ code: 200
+ duration: 3.874454375s
@@ -0,0 +1,149 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 915
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"gpt-4o-mini","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_00aee193a62b03b301690fb5e67a8481a0890292ac13193180",
+ "object": "response",
+ "created_at": 1762637286,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "gpt-4o-mini-2024-07-18",
+ "output": [
+ {
+ "id": "msg_00aee193a62b03b301690fb5e6bf6881a0ab198f7831a11cf0",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\",\"Epic\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Book",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "author": {
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "description": "Author's name",
+ "type": "string"
+ },
+ "nationality": {
+ "description": "Author's nationality",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "nationality"
+ ],
+ "type": "object"
+ },
+ "genres": {
+ "description": "List of genres",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "published_year": {
+ "description": "Year the book was published",
+ "type": "integer"
+ },
+ "title": {
+ "description": "The book title",
+ "type": "string"
+ }
+ },
+ "required": [
+ "title",
+ "author",
+ "genres",
+ "published_year"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 140,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 41,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 181
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.153408417s
@@ -0,0 +1,176 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 929
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"gpt-4o-mini","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,127 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 570
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"gpt-4o-mini","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_0bbc50a7c5ff86f001690fb5e419a8819087b97bd3cbc4c406",
+ "object": "response",
+ "created_at": 1762637284,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "gpt-4o-mini-2024-07-18",
+ "output": [
+ {
+ "id": "msg_0bbc50a7c5ff86f001690fb5e4fabc8190a9749a3e0c928bce",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Person",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "age": {
+ "description": "The person's age",
+ "type": "integer"
+ },
+ "city": {
+ "description": "The city where the person lives",
+ "type": "string"
+ },
+ "name": {
+ "description": "The person's name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "age",
+ "city"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 82,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 14,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 96
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.465307584s
@@ -0,0 +1,95 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 584
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"gpt-4o-mini","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,149 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 910
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"gpt-4o","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_00fee2bcbce77a6b01690fb5e194848191a77c3f9796902417",
+ "object": "response",
+ "created_at": 1762637281,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "gpt-4o-2024-08-06",
+ "output": [
+ {
+ "id": "msg_00fee2bcbce77a6b01690fb5e25dfc8191b275e2c92a87865a",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Book",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "author": {
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "description": "Author's name",
+ "type": "string"
+ },
+ "nationality": {
+ "description": "Author's nationality",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "nationality"
+ ],
+ "type": "object"
+ },
+ "genres": {
+ "description": "List of genres",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "published_year": {
+ "description": "Year the book was published",
+ "type": "integer"
+ },
+ "title": {
+ "description": "The book title",
+ "type": "string"
+ }
+ },
+ "required": [
+ "title",
+ "author",
+ "genres",
+ "published_year"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 140,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 39,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 179
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 1.348413375s
@@ -0,0 +1,176 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 924
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"gpt-4o","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,127 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 565
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"gpt-4o","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_0f20344bcc1b767601690fb5e04c9c81a08af63f829b10bb55",
+ "object": "response",
+ "created_at": 1762637280,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "gpt-4o-2024-08-06",
+ "output": [
+ {
+ "id": "msg_0f20344bcc1b767601690fb5e094e481a0833865218a771607",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": null,
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Person",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "age": {
+ "description": "The person's age",
+ "type": "integer"
+ },
+ "city": {
+ "description": "The city where the person lives",
+ "type": "string"
+ },
+ "name": {
+ "description": "The person's name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "age",
+ "city"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 82,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 14,
+ "output_tokens_details": {
+ "reasoning_tokens": 0
+ },
+ "total_tokens": 96
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 816.505042ms
@@ -0,0 +1,95 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 579
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"gpt-4o","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,154 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 909
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"gpt-5","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_0d7efb113168df8301690fb603c0f881a1a5ecec45f910e875",
+ "object": "response",
+ "created_at": 1762637315,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "gpt-5-2025-08-07",
+ "output": [
+ {
+ "id": "rs_0d7efb113168df8301690fb6041bb881a198a6e417f00bba70",
+ "type": "reasoning",
+ "summary": []
+ },
+ {
+ "id": "msg_0d7efb113168df8301690fb610bf2881a1be28a4885c1876ac",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"British\"},\"genres\":[\"Fantasy\",\"Adventure\",\"Epic fantasy\",\"High fantasy\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": "medium",
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Book",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "author": {
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "description": "Author's name",
+ "type": "string"
+ },
+ "nationality": {
+ "description": "Author's nationality",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "nationality"
+ ],
+ "type": "object"
+ },
+ "genres": {
+ "description": "List of genres",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "published_year": {
+ "description": "Year the book was published",
+ "type": "integer"
+ },
+ "title": {
+ "description": "The book title",
+ "type": "string"
+ }
+ },
+ "required": [
+ "title",
+ "author",
+ "genres",
+ "published_year"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 138,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 885,
+ "output_tokens_details": {
+ "reasoning_tokens": 832
+ },
+ "total_tokens": 1023
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 13.745367625s
@@ -0,0 +1,191 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 923
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"gpt-5","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,132 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 564
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"gpt-5","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_027d860ff8453c5c01690fb5e8b590819c98d4fefe82a2d697",
+ "object": "response",
+ "created_at": 1762637288,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "gpt-5-2025-08-07",
+ "output": [
+ {
+ "id": "rs_027d860ff8453c5c01690fb5e90bf8819cafd2ab51156cc379",
+ "type": "reasoning",
+ "summary": []
+ },
+ {
+ "id": "msg_027d860ff8453c5c01690fb5fe2978819ca252f996006bf96e",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": "medium",
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Person",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "age": {
+ "description": "The person's age",
+ "type": "integer"
+ },
+ "city": {
+ "description": "The city where the person lives",
+ "type": "string"
+ },
+ "name": {
+ "description": "The person's name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "age",
+ "city"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 80,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 1238,
+ "output_tokens_details": {
+ "reasoning_tokens": 1216
+ },
+ "total_tokens": 1318
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 22.08059475s
@@ -0,0 +1,101 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 578
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"gpt-5","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,154 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 911
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"o4-mini","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_017b2c309fa6f10301690fb622abf481a0a85dad143f87cc57",
+ "object": "response",
+ "created_at": 1762637346,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "o4-mini-2025-04-16",
+ "output": [
+ {
+ "id": "rs_017b2c309fa6f10301690fb623133881a0b253929605c93041",
+ "type": "reasoning",
+ "summary": []
+ },
+ {
+ "id": "msg_017b2c309fa6f10301690fb6269d6c81a0abb3acf886a1efe0",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"author\":{\"name\":\"J.R.R. Tolkien\",\"nationality\":\"English\"},\"genres\":[\"Fantasy\",\"Adventure\"],\"published_year\":1954,\"title\":\"The Lord of the Rings\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": "medium",
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Book",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "author": {
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "description": "Author's name",
+ "type": "string"
+ },
+ "nationality": {
+ "description": "Author's nationality",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "nationality"
+ ],
+ "type": "object"
+ },
+ "genres": {
+ "description": "List of genres",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "published_year": {
+ "description": "Year the book was published",
+ "type": "integer"
+ },
+ "title": {
+ "description": "The book title",
+ "type": "string"
+ }
+ },
+ "required": [
+ "title",
+ "author",
+ "genres",
+ "published_year"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 138,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 431,
+ "output_tokens_details": {
+ "reasoning_tokens": 384
+ },
+ "total_tokens": 569
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 4.660075375s
@@ -0,0 +1,176 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 925
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about ''The Lord of the Rings'' book by J.R.R. Tolkien, including genres like fantasy and adventure, and its publication year (1954).","type":"input_text"}],"role":"user"}],"model":"o4-mini","text":{"format":{"name":"Book","schema":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"properties":{"name":{"description":"Author''s name","type":"string"},"nationality":{"description":"Author''s nationality","type":"string"}},"required":["name","nationality"],"type":"object"},"genres":{"description":"List of genres","items":{"type":"string"},"type":"array"},"published_year":{"description":"Year the book was published","type":"integer"},"title":{"description":"The book title","type":"string"}},"required":["title","author","genres","published_year"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,132 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 566
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"o4-mini","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}}}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ uncompressed: true
+ body: |-
+ {
+ "id": "resp_098f453613ad314501690fb61bc6e0819382952362bbb4e647",
+ "object": "response",
+ "created_at": 1762637339,
+ "status": "completed",
+ "background": false,
+ "billing": {
+ "payer": "developer"
+ },
+ "error": null,
+ "incomplete_details": null,
+ "instructions": null,
+ "max_output_tokens": 4000,
+ "max_tool_calls": null,
+ "model": "o4-mini-2025-04-16",
+ "output": [
+ {
+ "id": "rs_098f453613ad314501690fb61c869c81939eea265ba983fa58",
+ "type": "reasoning",
+ "summary": []
+ },
+ {
+ "id": "msg_098f453613ad314501690fb61f8828819389d543a45e07bc0b",
+ "type": "message",
+ "status": "completed",
+ "content": [
+ {
+ "type": "output_text",
+ "annotations": [],
+ "logprobs": [],
+ "text": "{\"age\":30,\"city\":\"Paris\",\"name\":\"Alice\"}"
+ }
+ ],
+ "role": "assistant"
+ }
+ ],
+ "parallel_tool_calls": true,
+ "previous_response_id": null,
+ "prompt_cache_key": null,
+ "prompt_cache_retention": null,
+ "reasoning": {
+ "effort": "medium",
+ "summary": null
+ },
+ "safety_identifier": null,
+ "service_tier": "default",
+ "store": false,
+ "temperature": 1.0,
+ "text": {
+ "format": {
+ "type": "json_schema",
+ "description": null,
+ "name": "Person",
+ "schema": {
+ "additionalProperties": false,
+ "properties": {
+ "age": {
+ "description": "The person's age",
+ "type": "integer"
+ },
+ "city": {
+ "description": "The city where the person lives",
+ "type": "string"
+ },
+ "name": {
+ "description": "The person's name",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "age",
+ "city"
+ ],
+ "type": "object"
+ },
+ "strict": true
+ },
+ "verbosity": "medium"
+ },
+ "tool_choice": "auto",
+ "tools": [],
+ "top_logprobs": 0,
+ "top_p": 1.0,
+ "truncation": "disabled",
+ "usage": {
+ "input_tokens": 80,
+ "input_tokens_details": {
+ "cached_tokens": 0
+ },
+ "output_tokens": 342,
+ "output_tokens_details": {
+ "reasoning_tokens": 320
+ },
+ "total_tokens": 422
+ },
+ "user": null,
+ "metadata": {}
+ }
+ headers:
+ Content-Type:
+ - application/json
+ status: 200 OK
+ code: 200
+ duration: 4.174928125s
@@ -0,0 +1,101 @@
+---
+version: 2
+interactions:
+- id: 0
+ request:
+ proto: HTTP/1.1
+ proto_major: 1
+ proto_minor: 1
+ content_length: 580
+ host: ""
+ body: '{"max_output_tokens":4000,"store":false,"input":[{"content":[{"text":"Generate information about a person named Alice who is 30 years old and lives in Paris.","type":"input_text"}],"role":"user"}],"model":"o4-mini","text":{"format":{"name":"Person","schema":{"additionalProperties":false,"properties":{"age":{"description":"The person''s age","type":"integer"},"city":{"description":"The city where the person lives","type":"string"},"name":{"description":"The person''s name","type":"string"}},"required":["name","age","city"],"type":"object"},"type":"json_schema"}},"stream":true}'
+ headers:
+ Accept:
+ - application/json
+ Content-Type:
+ - application/json
+ User-Agent:
+ - OpenAI/Go 2.7.1
+ url: https://api.openai.com/v1/responses
+ method: POST
+ response:
+ proto: HTTP/2.0
+ proto_major: 2
+ proto_minor: 0
+ content_length: -1
+ body: |+
+ event: response.created
@@ -0,0 +1,403 @@
+// Package schema provides JSON schema generation and validation utilities.
+// It supports automatic schema generation from Go types and validation of parsed objects.
+package schema
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "slices"
+ "strings"
+
+ jsonrepair "github.com/RealAlexandreAI/json-repair"
+ "github.com/kaptinlin/jsonschema"
+)
+
+// ObjectRepairFunc is a function that attempts to repair invalid JSON output.
+// It receives the raw text and the error encountered during parsing or validation,
+// and returns repaired text or an error if repair is not possible.
+type ObjectRepairFunc func(ctx context.Context, text string, err error) (string, error)
+
+// ParseError is returned when object generation fails
+// due to parsing errors, validation errors, or model failures.
+type ParseError struct {
+ RawText string
+ ParseError error
+ ValidationError error
+}
+
+// Schema represents a JSON schema for tool input validation.
+type Schema struct {
+ Type string `json:"type,omitempty"`
+ Properties map[string]*Schema `json:"properties,omitempty"`
+ Required []string `json:"required,omitempty"`
+ Items *Schema `json:"items,omitempty"`
+ Description string `json:"description,omitempty"`
+ Enum []any `json:"enum,omitempty"`
+ Format string `json:"format,omitempty"`
+ Minimum *float64 `json:"minimum,omitempty"`
+ Maximum *float64 `json:"maximum,omitempty"`
+ MinLength *int `json:"minLength,omitempty"`
+ MaxLength *int `json:"maxLength,omitempty"`
+}
+
+// ParseState represents the state of JSON parsing.
+type ParseState string
+
+const (
+ // ParseStateUndefined means input was undefined/empty.
+ ParseStateUndefined ParseState = "undefined"
+
+ // ParseStateSuccessful means JSON parsed without repair.
+ ParseStateSuccessful ParseState = "successful"
+
+ // ParseStateRepaired means JSON parsed after repair.
+ ParseStateRepaired ParseState = "repaired"
+
+ // ParseStateFailed means JSON could not be parsed even after repair.
+ ParseStateFailed ParseState = "failed"
+)
+
+// ToParameters converts a Schema to the parameters map format expected by ToolInfo.
+func ToParameters(s Schema) map[string]any {
+ if s.Properties == nil {
+ return make(map[string]any)
+ }
+
+ result := make(map[string]any)
+ for name, propSchema := range s.Properties {
+ result[name] = ToMap(*propSchema)
+ }
+ return result
+}
+
+// Generate generates a JSON schema from a reflect.Type.
+// It recursively processes struct fields, arrays, maps, and primitive types.
+func Generate(t reflect.Type) Schema {
+ return generateSchemaRecursive(t, make(map[reflect.Type]bool))
+}
+
+func generateSchemaRecursive(t reflect.Type, visited map[reflect.Type]bool) Schema {
+ if t.Kind() == reflect.Pointer {
+ t = t.Elem()
+ }
+
+ if visited[t] {
+ return Schema{Type: "object"}
+ }
+ visited[t] = true
+ defer delete(visited, t)
+
+ switch t.Kind() {
+ case reflect.String:
+ return Schema{Type: "string"}
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return Schema{Type: "integer"}
+ case reflect.Float32, reflect.Float64:
+ return Schema{Type: "number"}
+ case reflect.Bool:
+ return Schema{Type: "boolean"}
+ case reflect.Slice, reflect.Array:
+ itemSchema := generateSchemaRecursive(t.Elem(), visited)
+ return Schema{
+ Type: "array",
+ Items: &itemSchema,
+ }
+ case reflect.Map:
+ if t.Key().Kind() == reflect.String {
+ valueSchema := generateSchemaRecursive(t.Elem(), visited)
+ schema := Schema{
+ Type: "object",
+ Properties: map[string]*Schema{
+ "*": &valueSchema,
+ },
+ }
+ return schema
+ }
+ return Schema{Type: "object"}
+ case reflect.Struct:
+ schema := Schema{
+ Type: "object",
+ Properties: make(map[string]*Schema),
+ }
+ for i := range t.NumField() {
+ field := t.Field(i)
+
+ if !field.IsExported() {
+ continue
+ }
+
+ jsonTag := field.Tag.Get("json")
+ if jsonTag == "-" {
+ continue
+ }
+
+ fieldName := field.Name
+ required := true
+
+ if jsonTag != "" {
+ parts := strings.Split(jsonTag, ",")
+ if parts[0] != "" {
+ fieldName = parts[0]
+ }
+
+ if slices.Contains(parts[1:], "omitempty") {
+ required = false
+ }
+ } else {
+ fieldName = toSnakeCase(fieldName)
+ }
+
+ fieldSchema := generateSchemaRecursive(field.Type, visited)
+
+ if desc := field.Tag.Get("description"); desc != "" {
+ fieldSchema.Description = desc
+ }
+
+ if enumTag := field.Tag.Get("enum"); enumTag != "" {
+ enumValues := strings.Split(enumTag, ",")
+ fieldSchema.Enum = make([]any, len(enumValues))
+ for i, v := range enumValues {
+ fieldSchema.Enum[i] = strings.TrimSpace(v)
+ }
+ }
+
+ schema.Properties[fieldName] = &fieldSchema
+
+ if required {
+ schema.Required = append(schema.Required, fieldName)
+ }
+ }
+
+ return schema
+ case reflect.Interface:
+ return Schema{Type: "object"}
+ default:
+ return Schema{Type: "object"}
+ }
+}
+
+// ToMap converts a Schema to a map representation suitable for JSON Schema.
+func ToMap(schema Schema) map[string]any {
+ result := make(map[string]any)
+
+ if schema.Type != "" {
+ result["type"] = schema.Type
+ }
+
+ if schema.Description != "" {
+ result["description"] = schema.Description
+ }
+
+ if len(schema.Enum) > 0 {
+ result["enum"] = schema.Enum
+ }
+
+ if schema.Format != "" {
+ result["format"] = schema.Format
+ }
+
+ if schema.Minimum != nil {
+ result["minimum"] = *schema.Minimum
+ }
+
+ if schema.Maximum != nil {
+ result["maximum"] = *schema.Maximum
+ }
+
+ if schema.MinLength != nil {
+ result["minLength"] = *schema.MinLength
+ }
+
+ if schema.MaxLength != nil {
+ result["maxLength"] = *schema.MaxLength
+ }
+
+ if schema.Properties != nil {
+ props := make(map[string]any)
+ for name, propSchema := range schema.Properties {
+ props[name] = ToMap(*propSchema)
+ }
+ result["properties"] = props
+ }
+
+ if len(schema.Required) > 0 {
+ result["required"] = schema.Required
+ }
+
+ if schema.Items != nil {
+ itemsMap := ToMap(*schema.Items)
+ // Ensure type is always set for items, even if it was blank for llama.cpp compatibility
+ if _, hasType := itemsMap["type"]; !hasType && schema.Items.Type == "" {
+ if len(schema.Items.Properties) > 0 {
+ itemsMap["type"] = "object"
+ }
+ }
+ result["items"] = itemsMap
+ }
+
+ return result
+}
+
+// ParsePartialJSON attempts to parse potentially incomplete JSON.
+// It first tries standard JSON parsing, then attempts repair if that fails.
+//
+// Returns:
+// - result: The parsed JSON value (map, slice, or primitive)
+// - state: Indicates whether parsing succeeded, needed repair, or failed
+// - err: The error if parsing failed completely
+//
+// Example:
+//
+// obj, state, err := ParsePartialJSON(`{"name": "John", "age": 25`)
+// // Result: map[string]any{"name": "John", "age": 25}, ParseStateRepaired, nil
+func ParsePartialJSON(text string) (any, ParseState, error) {
+ if text == "" {
+ return nil, ParseStateUndefined, nil
+ }
+
+ var result any
+ if err := json.Unmarshal([]byte(text), &result); err == nil {
+ return result, ParseStateSuccessful, nil
+ }
+
+ repaired, err := jsonrepair.RepairJSON(text)
+ if err != nil {
+ return nil, ParseStateFailed, fmt.Errorf("json repair failed: %w", err)
+ }
+
+ if err := json.Unmarshal([]byte(repaired), &result); err != nil {
+ return nil, ParseStateFailed, fmt.Errorf("failed to parse repaired json: %w", err)
+ }
+
+ return result, ParseStateRepaired, nil
+}
+
+// Error implements the error interface.
+func (e *ParseError) Error() string {
+ if e.ValidationError != nil {
+ return fmt.Sprintf("object validation failed: %v", e.ValidationError)
+ }
+ if e.ParseError != nil {
+ return fmt.Sprintf("failed to parse object: %v", e.ParseError)
+ }
+ return "failed to generate object"
+}
+
+// ParseAndValidate combines JSON parsing and validation.
+// Returns the parsed object if both parsing and validation succeed.
+func ParseAndValidate(text string, schema Schema) (any, error) {
+ obj, state, err := ParsePartialJSON(text)
+ if state == ParseStateFailed {
+ return nil, &ParseError{
+ RawText: text,
+ ParseError: err,
+ }
+ }
+
+ if err := validateAgainstSchema(obj, schema); err != nil {
+ return nil, &ParseError{
+ RawText: text,
+ ValidationError: err,
+ }
+ }
+
+ return obj, nil
+}
+
+// ValidateAgainstSchema validates a parsed object against a Schema.
+func ValidateAgainstSchema(obj any, schema Schema) error {
+ return validateAgainstSchema(obj, schema)
+}
+
+func validateAgainstSchema(obj any, schema Schema) error {
+ jsonSchemaBytes, err := json.Marshal(schema)
+ if err != nil {
+ return fmt.Errorf("failed to marshal schema: %w", err)
+ }
+
+ compiler := jsonschema.NewCompiler()
+ validator, err := compiler.Compile(jsonSchemaBytes)
+ if err != nil {
+ return fmt.Errorf("invalid schema: %w", err)
+ }
+
+ result := validator.Validate(obj)
+ if !result.IsValid() {
+ var errMsgs []string
+ for field, validationErr := range result.Errors {
+ errMsgs = append(errMsgs, fmt.Sprintf("%s: %s", field, validationErr.Message))
+ }
+ return fmt.Errorf("validation failed: %s", strings.Join(errMsgs, "; "))
+ }
+
+ return nil
+}
+
+// ParseAndValidateWithRepair attempts parsing, validation, and custom repair.
+func ParseAndValidateWithRepair(
+ ctx context.Context,
+ text string,
+ schema Schema,
+ repair ObjectRepairFunc,
+) (any, error) {
+ obj, state, parseErr := ParsePartialJSON(text)
+
+ if state == ParseStateSuccessful || state == ParseStateRepaired {
+ validationErr := validateAgainstSchema(obj, schema)
+ if validationErr == nil {
+ return obj, nil
+ }
+
+ if repair != nil {
+ repairedText, repairErr := repair(ctx, text, validationErr)
+ if repairErr == nil {
+ obj2, state2, _ := ParsePartialJSON(repairedText)
+ if state2 == ParseStateSuccessful || state2 == ParseStateRepaired {
+ if err := validateAgainstSchema(obj2, schema); err == nil {
+ return obj2, nil
+ }
+ }
+ }
+ }
+
+ return nil, &ParseError{
+ RawText: text,
+ ValidationError: validationErr,
+ }
+ }
+
+ if repair != nil {
+ repairedText, repairErr := repair(ctx, text, parseErr)
+ if repairErr == nil {
+ obj2, state2, parseErr2 := ParsePartialJSON(repairedText)
+ if state2 == ParseStateSuccessful || state2 == ParseStateRepaired {
+ if err := validateAgainstSchema(obj2, schema); err == nil {
+ return obj2, nil
+ }
+ }
+ return nil, &ParseError{
+ RawText: repairedText,
+ ParseError: parseErr2,
+ }
+ }
+ }
+
+ return nil, &ParseError{
+ RawText: text,
+ ParseError: parseErr,
+ }
+}
+
+func toSnakeCase(s string) string {
+ var result strings.Builder
+ for i, r := range s {
+ if i > 0 && r >= 'A' && r <= 'Z' {
+ result.WriteByte('_')
+ }
+ result.WriteRune(r)
+ }
+ return strings.ToLower(result.String())
+}
@@ -0,0 +1,534 @@
+package schema
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestEnumSupport(t *testing.T) {
+ // Test enum via struct tags
+ type WeatherInput struct {
+ Location string `json:"location" description:"City name"`
+ Units string `json:"units" enum:"celsius,fahrenheit,kelvin" description:"Temperature units"`
+ Format string `json:"format,omitempty" enum:"json,xml,text"`
+ }
+
+ schema := Generate(reflect.TypeOf(WeatherInput{}))
+
+ require.Equal(t, "object", schema.Type)
+
+ // Check units field has enum values
+ unitsSchema := schema.Properties["units"]
+ require.NotNil(t, unitsSchema, "Expected units property to exist")
+ require.Len(t, unitsSchema.Enum, 3)
+ expectedUnits := []string{"celsius", "fahrenheit", "kelvin"}
+ for i, expected := range expectedUnits {
+ require.Equal(t, expected, unitsSchema.Enum[i])
+ }
+
+ // Check required fields (format should not be required due to omitempty)
+ expectedRequired := []string{"location", "units"}
+ require.Len(t, schema.Required, len(expectedRequired))
+}
+
+func TestSchemaToParameters(t *testing.T) {
+ testSchema := Schema{
+ Type: "object",
+ Properties: map[string]*Schema{
+ "name": {
+ Type: "string",
+ Description: "The name field",
+ },
+ "age": {
+ Type: "integer",
+ Minimum: func() *float64 { v := 0.0; return &v }(),
+ Maximum: func() *float64 { v := 120.0; return &v }(),
+ },
+ "tags": {
+ Type: "array",
+ Items: &Schema{
+ Type: "string",
+ },
+ },
+ "priority": {
+ Type: "string",
+ Enum: []any{"low", "medium", "high"},
+ },
+ },
+ Required: []string{"name"},
+ }
+
+ params := ToParameters(testSchema)
+
+ // Check name parameter
+ nameParam, ok := params["name"].(map[string]any)
+ require.True(t, ok, "Expected name parameter to exist")
+ require.Equal(t, "string", nameParam["type"])
+ require.Equal(t, "The name field", nameParam["description"])
+
+ // Check age parameter with min/max
+ ageParam, ok := params["age"].(map[string]any)
+ require.True(t, ok, "Expected age parameter to exist")
+ require.Equal(t, "integer", ageParam["type"])
+ require.Equal(t, 0.0, ageParam["minimum"])
+ require.Equal(t, 120.0, ageParam["maximum"])
+
+ // Check priority parameter with enum
+ priorityParam, ok := params["priority"].(map[string]any)
+ require.True(t, ok, "Expected priority parameter to exist")
+ require.Equal(t, "string", priorityParam["type"])
+ enumValues, ok := priorityParam["enum"].([]any)
+ require.True(t, ok)
+ require.Len(t, enumValues, 3)
+}
+
+func TestGenerateSchemaBasicTypes(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ input any
+ expected Schema
+ }{
+ {
+ name: "string type",
+ input: "",
+ expected: Schema{Type: "string"},
+ },
+ {
+ name: "int type",
+ input: 0,
+ expected: Schema{Type: "integer"},
+ },
+ {
+ name: "int64 type",
+ input: int64(0),
+ expected: Schema{Type: "integer"},
+ },
+ {
+ name: "uint type",
+ input: uint(0),
+ expected: Schema{Type: "integer"},
+ },
+ {
+ name: "float64 type",
+ input: 0.0,
+ expected: Schema{Type: "number"},
+ },
+ {
+ name: "float32 type",
+ input: float32(0.0),
+ expected: Schema{Type: "number"},
+ },
+ {
+ name: "bool type",
+ input: false,
+ expected: Schema{Type: "boolean"},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ schema := Generate(reflect.TypeOf(tt.input))
+ require.Equal(t, tt.expected.Type, schema.Type)
+ })
+ }
+}
+
+func TestGenerateSchemaArrayTypes(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ input any
+ expected Schema
+ }{
+ {
+ name: "string slice",
+ input: []string{},
+ expected: Schema{
+ Type: "array",
+ Items: &Schema{Type: "string"},
+ },
+ },
+ {
+ name: "int slice",
+ input: []int{},
+ expected: Schema{
+ Type: "array",
+ Items: &Schema{Type: "integer"},
+ },
+ },
+ {
+ name: "string array",
+ input: [3]string{},
+ expected: Schema{
+ Type: "array",
+ Items: &Schema{Type: "string"},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ schema := Generate(reflect.TypeOf(tt.input))
+ require.Equal(t, tt.expected.Type, schema.Type)
+ require.NotNil(t, schema.Items, "Expected items schema to exist")
+ require.Equal(t, tt.expected.Items.Type, schema.Items.Type)
+ })
+ }
+}
+
+func TestGenerateSchemaMapTypes(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ input any
+ expected string
+ }{
+ {
+ name: "string to string map",
+ input: map[string]string{},
+ expected: "object",
+ },
+ {
+ name: "string to int map",
+ input: map[string]int{},
+ expected: "object",
+ },
+ {
+ name: "int to string map",
+ input: map[int]string{},
+ expected: "object",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ schema := Generate(reflect.TypeOf(tt.input))
+ require.Equal(t, tt.expected, schema.Type)
+ })
+ }
+}
+
+func TestGenerateSchemaStructTypes(t *testing.T) {
+ t.Parallel()
+
+ type SimpleStruct struct {
+ Name string `json:"name" description:"The name field"`
+ Age int `json:"age"`
+ }
+
+ type StructWithOmitEmpty struct {
+ Required string `json:"required"`
+ Optional string `json:"optional,omitempty"`
+ }
+
+ type StructWithJSONIgnore struct {
+ Visible string `json:"visible"`
+ Hidden string `json:"-"`
+ }
+
+ type StructWithoutJSONTags struct {
+ FirstName string
+ LastName string
+ }
+
+ tests := []struct {
+ name string
+ input any
+ validate func(t *testing.T, schema Schema)
+ }{
+ {
+ name: "simple struct",
+ input: SimpleStruct{},
+ validate: func(t *testing.T, schema Schema) {
+ require.Equal(t, "object", schema.Type)
+ require.Len(t, schema.Properties, 2)
+ require.NotNil(t, schema.Properties["name"], "Expected name property to exist")
+ require.Equal(t, "The name field", schema.Properties["name"].Description)
+ require.Len(t, schema.Required, 2)
+ },
+ },
+ {
+ name: "struct with omitempty",
+ input: StructWithOmitEmpty{},
+ validate: func(t *testing.T, schema Schema) {
+ require.Len(t, schema.Required, 1)
+ require.Equal(t, "required", schema.Required[0])
+ },
+ },
+ {
+ name: "struct with json ignore",
+ input: StructWithJSONIgnore{},
+ validate: func(t *testing.T, schema Schema) {
+ require.Len(t, schema.Properties, 1)
+ require.NotNil(t, schema.Properties["visible"], "Expected visible property to exist")
+ require.Nil(t, schema.Properties["hidden"], "Expected hidden property to not exist")
+ },
+ },
+ {
+ name: "struct without json tags",
+ input: StructWithoutJSONTags{},
+ validate: func(t *testing.T, schema Schema) {
+ require.NotNil(t, schema.Properties["first_name"], "Expected first_name property to exist")
+ require.NotNil(t, schema.Properties["last_name"], "Expected last_name property to exist")
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ schema := Generate(reflect.TypeOf(tt.input))
+ tt.validate(t, schema)
+ })
+ }
+}
+
+func TestGenerateSchemaPointerTypes(t *testing.T) {
+ t.Parallel()
+
+ type StructWithPointers struct {
+ Name *string `json:"name"`
+ Age *int `json:"age"`
+ }
+
+ schema := Generate(reflect.TypeOf(StructWithPointers{}))
+
+ require.Equal(t, "object", schema.Type)
+
+ require.NotNil(t, schema.Properties["name"], "Expected name property to exist")
+ require.Equal(t, "string", schema.Properties["name"].Type)
+
+ require.NotNil(t, schema.Properties["age"], "Expected age property to exist")
+ require.Equal(t, "integer", schema.Properties["age"].Type)
+}
+
+func TestGenerateSchemaNestedStructs(t *testing.T) {
+ t.Parallel()
+
+ type Address struct {
+ Street string `json:"street"`
+ City string `json:"city"`
+ }
+
+ type Person struct {
+ Name string `json:"name"`
+ Address Address `json:"address"`
+ }
+
+ schema := Generate(reflect.TypeOf(Person{}))
+
+ require.Equal(t, "object", schema.Type)
+
+ require.NotNil(t, schema.Properties["address"], "Expected address property to exist")
+
+ addressSchema := schema.Properties["address"]
+ require.Equal(t, "object", addressSchema.Type)
+
+ require.NotNil(t, addressSchema.Properties["street"], "Expected street property in address to exist")
+ require.NotNil(t, addressSchema.Properties["city"], "Expected city property in address to exist")
+}
+
+func TestGenerateSchemaRecursiveStructs(t *testing.T) {
+ t.Parallel()
+
+ type Node struct {
+ Value string `json:"value"`
+ Next *Node `json:"next,omitempty"`
+ }
+
+ schema := Generate(reflect.TypeOf(Node{}))
+
+ require.Equal(t, "object", schema.Type)
+
+ require.NotNil(t, schema.Properties["value"], "Expected value property to exist")
+
+ require.NotNil(t, schema.Properties["next"], "Expected next property to exist")
+
+ // The recursive reference should be handled gracefully
+ nextSchema := schema.Properties["next"]
+ require.Equal(t, "object", nextSchema.Type)
+}
+
+func TestGenerateSchemaWithEnumTags(t *testing.T) {
+ t.Parallel()
+
+ type ConfigInput struct {
+ Level string `json:"level" enum:"debug,info,warn,error" description:"Log level"`
+ Format string `json:"format" enum:"json,text"`
+ Optional string `json:"optional,omitempty" enum:"a,b,c"`
+ }
+
+ schema := Generate(reflect.TypeOf(ConfigInput{}))
+
+ // Check level field
+ levelSchema := schema.Properties["level"]
+ require.NotNil(t, levelSchema, "Expected level property to exist")
+ require.Len(t, levelSchema.Enum, 4)
+ expectedLevels := []string{"debug", "info", "warn", "error"}
+ for i, expected := range expectedLevels {
+ require.Equal(t, expected, levelSchema.Enum[i])
+ }
+
+ // Check format field
+ formatSchema := schema.Properties["format"]
+ require.NotNil(t, formatSchema, "Expected format property to exist")
+ require.Len(t, formatSchema.Enum, 2)
+
+ // Check required fields (optional should not be required due to omitempty)
+ expectedRequired := []string{"level", "format"}
+ require.Len(t, schema.Required, len(expectedRequired))
+}
+
+func TestGenerateSchemaComplexTypes(t *testing.T) {
+ t.Parallel()
+
+ type ComplexInput struct {
+ StringSlice []string `json:"string_slice"`
+ IntMap map[string]int `json:"int_map"`
+ NestedSlice []map[string]string `json:"nested_slice"`
+ Interface any `json:"interface"`
+ }
+
+ schema := Generate(reflect.TypeOf(ComplexInput{}))
+
+ // Check string slice
+ stringSliceSchema := schema.Properties["string_slice"]
+ require.NotNil(t, stringSliceSchema, "Expected string_slice property to exist")
+ require.Equal(t, "array", stringSliceSchema.Type)
+ require.Equal(t, "string", stringSliceSchema.Items.Type)
+
+ // Check int map
+ intMapSchema := schema.Properties["int_map"]
+ require.NotNil(t, intMapSchema, "Expected int_map property to exist")
+ require.Equal(t, "object", intMapSchema.Type)
+
+ // Check nested slice
+ nestedSliceSchema := schema.Properties["nested_slice"]
+ require.NotNil(t, nestedSliceSchema, "Expected nested_slice property to exist")
+ require.Equal(t, "array", nestedSliceSchema.Type)
+ require.Equal(t, "object", nestedSliceSchema.Items.Type)
+
+ // Check interface
+ interfaceSchema := schema.Properties["interface"]
+ require.NotNil(t, interfaceSchema, "Expected interface property to exist")
+ require.Equal(t, "object", interfaceSchema.Type)
+}
+
+func TestToSnakeCase(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"FirstName", "first_name"},
+ {"XMLHttpRequest", "x_m_l_http_request"},
+ {"ID", "i_d"},
+ {"HTTPSProxy", "h_t_t_p_s_proxy"},
+ {"simple", "simple"},
+ {"", ""},
+ {"A", "a"},
+ {"AB", "a_b"},
+ {"CamelCase", "camel_case"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.input, func(t *testing.T) {
+ t.Parallel()
+ result := toSnakeCase(tt.input)
+ require.Equal(t, tt.expected, result, "toSnakeCase(%s)", tt.input)
+ })
+ }
+}
+
+func TestSchemaToParametersEdgeCases(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ schema Schema
+ expected map[string]any
+ }{
+ {
+ name: "non-object schema",
+ schema: Schema{
+ Type: "string",
+ },
+ expected: map[string]any{},
+ },
+ {
+ name: "object with no properties",
+ schema: Schema{
+ Type: "object",
+ Properties: nil,
+ },
+ expected: map[string]any{},
+ },
+ {
+ name: "object with empty properties",
+ schema: Schema{
+ Type: "object",
+ Properties: map[string]*Schema{},
+ },
+ expected: map[string]any{},
+ },
+ {
+ name: "schema with all constraint types",
+ schema: Schema{
+ Type: "object",
+ Properties: map[string]*Schema{
+ "text": {
+ Type: "string",
+ Format: "email",
+ MinLength: func() *int { v := 5; return &v }(),
+ MaxLength: func() *int { v := 100; return &v }(),
+ },
+ "number": {
+ Type: "number",
+ Minimum: func() *float64 { v := 0.0; return &v }(),
+ Maximum: func() *float64 { v := 100.0; return &v }(),
+ },
+ },
+ },
+ expected: map[string]any{
+ "text": map[string]any{
+ "type": "string",
+ "format": "email",
+ "minLength": 5,
+ "maxLength": 100,
+ },
+ "number": map[string]any{
+ "type": "number",
+ "minimum": 0.0,
+ "maximum": 100.0,
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ result := ToParameters(tt.schema)
+ require.Len(t, result, len(tt.expected))
+ for key, expectedValue := range tt.expected {
+ require.NotNil(t, result[key], "Expected parameter %s to exist", key)
+ // Deep comparison would be complex, so we'll check key properties
+ resultParam := result[key].(map[string]any)
+ expectedParam := expectedValue.(map[string]any)
+ for propKey, propValue := range expectedParam {
+ require.Equal(t, propValue, resultParam[propKey], "Expected %s.%s", key, propKey)
+ }
+ }
+ })
+ }
+}
@@ -5,24 +5,12 @@ import (
"encoding/json"
"fmt"
"reflect"
- "slices"
- "strings"
+
+ "charm.land/fantasy/schema"
)
// Schema represents a JSON schema for tool input validation.
-type Schema struct {
- Type string `json:"type,omitempty"`
- Properties map[string]*Schema `json:"properties,omitempty"`
- Required []string `json:"required,omitempty"`
- Items *Schema `json:"items,omitempty"`
- Description string `json:"description,omitempty"`
- Enum []any `json:"enum,omitempty"`
- Format string `json:"format,omitempty"`
- Minimum *float64 `json:"minimum,omitempty"`
- Maximum *float64 `json:"maximum,omitempty"`
- MinLength *int `json:"minLength,omitempty"`
- MaxLength *int `json:"maxLength,omitempty"`
-}
+type Schema = schema.Schema
// ToolInfo represents tool metadata, matching the existing pattern.
type ToolInfo struct {
@@ -93,7 +81,7 @@ func NewAgentTool[TInput any](
fn func(ctx context.Context, input TInput, call ToolCall) (ToolResponse, error),
) AgentTool {
var input TInput
- schema := generateSchema(reflect.TypeOf(input))
+ schema := schema.Generate(reflect.TypeOf(input))
return &funcToolWrapper[TInput]{
name: name,
@@ -127,7 +115,7 @@ func (w *funcToolWrapper[TInput]) Info() ToolInfo {
return ToolInfo{
Name: w.name,
Description: w.description,
- Parameters: schemaToParameters(w.schema),
+ Parameters: schema.ToParameters(w.schema),
Required: w.schema.Required,
}
}
@@ -140,201 +128,3 @@ func (w *funcToolWrapper[TInput]) Run(ctx context.Context, params ToolCall) (Too
return w.fn(ctx, input, params)
}
-
-// schemaToParameters converts a Schema to the parameters map format expected by ToolInfo.
-func schemaToParameters(schema Schema) map[string]any {
- if schema.Type != "object" || schema.Properties == nil {
- return map[string]any{}
- }
-
- params := make(map[string]any)
- for name, propSchema := range schema.Properties {
- param := map[string]any{
- "type": propSchema.Type,
- }
-
- if propSchema.Description != "" {
- param["description"] = propSchema.Description
- }
-
- if len(propSchema.Enum) > 0 {
- param["enum"] = propSchema.Enum
- }
-
- if propSchema.Format != "" {
- param["format"] = propSchema.Format
- }
-
- if propSchema.Minimum != nil {
- param["minimum"] = *propSchema.Minimum
- }
-
- if propSchema.Maximum != nil {
- param["maximum"] = *propSchema.Maximum
- }
-
- if propSchema.MinLength != nil {
- param["minLength"] = *propSchema.MinLength
- }
-
- if propSchema.MaxLength != nil {
- param["maxLength"] = *propSchema.MaxLength
- }
-
- if propSchema.Items != nil {
- param["items"] = schemaToParameters(*propSchema.Items)
- }
-
- params[name] = param
- }
-
- return params
-}
-
-// generateSchema automatically generates a JSON schema from a Go type.
-func generateSchema(t reflect.Type) Schema {
- return generateSchemaRecursive(t, nil, make(map[reflect.Type]bool))
-}
-
-func generateSchemaRecursive(t, parent reflect.Type, visited map[reflect.Type]bool) Schema {
- // Handle pointers
- if t.Kind() == reflect.Pointer {
- t = t.Elem()
- }
-
- // Prevent infinite recursion
- if visited[t] {
- return Schema{Type: "object"}
- }
- visited[t] = true
- defer delete(visited, t)
-
- switch t.Kind() {
- case reflect.String:
- return Schema{Type: "string"}
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return Schema{Type: "integer"}
- case reflect.Float32, reflect.Float64:
- return Schema{Type: "number"}
- case reflect.Bool:
- return Schema{Type: "boolean"}
- case reflect.Slice, reflect.Array:
- itemSchema := generateSchemaRecursive(t.Elem(), t, visited)
- return Schema{
- Type: "array",
- Items: &itemSchema,
- }
- case reflect.Map:
- if t.Key().Kind() == reflect.String {
- valueSchema := generateSchemaRecursive(t.Elem(), t, visited)
- schema := Schema{
- Type: "object",
- Properties: map[string]*Schema{
- "*": &valueSchema,
- },
- }
- if useBlankType(parent) {
- schema.Type = ""
- }
- return schema
- }
- return Schema{Type: "object"}
- case reflect.Struct:
- schema := Schema{
- Type: "object",
- Properties: make(map[string]*Schema),
- }
- if useBlankType(parent) {
- schema.Type = ""
- }
-
- for i := range t.NumField() {
- field := t.Field(i)
-
- // Skip unexported fields
- if !field.IsExported() {
- continue
- }
-
- jsonTag := field.Tag.Get("json")
- if jsonTag == "-" {
- continue
- }
-
- fieldName := field.Name
- required := true
-
- // Parse JSON tag
- if jsonTag != "" {
- parts := strings.Split(jsonTag, ",")
- if parts[0] != "" {
- fieldName = parts[0]
- }
-
- // Check for omitempty
- if slices.Contains(parts[1:], "omitempty") {
- required = false
- }
- } else {
- // Convert field name to snake_case for JSON
- fieldName = toSnakeCase(fieldName)
- }
-
- fieldSchema := generateSchemaRecursive(field.Type, t, visited)
-
- // Add description from struct tag if available
- if desc := field.Tag.Get("description"); desc != "" {
- fieldSchema.Description = desc
- }
-
- // Add enum values from struct tag if available
- if enumTag := field.Tag.Get("enum"); enumTag != "" {
- enumValues := strings.Split(enumTag, ",")
- fieldSchema.Enum = make([]any, len(enumValues))
- for i, v := range enumValues {
- fieldSchema.Enum[i] = strings.TrimSpace(v)
- }
- }
-
- schema.Properties[fieldName] = &fieldSchema
-
- if required {
- schema.Required = append(schema.Required, fieldName)
- }
- }
-
- return schema
- case reflect.Interface:
- return Schema{Type: "object"}
- default:
- return Schema{Type: "object"}
- }
-}
-
-// toSnakeCase converts PascalCase to snake_case.
-func toSnakeCase(s string) string {
- var result strings.Builder
- for i, r := range s {
- if i > 0 && r >= 'A' && r <= 'Z' {
- result.WriteByte('_')
- }
- result.WriteRune(r)
- }
- return strings.ToLower(result.String())
-}
-
-// NOTE(@andreynering): This is a hacky workaround for llama.cpp.
-// Ideally, we should always output `type: object` for objects, but
-// llama.cpp complains if we do for arrays of objects.
-func useBlankType(parent reflect.Type) bool {
- if parent == nil {
- return false
- }
- switch parent.Kind() {
- case reflect.Slice, reflect.Array:
- return true
- default:
- return false
- }
-}
@@ -3,7 +3,6 @@ package fantasy
import (
"context"
"fmt"
- "reflect"
"testing"
"github.com/stretchr/testify/require"
@@ -64,7 +63,6 @@ func TestEnumToolExample(t *testing.T) {
return NewTextResponse(fmt.Sprintf("Weather in %s: %s, sunny", input.Location, temp)), nil
},
)
-
// Check that the schema includes enum values
info := tool.Info()
unitsParam, ok := info.Parameters["units"].(map[string]any)
@@ -85,529 +83,3 @@ func TestEnumToolExample(t *testing.T) {
require.Contains(t, result.Content, "San Francisco")
require.Contains(t, result.Content, "72°F")
}
-
-func TestEnumSupport(t *testing.T) {
- // Test enum via struct tags
- type WeatherInput struct {
- Location string `json:"location" description:"City name"`
- Units string `json:"units" enum:"celsius,fahrenheit,kelvin" description:"Temperature units"`
- Format string `json:"format,omitempty" enum:"json,xml,text"`
- }
-
- schema := generateSchema(reflect.TypeOf(WeatherInput{}))
-
- require.Equal(t, "object", schema.Type)
-
- // Check units field has enum values
- unitsSchema := schema.Properties["units"]
- require.NotNil(t, unitsSchema, "Expected units property to exist")
- require.Len(t, unitsSchema.Enum, 3)
- expectedUnits := []string{"celsius", "fahrenheit", "kelvin"}
- for i, expected := range expectedUnits {
- require.Equal(t, expected, unitsSchema.Enum[i])
- }
-
- // Check required fields (format should not be required due to omitempty)
- expectedRequired := []string{"location", "units"}
- require.Len(t, schema.Required, len(expectedRequired))
-}
-
-func TestSchemaToParameters(t *testing.T) {
- schema := Schema{
- Type: "object",
- Properties: map[string]*Schema{
- "name": {
- Type: "string",
- Description: "The name field",
- },
- "age": {
- Type: "integer",
- Minimum: func() *float64 { v := 0.0; return &v }(),
- Maximum: func() *float64 { v := 120.0; return &v }(),
- },
- "tags": {
- Type: "array",
- Items: &Schema{
- Type: "string",
- },
- },
- "priority": {
- Type: "string",
- Enum: []any{"low", "medium", "high"},
- },
- },
- Required: []string{"name"},
- }
-
- params := schemaToParameters(schema)
-
- // Check name parameter
- nameParam, ok := params["name"].(map[string]any)
- require.True(t, ok, "Expected name parameter to exist")
- require.Equal(t, "string", nameParam["type"])
- require.Equal(t, "The name field", nameParam["description"])
-
- // Check age parameter with min/max
- ageParam, ok := params["age"].(map[string]any)
- require.True(t, ok, "Expected age parameter to exist")
- require.Equal(t, "integer", ageParam["type"])
- require.Equal(t, 0.0, ageParam["minimum"])
- require.Equal(t, 120.0, ageParam["maximum"])
-
- // Check priority parameter with enum
- priorityParam, ok := params["priority"].(map[string]any)
- require.True(t, ok, "Expected priority parameter to exist")
- require.Equal(t, "string", priorityParam["type"])
- enumValues, ok := priorityParam["enum"].([]any)
- require.True(t, ok)
- require.Len(t, enumValues, 3)
-}
-
-func TestGenerateSchemaBasicTypes(t *testing.T) {
- t.Parallel()
-
- tests := []struct {
- name string
- input any
- expected Schema
- }{
- {
- name: "string type",
- input: "",
- expected: Schema{Type: "string"},
- },
- {
- name: "int type",
- input: 0,
- expected: Schema{Type: "integer"},
- },
- {
- name: "int64 type",
- input: int64(0),
- expected: Schema{Type: "integer"},
- },
- {
- name: "uint type",
- input: uint(0),
- expected: Schema{Type: "integer"},
- },
- {
- name: "float64 type",
- input: 0.0,
- expected: Schema{Type: "number"},
- },
- {
- name: "float32 type",
- input: float32(0.0),
- expected: Schema{Type: "number"},
- },
- {
- name: "bool type",
- input: false,
- expected: Schema{Type: "boolean"},
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- schema := generateSchema(reflect.TypeOf(tt.input))
- require.Equal(t, tt.expected.Type, schema.Type)
- })
- }
-}
-
-func TestGenerateSchemaArrayTypes(t *testing.T) {
- t.Parallel()
-
- tests := []struct {
- name string
- input any
- expected Schema
- }{
- {
- name: "string slice",
- input: []string{},
- expected: Schema{
- Type: "array",
- Items: &Schema{Type: "string"},
- },
- },
- {
- name: "int slice",
- input: []int{},
- expected: Schema{
- Type: "array",
- Items: &Schema{Type: "integer"},
- },
- },
- {
- name: "string array",
- input: [3]string{},
- expected: Schema{
- Type: "array",
- Items: &Schema{Type: "string"},
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- schema := generateSchema(reflect.TypeOf(tt.input))
- require.Equal(t, tt.expected.Type, schema.Type)
- require.NotNil(t, schema.Items, "Expected items schema to exist")
- require.Equal(t, tt.expected.Items.Type, schema.Items.Type)
- })
- }
-}
-
-func TestGenerateSchemaMapTypes(t *testing.T) {
- t.Parallel()
-
- tests := []struct {
- name string
- input any
- expected string
- }{
- {
- name: "string to string map",
- input: map[string]string{},
- expected: "object",
- },
- {
- name: "string to int map",
- input: map[string]int{},
- expected: "object",
- },
- {
- name: "int to string map",
- input: map[int]string{},
- expected: "object",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- schema := generateSchema(reflect.TypeOf(tt.input))
- require.Equal(t, tt.expected, schema.Type)
- })
- }
-}
-
-func TestGenerateSchemaStructTypes(t *testing.T) {
- t.Parallel()
-
- type SimpleStruct struct {
- Name string `json:"name" description:"The name field"`
- Age int `json:"age"`
- }
-
- type StructWithOmitEmpty struct {
- Required string `json:"required"`
- Optional string `json:"optional,omitempty"`
- }
-
- type StructWithJSONIgnore struct {
- Visible string `json:"visible"`
- Hidden string `json:"-"`
- }
-
- type StructWithoutJSONTags struct {
- FirstName string
- LastName string
- }
-
- tests := []struct {
- name string
- input any
- validate func(t *testing.T, schema Schema)
- }{
- {
- name: "simple struct",
- input: SimpleStruct{},
- validate: func(t *testing.T, schema Schema) {
- require.Equal(t, "object", schema.Type)
- require.Len(t, schema.Properties, 2)
- require.NotNil(t, schema.Properties["name"], "Expected name property to exist")
- require.Equal(t, "The name field", schema.Properties["name"].Description)
- require.Len(t, schema.Required, 2)
- },
- },
- {
- name: "struct with omitempty",
- input: StructWithOmitEmpty{},
- validate: func(t *testing.T, schema Schema) {
- require.Len(t, schema.Required, 1)
- require.Equal(t, "required", schema.Required[0])
- },
- },
- {
- name: "struct with json ignore",
- input: StructWithJSONIgnore{},
- validate: func(t *testing.T, schema Schema) {
- require.Len(t, schema.Properties, 1)
- require.NotNil(t, schema.Properties["visible"], "Expected visible property to exist")
- require.Nil(t, schema.Properties["hidden"], "Expected hidden property to not exist")
- },
- },
- {
- name: "struct without json tags",
- input: StructWithoutJSONTags{},
- validate: func(t *testing.T, schema Schema) {
- require.NotNil(t, schema.Properties["first_name"], "Expected first_name property to exist")
- require.NotNil(t, schema.Properties["last_name"], "Expected last_name property to exist")
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- schema := generateSchema(reflect.TypeOf(tt.input))
- tt.validate(t, schema)
- })
- }
-}
-
-func TestGenerateSchemaPointerTypes(t *testing.T) {
- t.Parallel()
-
- type StructWithPointers struct {
- Name *string `json:"name"`
- Age *int `json:"age"`
- }
-
- schema := generateSchema(reflect.TypeOf(StructWithPointers{}))
-
- require.Equal(t, "object", schema.Type)
-
- require.NotNil(t, schema.Properties["name"], "Expected name property to exist")
- require.Equal(t, "string", schema.Properties["name"].Type)
-
- require.NotNil(t, schema.Properties["age"], "Expected age property to exist")
- require.Equal(t, "integer", schema.Properties["age"].Type)
-}
-
-func TestGenerateSchemaNestedStructs(t *testing.T) {
- t.Parallel()
-
- type Address struct {
- Street string `json:"street"`
- City string `json:"city"`
- }
-
- type Person struct {
- Name string `json:"name"`
- Address Address `json:"address"`
- }
-
- schema := generateSchema(reflect.TypeOf(Person{}))
-
- require.Equal(t, "object", schema.Type)
-
- require.NotNil(t, schema.Properties["address"], "Expected address property to exist")
-
- addressSchema := schema.Properties["address"]
- require.Equal(t, "object", addressSchema.Type)
-
- require.NotNil(t, addressSchema.Properties["street"], "Expected street property in address to exist")
- require.NotNil(t, addressSchema.Properties["city"], "Expected city property in address to exist")
-}
-
-func TestGenerateSchemaRecursiveStructs(t *testing.T) {
- t.Parallel()
-
- type Node struct {
- Value string `json:"value"`
- Next *Node `json:"next,omitempty"`
- }
-
- schema := generateSchema(reflect.TypeOf(Node{}))
-
- require.Equal(t, "object", schema.Type)
-
- require.NotNil(t, schema.Properties["value"], "Expected value property to exist")
-
- require.NotNil(t, schema.Properties["next"], "Expected next property to exist")
-
- // The recursive reference should be handled gracefully
- nextSchema := schema.Properties["next"]
- require.Equal(t, "object", nextSchema.Type)
-}
-
-func TestGenerateSchemaWithEnumTags(t *testing.T) {
- t.Parallel()
-
- type ConfigInput struct {
- Level string `json:"level" enum:"debug,info,warn,error" description:"Log level"`
- Format string `json:"format" enum:"json,text"`
- Optional string `json:"optional,omitempty" enum:"a,b,c"`
- }
-
- schema := generateSchema(reflect.TypeOf(ConfigInput{}))
-
- // Check level field
- levelSchema := schema.Properties["level"]
- require.NotNil(t, levelSchema, "Expected level property to exist")
- require.Len(t, levelSchema.Enum, 4)
- expectedLevels := []string{"debug", "info", "warn", "error"}
- for i, expected := range expectedLevels {
- require.Equal(t, expected, levelSchema.Enum[i])
- }
-
- // Check format field
- formatSchema := schema.Properties["format"]
- require.NotNil(t, formatSchema, "Expected format property to exist")
- require.Len(t, formatSchema.Enum, 2)
-
- // Check required fields (optional should not be required due to omitempty)
- expectedRequired := []string{"level", "format"}
- require.Len(t, schema.Required, len(expectedRequired))
-}
-
-func TestGenerateSchemaComplexTypes(t *testing.T) {
- t.Parallel()
-
- type ComplexInput struct {
- StringSlice []string `json:"string_slice"`
- IntMap map[string]int `json:"int_map"`
- NestedSlice []map[string]string `json:"nested_slice"`
- Interface any `json:"interface"`
- }
-
- schema := generateSchema(reflect.TypeOf(ComplexInput{}))
-
- // Check string slice
- stringSliceSchema := schema.Properties["string_slice"]
- require.NotNil(t, stringSliceSchema, "Expected string_slice property to exist")
- require.Equal(t, "array", stringSliceSchema.Type)
- require.Equal(t, "string", stringSliceSchema.Items.Type)
-
- // Check int map
- intMapSchema := schema.Properties["int_map"]
- require.NotNil(t, intMapSchema, "Expected int_map property to exist")
- require.Equal(t, "object", intMapSchema.Type)
-
- // Check nested slice
- nestedSliceSchema := schema.Properties["nested_slice"]
- require.NotNil(t, nestedSliceSchema, "Expected nested_slice property to exist")
- require.Equal(t, "array", nestedSliceSchema.Type)
- require.Equal(t, "", nestedSliceSchema.Items.Type)
-
- // Check interface
- interfaceSchema := schema.Properties["interface"]
- require.NotNil(t, interfaceSchema, "Expected interface property to exist")
- require.Equal(t, "object", interfaceSchema.Type)
-}
-
-func TestToSnakeCase(t *testing.T) {
- t.Parallel()
-
- tests := []struct {
- input string
- expected string
- }{
- {"FirstName", "first_name"},
- {"XMLHttpRequest", "x_m_l_http_request"},
- {"ID", "i_d"},
- {"HTTPSProxy", "h_t_t_p_s_proxy"},
- {"simple", "simple"},
- {"", ""},
- {"A", "a"},
- {"AB", "a_b"},
- {"CamelCase", "camel_case"},
- }
-
- for _, tt := range tests {
- t.Run(tt.input, func(t *testing.T) {
- t.Parallel()
- result := toSnakeCase(tt.input)
- require.Equal(t, tt.expected, result, "toSnakeCase(%s)", tt.input)
- })
- }
-}
-
-func TestSchemaToParametersEdgeCases(t *testing.T) {
- t.Parallel()
-
- tests := []struct {
- name string
- schema Schema
- expected map[string]any
- }{
- {
- name: "non-object schema",
- schema: Schema{
- Type: "string",
- },
- expected: map[string]any{},
- },
- {
- name: "object with no properties",
- schema: Schema{
- Type: "object",
- Properties: nil,
- },
- expected: map[string]any{},
- },
- {
- name: "object with empty properties",
- schema: Schema{
- Type: "object",
- Properties: map[string]*Schema{},
- },
- expected: map[string]any{},
- },
- {
- name: "schema with all constraint types",
- schema: Schema{
- Type: "object",
- Properties: map[string]*Schema{
- "text": {
- Type: "string",
- Format: "email",
- MinLength: func() *int { v := 5; return &v }(),
- MaxLength: func() *int { v := 100; return &v }(),
- },
- "number": {
- Type: "number",
- Minimum: func() *float64 { v := 0.0; return &v }(),
- Maximum: func() *float64 { v := 100.0; return &v }(),
- },
- },
- },
- expected: map[string]any{
- "text": map[string]any{
- "type": "string",
- "format": "email",
- "minLength": 5,
- "maxLength": 100,
- },
- "number": map[string]any{
- "type": "number",
- "minimum": 0.0,
- "maximum": 100.0,
- },
- },
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- t.Parallel()
- result := schemaToParameters(tt.schema)
- require.Len(t, result, len(tt.expected))
- for key, expectedValue := range tt.expected {
- require.NotNil(t, result[key], "Expected parameter %s to exist", key)
- // Deep comparison would be complex, so we'll check key properties
- resultParam := result[key].(map[string]any)
- expectedParam := expectedValue.(map[string]any)
- for propKey, propValue := range expectedParam {
- require.Equal(t, propValue, resultParam[propKey], "Expected %s.%s", key, propKey)
- }
- }
- })
- }
-}