feat(codersdk): generate chat model provider options schema from Go structs (#22568)

This commit is contained in:
Kyle Carberry
2026-03-03 16:29:58 -05:00
committed by GitHub
parent 5b1cf4a6a3
commit f758443f44
6 changed files with 1005 additions and 77 deletions
+6
View File
@@ -427,6 +427,7 @@ SITE_GEN_FILES := \
site/src/api/typesGenerated.ts \
site/src/api/rbacresourcesGenerated.ts \
site/src/api/countriesGenerated.ts \
site/src/api/chatModelOptionsGenerated.json \
site/src/theme/icons.json
site/out/index.html: \
@@ -721,6 +722,7 @@ gen/mark-fresh:
coderd/rbac/scopes_constants_gen.go \
site/src/api/rbacresourcesGenerated.ts \
site/src/api/countriesGenerated.ts \
site/src/api/chatModelOptionsGenerated.json \
docs/admin/integrations/prometheus.md \
docs/reference/cli/index.md \
docs/admin/security/audit-logs.md \
@@ -917,6 +919,10 @@ site/src/api/countriesGenerated.ts: site/node_modules/.installed scripts/typegen
./scripts/biome_format.sh src/api/countriesGenerated.ts
touch "$@"
site/src/api/chatModelOptionsGenerated.json: scripts/modeloptionsgen/main.go codersdk/chats.go
go run ./scripts/modeloptionsgen/main.go | tail -n +2 > "$@"
cd site && pnpm biome format --write src/api/chatModelOptionsGenerated.json
scripts/metricsdocgen/generated_metrics: $(GO_SRC_FILES)
go run ./scripts/metricsdocgen/scanner > $@
+76 -76
View File
@@ -246,134 +246,134 @@ type ChatModelProviderOptions struct {
// ChatModelOpenAIProviderOptions configures OpenAI provider behavior.
type ChatModelOpenAIProviderOptions struct {
Include []string `json:"include,omitempty"`
Instructions *string `json:"instructions,omitempty"`
LogitBias map[string]int64 `json:"logit_bias,omitempty"`
LogProbs *bool `json:"log_probs,omitempty"`
TopLogProbs *int64 `json:"top_log_probs,omitempty"`
MaxToolCalls *int64 `json:"max_tool_calls,omitempty"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
User *string `json:"user,omitempty"`
ReasoningEffort *string `json:"reasoning_effort,omitempty"`
ReasoningSummary *string `json:"reasoning_summary,omitempty"`
MaxCompletionTokens *int64 `json:"max_completion_tokens,omitempty"`
TextVerbosity *string `json:"text_verbosity,omitempty"`
Prediction map[string]any `json:"prediction,omitempty"`
Store *bool `json:"store,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
PromptCacheKey *string `json:"prompt_cache_key,omitempty"`
SafetyIdentifier *string `json:"safety_identifier,omitempty"`
ServiceTier *string `json:"service_tier,omitempty"`
StructuredOutputs *bool `json:"structured_outputs,omitempty"`
StrictJSONSchema *bool `json:"strict_json_schema,omitempty"`
Include []string `json:"include,omitempty" description:"Model names to include in discovery" hidden:"true"`
Instructions *string `json:"instructions,omitempty" description:"System-level instructions prepended to the conversation" hidden:"true"`
LogitBias map[string]int64 `json:"logit_bias,omitempty" description:"Token IDs mapped to bias values from -100 to 100" hidden:"true"`
LogProbs *bool `json:"log_probs,omitempty" description:"Whether to return log probabilities of output tokens" hidden:"true"`
TopLogProbs *int64 `json:"top_log_probs,omitempty" description:"Number of most likely tokens to return log probabilities for" hidden:"true"`
MaxToolCalls *int64 `json:"max_tool_calls,omitempty" description:"Maximum number of tool calls per response"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty" description:"Whether the model may make multiple tool calls in parallel"`
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
ReasoningEffort *string `json:"reasoning_effort,omitempty" description:"Controls the level of reasoning effort" enum:"none,minimal,low,medium,high,xhigh"`
ReasoningSummary *string `json:"reasoning_summary,omitempty" description:"Controls whether reasoning tokens are summarized in the response"`
MaxCompletionTokens *int64 `json:"max_completion_tokens,omitempty" description:"Upper bound on tokens the model may generate"`
TextVerbosity *string `json:"text_verbosity,omitempty" description:"Controls the verbosity of the text response" enum:"low,medium,high"`
Prediction map[string]any `json:"prediction,omitempty" description:"Predicted output content to speed up responses" hidden:"true"`
Store *bool `json:"store,omitempty" description:"Whether to store the output for model distillation or evals" hidden:"true"`
Metadata map[string]any `json:"metadata,omitempty" description:"Arbitrary metadata to attach to the request" hidden:"true"`
PromptCacheKey *string `json:"prompt_cache_key,omitempty" description:"Key for enabling cross-request prompt caching" hidden:"true"`
SafetyIdentifier *string `json:"safety_identifier,omitempty" description:"Developer-specific safety identifier for the request" hidden:"true"`
ServiceTier *string `json:"service_tier,omitempty" description:"Latency tier to use for processing the request"`
StructuredOutputs *bool `json:"structured_outputs,omitempty" description:"Whether to enable structured JSON output mode" hidden:"true"`
StrictJSONSchema *bool `json:"strict_json_schema,omitempty" description:"Whether to enforce strict adherence to the JSON schema" hidden:"true"`
}
// ChatModelAnthropicThinkingOptions configures Anthropic thinking budget.
type ChatModelAnthropicThinkingOptions struct {
BudgetTokens *int64 `json:"budget_tokens,omitempty"`
BudgetTokens *int64 `json:"budget_tokens,omitempty" description:"Maximum number of tokens the model may use for thinking"`
}
// ChatModelAnthropicProviderOptions configures Anthropic provider behavior.
type ChatModelAnthropicProviderOptions struct {
SendReasoning *bool `json:"send_reasoning,omitempty"`
Thinking *ChatModelAnthropicThinkingOptions `json:"thinking,omitempty"`
Effort *string `json:"effort,omitempty"`
DisableParallelToolUse *bool `json:"disable_parallel_tool_use,omitempty"`
SendReasoning *bool `json:"send_reasoning,omitempty" description:"Whether to include reasoning content in the response"`
Thinking *ChatModelAnthropicThinkingOptions `json:"thinking,omitempty" description:"Configuration for extended thinking"`
Effort *string `json:"effort,omitempty" description:"Controls the level of reasoning effort" enum:"low,medium,high,max"`
DisableParallelToolUse *bool `json:"disable_parallel_tool_use,omitempty" description:"Whether to disable parallel tool execution"`
}
// ChatModelGoogleThinkingConfig configures Google thinking behavior.
type ChatModelGoogleThinkingConfig struct {
ThinkingBudget *int64 `json:"thinking_budget,omitempty"`
IncludeThoughts *bool `json:"include_thoughts,omitempty"`
ThinkingBudget *int64 `json:"thinking_budget,omitempty" description:"Maximum number of tokens the model may use for thinking"`
IncludeThoughts *bool `json:"include_thoughts,omitempty" description:"Whether to include thinking content in the response"`
}
// ChatModelGoogleSafetySetting configures Google safety filtering.
type ChatModelGoogleSafetySetting struct {
Category string `json:"category,omitempty"`
Threshold string `json:"threshold,omitempty"`
Category string `json:"category,omitempty" description:"The harm category to configure"`
Threshold string `json:"threshold,omitempty" description:"The blocking threshold for the harm category"`
}
// ChatModelGoogleProviderOptions configures Google provider behavior.
type ChatModelGoogleProviderOptions struct {
ThinkingConfig *ChatModelGoogleThinkingConfig `json:"thinking_config,omitempty"`
CachedContent string `json:"cached_content,omitempty"`
SafetySettings []ChatModelGoogleSafetySetting `json:"safety_settings,omitempty"`
Threshold string `json:"threshold,omitempty"`
ThinkingConfig *ChatModelGoogleThinkingConfig `json:"thinking_config,omitempty" description:"Configuration for extended thinking"`
CachedContent string `json:"cached_content,omitempty" description:"Resource name of a cached content object" hidden:"true"`
SafetySettings []ChatModelGoogleSafetySetting `json:"safety_settings,omitempty" description:"Safety filtering settings for harmful content categories" hidden:"true"`
Threshold string `json:"threshold,omitempty" hidden:"true"`
}
// ChatModelOpenAICompatProviderOptions configures OpenAI-compatible behavior.
type ChatModelOpenAICompatProviderOptions struct {
User *string `json:"user,omitempty"`
ReasoningEffort *string `json:"reasoning_effort,omitempty"`
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
ReasoningEffort *string `json:"reasoning_effort,omitempty" description:"Controls the level of reasoning effort" enum:"none,minimal,low,medium,high,xhigh"`
}
// ChatModelOpenRouterReasoningOptions configures OpenRouter reasoning behavior.
type ChatModelOpenRouterReasoningOptions struct {
Enabled *bool `json:"enabled,omitempty"`
Exclude *bool `json:"exclude,omitempty"`
MaxTokens *int64 `json:"max_tokens,omitempty"`
Effort *string `json:"effort,omitempty"`
Enabled *bool `json:"enabled,omitempty" description:"Whether reasoning is enabled"`
Exclude *bool `json:"exclude,omitempty" description:"Whether to exclude reasoning content from the response"`
MaxTokens *int64 `json:"max_tokens,omitempty" description:"Maximum number of tokens for reasoning output"`
Effort *string `json:"effort,omitempty" description:"Controls the level of reasoning effort" enum:"low,medium,high"`
}
// ChatModelOpenRouterProvider configures OpenRouter routing preferences.
type ChatModelOpenRouterProvider struct {
Order []string `json:"order,omitempty"`
AllowFallbacks *bool `json:"allow_fallbacks,omitempty"`
RequireParameters *bool `json:"require_parameters,omitempty"`
DataCollection *string `json:"data_collection,omitempty"`
Only []string `json:"only,omitempty"`
Ignore []string `json:"ignore,omitempty"`
Quantizations []string `json:"quantizations,omitempty"`
Sort *string `json:"sort,omitempty"`
Order []string `json:"order,omitempty" description:"Ordered list of preferred provider names"`
AllowFallbacks *bool `json:"allow_fallbacks,omitempty" description:"Whether to allow fallback to other providers"`
RequireParameters *bool `json:"require_parameters,omitempty" description:"Whether to require all parameters to be supported by the provider"`
DataCollection *string `json:"data_collection,omitempty" description:"Data collection policy preference"`
Only []string `json:"only,omitempty" description:"Restrict to only these provider names"`
Ignore []string `json:"ignore,omitempty" description:"Provider names to exclude from routing"`
Quantizations []string `json:"quantizations,omitempty" description:"Allowed model quantization levels"`
Sort *string `json:"sort,omitempty" description:"Sort order for provider selection"`
}
// ChatModelOpenRouterProviderOptions configures OpenRouter provider behavior.
type ChatModelOpenRouterProviderOptions struct {
Reasoning *ChatModelOpenRouterReasoningOptions `json:"reasoning,omitempty"`
ExtraBody map[string]any `json:"extra_body,omitempty"`
IncludeUsage *bool `json:"include_usage,omitempty"`
LogitBias map[string]int64 `json:"logit_bias,omitempty"`
LogProbs *bool `json:"log_probs,omitempty"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
User *string `json:"user,omitempty"`
Provider *ChatModelOpenRouterProvider `json:"provider,omitempty"`
Reasoning *ChatModelOpenRouterReasoningOptions `json:"reasoning,omitempty" description:"Configuration for reasoning behavior"`
ExtraBody map[string]any `json:"extra_body,omitempty" description:"Additional fields to include in the request body" hidden:"true"`
IncludeUsage *bool `json:"include_usage,omitempty" description:"Whether to include token usage information in the response" hidden:"true"`
LogitBias map[string]int64 `json:"logit_bias,omitempty" description:"Token IDs mapped to bias values from -100 to 100" hidden:"true"`
LogProbs *bool `json:"log_probs,omitempty" description:"Whether to return log probabilities of output tokens" hidden:"true"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty" description:"Whether the model may make multiple tool calls in parallel"`
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
Provider *ChatModelOpenRouterProvider `json:"provider,omitempty" description:"Routing preferences for provider selection" hidden:"true"`
}
// ChatModelVercelReasoningOptions configures Vercel reasoning behavior.
type ChatModelVercelReasoningOptions struct {
Enabled *bool `json:"enabled,omitempty"`
MaxTokens *int64 `json:"max_tokens,omitempty"`
Effort *string `json:"effort,omitempty"`
Exclude *bool `json:"exclude,omitempty"`
Enabled *bool `json:"enabled,omitempty" description:"Whether reasoning is enabled"`
MaxTokens *int64 `json:"max_tokens,omitempty" description:"Maximum number of tokens for reasoning output"`
Effort *string `json:"effort,omitempty" description:"Controls the level of reasoning effort" enum:"none,minimal,low,medium,high,xhigh"`
Exclude *bool `json:"exclude,omitempty" description:"Whether to exclude reasoning content from the response"`
}
// ChatModelVercelGatewayProviderOptions configures Vercel routing behavior.
type ChatModelVercelGatewayProviderOptions struct {
Order []string `json:"order,omitempty"`
Models []string `json:"models,omitempty"`
Order []string `json:"order,omitempty" description:"Ordered list of preferred provider names"`
Models []string `json:"models,omitempty" description:"Model identifiers to route across"`
}
// ChatModelVercelProviderOptions configures Vercel provider behavior.
type ChatModelVercelProviderOptions struct {
Reasoning *ChatModelVercelReasoningOptions `json:"reasoning,omitempty"`
ProviderOptions *ChatModelVercelGatewayProviderOptions `json:"providerOptions,omitempty"`
User *string `json:"user,omitempty"`
LogitBias map[string]int64 `json:"logit_bias,omitempty"`
LogProbs *bool `json:"logprobs,omitempty"`
TopLogProbs *int64 `json:"top_logprobs,omitempty"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty"`
ExtraBody map[string]any `json:"extra_body,omitempty"`
Reasoning *ChatModelVercelReasoningOptions `json:"reasoning,omitempty" description:"Configuration for reasoning behavior"`
ProviderOptions *ChatModelVercelGatewayProviderOptions `json:"providerOptions,omitempty" description:"Gateway routing options for provider selection" hidden:"true"`
User *string `json:"user,omitempty" description:"Unique identifier for the end user for abuse monitoring" hidden:"true"`
LogitBias map[string]int64 `json:"logit_bias,omitempty" description:"Token IDs mapped to bias values from -100 to 100" hidden:"true"`
LogProbs *bool `json:"logprobs,omitempty" description:"Whether to return log probabilities of output tokens" hidden:"true"`
TopLogProbs *int64 `json:"top_logprobs,omitempty" description:"Number of most likely tokens to return log probabilities for" hidden:"true"`
ParallelToolCalls *bool `json:"parallel_tool_calls,omitempty" description:"Whether the model may make multiple tool calls in parallel"`
ExtraBody map[string]any `json:"extra_body,omitempty" description:"Additional fields to include in the request body" hidden:"true"`
}
// ChatModelCallConfig configures per-call model behavior defaults.
type ChatModelCallConfig struct {
MaxOutputTokens *int64 `json:"max_output_tokens,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
TopP *float64 `json:"top_p,omitempty"`
TopK *int64 `json:"top_k,omitempty"`
PresencePenalty *float64 `json:"presence_penalty,omitempty"`
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
ProviderOptions *ChatModelProviderOptions `json:"provider_options,omitempty"`
MaxOutputTokens *int64 `json:"max_output_tokens,omitempty" description:"Upper bound on tokens the model may generate"`
Temperature *float64 `json:"temperature,omitempty" description:"Sampling temperature between 0 and 2"`
TopP *float64 `json:"top_p,omitempty" description:"Nucleus sampling probability cutoff"`
TopK *int64 `json:"top_k,omitempty" description:"Number of highest-probability tokens to keep for sampling"`
PresencePenalty *float64 `json:"presence_penalty,omitempty" description:"Penalty for tokens that have already appeared in the output"`
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty" description:"Penalty for tokens based on their frequency in the output"`
ProviderOptions *ChatModelProviderOptions `json:"provider_options,omitempty" description:"Provider-specific option overrides"`
}
// CreateChatModelConfigRequest creates a chat model config.
+241
View File
@@ -0,0 +1,241 @@
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
"strings"
"github.com/coder/coder/v2/codersdk"
)
// SchemaField describes a single form field in the generated schema.
type SchemaField struct {
JSONName string `json:"json_name"`
GoName string `json:"go_name"`
Type string `json:"type"`
Description string `json:"description,omitempty"`
Required bool `json:"required"`
Enum []string `json:"enum,omitempty"`
InputType string `json:"input_type"`
Hidden bool `json:"hidden,omitempty"`
}
// FieldGroup holds the fields for a struct or provider.
type FieldGroup struct {
Fields []SchemaField `json:"fields"`
}
// Schema is the top-level output structure.
type Schema struct {
General FieldGroup `json:"general"`
Providers map[string]FieldGroup `json:"providers"`
ProviderAliases map[string]string `json:"provider_aliases"`
}
func main() {
schema := Schema{
Providers: make(map[string]FieldGroup),
ProviderAliases: map[string]string{
"azure": "openai",
"bedrock": "anthropic",
},
}
// General options from ChatModelCallConfig, excluding
// the provider_options field which is handled separately.
schema.General = extractFields(
reflect.TypeOf(codersdk.ChatModelCallConfig{}),
"",
map[string]bool{"ProviderOptions": true},
)
// Provider-specific options. Each entry maps a provider key
// to the concrete options struct used for that provider.
providerTypes := []struct {
key string
typ reflect.Type
}{
{"openai", reflect.TypeOf(codersdk.ChatModelOpenAIProviderOptions{})},
{"anthropic", reflect.TypeOf(codersdk.ChatModelAnthropicProviderOptions{})},
{"google", reflect.TypeOf(codersdk.ChatModelGoogleProviderOptions{})},
{"openaicompat", reflect.TypeOf(codersdk.ChatModelOpenAICompatProviderOptions{})},
{"openrouter", reflect.TypeOf(codersdk.ChatModelOpenRouterProviderOptions{})},
{"vercel", reflect.TypeOf(codersdk.ChatModelVercelProviderOptions{})},
}
for _, p := range providerTypes {
schema.Providers[p.key] = extractFields(p.typ, "", nil)
}
out, err := json.MarshalIndent(schema, "", "\t")
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "marshal schema: %v\n", err)
os.Exit(1)
}
// Print the generated header and JSON body.
_, _ = fmt.Println("// Code generated by scripts/modeloptionsgen. DO NOT EDIT.")
_, _ = fmt.Println(string(out))
}
// extractFields walks the struct fields of t and returns a FieldGroup.
// prefix is used to build dot-separated json_name values for nested
// structs. skip lists Go field names to exclude from output.
func extractFields(t reflect.Type, prefix string, skip map[string]bool) FieldGroup {
var fields []SchemaField
for i := range t.NumField() {
f := t.Field(i)
if skip != nil && skip[f.Name] {
continue
}
jsonTag := f.Tag.Get("json")
if jsonTag == "" || jsonTag == "-" {
continue
}
jsonName := strings.Split(jsonTag, ",")[0]
if jsonName == "" {
continue
}
fullJSONName := jsonName
if prefix != "" {
fullJSONName = prefix + "." + jsonName
}
// Determine the underlying type, dereferencing pointers.
ft := f.Type
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
// Check the hidden tag before recursing into nested structs
// so that entire sub-objects can be marked hidden.
hidden := f.Tag.Get("hidden") == "true"
// If the field is a struct (not a map), recurse to flatten
// its children using dot-separated names — unless the
// entire struct is marked hidden, in which case emit it
// as a single opaque field.
if ft.Kind() == reflect.Struct && !hidden {
nested := extractFields(ft, fullJSONName, nil)
fields = append(fields, nested.Fields...)
continue
}
typeName := goTypeToSchemaType(f.Type)
description := f.Tag.Get("description")
enumTag := f.Tag.Get("enum")
var enumValues []string
if enumTag != "" {
enumValues = strings.Split(enumTag, ",")
}
required := !strings.Contains(jsonTag, "omitempty")
inputType := inferInputType(typeName, enumValues)
fields = append(fields, SchemaField{
JSONName: fullJSONName,
GoName: goFieldPath(prefix, f.Name, t, fullJSONName),
Type: typeName,
Description: description,
Required: required,
Enum: enumValues,
InputType: inputType,
Hidden: hidden,
})
}
return FieldGroup{Fields: fields}
}
// goFieldPath builds a dot-separated Go field name for nested fields.
// For top-level fields it returns just the field name. For nested
// fields it reconstructs the parent struct field name from the prefix
// by looking at the enclosing type's fields.
func goFieldPath(prefix, name string, _ reflect.Type, fullJSONName string) string {
if prefix == "" {
return name
}
// Build the Go path by walking the JSON name segments. Each
// segment maps to a struct field that we already traversed
// during recursion, so we reconstruct the path from the JSON
// parts. The parent extractFields call sets the prefix to the
// parent json name, so we can derive the Go path from the
// json segments by title-casing each part.
parts := strings.Split(fullJSONName, ".")
goNames := make([]string, 0, len(parts))
for _, p := range parts {
goNames = append(goNames, jsonSegmentToGoName(p))
}
return strings.Join(goNames, ".")
}
// jsonSegmentToGoName converts a snake_case JSON segment to a
// PascalCase Go field name using common conventions.
func jsonSegmentToGoName(seg string) string {
words := strings.Split(seg, "_")
var b strings.Builder
for _, w := range words {
if w == "" {
continue
}
// Handle common acronyms.
upper := strings.ToUpper(w)
switch upper {
case "ID", "URL", "IP", "HTTP", "JSON", "API", "UI":
_, _ = b.WriteString(upper)
default:
_, _ = b.WriteString(strings.ToUpper(w[:1]))
_, _ = b.WriteString(w[1:])
}
}
return b.String()
}
// goTypeToSchemaType maps a Go reflect.Type to a JSON schema type
// string.
func goTypeToSchemaType(t reflect.Type) string {
// Dereference pointers.
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.String:
return "string"
case reflect.Bool:
return "boolean"
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return "integer"
case reflect.Float32, reflect.Float64:
return "number"
case reflect.Slice:
return "array"
case reflect.Map:
return "object"
default:
return "string"
}
}
// inferInputType decides the appropriate frontend input widget for
// a field based on its schema type and enum values.
func inferInputType(typeName string, enum []string) string {
if len(enum) > 0 {
return "select"
}
switch typeName {
case "boolean":
return "select"
case "array", "object":
return "json"
default:
return "input"
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["./src/index.tsx", "./src/serviceWorker.ts"],
"project": ["./src/**/*.ts", "./src/**/*.tsx", "./e2e/**/*.ts"],
"ignore": ["**/*Generated.ts"],
"ignore": ["**/*Generated.ts", "src/api/chatModelOptions.ts"],
"ignoreBinaries": ["protoc"],
"ignoreDependencies": [
"@types/react-virtualized-auto-sizer",
+139
View File
@@ -0,0 +1,139 @@
import schema from "./chatModelOptionsGenerated.json";
/**
* Describes a single configurable field for a chat model provider.
* Generated from Go struct tags via `scripts/modeloptionsgen`.
*/
export interface FieldSchema {
/** The JSON key used in API payloads (may use dot-notation for nested fields). */
json_name: string;
/** The corresponding Go struct field name. */
go_name: string;
/** The JSON Schema type of this field. */
type: "string" | "integer" | "number" | "boolean" | "array" | "object";
/** Human-readable description of the field. May be absent for some fields. */
description?: string;
/** Whether this field is required when configuring the provider. */
required: boolean;
/** Hint for how the frontend should render the input control. */
input_type: "input" | "select" | "json";
/** If present, the field value must be one of these options. */
enum?: string[];
/** If true, this field should not be rendered in admin UI forms. */
hidden?: boolean;
}
/**
* A group of fields belonging to a single provider or the general section.
*/
export interface ProviderSchema {
fields: FieldSchema[];
}
/**
* Top-level schema describing all configurable chat model options.
*
* - `general` contains provider-independent fields (e.g. temperature).
* - `providers` maps canonical provider names to their specific fields.
* - `provider_aliases` maps alternate names to canonical provider names
* (e.g. "azure" → "openai").
*/
export interface ModelOptionsSchema {
general: ProviderSchema;
providers: Record<string, ProviderSchema>;
provider_aliases: Record<string, string>;
}
/** The imported schema, typed as {@link ModelOptionsSchema}. */
export const modelOptionsSchema: ModelOptionsSchema =
schema as ModelOptionsSchema;
/**
* Get the general (provider-independent) fields such as temperature
* and max_output_tokens.
*/
export function getGeneralFields(): FieldSchema[] {
return modelOptionsSchema.general.fields;
}
/**
* Get provider-specific fields for a given provider name.
* Handles aliases (e.g. "azure" → "openai", "bedrock" → "anthropic").
* Returns an empty array for unknown providers.
*/
export function getProviderFields(provider: string): FieldSchema[] {
const resolved = resolveProvider(provider);
return modelOptionsSchema.providers[resolved]?.fields ?? [];
}
/**
* Resolve a provider name through the alias table.
* If the name is an alias it returns the canonical provider;
* otherwise the original name is returned unchanged.
*
* @example
* resolveProvider("azure") // "openai"
* resolveProvider("bedrock") // "anthropic"
* resolveProvider("openai") // "openai"
*/
export function resolveProvider(provider: string): string {
return modelOptionsSchema.provider_aliases[provider] ?? provider;
}
/**
* Get all canonical provider names (excludes aliases).
* The order matches the JSON schema and is not guaranteed to be stable
* across regenerations.
*/
export function getProviderNames(): string[] {
return Object.keys(modelOptionsSchema.providers);
}
/**
* Check whether a provider is known, either as a canonical name or an alias.
*/
export function isKnownProvider(provider: string): boolean {
const resolved = resolveProvider(provider);
return resolved in modelOptionsSchema.providers;
}
/**
* Convert a snake_case segment to camelCase.
* Only the first character after each underscore is uppercased;
* the leading character stays lowercase.
*/
function snakeToCamel(s: string): string {
return s.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());
}
/**
* Convert a dot-notation `json_name` into a form field key namespaced
* under the given provider.
*
* Each dot-separated segment is converted from snake_case to camelCase
* and joined back with dots, then prefixed with the provider name.
*
* This bridges between the JSON schema (snake_case, flat `json_name`)
* and a typical React form state tree (camelCase, dot-separated paths).
*
* @example
* toFormFieldKey("anthropic", "thinking.budget_tokens")
* // "anthropic.thinking.budgetTokens"
*
* toFormFieldKey("openai", "max_completion_tokens")
* // "openai.maxCompletionTokens"
*/
export function toFormFieldKey(provider: string, jsonName: string): string {
const camelSegments = jsonName.split(".").map(snakeToCamel);
return `${provider}.${camelSegments.join(".")}`;
}
/** Get only the visible (non-hidden) fields for a provider. */
export function getVisibleProviderFields(provider: string): FieldSchema[] {
return getProviderFields(provider).filter((f) => !f.hidden);
}
/** Get only the visible (non-hidden) general fields. */
export function getVisibleGeneralFields(): FieldSchema[] {
return getGeneralFields().filter((f) => !f.hidden);
}
+542
View File
@@ -0,0 +1,542 @@
{
"general": {
"fields": [
{
"json_name": "max_output_tokens",
"go_name": "MaxOutputTokens",
"type": "integer",
"description": "Upper bound on tokens the model may generate",
"required": false,
"input_type": "input"
},
{
"json_name": "temperature",
"go_name": "Temperature",
"type": "number",
"description": "Sampling temperature between 0 and 2",
"required": false,
"input_type": "input"
},
{
"json_name": "top_p",
"go_name": "TopP",
"type": "number",
"description": "Nucleus sampling probability cutoff",
"required": false,
"input_type": "input"
},
{
"json_name": "top_k",
"go_name": "TopK",
"type": "integer",
"description": "Number of highest-probability tokens to keep for sampling",
"required": false,
"input_type": "input"
},
{
"json_name": "presence_penalty",
"go_name": "PresencePenalty",
"type": "number",
"description": "Penalty for tokens that have already appeared in the output",
"required": false,
"input_type": "input"
},
{
"json_name": "frequency_penalty",
"go_name": "FrequencyPenalty",
"type": "number",
"description": "Penalty for tokens based on their frequency in the output",
"required": false,
"input_type": "input"
}
]
},
"providers": {
"anthropic": {
"fields": [
{
"json_name": "send_reasoning",
"go_name": "SendReasoning",
"type": "boolean",
"description": "Whether to include reasoning content in the response",
"required": false,
"input_type": "select"
},
{
"json_name": "thinking.budget_tokens",
"go_name": "Thinking.BudgetTokens",
"type": "integer",
"description": "Maximum number of tokens the model may use for thinking",
"required": false,
"input_type": "input"
},
{
"json_name": "effort",
"go_name": "Effort",
"type": "string",
"description": "Controls the level of reasoning effort",
"required": false,
"enum": ["low", "medium", "high", "max"],
"input_type": "select"
},
{
"json_name": "disable_parallel_tool_use",
"go_name": "DisableParallelToolUse",
"type": "boolean",
"description": "Whether to disable parallel tool execution",
"required": false,
"input_type": "select"
}
]
},
"google": {
"fields": [
{
"json_name": "thinking_config.thinking_budget",
"go_name": "ThinkingConfig.ThinkingBudget",
"type": "integer",
"description": "Maximum number of tokens the model may use for thinking",
"required": false,
"input_type": "input"
},
{
"json_name": "thinking_config.include_thoughts",
"go_name": "ThinkingConfig.IncludeThoughts",
"type": "boolean",
"description": "Whether to include thinking content in the response",
"required": false,
"input_type": "select"
},
{
"json_name": "cached_content",
"go_name": "CachedContent",
"type": "string",
"description": "Resource name of a cached content object",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "safety_settings",
"go_name": "SafetySettings",
"type": "array",
"description": "Safety filtering settings for harmful content categories",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "threshold",
"go_name": "Threshold",
"type": "string",
"required": false,
"input_type": "input",
"hidden": true
}
]
},
"openai": {
"fields": [
{
"json_name": "include",
"go_name": "Include",
"type": "array",
"description": "Model names to include in discovery",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "instructions",
"go_name": "Instructions",
"type": "string",
"description": "System-level instructions prepended to the conversation",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "logit_bias",
"go_name": "LogitBias",
"type": "object",
"description": "Token IDs mapped to bias values from -100 to 100",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "log_probs",
"go_name": "LogProbs",
"type": "boolean",
"description": "Whether to return log probabilities of output tokens",
"required": false,
"input_type": "select",
"hidden": true
},
{
"json_name": "top_log_probs",
"go_name": "TopLogProbs",
"type": "integer",
"description": "Number of most likely tokens to return log probabilities for",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "max_tool_calls",
"go_name": "MaxToolCalls",
"type": "integer",
"description": "Maximum number of tool calls per response",
"required": false,
"input_type": "input"
},
{
"json_name": "parallel_tool_calls",
"go_name": "ParallelToolCalls",
"type": "boolean",
"description": "Whether the model may make multiple tool calls in parallel",
"required": false,
"input_type": "select"
},
{
"json_name": "user",
"go_name": "User",
"type": "string",
"description": "Unique identifier for the end user for abuse monitoring",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "reasoning_effort",
"go_name": "ReasoningEffort",
"type": "string",
"description": "Controls the level of reasoning effort",
"required": false,
"enum": ["none", "minimal", "low", "medium", "high", "xhigh"],
"input_type": "select"
},
{
"json_name": "reasoning_summary",
"go_name": "ReasoningSummary",
"type": "string",
"description": "Controls whether reasoning tokens are summarized in the response",
"required": false,
"input_type": "input"
},
{
"json_name": "max_completion_tokens",
"go_name": "MaxCompletionTokens",
"type": "integer",
"description": "Upper bound on tokens the model may generate",
"required": false,
"input_type": "input"
},
{
"json_name": "text_verbosity",
"go_name": "TextVerbosity",
"type": "string",
"description": "Controls the verbosity of the text response",
"required": false,
"enum": ["low", "medium", "high"],
"input_type": "select"
},
{
"json_name": "prediction",
"go_name": "Prediction",
"type": "object",
"description": "Predicted output content to speed up responses",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "store",
"go_name": "Store",
"type": "boolean",
"description": "Whether to store the output for model distillation or evals",
"required": false,
"input_type": "select",
"hidden": true
},
{
"json_name": "metadata",
"go_name": "Metadata",
"type": "object",
"description": "Arbitrary metadata to attach to the request",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "prompt_cache_key",
"go_name": "PromptCacheKey",
"type": "string",
"description": "Key for enabling cross-request prompt caching",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "safety_identifier",
"go_name": "SafetyIdentifier",
"type": "string",
"description": "Developer-specific safety identifier for the request",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "service_tier",
"go_name": "ServiceTier",
"type": "string",
"description": "Latency tier to use for processing the request",
"required": false,
"input_type": "input"
},
{
"json_name": "structured_outputs",
"go_name": "StructuredOutputs",
"type": "boolean",
"description": "Whether to enable structured JSON output mode",
"required": false,
"input_type": "select",
"hidden": true
},
{
"json_name": "strict_json_schema",
"go_name": "StrictJSONSchema",
"type": "boolean",
"description": "Whether to enforce strict adherence to the JSON schema",
"required": false,
"input_type": "select",
"hidden": true
}
]
},
"openaicompat": {
"fields": [
{
"json_name": "user",
"go_name": "User",
"type": "string",
"description": "Unique identifier for the end user for abuse monitoring",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "reasoning_effort",
"go_name": "ReasoningEffort",
"type": "string",
"description": "Controls the level of reasoning effort",
"required": false,
"enum": ["none", "minimal", "low", "medium", "high", "xhigh"],
"input_type": "select"
}
]
},
"openrouter": {
"fields": [
{
"json_name": "reasoning.enabled",
"go_name": "Reasoning.Enabled",
"type": "boolean",
"description": "Whether reasoning is enabled",
"required": false,
"input_type": "select"
},
{
"json_name": "reasoning.exclude",
"go_name": "Reasoning.Exclude",
"type": "boolean",
"description": "Whether to exclude reasoning content from the response",
"required": false,
"input_type": "select"
},
{
"json_name": "reasoning.max_tokens",
"go_name": "Reasoning.MaxTokens",
"type": "integer",
"description": "Maximum number of tokens for reasoning output",
"required": false,
"input_type": "input"
},
{
"json_name": "reasoning.effort",
"go_name": "Reasoning.Effort",
"type": "string",
"description": "Controls the level of reasoning effort",
"required": false,
"enum": ["low", "medium", "high"],
"input_type": "select"
},
{
"json_name": "extra_body",
"go_name": "ExtraBody",
"type": "object",
"description": "Additional fields to include in the request body",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "include_usage",
"go_name": "IncludeUsage",
"type": "boolean",
"description": "Whether to include token usage information in the response",
"required": false,
"input_type": "select",
"hidden": true
},
{
"json_name": "logit_bias",
"go_name": "LogitBias",
"type": "object",
"description": "Token IDs mapped to bias values from -100 to 100",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "log_probs",
"go_name": "LogProbs",
"type": "boolean",
"description": "Whether to return log probabilities of output tokens",
"required": false,
"input_type": "select",
"hidden": true
},
{
"json_name": "parallel_tool_calls",
"go_name": "ParallelToolCalls",
"type": "boolean",
"description": "Whether the model may make multiple tool calls in parallel",
"required": false,
"input_type": "select"
},
{
"json_name": "user",
"go_name": "User",
"type": "string",
"description": "Unique identifier for the end user for abuse monitoring",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "provider",
"go_name": "Provider",
"type": "string",
"description": "Routing preferences for provider selection",
"required": false,
"input_type": "input",
"hidden": true
}
]
},
"vercel": {
"fields": [
{
"json_name": "reasoning.enabled",
"go_name": "Reasoning.Enabled",
"type": "boolean",
"description": "Whether reasoning is enabled",
"required": false,
"input_type": "select"
},
{
"json_name": "reasoning.max_tokens",
"go_name": "Reasoning.MaxTokens",
"type": "integer",
"description": "Maximum number of tokens for reasoning output",
"required": false,
"input_type": "input"
},
{
"json_name": "reasoning.effort",
"go_name": "Reasoning.Effort",
"type": "string",
"description": "Controls the level of reasoning effort",
"required": false,
"enum": ["none", "minimal", "low", "medium", "high", "xhigh"],
"input_type": "select"
},
{
"json_name": "reasoning.exclude",
"go_name": "Reasoning.Exclude",
"type": "boolean",
"description": "Whether to exclude reasoning content from the response",
"required": false,
"input_type": "select"
},
{
"json_name": "providerOptions",
"go_name": "ProviderOptions",
"type": "string",
"description": "Gateway routing options for provider selection",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "user",
"go_name": "User",
"type": "string",
"description": "Unique identifier for the end user for abuse monitoring",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "logit_bias",
"go_name": "LogitBias",
"type": "object",
"description": "Token IDs mapped to bias values from -100 to 100",
"required": false,
"input_type": "json",
"hidden": true
},
{
"json_name": "logprobs",
"go_name": "LogProbs",
"type": "boolean",
"description": "Whether to return log probabilities of output tokens",
"required": false,
"input_type": "select",
"hidden": true
},
{
"json_name": "top_logprobs",
"go_name": "TopLogProbs",
"type": "integer",
"description": "Number of most likely tokens to return log probabilities for",
"required": false,
"input_type": "input",
"hidden": true
},
{
"json_name": "parallel_tool_calls",
"go_name": "ParallelToolCalls",
"type": "boolean",
"description": "Whether the model may make multiple tool calls in parallel",
"required": false,
"input_type": "select"
},
{
"json_name": "extra_body",
"go_name": "ExtraBody",
"type": "object",
"description": "Additional fields to include in the request body",
"required": false,
"input_type": "json",
"hidden": true
}
]
}
},
"provider_aliases": {
"azure": "openai",
"bedrock": "anthropic"
}
}