mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add Anthropic 1M context window toggle for Agents model configs (#27257)
This commit is contained in:
+25
-20
@@ -338,10 +338,11 @@ func (p *Server) resolveAdvisorModelOverride(
|
||||
return fallbackModel, fallbackCallConfig, nil
|
||||
}
|
||||
overrideModel, err := p.newModel(ctx, modelClientRequest{
|
||||
Chat: chat,
|
||||
ModelName: overrideConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
Chat: chat,
|
||||
ModelName: overrideConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
ConfigOptions: overrideConfig.Options,
|
||||
}, route, modelOpts)
|
||||
if err != nil {
|
||||
if overrideConfig.AIProviderID.Valid {
|
||||
@@ -2397,10 +2398,11 @@ func (p *Server) prepareManualTitleDebugRun(
|
||||
debugModelErr = routeErr
|
||||
} else {
|
||||
debugModel, debugModelErr = p.newModel(ctx, modelClientRequest{
|
||||
Chat: chat,
|
||||
ModelName: modelConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
Chat: chat,
|
||||
ModelName: modelConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
ConfigOptions: modelConfig.Options,
|
||||
}, route, debugOpts)
|
||||
}
|
||||
switch {
|
||||
@@ -2591,10 +2593,11 @@ func (p *Server) resolveManualTitleModel(
|
||||
return p.resolveFallbackManualTitleModel(ctx, chat, modelOpts)
|
||||
}
|
||||
model, err := p.newModel(ctx, modelClientRequest{
|
||||
Chat: chat,
|
||||
ModelName: config.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
Chat: chat,
|
||||
ModelName: config.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
ConfigOptions: config.Options,
|
||||
}, route, modelOpts)
|
||||
if err != nil {
|
||||
p.logger.Debug(ctx, "manual title preferred model unavailable",
|
||||
@@ -2625,10 +2628,11 @@ func (p *Server) resolveFallbackManualTitleModel(
|
||||
return nil, database.ChatModelConfig{}, err
|
||||
}
|
||||
model, err := p.newModel(ctx, modelClientRequest{
|
||||
Chat: chat,
|
||||
ModelName: config.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
Chat: chat,
|
||||
ModelName: config.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
ConfigOptions: config.Options,
|
||||
}, route, modelOpts)
|
||||
if err != nil {
|
||||
return nil, database.ChatModelConfig{}, xerrors.Errorf(
|
||||
@@ -3913,10 +3917,11 @@ func (p *Server) resolveChatModel(
|
||||
}
|
||||
|
||||
model, debugEnabled, err = p.newDebugAwareModel(ctx, modelClientRequest{
|
||||
Chat: chat,
|
||||
ModelName: dbConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
Chat: chat,
|
||||
ModelName: dbConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
ConfigOptions: dbConfig.Options,
|
||||
}, route, modelOpts)
|
||||
if err != nil {
|
||||
return nil, database.ChatModelConfig{}, aiGatewayModelRoute{}, false, "", "", xerrors.Errorf(
|
||||
|
||||
@@ -53,6 +53,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatadvisor"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatdebug"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatprompt"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatprovider"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatsanitize"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chatstate"
|
||||
"github.com/coder/coder/v2/coderd/x/chatd/chattest"
|
||||
@@ -7118,6 +7119,62 @@ func TestActiveServer_AnthropicSanitizesProviderToolBeforeRequest(t *testing.T)
|
||||
require.Contains(t, body, "redacted-payload")
|
||||
}
|
||||
|
||||
func TestActiveServer_AnthropicContext1MBetaHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
runChat := func(t *testing.T, callConfig *codersdk.ChatModelCallConfig) []chattest.AnthropicRequest {
|
||||
t.Helper()
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
requests := newAnthropicRequestRecorder()
|
||||
anthropicURL := chattest.NewAnthropic(t, func(req *chattest.AnthropicRequest) chattest.AnthropicResponse {
|
||||
requests.record(req)
|
||||
if !req.Stream {
|
||||
return chattest.AnthropicNonStreamingResponse("title")
|
||||
}
|
||||
return chattest.AnthropicStreamingResponse(chattest.AnthropicTextChunks("done")...)
|
||||
})
|
||||
user, org, model := seedAnthropicChatDependencies(t, db, anthropicURL)
|
||||
if callConfig != nil {
|
||||
model = updateChatModelCallConfig(t, db, model, *callConfig)
|
||||
}
|
||||
|
||||
server := newActiveTestServer(t, db, ps, func(cfg *chatd.Config) {
|
||||
cfg.AIBridgeTransportFactory = chatAIGatewayTransportFactoryPointer(chattest.NewMockAIBridgeTransport(t, anthropicURL, chattest.WithPreservePath()))
|
||||
})
|
||||
chat := createChatThroughServer(ctx, t, db, server, org.ID, user.ID, model.ID, "hello")
|
||||
waitForChatStatus(ctx, t, db, chat.ID, database.ChatStatusWaiting)
|
||||
|
||||
generationRequests := filterAnthropicStreamingRequests(requests.all())
|
||||
require.NotEmpty(t, generationRequests)
|
||||
return generationRequests
|
||||
}
|
||||
|
||||
t.Run("enabled sends beta header", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
generationRequests := runChat(t, &codersdk.ChatModelCallConfig{
|
||||
ProviderOptions: &codersdk.ChatModelProviderOptions{
|
||||
Anthropic: &codersdk.ChatModelAnthropicProviderOptions{
|
||||
Context1MEnabled: ptr.Ref(true),
|
||||
},
|
||||
},
|
||||
})
|
||||
for _, req := range generationRequests {
|
||||
require.Equal(t, chatprovider.AnthropicBetaContext1M, req.Header.Get(chatprovider.HeaderAnthropicBeta))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unset omits beta header", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
generationRequests := runChat(t, nil)
|
||||
for _, req := range generationRequests {
|
||||
require.Empty(t, req.Header.Get(chatprovider.HeaderAnthropicBeta))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestActiveServer_AnthropicProviderToolPreRequestGuard(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -874,6 +874,30 @@ func CoderHeaders(chat database.Chat) map[string]string {
|
||||
return h
|
||||
}
|
||||
|
||||
// AnthropicBetaContext1M is the beta token for Anthropic's 1M context window.
|
||||
const AnthropicBetaContext1M = "context-1m-2025-08-07"
|
||||
|
||||
// HeaderAnthropicBeta names Anthropic's beta feature header.
|
||||
const HeaderAnthropicBeta = "Anthropic-Beta"
|
||||
|
||||
// BetaHeadersFromCallConfig returns beta feature headers for Anthropic and
|
||||
// Bedrock calls.
|
||||
func BetaHeadersFromCallConfig(providerName string, config *codersdk.ChatModelCallConfig) map[string]string {
|
||||
if config == nil || config.ProviderOptions == nil || config.ProviderOptions.Anthropic == nil {
|
||||
return nil
|
||||
}
|
||||
enabled := config.ProviderOptions.Anthropic.Context1MEnabled
|
||||
if enabled == nil || !*enabled {
|
||||
return nil
|
||||
}
|
||||
switch NormalizeProvider(providerName) {
|
||||
case fantasyanthropic.Name, fantasybedrock.Name:
|
||||
return map[string]string{HeaderAnthropicBeta: AnthropicBetaContext1M}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ModelFromConfig resolves a provider/model pair and constructs a fantasy
|
||||
// language model client using the provided provider credentials. The
|
||||
// userAgent is sent as the User-Agent header on every outgoing LLM
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package chatprovider_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"io"
|
||||
@@ -1375,6 +1376,140 @@ func TestModelFromConfig_ExtraHeaders(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBetaHeadersFromCallConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
configWith1M := func(enabled *bool) *codersdk.ChatModelCallConfig {
|
||||
return &codersdk.ChatModelCallConfig{
|
||||
ProviderOptions: &codersdk.ChatModelProviderOptions{
|
||||
Anthropic: &codersdk.ChatModelAnthropicProviderOptions{
|
||||
Context1MEnabled: enabled,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
beta := map[string]string{
|
||||
chatprovider.HeaderAnthropicBeta: chatprovider.AnthropicBetaContext1M,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
provider string
|
||||
config *codersdk.ChatModelCallConfig
|
||||
want map[string]string
|
||||
}{
|
||||
{name: "NilConfig", provider: fantasyanthropic.Name, config: nil, want: nil},
|
||||
{name: "NilProviderOptions", provider: fantasyanthropic.Name, config: &codersdk.ChatModelCallConfig{}, want: nil},
|
||||
{name: "NilAnthropicOptions", provider: fantasyanthropic.Name, config: &codersdk.ChatModelCallConfig{ProviderOptions: &codersdk.ChatModelProviderOptions{}}, want: nil},
|
||||
{name: "Unset", provider: fantasyanthropic.Name, config: configWith1M(nil), want: nil},
|
||||
{name: "Disabled", provider: fantasyanthropic.Name, config: configWith1M(ptr.Ref(false)), want: nil},
|
||||
{name: "EnabledAnthropic", provider: fantasyanthropic.Name, config: configWith1M(ptr.Ref(true)), want: beta},
|
||||
{name: "EnabledBedrock", provider: fantasybedrock.Name, config: configWith1M(ptr.Ref(true)), want: beta},
|
||||
{name: "EnabledOpenAI", provider: fantasyopenai.Name, config: configWith1M(ptr.Ref(true)), want: nil},
|
||||
{name: "EnabledUnknownProvider", provider: "does-not-exist", config: configWith1M(ptr.Ref(true)), want: nil},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
require.Equal(t, tt.want, chatprovider.BetaHeadersFromCallConfig(tt.provider, tt.config))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateHello(ctx context.Context, model fantasy.LanguageModel) error {
|
||||
_, err := model.Generate(ctx, fantasy.Call{
|
||||
Prompt: []fantasy.Message{
|
||||
{
|
||||
Role: fantasy.MessageRoleUser,
|
||||
Content: []fantasy.MessagePart{fantasy.TextPart{Text: "hello"}},
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func TestModelFromConfig_AnthropicBetaExtraHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
called := make(chan struct{})
|
||||
serverURL := chattest.NewAnthropic(t, func(req *chattest.AnthropicRequest) chattest.AnthropicResponse {
|
||||
assert.Equal(t, chatprovider.AnthropicBetaContext1M, req.Header.Get(chatprovider.HeaderAnthropicBeta))
|
||||
close(called)
|
||||
return chattest.AnthropicNonStreamingResponse("hello")
|
||||
})
|
||||
|
||||
keys := chatprovider.ProviderAPIKeys{
|
||||
ByProvider: map[string]string{fantasyanthropic.Name: "test-key"},
|
||||
BaseURLByProvider: map[string]string{fantasyanthropic.Name: serverURL},
|
||||
}
|
||||
betaHeaders := map[string]string{
|
||||
chatprovider.HeaderAnthropicBeta: chatprovider.AnthropicBetaContext1M,
|
||||
}
|
||||
model, err := chatprovider.ModelFromConfig(fantasyanthropic.Name, "claude-sonnet-4-20250514", keys, chatprovider.UserAgent(), betaHeaders, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, generateHello(ctx, model))
|
||||
_ = testutil.TryReceive(ctx, t, called)
|
||||
}
|
||||
|
||||
// The Anthropic SDK's Bedrock middleware moves Anthropic-Beta into the
|
||||
// request body before SigV4 signing.
|
||||
func TestModelFromConfig_BedrockBetaExtraHeader(t *testing.T) {
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
|
||||
t.Setenv("AWS_ACCESS_KEY_ID", "test-access-key")
|
||||
t.Setenv("AWS_SECRET_ACCESS_KEY", "test-secret-key")
|
||||
t.Setenv("AWS_SESSION_TOKEN", "test-session-token")
|
||||
|
||||
type requestCapture struct {
|
||||
AnthropicBeta string
|
||||
Authorization string
|
||||
Body string
|
||||
ReadError error
|
||||
}
|
||||
|
||||
requests := make(chan requestCapture, 1)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
|
||||
requests <- requestCapture{
|
||||
AnthropicBeta: r.Header.Get(chatprovider.HeaderAnthropicBeta),
|
||||
Authorization: r.Header.Get("Authorization"),
|
||||
Body: string(body),
|
||||
ReadError: err,
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(bedrockNonStreamingResponse())
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
model, err := chatprovider.ModelFromConfig(
|
||||
fantasybedrock.Name,
|
||||
"anthropic.claude-opus-4-6-v1",
|
||||
chatprovider.ProviderAPIKeys{
|
||||
ByProvider: map[string]string{fantasybedrock.Name: ""},
|
||||
BaseURLByProvider: map[string]string{fantasybedrock.Name: server.URL},
|
||||
RegionByProvider: map[string]string{fantasybedrock.Name: "us-east-2"},
|
||||
},
|
||||
chatprovider.UserAgent(),
|
||||
map[string]string{
|
||||
chatprovider.HeaderAnthropicBeta: chatprovider.AnthropicBetaContext1M,
|
||||
},
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, generateHello(ctx, model))
|
||||
|
||||
got := testutil.TryReceive(ctx, t, requests)
|
||||
require.NoError(t, got.ReadError)
|
||||
require.Empty(t, got.AnthropicBeta)
|
||||
require.Contains(t, got.Authorization, "AWS4-HMAC-SHA256")
|
||||
require.Contains(t, got.Body, `"anthropic_beta":["context-1m-2025-08-07"]`)
|
||||
}
|
||||
|
||||
// TestModelFromConfig_AnthropicPDFFilePartReachesProvider pins the end-to-end
|
||||
// path that lets a user-uploaded PDF actually reach Claude/Bedrock: a
|
||||
// fantasy.FilePart with MediaType "application/pdf" must be serialized as an
|
||||
|
||||
@@ -2,6 +2,7 @@ package chatd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"charm.land/fantasy"
|
||||
@@ -18,6 +19,9 @@ type modelClientRequest struct {
|
||||
ModelName string
|
||||
UserAgent string
|
||||
ExtraHeaders map[string]string
|
||||
// ConfigOptions holds the model config row's Options JSONB; empty for
|
||||
// paths without a config row.
|
||||
ConfigOptions json.RawMessage
|
||||
}
|
||||
|
||||
type modelBuildOptions struct {
|
||||
|
||||
@@ -3,6 +3,7 @@ package chatd
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -166,16 +167,48 @@ func (p *Server) newModel(
|
||||
}
|
||||
|
||||
config := fantasyConfigForAIBridge(route.Provider.Type)
|
||||
extraHeaders, err := mergeConfigBetaHeaders(req.ExtraHeaders, config.ProviderHint, req.ConfigOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newLanguageModel(
|
||||
config.ProviderHint,
|
||||
req.ModelName,
|
||||
config.Keys,
|
||||
req.UserAgent,
|
||||
req.ExtraHeaders,
|
||||
extraHeaders,
|
||||
&http.Client{Transport: baseRT},
|
||||
)
|
||||
}
|
||||
|
||||
// mergeConfigBetaHeaders never mutates extraHeaders; existing entries win
|
||||
// over config-derived ones.
|
||||
func mergeConfigBetaHeaders(
|
||||
extraHeaders map[string]string,
|
||||
providerHint string,
|
||||
configOptions json.RawMessage,
|
||||
) (map[string]string, error) {
|
||||
if len(configOptions) == 0 {
|
||||
return extraHeaders, nil
|
||||
}
|
||||
var callConfig codersdk.ChatModelCallConfig
|
||||
if err := json.Unmarshal(configOptions, &callConfig); err != nil {
|
||||
return nil, xerrors.Errorf("parse model config options: %w", err)
|
||||
}
|
||||
betaHeaders := chatprovider.BetaHeadersFromCallConfig(providerHint, &callConfig)
|
||||
if len(betaHeaders) == 0 {
|
||||
return extraHeaders, nil
|
||||
}
|
||||
merged := make(map[string]string, len(extraHeaders)+len(betaHeaders))
|
||||
for name, value := range betaHeaders {
|
||||
merged[name] = value
|
||||
}
|
||||
for name, value := range extraHeaders {
|
||||
merged[name] = value
|
||||
}
|
||||
return merged, nil
|
||||
}
|
||||
|
||||
type aibridgeFantasyConfig struct {
|
||||
ProviderHint string
|
||||
Keys chatprovider.ProviderAPIKeys
|
||||
|
||||
@@ -95,10 +95,11 @@ func (p *Server) resolveTitleGenerationModelOverride(
|
||||
return database.ChatModelConfig{}, nil, aiGatewayModelRoute{}, true, err
|
||||
}
|
||||
model, err := p.newModel(ctx, modelClientRequest{
|
||||
Chat: chat,
|
||||
ModelName: modelConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
Chat: chat,
|
||||
ModelName: modelConfig.Model,
|
||||
UserAgent: chatprovider.UserAgent(),
|
||||
ExtraHeaders: chatprovider.CoderHeaders(chat),
|
||||
ConfigOptions: modelConfig.Options,
|
||||
}, route, modelOpts)
|
||||
if err != nil {
|
||||
return database.ChatModelConfig{}, nil, aiGatewayModelRoute{}, true, xerrors.Errorf(
|
||||
|
||||
@@ -1354,6 +1354,7 @@ type ChatModelAnthropicProviderOptions struct {
|
||||
WebSearchEnabled *bool `json:"web_search_enabled,omitempty" description:"Enable Anthropic web search tool for grounding responses with real-time information"`
|
||||
AllowedDomains []string `json:"allowed_domains,omitempty" label:"Web Search: Allowed Domains" description:"Restrict web search to these domains (cannot be used with blocked_domains)" visible_when:"web_search_enabled" conflicts_with:"blocked_domains"`
|
||||
BlockedDomains []string `json:"blocked_domains,omitempty" label:"Web Search: Blocked Domains" description:"Block web search on these domains (cannot be used with allowed_domains)" visible_when:"web_search_enabled" conflicts_with:"allowed_domains"`
|
||||
Context1MEnabled *bool `json:"context_1m_enabled,omitempty" label:"1M Context Window" description:"Send the anthropic-beta context-1m-2025-08-07 header to unlock the 1M token context window on supported Claude models. Pair with a matching Context Limit. Long-context pricing and higher latency may apply above 200K tokens."`
|
||||
}
|
||||
|
||||
// ChatModelGoogleThinkingConfig configures Google thinking behavior.
|
||||
|
||||
@@ -188,10 +188,11 @@ fields appear dynamically in the admin UI when you select a provider.
|
||||
|
||||
#### Anthropic
|
||||
|
||||
| Option | Description |
|
||||
|------------------------|------------------------------------------------------------------|
|
||||
| Thinking Budget Tokens | Maximum tokens allocated for extended thinking. |
|
||||
| Effort | Thinking effort level (`low`, `medium`, `high`, `xhigh`, `max`). |
|
||||
| Option | Description |
|
||||
|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Thinking Budget Tokens | Maximum tokens allocated for extended thinking. |
|
||||
| Effort | Thinking effort level (`low`, `medium`, `high`, `xhigh`, `max`). |
|
||||
| 1M Context Window | Sends the `anthropic-beta: context-1m-2025-08-07` header to unlock the 1M token context window on supported Claude models. Pair it with a raised Context Limit, which still controls compaction. Long-context pricing may apply. |
|
||||
|
||||
#### OpenAI
|
||||
|
||||
|
||||
@@ -169,6 +169,15 @@
|
||||
"input_type": "json",
|
||||
"visible_when": "web_search_enabled",
|
||||
"conflicts_with": ["allowed_domains"]
|
||||
},
|
||||
{
|
||||
"json_name": "context_1m_enabled",
|
||||
"go_name": "Context1MEnabled",
|
||||
"type": "boolean",
|
||||
"description": "Send the anthropic-beta context-1m-2025-08-07 header to unlock the 1M token context window on supported Claude models. Pair with a matching Context Limit. Long-context pricing and higher latency may apply above 200K tokens.",
|
||||
"label": "1M Context Window",
|
||||
"required": false,
|
||||
"input_type": "select"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Generated
+1
@@ -2526,6 +2526,7 @@ export interface ChatModelAnthropicProviderOptions {
|
||||
readonly web_search_enabled?: boolean;
|
||||
readonly allowed_domains?: readonly string[];
|
||||
readonly blocked_domains?: readonly string[];
|
||||
readonly context_1m_enabled?: boolean;
|
||||
}
|
||||
|
||||
// From codersdk/chats.go
|
||||
|
||||
@@ -324,6 +324,22 @@ describe("extractModelConfigFormState", () => {
|
||||
expect(anthropic.disableParallelToolUse).toBe("false");
|
||||
});
|
||||
|
||||
it("extracts Anthropic 1M context window option", () => {
|
||||
const model: TypesGen.ChatModelConfig = {
|
||||
...baseChatModelConfig,
|
||||
model_config: {
|
||||
provider_options: {
|
||||
anthropic: {
|
||||
context_1m_enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = extractModelConfigFormState(model);
|
||||
const anthropic = result.anthropic as Record<string, unknown>;
|
||||
expect(anthropic.context1mEnabled).toBe("true");
|
||||
});
|
||||
|
||||
it("extracts Google provider options with safety settings", () => {
|
||||
const safetySettings = [{ category: "harm", threshold: "block" }];
|
||||
const model: TypesGen.ChatModelConfig = {
|
||||
@@ -835,6 +851,17 @@ describe("buildModelConfigFromForm", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("builds Anthropic options with 1M context window enabled", () => {
|
||||
const result = buildModelConfigFromForm(
|
||||
"anthropic",
|
||||
formWith({ anthropic: { context1mEnabled: "true" } }),
|
||||
);
|
||||
expect(result.fieldErrors).toEqual({});
|
||||
expect(result.modelConfig?.provider_options?.anthropic).toEqual({
|
||||
context_1m_enabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("builds Anthropic options with thinking budget", () => {
|
||||
const result = buildModelConfigFromForm(
|
||||
"anthropic",
|
||||
|
||||
Reference in New Issue
Block a user