@jaypie/mcp 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/prompts/Jaypie_CDK_Constructs_and_Patterns.md +23 -28
- package/prompts/Jaypie_DynamoDB_Package.md +55 -55
- package/prompts/Jaypie_Express_Package.md +0 -44
- package/prompts/{Jaypie_Vocabulary_Commander.md → Jaypie_Fabric_Commander.md} +38 -38
- package/prompts/{Jaypie_Vocabulary_LLM.md → Jaypie_Fabric_LLM.md} +45 -45
- package/prompts/{Jaypie_Vocabulary_Lambda.md → Jaypie_Fabric_Lambda.md} +40 -42
- package/prompts/{Jaypie_Vocabulary_MCP.md → Jaypie_Fabric_MCP.md} +39 -39
- package/prompts/{Jaypie_Vocabulary_Package.md → Jaypie_Fabric_Package.md} +151 -142
- package/prompts/Jaypie_Init_CICD_with_GitHub_Actions.md +12 -12
- package/prompts/Jaypie_Streaming.md +408 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Complete guide to streaming in Jaypie - Lambda, Express, and SSE patterns
|
|
3
|
+
globs: packages/express/**, packages/lambda/**, packages/aws/**
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Jaypie Streaming Guide
|
|
7
|
+
|
|
8
|
+
Jaypie provides three distinct streaming patterns for different deployment scenarios. This guide explains when to use each approach and how to implement streaming correctly.
|
|
9
|
+
|
|
10
|
+
## Quick Reference
|
|
11
|
+
|
|
12
|
+
| Handler | Package | Express Required | Use Case |
|
|
13
|
+
|---------|---------|------------------|----------|
|
|
14
|
+
| `lambdaStreamHandler` | `@jaypie/lambda` | No | Pure Lambda Function URL streaming |
|
|
15
|
+
| `expressStreamHandler` | `@jaypie/express` | Yes | Express routes with SSE |
|
|
16
|
+
| `createLambdaStreamHandler` | `@jaypie/express` | Yes | Express app deployed to Lambda with streaming |
|
|
17
|
+
|
|
18
|
+
### Decision Guide
|
|
19
|
+
|
|
20
|
+
- **Building a pure Lambda function?** Use `lambdaStreamHandler`
|
|
21
|
+
- **Building an Express app for non-Lambda deployment?** Use `expressStreamHandler`
|
|
22
|
+
- **Building an Express app that runs on Lambda?** Use `createLambdaStreamHandler`
|
|
23
|
+
|
|
24
|
+
## Streaming Utilities
|
|
25
|
+
|
|
26
|
+
All streaming utilities are exported from `@jaypie/aws` and re-exported through `jaypie`:
|
|
27
|
+
|
|
28
|
+
| Function | Purpose |
|
|
29
|
+
|----------|---------|
|
|
30
|
+
| `createLambdaStream(stream, writer)` | Pipe async iterable to Lambda response writer |
|
|
31
|
+
| `createExpressStream(stream, res)` | Pipe async iterable to Express response with SSE headers |
|
|
32
|
+
| `JaypieStream` | Wrapper class with `.toLambda()` and `.toExpress()` methods |
|
|
33
|
+
| `createJaypieStream(source)` | Factory function for JaypieStream |
|
|
34
|
+
| `formatSSE(chunk)` | Format a chunk as SSE event string |
|
|
35
|
+
| `streamToSSE(stream)` | Convert async iterable to SSE-formatted strings |
|
|
36
|
+
|
|
37
|
+
### Types
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import type {
|
|
41
|
+
ExpressStreamResponse,
|
|
42
|
+
LambdaStreamWriter,
|
|
43
|
+
SSEEvent,
|
|
44
|
+
StreamChunk,
|
|
45
|
+
} from "jaypie";
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Pattern 1: lambdaStreamHandler
|
|
49
|
+
|
|
50
|
+
Use for pure Lambda functions without Express. Requires AWS Lambda Response Streaming via Function URL.
|
|
51
|
+
|
|
52
|
+
### Basic Usage
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { lambdaStreamHandler } from "jaypie";
|
|
56
|
+
import type { StreamHandlerContext } from "@jaypie/lambda";
|
|
57
|
+
|
|
58
|
+
const streamWorker = lambdaStreamHandler(
|
|
59
|
+
async (event: unknown, context: StreamHandlerContext) => {
|
|
60
|
+
const { responseStream } = context;
|
|
61
|
+
|
|
62
|
+
responseStream.write("event: start\ndata: {}\n\n");
|
|
63
|
+
|
|
64
|
+
for (let i = 0; i < 5; i++) {
|
|
65
|
+
responseStream.write(`event: data\ndata: {"count": ${i}}\n\n`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
responseStream.write("event: done\ndata: {}\n\n");
|
|
69
|
+
// Handler automatically calls responseStream.end()
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "streamWorker",
|
|
73
|
+
contentType: "text/event-stream",
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Wrap with AWS streamifyResponse for Lambda
|
|
78
|
+
declare const awslambda: { streamifyResponse: <T>(handler: T) => T };
|
|
79
|
+
export const handler = awslambda.streamifyResponse(streamWorker);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### With LLM Streaming
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { lambdaStreamHandler, createLambdaStream, Llm } from "jaypie";
|
|
86
|
+
import type { StreamHandlerContext } from "@jaypie/lambda";
|
|
87
|
+
|
|
88
|
+
interface PromptEvent {
|
|
89
|
+
prompt?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const llmStreamHandler = lambdaStreamHandler(
|
|
93
|
+
async (event: PromptEvent, context: StreamHandlerContext) => {
|
|
94
|
+
const llm = new Llm("anthropic");
|
|
95
|
+
const stream = llm.stream(event.prompt || "Hello");
|
|
96
|
+
|
|
97
|
+
// createLambdaStream pipes chunks as SSE events
|
|
98
|
+
await createLambdaStream(stream, context.responseStream);
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "llmStream",
|
|
102
|
+
secrets: ["ANTHROPIC_API_KEY"],
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
declare const awslambda: { streamifyResponse: <T>(handler: T) => T };
|
|
107
|
+
export const handler = awslambda.streamifyResponse(llmStreamHandler);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Handler Options
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import type { LambdaStreamHandlerOptions } from "@jaypie/lambda";
|
|
114
|
+
|
|
115
|
+
const options: LambdaStreamHandlerOptions = {
|
|
116
|
+
name: "myStreamHandler", // Handler name for logging
|
|
117
|
+
contentType: "text/event-stream", // Response content type (default)
|
|
118
|
+
chaos: "low", // Chaos testing level
|
|
119
|
+
secrets: ["API_KEY"], // AWS secrets to load into process.env
|
|
120
|
+
setup: [], // Setup function(s)
|
|
121
|
+
teardown: [], // Teardown function(s)
|
|
122
|
+
validate: [], // Validation function(s)
|
|
123
|
+
throw: false, // Re-throw errors instead of SSE error
|
|
124
|
+
unavailable: false, // Return 503 if true
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### CDK Configuration
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { JaypieLambda } from "@jaypie/constructs";
|
|
132
|
+
import { FunctionUrlAuthType, InvokeMode } from "aws-cdk-lib/aws-lambda";
|
|
133
|
+
|
|
134
|
+
const streamingLambda = new JaypieLambda(this, "StreamingFunction", {
|
|
135
|
+
code: "dist",
|
|
136
|
+
handler: "streamWorker.handler",
|
|
137
|
+
timeout: Duration.minutes(5),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Enable Lambda Response Streaming
|
|
141
|
+
streamingLambda.addFunctionUrl({
|
|
142
|
+
authType: FunctionUrlAuthType.NONE,
|
|
143
|
+
invokeMode: InvokeMode.RESPONSE_STREAM,
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Pattern 2: expressStreamHandler
|
|
148
|
+
|
|
149
|
+
Use for Express applications not running on Lambda. Sets SSE headers automatically.
|
|
150
|
+
|
|
151
|
+
### Basic Usage
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { expressStreamHandler } from "jaypie";
|
|
155
|
+
import type { Request, Response } from "express";
|
|
156
|
+
|
|
157
|
+
const streamRoute = expressStreamHandler(async (req: Request, res: Response) => {
|
|
158
|
+
// Write SSE events directly to response
|
|
159
|
+
res.write("event: message\ndata: {\"text\": \"Hello\"}\n\n");
|
|
160
|
+
res.write("event: message\ndata: {\"text\": \"World\"}\n\n");
|
|
161
|
+
// Handler automatically ends the stream
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
app.get("/stream", streamRoute);
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### With LLM Streaming
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { expressStreamHandler, createExpressStream, Llm } from "jaypie";
|
|
171
|
+
|
|
172
|
+
const llmStreamRoute = expressStreamHandler(async (req: Request, res: Response) => {
|
|
173
|
+
const llm = new Llm("anthropic");
|
|
174
|
+
const stream = llm.stream(req.body.prompt);
|
|
175
|
+
|
|
176
|
+
// createExpressStream pipes LLM chunks as SSE events
|
|
177
|
+
await createExpressStream(stream, res);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
app.post("/chat", llmStreamRoute);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Handler Options
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import type { ExpressStreamHandlerOptions } from "jaypie";
|
|
187
|
+
|
|
188
|
+
const options: ExpressStreamHandlerOptions = {
|
|
189
|
+
name: "myStreamHandler", // Handler name for logging
|
|
190
|
+
contentType: "text/event-stream", // Default SSE content type
|
|
191
|
+
chaos: "low", // Chaos testing level
|
|
192
|
+
secrets: ["API_KEY"], // Secrets to load
|
|
193
|
+
setup: [], // Setup function(s)
|
|
194
|
+
teardown: [], // Teardown function(s)
|
|
195
|
+
validate: [], // Validation function(s)
|
|
196
|
+
locals: {}, // Values to set on req.locals
|
|
197
|
+
unavailable: false, // Return 503 if true
|
|
198
|
+
};
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### SSE Headers
|
|
202
|
+
|
|
203
|
+
`expressStreamHandler` automatically sets these headers:
|
|
204
|
+
|
|
205
|
+
- `Content-Type: text/event-stream`
|
|
206
|
+
- `Cache-Control: no-cache`
|
|
207
|
+
- `Connection: keep-alive`
|
|
208
|
+
- `X-Accel-Buffering: no` (disables nginx buffering)
|
|
209
|
+
|
|
210
|
+
## Pattern 3: createLambdaStreamHandler
|
|
211
|
+
|
|
212
|
+
Use for Express applications deployed to AWS Lambda with streaming support.
|
|
213
|
+
|
|
214
|
+
### Basic Usage
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import express from "express";
|
|
218
|
+
import { createLambdaStreamHandler, expressStreamHandler } from "jaypie";
|
|
219
|
+
|
|
220
|
+
const app = express();
|
|
221
|
+
|
|
222
|
+
app.get("/stream", expressStreamHandler(async (req, res) => {
|
|
223
|
+
res.write("event: message\ndata: {\"text\": \"Hello\"}\n\n");
|
|
224
|
+
res.write("event: message\ndata: {\"text\": \"World\"}\n\n");
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
// Export streaming Lambda handler for Express app
|
|
228
|
+
export const handler = createLambdaStreamHandler(app);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Combined Buffered and Streaming
|
|
232
|
+
|
|
233
|
+
When you need both regular endpoints and streaming endpoints:
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import express from "express";
|
|
237
|
+
import {
|
|
238
|
+
createLambdaHandler,
|
|
239
|
+
createLambdaStreamHandler,
|
|
240
|
+
expressHandler,
|
|
241
|
+
expressStreamHandler,
|
|
242
|
+
cors,
|
|
243
|
+
} from "jaypie";
|
|
244
|
+
|
|
245
|
+
const app = express();
|
|
246
|
+
app.use(express.json());
|
|
247
|
+
app.use(cors());
|
|
248
|
+
|
|
249
|
+
// Standard buffered route
|
|
250
|
+
app.get("/api/data", expressHandler(async (req, res) => {
|
|
251
|
+
return { data: "buffered response" };
|
|
252
|
+
}));
|
|
253
|
+
|
|
254
|
+
// SSE streaming route
|
|
255
|
+
app.get("/api/stream", expressStreamHandler(async (req, res) => {
|
|
256
|
+
for (let i = 0; i < 5; i++) {
|
|
257
|
+
res.write(`event: update\ndata: {"count": ${i}}\n\n`);
|
|
258
|
+
}
|
|
259
|
+
}));
|
|
260
|
+
|
|
261
|
+
// Choose based on needs:
|
|
262
|
+
// - createLambdaHandler for buffered-only
|
|
263
|
+
// - createLambdaStreamHandler for streaming support
|
|
264
|
+
export const handler = createLambdaStreamHandler(app);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Streaming Utilities Deep Dive
|
|
268
|
+
|
|
269
|
+
### JaypieStream Class
|
|
270
|
+
|
|
271
|
+
Wraps an async iterable for convenient streaming to different targets:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
import { JaypieStream, createJaypieStream, Llm } from "jaypie";
|
|
275
|
+
|
|
276
|
+
const llm = new Llm("anthropic");
|
|
277
|
+
const stream = createJaypieStream(llm.stream("Hello"));
|
|
278
|
+
|
|
279
|
+
// Pipe to Lambda
|
|
280
|
+
await stream.toLambda(context.responseStream);
|
|
281
|
+
|
|
282
|
+
// Or pipe to Express
|
|
283
|
+
await stream.toExpress(res);
|
|
284
|
+
|
|
285
|
+
// Or iterate directly
|
|
286
|
+
for await (const chunk of stream) {
|
|
287
|
+
console.log(chunk);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Or convert to SSE strings
|
|
291
|
+
for await (const sseEvent of stream.toSSE()) {
|
|
292
|
+
console.log(sseEvent);
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Manual SSE Formatting
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import { formatSSE } from "jaypie";
|
|
300
|
+
|
|
301
|
+
const chunk = { type: "message", content: "Hello" };
|
|
302
|
+
const sseString = formatSSE(chunk);
|
|
303
|
+
// "event: message\ndata: {\"type\":\"message\",\"content\":\"Hello\"}\n\n"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Converting Async Iterables
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { streamToSSE } from "jaypie";
|
|
310
|
+
|
|
311
|
+
async function* myGenerator() {
|
|
312
|
+
yield { type: "start", data: {} };
|
|
313
|
+
yield { type: "data", value: 42 };
|
|
314
|
+
yield { type: "end", data: {} };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
for await (const sseEvent of streamToSSE(myGenerator())) {
|
|
318
|
+
responseStream.write(sseEvent);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Error Handling
|
|
323
|
+
|
|
324
|
+
Errors in streaming handlers are written as SSE error events:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// Format: event: error\ndata: {"errors":[{"status":500,"title":"Internal Error"}]}\n\n
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
For `lambdaStreamHandler`, set `throw: true` to re-throw errors instead of writing to stream:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
const handler = lambdaStreamHandler(
|
|
334
|
+
async (event, context) => {
|
|
335
|
+
throw new InternalError("Something went wrong");
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
throw: true, // Re-throw instead of SSE error
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Testing Streaming Handlers
|
|
344
|
+
|
|
345
|
+
### Mocking for Unit Tests
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { describe, expect, it, vi } from "vitest";
|
|
349
|
+
|
|
350
|
+
vi.mock("jaypie", async () => {
|
|
351
|
+
const testkit = await import("@jaypie/testkit/mock");
|
|
352
|
+
return testkit;
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
import { handler } from "./streamWorker.js";
|
|
356
|
+
|
|
357
|
+
describe("Stream Handler", () => {
|
|
358
|
+
it("writes expected events", async () => {
|
|
359
|
+
const writes: string[] = [];
|
|
360
|
+
const mockWriter = {
|
|
361
|
+
write: vi.fn((chunk) => writes.push(chunk)),
|
|
362
|
+
end: vi.fn(),
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const event = { prompt: "test" };
|
|
366
|
+
const context = { responseStream: mockWriter };
|
|
367
|
+
|
|
368
|
+
await handler(event, context);
|
|
369
|
+
|
|
370
|
+
expect(mockWriter.write).toHaveBeenCalled();
|
|
371
|
+
expect(writes.some(w => w.includes("event:"))).toBe(true);
|
|
372
|
+
expect(mockWriter.end).toHaveBeenCalled();
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Integration Testing
|
|
378
|
+
|
|
379
|
+
Use the Docker setup in `packages/express/docker/` for local Lambda streaming tests.
|
|
380
|
+
|
|
381
|
+
## TypeScript Types
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// From @jaypie/lambda
|
|
385
|
+
import type {
|
|
386
|
+
LambdaStreamHandlerOptions,
|
|
387
|
+
StreamHandlerContext,
|
|
388
|
+
ResponseStream,
|
|
389
|
+
AwsStreamingHandler,
|
|
390
|
+
} from "@jaypie/lambda";
|
|
391
|
+
|
|
392
|
+
// From @jaypie/express (or jaypie)
|
|
393
|
+
import type {
|
|
394
|
+
ExpressStreamHandlerOptions,
|
|
395
|
+
ExpressStreamHandlerLocals,
|
|
396
|
+
JaypieStreamHandlerSetup,
|
|
397
|
+
JaypieStreamHandlerTeardown,
|
|
398
|
+
JaypieStreamHandlerValidate,
|
|
399
|
+
} from "jaypie";
|
|
400
|
+
|
|
401
|
+
// From @jaypie/aws (or jaypie)
|
|
402
|
+
import type {
|
|
403
|
+
ExpressStreamResponse,
|
|
404
|
+
LambdaStreamWriter,
|
|
405
|
+
SSEEvent,
|
|
406
|
+
StreamChunk,
|
|
407
|
+
} from "jaypie";
|
|
408
|
+
```
|