Ctrl K
ComunidadGuías…

coderd/exp_chats_reasoning_mode_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package coderd_test

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/coder/coder/v2/coderd/coderdtest"
	"github.com/coder/coder/v2/coderd/util/ptr"
	"github.com/coder/coder/v2/codersdk"
	"github.com/coder/coder/v2/testutil"
)

func TestChatModelReasoningModePersistence(t *testing.T) {
	t.Parallel()

	// Model support is not validated so a newly released model needs no code change.
	for _, model := range []string{"gpt-5.6-sol", "gpt-4o", "gpt-daybreak-blue-latest", "gpt-7-nova"} {
		t.Run(model, func(t *testing.T) {
			t.Parallel()
			ctx := testutil.Context(t, testutil.WaitLong)
			client := newChatClient(t)
			firstUser := coderdtest.CreateFirstUser(t, client.Client)
			provider := createAIProviderForTest(t, client, "openai", "test-api-key")
			config := chatModelReasoningModeConfig("pro", nil)
			created, err := client.CreateChatModel(ctx, firstUser.OrganizationID, codersdk.CreateChatModelRequest{
				AIProviderID: &provider.ID,
				Model:        model,
				ContextLimit: ptr.Ref(int64(4096)),
				ModelConfig:  config,
			})
			require.NoError(t, err)
			require.Equal(t, config, created.ModelConfig)
			stored, err := client.ChatModel(ctx, created.OrganizationID, created.ID)
			require.NoError(t, err)
			require.Equal(t, config, stored.ModelConfig)

			updated, err := client.UpdateChatModel(ctx, created.OrganizationID, created.ID, codersdk.UpdateChatModelRequest{DisplayName: "renamed"})
			require.NoError(t, err)
			require.Equal(t, config, updated.ModelConfig)

			config = chatModelReasoningModeConfig("pro", ptr.Ref(true))
			updated, err = client.UpdateChatModel(ctx, created.OrganizationID, created.ID, codersdk.UpdateChatModelRequest{ModelConfig: config})
			require.NoError(t, err)
			require.Equal(t, config, updated.ModelConfig)
			stored, err = client.ChatModel(ctx, created.OrganizationID, created.ID)
			require.NoError(t, err)
			require.Equal(t, config, stored.ModelConfig)

			config = &codersdk.ChatModelCallConfig{
				ProviderOptions: &codersdk.ChatModelProviderOptions{
					OpenAI: &codersdk.ChatModelOpenAIProviderOptions{ReasoningSummary: ptr.Ref("detailed")},
				},
			}
			updated, err = client.UpdateChatModel(ctx, created.OrganizationID, created.ID, codersdk.UpdateChatModelRequest{ModelConfig: config})
			require.NoError(t, err)
			require.Equal(t, config, updated.ModelConfig)
			stored, err = client.ChatModel(ctx, created.OrganizationID, created.ID)
			require.NoError(t, err)
			require.Equal(t, config, stored.ModelConfig)

			updated, err = client.UpdateChatModel(ctx, created.OrganizationID, created.ID, codersdk.UpdateChatModelRequest{
				Model:       "gpt-4o",
				ModelConfig: &codersdk.ChatModelCallConfig{},
			})
			require.NoError(t, err)
			require.Nil(t, updated.ModelConfig)
			stored, err = client.ChatModel(ctx, created.OrganizationID, created.ID)
			require.NoError(t, err)
			require.Nil(t, stored.ModelConfig)
			require.Equal(t, "gpt-4o", stored.Model)
		})
	}
}

func TestChatModelReasoningModeInvalid(t *testing.T) {
	t.Parallel()

	for _, tc := range []struct {
		name      string
		provider  string
		model     string
		mode      string
		responses *bool
	}{
		{name: "UnknownMode", provider: "openai", model: "gpt-5.6", mode: "turbo"},
		{name: "StandardMode", provider: "openai", model: "gpt-5.6", mode: "standard"},
		{name: "EmptyMode", provider: "openai", model: "gpt-5.6", mode: ""},
		{name: "UppercaseMode", provider: "openai", model: "gpt-5.6", mode: "PRO"},
		{name: "WhitespaceMode", provider: "openai", model: "gpt-5.6", mode: " pro "},
		{name: "ChatCompletions", provider: "openai", model: "gpt-5.6", mode: "pro", responses: ptr.Ref(false)},
		{name: "Anthropic", provider: "anthropic", model: "gpt-5.6", mode: "pro", responses: ptr.Ref(true)},
		{name: "Azure", provider: "azure", model: "gpt-5.6", mode: "pro", responses: ptr.Ref(true)},
		{name: "OpenAICompat", provider: "openai-compat", model: "gpt-5.6", mode: "pro", responses: ptr.Ref(true)},
	} {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			ctx := testutil.Context(t, testutil.WaitLong)
			client := newChatClient(t)
			firstUser := coderdtest.CreateFirstUser(t, client.Client)
			provider := createAIProviderForTest(t, client, tc.provider, "test-api-key")
			request := codersdk.CreateChatModelRequest{
				AIProviderID: &provider.ID,
				Model:        tc.model,
				ContextLimit: ptr.Ref(int64(4096)),
			}
			created, err := client.CreateChatModel(ctx, firstUser.OrganizationID, request)
			require.NoError(t, err)

			config := chatModelReasoningModeConfig(tc.mode, tc.responses)
			_, err = client.UpdateChatModel(ctx, created.OrganizationID, created.ID, codersdk.UpdateChatModelRequest{ModelConfig: config})
			sdkErr := requireSDKError(t, err, http.StatusBadRequest)
			require.Contains(t, sdkErr.Detail, "reasoning_mode")
			stored, err := client.ChatModel(ctx, created.OrganizationID, created.ID)
			require.NoError(t, err)
			require.Nil(t, stored.ModelConfig)

			require.NoError(t, client.DeleteChatModel(ctx, created.OrganizationID, created.ID))
			request.ModelConfig = config
			_, err = client.CreateChatModel(ctx, firstUser.OrganizationID, request)
			sdkErr = requireSDKError(t, err, http.StatusBadRequest)
			require.Contains(t, sdkErr.Detail, "reasoning_mode")
		})
	}
}

func TestChatModelReasoningModePartialUpdate(t *testing.T) {
	t.Parallel()

	for _, change := range []string{"Provider", "AzureProvider", "Transport"} {
		t.Run(change, func(t *testing.T) {
			t.Parallel()
			ctx := testutil.Context(t, testutil.WaitLong)
			client := newChatClient(t)
			firstUser := coderdtest.CreateFirstUser(t, client.Client)
			provider := createAIProviderForTest(t, client, "openai", "test-api-key")
			config := chatModelReasoningModeConfig("pro", nil)
			created, err := client.CreateChatModel(ctx, firstUser.OrganizationID, codersdk.CreateChatModelRequest{
				AIProviderID: &provider.ID,
				Model:        "gpt-5.6",
				ContextLimit: ptr.Ref(int64(4096)),
				ModelConfig:  config,
			})
			require.NoError(t, err)

			var request codersdk.UpdateChatModelRequest
			switch change {
			case "Provider", "AzureProvider":
				providerType := "openai-compat"
				if change == "AzureProvider" {
					providerType = "azure"
				}
				other := createAIProviderForTest(t, client, providerType, "test-api-key")
				request.AIProviderID = &other.ID
			case "Transport":
				request.ModelConfig = chatModelReasoningModeConfig("pro", ptr.Ref(false))
			}
			_, err = client.UpdateChatModel(ctx, created.OrganizationID, created.ID, request)
			sdkErr := requireSDKError(t, err, http.StatusBadRequest)
			require.Contains(t, sdkErr.Detail, "reasoning_mode")
			stored, err := client.ChatModel(ctx, created.OrganizationID, created.ID)
			require.NoError(t, err)
			require.Equal(t, created, stored)

			request.ModelConfig = &codersdk.ChatModelCallConfig{}
			updated, err := client.UpdateChatModel(ctx, created.OrganizationID, created.ID, request)
			require.NoError(t, err)
			require.Nil(t, updated.ModelConfig)
		})
	}
}

func chatModelReasoningModeConfig(mode string, responses *bool) *codersdk.ChatModelCallConfig {
	config := &codersdk.ChatModelCallConfig{
		ProviderOptions: &codersdk.ChatModelProviderOptions{
			OpenAI: &codersdk.ChatModelOpenAIProviderOptions{ReasoningMode: &mode},
		},
	}
	if responses != nil {
		config.OpenAIConfig = &codersdk.ChatModelOpenAIConfig{UseResponsesAPI: responses}
	}
	return config
}