summaryrefslogtreecommitdiff
path: root/internal/appconfig/app_sections.go
blob: 05b3171013f56b0499f70cc847cafc0fcf7d54f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package appconfig

import "slices"

// CoreConfig contains core runtime and interaction settings.
// It is embedded in App; JSON tags ensure marshalling works correctly.
type CoreConfig struct {
	MaxTokens          int    `json:"max_tokens"`
	ContextMode        string `json:"context_mode"`
	ContextWindowLines int    `json:"context_window_lines"`
	MaxContextTokens   int    `json:"max_context_tokens"`
	LogPreviewLimit    int    `json:"log_preview_limit"`
	RequestTimeout     int    `json:"request_timeout"`
	// Single knob for LSP requests; if set, overrides hardcoded temps in LSP.
	CodingTemperature *float64 `json:"coding_temperature"`
	// Minimum identifier characters required for manual (TriggerKind=1) invoke
	// to proceed without structural triggers. 0 means always allow.
	ManualInvokeMinPrefix int `json:"manual_invoke_min_prefix"`
	// Completion debounce in milliseconds. When > 0, the server waits until
	// there has been no text change for at least this duration before sending
	// an LLM completion request.
	CompletionDebounceMs int `json:"completion_debounce_ms"`
	// Completion throttle in milliseconds. When > 0, caps the minimum spacing
	// between LLM requests (both chat and code-completer paths).
	CompletionThrottleMs int `json:"completion_throttle_ms"`
	// CompletionWaitAll controls whether to wait for all configured completion
	// backends before returning results. When true (default), waits for all
	// backends. When false, returns the first result immediately.
	CompletionWaitAll *bool    `json:"completion_wait_all"`
	TriggerCharacters []string `json:"trigger_characters"`
	Provider          string   `json:"provider"`
	// Inline prompt trigger characters (default: >!text> and >>!text>)
	InlineOpen  string `json:"inline_open"`
	InlineClose string `json:"inline_close"`
	// In-editor chat triggers (default: suffix ">" after one of [?, !, :, ;])
	ChatSuffix   string   `json:"chat_suffix"`
	ChatPrefixes []string `json:"chat_prefixes"`
}

// ProviderConfig contains provider endpoints/models and per-surface model overrides.
// It is embedded in App; JSON tags ensure marshalling works correctly.
type ProviderConfig struct {
	// Provider-specific options
	OpenAIBaseURL string `json:"openai_base_url"`
	OpenAIModel   string `json:"openai_model"`
	// Default temperature for OpenAI requests (nil means use provider default)
	OpenAITemperature *float64 `json:"openai_temperature"`
	OpenRouterBaseURL string   `json:"openrouter_base_url"`
	OpenRouterModel   string   `json:"openrouter_model"`
	// Default temperature for OpenRouter requests (nil means use provider default)
	OpenRouterTemperature *float64 `json:"openrouter_temperature"`
	OllamaBaseURL         string   `json:"ollama_base_url"`
	OllamaModel           string   `json:"ollama_model"`
	// Default temperature for Ollama requests (nil means use provider default)
	OllamaTemperature *float64 `json:"ollama_temperature"`
	AnthropicBaseURL  string   `json:"anthropic_base_url"`
	AnthropicModel    string   `json:"anthropic_model"`
	// Default temperature for Anthropic requests (nil means use provider default)
	AnthropicTemperature *float64 `json:"anthropic_temperature"`
	// YouSearch options
	YouSearchResearchEffort string `json:"yousearch_research_effort"` // lite|standard|deep|exhaustive
	// Per-surface provider/model configurations (ordered; first entry is primary)
	CompletionConfigs []SurfaceConfig `json:"-"`
	CodeActionConfigs []SurfaceConfig `json:"-"`
	ChatConfigs       []SurfaceConfig `json:"-"`
	CLIConfigs        []SurfaceConfig `json:"-"`
}

// PromptConfig contains all prompt templates and custom action prompts.
// It is embedded in App; fields use json:"-" since prompts are not exposed via JSON.
type PromptConfig struct {
	// Prompt templates (configured only via file; no env overrides)
	// Completion
	PromptCompletionSystemGeneral string `json:"-"`
	PromptCompletionSystemParams  string `json:"-"`
	PromptCompletionSystemInline  string `json:"-"`
	PromptCompletionUserGeneral   string `json:"-"`
	PromptCompletionUserParams    string `json:"-"`
	PromptCompletionExtraHeader   string `json:"-"`
	// Provider-native code-completer
	PromptNativeCompletion string `json:"-"`
	// In-editor chat
	PromptChatSystem string `json:"-"`
	// Code actions
	PromptCodeActionRewriteSystem     string `json:"-"`
	PromptCodeActionDiagnosticsSystem string `json:"-"`
	PromptCodeActionDocumentSystem    string `json:"-"`
	PromptCodeActionRewriteUser       string `json:"-"`
	PromptCodeActionDiagnosticsUser   string `json:"-"`
	PromptCodeActionDocumentUser      string `json:"-"`
	PromptCodeActionGoTestSystem      string `json:"-"`
	PromptCodeActionGoTestUser        string `json:"-"`
	PromptCodeActionSimplifySystem    string `json:"-"`
	PromptCodeActionSimplifyUser      string `json:"-"`
	PromptCodeActionFixTyposSystem    string `json:"-"`
	PromptCodeActionFixTyposUser      string `json:"-"`
	// CLI
	PromptCLIDefaultSystem string `json:"-"`
	PromptCLIExplainSystem string `json:"-"`
	// Custom code actions and tmux integration
	CustomActions        []CustomAction `json:"-"`
	TmuxCustomMenuHotkey string         `json:"-"`
}

// FeatureConfig contains non-LLM feature toggles/integration settings.
// It is embedded in App; fields use json:"-" since features are not exposed via JSON.
type FeatureConfig struct {
	// Stats
	StatsWindowMinutes int `json:"-"`
	// Ignore: gitignore-aware file filtering for LSP
	IgnoreGitignore     *bool    `json:"-"`
	IgnoreExtraPatterns []string `json:"-"`
	IgnoreLSPNotify     *bool    `json:"-"`
	// TmuxEdit: popup editor settings for hexai-tmux-edit
	TmuxEditPopupWidth   string             `json:"-"`
	TmuxEditPopupHeight  string             `json:"-"`
	TmuxEditDefaultAgent string             `json:"-"`
	TmuxEditAgents       []TmuxEditAgentCfg `json:"-"`
	// TmuxAction: configurable main menu for hexai-tmux-action
	TmuxActionMenu []TmuxActionMenuEntry `json:"-"`
	// MCP: Model Context Protocol server settings
	MCPPromptsDir       string `json:"-"` // Directory for prompt storage
	MCPSlashCommandSync bool   `json:"-"` // Enable slash command sync
	MCPSlashCommandDir  string `json:"-"` // Directory for slash command files
}

// AppSections is the focused split of App into subsystem-specific config groups.
type AppSections struct {
	Core      CoreConfig
	Providers ProviderConfig
	Prompts   PromptConfig
	Features  FeatureConfig
}

// Sections returns the app configuration split into focused sub-configs.
func (a *App) Sections() AppSections {
	return AppSections{
		Core:      a.CoreSection(),
		Providers: a.ProviderSection(),
		Prompts:   a.PromptSection(),
		Features:  a.FeatureSection(),
	}
}

// ApplySections applies focused sub-config groups back onto App.
func (a *App) ApplySections(sections AppSections) {
	a.ApplyCoreSection(sections.Core)
	a.ApplyProviderSection(sections.Providers)
	a.ApplyPromptSection(sections.Prompts)
	a.ApplyFeatureSection(sections.Features)
}

// CoreSection returns a deep copy of the core runtime and interaction settings.
// Slices are cloned to prevent callers from mutating the original.
func (a *App) CoreSection() CoreConfig {
	c := a.CoreConfig
	c.TriggerCharacters = slices.Clone(a.TriggerCharacters)
	c.ChatPrefixes = slices.Clone(a.ChatPrefixes)
	return c
}

// ApplyCoreSection applies core runtime and interaction settings.
// Slices are cloned to prevent the caller's copy from being shared.
func (a *App) ApplyCoreSection(core CoreConfig) {
	a.CoreConfig = core
	a.TriggerCharacters = slices.Clone(core.TriggerCharacters)
	a.ChatPrefixes = slices.Clone(core.ChatPrefixes)
}

// ProviderSection returns a deep copy of provider endpoint/model settings.
// Surface config slices are cloned to prevent callers from mutating the original.
func (a *App) ProviderSection() ProviderConfig {
	p := a.ProviderConfig
	p.CompletionConfigs = cloneSurfaceConfigs(a.CompletionConfigs)
	p.CodeActionConfigs = cloneSurfaceConfigs(a.CodeActionConfigs)
	p.ChatConfigs = cloneSurfaceConfigs(a.ChatConfigs)
	p.CLIConfigs = cloneSurfaceConfigs(a.CLIConfigs)
	return p
}

// ApplyProviderSection applies provider endpoint/model settings and surface overrides.
// Surface config slices are cloned to prevent the caller's copy from being shared.
func (a *App) ApplyProviderSection(providers ProviderConfig) {
	a.ProviderConfig = providers
	a.CompletionConfigs = cloneSurfaceConfigs(providers.CompletionConfigs)
	a.CodeActionConfigs = cloneSurfaceConfigs(providers.CodeActionConfigs)
	a.ChatConfigs = cloneSurfaceConfigs(providers.ChatConfigs)
	a.CLIConfigs = cloneSurfaceConfigs(providers.CLIConfigs)
}

// PromptSection returns a deep copy of prompt templates and custom action settings.
// The CustomActions slice is cloned to prevent callers from mutating the original.
func (a *App) PromptSection() PromptConfig {
	p := a.PromptConfig
	p.CustomActions = append([]CustomAction{}, a.CustomActions...)
	return p
}

// ApplyPromptSection applies prompt templates and custom action prompt settings.
// The CustomActions slice is cloned to prevent the caller's copy from being shared.
func (a *App) ApplyPromptSection(prompts PromptConfig) {
	a.PromptConfig = prompts
	a.CustomActions = append([]CustomAction{}, prompts.CustomActions...)
}

// FeatureSection returns a deep copy of non-LLM feature toggles and integrations.
// Slices are cloned to prevent callers from mutating the original.
func (a *App) FeatureSection() FeatureConfig {
	f := a.FeatureConfig
	f.IgnoreExtraPatterns = slices.Clone(a.IgnoreExtraPatterns)
	f.TmuxEditAgents = append([]TmuxEditAgentCfg{}, a.TmuxEditAgents...)
	f.TmuxActionMenu = append([]TmuxActionMenuEntry{}, a.TmuxActionMenu...)
	return f
}

// ApplyFeatureSection applies non-LLM feature toggles and integrations.
// Slices are cloned to prevent the caller's copy from being shared.
func (a *App) ApplyFeatureSection(features FeatureConfig) {
	a.FeatureConfig = features
	a.IgnoreExtraPatterns = slices.Clone(features.IgnoreExtraPatterns)
	a.TmuxEditAgents = append([]TmuxEditAgentCfg{}, features.TmuxEditAgents...)
	a.TmuxActionMenu = append([]TmuxActionMenuEntry{}, features.TmuxActionMenu...)
}