diff options
| author | Paul Buetow <paul@buetow.org> | 2025-09-24 23:21:43 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-09-24 23:21:43 +0300 |
| commit | c3c71345db9086392cd9b7529c7f5287009c226e (patch) | |
| tree | d227894ab900d6050cbe1418984526088a692db5 /internal/hexailsp | |
| parent | 127844a4ee481590ef53b6777d34bf2114cb3ab1 (diff) | |
Add runtime config store and reload command
Diffstat (limited to 'internal/hexailsp')
| -rw-r--r-- | internal/hexailsp/run.go | 23 | ||||
| -rw-r--r-- | internal/hexailsp/run_more_test.go | 54 |
2 files changed, 76 insertions, 1 deletions
diff --git a/internal/hexailsp/run.go b/internal/hexailsp/run.go index 554e604..ffb9f86 100644 --- a/internal/hexailsp/run.go +++ b/internal/hexailsp/run.go @@ -13,6 +13,7 @@ import ( "codeberg.org/snonux/hexai/internal/llm" "codeberg.org/snonux/hexai/internal/logging" "codeberg.org/snonux/hexai/internal/lsp" + "codeberg.org/snonux/hexai/internal/runtimeconfig" "codeberg.org/snonux/hexai/internal/stats" ) @@ -55,8 +56,26 @@ func RunWithFactory(logPath string, stdin io.Reader, stdout io.Writer, logger *l client = buildClientIfNil(cfg, client) factory = ensureFactory(factory) - opts := makeServerOptions(cfg, strings.TrimSpace(logPath) != "", client) + store := runtimeconfig.New(cfg) + logContext := strings.TrimSpace(logPath) != "" + opts := makeServerOptions(cfg, logContext, client) + opts.ConfigStore = store server := factory(stdin, stdout, logger, opts) + if configurable, ok := server.(interface{ ApplyOptions(lsp.ServerOptions) }); ok { + store.Subscribe(func(oldCfg, newCfg appconfig.App) { + updated := newCfg + normalizeLoggingConfig(&updated) + if updated.StatsWindowMinutes > 0 { + stats.SetWindow(time.Duration(updated.StatsWindowMinutes) * time.Minute) + } + if newClient := buildClientIfNil(updated, nil); newClient != nil { + client = newClient + } + opts := makeServerOptions(updated, logContext, client) + opts.ConfigStore = store + configurable.ApplyOptions(opts) + }) + } if err := server.Run(); err != nil { logger.Fatalf("server error: %v", err) } @@ -135,6 +154,8 @@ func makeServerOptions(cfg appconfig.App, logContext bool, client llm.Client) ls } return lsp.ServerOptions{ LogContext: logContext, + ConfigStore: nil, + Config: &cfg, MaxTokens: cfg.MaxTokens, ContextMode: cfg.ContextMode, WindowLines: cfg.ContextWindowLines, diff --git a/internal/hexailsp/run_more_test.go b/internal/hexailsp/run_more_test.go index 00b79c1..faaae41 100644 --- a/internal/hexailsp/run_more_test.go +++ b/internal/hexailsp/run_more_test.go @@ -2,18 +2,34 @@ package hexailsp import ( "bytes" + "context" "io" "log" "testing" "codeberg.org/snonux/hexai/internal/appconfig" + "codeberg.org/snonux/hexai/internal/llm" "codeberg.org/snonux/hexai/internal/lsp" + "codeberg.org/snonux/hexai/internal/runtimeconfig" ) type recRunner struct{ ran bool } func (r *recRunner) Run() error { r.ran = true; return nil } +type applyRunner struct{ opts []lsp.ServerOptions } + +func (r *applyRunner) Run() error { return nil } +func (r *applyRunner) ApplyOptions(opts lsp.ServerOptions) { r.opts = append(r.opts, opts) } + +type stubClient struct{} + +func (stubClient) Chat(context.Context, []llm.Message, ...llm.RequestOption) (string, error) { + return "", nil +} +func (stubClient) Name() string { return "stub" } +func (stubClient) DefaultModel() string { return "stub-model" } + func TestRunWithFactory_BuildsOptionsAndClient(t *testing.T) { var captured lsp.ServerOptions factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner { @@ -41,3 +57,41 @@ func TestRunWithFactory_BuildsOptionsAndClient(t *testing.T) { t.Fatalf("expected client to be constructed") } } + +func TestRunWithFactory_SubscriptionAppliesUpdates(t *testing.T) { + var in, out bytes.Buffer + logger := log.New(io.Discard, "", 0) + runner := &applyRunner{} + var capturedStore *runtimeconfig.Store + factory := func(r io.Reader, w io.Writer, logger *log.Logger, opts lsp.ServerOptions) ServerRunner { + capturedStore = opts.ConfigStore + runner.opts = append(runner.opts, opts) + return runner + } + cfg := appconfig.Load(nil) + cfg.StatsWindowMinutes = 0 + cfg.ContextMode = " WINDOW " + if err := RunWithFactory("", &in, &out, logger, cfg, stubClient{}, factory); err != nil { + t.Fatalf("RunWithFactory error: %v", err) + } + if capturedStore == nil { + t.Fatal("expected config store to be passed to factory") + } + if len(runner.opts) == 0 { + t.Fatal("expected initial options to be recorded") + } + updated := cfg + updated.MaxTokens = cfg.MaxTokens + 10 + updated.ContextMode = "always-full" + capturedStore.Set(updated) + if len(runner.opts) < 2 { + t.Fatalf("expected ApplyOptions to be invoked on config update, got %d calls", len(runner.opts)) + } + latest := runner.opts[len(runner.opts)-1] + if latest.MaxTokens != updated.MaxTokens { + t.Fatalf("expected updated max tokens, got %+v", latest) + } + if latest.ContextMode != "always-full" { + t.Fatalf("expected normalized context mode, got %+v", latest) + } +} |
