@aisa-one/cli 0.1.8 → 0.2.1
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/CHANGELOG.md +62 -0
- package/README.md +8 -8
- package/dist/agent/env-setup.d.ts +5 -0
- package/dist/agent/env-setup.js +49 -0
- package/dist/agent/index-client.d.ts +7 -0
- package/dist/agent/index-client.js +22 -0
- package/dist/agent/installer.d.ts +19 -0
- package/dist/agent/installer.js +70 -0
- package/dist/agent/setup-python.d.ts +15 -0
- package/dist/agent/setup-python.js +51 -0
- package/dist/api.d.ts +0 -1
- package/dist/api.js +0 -1
- package/dist/commands/agent.d.ts +37 -0
- package/dist/commands/agent.js +129 -0
- package/dist/commands/chat.js +1 -1
- package/dist/commands/configCmd.js +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/skills.js +16 -16
- package/dist/commands/twitter.d.ts +127 -3
- package/dist/commands/twitter.js +296 -36
- package/dist/config.js +3 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/index.js +205 -6
- package/dist/types.d.ts +0 -16
- package/dist/utils/file.d.ts +0 -4
- package/dist/utils/file.js +1 -26
- package/dist/utils/streaming.d.ts +0 -1
- package/dist/utils/streaming.js +2 -1
- package/package.json +1 -2
- package/tests/streaming.test.ts +73 -0
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { webSearchAction, scholarAction } from "./commands/search.js";
|
|
|
18
18
|
// Finance
|
|
19
19
|
import { stockAction, cryptoAction, screenerAction } from "./commands/finance.js";
|
|
20
20
|
// Twitter
|
|
21
|
-
import { tweetAction, twitterSearchAction, twitterUserAction, twitterTrendsAction } from "./commands/twitter.js";
|
|
21
|
+
import { tweetAction, twitterSearchAction, twitterUserAction, twitterTrendsAction, twitterUserAboutAction, twitterBatchUsersAction, twitterUserTweetsAction, twitterMentionsAction, twitterFollowersAction, twitterFollowingAction, twitterVerifiedFollowersAction, twitterCheckFollowAction, twitterUserSearchAction, twitterDetailAction, twitterRepliesAction, twitterQuotesAction, twitterRetweetersAction, twitterThreadAction, twitterArticleAction, twitterListMembersAction, twitterListFollowersAction, twitterCommunityInfoAction, twitterCommunityMembersAction, twitterCommunityModsAction, twitterCommunityTweetsAction, twitterCommunitySearchAction, twitterSpaceAction, twitterLoginAction, twitterLogoutAction, twitterLikeAction, twitterUnlikeAction, twitterRetweetAction, twitterDeleteAction, twitterFollowAction, twitterUnfollowAction, twitterUploadMediaAction, twitterDmAction, } from "./commands/twitter.js";
|
|
22
22
|
// Video
|
|
23
23
|
import { videoCreateAction, videoStatusAction } from "./commands/video.js";
|
|
24
24
|
// Skills
|
|
@@ -107,7 +107,7 @@ program
|
|
|
107
107
|
program
|
|
108
108
|
.command("chat [message]")
|
|
109
109
|
.description("Chat with AI models via the AISA gateway")
|
|
110
|
-
.option("--model <model>", "Model ID (default: gpt-4.1)")
|
|
110
|
+
.option("--model <model>", "Model ID (default: gpt-4.1-mini)")
|
|
111
111
|
.option("--system <prompt>", "System prompt")
|
|
112
112
|
.option("--no-stream", "Disable streaming")
|
|
113
113
|
.option("--json", "Output raw JSON response")
|
|
@@ -162,27 +162,226 @@ program
|
|
|
162
162
|
// ── Twitter shortcuts ──
|
|
163
163
|
program
|
|
164
164
|
.command("tweet <text>")
|
|
165
|
-
.description("Post a tweet")
|
|
165
|
+
.description("Post a tweet (requires twitter login)")
|
|
166
166
|
.option("--reply-to <id>", "Reply to tweet ID")
|
|
167
|
+
.option("--media-ids <ids>", "Comma-separated media IDs")
|
|
167
168
|
.option("--raw", "Raw JSON output")
|
|
168
169
|
.action(wrap(tweetAction));
|
|
169
170
|
const twitter = program.command("twitter").description("Twitter/X operations");
|
|
171
|
+
// Auth
|
|
172
|
+
twitter
|
|
173
|
+
.command("login")
|
|
174
|
+
.description("Login to Twitter or import cookies")
|
|
175
|
+
.option("--username <name>", "Twitter username")
|
|
176
|
+
.option("--email <email>", "Account email")
|
|
177
|
+
.option("--password <pass>", "Account password")
|
|
178
|
+
.option("--proxy <url>", "Proxy URL (required)")
|
|
179
|
+
.option("--totp <secret>", "2FA TOTP secret")
|
|
180
|
+
.option("--cookies <cookies>", "Import login_cookies directly")
|
|
181
|
+
.option("--raw", "Raw JSON output")
|
|
182
|
+
.action(wrap(twitterLoginAction));
|
|
183
|
+
twitter
|
|
184
|
+
.command("logout")
|
|
185
|
+
.description("Clear stored Twitter cookies")
|
|
186
|
+
.action(twitterLogoutAction);
|
|
187
|
+
// User read
|
|
188
|
+
twitter
|
|
189
|
+
.command("user <username>")
|
|
190
|
+
.description("Get user profile")
|
|
191
|
+
.option("--raw", "Raw JSON output")
|
|
192
|
+
.action(wrap(twitterUserAction));
|
|
193
|
+
twitter
|
|
194
|
+
.command("user-about <username>")
|
|
195
|
+
.description("Get user profile details (country, verification, name history)")
|
|
196
|
+
.option("--raw", "Raw JSON output")
|
|
197
|
+
.action(wrap(twitterUserAboutAction));
|
|
198
|
+
twitter
|
|
199
|
+
.command("batch-users <ids>")
|
|
200
|
+
.description("Get multiple users by comma-separated IDs")
|
|
201
|
+
.option("--raw", "Raw JSON output")
|
|
202
|
+
.action(wrap(twitterBatchUsersAction));
|
|
203
|
+
twitter
|
|
204
|
+
.command("user-tweets <username>")
|
|
205
|
+
.description("Get user's recent tweets")
|
|
206
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
207
|
+
.option("--raw", "Raw JSON output")
|
|
208
|
+
.action(wrap(twitterUserTweetsAction));
|
|
209
|
+
twitter
|
|
210
|
+
.command("mentions <username>")
|
|
211
|
+
.description("Get user mentions")
|
|
212
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
213
|
+
.option("--raw", "Raw JSON output")
|
|
214
|
+
.action(wrap(twitterMentionsAction));
|
|
215
|
+
twitter
|
|
216
|
+
.command("followers <username>")
|
|
217
|
+
.description("Get user followers")
|
|
218
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
219
|
+
.option("--raw", "Raw JSON output")
|
|
220
|
+
.action(wrap(twitterFollowersAction));
|
|
221
|
+
twitter
|
|
222
|
+
.command("following <username>")
|
|
223
|
+
.description("Get user followings")
|
|
224
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
225
|
+
.option("--raw", "Raw JSON output")
|
|
226
|
+
.action(wrap(twitterFollowingAction));
|
|
227
|
+
twitter
|
|
228
|
+
.command("verified-followers <user-id>")
|
|
229
|
+
.description("Get verified followers (requires user ID)")
|
|
230
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
231
|
+
.option("--raw", "Raw JSON output")
|
|
232
|
+
.action(wrap(twitterVerifiedFollowersAction));
|
|
233
|
+
twitter
|
|
234
|
+
.command("check-follow <source> <target>")
|
|
235
|
+
.description("Check follow relationship between two usernames")
|
|
236
|
+
.option("--raw", "Raw JSON output")
|
|
237
|
+
.action(wrap(twitterCheckFollowAction));
|
|
238
|
+
twitter
|
|
239
|
+
.command("user-search <query>")
|
|
240
|
+
.description("Search users by keyword")
|
|
241
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
242
|
+
.option("--raw", "Raw JSON output")
|
|
243
|
+
.action(wrap(twitterUserSearchAction));
|
|
244
|
+
// Tweet read
|
|
170
245
|
twitter
|
|
171
246
|
.command("search <query>")
|
|
172
247
|
.description("Search tweets")
|
|
248
|
+
.option("--type <type>", "Query type: latest or top", "latest")
|
|
173
249
|
.option("--limit <n>", "Max results")
|
|
250
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
174
251
|
.option("--raw", "Raw JSON output")
|
|
175
252
|
.action(wrap(twitterSearchAction));
|
|
176
253
|
twitter
|
|
177
|
-
.command("
|
|
178
|
-
.description("Get
|
|
254
|
+
.command("detail <ids>")
|
|
255
|
+
.description("Get tweets by comma-separated IDs")
|
|
179
256
|
.option("--raw", "Raw JSON output")
|
|
180
|
-
.action(wrap(
|
|
257
|
+
.action(wrap(twitterDetailAction));
|
|
258
|
+
twitter
|
|
259
|
+
.command("replies <tweet-id>")
|
|
260
|
+
.description("Get tweet replies")
|
|
261
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
262
|
+
.option("--raw", "Raw JSON output")
|
|
263
|
+
.action(wrap(twitterRepliesAction));
|
|
264
|
+
twitter
|
|
265
|
+
.command("quotes <tweet-id>")
|
|
266
|
+
.description("Get tweet quotes")
|
|
267
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
268
|
+
.option("--raw", "Raw JSON output")
|
|
269
|
+
.action(wrap(twitterQuotesAction));
|
|
270
|
+
twitter
|
|
271
|
+
.command("retweeters <tweet-id>")
|
|
272
|
+
.description("Get tweet retweeters")
|
|
273
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
274
|
+
.option("--raw", "Raw JSON output")
|
|
275
|
+
.action(wrap(twitterRetweetersAction));
|
|
276
|
+
twitter
|
|
277
|
+
.command("thread <tweet-id>")
|
|
278
|
+
.description("Get full conversation thread")
|
|
279
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
280
|
+
.option("--raw", "Raw JSON output")
|
|
281
|
+
.action(wrap(twitterThreadAction));
|
|
282
|
+
twitter
|
|
283
|
+
.command("article <tweet-id>")
|
|
284
|
+
.description("Get article content by tweet ID")
|
|
285
|
+
.option("--raw", "Raw JSON output")
|
|
286
|
+
.action(wrap(twitterArticleAction));
|
|
287
|
+
// Trends
|
|
181
288
|
twitter
|
|
182
289
|
.command("trends")
|
|
183
290
|
.description("Get trending topics")
|
|
291
|
+
.option("--woeid <id>", "Location WOEID (1 = worldwide)", "1")
|
|
292
|
+
.option("--count <n>", "Number of trends")
|
|
184
293
|
.option("--raw", "Raw JSON output")
|
|
185
294
|
.action(wrap(twitterTrendsAction));
|
|
295
|
+
// Lists
|
|
296
|
+
twitter
|
|
297
|
+
.command("list-members <list-id>")
|
|
298
|
+
.description("Get list members")
|
|
299
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
300
|
+
.option("--raw", "Raw JSON output")
|
|
301
|
+
.action(wrap(twitterListMembersAction));
|
|
302
|
+
twitter
|
|
303
|
+
.command("list-followers <list-id>")
|
|
304
|
+
.description("Get list followers")
|
|
305
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
306
|
+
.option("--raw", "Raw JSON output")
|
|
307
|
+
.action(wrap(twitterListFollowersAction));
|
|
308
|
+
// Communities
|
|
309
|
+
twitter
|
|
310
|
+
.command("community-info <community-id>")
|
|
311
|
+
.description("Get community info")
|
|
312
|
+
.option("--raw", "Raw JSON output")
|
|
313
|
+
.action(wrap(twitterCommunityInfoAction));
|
|
314
|
+
twitter
|
|
315
|
+
.command("community-members <community-id>")
|
|
316
|
+
.description("Get community members")
|
|
317
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
318
|
+
.option("--raw", "Raw JSON output")
|
|
319
|
+
.action(wrap(twitterCommunityMembersAction));
|
|
320
|
+
twitter
|
|
321
|
+
.command("community-mods <community-id>")
|
|
322
|
+
.description("Get community moderators")
|
|
323
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
324
|
+
.option("--raw", "Raw JSON output")
|
|
325
|
+
.action(wrap(twitterCommunityModsAction));
|
|
326
|
+
twitter
|
|
327
|
+
.command("community-tweets <community-id>")
|
|
328
|
+
.description("Get community tweets")
|
|
329
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
330
|
+
.option("--raw", "Raw JSON output")
|
|
331
|
+
.action(wrap(twitterCommunityTweetsAction));
|
|
332
|
+
twitter
|
|
333
|
+
.command("community-search <query>")
|
|
334
|
+
.description("Search tweets across all communities")
|
|
335
|
+
.option("--cursor <cursor>", "Pagination cursor")
|
|
336
|
+
.option("--raw", "Raw JSON output")
|
|
337
|
+
.action(wrap(twitterCommunitySearchAction));
|
|
338
|
+
// Spaces
|
|
339
|
+
twitter
|
|
340
|
+
.command("space <space-id>")
|
|
341
|
+
.description("Get Space details")
|
|
342
|
+
.option("--raw", "Raw JSON output")
|
|
343
|
+
.action(wrap(twitterSpaceAction));
|
|
344
|
+
// Write operations
|
|
345
|
+
twitter
|
|
346
|
+
.command("like <tweet-id>")
|
|
347
|
+
.description("Like a tweet")
|
|
348
|
+
.option("--raw", "Raw JSON output")
|
|
349
|
+
.action(wrap(twitterLikeAction));
|
|
350
|
+
twitter
|
|
351
|
+
.command("unlike <tweet-id>")
|
|
352
|
+
.description("Unlike a tweet")
|
|
353
|
+
.option("--raw", "Raw JSON output")
|
|
354
|
+
.action(wrap(twitterUnlikeAction));
|
|
355
|
+
twitter
|
|
356
|
+
.command("retweet <tweet-id>")
|
|
357
|
+
.description("Retweet a tweet")
|
|
358
|
+
.option("--raw", "Raw JSON output")
|
|
359
|
+
.action(wrap(twitterRetweetAction));
|
|
360
|
+
twitter
|
|
361
|
+
.command("delete <tweet-id>")
|
|
362
|
+
.description("Delete a tweet")
|
|
363
|
+
.option("--raw", "Raw JSON output")
|
|
364
|
+
.action(wrap(twitterDeleteAction));
|
|
365
|
+
twitter
|
|
366
|
+
.command("follow <user-id>")
|
|
367
|
+
.description("Follow a user (requires user ID)")
|
|
368
|
+
.option("--raw", "Raw JSON output")
|
|
369
|
+
.action(wrap(twitterFollowAction));
|
|
370
|
+
twitter
|
|
371
|
+
.command("unfollow <user-id>")
|
|
372
|
+
.description("Unfollow a user (requires user ID)")
|
|
373
|
+
.option("--raw", "Raw JSON output")
|
|
374
|
+
.action(wrap(twitterUnfollowAction));
|
|
375
|
+
twitter
|
|
376
|
+
.command("upload-media <file-path>")
|
|
377
|
+
.description("Upload media file for tweets")
|
|
378
|
+
.option("--raw", "Raw JSON output")
|
|
379
|
+
.action(wrap(twitterUploadMediaAction));
|
|
380
|
+
twitter
|
|
381
|
+
.command("dm <user-id> <text>")
|
|
382
|
+
.description("Send a direct message")
|
|
383
|
+
.option("--raw", "Raw JSON output")
|
|
384
|
+
.action(wrap(twitterDmAction));
|
|
186
385
|
// ── Video shortcuts ──
|
|
187
386
|
const video = program.command("video").description("AI video generation");
|
|
188
387
|
video
|
package/dist/types.d.ts
CHANGED
|
@@ -25,22 +25,6 @@ export interface Model {
|
|
|
25
25
|
owned_by: string;
|
|
26
26
|
supported_endpoint_types?: string[] | null;
|
|
27
27
|
}
|
|
28
|
-
export interface SkillMeta {
|
|
29
|
-
name: string;
|
|
30
|
-
description: string;
|
|
31
|
-
owner: string;
|
|
32
|
-
slug: string;
|
|
33
|
-
tags: string[];
|
|
34
|
-
category: string;
|
|
35
|
-
verified: boolean;
|
|
36
|
-
installs: number;
|
|
37
|
-
}
|
|
38
|
-
export interface SkillDetail extends SkillMeta {
|
|
39
|
-
files: Array<{
|
|
40
|
-
path: string;
|
|
41
|
-
content: string;
|
|
42
|
-
}>;
|
|
43
|
-
}
|
|
44
28
|
export interface BalanceResponse {
|
|
45
29
|
balance: number;
|
|
46
30
|
currency: string;
|
package/dist/utils/file.d.ts
CHANGED
|
@@ -4,9 +4,5 @@ export declare function writeSkillFiles(targetDir: string, files: Array<{
|
|
|
4
4
|
path: string;
|
|
5
5
|
content: string;
|
|
6
6
|
}>): void;
|
|
7
|
-
export declare function readSkillDir(dirPath: string): Array<{
|
|
8
|
-
path: string;
|
|
9
|
-
content: string;
|
|
10
|
-
}>;
|
|
11
7
|
export declare function removeDir(dirPath: string): void;
|
|
12
8
|
export declare function detectAgents(agentDirs: Record<string, string>): string[];
|
package/dist/utils/file.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync,
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
2
2
|
import { join, resolve, relative, sep } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
export function expandHome(p) {
|
|
@@ -20,31 +20,6 @@ export function writeSkillFiles(targetDir, files) {
|
|
|
20
20
|
writeFileSync(fullPath, file.content, "utf-8");
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
export function readSkillDir(dirPath) {
|
|
24
|
-
const files = [];
|
|
25
|
-
function walk(dir) {
|
|
26
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
27
|
-
const fullPath = join(dir, entry.name);
|
|
28
|
-
if (entry.isDirectory()) {
|
|
29
|
-
if (entry.name === "node_modules" || entry.name === ".git")
|
|
30
|
-
continue;
|
|
31
|
-
walk(fullPath);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
const relPath = relative(dirPath, fullPath);
|
|
35
|
-
const stat = statSync(fullPath);
|
|
36
|
-
if (stat.size > 1024 * 1024)
|
|
37
|
-
continue; // skip files > 1MB
|
|
38
|
-
files.push({
|
|
39
|
-
path: relPath,
|
|
40
|
-
content: readFileSync(fullPath, "utf-8"),
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
walk(dirPath);
|
|
46
|
-
return files;
|
|
47
|
-
}
|
|
48
23
|
export function removeDir(dirPath) {
|
|
49
24
|
if (existsSync(dirPath)) {
|
|
50
25
|
rmSync(dirPath, { recursive: true, force: true });
|
package/dist/utils/streaming.js
CHANGED
|
@@ -2,10 +2,11 @@ export async function handleSSEStream(res, onToken, onDone) {
|
|
|
2
2
|
const body = res.body;
|
|
3
3
|
if (!body)
|
|
4
4
|
throw new Error("No response body");
|
|
5
|
+
const decoder = new TextDecoder();
|
|
5
6
|
let buffer = "";
|
|
6
7
|
let usage;
|
|
7
8
|
for await (const chunk of body) {
|
|
8
|
-
buffer +=
|
|
9
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
9
10
|
const lines = buffer.split("\n");
|
|
10
11
|
buffer = lines.pop() || "";
|
|
11
12
|
for (const line of lines) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aisa-one/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CLI for the AISA unified AI infrastructure platform - access 70+ LLMs, web search, finance, Twitter, and video generation APIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"chalk": "^5.3.0",
|
|
27
27
|
"commander": "^12.0.0",
|
|
28
28
|
"conf": "^12.0.0",
|
|
29
|
-
"node-fetch": "^3.3.2",
|
|
30
29
|
"ora": "^8.0.1"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { handleSSEStream } from "../src/utils/streaming.js";
|
|
3
|
+
|
|
4
|
+
function sseResponse(chunks: string[]): Response {
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
7
|
+
start(controller) {
|
|
8
|
+
for (const chunk of chunks) {
|
|
9
|
+
controller.enqueue(encoder.encode(chunk));
|
|
10
|
+
}
|
|
11
|
+
controller.close();
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
return new Response(stream);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("handleSSEStream", () => {
|
|
18
|
+
it("emits tokens from a native fetch Response body", async () => {
|
|
19
|
+
const res = sseResponse([
|
|
20
|
+
'data: {"choices":[{"delta":{"content":"Hello"}}]}\n\n',
|
|
21
|
+
'data: {"choices":[{"delta":{"content":" world"}}]}\n\n',
|
|
22
|
+
"data: [DONE]\n\n",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const tokens: string[] = [];
|
|
26
|
+
let done = false;
|
|
27
|
+
await handleSSEStream(
|
|
28
|
+
res,
|
|
29
|
+
(t) => tokens.push(t),
|
|
30
|
+
() => {
|
|
31
|
+
done = true;
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
expect(tokens.join("")).toBe("Hello world");
|
|
36
|
+
expect(done).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("handles multi-byte characters split across chunks", async () => {
|
|
40
|
+
const encoder = new TextEncoder();
|
|
41
|
+
const line = encoder.encode(
|
|
42
|
+
'data: {"choices":[{"delta":{"content":"你好"}}]}\n\ndata: [DONE]\n\n'
|
|
43
|
+
);
|
|
44
|
+
// split in the middle of a multi-byte character
|
|
45
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
46
|
+
start(controller) {
|
|
47
|
+
controller.enqueue(line.slice(0, 40));
|
|
48
|
+
controller.enqueue(line.slice(40));
|
|
49
|
+
controller.close();
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const res = new Response(stream);
|
|
53
|
+
|
|
54
|
+
const tokens: string[] = [];
|
|
55
|
+
await handleSSEStream(res, (t) => tokens.push(t));
|
|
56
|
+
|
|
57
|
+
expect(tokens.join("")).toBe("你好");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("reports usage to onDone", async () => {
|
|
61
|
+
const res = sseResponse([
|
|
62
|
+
'data: {"choices":[{"delta":{"content":"hi"}}],"usage":{"prompt_tokens":3,"completion_tokens":1}}\n\n',
|
|
63
|
+
"data: [DONE]\n\n",
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
let usage: { prompt_tokens: number; completion_tokens: number } | undefined;
|
|
67
|
+
await handleSSEStream(res, () => {}, (u) => {
|
|
68
|
+
usage = u;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(usage).toEqual({ prompt_tokens: 3, completion_tokens: 1 });
|
|
72
|
+
});
|
|
73
|
+
});
|