internal/config/runtime_configuration.go
Rama main · réplica de lectura del repositorio original.
package config
import (
"encoding/hex"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/yeomyeonggeori/blueclaw/internal/capability"
)
type RuntimeConfiguration struct {
BaseURL string `json:"baseURL"`
ServiceTokenPath string `json:"serviceTokenPath,omitempty"`
Capabilities CapabilityConfiguration `json:"capabilities"`
AgentProfiles []AgentProfileConfiguration `json:"agentProfiles"`
LanguageModel LanguageModelConfiguration `json:"languageModel"`
Database DatabaseConfiguration `json:"database"`
Memory MemoryConfiguration `json:"memory"`
Agent AgentConfiguration `json:"agent"`
Connectors ConnectorConfiguration `json:"connectors"`
Logging LoggingConfiguration `json:"logging"`
Terminal TerminalConfiguration `json:"terminal"`
Scheduler SchedulerConfiguration `json:"scheduler"`
}
type CapabilityConfiguration struct {
Endpoint string `json:"endpoint"`
UnixSocketPath string `json:"unixSocketPath"`
TimeoutSecond int `json:"timeoutSecond"`
ProtocolVersion string `json:"protocolVersion"`
AggregateProtocolHash string `json:"aggregateProtocolHash"`
ToolDescriptors []CapabilityToolDescriptor `json:"toolDescriptors,omitempty"`
}
// IsConfigured reports whether a capability service is reachable. Without one
// Blueclaw runs standalone and the capability protocol identity is meaningless.
func (configuration CapabilityConfiguration) IsConfigured() bool {
return strings.TrimSpace(configuration.Endpoint) != "" || strings.TrimSpace(configuration.UnixSocketPath) != ""
}
type CapabilityToolDescriptor = capability.ToolDescriptor
type CapabilityToolResultContract = capability.ToolResultContract
type CapabilityResourceEffectContract = capability.ResourceEffectContract
type CapabilityCompletionEvidence = capability.CompletionEvidence
type CapabilityAvailability = capability.Availability
type CapabilityIdempotency = capability.Idempotency
type AgentProfileConfiguration struct {
Name string `json:"name"`
AllowedToolNames []string `json:"allowedToolNames"`
}
type EvidenceCondition = capability.EvidenceCondition
type AgentConfiguration struct {
Intake AgentIntakeConfiguration `json:"intake"`
FailureRecovery AgentFailureRecovery `json:"failureRecovery"`
GenerationOptions AgentGenerationOptions `json:"generationOptions,omitempty"`
AllowAdminTaskDiagnostic bool `json:"allowAdminTaskDiagnostic"`
Harness HarnessConfiguration `json:"harness,omitempty"`
}
type HarnessConfiguration struct {
Name string `json:"name,omitempty"`
AgentCommandPath string `json:"agentCommandPath,omitempty"`
AgentArguments []string `json:"agentArguments,omitempty"`
ToolCatalogURL string `json:"toolCatalogURL,omitempty"`
}
type AgentIntakeConfiguration struct {
Enabled bool `json:"enabled"`
ExecutionMode string `json:"executionMode"`
}
type AgentFailureRecovery struct {
FailureDebtFinalizationGate bool `json:"failureDebtFinalizationGate"`
AttemptFingerprint string `json:"attemptFingerprint"`
RecoveryBudget AgentRecoveryBudget `json:"recoveryBudget"`
}
type AgentRecoveryBudget struct {
CorrectedRetry int `json:"correctedRetry"`
AlternateRoute int `json:"alternateRoute"`
AdjacentTool int `json:"adjacentTool"`
NoToolFallback int `json:"noToolFallback"`
}
type AgentGenerationOptions struct {
Seed *int64 `json:"seed,omitempty"`
Temperature *float64 `json:"temperature,omitempty"`
}
type LanguageModelConfiguration struct {
Tiers map[string][]ModelEndpointConfiguration `json:"tiers,omitempty"`
Embedding ModelEndpointConfiguration `json:"embedding,omitempty"`
Decision ModelEndpointConfiguration `json:"decision,omitempty"`
VisualReview ModelEndpointConfiguration `json:"visualReview,omitempty"`
MaximumModelTier string `json:"maximumModelTier,omitempty"`
MinimumModelTier string `json:"minimumModelTier,omitempty"`
ContextWindowTokens int `json:"contextWindowTokens,omitempty"`
Capability LanguageModelCapabilityConfiguration `json:"capability,omitempty"`
}
// ModelEndpointConfiguration is one rung: where to ask, what the model is
// called there, and the file holding that endpoint's key. A tier is an ordered
// list of these, tried in order, and nothing in this repository decides what
// goes in one.
type ModelEndpointConfiguration struct {
Endpoint string `json:"endpoint"`
Model string `json:"model"`
APIKeyPath string `json:"apiKeyPath,omitempty"`
APIKeyEnvironment string `json:"apiKeyEnvironment,omitempty"`
ProviderOrder []string `json:"providerOrder,omitempty"`
ProviderSort string `json:"providerSort,omitempty"`
ReasoningEffort string `json:"reasoningEffort,omitempty"`
RequesterEmailHeader string `json:"requesterEmailHeader,omitempty"`
}
type LanguageModelCapabilityConfiguration struct {
Model string `json:"model"`
MaxModel string `json:"maxModel"`
XHighModel string `json:"xhighModel"`
HighModel string `json:"highModel"`
MediumModel string `json:"mediumModel"`
LowModel string `json:"lowModel"`
XLowModel string `json:"xlowModel"`
DecisionModel string `json:"decisionModel"`
ExecutionMode string `json:"executionMode"`
}
type DatabaseConfiguration struct {
Driver string `json:"driver"`
ConnectionString string `json:"connectionString"`
MigrationDirectoryPath string `json:"migrationDirectoryPath"`
MaxOpenConnections int `json:"maxOpenConnections"`
}
type MemoryConfiguration struct {
AdminAssertionKeyPath string `json:"adminAssertionKeyPath"`
EmbeddingModel string `json:"embeddingModel"`
EmbeddingDimensions int `json:"embeddingDimensions"`
EmbeddingExecutionMode string `json:"embeddingExecutionMode"`
ExtractionDisabled bool `json:"extractionDisabled"`
}
type ConnectorConfiguration struct {
Chatd ChatdConnectorConfiguration `json:"chatd"`
}
type ChatdConnectorConfiguration struct {
Endpoint string `json:"endpoint,omitempty"`
TimeoutSecond int `json:"timeoutSecond,omitempty"`
EnabledPlatforms []string `json:"enabledPlatforms,omitempty"`
}
type LoggingConfiguration struct {
DirectoryPath string `json:"directoryPath"`
RetentionDays int `json:"retentionDays"`
}
type TerminalConfiguration struct {
Mode string `json:"mode"`
SandboxProvider string `json:"sandboxProvider"`
WorkspaceRootPath string `json:"workspaceRootPath"`
POSIXHelperPath string `json:"posixHelperPath"`
TimeoutSecond int `json:"timeoutSecond"`
OutputMaxBytes int `json:"outputMaxBytes"`
SessionMaxCount int `json:"sessionMaxCount"`
AllowNetwork bool `json:"allowNetwork"`
AllowInteractiveShell bool `json:"allowInteractiveShell"`
}
type SchedulerConfiguration struct {
RetentionCheckIntervalMinute int `json:"retentionCheckIntervalMinute"`
SchedulePollIntervalSecond int `json:"taskSchedulePollIntervalSecond"`
TaskRetentionDays int `json:"taskRetentionDays"`
TasklessLLMCallRetentionDays int `json:"tasklessLLMCallRetentionDays"`
}
func LoadRuntimeConfiguration(path string) (RuntimeConfiguration, error) {
document, errorValue := os.ReadFile(path)
if errorValue != nil {
return RuntimeConfiguration{}, errorValue
}
document, errorValue = expandEnvironmentReferences(document)
if errorValue != nil {
return RuntimeConfiguration{}, errorValue
}
var configuration RuntimeConfiguration
errorValue = json.Unmarshal(document, &configuration)
if errorValue != nil {
return RuntimeConfiguration{}, errorValue
}
if errorValue := validateCapabilityProtocolIdentity(&configuration.Capabilities); errorValue != nil {
return RuntimeConfiguration{}, errorValue
}
return configuration, nil
}
func capabilityRegistryIsUnstamped(configuration *CapabilityConfiguration) bool {
return len(configuration.ToolDescriptors) == 0 &&
strings.TrimSpace(configuration.ProtocolVersion) == "" &&
strings.TrimSpace(configuration.AggregateProtocolHash) == ""
}
func validateCapabilityProtocolIdentity(configuration *CapabilityConfiguration) error {
if !configuration.IsConfigured() {
return nil
}
if capabilityRegistryIsUnstamped(configuration) {
return nil
}
if configuration.ProtocolVersion == "" || configuration.ProtocolVersion != strings.TrimSpace(configuration.ProtocolVersion) {
return fmt.Errorf("capabilities.protocolVersion must be a non-empty trimmed string")
}
if configuration.AggregateProtocolHash != strings.TrimSpace(configuration.AggregateProtocolHash) {
return fmt.Errorf("capabilities.aggregateProtocolHash must be a 64-character lowercase hexadecimal hash")
}
if len(configuration.AggregateProtocolHash) != 64 {
return fmt.Errorf("capabilities.aggregateProtocolHash must be a 64-character lowercase hexadecimal hash")
}
if _, errorValue := hex.DecodeString(configuration.AggregateProtocolHash); errorValue != nil || strings.ToLower(configuration.AggregateProtocolHash) != configuration.AggregateProtocolHash {
return fmt.Errorf("capabilities.aggregateProtocolHash must be a 64-character lowercase hexadecimal hash")
}
return nil
}