loadLLMRuntime loads tool defs from toolConfigPath, reads the app config from the user's default config path (applying the configured language to tpl — defaulting when the config file is absent), resolves the LLM endpoint (honoring resolveOpts), and returns the runtime bundle. tpl is mutated in plac
(tpl *template.Template, toolConfigPath string, resolveOpts llm.ResolveOptions)
| 171 | // endpoint (honoring resolveOpts), and |
| 172 | // returns the runtime bundle. tpl is mutated in place. |
| 173 | func loadLLMRuntime(tpl *template.Template, toolConfigPath string, resolveOpts llm.ResolveOptions) (*llmRuntime, error) { |
| 174 | toolEntries, err := toolsconfig.Load(toolConfigPath) |
| 175 | if err != nil { |
| 176 | return nil, fmt.Errorf("load tools: %w", err) |
| 177 | } |
| 178 | planToolDefs := agent.BuildToolDefs(toolEntries, true) |
| 179 | mainToolDefs := agent.BuildToolDefs(toolEntries, false) |
| 180 | |
| 181 | cfgPath, err := defaultConfigPath() |
| 182 | if err != nil { |
| 183 | return nil, err |
| 184 | } |
| 185 | appCfg, err := LoadAppConfig(cfgPath) |
| 186 | if err != nil { |
| 187 | return nil, fmt.Errorf("load app config: %w", err) |
| 188 | } |
| 189 | // Apply the language directive even when the config file is missing |
| 190 | // (upstream #fix: ApplyLanguage with empty lang falls back to default). |
| 191 | var lang string |
| 192 | if appCfg != nil { |
| 193 | lang = appCfg.Language |
| 194 | } |
| 195 | tpl.ApplyLanguage(lang) |
| 196 | |
| 197 | ep, err := llm.ResolveEndpointWithOptions(cfgPath, resolveOpts) |
| 198 | if err != nil { |
| 199 | return nil, fmt.Errorf("resolve LLM endpoint: %w", err) |
| 200 | } |
| 201 | |
| 202 | return &llmRuntime{ |
| 203 | Client: llm.NewLLMClient(ep), |
| 204 | Model: ep.Model, |
| 205 | Provider: ep.Provider, |
| 206 | PlanToolDefs: planToolDefs, |
| 207 | MainToolDefs: mainToolDefs, |
| 208 | Collector: tool.NewCommentCollector(), |
| 209 | AppCfg: appCfg, |
| 210 | RuntimeConfig: agent.RuntimeConfig{ |
| 211 | Protocol: ep.Protocol, |
| 212 | EndpointHost: sanitizeEndpointHost(ep.URL), |
| 213 | Language: lang, |
| 214 | Timeout: ep.Timeout, |
| 215 | }, |
| 216 | }, nil |
| 217 | } |
| 218 | |
| 219 | // sanitizeEndpointHost extracts the credential-free host[:port] from a full LLM |
| 220 | // endpoint URL, dropping scheme, any embedded userinfo, path, query and fragment |