Skip to content

Commit 52f4d30

Browse files
authored
Merge pull request #207 from link-assistant/issue-206-015289ba709a
fix: check verbose flag at HTTP call time, not SDK creation time (#206)
2 parents f2b4547 + 2def3be commit 52f4d30

7 files changed

Lines changed: 2683 additions & 3 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Case Study: Issue #206 - No debug HTTP requests/responses in --verbose mode
2+
3+
## Issue Summary
4+
5+
When running `agent --model opencode/kimi-k2.5-free --verbose`, the `--verbose` flag enables detailed JSON logging output, but raw HTTP requests and responses to AI model providers are not logged. This makes it impossible to diagnose provider failures — the user can see the model returned zero tokens but cannot see the actual HTTP request/response that caused the failure.
6+
7+
## Evidence
8+
9+
Log file: `solution-draft-log.txt` (2237 lines from gist `a0bb13a57b06e7df16e36caf9093e9e6`)
10+
11+
Key observations from the log:
12+
- Version `0.16.11` was running (which includes the verbose HTTP logging from PR #205/issue #204)
13+
- `getSDK` log messages appear at lines 681 and 700 — confirming the SDK was created
14+
- Model `opencode/kimi-k2.5-free` resolved to `@ai-sdk/openai-compatible` bundled provider
15+
- The model responded with text and tool calls at lines 1970-1976
16+
- **Zero "HTTP request" or "HTTP response" log messages** appear anywhere in the 2237-line log
17+
- Provider returned zero tokens with unknown finish reason at line 1995
18+
19+
## Root Cause Analysis
20+
21+
The verbose HTTP logging code in `js/src/provider/provider.ts` (lines 1204-1388) checked `Flag.OPENCODE_VERBOSE` at **SDK creation time** using a conditional guard:
22+
23+
```typescript
24+
// OLD CODE (buggy)
25+
if (Flag.OPENCODE_VERBOSE) {
26+
const innerFetch = options['fetch'];
27+
options['fetch'] = async (...) => { /* logging wrapper */ };
28+
}
29+
```
30+
31+
This created a race condition between:
32+
1. **SDK creation time**: When `getSDK()` creates and caches the provider SDK
33+
2. **Flag initialization time**: When `Flag.setVerbose(true)` is called from the yargs middleware
34+
35+
The SDK cache uses `Bun.hash.xxHash32(JSON.stringify({ pkg, options }))` as the key. Since `JSON.stringify` strips function values, the cache key is the same regardless of whether the verbose wrapper was applied. This means:
36+
37+
- If the SDK is created **before** verbose mode is enabled, the fetch wrapper is never applied
38+
- The cached SDK (without verbose logging) is then reused for all subsequent requests
39+
- Even though `Flag.OPENCODE_VERBOSE` is `true` by the time the actual HTTP request happens, the wrapper was never installed
40+
41+
### Investigation Timeline
42+
43+
1. Verified `Flag.setVerbose(true)` is called in yargs middleware before the command handler
44+
2. Verified the AI SDK properly passes custom `fetch` to `postJsonToApi` (confirmed via experiment `test-verbose-fetch-wrapper.ts`)
45+
3. Verified the fetch wrapping mechanism works correctly via experiment
46+
4. Verified `Flag.OPENCODE_VERBOSE` timing with experiment `test-verbose-flag-timing.ts`
47+
5. Identified that while individual tests pass, the production SDK caching and module loading order may cause the flag check to fail
48+
49+
## Fix
50+
51+
Changed the verbose logging wrapper from a creation-time conditional to a call-time check:
52+
53+
```typescript
54+
// NEW CODE (fixed)
55+
{
56+
const innerFetch = options['fetch'];
57+
options['fetch'] = async (input, init) => {
58+
// Check verbose flag at call time — not at SDK creation time
59+
if (!Flag.OPENCODE_VERBOSE) {
60+
return innerFetch(input, init);
61+
}
62+
// ... logging logic ...
63+
};
64+
}
65+
```
66+
67+
Key changes:
68+
- **Always** wrap the fetch function (remove outer `if` guard)
69+
- Check `Flag.OPENCODE_VERBOSE` **inside** the wrapper at call time
70+
- When verbose is disabled, the wrapper is a zero-overhead passthrough (just a boolean check)
71+
- When verbose is enabled (even if it was disabled at SDK creation), logging works correctly
72+
73+
This is robust because:
74+
- No dependency on flag timing vs SDK creation order
75+
- Verbose mode can be toggled dynamically
76+
- Negligible overhead when verbose is disabled (single boolean check per HTTP request)
77+
78+
## Files Changed
79+
80+
- `js/src/provider/provider.ts`: Move verbose flag check from SDK creation time to fetch call time
81+
- `js/tests/verbose-http-logging.test.ts`: Add tests for runtime flag checking behavior
82+
- `js/experiments/test-verbose-fetch-wrapper.ts`: Experiment verifying fetch wrapping works
83+
- `js/experiments/test-verbose-flag-timing.ts`: Experiment verifying flag timing
84+
85+
## Test Coverage
86+
87+
Added 3 new tests in `verbose-http-logging.test.ts`:
88+
1. Verbose wrapper is a no-op when verbose is disabled
89+
2. Verbose wrapper logs when verbose is enabled at call time
90+
3. Verbose flag can change between SDK creation and fetch call

0 commit comments

Comments
 (0)