fix(ai): memoized PI_REQ_DEBUG response-log close to avoid truncated body on cancel

This commit is contained in:
can1357
2026-06-07 09:34:20 +02:00
parent 84573a0d1a
commit b6d422ff65
2 changed files with 13 additions and 7 deletions
+1
View File
@@ -27,6 +27,7 @@
- Fixed the HTTP 400 raw-request dumper (`appendRawHttpRequestDumpFor400`) littering the real `~/.omp/logs/http-400-requests` directory during tests. Provider suites exercise the 400 error path with mocked `fetch` responses, which the dumper could not distinguish from genuine failures; it now skips persistence under the Bun test runner (`isBunTestRuntime()`).
- Fixed Anthropic Opus requests unnecessarily forcing `tool_choice.disable_parallel_tool_use`, allowing Claude Opus to use the provider's default parallel tool-calling behavior again.
- Fixed parallel `function_call` items losing arguments against llama.cpp's OpenAI Responses endpoint (`/v1/responses`), where every call but the last finalized with `{}` and the agent rejected them with `path: Invalid input: expected string, received undefined`. llama.cpp's `to_json_oaicompat_resp` emits `output_item.added` with only `item.call_id` (no `item.id`, no `output_index`) while the matching `function_call_arguments.delta` carries `item_id: "fc_<call_id>"`. `processResponsesStream` now registers function-call and custom-tool-call items under `item.call_id` as a secondary lookup key (alongside `item.id`/`output_index`) so identifier-deviant hosts route deltas and done events to the right block. ([#2015](https://github.com/can1357/oh-my-pi/issues/2015))
- Fixed `PI_REQ_DEBUG` response recording truncating the captured body when a streamed response was cancelled mid-flight. The response tee in `wrapResponse` could call `FileRequestDebugResponseLog.close()` from both the `cancel` callback and the resumed `pull` (which observes `done` once the source reader is cancelled); the second caller saw the handle already nulled and returned before the first caller's pending write flushed, so the `.res.log` lost the already-buffered chunk. `close()` now memoizes its flush-and-close promise so every caller awaits the same completion.
## [15.10.0] - 2026-06-06
+12 -7
View File
@@ -170,6 +170,7 @@ class FileRequestDebugSession implements RequestDebugSession {
class FileRequestDebugResponseLog implements RequestDebugResponseLog {
#handle: fs.FileHandle | undefined;
#pending: Promise<void> = Promise.resolve();
#closed: Promise<void> | undefined;
constructor(handle: fs.FileHandle) {
this.#handle = handle;
@@ -184,15 +185,19 @@ class FileRequestDebugResponseLog implements RequestDebugResponseLog {
});
}
async close(): Promise<void> {
close(): Promise<void> {
if (this.#closed) return this.#closed;
const handle = this.#handle;
if (!handle) return;
if (!handle) return Promise.resolve();
this.#handle = undefined;
try {
await this.#pending;
} finally {
await handle.close();
}
this.#closed = (async () => {
try {
await this.#pending;
} finally {
await handle.close();
}
})();
return this.#closed;
}
}