@@ -8,6 +8,8 @@ import Replicate, {
8
8
} from "replicate" ;
9
9
import nock from "nock" ;
10
10
import fetch from "cross-fetch" ;
11
+ import { Stream } from "./lib/stream" ;
12
+ import { PassThrough } from "node:stream" ;
11
13
12
14
let client : Replicate ;
13
15
const BASE_URL = "https://api.replicate.com/v1" ;
@@ -251,7 +253,7 @@ describe("Replicate client", () => {
251
253
let actual : Record < string , any > | undefined ;
252
254
nock ( BASE_URL )
253
255
. post ( "/predictions" )
254
- . reply ( 201 , ( uri : string , body : Record < string , any > ) => {
256
+ . reply ( 201 , ( _uri : string , body : Record < string , any > ) => {
255
257
actual = body ;
256
258
return body ;
257
259
} ) ;
@@ -1010,8 +1012,6 @@ describe("Replicate client", () => {
1010
1012
} ) ;
1011
1013
1012
1014
test ( "Calls the correct API routes for a model" , async ( ) => {
1013
- const firstPollingRequest = true ;
1014
-
1015
1015
nock ( BASE_URL )
1016
1016
. post ( "/models/replicate/hello-world/predictions" )
1017
1017
. reply ( 201 , {
@@ -1179,12 +1179,294 @@ describe("Replicate client", () => {
1179
1179
// This is a test secret and should not be used in production
1180
1180
const secret = "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw" ;
1181
1181
1182
- const isValid = await validateWebhook ( request , secret ) ;
1182
+ const isValid = validateWebhook ( request , secret ) ;
1183
1183
expect ( isValid ) . toBe ( true ) ;
1184
1184
} ) ;
1185
1185
1186
1186
// Add more tests for error handling, edge cases, etc.
1187
1187
} ) ;
1188
1188
1189
1189
// Continue with tests for other methods
1190
+
1191
+ describe ( "Stream" , ( ) => {
1192
+ function createStream ( body : string | NodeJS . ReadableStream ) {
1193
+ const streamEndpoint = "https://stream.replicate.com" ;
1194
+ nock ( streamEndpoint )
1195
+ . get ( "/fake_stream" )
1196
+ . matchHeader ( "Accept" , "text/event-stream" )
1197
+ . reply ( 200 , body ) ;
1198
+
1199
+ return new Stream ( { url : `${ streamEndpoint } /fake_stream` , fetch } ) ;
1200
+ }
1201
+
1202
+ test ( "consumes a server sent event stream" , async ( ) => {
1203
+ const stream = createStream (
1204
+ `
1205
+ event: output
1206
+ id: EVENT_1
1207
+ data: hello world
1208
+
1209
+ event: done
1210
+ id: EVENT_2
1211
+ data: {}
1212
+ `
1213
+ . trim ( )
1214
+ . replace ( / ^ [ ] + / gm, "" )
1215
+ ) ;
1216
+
1217
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1218
+
1219
+ expect ( await iterator . next ( ) ) . toEqual ( {
1220
+ done : false ,
1221
+ value : { event : "output" , id : "EVENT_1" , data : "hello world" } ,
1222
+ } ) ;
1223
+ expect ( await iterator . next ( ) ) . toEqual ( {
1224
+ done : false ,
1225
+ value : { event : "done" , id : "EVENT_2" , data : "{}" } ,
1226
+ } ) ;
1227
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1228
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1229
+ } ) ;
1230
+
1231
+ test ( "consumes multiple events" , async ( ) => {
1232
+ const stream = createStream (
1233
+ `
1234
+ event: output
1235
+ id: EVENT_1
1236
+ data: hello world
1237
+
1238
+ event: output
1239
+ id: EVENT_2
1240
+ data: hello dave
1241
+
1242
+ event: done
1243
+ id: EVENT_3
1244
+ data: {}
1245
+ `
1246
+ . trim ( )
1247
+ . replace ( / ^ [ ] + / gm, "" )
1248
+ ) ;
1249
+
1250
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1251
+
1252
+ expect ( await iterator . next ( ) ) . toEqual ( {
1253
+ done : false ,
1254
+ value : { event : "output" , id : "EVENT_1" , data : "hello world" } ,
1255
+ } ) ;
1256
+ expect ( await iterator . next ( ) ) . toEqual ( {
1257
+ done : false ,
1258
+ value : { event : "output" , id : "EVENT_2" , data : "hello dave" } ,
1259
+ } ) ;
1260
+ expect ( await iterator . next ( ) ) . toEqual ( {
1261
+ done : false ,
1262
+ value : { event : "done" , id : "EVENT_3" , data : "{}" } ,
1263
+ } ) ;
1264
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1265
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1266
+ } ) ;
1267
+
1268
+ test ( "ignores unexpected characters" , async ( ) => {
1269
+ const stream = createStream (
1270
+ `
1271
+ : hi
1272
+
1273
+ event: output
1274
+ id: EVENT_1
1275
+ data: hello world
1276
+
1277
+ event: done
1278
+ id: EVENT_2
1279
+ data: {}
1280
+ `
1281
+ . trim ( )
1282
+ . replace ( / ^ [ ] + / gm, "" )
1283
+ ) ;
1284
+
1285
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1286
+
1287
+ expect ( await iterator . next ( ) ) . toEqual ( {
1288
+ done : false ,
1289
+ value : { event : "output" , id : "EVENT_1" , data : "hello world" } ,
1290
+ } ) ;
1291
+ expect ( await iterator . next ( ) ) . toEqual ( {
1292
+ done : false ,
1293
+ value : { event : "done" , id : "EVENT_2" , data : "{}" } ,
1294
+ } ) ;
1295
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1296
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1297
+ } ) ;
1298
+
1299
+ test ( "supports multiple lines of output in a single event" , async ( ) => {
1300
+ const stream = createStream (
1301
+ `
1302
+ : hi
1303
+
1304
+ event: output
1305
+ id: EVENT_1
1306
+ data: hello,
1307
+ data: this is a new line,
1308
+ data: and this is a new line too
1309
+
1310
+ event: done
1311
+ id: EVENT_2
1312
+ data: {}
1313
+ `
1314
+ . trim ( )
1315
+ . replace ( / ^ [ ] + / gm, "" )
1316
+ ) ;
1317
+
1318
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1319
+
1320
+ expect ( await iterator . next ( ) ) . toEqual ( {
1321
+ done : false ,
1322
+ value : {
1323
+ event : "output" ,
1324
+ id : "EVENT_1" ,
1325
+ data : "hello,\nthis is a new line,\nand this is a new line too" ,
1326
+ } ,
1327
+ } ) ;
1328
+ expect ( await iterator . next ( ) ) . toEqual ( {
1329
+ done : false ,
1330
+ value : { event : "done" , id : "EVENT_2" , data : "{}" } ,
1331
+ } ) ;
1332
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1333
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1334
+ } ) ;
1335
+
1336
+ test ( "supports the server writing data lines in multiple chunks" , async ( ) => {
1337
+ const body = new PassThrough ( ) ;
1338
+ const stream = createStream ( body ) ;
1339
+
1340
+ // Create a stream of data chunks split on the pipe character for readability.
1341
+ const data = `
1342
+ event: output
1343
+ id: EVENT_1
1344
+ data: hello,|
1345
+ data: this is a new line,|
1346
+ data: and this is a new line too
1347
+
1348
+ event: done
1349
+ id: EVENT_2
1350
+ data: {}
1351
+ `
1352
+ . trim ( )
1353
+ . replace ( / ^ [ ] + / gm, "" ) ;
1354
+
1355
+ const chunks = data . split ( "|" ) ;
1356
+
1357
+ // Consume the iterator in parallel to writing it.
1358
+ const reading = new Promise ( ( resolve , reject ) => {
1359
+ ( async ( ) => {
1360
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1361
+ expect ( await iterator . next ( ) ) . toEqual ( {
1362
+ done : false ,
1363
+ value : {
1364
+ event : "output" ,
1365
+ id : "EVENT_1" ,
1366
+ data : "hello,\nthis is a new line,\nand this is a new line too" ,
1367
+ } ,
1368
+ } ) ;
1369
+ expect ( await iterator . next ( ) ) . toEqual ( {
1370
+ done : false ,
1371
+ value : { event : "done" , id : "EVENT_2" , data : "{}" } ,
1372
+ } ) ;
1373
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1374
+ } ) ( ) . then ( resolve , reject ) ;
1375
+ } ) ;
1376
+
1377
+ // Write the chunks to the stream at an interval.
1378
+ const writing = new Promise ( ( resolve , reject ) => {
1379
+ ( async ( ) => {
1380
+ for await ( const chunk of chunks ) {
1381
+ body . write ( chunk ) ;
1382
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1 ) ) ;
1383
+ }
1384
+ body . end ( ) ;
1385
+ resolve ( null ) ;
1386
+ } ) ( ) . then ( resolve , reject ) ;
1387
+ } ) ;
1388
+
1389
+ // Wait for both promises to resolve.
1390
+ await Promise . all ( [ reading , writing ] ) ;
1391
+ } ) ;
1392
+
1393
+ test ( "supports the server writing data in a complete mess" , async ( ) => {
1394
+ const body = new PassThrough ( ) ;
1395
+ const stream = createStream ( body ) ;
1396
+
1397
+ // Create a stream of data chunks split on the pipe character for readability.
1398
+ const data = `
1399
+ : hi
1400
+
1401
+ ev|ent: output
1402
+ id: EVENT_1
1403
+ data: hello,
1404
+ data: this |is a new line,|
1405
+ data: and this is |a new line too
1406
+
1407
+ event: d|one
1408
+ id: EVENT|_2
1409
+ data: {}
1410
+ `
1411
+ . trim ( )
1412
+ . replace ( / ^ [ ] + / gm, "" ) ;
1413
+
1414
+ const chunks = data . split ( "|" ) ;
1415
+
1416
+ // Consume the iterator in parallel to writing it.
1417
+ const reading = new Promise ( ( resolve , reject ) => {
1418
+ ( async ( ) => {
1419
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1420
+ expect ( await iterator . next ( ) ) . toEqual ( {
1421
+ done : false ,
1422
+ value : {
1423
+ event : "output" ,
1424
+ id : "EVENT_1" ,
1425
+ data : "hello,\nthis is a new line,\nand this is a new line too" ,
1426
+ } ,
1427
+ } ) ;
1428
+ expect ( await iterator . next ( ) ) . toEqual ( {
1429
+ done : false ,
1430
+ value : { event : "done" , id : "EVENT_2" , data : "{}" } ,
1431
+ } ) ;
1432
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1433
+ } ) ( ) . then ( resolve , reject ) ;
1434
+ } ) ;
1435
+
1436
+ // Write the chunks to the stream at an interval.
1437
+ const writing = new Promise ( ( resolve , reject ) => {
1438
+ ( async ( ) => {
1439
+ for await ( const chunk of chunks ) {
1440
+ body . write ( chunk ) ;
1441
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1 ) ) ;
1442
+ }
1443
+ body . end ( ) ;
1444
+ resolve ( null ) ;
1445
+ } ) ( ) . then ( resolve , reject ) ;
1446
+ } ) ;
1447
+
1448
+ // Wait for both promises to resolve.
1449
+ await Promise . all ( [ reading , writing ] ) ;
1450
+ } ) ;
1451
+
1452
+ test ( "supports ending without a done" , async ( ) => {
1453
+ const stream = createStream (
1454
+ `
1455
+ event: output
1456
+ id: EVENT_1
1457
+ data: hello world
1458
+
1459
+ `
1460
+ . trim ( )
1461
+ . replace ( / ^ [ ] + / gm, "" )
1462
+ ) ;
1463
+
1464
+ const iterator = stream [ Symbol . asyncIterator ] ( ) ;
1465
+ expect ( await iterator . next ( ) ) . toEqual ( {
1466
+ done : false ,
1467
+ value : { event : "output" , id : "EVENT_1" , data : "hello world" } ,
1468
+ } ) ;
1469
+ expect ( await iterator . next ( ) ) . toEqual ( { done : true } ) ;
1470
+ } ) ;
1471
+ } ) ;
1190
1472
} ) ;
0 commit comments