ebbd4d9b06e49460b5fb801e394600aff00bf0e6

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Refactor configs

Diff

  1diff --git a/AGENTS.md b/AGENTS.md
  2index 347f4acb342f93210a0eca3f6e6b0dc374d6f62b..7d33100c926b9d1d250b73fc83a6034cee198cdc 100644
  3--- a/AGENTS.md
  4+++ b/AGENTS.md
  5@@ -36,14 +36,8 @@ when it grows past budget (`internal/game/state.go`,
  6 Model services are external, user-managed processes. GPU VRAM is normally
  7 almost fully allocated to the chat model.
  8 
  9-- Never start, stop, restart, kill, supervise, or reconfigure `llama-server`,
 10-  `audiocpp_server`, or `whisper-server`.
 11-- Never run `llama-cli`, download a model, or use a command that can load a
 12-  model into VRAM.
 13-- Never run `make audiocpp`; it starts the audio service, which only the user
 14-  may do.
 15-- The application is an HTTP client only. Configure endpoints with flags or
 16-  environment variables and report unavailable services clearly.
 17+- Never start, stop, or restart the `llama-server` or `audiocpp_server`.
 18+- Never run `llama-cli`, or use a command that can load a model into VRAM.
 19 
 20 ### Product invariants
 21 
 22diff --git a/Makefile b/Makefile
 23index e130a4cda6c4de483d121b79efd3afe7fe317834..6c24344555e10a32e18fd097d9101ae2827e1965 100644
 24--- a/Makefile
 25+++ b/Makefile
 26@@ -4,6 +4,7 @@ lint:
 27 	golangci-lint run ./...
 28 
 29 fmt:
 30+	go mod tidy
 31 	golangci-lint fmt ./...
 32 
 33 build:
 34diff --git a/README.md b/README.md
 35index e908af65c42f75eb50d7303c5720cd9a3e3c1de4..af02aa089876566b7fb79eb8399b2255e4de2bdf 100644
 36--- a/README.md
 37+++ b/README.md
 38@@ -40,14 +40,14 @@ launches a service to make a check pass.
 39 Every setting is a flag or an environment variable; flags win over env vars,
 40 which win over defaults. Defaults match a local setup.
 41 
 42-| Setting          | Flag                 | Env var               | Default                                   | Use                                             |
 43-| ---------------- | -------------------- | --------------------- | ----------------------------------------- | ----------------------------------------------- |
 44-| LLM router URL   | `--url`              | `JP_LLM_BASE_URL`     | `https://llama.home.theedgeofrage.com`    | llama.cpp router; `POST /v1/chat/completions`   |
 45-| Temperature      | `--temperature`      | `TEMPERATURE`         | `1.0`                                     | sampling temperature                            |
 46-| Max tokens       | `--max-tokens`       | `MAX_TOKENS`          | `256`                                     | max output tokens per reply                     |
 47-| Thinking mode    | `--enable-thinking`  | `ENABLE_THINKING`     | `false`                                   | model thinking (`chat_template_kwargs.enable_thinking`) |
 48-| Audio base URL   | `--audio-url`        | `JP_AUDIO_BASE_URL`   | `http://127.0.0.1:8080`                  | audio.cpp server (TTS + ASR)                    |
 49-| Scenario brief   | `--scenario`         | `JP_SCENARIO_PATH`    | `assets/scenarios/small_city.md`          | plain-text scenario brief file                  |
 50+| Setting        | Flag                | Env var              | Default                                | Use                                                     |
 51+| -------------- | ------------------- | -------------------- | -------------------------------------- | ------------------------------------------------------- |
 52+| LLM router URL | `--url`             | `JP_LLM_BASE_URL`    | `https://llama.home.theedgeofrage.com` | llama.cpp router; `POST /v1/chat/completions`           |
 53+| Temperature    | `--temperature`     | `JP_TEMPERATURE`     | `1.0`                                  | model sampling temperature                              |
 54+| Max tokens     | `--max-tokens`      | `JP_MAX_TOKENS`      | `256`                                  | max output tokens per reply                             |
 55+| Thinking mode  | `--enable-thinking` | `JP_ENABLE_THINKING` | `false`                                | model thinking (`chat_template_kwargs.enable_thinking`) |
 56+| Audio base URL | `--audio-url`       | `JP_AUDIO_BASE_URL`  | `http://127.0.0.1:8080`                | audio.cpp server (TTS + ASR)                            |
 57+| Scenario brief | `--scenario`        | `JP_SCENARIO_PATH`   | `assets/scenarios/small_city.md`       | plain-text scenario brief file                          |
 58 
 59 Run `jp -help` for the full flag list.
 60 
 61diff --git a/cmd/jp/main.go b/cmd/jp/main.go
 62index b06b8a50dc65a2068f7cb7f0fd5f9bcb1ef83b1b..9f43a4bad397744bc20e4807fee1b509a7818eba 100644
 63--- a/cmd/jp/main.go
 64+++ b/cmd/jp/main.go
 65@@ -22,9 +22,9 @@ import (
 66 
 67 // recordCap bounds a single push-to-talk capture. It is passed to the recorder
 68 // (which enforces it) and to the UI (which shows it as the cap indicator).
 69-const recordCap = 10 * time.Second
 70+const recordCap = 15 * time.Second
 71 const requestTimeout = 10 * time.Second
 72-const warmupTimeout = 60 * time.Second
 73+const warmupTimeout = 30 * time.Second
 74 
 75 // llama-server slot assignments and the capture command are fixed for this
 76 // deployment; they are not user-configurable. One slot per prompt family keeps
 77@@ -53,7 +53,7 @@ func main() {
 78 	sheetClient := newLLMClient(cfg, hc, sheetSlot)
 79 	scratchClient := newLLMClient(cfg, hc, scratchSlot)
 80 
 81-	if err := availability.CheckAudio(hc, cfg.AudioBaseURL, []string{tts.ModelName, stt.ASRModelName}); err != nil {
 82+	if err := availability.CheckAudio(hc, cfg.AudioConfig.AudioBaseURL, []string{tts.ModelName, stt.ASRModelName}); err != nil {
 83 		fmt.Fprintf(os.Stderr, "jp: audio service unavailable: %v\n", err)
 84 	}
 85 
 86@@ -96,10 +96,10 @@ func buildOrchestrator(cfg *config.Config, state *game.State, hc *http.Client, g
 87 
 88 	speechIn := &adapters.SpeechInput{
 89 		Recorder: stt.NewRecorder(recordCommand, recordCap),
 90-		ASR:      stt.NewASRClient(cfg.AudioBaseURL, hc),
 91+		ASR:      stt.NewASRClient(cfg.AudioConfig.AudioBaseURL, hc),
 92 	}
 93 	speechOut := &adapters.SpeechOutput{
 94-		Client: tts.NewClient(cfg.AudioBaseURL, hc),
 95+		Client: tts.NewClient(cfg.AudioConfig.AudioBaseURL, hc),
 96 		Player: tts.NewPlayer(),
 97 	}
 98 
 99diff --git a/go.mod b/go.mod
100index 85cb01ed073b6aaa1430fbd9525790285708da95..6f93f566dbfda9618b760f6d322e316a7d67816c 100644
101--- a/go.mod
102+++ b/go.mod
103@@ -4,29 +4,31 @@ go 1.27.0
104 
105 require (
106 	github.com/charmbracelet/bubbletea v1.3.10
107+	github.com/charmbracelet/lipgloss v1.1.0
108 	github.com/ebitengine/oto/v3 v3.5.0
109+	github.com/jessevdk/go-flags v1.6.1
110 )
111 
112 require (
113 	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
114-	github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
115-	github.com/charmbracelet/lipgloss v1.1.0 // indirect
116-	github.com/charmbracelet/x/ansi v0.10.1 // indirect
117-	github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
118-	github.com/charmbracelet/x/term v0.2.1 // indirect
119+	github.com/charmbracelet/colorprofile v0.4.3 // indirect
120+	github.com/charmbracelet/x/ansi v0.11.8 // indirect
121+	github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
122+	github.com/charmbracelet/x/term v0.2.2 // indirect
123+	github.com/clipperhouse/displaywidth v0.11.0 // indirect
124+	github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
125 	github.com/ebitengine/purego v0.11.0 // indirect
126 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
127-	github.com/jessevdk/go-flags v1.6.1 // indirect
128 	github.com/jfreymuth/pulse v0.1.3 // indirect
129-	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
130-	github.com/mattn/go-isatty v0.0.20 // indirect
131+	github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
132+	github.com/mattn/go-isatty v0.0.24 // indirect
133 	github.com/mattn/go-localereader v0.0.1 // indirect
134-	github.com/mattn/go-runewidth v0.0.16 // indirect
135+	github.com/mattn/go-runewidth v0.0.30 // indirect
136 	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
137 	github.com/muesli/cancelreader v0.2.2 // indirect
138 	github.com/muesli/termenv v0.16.0 // indirect
139 	github.com/rivo/uniseg v0.4.7 // indirect
140-	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
141-	golang.org/x/sys v0.47.0 // indirect
142-	golang.org/x/text v0.32.0 // indirect
143+	github.com/xo/terminfo v1.0.0 // indirect
144+	golang.org/x/sys v0.48.0 // indirect
145+	golang.org/x/text v0.42.0 // indirect
146 )
147diff --git a/go.sum b/go.sum
148index 84771d0a0b2a16e68b126abdd8d9686bda887fb5..5dd27f197d3f1f48651d086f4801abe4d82fc324 100644
149--- a/go.sum
150+++ b/go.sum
151@@ -2,16 +2,20 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
152 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
153 github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
154 github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
155-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
156-github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
157+github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
158+github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
159 github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
160 github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
161-github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
162-github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
163-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
164-github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
165-github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
166-github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
167+github.com/charmbracelet/x/ansi v0.11.8 h1:JMFwp0CgDC2+jcOB162HH5k7I3FVbgFSMMYg7dSPBQQ=
168+github.com/charmbracelet/x/ansi v0.11.8/go.mod h1:ZNN+3mXny/516oTQPLMPIBeSINvNJJQ8uQXDgbeJxY0=
169+github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
170+github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
171+github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
172+github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
173+github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
174+github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
175+github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
176+github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
177 github.com/ebitengine/oto/v3 v3.5.0 h1:8ZDhKP3Hhslyb9NL3g4HWvhRQqwI+hKOHAXAqD6GPXs=
178 github.com/ebitengine/oto/v3 v3.5.0/go.mod h1:Elkm7yzTRns3w2efvibzVOoQ65YOwmec9a76dCiK10o=
179 github.com/ebitengine/purego v0.11.0 h1:jhp/D+Nyv7UUW8HAcmcjt2N2rYrYi9m3SL21k0Ua/NI=
180@@ -22,30 +26,28 @@ github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bB
181 github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
182 github.com/jfreymuth/pulse v0.1.3 h1:bc5TdxiB8E+2INnFjFWWgyfgXtz2IyNNNCX+Wt/ZD14=
183 github.com/jfreymuth/pulse v0.1.3/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
184-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
185-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
186-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
187-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
188+github.com/lucasb-eyer/go-colorful v1.4.1 h1:1EO+WB73+EH8EVbzlrG3KLAfEypQWVHIBqlTf+2hNss=
189+github.com/lucasb-eyer/go-colorful v1.4.1/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
190+github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
191+github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
192 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
193 github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
194-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
195-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
196+github.com/mattn/go-runewidth v0.0.30 h1:+KUuiDA4fF0R1p5FeueHefjDm+GIM+kWfFnDjybOPgk=
197+github.com/mattn/go-runewidth v0.0.30/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
198 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
199 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
200 github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
201 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
202 github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
203 github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
204-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
205 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
206 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
207-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
208-github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
209-golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
210-golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
211+github.com/xo/terminfo v1.0.0 h1:2ZpYzqWzyyytjk3TP6aJVDhkMAkc99/1xKQdA3TDTBY=
212+github.com/xo/terminfo v1.0.0/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
213+golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
214+golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
215 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
216-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
217-golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
218-golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
219-golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
220-golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
221+golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
222+golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
223+golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
224+golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
225diff --git a/internal/config/config.go b/internal/config/config.go
226index 33da5bcf950a5930172f5512d228567490338c70..f558f32ec596fbda3c078d35e8f5f5447102b733 100644
227--- a/internal/config/config.go
228+++ b/internal/config/config.go
229@@ -12,17 +12,20 @@ import (
230 )
231 
232 type LLMConfig struct {
233-	BaseURL        string  `long:"url" env:"JP_LLM_BASE_URL" default:"https://llama.home.theedgeofrage.com" description:"llama.cpp router base URL"`
234+	BaseURL        string  `long:"url" env:"BASE_URL" default:"https://llama.home.theedgeofrage.com" description:"llama.cpp router base URL"`
235 	Temperature    float64 `long:"temperature" env:"TEMPERATURE" default:"1.0"`
236 	MaxTokens      int     `long:"max-tokens" env:"MAX_TOKENS" default:"256"`
237 	EnableThinking bool    `long:"enable-thinking" env:"ENABLE_THINKING"`
238 }
239 
240+type AudioConfig struct {
241+	AudioBaseURL string `long:"audio-url" env:"BASE_URL" default:"http://127.0.0.1:8080" description:"audio.cpp server base URL (TTS + ASR)"`
242+}
243+
244 // Config is the shared option group. Parsed with github.com/jessevdk/go-flags.
245 type Config struct {
246-	LLMConfig LLMConfig
247-
248-	AudioBaseURL string `long:"audio-url" env:"JP_AUDIO_BASE_URL" default:"http://127.0.0.1:8080" description:"audio.cpp server base URL (TTS + ASR)"`
249+	LLMConfig   LLMConfig   `env-namespace:"JP_LLM"`
250+	AudioConfig AudioConfig `env-namespace:"JP_AUDIO"`
251 
252 	Scenario string `long:"scenario" env:"JP_SCENARIO_PATH" default:"assets/scenarios/small_city.md" description:"path to the scenario brief file"`
253 }