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
220 lines
6.4 KiB
Go
220 lines
6.4 KiB
Go
package agent
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/tutti-os/tutti/packages/agent/daemon/modelcatalog"
|
|
"github.com/tutti-os/tutti/packages/agent/daemon/providerregistry"
|
|
)
|
|
|
|
func composerConfigOptions(
|
|
provider string,
|
|
settings ComposerSettings,
|
|
modelOptions []ComposerConfigOptionValue,
|
|
reasoningOptions []ComposerConfigOptionValue,
|
|
speedOptions []ComposerConfigOptionValue,
|
|
) []map[string]any {
|
|
profile := composerProfileFor(provider)
|
|
if !profile.ModelSelection && !profile.ReasoningEffort && !profile.Speed {
|
|
return []map[string]any{}
|
|
}
|
|
if modelOptions == nil {
|
|
modelOptions = composerSelectedModelOptions(settings.Model)
|
|
}
|
|
options := make([]map[string]any, 0, 3)
|
|
if profile.ModelSelection && len(modelOptions) > 0 {
|
|
configOptionID := strings.TrimSpace(profile.ModelConfigOptionID)
|
|
if configOptionID == "" {
|
|
configOptionID = "model"
|
|
}
|
|
options = append(options, map[string]any{
|
|
"currentValue": nullableString(settings.Model),
|
|
"id": configOptionID,
|
|
"options": composerConfigOptionValuesToRuntimeModelOptions(modelOptions),
|
|
})
|
|
}
|
|
if profile.ReasoningEffort && profile.ReasoningEffortOptions != providerregistry.ReasoningEffortOptionsStrictModelCatalog {
|
|
if len(reasoningOptions) > 0 {
|
|
options = append(options, map[string]any{
|
|
"currentValue": nullableString(settings.ReasoningEffort),
|
|
"id": reasoningConfigOptionID(provider),
|
|
"options": composerConfigOptionValuesToRuntimeOptions(reasoningOptions),
|
|
})
|
|
}
|
|
}
|
|
if profile.Speed {
|
|
options = append(options, map[string]any{
|
|
"currentValue": nullableString(settings.Speed),
|
|
"id": speedConfigOptionID(provider),
|
|
"options": composerConfigOptionValuesToRuntimeOptions(speedOptions),
|
|
})
|
|
}
|
|
return options
|
|
}
|
|
|
|
func extractModelOptionsFromRuntimeContext(runtimeContext map[string]any, optionIDs ...string) []ComposerConfigOptionValue {
|
|
if len(runtimeContext) == 0 {
|
|
return nil
|
|
}
|
|
modelOptionID := "model"
|
|
if len(optionIDs) > 0 && strings.TrimSpace(optionIDs[0]) != "" {
|
|
modelOptionID = strings.TrimSpace(optionIDs[0])
|
|
}
|
|
models, advertised, err := modelcatalog.ParseRuntimeConfigOptionModels(
|
|
runtimeContext["configOptions"],
|
|
modelOptionID,
|
|
)
|
|
if err != nil || !advertised {
|
|
return nil
|
|
}
|
|
return composerModelOptionsFromCanonicalCatalog(models)
|
|
}
|
|
|
|
func extractEffectiveModelFromRuntimeContext(
|
|
runtimeContext map[string]any,
|
|
optionIDs ...string,
|
|
) string {
|
|
if len(runtimeContext) == 0 {
|
|
return ""
|
|
}
|
|
modelOptionID := "model"
|
|
if len(optionIDs) > 0 && strings.TrimSpace(optionIDs[0]) != "" {
|
|
modelOptionID = strings.TrimSpace(optionIDs[0])
|
|
}
|
|
for _, option := range runtimeConfigOptionsAsMapSlice(runtimeContext["configOptions"]) {
|
|
if strings.TrimSpace(stringFromAny(option["id"])) != modelOptionID {
|
|
continue
|
|
}
|
|
return strings.TrimSpace(stringFromAny(option["effectiveValue"]))
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func extractCurrentModelFromRuntimeContext(
|
|
runtimeContext map[string]any,
|
|
optionIDs ...string,
|
|
) string {
|
|
if len(runtimeContext) == 0 {
|
|
return ""
|
|
}
|
|
modelOptionID := "model"
|
|
if len(optionIDs) > 0 && strings.TrimSpace(optionIDs[0]) != "" {
|
|
modelOptionID = strings.TrimSpace(optionIDs[0])
|
|
}
|
|
for _, option := range runtimeConfigOptionsAsMapSlice(runtimeContext["configOptions"]) {
|
|
if strings.TrimSpace(stringFromAny(option["id"])) == modelOptionID {
|
|
return strings.TrimSpace(stringFromAny(option["currentValue"]))
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func composerModelOptionsFromCanonicalCatalog(models []modelcatalog.ModelOption) []ComposerConfigOptionValue {
|
|
options := make([]ComposerConfigOptionValue, 0, len(models))
|
|
for _, model := range models {
|
|
value := strings.TrimSpace(model.ID)
|
|
if value == "" {
|
|
continue
|
|
}
|
|
if containsModelOption(options, value) {
|
|
continue
|
|
}
|
|
label := strings.TrimSpace(model.DisplayName)
|
|
if label == "" {
|
|
label = value
|
|
}
|
|
supportsReasoningEffort := (*bool)(nil)
|
|
if model.ReasoningEffortsAdvertised {
|
|
supported := len(model.SupportedReasoningEfforts) > 0
|
|
supportsReasoningEffort = &supported
|
|
}
|
|
options = append(options, ComposerConfigOptionValue{
|
|
ID: value,
|
|
Label: label,
|
|
Value: value,
|
|
Description: strings.TrimSpace(model.Description),
|
|
ConsumptionMultiplier: strings.TrimSpace(model.ConsumptionMultiplier),
|
|
SupportsImageInput: model.SupportsImageInput,
|
|
SupportsReasoningEffort: supportsReasoningEffort,
|
|
ReasoningEffort: strings.TrimSpace(model.DefaultReasoningEffort),
|
|
ReasoningEfforts: append([]AgentModelReasoningEffortOption(nil), model.SupportedReasoningEfforts...),
|
|
ReasoningEffortsAdvertised: model.ReasoningEffortsAdvertised,
|
|
})
|
|
}
|
|
return options
|
|
}
|
|
|
|
func composerConfigOptionValuesFromAny(input any) []ComposerConfigOptionValue {
|
|
rawOptions := anySlice(input)
|
|
if len(rawOptions) == 0 {
|
|
return nil
|
|
}
|
|
options := make([]ComposerConfigOptionValue, 0, len(rawOptions))
|
|
for _, raw := range rawOptions {
|
|
optionMap := anyMap(raw)
|
|
value := strings.TrimSpace(stringFromAny(optionMap["value"]))
|
|
if value == "" {
|
|
continue
|
|
}
|
|
label := strings.TrimSpace(stringFromAny(optionMap["label"]))
|
|
if label == "" {
|
|
label = strings.TrimSpace(stringFromAny(optionMap["name"]))
|
|
}
|
|
if label == "" {
|
|
label = value
|
|
}
|
|
id := strings.TrimSpace(stringFromAny(optionMap["id"]))
|
|
if id == "" {
|
|
id = value
|
|
}
|
|
options = append(options, ComposerConfigOptionValue{
|
|
ID: id,
|
|
Label: label,
|
|
Value: value,
|
|
Description: strings.TrimSpace(stringFromAny(optionMap["description"])),
|
|
ConsumptionMultiplier: strings.TrimSpace(stringFromAny(optionMap["consumptionMultiplier"])),
|
|
})
|
|
}
|
|
return options
|
|
}
|
|
|
|
func anySlice(input any) []any {
|
|
switch typed := input.(type) {
|
|
case []any:
|
|
return typed
|
|
case []map[string]any:
|
|
result := make([]any, len(typed))
|
|
for index := range typed {
|
|
result[index] = typed[index]
|
|
}
|
|
return result
|
|
case []map[string]string:
|
|
result := make([]any, len(typed))
|
|
for index := range typed {
|
|
record := make(map[string]any, len(typed[index]))
|
|
for key, value := range typed[index] {
|
|
record[key] = value
|
|
}
|
|
result[index] = record
|
|
}
|
|
return result
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func anyMap(input any) map[string]any {
|
|
switch typed := input.(type) {
|
|
case map[string]any:
|
|
return typed
|
|
case map[string]string:
|
|
result := make(map[string]any, len(typed))
|
|
for key, value := range typed {
|
|
result[key] = value
|
|
}
|
|
return result
|
|
default:
|
|
return nil
|
|
}
|
|
}
|