refactor(config): reconcile defaults with main
This commit is contained in:
parent
718a5e7c75
commit
e6e724a827
3 changed files with 175 additions and 40 deletions
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -251,6 +252,8 @@ type AgentDefaults struct {
|
||||||
SubTurn SubTurnConfig `json:"subturn" envPrefix:"PICOCLAW_AGENTS_DEFAULTS_SUBTURN_"`
|
SubTurn SubTurnConfig `json:"subturn" envPrefix:"PICOCLAW_AGENTS_DEFAULTS_SUBTURN_"`
|
||||||
ToolFeedback ToolFeedbackConfig `json:"tool_feedback,omitempty"`
|
ToolFeedback ToolFeedbackConfig `json:"tool_feedback,omitempty"`
|
||||||
SplitOnMarker bool `json:"split_on_marker" env:"PICOCLAW_AGENTS_DEFAULTS_SPLIT_ON_MARKER"` // split messages on <|[SPLIT]|> marker
|
SplitOnMarker bool `json:"split_on_marker" env:"PICOCLAW_AGENTS_DEFAULTS_SPLIT_ON_MARKER"` // split messages on <|[SPLIT]|> marker
|
||||||
|
ContextManager string `json:"context_manager,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_CONTEXT_MANAGER"`
|
||||||
|
ContextManagerConfig json.RawMessage `json:"context_manager_config,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_CONTEXT_MANAGER_CONFIG"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultMaxMediaSize = 20 * 1024 * 1024 // 20 MB
|
const DefaultMaxMediaSize = 20 * 1024 * 1024 // 20 MB
|
||||||
|
|
@ -298,6 +301,8 @@ type ChannelsConfig struct {
|
||||||
Pico PicoConfig `json:"pico" yaml:"pico,omitempty"`
|
Pico PicoConfig `json:"pico" yaml:"pico,omitempty"`
|
||||||
PicoClient PicoClientConfig `json:"pico_client" yaml:"pico_client,omitempty"`
|
PicoClient PicoClientConfig `json:"pico_client" yaml:"pico_client,omitempty"`
|
||||||
IRC IRCConfig `json:"irc" yaml:"irc,omitempty"`
|
IRC IRCConfig `json:"irc" yaml:"irc,omitempty"`
|
||||||
|
VK VKConfig `json:"vk" yaml:"vk,omitempty"`
|
||||||
|
TeamsWebhook TeamsWebhookConfig `json:"teams_webhook" yaml:"teams_webhook,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupTriggerConfig controls when the bot responds in group chats.
|
// GroupTriggerConfig controls when the bot responds in group chats.
|
||||||
|
|
@ -552,6 +557,34 @@ type IRCConfig struct {
|
||||||
ReasoningChannelID string `json:"reasoning_channel_id" yaml:"-"`
|
ReasoningChannelID string `json:"reasoning_channel_id" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VKConfig struct {
|
||||||
|
Enabled bool `json:"enabled" yaml:"-" env:"PICOCLAW_CHANNELS_VK_ENABLED"`
|
||||||
|
Token SecureString `json:"token,omitzero" yaml:"token,omitempty" env:"PICOCLAW_CHANNELS_VK_TOKEN"`
|
||||||
|
GroupID int `json:"group_id" yaml:"-" env:"PICOCLAW_CHANNELS_VK_GROUP_ID"`
|
||||||
|
AllowFrom FlexibleStringSlice `json:"allow_from" yaml:"-" env:"PICOCLAW_CHANNELS_VK_ALLOW_FROM"`
|
||||||
|
GroupTrigger GroupTriggerConfig `json:"group_trigger,omitempty" yaml:"-"`
|
||||||
|
Typing TypingConfig `json:"typing,omitempty" yaml:"-"`
|
||||||
|
Placeholder PlaceholderConfig `json:"placeholder,omitempty" yaml:"-"`
|
||||||
|
ReasoningChannelID string `json:"reasoning_channel_id" yaml:"-" env:"PICOCLAW_CHANNELS_VK_REASONING_CHANNEL_ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VKConfig) SetToken(token string) {
|
||||||
|
c.Token = *NewSecureString(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamsWebhookConfig configures the output-only Microsoft Teams webhook channel.
|
||||||
|
// Multiple webhook targets can be configured and selected via ChatID at send time.
|
||||||
|
type TeamsWebhookConfig struct {
|
||||||
|
Enabled bool `json:"enabled" yaml:"-" env:"PICOCLAW_CHANNELS_TEAMS_WEBHOOK_ENABLED"`
|
||||||
|
Webhooks map[string]TeamsWebhookTarget `json:"webhooks" yaml:"webhooks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeamsWebhookTarget represents a single Teams webhook destination.
|
||||||
|
type TeamsWebhookTarget struct {
|
||||||
|
WebhookURL SecureString `json:"webhook_url,omitzero" yaml:"webhook_url,omitempty"`
|
||||||
|
Title string `json:"title,omitempty" yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
type HeartbeatConfig struct {
|
type HeartbeatConfig struct {
|
||||||
Enabled bool `json:"enabled" env:"PICOCLAW_HEARTBEAT_ENABLED"`
|
Enabled bool `json:"enabled" env:"PICOCLAW_HEARTBEAT_ENABLED"`
|
||||||
Interval int `json:"interval" env:"PICOCLAW_HEARTBEAT_INTERVAL"` // minutes, min 5
|
Interval int `json:"interval" env:"PICOCLAW_HEARTBEAT_INTERVAL"` // minutes, min 5
|
||||||
|
|
@ -564,6 +597,7 @@ type DevicesConfig struct {
|
||||||
|
|
||||||
type VoiceConfig struct {
|
type VoiceConfig struct {
|
||||||
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_VOICE_MODEL_NAME"`
|
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_VOICE_MODEL_NAME"`
|
||||||
|
TTSModelName string `json:"tts_model_name,omitempty" env:"PICOCLAW_VOICE_TTS_MODEL_NAME"`
|
||||||
EchoTranscription bool `json:"echo_transcription" env:"PICOCLAW_VOICE_ECHO_TRANSCRIPTION"`
|
EchoTranscription bool `json:"echo_transcription" env:"PICOCLAW_VOICE_ECHO_TRANSCRIPTION"`
|
||||||
ElevenLabsAPIKey string `json:"elevenlabs_api_key,omitempty" env:"PICOCLAW_VOICE_ELEVENLABS_API_KEY"`
|
ElevenLabsAPIKey string `json:"elevenlabs_api_key,omitempty" env:"PICOCLAW_VOICE_ELEVENLABS_API_KEY"`
|
||||||
}
|
}
|
||||||
|
|
@ -596,6 +630,7 @@ type ModelConfig struct {
|
||||||
RequestTimeout int `json:"request_timeout,omitempty"`
|
RequestTimeout int `json:"request_timeout,omitempty"`
|
||||||
ThinkingLevel string `json:"thinking_level,omitempty"` // Extended thinking: off|low|medium|high|xhigh|adaptive
|
ThinkingLevel string `json:"thinking_level,omitempty"` // Extended thinking: off|low|medium|high|xhigh|adaptive
|
||||||
ExtraBody map[string]any `json:"extra_body,omitempty"` // Additional fields to inject into request body
|
ExtraBody map[string]any `json:"extra_body,omitempty"` // Additional fields to inject into request body
|
||||||
|
CustomHeaders map[string]string `json:"custom_headers,omitempty"` // Additional headers to inject into every HTTP request
|
||||||
|
|
||||||
APIKeys SecureStrings `json:"api_keys,omitzero" yaml:"api_keys,omitempty"` // API authentication keys (multiple keys for failover)
|
APIKeys SecureStrings `json:"api_keys,omitzero" yaml:"api_keys,omitempty"` // API authentication keys (multiple keys for failover)
|
||||||
|
|
||||||
|
|
@ -603,6 +638,8 @@ type ModelConfig struct {
|
||||||
// existing configs, the field is inferred during load: models with API keys
|
// existing configs, the field is inferred during load: models with API keys
|
||||||
// or the reserved "local-model" name are auto-enabled.
|
// or the reserved "local-model" name are auto-enabled.
|
||||||
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"`
|
||||||
|
// UserAgent is the user agent string to use for HTTP requests.
|
||||||
|
UserAgent string `json:"user_agent,omitempty" yaml:"-"`
|
||||||
|
|
||||||
// isVirtual marks this model as a virtual model generated from multi-key expansion.
|
// isVirtual marks this model as a virtual model generated from multi-key expansion.
|
||||||
// Virtual models should not be persisted to config files.
|
// Virtual models should not be persisted to config files.
|
||||||
|
|
@ -805,9 +842,26 @@ type MediaCleanupConfig struct {
|
||||||
|
|
||||||
type ReadFileToolConfig struct {
|
type ReadFileToolConfig struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
MaxReadFileSize int `json:"max_read_file_size"`
|
MaxReadFileSize int `json:"max_read_file_size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReadFileModeBytes = "bytes"
|
||||||
|
ReadFileModeLines = "lines"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c ReadFileToolConfig) EffectiveMode() string {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(c.Mode)) {
|
||||||
|
case ReadFileModeLines:
|
||||||
|
return ReadFileModeLines
|
||||||
|
case "", ReadFileModeBytes:
|
||||||
|
return ReadFileModeBytes
|
||||||
|
default:
|
||||||
|
return ReadFileModeBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ToolsConfig struct {
|
type ToolsConfig struct {
|
||||||
AllowReadPaths []string `json:"allow_read_paths" yaml:"-" env:"PICOCLAW_TOOLS_ALLOW_READ_PATHS"`
|
AllowReadPaths []string `json:"allow_read_paths" yaml:"-" env:"PICOCLAW_TOOLS_ALLOW_READ_PATHS"`
|
||||||
AllowWritePaths []string `json:"allow_write_paths" yaml:"-" env:"PICOCLAW_TOOLS_ALLOW_WRITE_PATHS"`
|
AllowWritePaths []string `json:"allow_write_paths" yaml:"-" env:"PICOCLAW_TOOLS_ALLOW_WRITE_PATHS"`
|
||||||
|
|
@ -834,6 +888,7 @@ type ToolsConfig struct {
|
||||||
Message ToolConfig `json:"message" yaml:"-" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"`
|
Message ToolConfig `json:"message" yaml:"-" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"`
|
||||||
ReadFile ReadFileToolConfig `json:"read_file" yaml:"-" envPrefix:"PICOCLAW_TOOLS_READ_FILE_"`
|
ReadFile ReadFileToolConfig `json:"read_file" yaml:"-" envPrefix:"PICOCLAW_TOOLS_READ_FILE_"`
|
||||||
SendFile ToolConfig `json:"send_file" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SEND_FILE_"`
|
SendFile ToolConfig `json:"send_file" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SEND_FILE_"`
|
||||||
|
SendTTS ToolConfig `json:"send_tts" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SEND_TTS_"`
|
||||||
Spawn ToolConfig `json:"spawn" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SPAWN_"`
|
Spawn ToolConfig `json:"spawn" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SPAWN_"`
|
||||||
SpawnStatus ToolConfig `json:"spawn_status" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SPAWN_STATUS_"`
|
SpawnStatus ToolConfig `json:"spawn_status" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SPAWN_STATUS_"`
|
||||||
SPI ToolConfig `json:"spi" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SPI_"`
|
SPI ToolConfig `json:"spi" yaml:"-" envPrefix:"PICOCLAW_TOOLS_SPI_"`
|
||||||
|
|
@ -909,10 +964,21 @@ type MCPServerConfig struct {
|
||||||
type MCPConfig struct {
|
type MCPConfig struct {
|
||||||
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_MCP_"`
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_MCP_"`
|
||||||
Discovery ToolDiscoveryConfig ` json:"discovery"`
|
Discovery ToolDiscoveryConfig ` json:"discovery"`
|
||||||
|
// MaxInlineTextChars controls how much MCP text stays inline before it is saved as an artifact.
|
||||||
|
MaxInlineTextChars int `json:"max_inline_text_chars,omitempty" env:"PICOCLAW_TOOLS_MCP_MAX_INLINE_TEXT_CHARS"`
|
||||||
// Servers is a map of server name to server configuration
|
// Servers is a map of server name to server configuration
|
||||||
Servers map[string]MCPServerConfig `json:"servers,omitempty"`
|
Servers map[string]MCPServerConfig `json:"servers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DefaultMCPMaxInlineTextChars = 16 * 1024
|
||||||
|
|
||||||
|
func (c *MCPConfig) GetMaxInlineTextChars() int {
|
||||||
|
if c.MaxInlineTextChars > 0 {
|
||||||
|
return c.MaxInlineTextChars
|
||||||
|
}
|
||||||
|
return DefaultMCPMaxInlineTextChars
|
||||||
|
}
|
||||||
|
|
||||||
func LoadConfig(path string) (*Config, error) {
|
func LoadConfig(path string) (*Config, error) {
|
||||||
logger.Debugf("loading config from %s", path)
|
logger.Debugf("loading config from %s", path)
|
||||||
|
|
||||||
|
|
@ -1210,6 +1276,8 @@ func expandMultiKeyModels(models []*ModelConfig) []*ModelConfig {
|
||||||
RequestTimeout: m.RequestTimeout,
|
RequestTimeout: m.RequestTimeout,
|
||||||
ThinkingLevel: m.ThinkingLevel,
|
ThinkingLevel: m.ThinkingLevel,
|
||||||
ExtraBody: m.ExtraBody,
|
ExtraBody: m.ExtraBody,
|
||||||
|
CustomHeaders: m.CustomHeaders,
|
||||||
|
UserAgent: m.UserAgent,
|
||||||
isVirtual: true,
|
isVirtual: true,
|
||||||
}
|
}
|
||||||
expanded = append(expanded, additionalEntry)
|
expanded = append(expanded, additionalEntry)
|
||||||
|
|
@ -1230,6 +1298,8 @@ func expandMultiKeyModels(models []*ModelConfig) []*ModelConfig {
|
||||||
RequestTimeout: m.RequestTimeout,
|
RequestTimeout: m.RequestTimeout,
|
||||||
ThinkingLevel: m.ThinkingLevel,
|
ThinkingLevel: m.ThinkingLevel,
|
||||||
ExtraBody: m.ExtraBody,
|
ExtraBody: m.ExtraBody,
|
||||||
|
CustomHeaders: m.CustomHeaders,
|
||||||
|
UserAgent: m.UserAgent,
|
||||||
APIKeys: SimpleSecureStrings(keys[0]),
|
APIKeys: SimpleSecureStrings(keys[0]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1286,6 +1356,8 @@ func (t *ToolsConfig) IsToolEnabled(name string) bool {
|
||||||
return t.WebFetch.Enabled
|
return t.WebFetch.Enabled
|
||||||
case "send_file":
|
case "send_file":
|
||||||
return t.SendFile.Enabled
|
return t.SendFile.Enabled
|
||||||
|
case "send_tts":
|
||||||
|
return t.SendTTS.Enabled
|
||||||
case "write_file":
|
case "write_file":
|
||||||
return t.WriteFile.Enabled
|
return t.WriteFile.Enabled
|
||||||
case "mcp":
|
case "mcp":
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,41 @@ func TestAgentConfig_ParsesDispatchRules(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultConfig_MCPMaxInlineTextChars(t *testing.T) {
|
||||||
|
cfg := DefaultConfig()
|
||||||
|
if cfg.Tools.MCP.GetMaxInlineTextChars() != DefaultMCPMaxInlineTextChars {
|
||||||
|
t.Fatalf(
|
||||||
|
"DefaultConfig().Tools.MCP.GetMaxInlineTextChars() = %d, want %d",
|
||||||
|
cfg.Tools.MCP.GetMaxInlineTextChars(),
|
||||||
|
DefaultMCPMaxInlineTextChars,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadConfig_MCPMaxInlineTextChars(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
configPath := filepath.Join(dir, "config.json")
|
||||||
|
raw := `{
|
||||||
|
"tools": {
|
||||||
|
"mcp": {
|
||||||
|
"enabled": true,
|
||||||
|
"max_inline_text_chars": 2048
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
if err := os.WriteFile(configPath, []byte(raw), 0o644); err != nil {
|
||||||
|
t.Fatalf("WriteFile(configPath): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := LoadConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadConfig() error: %v", err)
|
||||||
|
}
|
||||||
|
if got := cfg.Tools.MCP.GetMaxInlineTextChars(); got != 2048 {
|
||||||
|
t.Fatalf("cfg.Tools.MCP.GetMaxInlineTextChars() = %d, want 2048", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestDefaultConfig_HeartbeatEnabled verifies heartbeat is enabled by default
|
// TestDefaultConfig_HeartbeatEnabled verifies heartbeat is enabled by default
|
||||||
func TestDefaultConfig_HeartbeatEnabled(t *testing.T) {
|
func TestDefaultConfig_HeartbeatEnabled(t *testing.T) {
|
||||||
cfg := DefaultConfig()
|
cfg := DefaultConfig()
|
||||||
|
|
@ -331,6 +366,13 @@ func TestDefaultConfig_Channels(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultConfig_ReadFileMode(t *testing.T) {
|
||||||
|
cfg := DefaultConfig()
|
||||||
|
if cfg.Tools.ReadFile.EffectiveMode() != ReadFileModeBytes {
|
||||||
|
t.Fatalf("expected default read_file mode %q, got %q", ReadFileModeBytes, cfg.Tools.ReadFile.EffectiveMode())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestDefaultConfig_WebTools verifies web tools config
|
// TestDefaultConfig_WebTools verifies web tools config
|
||||||
func TestDefaultConfig_WebTools(t *testing.T) {
|
func TestDefaultConfig_WebTools(t *testing.T) {
|
||||||
cfg := DefaultConfig()
|
cfg := DefaultConfig()
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,13 @@ func DefaultConfig() *Config {
|
||||||
APIBase: "https://api.deepseek.com/v1",
|
APIBase: "https://api.deepseek.com/v1",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Venice AI - https://venice.ai
|
||||||
|
{
|
||||||
|
ModelName: "venice-uncensored",
|
||||||
|
Model: "venice/venice-uncensored",
|
||||||
|
APIBase: "https://api.venice.ai/api/v1",
|
||||||
|
},
|
||||||
|
|
||||||
// Google Gemini - https://ai.google.dev/
|
// Google Gemini - https://ai.google.dev/
|
||||||
{
|
{
|
||||||
ModelName: "gemini-2.0-flash",
|
ModelName: "gemini-2.0-flash",
|
||||||
|
|
@ -334,6 +341,13 @@ func DefaultConfig() *Config {
|
||||||
APIBase: "http://localhost:8000/v1",
|
APIBase: "http://localhost:8000/v1",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// LM Studio (local) - http://localhost:1234
|
||||||
|
{
|
||||||
|
ModelName: "lmstudio-local",
|
||||||
|
Model: "lmstudio/openai/gpt-oss-20b",
|
||||||
|
APIBase: "http://localhost:1234/v1",
|
||||||
|
},
|
||||||
|
|
||||||
// Azure OpenAI - https://portal.azure.com
|
// Azure OpenAI - https://portal.azure.com
|
||||||
// model_name is a user-friendly alias; the model field's path after "azure/" is your deployment name
|
// model_name is a user-friendly alias; the model field's path after "azure/" is your deployment name
|
||||||
{
|
{
|
||||||
|
|
@ -433,6 +447,9 @@ func DefaultConfig() *Config {
|
||||||
SendFile: ToolConfig{
|
SendFile: ToolConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
},
|
},
|
||||||
|
SendTTS: ToolConfig{
|
||||||
|
Enabled: false,
|
||||||
|
},
|
||||||
MCP: MCPConfig{
|
MCP: MCPConfig{
|
||||||
ToolConfig: ToolConfig{
|
ToolConfig: ToolConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
|
|
@ -444,6 +461,7 @@ func DefaultConfig() *Config {
|
||||||
UseBM25: true,
|
UseBM25: true,
|
||||||
UseRegex: false,
|
UseRegex: false,
|
||||||
},
|
},
|
||||||
|
MaxInlineTextChars: DefaultMCPMaxInlineTextChars,
|
||||||
Servers: map[string]MCPServerConfig{},
|
Servers: map[string]MCPServerConfig{},
|
||||||
},
|
},
|
||||||
AppendFile: ToolConfig{
|
AppendFile: ToolConfig{
|
||||||
|
|
@ -469,6 +487,7 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
ReadFile: ReadFileToolConfig{
|
ReadFile: ReadFileToolConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
Mode: ReadFileModeBytes,
|
||||||
MaxReadFileSize: 64 * 1024, // 64KB
|
MaxReadFileSize: 64 * 1024, // 64KB
|
||||||
},
|
},
|
||||||
Spawn: ToolConfig{
|
Spawn: ToolConfig{
|
||||||
|
|
@ -500,7 +519,9 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
Voice: VoiceConfig{
|
Voice: VoiceConfig{
|
||||||
ModelName: "",
|
ModelName: "",
|
||||||
|
TTSModelName: "",
|
||||||
EchoTranscription: false,
|
EchoTranscription: false,
|
||||||
|
ElevenLabsAPIKey: "",
|
||||||
},
|
},
|
||||||
BuildInfo: BuildInfo{
|
BuildInfo: BuildInfo{
|
||||||
Version: Version,
|
Version: Version,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue