Skip to content

Commit 19fe1bd

Browse files
feat: stream AI responses
Co-authored-by: devmap-agent <238585242+devmap-agent@users.noreply.github.com>
1 parent 5fa3ec8 commit 19fe1bd

18 files changed

Lines changed: 595 additions & 47 deletions

PRD.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ Question
379379
- Prefer 3–5 most relevant files
380380
- Include related files in output
381381
- Keep technical labels readable and consistent
382+
- Stream human-readable AI responses progressively
383+
- Keep `--json` buffered so stdout remains exactly one valid JSON document
382384

383385
---
384386

@@ -454,6 +456,7 @@ JSON mode rules:
454456
- no ANSI colors, Markdown rendering, box drawing, or progress text
455457
- runtime errors use a stable `{ "status": "error", "error": "...", "hint": "..." }` shape
456458
- `init --json` is non-interactive and requires `GROQ_API_KEY` or existing config
459+
- AI responses are buffered instead of streamed
457460
- human-readable output remains the default
458461
- package-manager wrappers may still write their own warnings to stderr
459462

docs/architecture.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -502,11 +502,31 @@ Only Groq production models should be used as public defaults.
502502
Users can override automatic routing with `devmap config model <model>`.
503503
Running `devmap config model auto` restores the defaults above.
504504

505-
Raw provider errors should not be shown directly to users.
506-
507-
---
508-
509-
## Prompt Strategy
505+
Raw provider errors should not be shown directly to users.
506+
507+
---
508+
509+
## Streaming AI Output
510+
511+
Groq chat completions use server-sent events for human-readable `analyze` and
512+
`ask` output. The provider adapter reconstructs the complete response while
513+
emitting incremental deltas to the output layer.
514+
515+
Terminal Markdown is buffered to paragraph boundaries before rendering. This
516+
keeps headings, lists, tables, wrapping, and inline formatting readable while
517+
still showing the answer before generation has fully completed.
518+
519+
Rules:
520+
521+
* streaming is an optional `AiClient` capability
522+
* commands fall back to regular completion for clients without streaming
523+
* the final reconstructed text is used for snapshot persistence and metadata
524+
* rate-limit retry and model fallback happen before consuming response deltas
525+
* `--json` never streams because stdout must contain one complete JSON document
526+
527+
---
528+
529+
## Prompt Strategy
510530

511531
Prompt templates should be centralized.
512532

docs/commands.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ Shared utilities, database access, authentication logic, and helpers.
234234
* Do not send the entire project source to AI
235235
* Snapshot must be regenerated after analyze
236236
* Snapshot must remain compact and deterministic
237-
* Raw provider errors must not be shown directly to users
237+
* Raw provider errors must not be shown directly to users
238+
* New AI interpretation streams progressively in human-readable mode
239+
* Cached interpretation is rendered immediately without a provider request
238240

239241
---
240242

@@ -317,7 +319,9 @@ app/api/auth/*
317319
* Include related files in output
318320
* Keep answer readable
319321
* Respond in the same language as the question
320-
* Technical labels can remain in English
322+
* Technical labels can remain in English
323+
* Stream new AI answers progressively in human-readable mode
324+
* Do not stream `--json`; emit one complete JSON document instead
321325

322326
### Output Example
323327

@@ -508,6 +512,7 @@ Contract:
508512
* stdout contains exactly one JSON document
509513
* ANSI codes and terminal decoration are disabled
510514
* progress sections and Markdown rendering are omitted
515+
* AI responses are buffered instead of streamed
511516
* runtime failures return a JSON object with `status`, `error`, and optional `hint`
512517
* `init --json` never prompts and therefore requires `GROQ_API_KEY` or an
513518
existing API key

docs/development-testing.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,22 @@ Parse stdout directly as JSON and verify that it contains no ANSI codes or
1414
terminal decoration. When invoking through `npm exec` or another package
1515
manager, ignore wrapper-owned stderr warnings and validate DevMap stdout
1616
separately.
17+
18+
## AI Streaming Output
19+
20+
Focused verification:
21+
22+
```bash
23+
pnpm --filter devmap exec tsx --test test/ai-client.test.ts test/ask-command.test.ts test/analyze-ai.test.ts test/json-output.test.ts
24+
```
25+
26+
With a live Groq key, run:
27+
28+
```bash
29+
devmap analyze --fresh
30+
devmap ask "explain the main architecture"
31+
devmap ask "explain the main architecture" --json
32+
```
33+
34+
Human output should appear progressively without raw Markdown markers. JSON
35+
output should wait for completion and remain one parseable document.

docs/for-me-personal/PROGRESS.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
# Progress DevMap
22

3-
Terakhir diperbarui: 2026-06-14
3+
Terakhir diperbarui: 2026-06-15
4+
5+
## Update 2026-06-15
6+
7+
### AI Response Streaming
8+
9+
- Human-readable `devmap ask` dan AI interpretation pada `devmap analyze`
10+
sekarang memakai Groq server-sent events.
11+
- Delta response direkonstruksi menjadi hasil lengkap untuk token metadata,
12+
snapshot persistence, dan cache.
13+
- Output ditampilkan progresif per paragraf agar heading, list, table, wrapping,
14+
dan inline Markdown tetap rapi.
15+
- Provider yang belum memiliki method streaming tetap memakai regular
16+
completion tanpa mengubah public command behavior.
17+
- Retry rate limit dan model fallback tetap berjalan sebelum stream dibaca.
18+
- `--json` sengaja tidak memakai streaming agar stdout tetap satu dokumen JSON.
19+
- Automated test mencakup SSE yang terpecah antar-network chunk, command
20+
streaming, snapshot persistence, dan JSON non-streaming.
21+
- Automated test saat ini berjumlah 65 dan seluruhnya lulus.
422

523
## Update 2026-06-14
624

docs/for-me-personal/TEST.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,39 @@ Expected:
101101
- API keys are never included;
102102
- packed package E2E verifies JSON output after tarball installation.
103103

104+
## AI Response Streaming
105+
106+
Focused automated test:
107+
108+
```powershell
109+
pnpm --filter devmap exec tsx --test test/ai-client.test.ts test/ask-command.test.ts test/analyze-ai.test.ts test/json-output.test.ts
110+
```
111+
112+
Coverage penting:
113+
114+
- SSE event tetap terbaca ketika JSON event terpecah pada network chunk;
115+
- delta dikirim berurutan dan hasil lengkap dikembalikan provider;
116+
- `ask` dan fresh AI interpretation `analyze` memakai streaming jika tersedia;
117+
- hasil lengkap `analyze` tetap disimpan ke snapshot;
118+
- `--json` memakai completion penuh dan tidak memanggil streaming.
119+
120+
Manual live check:
121+
122+
```powershell
123+
$env:GROQ_API_KEY="gsk_your_key"
124+
pnpm dev:cli -- analyze --fresh
125+
pnpm dev:cli -- ask "explain the main architecture"
126+
pnpm dev:cli -- ask "explain the main architecture" --json | ConvertFrom-Json
127+
Remove-Item Env:GROQ_API_KEY
128+
```
129+
130+
Expected:
131+
132+
- human output mulai tampil sebelum seluruh AI response selesai;
133+
- Markdown tidak tampil mentah;
134+
- model dan token usage tetap muncul setelah stream selesai;
135+
- JSON baru dicetak setelah response lengkap dan dapat diparse langsung.
136+
104137
## Urutan Testing Yang Direkomendasikan
105138

106139
Untuk development harian:

docs/roadmap.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ adding AI on top. If the foundation is wrong, AI output will be wrong too.
3434
- Groq integration with provider abstraction layer
3535
- Prompt templates for analyze and ask
3636
- Context Builder — keyword search + file ranking
37-
- Streaming output
37+
- [x] Streaming output for human `analyze` and `ask` responses
3838
- Retry logic + model fallback
3939
- Token-aware context trimming (max 5 files, max 200 lines each)
4040
- Cache integration — skip AI for unchanged files
@@ -120,4 +120,4 @@ Not planned. Not scheduled. Revisit when Phase 5 ships.
120120
| 1.2.0 | 2 | Express support solidified |
121121
| 2.0.0 | 3 | `devmap docs` + `devmap onboard` |
122122
| 3.0.0 | 4 | `devmap deadcode` + `devmap flow` + `devmap report` |
123-
| 4.0.0 | 5 | OpenAI + Gemini support |
123+
| 4.0.0 | 5 | OpenAI + Gemini support |

packages/cli/src/ai/completion.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { output } from "../utils/output.js";
2+
import type {
3+
AiClient,
4+
AiCompletionRequest,
5+
AiCompletionResult
6+
} from "./types.js";
7+
8+
export type AiCompletionExecution = {
9+
result: AiCompletionResult;
10+
streamed: boolean;
11+
};
12+
13+
export async function completeWithOptionalStreaming(
14+
client: AiClient,
15+
request: AiCompletionRequest,
16+
enabled: boolean,
17+
onStreamStart?: () => void
18+
): Promise<AiCompletionExecution> {
19+
if (!enabled || !client.stream) {
20+
return {
21+
result: await client.complete(request),
22+
streamed: false
23+
};
24+
}
25+
26+
const renderer = output.markdownStream();
27+
let started = false;
28+
try {
29+
const result = await client.stream(request, (delta) => {
30+
if (!started) {
31+
onStreamStart?.();
32+
started = true;
33+
}
34+
renderer.write(delta);
35+
});
36+
renderer.end();
37+
return { result, streamed: true };
38+
} catch (error) {
39+
renderer.end();
40+
throw error;
41+
}
42+
}

0 commit comments

Comments
 (0)