Skip to content

feat(ai): stream AI suggestions over HTTP - #664

Open
Reversean wants to merge 1 commit into
fix/ai-prompt-injectionfrom
feat/ai-streaming
Open

feat(ai): stream AI suggestions over HTTP#664
Reversean wants to merge 1 commit into
fix/ai-prompt-injectionfrom
feat/ai-streaming

Conversation

@Reversean

@Reversean Reversean commented Jul 28, 2026

Copy link
Copy Markdown
Member
  • AI suggestion stream is served through a dedicated GET /integration/ai/stream Express route.
  • The Fetch API Response returned by the AI SDK's toUIMessageStreamResponse() is adapted onto the Express response.
  • Declares ReadableStream/Response as ESLint globals (.eslintrc.js) - valid Node 18+ runtime globals that predate ESLint's bundled node env.

@Reversean
Reversean requested a review from FeironoX5 July 28, 2026 14:48
@Reversean
Reversean force-pushed the feat/ai-streaming branch from 2265cba to 6bf913f Compare July 29, 2026 13:26
@Reversean
Reversean changed the base branch from chore/bump-types-node to fix/ai-prompt-injection July 29, 2026 14:31
@Reversean
Reversean force-pushed the feat/ai-streaming branch from 2ee7bd0 to 497be8b Compare July 29, 2026 16:04
@Reversean
Reversean force-pushed the feat/ai-streaming branch from 497be8b to 9ff73a0 Compare July 29, 2026 16:07
@Reversean
Reversean force-pushed the feat/ai-streaming branch from 9ff73a0 to 8f8ee4a Compare July 29, 2026 17:21
@Reversean Reversean changed the title feat: stream AI suggestions over HTTP via a plain Express route feat(ai): stream AI suggestions over HTTP Jul 29, 2026
An Ask AI suggestion takes tens of seconds to generate, and the GraphQL resolver can only return it once the model has finished.

Suggestions are now also available as a stream over a plain Express route, guarded by the same workspace membership check as the resolver and rejecting requests missing the project, event or repetition id before reaching the model.
@Reversean
Reversean force-pushed the feat/ai-streaming branch from 8f8ee4a to bd3667c Compare July 29, 2026 17:49
});
}

const res: any = new Writable({

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably could be typed, since you assign it right away

Comment on lines +93 to +108
const response = result.toUIMessageStreamResponse();

res.status(response.status);
response.headers.forEach((value, key) => res.setHeader(key, value));

if (!response.body) {
res.end();

return;
}

Readable.fromWeb(response.body as NodeReadableStream<Uint8Array>).pipe(res);
} catch (error) {
next(error);
}
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can just use

result.pipeUIMessageStreamToResponse(res);

with extra options or just

result.pipeTextStreamToResponse(res);

since we don't need any metadata toolcalls etc

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.

2 participants