Skip to content

Commit d4f533f

Browse files
committed
Improve the way we stream from tasks to the server
1 parent 0dbc164 commit d4f533f

File tree

6 files changed

+53
-44
lines changed

6 files changed

+53
-44
lines changed

apps/webapp/server.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,19 @@ if (process.env.HTTP_SERVER_DISABLED !== "true") {
8181
});
8282

8383
if (process.env.DASHBOARD_AND_API_DISABLED !== "true") {
84+
if (process.env.ALLOW_ONLY_REALTIME_API === "true") {
85+
// Block all requests that do not start with /realtime
86+
app.use((req, res, next) => {
87+
// Make sure /healthcheck is still accessible
88+
if (!req.url.startsWith("/realtime") && req.url !== "/healthcheck") {
89+
res.status(404).send("Not Found");
90+
return;
91+
}
92+
93+
next();
94+
});
95+
}
96+
8497
app.use(apiRateLimiter);
8598

8699
app.all(

packages/core/src/v3/runMetadata/manager.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,14 @@ export class StandardMetadataManager implements RunMetadataManager {
233233
// Add the key to the special stream metadata object
234234
this.appendKey(`$$streams`, key);
235235
this.setKey("$$streamsVersion", this.streamsVersion);
236+
this.setKey("$$streamsBaseUrl", this.streamsBaseUrl);
236237

237238
await this.flush();
238239

239240
const streamInstance = new MetadataStream({
240241
key,
241242
runId: this.runId,
242-
iterator: $value[Symbol.asyncIterator](),
243+
source: $value,
243244
baseUrl: this.streamsBaseUrl,
244245
headers: this.apiClient.getHeaders(),
245246
signal,

packages/core/src/v3/runMetadata/metadataStream.ts

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,48 @@ export type MetadataOptions<T> = {
22
baseUrl: string;
33
runId: string;
44
key: string;
5-
iterator: AsyncIterator<T>;
5+
source: AsyncIterable<T>;
66
headers?: Record<string, string>;
77
signal?: AbortSignal;
88
version?: "v1" | "v2";
99
};
1010

1111
export class MetadataStream<T> {
1212
private controller = new AbortController();
13-
private serverQueue: Array<Promise<IteratorResult<T>>> = [];
14-
private consumerQueue: Array<Promise<IteratorResult<T>>> = [];
15-
private serverIterator: AsyncIterator<T>;
16-
private consumerIterator: AsyncIterator<T>;
13+
private serverStream: ReadableStream<T>;
14+
private consumerStream: ReadableStream<T>;
1715
private streamPromise: Promise<void | Response>;
1816

1917
constructor(private options: MetadataOptions<T>) {
20-
const { serverIterator, consumerIterator } = this.createTeeIterators();
21-
this.serverIterator = serverIterator;
22-
this.consumerIterator = consumerIterator;
18+
const [serverStream, consumerStream] = this.createTeeStreams();
19+
this.serverStream = serverStream;
20+
this.consumerStream = consumerStream;
2321

2422
this.streamPromise = this.initializeServerStream();
2523
}
2624

27-
private createTeeIterators() {
28-
const teeIterator = (queue: Array<Promise<IteratorResult<T>>>): AsyncIterator<T> => ({
29-
next: () => {
30-
if (queue.length === 0) {
31-
const result = this.options.iterator.next();
32-
this.serverQueue.push(result);
33-
this.consumerQueue.push(result);
25+
private createTeeStreams() {
26+
const readableSource = new ReadableStream<T>({
27+
start: async (controller) => {
28+
for await (const value of this.options.source) {
29+
controller.enqueue(value);
3430
}
35-
return queue.shift()!;
31+
32+
controller.close();
3633
},
3734
});
3835

39-
return {
40-
serverIterator: teeIterator(this.serverQueue),
41-
consumerIterator: teeIterator(this.consumerQueue),
42-
};
36+
return readableSource.tee();
4337
}
4438

45-
private initializeServerStream(): Promise<void | Response> {
46-
const serverIterator = this.serverIterator;
47-
48-
const serverStream = new ReadableStream({
49-
async pull(controller) {
50-
try {
51-
const { value, done } = await serverIterator.next();
52-
if (done) {
53-
controller.close();
54-
return;
55-
}
56-
57-
controller.enqueue(JSON.stringify(value) + "\n");
58-
} catch (err) {
59-
controller.error(err);
60-
}
61-
},
62-
cancel: () => this.controller.abort(),
63-
});
39+
private initializeServerStream(): Promise<Response> {
40+
const serverStream = this.serverStream.pipeThrough(
41+
new TransformStream<T, string>({
42+
async transform(chunk, controller) {
43+
controller.enqueue(JSON.stringify(chunk) + "\n");
44+
},
45+
})
46+
);
6447

6548
return fetch(
6649
`${this.options.baseUrl}/realtime/${this.options.version ?? "v1"}/streams/${
@@ -82,6 +65,19 @@ export class MetadataStream<T> {
8265
}
8366

8467
public [Symbol.asyncIterator]() {
85-
return this.consumerIterator;
68+
return streamToAsyncIterator(this.consumerStream);
69+
}
70+
}
71+
72+
async function* streamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterableIterator<T> {
73+
const reader = stream.getReader();
74+
try {
75+
while (true) {
76+
const { done, value } = await reader.read();
77+
if (done) return;
78+
yield value;
79+
}
80+
} finally {
81+
reader.releaseLock();
8682
}
8783
}

references/nextjs-realtime/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"build": "next build",
88
"start": "next start",
99
"lint": "next lint",
10-
"dev:trigger": "trigger dev"
10+
"dev:trigger": "trigger dev",
11+
"deploy": "trigger deploy"
1112
},
1213
"dependencies": {
1314
"@ai-sdk/openai": "^1.0.1",

references/nextjs-realtime/src/components/TriggerButtonWithStreaming.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export default function TriggerButton({ accessToken }: { accessToken: string })
2323
>("openai-streaming", {
2424
accessToken,
2525
baseURL: process.env.NEXT_PUBLIC_TRIGGER_API_URL,
26-
experimental_throttleInMs: 100,
2726
});
2827

2928
const openWeatherReport = useCallback(() => {

references/nextjs-realtime/src/trigger/ai.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ export const openaiStreaming = schemaTask({
108108
});
109109

110110
const stream = await metadata.stream("openai", result.fullStream);
111-
await metadata.stream("openaiText", result.textStream);
112111
},
113112
});
114113

0 commit comments

Comments
 (0)