From b3aaa9a4af9a49ddd17260f568f7154b8719ccf5 Mon Sep 17 00:00:00 2001 From: elpresidank Date: Tue, 2 Jun 2026 02:58:19 -0500 Subject: [PATCH] Simplify text completion generator boundary --- ts/EFFECT_NATIVE_REWRITE_AUDIT.md | 28 +++++++++++++++++-- .../__tests__/text-completion-common.test.ts | 24 ++++++++++++++++ .../flow/src/model/text-completion/common.ts | 12 ++++---- 3 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 ts/packages/flow/src/__tests__/text-completion-common.test.ts diff --git a/ts/EFFECT_NATIVE_REWRITE_AUDIT.md b/ts/EFFECT_NATIVE_REWRITE_AUDIT.md index a712eecc..7949973d 100644 --- a/ts/EFFECT_NATIVE_REWRITE_AUDIT.md +++ b/ts/EFFECT_NATIVE_REWRITE_AUDIT.md @@ -13,11 +13,11 @@ Verified source roots: - Installed Effect beta used by this workspace: `ts/node_modules/effect` Current signal counts from `ts/packages` after the 2026-06-02 Text completion -stream sentinel slice: +generator boundary slice: | Signal | Count | | --- | ---: | -| `Effect.runPromise` | 169 | +| `Effect.runPromise` | 168 | | `Map<` | 88 | | `WebSocket` | 72 | | `new Map` | 62 | @@ -72,6 +72,10 @@ Notes: `Effect.void as Effect.Effect` assertions from provider stream unfold branches. Counts are unchanged because this was an Effect diagnostic and type-channel cleanup. +- The text completion generator boundary slice removed the + `Effect.runPromise(Effect.fail(...))` fallback and the related + `AsyncGenerator`/`IteratorResult` assertions from + `model/text-completion/common.ts`. - `Record` and `throwLibrarianServiceError` are now clean in `ts/packages`. @@ -565,6 +569,26 @@ Notes: - `cd ts && bun run test` - `git diff --check` +### 2026-06-02: Text Completion Generator Boundary Slice + +- Status: migrated and root-verified. +- Completed: + - `ts/packages/flow/src/model/text-completion/common.ts` now rejects + fallback `AsyncGenerator.throw(...)` calls with the mapped tagged provider + error directly instead of running `Effect.fail(...)` through + `Effect.runPromise`. + - The custom generator object no longer uses `as AsyncGenerator`, + `as Promise>`, or `as LlmChunk` assertions. + - Added a focused unit test for fallback throw mapping. +- Verification: + - `bun run --cwd ts/packages/flow test -- src/__tests__/text-completion-common.test.ts` + - `bun run --cwd ts/packages/flow build` + - `cd ts && bun run check` + - `bun run --cwd ts/packages/flow test` + - `cd ts && bun run build` + - `cd ts && bun run test` + - `git diff --check` + ## Subagent Findings To Preserve - MCP/workbench: diff --git a/ts/packages/flow/src/__tests__/text-completion-common.test.ts b/ts/packages/flow/src/__tests__/text-completion-common.test.ts new file mode 100644 index 00000000..8001fc47 --- /dev/null +++ b/ts/packages/flow/src/__tests__/text-completion-common.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "@effect/vitest"; +import type { LlmChunk } from "@trustgraph/base"; +import { providerRuntimeError, toAsyncGenerator } from "../model/text-completion/common.js"; + +const emptyChunkIterator = (): AsyncIterable => ({ + [Symbol.asyncIterator]: () => ({ + next: () => Promise.resolve({ done: true, value: undefined }), + }), +}); + +describe("text completion common helpers", () => { + it("maps fallback generator throw failures into tagged provider errors", async () => { + const generator = toAsyncGenerator( + emptyChunkIterator(), + (error) => providerRuntimeError("test-provider", error), + ); + + await expect(generator.throw("provider failed")).rejects.toMatchObject({ + _tag: "TextCompletionProviderError", + provider: "test-provider", + message: "provider failed", + }); + }); +}); diff --git a/ts/packages/flow/src/model/text-completion/common.ts b/ts/packages/flow/src/model/text-completion/common.ts index a4532e9f..b512ecea 100644 --- a/ts/packages/flow/src/model/text-completion/common.ts +++ b/ts/packages/flow/src/model/text-completion/common.ts @@ -86,16 +86,16 @@ export const toAsyncGenerator = ( const iterator = iterable[Symbol.asyncIterator](); let generator: AsyncGenerator; generator = { - next: (value?: unknown) => iterator.next(value as never), + next: (value?: unknown) => iterator.next(value), return: (value?: unknown) => iterator.return === undefined - ? Promise.resolve({ done: true, value: value as LlmChunk }) - : iterator.return(value as never) as Promise>, + ? Promise.resolve({ done: true, value }) + : iterator.return(value), throw: (error?: unknown) => iterator.throw === undefined - ? Effect.runPromise(Effect.fail(mapError(error))) as Promise> - : iterator.throw(error) as Promise>, + ? Promise.reject(mapError(error)) + : iterator.throw(error), [Symbol.asyncIterator]: () => generator, - } as AsyncGenerator; + }; return generator; };