chore: initial rework of tests

kujtimiihoxha created

Change summary

providertests/anthropic_test.go                                                            |  25 
providertests/builders_test.go                                                             | 101 
providertests/common_test.go                                                               | 226 
providertests/google_test.go                                                               |  35 
providertests/openai_test.go                                                               |  43 
providertests/opernrouter_test.go                                                          |  78 
providertests/provider_test.go                                                             | 378 
providertests/testdata/TestAnthropicCommon/simple_claude-sonnet-4.yaml                     |  33 
providertests/testdata/TestAnthropicCommon/simple_streaming_claude-sonnet-4.yaml           |  62 
providertests/testdata/TestGoogleCommon/simple_gemini-2.5-flash.yaml                       |  62 
providertests/testdata/TestGoogleCommon/simple_gemini-2.5-pro.yaml                         |  62 
providertests/testdata/TestGoogleCommon/simple_streaming_gemini-2.5-flash.yaml             |  34 
providertests/testdata/TestGoogleCommon/simple_streaming_gemini-2.5-pro.yaml               |  27 
providertests/testdata/TestOpenAICommon/simple_gpt-4o-mini.yaml                            |  69 
providertests/testdata/TestOpenAICommon/simple_gpt-4o.yaml                                 |  69 
providertests/testdata/TestOpenAICommon/simple_gpt-5.yaml                                  |  68 
providertests/testdata/TestOpenAICommon/simple_streaming_gpt-4o-mini.yaml                  |  44 
providertests/testdata/TestOpenAICommon/simple_streaming_gpt-4o.yaml                       |  44 
providertests/testdata/TestOpenAICommon/simple_streaming_gpt-5.yaml                        |  44 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_baseten.yaml                    |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_chutes.yaml                     |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_deepinfra.yaml                  |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_fireworks.yaml                  |  26 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_moonshotai.yaml                 |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_moonshotai/turbo.yaml           |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_novita.yaml                     |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_siliconflow.yaml                |  62 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_together.yaml                   |  33 
providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_wandb.yaml                      |  62 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_baseten.yaml          |  44 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_chutes.yaml           |  46 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_deepinfra.yaml        |  44 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_fireworks.yaml        |  42 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_moonshotai.yaml       |  46 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_moonshotai/turbo.yaml |  46 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_novita.yaml           |  44 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_siliconflow.yaml      |  44 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_together.yaml         |  46 
providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_wandb.yaml            |  46 
providertests/testdata/TestSimple/anthropic-claude-sonnet.yaml                             |  33 
providertests/testdata/TestSimple/google-gemini-2.5-flash.yaml                             |  31 
providertests/testdata/TestSimple/google-gemini-2.5-pro.yaml                               |  31 
providertests/testdata/TestSimple/openai-gpt-4o-mini.yaml                                  |  33 
providertests/testdata/TestSimple/openai-gpt-4o.yaml                                       |  33 
providertests/testdata/TestSimple/openrouter-kimi-k2.yaml                                  |  33 
providertests/testdata/TestStream/anthropic-claude-sonnet.yaml                             |  25 
providertests/testdata/TestStream/google-gemini-2.5-flash.yaml                             |  33 
providertests/testdata/TestStream/google-gemini-2.5-pro.yaml                               |  33 
providertests/testdata/TestStream/openai-gpt-4o-mini.yaml                                  |  25 
providertests/testdata/TestStream/openai-gpt-4o.yaml                                       |  25 
providertests/testdata/TestStream/openrouter-kimi-k2.yaml                                  |  25 
providertests/testdata/TestStreamWithMultipleTools/anthropic-claude-sonnet.yaml            |  25 
providertests/testdata/TestStreamWithMultipleTools/google-gemini-2.5-flash.yaml            |  40 
providertests/testdata/TestStreamWithMultipleTools/google-gemini-2.5-pro.yaml              |  26 
providertests/testdata/TestStreamWithMultipleTools/openai-gpt-4o-mini.yaml                 |  25 
providertests/testdata/TestStreamWithMultipleTools/openai-gpt-4o.yaml                      |  25 
providertests/testdata/TestStreamWithMultipleTools/openrouter-kimi-k2.yaml                 |  10 
providertests/testdata/TestStreamWithTools/anthropic-claude-sonnet.yaml                    |  25 
providertests/testdata/TestStreamWithTools/google-gemini-2.5-flash.yaml                    |  26 
providertests/testdata/TestStreamWithTools/google-gemini-2.5-pro.yaml                      |  26 
providertests/testdata/TestStreamWithTools/openai-gpt-4o-mini.yaml                         |  25 
providertests/testdata/TestStreamWithTools/openai-gpt-4o.yaml                              |  25 
providertests/testdata/TestStreamWithTools/openrouter-kimi-k2.yaml                         |  25 
providertests/testdata/TestThinking/anthropic-claude-sonnet.yaml                           |  26 
providertests/testdata/TestThinking/google-gemini-2.5-pro.yaml                             |  24 
providertests/testdata/TestThinking/openai-gpt-5.yaml                                      |  26 
providertests/testdata/TestThinking/openrouter-glm-4.5.yaml                                |  26 
providertests/testdata/TestThinkingStreaming/anthropic-claude-sonnet.yaml                  |  25 
providertests/testdata/TestThinkingStreaming/google-gemini-2.5-pro.yaml                    |  26 
providertests/testdata/TestThinkingStreaming/openai-gpt-5.yaml                             |  25 
providertests/testdata/TestThinkingStreaming/openrouter-glm-4.5.yaml                       |  25 
providertests/testdata/TestTool/anthropic-claude-sonnet.yaml                               |  63 
providertests/testdata/TestTool/google-gemini-2.5-flash.yaml                               |  24 
providertests/testdata/TestTool/google-gemini-2.5-pro.yaml                                 |  24 
providertests/testdata/TestTool/openai-gpt-4o-mini.yaml                                    |  26 
providertests/testdata/TestTool/openai-gpt-4o.yaml                                         |  26 
providertests/testdata/TestTool/openrouter-kimi-k2.yaml                                    |  26 
77 files changed, 1,854 insertions(+), 1,534 deletions(-)

Detailed changes

providertests/anthropic_test.go ๐Ÿ”—

@@ -0,0 +1,25 @@
+package providertests
+
+import (
+	"net/http"
+	"os"
+	"testing"
+
+	"github.com/charmbracelet/fantasy/ai"
+	"github.com/charmbracelet/fantasy/anthropic"
+	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
+)
+
+func TestAnthropicCommon(t *testing.T) {
+	testCommon(t, []builderPair{
+		{"claude-sonnet-4", builderAnthropicClaudeSonnet4, nil},
+	})
+}
+
+func builderAnthropicClaudeSonnet4(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := anthropic.New(
+		anthropic.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
+		anthropic.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("claude-sonnet-4-20250514")
+}

providertests/builders_test.go ๐Ÿ”—

@@ -1,101 +0,0 @@
-package providertests
-
-import (
-	"cmp"
-	"net/http"
-	"os"
-
-	"github.com/charmbracelet/fantasy/ai"
-	"github.com/charmbracelet/fantasy/anthropic"
-	"github.com/charmbracelet/fantasy/google"
-	"github.com/charmbracelet/fantasy/openai"
-	"github.com/charmbracelet/fantasy/openrouter"
-	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
-)
-
-type builderFunc func(r *recorder.Recorder) (ai.LanguageModel, error)
-
-type builderPair struct {
-	name    string
-	builder builderFunc
-}
-
-var languageModelBuilders = []builderPair{
-	{"openai-gpt-4o", builderOpenaiGpt4o},
-	{"openai-gpt-4o-mini", builderOpenaiGpt4oMini},
-	{"anthropic-claude-sonnet", builderAnthropicClaudeSonnet4},
-	{"google-gemini-2.5-flash", builderGoogleGemini25Flash},
-	{"google-gemini-2.5-pro", builderGoogleGemini25Pro},
-	{"openrouter-kimi-k2", builderOpenrouterKimiK2},
-}
-
-var thinkingLanguageModelBuilders = []builderPair{
-	{"openai-gpt-5", builderOpenaiGpt5},
-	{"anthropic-claude-sonnet", builderAnthropicClaudeSonnet4},
-	{"google-gemini-2.5-pro", builderGoogleGemini25Pro},
-	{"openrouter-glm-4.5", builderOpenrouterGLM45},
-}
-
-func builderOpenaiGpt4o(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := openai.New(
-		openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
-		openai.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("gpt-4o")
-}
-
-func builderOpenaiGpt4oMini(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := openai.New(
-		openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
-		openai.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("gpt-4o-mini")
-}
-
-func builderOpenaiGpt5(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := openai.New(
-		openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
-		openai.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("gpt-5")
-}
-
-func builderAnthropicClaudeSonnet4(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := anthropic.New(
-		anthropic.WithAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
-		anthropic.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("claude-sonnet-4-20250514")
-}
-
-func builderGoogleGemini25Flash(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := google.New(
-		google.WithAPIKey(cmp.Or(os.Getenv("GEMINI_API_KEY"), "(missing)")),
-		google.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("gemini-2.5-flash")
-}
-
-func builderGoogleGemini25Pro(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := google.New(
-		google.WithAPIKey(cmp.Or(os.Getenv("GEMINI_API_KEY"), "(missing)")),
-		google.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("gemini-2.5-pro")
-}
-
-func builderOpenrouterKimiK2(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := openrouter.New(
-		openrouter.WithAPIKey(os.Getenv("OPENROUTER_API_KEY")),
-		openrouter.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("moonshotai/kimi-k2-0905")
-}
-
-func builderOpenrouterGLM45(r *recorder.Recorder) (ai.LanguageModel, error) {
-	provider := openrouter.New(
-		openrouter.WithAPIKey(os.Getenv("OPENROUTER_API_KEY")),
-		openrouter.WithHTTPClient(&http.Client{Transport: r}),
-	)
-	return provider.LanguageModel("z-ai/glm-4.5")
-}

providertests/common_test.go ๐Ÿ”—

@@ -0,0 +1,226 @@
+package providertests
+
+import (
+	"context"
+	"strconv"
+	"strings"
+	"testing"
+
+	"github.com/charmbracelet/fantasy/ai"
+	"github.com/stretchr/testify/require"
+	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
+)
+
+type builderFunc func(r *recorder.Recorder) (ai.LanguageModel, error)
+
+type builderPair struct {
+	name            string
+	builder         builderFunc
+	providerOptions ai.ProviderOptions
+}
+
+func testCommon(t *testing.T, pairs []builderPair) {
+	for _, pair := range pairs {
+		testSimple(t, pair)
+	}
+}
+
+func testSimple(t *testing.T, pair builderPair) {
+	checkResult := func(t *testing.T, result *ai.AgentResult) {
+		option1 := "Oi"
+		option2 := "Olรก"
+		got := result.Response.Content.Text()
+		require.True(t, strings.Contains(got, option1) || strings.Contains(got, option2), "unexpected response: got %q, want %q or %q", got, option1, option2)
+	}
+
+	t.Run("simple "+pair.name, func(t *testing.T) {
+		r := newRecorder(t)
+
+		languageModel, err := pair.builder(r)
+		require.NoError(t, err, "failed to build language model")
+
+		agent := ai.NewAgent(
+			languageModel,
+			ai.WithSystemPrompt("You are a helpful assistant"),
+		)
+		result, err := agent.Generate(t.Context(), ai.AgentCall{
+			Prompt:          "Say hi in Portuguese",
+			ProviderOptions: pair.providerOptions,
+			MaxOutputTokens: ai.IntOption(4000),
+		})
+		require.NoError(t, err, "failed to generate")
+		checkResult(t, result)
+	})
+	t.Run("simple streaming "+pair.name, func(t *testing.T) {
+		r := newRecorder(t)
+
+		languageModel, err := pair.builder(r)
+		require.NoError(t, err, "failed to build language model")
+
+		agent := ai.NewAgent(
+			languageModel,
+			ai.WithSystemPrompt("You are a helpful assistant"),
+		)
+		result, err := agent.Stream(t.Context(), ai.AgentStreamCall{
+			Prompt:          "Say hi in Portuguese",
+			ProviderOptions: pair.providerOptions,
+			MaxOutputTokens: ai.IntOption(4000),
+		})
+		require.NoError(t, err, "failed to generate")
+		checkResult(t, result)
+	})
+}
+
+func testTool(t *testing.T, pair builderPair) {
+	type WeatherInput struct {
+		Location string `json:"location" description:"the city"`
+	}
+
+	weatherTool := ai.NewAgentTool(
+		"weather",
+		"Get weather information for a location",
+		func(ctx context.Context, input WeatherInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			return ai.NewTextResponse("40 C"), nil
+		},
+	)
+	checkResult := func(t *testing.T, result *ai.AgentResult) {
+		require.Len(t, result.Steps, 2)
+
+		var toolCalls []ai.ToolCallContent
+		for _, content := range result.Steps[0].Content {
+			if content.GetType() == ai.ContentTypeToolCall {
+				toolCalls = append(toolCalls, content.(ai.ToolCallContent))
+			}
+		}
+		require.Len(t, toolCalls, 1)
+		require.Equal(t, toolCalls[0].ToolName, "weather")
+
+		want1 := "Florence"
+		want2 := "40"
+		got := result.Response.Content.Text()
+		require.True(t, strings.Contains(got, want1) && strings.Contains(got, want2), "unexpected response: got %q, want %q %q", got, want1, want2)
+	}
+
+	t.Run("tool "+pair.name, func(t *testing.T) {
+		r := newRecorder(t)
+
+		languageModel, err := pair.builder(r)
+		require.NoError(t, err, "failed to build language model")
+
+		agent := ai.NewAgent(
+			languageModel,
+			ai.WithSystemPrompt("You are a helpful assistant"),
+			ai.WithTools(weatherTool),
+		)
+		result, err := agent.Generate(t.Context(), ai.AgentCall{
+			Prompt:          "What's the weather in Florence,Italy?",
+			ProviderOptions: pair.providerOptions,
+			MaxOutputTokens: ai.IntOption(4000),
+		})
+		require.NoError(t, err, "failed to generate")
+		checkResult(t, result)
+	})
+	t.Run("tool streaming "+pair.name, func(t *testing.T) {
+		r := newRecorder(t)
+
+		languageModel, err := pair.builder(r)
+		require.NoError(t, err, "failed to build language model")
+
+		agent := ai.NewAgent(
+			languageModel,
+			ai.WithSystemPrompt("You are a helpful assistant"),
+			ai.WithTools(weatherTool),
+		)
+		result, err := agent.Stream(t.Context(), ai.AgentStreamCall{
+			Prompt:          "What's the weather in Florence,Italy?",
+			ProviderOptions: pair.providerOptions,
+			MaxOutputTokens: ai.IntOption(4000),
+		})
+		require.NoError(t, err, "failed to generate")
+		checkResult(t, result)
+	})
+}
+
+func testMultiTool(t *testing.T, pair builderPair) {
+	type WeatherInput struct {
+		Location string `json:"location" description:"the city"`
+	}
+
+	type CalculatorInput struct {
+		A int `json:"a" description:"first number"`
+		B int `json:"b" description:"second number"`
+	}
+
+	addTool := ai.NewAgentTool(
+		"add",
+		"Add two numbers",
+		func(ctx context.Context, input CalculatorInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			result := input.A + input.B
+			return ai.NewTextResponse(strings.TrimSpace(strconv.Itoa(result))), nil
+		},
+	)
+	multiplyTool := ai.NewAgentTool(
+		"multiply",
+		"Multiply two numbers",
+		func(ctx context.Context, input CalculatorInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			result := input.A * input.B
+			return ai.NewTextResponse(strings.TrimSpace(strconv.Itoa(result))), nil
+		},
+	)
+	checkResult := func(t *testing.T, result *ai.AgentResult) {
+		require.Len(t, result.Steps, 2)
+
+		var toolCalls []ai.ToolCallContent
+		for _, content := range result.Steps[0].Content {
+			if content.GetType() == ai.ContentTypeToolCall {
+				toolCalls = append(toolCalls, content.(ai.ToolCallContent))
+			}
+		}
+		require.Len(t, toolCalls, 2)
+
+		finalText := result.Response.Content.Text()
+		require.Contains(t, finalText, "5", "expected response to contain '5', got: %q", finalText)
+		require.Contains(t, finalText, "6", "expected response to contain '6', got: %q", finalText)
+	}
+
+	t.Run("multi tool "+pair.name, func(t *testing.T) {
+		r := newRecorder(t)
+
+		languageModel, err := pair.builder(r)
+		require.NoError(t, err, "failed to build language model")
+
+		agent := ai.NewAgent(
+			languageModel,
+			ai.WithSystemPrompt("You are a helpful assistant. Always use both add and multiply at the same time."),
+			ai.WithTools(addTool),
+			ai.WithTools(multiplyTool),
+		)
+		result, err := agent.Generate(t.Context(), ai.AgentCall{
+			Prompt:          "Add and multiply the number 2 and 3",
+			ProviderOptions: pair.providerOptions,
+			MaxOutputTokens: ai.IntOption(4000),
+		})
+		require.NoError(t, err, "failed to generate")
+		checkResult(t, result)
+	})
+	t.Run("multi tool streaming "+pair.name, func(t *testing.T) {
+		r := newRecorder(t)
+
+		languageModel, err := pair.builder(r)
+		require.NoError(t, err, "failed to build language model")
+
+		agent := ai.NewAgent(
+			languageModel,
+			ai.WithSystemPrompt("You are a helpful assistant. Always use both add and multiply at the same time."),
+			ai.WithTools(addTool),
+			ai.WithTools(multiplyTool),
+		)
+		result, err := agent.Stream(t.Context(), ai.AgentStreamCall{
+			Prompt:          "Add and multiply the number 2 and 3",
+			ProviderOptions: pair.providerOptions,
+			MaxOutputTokens: ai.IntOption(4000),
+		})
+		require.NoError(t, err, "failed to generate")
+		checkResult(t, result)
+	})
+}

providertests/google_test.go ๐Ÿ”—

@@ -0,0 +1,35 @@
+package providertests
+
+import (
+	"cmp"
+	"net/http"
+	"os"
+	"testing"
+
+	"github.com/charmbracelet/fantasy/ai"
+	"github.com/charmbracelet/fantasy/google"
+	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
+)
+
+func TestGoogleCommon(t *testing.T) {
+	testCommon(t, []builderPair{
+		{"gemini-2.5-flash", builderGoogleGemini25Flash, nil},
+		{"gemini-2.5-pro", builderGoogleGemini25Pro, nil},
+	})
+}
+
+func builderGoogleGemini25Flash(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := google.New(
+		google.WithAPIKey(cmp.Or(os.Getenv("GEMINI_API_KEY"), "(missing)")),
+		google.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("gemini-2.5-flash")
+}
+
+func builderGoogleGemini25Pro(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := google.New(
+		google.WithAPIKey(cmp.Or(os.Getenv("GEMINI_API_KEY"), "(missing)")),
+		google.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("gemini-2.5-pro")
+}

providertests/openai_test.go ๐Ÿ”—

@@ -0,0 +1,43 @@
+package providertests
+
+import (
+	"net/http"
+	"os"
+	"testing"
+
+	"github.com/charmbracelet/fantasy/ai"
+	"github.com/charmbracelet/fantasy/openai"
+	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
+)
+
+func TestOpenAICommon(t *testing.T) {
+	testCommon(t, []builderPair{
+		{"gpt-4o", builderOpenaiGpt4o, nil},
+		{"gpt-4o-mini", builderOpenaiGpt4oMini, nil},
+		{"gpt-5", builderOpenaiGpt5, nil},
+	})
+}
+
+func builderOpenaiGpt4o(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := openai.New(
+		openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
+		openai.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("gpt-4o")
+}
+
+func builderOpenaiGpt4oMini(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := openai.New(
+		openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
+		openai.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("gpt-4o-mini")
+}
+
+func builderOpenaiGpt5(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := openai.New(
+		openai.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
+		openai.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("gpt-5")
+}

providertests/opernrouter_test.go ๐Ÿ”—

@@ -0,0 +1,78 @@
+package providertests
+
+import (
+	"net/http"
+	"os"
+	"testing"
+
+	"github.com/charmbracelet/fantasy/ai"
+	"github.com/charmbracelet/fantasy/openrouter"
+	"gopkg.in/dnaeon/go-vcr.v4/pkg/recorder"
+)
+
+type openrouterModel struct {
+	name        string
+	builderFunc builderFunc
+	providers   []string
+}
+
+func TestOpenRouterCommon(t *testing.T) {
+	var pairs []builderPair
+	models := []openrouterModel{
+		kimiK2(),
+	}
+
+	for _, model := range models {
+		for _, provider := range model.providers {
+			pairs = append(
+				pairs,
+				builderPair{
+					model.name + "_" + provider,
+					model.builderFunc,
+					ai.ProviderOptions{
+						openrouter.Name: &openrouter.ProviderOptions{
+							Provider: &openrouter.Provider{
+								Only: []string{provider},
+							},
+						},
+					},
+				})
+		}
+	}
+
+	testCommon(t, pairs)
+}
+
+func kimiK2() openrouterModel {
+	return openrouterModel{
+		name: "kimi-k2",
+		builderFunc: func(r *recorder.Recorder) (ai.LanguageModel, error) {
+			provider := openrouter.New(
+				openrouter.WithAPIKey(os.Getenv("OPENROUTER_API_KEY")),
+				openrouter.WithHTTPClient(&http.Client{Transport: r}),
+			)
+			return provider.LanguageModel("moonshotai/kimi-k2-0905")
+		},
+		providers: []string{
+			"chutes",
+			"deepinfra",
+			"siliconflow",
+			"fireworks",
+			"moonshotai",
+			"novita",
+			"baseten",
+			"together",
+			// "groq",
+			"moonshotai/turbo",
+			"wandb",
+		},
+	}
+}
+
+func builderOpenrouterGLM45(r *recorder.Recorder) (ai.LanguageModel, error) {
+	provider := openrouter.New(
+		openrouter.WithAPIKey(os.Getenv("OPENROUTER_API_KEY")),
+		openrouter.WithHTTPClient(&http.Client{Transport: r}),
+	)
+	return provider.LanguageModel("z-ai/glm-4.5")
+}

providertests/provider_test.go ๐Ÿ”—

@@ -1,378 +0,0 @@
-package providertests
-
-import (
-	"context"
-	"strconv"
-	"strings"
-	"testing"
-
-	"github.com/charmbracelet/fantasy/ai"
-	"github.com/charmbracelet/fantasy/anthropic"
-	"github.com/charmbracelet/fantasy/google"
-	"github.com/charmbracelet/fantasy/openai"
-	"github.com/charmbracelet/fantasy/openrouter"
-	_ "github.com/joho/godotenv/autoload"
-	"github.com/stretchr/testify/require"
-)
-
-func TestSimple(t *testing.T) {
-	for _, pair := range languageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant"),
-			)
-			result, err := agent.Generate(t.Context(), ai.AgentCall{
-				Prompt: "Say hi in Portuguese",
-			})
-			require.NoError(t, err, "failed to generate")
-
-			option1 := "Oi"
-			option2 := "Olรก"
-			got := result.Response.Content.Text()
-			require.True(t, strings.Contains(got, option1) || strings.Contains(got, option2), "unexpected response: got %q, want %q or %q", got, option1, option2)
-		})
-	}
-}
-
-func TestTool(t *testing.T) {
-	for _, pair := range languageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			type WeatherInput struct {
-				Location string `json:"location" description:"the city"`
-			}
-
-			weatherTool := ai.NewAgentTool(
-				"weather",
-				"Get weather information for a location",
-				func(ctx context.Context, input WeatherInput, _ ai.ToolCall) (ai.ToolResponse, error) {
-					return ai.NewTextResponse("40 C"), nil
-				},
-			)
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant"),
-				ai.WithTools(weatherTool),
-			)
-			result, err := agent.Generate(t.Context(), ai.AgentCall{
-				Prompt: "What's the weather in Florence?",
-			})
-			require.NoError(t, err, "failed to generate")
-
-			want1 := "Florence"
-			want2 := "40"
-			got := result.Response.Content.Text()
-			require.True(t, strings.Contains(got, want1) && strings.Contains(got, want2), "unexpected response: got %q, want %q %q", got, want1, want2)
-		})
-	}
-}
-
-func TestThinking(t *testing.T) {
-	for _, pair := range thinkingLanguageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			type WeatherInput struct {
-				Location string `json:"location" description:"the city"`
-			}
-
-			weatherTool := ai.NewAgentTool(
-				"weather",
-				"Get weather information for a location",
-				func(ctx context.Context, input WeatherInput, _ ai.ToolCall) (ai.ToolResponse, error) {
-					return ai.NewTextResponse("40 C"), nil
-				},
-			)
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant"),
-				ai.WithTools(weatherTool),
-			)
-			result, err := agent.Generate(t.Context(), ai.AgentCall{
-				Prompt: "What's the weather in Florence, Italy?",
-				ProviderOptions: ai.ProviderOptions{
-					"anthropic": &anthropic.ProviderOptions{
-						Thinking: &anthropic.ThinkingProviderOption{
-							BudgetTokens: 10_000,
-						},
-					},
-					"google": &google.ProviderOptions{
-						ThinkingConfig: &google.ThinkingConfig{
-							ThinkingBudget:  ai.IntOption(100),
-							IncludeThoughts: ai.BoolOption(true),
-						},
-					},
-					"openai": &openai.ProviderOptions{
-						ReasoningEffort: openai.ReasoningEffortOption(openai.ReasoningEffortMedium),
-					},
-					"openrouter": &openrouter.ProviderOptions{
-						Reasoning: &openrouter.ReasoningOptions{
-							Effort: openrouter.ReasoningEffortOption(openrouter.ReasoningEffortHigh),
-						},
-					},
-				},
-			})
-			require.NoError(t, err, "failed to generate")
-
-			want1 := "Florence"
-			want2 := "40"
-			got := result.Response.Content.Text()
-			require.True(t, strings.Contains(got, want1) && strings.Contains(got, want2), "unexpected response: got %q, want %q %q", got, want1, want2)
-
-			testThinking(t, languageModel.Provider(), result.Steps)
-		})
-	}
-}
-
-func TestThinkingStreaming(t *testing.T) {
-	for _, pair := range thinkingLanguageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			type WeatherInput struct {
-				Location string `json:"location" description:"the city"`
-			}
-
-			weatherTool := ai.NewAgentTool(
-				"weather",
-				"Get weather information for a location",
-				func(ctx context.Context, input WeatherInput, _ ai.ToolCall) (ai.ToolResponse, error) {
-					return ai.NewTextResponse("40 C"), nil
-				},
-			)
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant"),
-				ai.WithTools(weatherTool),
-			)
-			result, err := agent.Stream(t.Context(), ai.AgentStreamCall{
-				Prompt: "What's the weather in Florence, Italy?",
-				ProviderOptions: ai.ProviderOptions{
-					"anthropic": &anthropic.ProviderOptions{
-						Thinking: &anthropic.ThinkingProviderOption{
-							BudgetTokens: 10_000,
-						},
-					},
-					"google": &google.ProviderOptions{
-						ThinkingConfig: &google.ThinkingConfig{
-							ThinkingBudget:  ai.IntOption(100),
-							IncludeThoughts: ai.BoolOption(true),
-						},
-					},
-					"openai": &openai.ProviderOptions{
-						ReasoningEffort: openai.ReasoningEffortOption(openai.ReasoningEffortMedium),
-					},
-				},
-			})
-			require.NoError(t, err, "failed to generate")
-
-			want1 := "Florence"
-			want2 := "40"
-			got := result.Response.Content.Text()
-			require.True(t, strings.Contains(got, want1) && strings.Contains(got, want2), "unexpected response: got %q, want %q %q", got, want1, want2)
-
-			testThinking(t, languageModel.Provider(), result.Steps)
-		})
-	}
-}
-
-func TestStream(t *testing.T) {
-	for _, pair := range languageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant"),
-			)
-
-			var collectedText strings.Builder
-			textDeltaCount := 0
-			stepCount := 0
-
-			streamCall := ai.AgentStreamCall{
-				Prompt: "Count from 1 to 3 in Spanish",
-				OnTextDelta: func(id, text string) error {
-					textDeltaCount++
-					collectedText.WriteString(text)
-					return nil
-				},
-				OnStepFinish: func(step ai.StepResult) error {
-					stepCount++
-					return nil
-				},
-			}
-
-			result, err := agent.Stream(t.Context(), streamCall)
-			require.NoError(t, err, "failed to stream")
-
-			finalText := result.Response.Content.Text()
-			require.NotEmpty(t, finalText, "expected non-empty response")
-
-			require.True(t, strings.Contains(strings.ToLower(finalText), "uno") &&
-				strings.Contains(strings.ToLower(finalText), "dos") &&
-				strings.Contains(strings.ToLower(finalText), "tres"), "unexpected response: %q", finalText)
-
-			require.Greater(t, textDeltaCount, 0, "expected at least one text delta callback")
-
-			require.Greater(t, stepCount, 0, "expected at least one step finish callback")
-
-			require.NotEmpty(t, collectedText.String(), "expected collected text from deltas to be non-empty")
-		})
-	}
-}
-
-func TestStreamWithTools(t *testing.T) {
-	for _, pair := range languageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			type CalculatorInput struct {
-				A int `json:"a" description:"first number"`
-				B int `json:"b" description:"second number"`
-			}
-
-			calculatorTool := ai.NewAgentTool(
-				"add",
-				"Add two numbers",
-				func(ctx context.Context, input CalculatorInput, _ ai.ToolCall) (ai.ToolResponse, error) {
-					result := input.A + input.B
-					return ai.NewTextResponse(strings.TrimSpace(strconv.Itoa(result))), nil
-				},
-			)
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant. Use the add tool to perform calculations."),
-				ai.WithTools(calculatorTool),
-			)
-
-			toolCallCount := 0
-			toolResultCount := 0
-			var collectedText strings.Builder
-
-			streamCall := ai.AgentStreamCall{
-				Prompt: "What is 15 + 27?",
-				OnTextDelta: func(id, text string) error {
-					collectedText.WriteString(text)
-					return nil
-				},
-				OnToolCall: func(toolCall ai.ToolCallContent) error {
-					toolCallCount++
-					require.Equal(t, "add", toolCall.ToolName, "unexpected tool name")
-					return nil
-				},
-				OnToolResult: func(result ai.ToolResultContent) error {
-					toolResultCount++
-					return nil
-				},
-			}
-
-			result, err := agent.Stream(t.Context(), streamCall)
-			require.NoError(t, err, "failed to stream")
-
-			finalText := result.Response.Content.Text()
-			require.Contains(t, finalText, "42", "expected response to contain '42', got: %q", finalText)
-
-			require.Greater(t, toolCallCount, 0, "expected at least one tool call")
-
-			require.Greater(t, toolResultCount, 0, "expected at least one tool result")
-		})
-	}
-}
-
-func TestStreamWithMultipleTools(t *testing.T) {
-	for _, pair := range languageModelBuilders {
-		t.Run(pair.name, func(t *testing.T) {
-			r := newRecorder(t)
-
-			languageModel, err := pair.builder(r)
-			require.NoError(t, err, "failed to build language model")
-
-			type CalculatorInput struct {
-				A int `json:"a" description:"first number"`
-				B int `json:"b" description:"second number"`
-			}
-
-			addTool := ai.NewAgentTool(
-				"add",
-				"Add two numbers",
-				func(ctx context.Context, input CalculatorInput, _ ai.ToolCall) (ai.ToolResponse, error) {
-					result := input.A + input.B
-					return ai.NewTextResponse(strings.TrimSpace(strconv.Itoa(result))), nil
-				},
-			)
-			multiplyTool := ai.NewAgentTool(
-				"multiply",
-				"Multiply two numbers",
-				func(ctx context.Context, input CalculatorInput, _ ai.ToolCall) (ai.ToolResponse, error) {
-					result := input.A * input.B
-					return ai.NewTextResponse(strings.TrimSpace(strconv.Itoa(result))), nil
-				},
-			)
-
-			agent := ai.NewAgent(
-				languageModel,
-				ai.WithSystemPrompt("You are a helpful assistant. Always use both add and multiply at the same time."),
-				ai.WithTools(addTool),
-				ai.WithTools(multiplyTool),
-			)
-
-			toolCallCount := 0
-			toolResultCount := 0
-			var collectedText strings.Builder
-
-			streamCall := ai.AgentStreamCall{
-				Prompt: "Add and multiply the number 2 and 3",
-				OnTextDelta: func(id, text string) error {
-					collectedText.WriteString(text)
-					return nil
-				},
-				OnToolCall: func(toolCall ai.ToolCallContent) error {
-					toolCallCount++
-					return nil
-				},
-				OnToolResult: func(result ai.ToolResultContent) error {
-					toolResultCount++
-					return nil
-				},
-			}
-
-			result, err := agent.Stream(t.Context(), streamCall)
-			require.NoError(t, err, "failed to stream")
-			require.Equal(t, len(result.Steps), 2, "expected all tool calls in step 1")
-			finalText := result.Response.Content.Text()
-			require.Contains(t, finalText, "5", "expected response to contain '5', got: %q", finalText)
-			require.Contains(t, finalText, "6", "expected response to contain '5', got: %q", finalText)
-
-			require.Greater(t, toolCallCount, 0, "expected at least one tool call")
-
-			require.Greater(t, toolResultCount, 0, "expected at least one tool result")
-		})
-	}
-}

providertests/testdata/TestAnthropicCommon/simple_claude-sonnet-4.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 205
+        host: ""
+        body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Say hi in Portuguese","type":"text"}],"role":"user"}],"model":"claude-sonnet-4-20250514","system":[{"text":"You are a helpful assistant","type":"text"}]}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - Anthropic/Go 1.10.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: '{"id":"msg_017KTuKXoM4C6SAss2KSxPAK","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"Oi! \n\n(That''s an informal \"hi\" in Portuguese. You could also say \"Olรก\" for a slightly more formal greeting.)"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":16,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":36,"service_tier":"standard"}}'
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 4.631234083s

providertests/testdata/TestAnthropicCommon/simple_streaming_claude-sonnet-4.yaml ๐Ÿ”—

@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 219
+        host: ""
+        body: '{"max_tokens":4000,"messages":[{"content":[{"text":"Say hi in Portuguese","type":"text"}],"role":"user"}],"model":"claude-sonnet-4-20250514","system":[{"text":"You are a helpful assistant","type":"text"}],"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - Anthropic/Go 1.10.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":{"id":"msg_01RFuVPPnYCFGL9ucejc3Rd4","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":16,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":5,"service_tier":"standard"}}           }
+
+            event: content_block_start
+            data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}       }
+
+            event: content_block_delta
+            data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Olรก! "}           }
+
+            event: content_block_delta
+            data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n\n(That's \"hi\" in Portuguese!)"}      }
+
+            event: ping
+            data: {"type": "ping"}
+
+            event: content_block_stop
+            data: {"type":"content_block_stop","index":0            }
+
+            event: ping
+            data: {"type": "ping"}
+
+            event: ping
+            data: {"type": "ping"}
+
+            event: message_delta
+            data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":16,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":18}         }
+
+            event: message_stop
+            data: {"type":"message_stop"        }
+
+        headers:
+            Content-Type:
+                - text/event-stream; charset=utf-8
+        status: 200 OK
+        code: 200
+        duration: 1.141609875s

providertests/testdata/TestGoogleCommon/simple_gemini-2.5-flash.yaml ๐Ÿ”—

@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 202
+        host: generativelanguage.googleapis.com
+        body: |
+            {"contents":[{"parts":[{"text":"Say hi in Portuguese"}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"}}
+        headers:
+            Content-Type:
+                - application/json
+            User-Agent:
+                - google-genai-sdk/1.23.0 gl-go/go1.25.1
+        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": "Olรก!"
+                      }
+                    ],
+                    "role": "model"
+                  },
+                  "finishReason": "STOP",
+                  "index": 0
+                }
+              ],
+              "usageMetadata": {
+                "promptTokenCount": 11,
+                "candidatesTokenCount": 2,
+                "totalTokenCount": 38,
+                "promptTokensDetails": [
+                  {
+                    "modality": "TEXT",
+                    "tokenCount": 11
+                  }
+                ],
+                "thoughtsTokenCount": 25
+              },
+              "modelVersion": "gemini-2.5-flash",
+              "responseId": "j7HSaNb8E-fknsEPzczo4QI"
+            }
+        headers:
+            Content-Type:
+                - application/json; charset=UTF-8
+        status: 200 OK
+        code: 200
+        duration: 687.882791ms

providertests/testdata/TestGoogleCommon/simple_gemini-2.5-pro.yaml ๐Ÿ”—

@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 202
+        host: generativelanguage.googleapis.com
+        body: |
+            {"contents":[{"parts":[{"text":"Say hi in Portuguese"}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"}}
+        headers:
+            Content-Type:
+                - application/json
+            User-Agent:
+                - google-genai-sdk/1.23.0 gl-go/go1.25.1
+        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": "Of course!\n\nThe most common ways to say \"hi\" in Portuguese are:\n\n*   **Oi** (more informal and very common, especially in Brazil)\n*   **Olรก** (a bit more formal, like \"hello\")"
+                      }
+                    ],
+                    "role": "model"
+                  },
+                  "finishReason": "STOP",
+                  "index": 0
+                }
+              ],
+              "usageMetadata": {
+                "promptTokenCount": 11,
+                "candidatesTokenCount": 50,
+                "totalTokenCount": 667,
+                "promptTokensDetails": [
+                  {
+                    "modality": "TEXT",
+                    "tokenCount": 11
+                  }
+                ],
+                "thoughtsTokenCount": 606
+              },
+              "modelVersion": "gemini-2.5-pro",
+              "responseId": "mLHSaNy4DqeunsEPy_THqAY"
+            }
+        headers:
+            Content-Type:
+                - application/json; charset=UTF-8
+        status: 200 OK
+        code: 200
+        duration: 7.978276458s

providertests/testdata/TestGoogleCommon/simple_streaming_gemini-2.5-flash.yaml ๐Ÿ”—

@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 202
+        host: generativelanguage.googleapis.com
+        body: |
+            {"contents":[{"parts":[{"text":"Say hi in Portuguese"}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"}}
+        form:
+            alt:
+                - sse
+        headers:
+            Content-Type:
+                - application/json
+            User-Agent:
+                - google-genai-sdk/1.23.0 gl-go/go1.25.1
+        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\": \"Olรก!\"}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 11,\"candidatesTokenCount\": 2,\"totalTokenCount\": 32,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 11}],\"thoughtsTokenCount\": 19},\"modelVersion\": \"gemini-2.5-flash\",\"responseId\": \"kLHSaLKrBOm1nsEPkOi30AM\"}\r\n\r\n"
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 903.535375ms

providertests/testdata/TestGoogleCommon/simple_streaming_gemini-2.5-pro.yaml ๐Ÿ”—

@@ -0,0 +1,34 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 202
+        host: generativelanguage.googleapis.com
+        body: |
+            {"contents":[{"parts":[{"text":"Say hi in Portuguese"}],"role":"user"}],"generationConfig":{"maxOutputTokens":4000},"systemInstruction":{"parts":[{"text":"You are a helpful assistant"}],"role":"user"}}
+        form:
+            alt:
+                - sse
+        headers:
+            Content-Type:
+                - application/json
+            User-Agent:
+                - google-genai-sdk/1.23.0 gl-go/go1.25.1
+        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

providertests/testdata/TestOpenAICommon/simple_gpt-4o-mini.yaml ๐Ÿ”—

@@ -0,0 +1,69 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 161
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"gpt-4o-mini","max_tokens":4000}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        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-CIyJYqz8D13qvLHrzmPDBgcLI2yis",
+              "object": "chat.completion",
+              "created": 1758637788,
+              "model": "gpt-4o-mini-2024-07-18",
+              "choices": [
+                {
+                  "index": 0,
+                  "message": {
+                    "role": "assistant",
+                    "content": "Hi! In Portuguese, you would say \"Oi!\"",
+                    "refusal": null,
+                    "annotations": []
+                  },
+                  "logprobs": null,
+                  "finish_reason": "stop"
+                }
+              ],
+              "usage": {
+                "prompt_tokens": 20,
+                "completion_tokens": 11,
+                "total_tokens": 31,
+                "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.50388225s

providertests/testdata/TestOpenAICommon/simple_gpt-4o.yaml ๐Ÿ”—

@@ -0,0 +1,69 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 156
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"gpt-4o","max_tokens":4000}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        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-CIyJWzUZJShy0RouHvhJJ2Hy2UAfY",
+              "object": "chat.completion",
+              "created": 1758637786,
+              "model": "gpt-4o-2024-08-06",
+              "choices": [
+                {
+                  "index": 0,
+                  "message": {
+                    "role": "assistant",
+                    "content": "Olรก!",
+                    "refusal": null,
+                    "annotations": []
+                  },
+                  "logprobs": null,
+                  "finish_reason": "stop"
+                }
+              ],
+              "usage": {
+                "prompt_tokens": 20,
+                "completion_tokens": 2,
+                "total_tokens": 22,
+                "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_f33640a400"
+            }
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 1.153691459s

providertests/testdata/TestOpenAICommon/simple_gpt-5.yaml ๐Ÿ”—

@@ -0,0 +1,68 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 166
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"gpt-5","max_completion_tokens":4000}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        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-CIyJdVnMfKeENR9RRovE2NBB00W7o",
+              "object": "chat.completion",
+              "created": 1758637793,
+              "model": "gpt-5-2025-08-07",
+              "choices": [
+                {
+                  "index": 0,
+                  "message": {
+                    "role": "assistant",
+                    "content": "Olรก!",
+                    "refusal": null,
+                    "annotations": []
+                  },
+                  "finish_reason": "stop"
+                }
+              ],
+              "usage": {
+                "prompt_tokens": 19,
+                "completion_tokens": 139,
+                "total_tokens": 158,
+                "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: 7.150942875s

providertests/testdata/TestOpenAICommon/simple_streaming_gpt-4o-mini.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 215
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"gpt-4o-mini","max_tokens":4000,"stream_options":{"include_usage":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        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-CIyJaOZW1cSN0oFzR0KNpr0DEQdxO","object":"chat.completion.chunk","created":1758637790,"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":"qa8IO7ML3"}
+
+            data: {"id":"chatcmpl-CIyJaOZW1cSN0oFzR0KNpr0DEQdxO","object":"chat.completion.chunk","created":1758637790,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[{"index":0,"delta":{"content":"Oi"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"epi9ETo8G"}
+
+            data: {"id":"chatcmpl-CIyJaOZW1cSN0oFzR0KNpr0DEQdxO","object":"chat.completion.chunk","created":1758637790,"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":"BVCz1VPweF"}
+
+            data: {"id":"chatcmpl-CIyJaOZW1cSN0oFzR0KNpr0DEQdxO","object":"chat.completion.chunk","created":1758637790,"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":"VS66Q"}
+
+            data: {"id":"chatcmpl-CIyJaOZW1cSN0oFzR0KNpr0DEQdxO","object":"chat.completion.chunk","created":1758637790,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_560af6e559","choices":[],"usage":{"prompt_tokens":20,"completion_tokens":2,"total_tokens":22,"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":"nek2YcQCjRz"}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream; charset=utf-8
+        status: 200 OK
+        code: 200
+        duration: 3.206862375s

providertests/testdata/TestOpenAICommon/simple_streaming_gpt-4o.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 210
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"gpt-4o","max_tokens":4000,"stream_options":{"include_usage":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        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-CIyJXHI3WU565cXirytCPOLgRqHly","object":"chat.completion.chunk","created":1758637787,"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":"yWAz3isSyfZvUk"}
+
+            data: {"id":"chatcmpl-CIyJXHI3WU565cXirytCPOLgRqHly","object":"chat.completion.chunk","created":1758637787,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[{"index":0,"delta":{"content":"Olรก"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"DFLb9RhUI6jN1"}
+
+            data: {"id":"chatcmpl-CIyJXHI3WU565cXirytCPOLgRqHly","object":"chat.completion.chunk","created":1758637787,"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":"Lp6KWcGxa5knPEU"}
+
+            data: {"id":"chatcmpl-CIyJXHI3WU565cXirytCPOLgRqHly","object":"chat.completion.chunk","created":1758637787,"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":"TPrEUpV8Bn"}
+
+            data: {"id":"chatcmpl-CIyJXHI3WU565cXirytCPOLgRqHly","object":"chat.completion.chunk","created":1758637787,"model":"gpt-4o-2024-08-06","service_tier":"default","system_fingerprint":"fp_cbf1785567","choices":[],"usage":{"prompt_tokens":20,"completion_tokens":2,"total_tokens":22,"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":""}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream; charset=utf-8
+        status: 200 OK
+        code: 200
+        duration: 818.509375ms

providertests/testdata/TestOpenAICommon/simple_streaming_gpt-5.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 220
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"gpt-5","max_completion_tokens":4000,"stream_options":{"include_usage":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        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-CIyJk1jvLtm20C4X3sbgJxP3jPduL","object":"chat.completion.chunk","created":1758637800,"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":"uHhgO9zaTG"}
+
+            data: {"id":"chatcmpl-CIyJk1jvLtm20C4X3sbgJxP3jPduL","object":"chat.completion.chunk","created":1758637800,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"Olรก"},"finish_reason":null}],"usage":null,"obfuscation":"CDpyKFZPF"}
+
+            data: {"id":"chatcmpl-CIyJk1jvLtm20C4X3sbgJxP3jPduL","object":"chat.completion.chunk","created":1758637800,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}],"usage":null,"obfuscation":"16B86R3N6bM"}
+
+            data: {"id":"chatcmpl-CIyJk1jvLtm20C4X3sbgJxP3jPduL","object":"chat.completion.chunk","created":1758637800,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":null,"obfuscation":"OzkKXU"}
+
+            data: {"id":"chatcmpl-CIyJk1jvLtm20C4X3sbgJxP3jPduL","object":"chat.completion.chunk","created":1758637800,"model":"gpt-5-2025-08-07","service_tier":"default","system_fingerprint":null,"choices":[],"usage":{"prompt_tokens":19,"completion_tokens":139,"total_tokens":158,"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}},"obfuscation":"ECuqld8"}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream; charset=utf-8
+        status: 200 OK
+        code: 200
+        duration: 5.437192459s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_baseten.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 230
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["baseten"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n\n         \n\n         \n\n         \n\n         \n\n         \n{\"id\":\"gen-1758639328-AzATqlbuJcP77p24ALVP\",\"provider\":\"BaseTen\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639328,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Olรก!\",\"refusal\":null,\"reasoning\":null}}],\"system_fingerprint\":null,\"usage\":{\"prompt_tokens\":21,\"completion_tokens\":4,\"total_tokens\":25,\"cost\":0.0000226,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.0000126,\"upstream_inference_completions_cost\":0.00001},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 3.172332875s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_chutes.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 229
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"usage":{"include":true},"provider":{"only":["chutes"]}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n{\"id\":\"gen-1758639299-GKSxis3jImn2oU1Eh6ny\",\"provider\":\"Chutes\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639299,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Olรก!\",\"refusal\":null,\"reasoning\":null}}],\"usage\":{\"prompt_tokens\":21,\"completion_tokens\":4,\"total_tokens\":25,\"cost\":0.00001406,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.00000798,\"upstream_inference_completions_cost\":0.00000608},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 917.195084ms

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_deepinfra.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 232
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["deepinfra"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n{\"id\":\"gen-1758639301-0fTwU9jWsZMToUgKXztP\",\"provider\":\"DeepInfra\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639301,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Olรก!\",\"refusal\":null,\"reasoning\":null}}],\"usage\":{\"prompt_tokens\":20,\"completion_tokens\":3,\"total_tokens\":23,\"cost\":0.000016,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.00001,\"upstream_inference_completions_cost\":0.000006},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 594.821083ms

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_fireworks.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 232
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["fireworks"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_moonshotai.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 233
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"usage":{"include":true},"provider":{"only":["moonshotai"]}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n\n         \n\n         \n{\"id\":\"gen-1758639321-VwBlIANQZ6xzs4I5czRO\",\"provider\":\"Moonshot AI\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639321,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Oi!\",\"refusal\":null,\"reasoning\":null}}],\"system_fingerprint\":\"fpv0_ef28f882\",\"usage\":{\"prompt_tokens\":20,\"completion_tokens\":3,\"total_tokens\":23,\"cost\":0.0000195,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.000012,\"upstream_inference_completions_cost\":0.0000075},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 1.853341958s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_moonshotai/turbo.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 239
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["moonshotai/turbo"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n\n         \n{\"id\":\"gen-1758639334-hxwi7fkQWt0putQK0ABp\",\"provider\":\"Moonshot AI\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639334,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Olรก!\",\"refusal\":null,\"reasoning\":null}}],\"system_fingerprint\":\"fpv0_ec889fbb\",\"usage\":{\"prompt_tokens\":20,\"completion_tokens\":4,\"total_tokens\":24,\"cost\":0.000044,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.000024,\"upstream_inference_completions_cost\":0.00002},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 1.365624167s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_novita.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 229
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["novita"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n\n         \n{\"id\":\"gen-1758639324-If4icmPpJg9xF7NBFIL9\",\"provider\":\"Novita\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639324,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Oi!\",\"refusal\":null,\"reasoning\":null}}],\"system_fingerprint\":\"fpv0_ef28f882\",\"usage\":{\"prompt_tokens\":20,\"completion_tokens\":3,\"total_tokens\":23,\"cost\":0.0000195,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.000012,\"upstream_inference_completions_cost\":0.0000075},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 1.467689625s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_siliconflow.yaml ๐Ÿ”—

@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 234
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["siliconflow"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: '{"error":{"message":"Provider returned error","code":429,"metadata":{"raw":"moonshotai/kimi-k2-0905 is temporarily rate-limited upstream. Please retry shortly, or add your own key to accumulate your rate limits: https://openrouter.ai/settings/integrations","provider_name":"SiliconFlow"}},"user_id":"user_2zMGmKqlf4zmAvL9snVImB1Z1ZQ"}'
+        headers:
+            Content-Type:
+                - application/json
+        status: 429 Too Many Requests
+        code: 429
+        duration: 3.401816333s
+    - id: 1
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 234
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"provider":{"only":["siliconflow"]},"usage":{"include":true}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n\n         \n{\"id\":\"gen-1758639306-G4HkxVEjJmlvSXb92TQJ\",\"provider\":\"SiliconFlow\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639306,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Oi!\",\"refusal\":null,\"reasoning\":null}}],\"system_fingerprint\":\"\",\"usage\":{\"prompt_tokens\":21,\"completion_tokens\":2,\"total_tokens\":23,\"cost\":0.00001676,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.00001218,\"upstream_inference_completions_cost\":0.00000458},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 1.323651459s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_together.yaml ๐Ÿ”—

@@ -0,0 +1,33 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 231
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"usage":{"include":true},"provider":{"only":["together"]}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n\n         \n\n         \n{\"id\":\"gen-1758639332-Tug4bmqaH3ANNQGKvKml\",\"provider\":\"Together\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639332,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Olรก!\",\"refusal\":null,\"reasoning\":null}}],\"usage\":{\"prompt_tokens\":21,\"completion_tokens\":4,\"total_tokens\":25,\"cost\":0.000033,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.000021,\"upstream_inference_completions_cost\":0.000012},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 1.274655375s

providertests/testdata/TestOpenRouterCommon/simple_kimi-k2_wandb.yaml ๐Ÿ”—

@@ -0,0 +1,62 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 228
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"usage":{"include":true},"provider":{"only":["wandb"]}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: '{"error":{"message":"Provider returned error","code":429,"metadata":{"raw":"moonshotai/kimi-k2-0905 is temporarily rate-limited upstream. Please retry shortly, or add your own key to accumulate your rate limits: https://openrouter.ai/settings/integrations","provider_name":"WandB"}},"user_id":"user_2zMGmKqlf4zmAvL9snVImB1Z1ZQ"}'
+        headers:
+            Content-Type:
+                - application/json
+        status: 429 Too Many Requests
+        code: 429
+        duration: 380.91475ms
+    - id: 1
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 228
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"usage":{"include":true},"provider":{"only":["wandb"]}}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        uncompressed: true
+        body: "\n         \n{\"id\":\"gen-1758639338-u2WBkAEXgqR4CVf9CEDs\",\"provider\":\"WandB\",\"model\":\"moonshotai/kimi-k2-0905\",\"object\":\"chat.completion\",\"created\":1758639338,\"choices\":[{\"logprobs\":null,\"finish_reason\":\"stop\",\"native_finish_reason\":\"stop\",\"index\":0,\"message\":{\"role\":\"assistant\",\"content\":\"Olรก!\",\"refusal\":null,\"reasoning\":null}}],\"usage\":{\"prompt_tokens\":20,\"completion_tokens\":4,\"total_tokens\":24,\"cost\":0.000043,\"is_byok\":false,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"cost_details\":{\"upstream_inference_cost\":null,\"upstream_inference_prompt_cost\":0.000027,\"upstream_inference_completions_cost\":0.000016},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"image_tokens\":0}}}"
+        headers:
+            Content-Type:
+                - application/json
+        status: 200 OK
+        code: 200
+        duration: 693.287417ms

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_baseten.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 284
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["baseten"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639331-sFEtjceKZgX1JHdtpEWL","provider":"BaseTen","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639331,"choices":[{"index":0,"delta":{"role":"assistant","content":"Ol"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":null}
+
+            data: {"id":"gen-1758639331-sFEtjceKZgX1JHdtpEWL","provider":"BaseTen","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639331,"choices":[{"index":0,"delta":{"role":"assistant","content":"รก"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":null}
+
+            data: {"id":"gen-1758639331-sFEtjceKZgX1JHdtpEWL","provider":"BaseTen","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639331,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":null}
+
+            data: {"id":"gen-1758639331-sFEtjceKZgX1JHdtpEWL","provider":"BaseTen","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639331,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}],"system_fingerprint":null}
+
+            data: {"id":"gen-1758639331-sFEtjceKZgX1JHdtpEWL","provider":"BaseTen","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639331,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":21,"completion_tokens":4,"total_tokens":25,"cost":0.0000226,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.0000126,"upstream_inference_completions_cost":0.00001},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 568.780375ms

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_chutes.yaml ๐Ÿ”—

@@ -0,0 +1,46 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 283
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["chutes"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639300-DbIGuRk5TqxyMyDeicfb","provider":"Chutes","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639300,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639300-DbIGuRk5TqxyMyDeicfb","provider":"Chutes","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639300,"choices":[{"index":0,"delta":{"role":"assistant","content":"Ol"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639300-DbIGuRk5TqxyMyDeicfb","provider":"Chutes","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639300,"choices":[{"index":0,"delta":{"role":"assistant","content":"รก"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639300-DbIGuRk5TqxyMyDeicfb","provider":"Chutes","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639300,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639300-DbIGuRk5TqxyMyDeicfb","provider":"Chutes","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639300,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}
+
+            data: {"id":"gen-1758639300-DbIGuRk5TqxyMyDeicfb","provider":"Chutes","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639300,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":21,"completion_tokens":4,"total_tokens":25,"cost":0.00001406,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.00000798,"upstream_inference_completions_cost":0.00000608},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 704.728291ms

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_deepinfra.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 286
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["deepinfra"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639301-oQimFucmUUib7SCHhV0q","provider":"DeepInfra","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639302,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639301-oQimFucmUUib7SCHhV0q","provider":"DeepInfra","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639302,"choices":[{"index":0,"delta":{"role":"assistant","content":"Oi"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639301-oQimFucmUUib7SCHhV0q","provider":"DeepInfra","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639302,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639301-oQimFucmUUib7SCHhV0q","provider":"DeepInfra","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639302,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}
+
+            data: {"id":"gen-1758639301-oQimFucmUUib7SCHhV0q","provider":"DeepInfra","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639302,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":20,"completion_tokens":2,"total_tokens":22,"cost":0.000014,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.00001,"upstream_inference_completions_cost":0.000004},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 609.997125ms

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_fireworks.yaml ๐Ÿ”—

@@ -0,0 +1,42 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 286
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["fireworks"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639319-3YvVwuDwqwthyWXlQVFY","provider":"Fireworks","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639319,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639319-3YvVwuDwqwthyWXlQVFY","provider":"Fireworks","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639319,"choices":[{"index":0,"delta":{"role":"assistant","content":"Oi!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639319-3YvVwuDwqwthyWXlQVFY","provider":"Fireworks","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639319,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}
+
+            data: {"id":"gen-1758639319-3YvVwuDwqwthyWXlQVFY","provider":"Fireworks","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639319,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":20,"completion_tokens":3,"total_tokens":23,"cost":0.0000195,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.000012,"upstream_inference_completions_cost":0.0000075},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 1.570646291s

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_moonshotai.yaml ๐Ÿ”—

@@ -0,0 +1,46 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 287
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"usage":{"include":true},"provider":{"only":["moonshotai"]},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639323-AYoeSaD2V0lFcYlfea27","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639323,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ef28f882"}
+
+            data: {"id":"gen-1758639323-AYoeSaD2V0lFcYlfea27","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639323,"choices":[{"index":0,"delta":{"role":"assistant","content":"Ol"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ef28f882"}
+
+            data: {"id":"gen-1758639323-AYoeSaD2V0lFcYlfea27","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639323,"choices":[{"index":0,"delta":{"role":"assistant","content":"รก"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ef28f882"}
+
+            data: {"id":"gen-1758639323-AYoeSaD2V0lFcYlfea27","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639323,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ef28f882"}
+
+            data: {"id":"gen-1758639323-AYoeSaD2V0lFcYlfea27","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639323,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}],"system_fingerprint":"fpv0_ef28f882"}
+
+            data: {"id":"gen-1758639323-AYoeSaD2V0lFcYlfea27","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639323,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":20,"completion_tokens":4,"total_tokens":24,"cost":0.000022,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.000012,"upstream_inference_completions_cost":0.00001},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 1.609822667s

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_moonshotai/turbo.yaml ๐Ÿ”—

@@ -0,0 +1,46 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 293
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["moonshotai/turbo"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639335-6tKyMLfjzFV2qR97Acz8","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639335,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ec889fbb"}
+
+            data: {"id":"gen-1758639335-6tKyMLfjzFV2qR97Acz8","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639335,"choices":[{"index":0,"delta":{"role":"assistant","content":"Ol"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ec889fbb"}
+
+            data: {"id":"gen-1758639335-6tKyMLfjzFV2qR97Acz8","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639335,"choices":[{"index":0,"delta":{"role":"assistant","content":"รก"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ec889fbb"}
+
+            data: {"id":"gen-1758639335-6tKyMLfjzFV2qR97Acz8","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639335,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":"fpv0_ec889fbb"}
+
+            data: {"id":"gen-1758639335-6tKyMLfjzFV2qR97Acz8","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639335,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}],"system_fingerprint":"fpv0_ec889fbb"}
+
+            data: {"id":"gen-1758639335-6tKyMLfjzFV2qR97Acz8","provider":"Moonshot AI","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639335,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":20,"completion_tokens":4,"total_tokens":24,"cost":0.000044,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.000024,"upstream_inference_completions_cost":0.00002},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 1.289811792s

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_novita.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 283
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["novita"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639326-mMzrivCI9J10CUf45IMq","provider":"Novita","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639326,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639326-mMzrivCI9J10CUf45IMq","provider":"Novita","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639326,"choices":[{"index":0,"delta":{"role":"assistant","content":"Oi"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639326-mMzrivCI9J10CUf45IMq","provider":"Novita","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639326,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639326-mMzrivCI9J10CUf45IMq","provider":"Novita","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639326,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639326-mMzrivCI9J10CUf45IMq","provider":"Novita","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639326,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":21,"completion_tokens":2,"total_tokens":23,"cost":0.0000176,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.0000126,"upstream_inference_completions_cost":0.000005},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 1.952843334s

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_siliconflow.yaml ๐Ÿ”—

@@ -0,0 +1,44 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 288
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["siliconflow"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639308-HFBLo1Zjk56YcERtInn9","provider":"SiliconFlow","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639308,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639308-HFBLo1Zjk56YcERtInn9","provider":"SiliconFlow","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639308,"choices":[{"index":0,"delta":{"role":"assistant","content":"Oi"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639308-HFBLo1Zjk56YcERtInn9","provider":"SiliconFlow","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639308,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639308-HFBLo1Zjk56YcERtInn9","provider":"SiliconFlow","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639308,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}],"system_fingerprint":""}
+
+            data: {"id":"gen-1758639308-HFBLo1Zjk56YcERtInn9","provider":"SiliconFlow","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639308,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":21,"completion_tokens":2,"total_tokens":23,"cost":0.00001676,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.00001218,"upstream_inference_completions_cost":0.00000458},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 1.296429666s

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_together.yaml ๐Ÿ”—

@@ -0,0 +1,46 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 285
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["together"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639333-BByAqWD42pHSJWup3zEp","provider":"Together","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639333,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639333-BByAqWD42pHSJWup3zEp","provider":"Together","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639333,"choices":[{"index":0,"delta":{"role":"assistant","content":"Ol"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639333-BByAqWD42pHSJWup3zEp","provider":"Together","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639333,"choices":[{"index":0,"delta":{"role":"assistant","content":"รก"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639333-BByAqWD42pHSJWup3zEp","provider":"Together","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639333,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639333-BByAqWD42pHSJWup3zEp","provider":"Together","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639333,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}
+
+            data: {"id":"gen-1758639333-BByAqWD42pHSJWup3zEp","provider":"Together","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639333,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":21,"completion_tokens":4,"total_tokens":25,"cost":0.000033,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.000021,"upstream_inference_completions_cost":0.000012},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 630.269125ms

providertests/testdata/TestOpenRouterCommon/simple_streaming_kimi-k2_wandb.yaml ๐Ÿ”—

@@ -0,0 +1,46 @@
+---
+version: 2
+interactions:
+    - id: 0
+      request:
+        proto: HTTP/1.1
+        proto_major: 1
+        proto_minor: 1
+        content_length: 282
+        host: ""
+        body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","max_tokens":4000,"stream_options":{"include_usage":true},"provider":{"only":["wandb"]},"usage":{"include":true},"stream":true}'
+        headers:
+            Accept:
+                - application/json
+            Content-Type:
+                - application/json
+            User-Agent:
+                - OpenAI/Go 2.3.0
+        url: https://openrouter.ai/api/v1/chat/completions
+        method: POST
+      response:
+        proto: HTTP/2.0
+        proto_major: 2
+        proto_minor: 0
+        content_length: -1
+        body: |+
+            data: {"id":"gen-1758639338-aZHT7rLqW05Vh35xd7ga","provider":"WandB","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639338,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639338-aZHT7rLqW05Vh35xd7ga","provider":"WandB","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639338,"choices":[{"index":0,"delta":{"role":"assistant","content":"Ol"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639338-aZHT7rLqW05Vh35xd7ga","provider":"WandB","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639338,"choices":[{"index":0,"delta":{"role":"assistant","content":"รก"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639338-aZHT7rLqW05Vh35xd7ga","provider":"WandB","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639338,"choices":[{"index":0,"delta":{"role":"assistant","content":"!"},"finish_reason":null,"native_finish_reason":null,"logprobs":null}]}
+
+            data: {"id":"gen-1758639338-aZHT7rLqW05Vh35xd7ga","provider":"WandB","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639338,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":"stop","native_finish_reason":"stop","logprobs":null}]}
+
+            data: {"id":"gen-1758639338-aZHT7rLqW05Vh35xd7ga","provider":"WandB","model":"moonshotai/kimi-k2-0905","object":"chat.completion.chunk","created":1758639338,"choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null,"native_finish_reason":null,"logprobs":null}],"usage":{"prompt_tokens":20,"completion_tokens":4,"total_tokens":24,"cost":0.000043,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.000027,"upstream_inference_completions_cost":0.000016},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}
+
+            data: [DONE]
+
+        headers:
+            Content-Type:
+                - text/event-stream
+        status: 200 OK
+        code: 200
+        duration: 320.202417ms

providertests/testdata/TestSimple/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 205
-    host: ""
-    body: "{\"max_tokens\":4096,\"messages\":[{\"content\":[{\"text\":\"Say hi in Portuguese\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant\",\"type\":\"text\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.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: "{\"id\":\"msg_014AQFTJZeZ1KNGT5y9TSMSs\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[{\"type\":\"text\",\"text\":\"Oi! or Olรก!\\n\\nBoth are common ways to say \\\"hi\\\" in Portuguese. \\\"Oi\\\" is more casual and commonly used in Brazilian Portuguese, while \\\"Olรก\\\" is a bit more formal and used in both Brazilian and European Portuguese.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":16,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":60,\"service_tier\":\"standard\"}}"
-    headers:
-      Content-Type:
-      - application/json
-    status: 200 OK
-    code: 200
-    duration: 2.412166125s

providertests/testdata/TestSimple/google-gemini-2.5-flash.yaml ๐Ÿ”—

@@ -1,31 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 180
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"Say hi in Portuguese\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"}}\n"
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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: "{\n  \"candidates\": [\n    {\n      \"content\": {\n        \"parts\": [\n          {\n            \"text\": \"Olรก!\"\n          }\n        ],\n        \"role\": \"model\"\n      },\n      \"finishReason\": \"STOP\",\n      \"index\": 0\n    }\n  ],\n  \"usageMetadata\": {\n    \"promptTokenCount\": 11,\n    \"candidatesTokenCount\": 2,\n    \"totalTokenCount\": 39,\n    \"promptTokensDetails\": [\n      {\n        \"modality\": \"TEXT\",\n        \"tokenCount\": 11\n      }\n    ],\n    \"thoughtsTokenCount\": 26\n  },\n  \"modelVersion\": \"gemini-2.5-flash\",\n  \"responseId\": \"_Ei7aJ_lFZ7nz7IPwKK82Qw\"\n}\n"
-    headers:
-      Content-Type:
-      - application/json; charset=UTF-8
-    status: 200 OK
-    code: 200
-    duration: 1.683615083s

providertests/testdata/TestSimple/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,31 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 180
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"Say hi in Portuguese\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"}}\n"
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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: "{\n  \"candidates\": [\n    {\n      \"content\": {\n        \"parts\": [\n          {\n            \"text\": \"Of course!\\n\\nThe most common ways to say \\\"hi\\\" in Portuguese are:\\n\\n*   **Oi** (more informal, like \\\"hi\\\")\\n*   **Olรก** (a bit more standard, like \\\"hello\\\")\\n\\nYou can also use greetings that depend on the time of day:\\n\\n*   **Bom dia** (Good morning)\\n*   **Boa tarde** (Good afternoon)\\n*   **Boa noite** (Good evening / Good night)\"\n          }\n        ],\n        \"role\": \"model\"\n      },\n      \"finishReason\": \"STOP\",\n      \"index\": 0\n    }\n  ],\n  \"usageMetadata\": {\n    \"promptTokenCount\": 11,\n    \"candidatesTokenCount\": 97,\n    \"totalTokenCount\": 830,\n    \"promptTokensDetails\": [\n      {\n        \"modality\": \"TEXT\",\n        \"tokenCount\": 11\n      }\n    ],\n    \"thoughtsTokenCount\": 722\n  },\n  \"modelVersion\": \"gemini-2.5-pro\",\n  \"responseId\": \"ZovAaND9LPiK6dkPtrLnqQM\"\n}\n"
-    headers:
-      Content-Type:
-      - application/json; charset=UTF-8
-    status: 200 OK
-    code: 200
-    duration: 7.97170275s

providertests/testdata/TestSimple/openai-gpt-4o-mini.yaml ๐Ÿ”—

@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 143
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"Say hi in Portuguese\",\"role\":\"user\"}],\"model\":\"gpt-4o-mini\"}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    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: "{\n  \"id\": \"chatcmpl-CC86Cz7oEjbaC9INACjv4rZgUqkui\",\n  \"object\": \"chat.completion\",\n  \"created\": 1757007104,\n  \"model\": \"gpt-4o-mini-2024-07-18\",\n  \"choices\": [\n    {\n      \"index\": 0,\n      \"message\": {\n        \"role\": \"assistant\",\n        \"content\": \"Olรก!\",\n        \"refusal\": null,\n        \"annotations\": []\n      },\n      \"logprobs\": null,\n      \"finish_reason\": \"stop\"\n    }\n  ],\n  \"usage\": {\n    \"prompt_tokens\": 20,\n    \"completion_tokens\": 2,\n    \"total_tokens\": 22,\n    \"prompt_tokens_details\": {\n      \"cached_tokens\": 0,\n      \"audio_tokens\": 0\n    },\n    \"completion_tokens_details\": {\n      \"reasoning_tokens\": 0,\n      \"audio_tokens\": 0,\n      \"accepted_prediction_tokens\": 0,\n      \"rejected_prediction_tokens\": 0\n    }\n  },\n  \"service_tier\": \"default\",\n  \"system_fingerprint\": \"fp_e665f7564b\"\n}\n"
-    headers:
-      Content-Type:
-      - application/json
-    status: 200 OK
-    code: 200
-    duration: 1.377235042s

providertests/testdata/TestSimple/openai-gpt-4o.yaml ๐Ÿ”—

@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 138
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"Say hi in Portuguese\",\"role\":\"user\"}],\"model\":\"gpt-4o\"}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    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: "{\n  \"id\": \"chatcmpl-CBolfurp0H2jFXSwJHVGbLWOYHhbM\",\n  \"object\": \"chat.completion\",\n  \"created\": 1756932795,\n  \"model\": \"gpt-4o-2024-08-06\",\n  \"choices\": [\n    {\n      \"index\": 0,\n      \"message\": {\n        \"role\": \"assistant\",\n        \"content\": \"Olรก! Como posso ajudar vocรช hoje?\",\n        \"refusal\": null,\n        \"annotations\": []\n      },\n      \"logprobs\": null,\n      \"finish_reason\": \"stop\"\n    }\n  ],\n  \"usage\": {\n    \"prompt_tokens\": 20,\n    \"completion_tokens\": 8,\n    \"total_tokens\": 28,\n    \"prompt_tokens_details\": {\n      \"cached_tokens\": 0,\n      \"audio_tokens\": 0\n    },\n    \"completion_tokens_details\": {\n      \"reasoning_tokens\": 0,\n      \"audio_tokens\": 0,\n      \"accepted_prediction_tokens\": 0,\n      \"rejected_prediction_tokens\": 0\n    }\n  },\n  \"service_tier\": \"default\",\n  \"system_fingerprint\": \"fp_f33640a400\"\n}\n"
-    headers:
-      Content-Type:
-      - application/json
-    status: 200 OK
-    code: 200
-    duration: 3.363218s

providertests/testdata/TestSimple/openrouter-kimi-k2.yaml ๐Ÿ”—

@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
-  - id: 0
-    request:
-      proto: HTTP/1.1
-      proto_major: 1
-      proto_minor: 1
-      content_length: 180
-      host: ""
-      body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"Say hi in Portuguese","role":"user"}],"model":"moonshotai/kimi-k2-0905","usage":{"include":true}}'
-      headers:
-        Accept:
-          - application/json
-        Content-Type:
-          - application/json
-        User-Agent:
-          - OpenAI/Go 2.3.0
-      url: https://openrouter.ai/api/v1/chat/completions
-      method: POST
-    response:
-      proto: HTTP/2.0
-      proto_major: 2
-      proto_minor: 0
-      content_length: -1
-      uncompressed: true
-      body: '{"id":"gen-1758536663-isAYACl4o5P2svHApNjR","provider":"DeepInfra","model":"moonshotai/kimi-k2-0905","object":"chat.completion","created":1758536663,"choices":[{"logprobs":null,"finish_reason":"stop","native_finish_reason":"stop","index":0,"message":{"role":"assistant","content":"Olรก!","refusal":null,"reasoning":null}}],"usage":{"prompt_tokens":20,"completion_tokens":3,"total_tokens":23,"cost":0.000016,"is_byok":false,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"cost_details":{"upstream_inference_cost":null,"upstream_inference_prompt_cost":0.00001,"upstream_inference_completions_cost":0.000006},"completion_tokens_details":{"reasoning_tokens":0,"image_tokens":0}}}'
-      headers:
-        Content-Type:
-          - application/json
-      status: 200 OK
-      code: 200
-      duration: 937.162041ms

providertests/testdata/TestStream/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,32 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 227
-    host: ""
-    body: "{\"max_tokens\":4096,\"messages\":[{\"content\":[{\"text\":\"Count from 1 to 3 in Spanish\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant\",\"type\":\"text\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.0
-    url: https://api.anthropic.com/v1/messages
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStream/google-gemini-2.5-flash.yaml ๐Ÿ”—

@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 188
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"Count from 1 to 3 in Spanish\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"}}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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\": \"Here you go:\\n\\n1.  **Uno**\\n2.  **Dos**\\n3.  **Tres**\"}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 16,\"candidatesTokenCount\": 25,\"totalTokenCount\": 74,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 16}],\"thoughtsTokenCount\": 33},\"modelVersion\": \"gemini-2.5-flash\",\"responseId\": \"jl3AaOWjGefyqtsPi_C6sAM\"}\r\n\r\n"
-    headers:
-      Content-Type:
-      - text/event-stream
-    status: 200 OK
-    code: 200
-    duration: 1.178272625s

providertests/testdata/TestStream/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 188
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"Count from 1 to 3 in Spanish\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"}}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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\": \"Of course! Here is how you count from 1 to 3 in Spanish:\\n\\n1.  **Uno**\"}],\"role\": \"model\"},\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 16,\"candidatesTokenCount\": 24,\"totalTokenCount\": 717,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 16}],\"thoughtsTokenCount\": 677},\"modelVersion\": \"gemini-2.5-pro\",\"responseId\": \"IIvAaJnINPn6qtsPoq_lgQM\"}\r\n\r\ndata: {\"candidates\": [{\"content\": {\"parts\": [{\"text\": \"\\n2.  **Dos**\\n3.  **Tres**\"}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 16,\"candidatesTokenCount\": 37,\"totalTokenCount\": 730,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 16}],\"thoughtsTokenCount\": 677},\"modelVersion\": \"gemini-2.5-pro\",\"responseId\": \"IIvAaJnINPn6qtsPoq_lgQM\"}\r\n\r\n"
-    headers:
-      Content-Type:
-      - text/event-stream
-    status: 200 OK
-    code: 200
-    duration: 6.777174625s

providertests/testdata/TestStream/openai-gpt-4o-mini.yaml ๐Ÿ”—

@@ -1,32 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 205
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"Count from 1 to 3 in Spanish\",\"role\":\"user\"}],\"model\":\"gpt-4o-mini\",\"stream_options\":{\"include_usage\":true},\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStream/openai-gpt-4o.yaml ๐Ÿ”—

@@ -1,32 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 200
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"Count from 1 to 3 in Spanish\",\"role\":\"user\"}],\"model\":\"gpt-4o\",\"stream_options\":{\"include_usage\":true},\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStream/openrouter-kimi-k2.yaml ๐Ÿ”—

@@ -1,32 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 242
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"Count from 1 to 3 in Spanish\",\"role\":\"user\"}],\"model\":\"moonshotai/kimi-k2-0905\",\"stream_options\":{\"include_usage\":true},\"usage\":{\"include\":true},\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://openrouter.ai/api/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithMultipleTools/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 812
-    host: ""
-    body: "{\"max_tokens\":4096,\"messages\":[{\"content\":[{\"text\":\"Add and multiply the number 2 and 3\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant. Always use both add and multiply at the same time.\",\"type\":\"text\"}],\"tool_choice\":{\"disable_parallel_tool_use\":false,\"type\":\"auto\"},\"tools\":[{\"input_schema\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"},\"name\":\"add\",\"description\":\"Add two numbers\"},{\"input_schema\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"},\"name\":\"multiply\",\"description\":\"Multiply two numbers\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.0
-    url: https://api.anthropic.com/v1/messages
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithMultipleTools/google-gemini-2.5-flash.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 787
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"Add and multiply the number 2 and 3\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant. Always use both add and multiply at the same time.\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Add two numbers\",\"name\":\"add\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"INTEGER\"},\"b\":{\"description\":\"second number\",\"type\":\"INTEGER\"}},\"required\":[\"a\",\"b\"],\"type\":\"OBJECT\"}},{\"description\":\"Multiply two numbers\",\"name\":\"multiply\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"INTEGER\"},\"b\":{\"description\":\"second number\",\"type\":\"INTEGER\"}},\"required\":[\"a\",\"b\"],\"type\":\"OBJECT\"}}]}]}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.25.1
-    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\": [{\"functionCall\": {\"name\": \"add\",\"args\": {\"a\": 2,\"b\": 3}},\"thoughtSignature\": \"CikB0e2Kb9Cbo981mR8v4MhGuu8kkazE7e5W2Yaehwjb6iqXgIQTnlkiEQptAdHtim/2wYeqmL8JTZVWRWzYrj1Yh72I2/C1joGgI7SMVWgEv1EcSoryWwxuWg8XnVTwE8etvpuv42AzaAjwdyTHeRpMZRFTwCsbs72ZcfZaNp5fGcdsULLk8ofUEgdjv3traQpoVe6f8gwCqgpnAdHtim8gitawv43Dk2C2hFdN2eoi7yDLD4TzcSJlI30wo3U2t3dO08m4RSdj9n8cItxy0Xgr2oMrYlx0gxObVepGmwdc0JVLiruE9sWE8FC/Jr4nq9avS6jXag4yzb1cmWSb9jqdmA==\"},{\"functionCall\": {\"name\": \"multiply\",\"args\": {\"a\": 2,\"b\": 3}}}],\"role\": \"model\"},\"finishReason\": \"STOP\",\"index\": 0}],\"usageMetadata\": {\"promptTokenCount\": 121,\"candidatesTokenCount\": 36,\"totalTokenCount\": 195,\"promptTokensDetails\": [{\"modality\": \"TEXT\",\"tokenCount\": 121}],\"thoughtsTokenCount\": 38},\"modelVersion\": \"gemini-2.5-flash\",\"responseId\": \"Cy7RaKyyIPGO28oPouv7mAg\"}\r\n\r\n"
-    headers:
-      Content-Type:
-      - text/event-stream
-    status: 200 OK
-    code: 200
-    duration: 974.596292ms
-- id: 1
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 1134
-    host: generativelanguage.googleapis.com

providertests/testdata/TestStreamWithMultipleTools/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 787
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"Add and multiply the number 2 and 3\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant. Always use both add and multiply at the same time.\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Add two numbers\",\"name\":\"add\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"INTEGER\"},\"b\":{\"description\":\"second number\",\"type\":\"INTEGER\"}},\"required\":[\"a\",\"b\"],\"type\":\"OBJECT\"}},{\"description\":\"Multiply two numbers\",\"name\":\"multiply\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"INTEGER\"},\"b\":{\"description\":\"second number\",\"type\":\"INTEGER\"}},\"required\":[\"a\",\"b\"],\"type\":\"OBJECT\"}}]}]}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.25.1
-    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

providertests/testdata/TestStreamWithMultipleTools/openai-gpt-4o-mini.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 835
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant. Always use both add and multiply at the same time.\",\"role\":\"system\"},{\"content\":\"Add and multiply the number 2 and 3\",\"role\":\"user\"}],\"model\":\"gpt-4o-mini\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"add\",\"strict\":false,\"description\":\"Add two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"multiply\",\"strict\":false,\"description\":\"Multiply two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithMultipleTools/openai-gpt-4o.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 830
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant. Always use both add and multiply at the same time.\",\"role\":\"system\"},{\"content\":\"Add and multiply the number 2 and 3\",\"role\":\"user\"}],\"model\":\"gpt-4o\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"add\",\"strict\":false,\"description\":\"Add two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"},{\"function\":{\"name\":\"multiply\",\"strict\":false,\"description\":\"Multiply two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithTools/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 553
-    host: ""
-    body: "{\"max_tokens\":4096,\"messages\":[{\"content\":[{\"text\":\"What is 15 + 27?\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant. Use the add tool to perform calculations.\",\"type\":\"text\"}],\"tool_choice\":{\"disable_parallel_tool_use\":false,\"type\":\"auto\"},\"tools\":[{\"input_schema\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"},\"name\":\"add\",\"description\":\"Add two numbers\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.0
-    url: https://api.anthropic.com/v1/messages
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithTools/google-gemini-2.5-flash.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 530
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"What is 15 + 27?\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant. Use the add tool to perform calculations.\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Add two numbers\",\"name\":\"add\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"INTEGER\"},\"b\":{\"description\":\"second number\",\"type\":\"INTEGER\"}},\"required\":[\"a\",\"b\"],\"type\":\"OBJECT\"}}]}]}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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

providertests/testdata/TestStreamWithTools/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 530
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"What is 15 + 27?\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant. Use the add tool to perform calculations.\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Add two numbers\",\"name\":\"add\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"INTEGER\"},\"b\":{\"description\":\"second number\",\"type\":\"INTEGER\"}},\"required\":[\"a\",\"b\"],\"type\":\"OBJECT\"}}]}]}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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

providertests/testdata/TestStreamWithTools/openai-gpt-4o-mini.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 532
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant. Use the add tool to perform calculations.\",\"role\":\"system\"},{\"content\":\"What is 15 + 27?\",\"role\":\"user\"}],\"model\":\"gpt-4o-mini\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"add\",\"strict\":false,\"description\":\"Add two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithTools/openai-gpt-4o.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 527
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant. Use the add tool to perform calculations.\",\"role\":\"system\"},{\"content\":\"What is 15 + 27?\",\"role\":\"user\"}],\"model\":\"gpt-4o\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"add\",\"strict\":false,\"description\":\"Add two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestStreamWithTools/openrouter-kimi-k2.yaml ๐Ÿ”—

@@ -1,90 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 569
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant. Use the add tool to perform calculations.\",\"role\":\"system\"},{\"content\":\"What is 15 + 27?\",\"role\":\"user\"}],\"model\":\"moonshotai/kimi-k2-0905\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"add\",\"strict\":false,\"description\":\"Add two numbers\",\"parameters\":{\"properties\":{\"a\":{\"description\":\"first number\",\"type\":\"integer\"},\"b\":{\"description\":\"second number\",\"type\":\"integer\"}},\"required\":[\"a\",\"b\"],\"type\":\"object\"}},\"type\":\"function\"}],\"usage\":{\"include\":true},\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://openrouter.ai/api/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestThinking/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 550
-    host: ""
-    body: "{\"max_tokens\":14096,\"messages\":[{\"content\":[{\"text\":\"What's the weather in Florence, Italy?\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant\",\"type\":\"text\"}],\"thinking\":{\"budget_tokens\":10000,\"type\":\"enabled\"},\"tool_choice\":{\"disable_parallel_tool_use\":false,\"type\":\"auto\"},\"tools\":[{\"input_schema\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"},\"name\":\"weather\",\"description\":\"Get weather information for a location\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.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

providertests/testdata/TestThinking/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,59 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 550
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"What's the weather in Florence, Italy?\"}],\"role\":\"user\"}],\"generationConfig\":{\"thinkingConfig\":{\"includeThoughts\":true,\"thinkingBudget\":128}},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Get weather information for a location\",\"name\":\"weather\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"STRING\"}},\"required\":[\"location\"],\"type\":\"OBJECT\"}}]}]}\n"
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.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

providertests/testdata/TestThinking/openai-gpt-5.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 458
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"What's the weather in Florence, Italy?\",\"role\":\"user\"}],\"model\":\"gpt-5\",\"reasoning_effort\":\"medium\",\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"weather\",\"strict\":false,\"description\":\"Get weather information for a location\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"}},\"type\":\"function\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    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

providertests/testdata/TestThinking/openrouter-glm-4.5.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 492
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"What's the weather in Florence, Italy?\",\"role\":\"user\"}],\"model\":\"z-ai/glm-4.5\",\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"weather\",\"strict\":false,\"description\":\"Get weather information for a location\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"}},\"type\":\"function\"}],\"reasoning\":{\"effort\":\"high\"},\"usage\":{\"include\":true}}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://openrouter.ai/api/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1
-    uncompressed: true

providertests/testdata/TestThinkingStreaming/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 564
-    host: ""
-    body: "{\"max_tokens\":14096,\"messages\":[{\"content\":[{\"text\":\"What's the weather in Florence, Italy?\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant\",\"type\":\"text\"}],\"thinking\":{\"budget_tokens\":10000,\"type\":\"enabled\"},\"tool_choice\":{\"disable_parallel_tool_use\":false,\"type\":\"auto\"},\"tools\":[{\"input_schema\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"},\"name\":\"weather\",\"description\":\"Get weather information for a location\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.0
-    url: https://api.anthropic.com/v1/messages
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestThinkingStreaming/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 550
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"What's the weather in Florence, Italy?\"}],\"role\":\"user\"}],\"generationConfig\":{\"thinkingConfig\":{\"includeThoughts\":true,\"thinkingBudget\":128}},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Get weather information for a location\",\"name\":\"weather\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"STRING\"}},\"required\":[\"location\"],\"type\":\"OBJECT\"}}]}]}\n"
-    form:
-      alt:
-      - sse
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.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

providertests/testdata/TestThinkingStreaming/openai-gpt-5.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 512
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"What's the weather in Florence, Italy?\",\"role\":\"user\"}],\"model\":\"gpt-5\",\"reasoning_effort\":\"medium\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"weather\",\"strict\":false,\"description\":\"Get weather information for a location\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"}},\"type\":\"function\"}],\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://api.openai.com/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestThinkingStreaming/openrouter-glm-4.5.yaml ๐Ÿ”—

@@ -1,61 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 516
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"What's the weather in Florence, Italy?\",\"role\":\"user\"}],\"model\":\"z-ai/glm-4.5\",\"stream_options\":{\"include_usage\":true},\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"weather\",\"strict\":false,\"description\":\"Get weather information for a location\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"}},\"type\":\"function\"}],\"usage\":{\"include\":true},\"stream\":true}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    url: https://openrouter.ai/api/v1/chat/completions
-    method: POST
-  response:
-    proto: HTTP/2.0
-    proto_major: 2
-    proto_minor: 0
-    content_length: -1

providertests/testdata/TestTool/anthropic-claude-sonnet.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 490
-    host: ""
-    body: "{\"max_tokens\":4096,\"messages\":[{\"content\":[{\"text\":\"What's the weather in Florence?\",\"type\":\"text\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant\",\"type\":\"text\"}],\"tool_choice\":{\"disable_parallel_tool_use\":false,\"type\":\"auto\"},\"tools\":[{\"input_schema\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"},\"name\":\"weather\",\"description\":\"Get weather information for a location\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.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: "{\"id\":\"msg_01EnEXYvvJsZqCkmy6JSJbmZ\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[{\"type\":\"text\",\"text\":\"I'll get the weather information for Florence for you.\"},{\"type\":\"tool_use\",\"id\":\"toolu_01DuHpbePNYnbErjgBf5iHsF\",\"name\":\"weather\",\"input\":{\"location\":\"Florence\"}}],\"stop_reason\":\"tool_use\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":392,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":63,\"service_tier\":\"standard\"}}"
-    headers:
-      Content-Type:
-      - application/json
-    status: 200 OK
-    code: 200
-    duration: 2.213104625s
-- id: 1
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 850
-    host: ""
-    body: "{\"max_tokens\":4096,\"messages\":[{\"content\":[{\"text\":\"What's the weather in Florence?\",\"type\":\"text\"}],\"role\":\"user\"},{\"content\":[{\"text\":\"I'll get the weather information for Florence for you.\",\"type\":\"text\"},{\"id\":\"toolu_01DuHpbePNYnbErjgBf5iHsF\",\"input\":{\"location\":\"Florence\"},\"name\":\"weather\",\"type\":\"tool_use\"}],\"role\":\"assistant\"},{\"content\":[{\"tool_use_id\":\"toolu_01DuHpbePNYnbErjgBf5iHsF\",\"content\":[{\"text\":\"40 C\",\"type\":\"text\"}],\"type\":\"tool_result\"}],\"role\":\"user\"}],\"model\":\"claude-sonnet-4-20250514\",\"system\":[{\"text\":\"You are a helpful assistant\",\"type\":\"text\"}],\"tool_choice\":{\"disable_parallel_tool_use\":false,\"type\":\"auto\"},\"tools\":[{\"input_schema\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"},\"name\":\"weather\",\"description\":\"Get weather information for a location\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - Anthropic/Go 1.10.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: "{\"id\":\"msg_01HyNDRGB4Vs42jfUPPcBaak\",\"type\":\"message\",\"role\":\"assistant\",\"model\":\"claude-sonnet-4-20250514\",\"content\":[{\"type\":\"text\",\"text\":\"The current temperature in Florence is 40ยฐC (104ยฐF). That's quite hot! Make sure to stay hydrated and seek shade or air conditioning if you're in the area.\"}],\"stop_reason\":\"end_turn\",\"stop_sequence\":null,\"usage\":{\"input_tokens\":470,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":42,\"service_tier\":\"standard\"}}"
-    headers:
-      Content-Type:
-      - application/json
-    status: 200 OK
-    code: 200
-    duration: 2.306400542s

providertests/testdata/TestTool/google-gemini-2.5-flash.yaml ๐Ÿ”—

@@ -1,59 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 481
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"What's the weather in Florence?\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Get weather information for a location\",\"name\":\"weather\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"STRING\"}},\"required\":[\"location\"],\"type\":\"OBJECT\"}}]}]}\n"
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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

providertests/testdata/TestTool/google-gemini-2.5-pro.yaml ๐Ÿ”—

@@ -1,59 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 481
-    host: generativelanguage.googleapis.com
-    body: "{\"contents\":[{\"parts\":[{\"text\":\"What's the weather in Florence?\"}],\"role\":\"user\"}],\"generationConfig\":{},\"systemInstruction\":{\"parts\":[{\"text\":\"You are a helpful assistant\"}],\"role\":\"user\"},\"toolConfig\":{\"functionCallingConfig\":{\"mode\":\"AUTO\"}},\"tools\":[{\"functionDeclarations\":[{\"description\":\"Get weather information for a location\",\"name\":\"weather\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"STRING\"}},\"required\":[\"location\"],\"type\":\"OBJECT\"}}]}]}\n"
-    headers:
-      Content-Type:
-      - application/json
-      User-Agent:
-      - google-genai-sdk/1.23.0 gl-go/go1.24.5
-    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

providertests/testdata/TestTool/openai-gpt-4o-mini.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 429
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"What's the weather in Florence?\",\"role\":\"user\"}],\"model\":\"gpt-4o-mini\",\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"weather\",\"strict\":false,\"description\":\"Get weather information for a location\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"}},\"type\":\"function\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    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

providertests/testdata/TestTool/openai-gpt-4o.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-- id: 0
-  request:
-    proto: HTTP/1.1
-    proto_major: 1
-    proto_minor: 1
-    content_length: 424
-    host: ""
-    body: "{\"messages\":[{\"content\":\"You are a helpful assistant\",\"role\":\"system\"},{\"content\":\"What's the weather in Florence?\",\"role\":\"user\"}],\"model\":\"gpt-4o\",\"tool_choice\":\"auto\",\"tools\":[{\"function\":{\"name\":\"weather\",\"strict\":false,\"description\":\"Get weather information for a location\",\"parameters\":{\"properties\":{\"location\":{\"description\":\"the city\",\"type\":\"string\"}},\"required\":[\"location\"],\"type\":\"object\"}},\"type\":\"function\"}]}"
-    headers:
-      Accept:
-      - application/json
-      Content-Type:
-      - application/json
-      User-Agent:
-      - OpenAI/Go 2.3.0
-    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

providertests/testdata/TestTool/openrouter-kimi-k2.yaml ๐Ÿ”—

@@ -1,63 +0,0 @@
----
-version: 2
-interactions:
-  - id: 0
-    request:
-      proto: HTTP/1.1
-      proto_major: 1
-      proto_minor: 1
-      content_length: 466
-      host: ""
-      body: '{"messages":[{"content":"You are a helpful assistant","role":"system"},{"content":"What''s the weather in Florence?","role":"user"}],"model":"moonshotai/kimi-k2-0905","tool_choice":"auto","tools":[{"function":{"name":"weather","strict":false,"description":"Get weather information for a location","parameters":{"properties":{"location":{"description":"the city","type":"string"}},"required":["location"],"type":"object"}},"type":"function"}],"usage":{"include":true}}'
-      headers:
-        Accept:
-          - application/json
-        Content-Type:
-          - application/json
-        User-Agent:
-          - OpenAI/Go 2.3.0
-      url: https://openrouter.ai/api/v1/chat/completions
-      method: POST
-    response:
-      proto: HTTP/2.0
-      proto_major: 2
-      proto_minor: 0
-      content_length: -1
-      uncompressed: true