Skip to content

perf(ai): bound stream parser buffers - #603

Closed
JustYannicc wants to merge 121 commits into
SuperCmdLabs:mainfrom
JustYannicc:codex/perf-ai-stream-parser-bounds
Closed

perf(ai): bound stream parser buffers#603
JustYannicc wants to merge 121 commits into
SuperCmdLabs:mainfrom
JustYannicc:codex/perf-ai-stream-parser-bounds

Conversation

@JustYannicc

Copy link
Copy Markdown
Collaborator

What changed

  • Replaced per-chunk toString() decoding in AI SSE and NDJSON streams with streaming UTF-8 TextDecoder handling.
  • Added AI_STREAM_PARSER_MAX_BUFFER_CHARS at 4 MiB to cap retained incomplete stream lines.
  • Added scripts/test-ai-provider-stream-parser-bounds.mjs for split UTF-8, valid long payloads, and bounded incomplete-line behavior across SSE and NDJSON.

Why

  • A provider that split a multi-byte character across chunks could corrupt streamed JSON text under per-chunk decoding.
  • A malformed provider could send an unterminated SSE/NDJSON line indefinitely and grow the retained parser buffer without bound.

Compatibility impact

  • Provider extractors and request lifecycle behavior are unchanged for OpenAI, OpenAI-compatible, Anthropic, Gemini, Ollama, and LM Studio routes.
  • Valid completed long-ish provider payloads below the 4 MiB retained-buffer limit continue to stream.
  • Malformed streams that exceed 4 MiB without a line break now fail with a bounded parser error instead of retaining more memory.

How tested

  • node --test scripts/test-ai-provider-stream-parser-bounds.mjs
  • node --test scripts/test-ai-provider-gemini-prompt-streaming.mjs
  • node --test scripts/test-ai-stream-ipc-batching.mjs
  • node --test scripts/test-raycast-ai-abort-listeners.mjs
  • node --test scripts/test-ai-provider-gemini-prompt-streaming.mjs scripts/test-ai-stream-ipc-batching.mjs scripts/test-raycast-ai-abort-listeners.mjs scripts/test-ai-provider-stream-parser-bounds.mjs
  • node /opt/homebrew/lib/node_modules/npm/bin/npm-cli.js run build:main
  • Codex LSP diagnostics on src/main/ai-provider.ts: no diagnostics found.

Performance evidence

  • New harness diagnostics show legacy per-chunk decoding does not round-trip split UTF-8 for SSE or NDJSON, while streaming TextDecoder does.
  • New harness diagnostics show legacy retained incomplete SSE/NDJSON text could grow past 4,194,305 characters; bounded parser retention stops at 4,194,304 characters and rejects over-limit incomplete lines.
  • Existing IPC batching evidence remains intact: 600 provider chunks coalesce to 1 IPC chunk event in the focused batching harness.

Stack validation

  • Verified starting git rev-parse HEAD was ac90ce403aeddab98d0a1b245afce268c48b8bfd before branching from codex/perf-wave4-integrated-stack-20260705.
  • Baseline dependency state: node_modules was absent, so TS-import harnesses could not use local esbuild until dependencies were installed.
  • npm ci through the local npm shim was blocked by machine policy; direct Homebrew npm ci reached dependency scripts but nested npm calls hit the same shim. Installed locked dependencies with node /opt/homebrew/lib/node_modules/npm/bin/npm-cli.js ci --ignore-scripts for focused tests.

Replaces

  • None.

(cherry picked from commit 83c2a89)
(cherry picked from commit 0a7560b)
# Conflicts:
#	src/renderer/src/raycast-api/hooks/use-cached-promise.ts
…launcher surfaces

# Conflicts:
#	src/renderer/src/raycast-api/list-runtime-renderers.tsx
#	src/renderer/src/raycast-api/list-runtime.tsx
…IO stalls

# Conflicts:
#	src/renderer/src/CameraExtension.tsx
# Conflicts:
#	src/renderer/src/raycast-api/list-runtime.tsx
…resh overhead

# Conflicts:
#	scripts/bench-script-command-discovery.mjs
#	scripts/benchmark-file-search-delete-batch.mjs
#	scripts/lib/script-command-runner-harness.mjs
#	scripts/lib/ts-import.mjs
#	scripts/test-file-search-delete-batch.mjs
#	scripts/test-script-command-runner.mjs
#	src/main/file-search-index.ts
#	src/main/main.ts
…hurn

# Conflicts:
#	scripts/lib/script-command-runner-harness.mjs
#	scripts/lib/ts-import.mjs
#	scripts/test-extension-lifecycle-sandbox.mjs
#	scripts/test-script-command-runner.mjs
#	src/main/main.ts
#	src/renderer/src/ExtensionView.tsx
… hot paths

# Conflicts:
#	scripts/test-use-frecency-sorting.mjs
#	src/renderer/src/raycast-api/form-runtime.tsx
#	src/renderer/src/raycast-api/grid-runtime-hooks.ts
#	src/renderer/src/raycast-api/grid-runtime-items.tsx
#	src/renderer/src/raycast-api/hooks/use-frecency-sorting.ts
# Conflicts:
#	.github/workflows/project-checks.yml
#	package.json
#	scripts/file-search-perf-harness.mjs
#	scripts/test-browser-search-performance.mjs
#	scripts/test-file-search-perf-harness.mjs
#	scripts/test-root-search-perf.mjs
#	src/renderer/src/CameraExtension.tsx
#	src/renderer/src/hooks/useBrowserSearch.ts
#	src/renderer/src/raycast-api/list-runtime.tsx
…cle and media fixes

# Conflicts:
#	scripts/lib/ts-import.mjs
#	scripts/measure-extension-bundle-cache.mjs
#	src/main/commands.ts
#	src/main/extension-runner.ts
#	src/main/main.ts
#	src/renderer/src/ExtensionView.tsx
#	src/renderer/src/raycast-api/grid-runtime-hooks.ts
#	src/renderer/src/raycast-api/list-runtime-hooks.ts
@JustYannicc

Copy link
Copy Markdown
Collaborator Author

Superseded by the consolidated runtime/lifecycle/media PR: #622

@JustYannicc JustYannicc closed this Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant