Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

182 lines
6.9 KiB
Go

package modelcatalog
import (
"encoding/json"
"fmt"
"strings"
)
// ParseRuntimeConfigOptionModels reads the canonical model select descriptor
// published by provider runtimes. The returned advertised bit distinguishes a
// missing descriptor from an explicitly advertised empty model catalog.
func ParseRuntimeConfigOptionModels(value any, modelOptionID string) ([]ModelOption, bool, error) {
raw, err := json.Marshal(value)
if err != nil {
return nil, false, fmt.Errorf("marshal runtime config options: %w", err)
}
var descriptors []struct {
ID string `json:"id"`
Options []json.RawMessage `json:"options"`
}
if err := json.Unmarshal(raw, &descriptors); err != nil {
return nil, false, fmt.Errorf("parse runtime config options: %w", err)
}
modelOptionID = strings.TrimSpace(modelOptionID)
if modelOptionID == "" {
modelOptionID = "model"
}
for _, descriptor := range descriptors {
if strings.TrimSpace(descriptor.ID) != modelOptionID {
continue
}
models := make([]ModelOption, 0, len(descriptor.Options))
for _, rawOption := range descriptor.Options {
if model, ok := NormalizeRuntimeConfigOptionModel(rawOption); ok {
models = append(models, model)
}
}
return models, true, nil
}
return nil, false, nil
}
// NormalizeRuntimeConfigOptionModel converts one provider-neutral model option
// descriptor into the same canonical catalog type used by CLI model/list
// discovery. Field presence remains authoritative for empty capability sets.
func NormalizeRuntimeConfigOptionModel(raw json.RawMessage) (ModelOption, bool) {
var object map[string]any
if err := json.Unmarshal(raw, &object); err != nil {
return ModelOption{}, false
}
id := firstNonBlank(stringMapValue(object, "value"), stringMapValue(object, "model"), stringMapValue(object, "id"))
if id == "" {
return ModelOption{}, false
}
displayName := firstNonBlank(
stringMapValue(object, "label"),
stringMapValue(object, "name"),
stringMapValue(object, "displayName"),
stringMapValue(object, "display_name"),
id,
)
reasoningValue, reasoningAdvertised := advertisedValue(object, "reasoningEfforts", "supportedReasoningEfforts", "supported_reasoning_efforts")
supportsReasoning, supportsReasoningAdvertised := object["supportsReasoningEffort"].(bool)
reasoningOptions := normalizeCodexReasoningEfforts(reasoningValue)
if supportsReasoningAdvertised && !supportsReasoning {
reasoningOptions = []ReasoningEffortOption{}
}
defaultReasoning := firstNonBlank(
stringMapValue(object, "reasoningEffort"),
stringMapValue(object, "defaultReasoningEffort"),
stringMapValue(object, "default_reasoning_effort"),
)
reasoningAdvertised = reasoningAdvertised || supportsReasoningAdvertised || defaultReasoning != ""
speedValue, speedsAdvertised := advertisedValue(object, "speeds", "serviceTiers", "service_tiers")
speeds := normalizeRuntimeSpeedOptions(speedValue)
defaultSpeed := firstNonBlank(stringMapValue(object, "speed"), stringMapValue(object, "defaultSpeed"), stringMapValue(object, "defaultServiceTier"))
return ModelOption{
ID: id,
DisplayName: displayName,
Description: stringMapValue(object, "description"),
ConsumptionMultiplier: stringMapValue(object, "consumptionMultiplier"),
DefaultReasoningEffort: validReasoningDefault(reasoningOptions, defaultReasoning),
DefaultSpeed: validSpeedDefault(speeds, canonicalCodexSpeed(defaultSpeed)),
IsDefault: id == "default" || boolMapValue(object, "default") || boolMapValue(object, "isDefault") || boolMapValue(object, "is_default"),
ReasoningEffortsAdvertised: reasoningAdvertised,
SupportedReasoningEfforts: reasoningOptions,
SpeedsAdvertised: speedsAdvertised,
SupportedSpeeds: speeds,
SupportsImageInput: runtimeConfigImageInputSupport(object),
}, true
}
// ProjectRuntimeConfigOptionModel publishes one canonical catalog model using
// the provider-neutral descriptor vocabulary consumed by every Agent host.
func ProjectRuntimeConfigOptionModel(model ModelOption) map[string]any {
option := map[string]any{
"value": strings.TrimSpace(model.ID),
"name": firstNonBlank(model.DisplayName, model.ID),
"description": strings.TrimSpace(model.Description),
"default": model.IsDefault,
}
if consumptionMultiplier := strings.TrimSpace(model.ConsumptionMultiplier); consumptionMultiplier != "" {
option["consumptionMultiplier"] = consumptionMultiplier
}
if model.SupportsImageInput != nil {
option["supportsImageInput"] = *model.SupportsImageInput
}
if model.ReasoningEffortsAdvertised {
reasoningOptions := make([]map[string]any, 0, len(model.SupportedReasoningEfforts))
for _, reasoning := range model.SupportedReasoningEfforts {
value := strings.TrimSpace(reasoning.Value)
if value == "" {
continue
}
reasoningOptions = append(reasoningOptions, map[string]any{
"value": value,
"name": firstNonBlank(reasoning.Label, value),
"description": strings.TrimSpace(reasoning.Description),
"default": reasoning.Default || value == strings.TrimSpace(model.DefaultReasoningEffort),
})
}
option["supportsReasoningEffort"] = len(reasoningOptions) > 0
option["reasoningEffort"] = validReasoningDefault(model.SupportedReasoningEfforts, model.DefaultReasoningEffort)
option["reasoningEfforts"] = reasoningOptions
}
if model.SpeedsAdvertised {
speedOptions := make([]map[string]any, 0, len(model.SupportedSpeeds))
for _, speed := range model.SupportedSpeeds {
value := strings.TrimSpace(speed.Value)
if value == "" {
continue
}
speedOptions = append(speedOptions, map[string]any{
"value": value,
"name": firstNonBlank(speed.Label, value),
"description": strings.TrimSpace(speed.Description),
})
}
option["speed"] = validSpeedDefault(model.SupportedSpeeds, model.DefaultSpeed)
option["speeds"] = speedOptions
}
return option
}
func normalizeRuntimeSpeedOptions(value any) []SpeedOption {
rawOptions, ok := value.([]any)
if !ok {
return nil
}
options := make([]SpeedOption, 0, len(rawOptions))
seen := make(map[string]struct{}, len(rawOptions))
for _, rawOption := range rawOptions {
var option SpeedOption
switch typed := rawOption.(type) {
case string:
option.Value = canonicalCodexSpeed(typed)
case map[string]any:
option.Value = canonicalCodexSpeed(firstNonBlank(stringMapValue(typed, "value"), stringMapValue(typed, "id")))
option.Label = firstNonBlank(stringMapValue(typed, "label"), stringMapValue(typed, "name"))
option.Description = stringMapValue(typed, "description")
}
if option.Value == "" {
continue
}
if _, exists := seen[option.Value]; exists {
continue
}
seen[option.Value] = struct{}{}
options = append(options, option)
}
return options
}
func runtimeConfigImageInputSupport(object map[string]any) *bool {
if supported, advertised := object["supportsImageInput"].(bool); advertised {
return &supported
}
return codexImageInputSupport(object)
}