assistant-stream 0.3.28 → 0.3.29
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlainText.d.ts","names":[],"sources":["../../../src/core/serialization/PlainText.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"PlainText.d.ts","names":[],"sources":["../../../src/core/serialization/PlainText.ts"],"mappings":";;;;cASa,yBACH,wBAAwB,sBAAsB,WAAW,yBACtD;EAEX,SAAO;;;cAsBI,yBAAyB,wBACpC,WAAW,cACX"}
|
|
@@ -1,29 +1,15 @@
|
|
|
1
1
|
import { AssistantTransformStream } from "../utils/stream/AssistantTransformStream.js";
|
|
2
2
|
import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream.js";
|
|
3
|
+
import { AssistantMetaTransformStream } from "../utils/stream/AssistantMetaTransformStream.js";
|
|
3
4
|
//#region src/core/serialization/PlainText.ts
|
|
4
5
|
var PlainTextEncoder = class extends PipeableTransformStream {
|
|
5
|
-
headers = new Headers({
|
|
6
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
7
|
-
"x-vercel-ai-data-stream": "v1"
|
|
8
|
-
});
|
|
6
|
+
headers = new Headers({ "Content-Type": "text/plain; charset=utf-8" });
|
|
9
7
|
constructor() {
|
|
10
8
|
super((readable) => {
|
|
11
9
|
const transform = new TransformStream({ transform(chunk, controller) {
|
|
12
|
-
|
|
13
|
-
switch (type) {
|
|
14
|
-
case "text-delta":
|
|
15
|
-
controller.enqueue(chunk.textDelta);
|
|
16
|
-
break;
|
|
17
|
-
case "part-start":
|
|
18
|
-
case "part-finish":
|
|
19
|
-
case "step-start":
|
|
20
|
-
case "step-finish":
|
|
21
|
-
case "message-finish":
|
|
22
|
-
case "error": break;
|
|
23
|
-
default: throw new Error(`unsupported chunk type: ${type}`);
|
|
24
|
-
}
|
|
10
|
+
if (chunk.type === "text-delta" && chunk.meta.type === "text") controller.enqueue(chunk.textDelta);
|
|
25
11
|
} });
|
|
26
|
-
return readable.pipeThrough(transform).pipeThrough(new TextEncoderStream());
|
|
12
|
+
return readable.pipeThrough(new AssistantMetaTransformStream()).pipeThrough(transform).pipeThrough(new TextEncoderStream());
|
|
27
13
|
});
|
|
28
14
|
}
|
|
29
15
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlainText.js","names":[
|
|
1
|
+
{"version":3,"file":"PlainText.js","names":[],"sources":["../../../src/core/serialization/PlainText.ts"],"sourcesContent":["import type { AssistantStreamEncoder } from \"../AssistantStream\";\nimport type { AssistantStreamChunk } from \"../AssistantStreamChunk\";\nimport {\n AssistantMetaTransformStream,\n type AssistantMetaStreamChunk,\n} from \"../utils/stream/AssistantMetaTransformStream\";\nimport { AssistantTransformStream } from \"../utils/stream/AssistantTransformStream\";\nimport { PipeableTransformStream } from \"../utils/stream/PipeableTransformStream\";\n\nexport class PlainTextEncoder\n extends PipeableTransformStream<AssistantStreamChunk, Uint8Array<ArrayBuffer>>\n implements AssistantStreamEncoder\n{\n headers = new Headers({\n \"Content-Type\": \"text/plain; charset=utf-8\",\n });\n\n constructor() {\n super((readable) => {\n const transform = new TransformStream<AssistantMetaStreamChunk, string>({\n transform(chunk, controller) {\n if (chunk.type === \"text-delta\" && chunk.meta.type === \"text\") {\n controller.enqueue(chunk.textDelta);\n }\n },\n });\n\n return readable\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeThrough(transform)\n .pipeThrough(new TextEncoderStream());\n });\n }\n}\n\nexport class PlainTextDecoder extends PipeableTransformStream<\n Uint8Array<ArrayBuffer>,\n AssistantStreamChunk\n> {\n constructor() {\n super((readable) => {\n const transform = new AssistantTransformStream<string>({\n transform(chunk, controller) {\n controller.appendText(chunk);\n },\n });\n\n return readable\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(transform);\n });\n }\n}\n"],"mappings":";;;;AASA,IAAa,mBAAb,cACU,wBAEV;CACE,UAAU,IAAI,QAAQ,EACpB,gBAAgB,4BAClB,CAAC;CAED,cAAc;EACZ,OAAO,aAAa;GAClB,MAAM,YAAY,IAAI,gBAAkD,EACtE,UAAU,OAAO,YAAY;IAC3B,IAAI,MAAM,SAAS,gBAAgB,MAAM,KAAK,SAAS,QACrD,WAAW,QAAQ,MAAM,SAAS;GAEtC,EACF,CAAC;GAED,OAAO,SACJ,YAAY,IAAI,6BAA6B,CAAC,CAAC,CAC/C,YAAY,SAAS,CAAC,CACtB,YAAY,IAAI,kBAAkB,CAAC;EACxC,CAAC;CACH;AACF;AAEA,IAAa,mBAAb,cAAsC,wBAGpC;CACA,cAAc;EACZ,OAAO,aAAa;GAClB,MAAM,YAAY,IAAI,yBAAiC,EACrD,UAAU,OAAO,YAAY;IAC3B,WAAW,WAAW,KAAK;GAC7B,EACF,CAAC;GAED,OAAO,SACJ,YAAY,IAAI,kBAAkB,CAAC,CAAC,CACpC,YAAY,SAAS;EAC1B,CAAC;CACH;AACF"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { AssistantStream } from "../AssistantStream";
|
|
3
|
+
import type { AssistantStreamChunk } from "../AssistantStreamChunk";
|
|
4
|
+
import { createAssistantStream } from "../modules/assistant-stream";
|
|
5
|
+
import { PlainTextEncoder } from "./PlainText";
|
|
6
|
+
|
|
7
|
+
describe("PlainTextEncoder", () => {
|
|
8
|
+
it("emits only the assistant text for a reasoning + tool-call + text stream", async () => {
|
|
9
|
+
const stream = createAssistantStream(async (controller) => {
|
|
10
|
+
controller.appendReasoning("internal chain of thought. ");
|
|
11
|
+
const tool = controller.addToolCallPart({
|
|
12
|
+
toolCallId: "call_1",
|
|
13
|
+
toolName: "search",
|
|
14
|
+
});
|
|
15
|
+
tool.argsText.append('{"query":"secret"}');
|
|
16
|
+
tool.setResponse({ result: "ok" });
|
|
17
|
+
tool.close();
|
|
18
|
+
controller.appendText("Hello!");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const response = AssistantStream.toResponse(stream, new PlainTextEncoder());
|
|
22
|
+
await expect(response.text()).resolves.toBe("Hello!");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("skips annotations, data, and update-state chunks without throwing", async () => {
|
|
26
|
+
const chunks: AssistantStreamChunk[] = [
|
|
27
|
+
{ type: "part-start", path: [], part: { type: "text" } },
|
|
28
|
+
{ type: "text-delta", path: [0], textDelta: "Hi" },
|
|
29
|
+
{ type: "annotations", path: [], annotations: [{ note: "cited" }] },
|
|
30
|
+
{ type: "data", path: [], data: [{ progress: 1 }] },
|
|
31
|
+
{
|
|
32
|
+
type: "update-state",
|
|
33
|
+
path: [],
|
|
34
|
+
operations: [{ type: "set", path: ["k"], value: "v" }],
|
|
35
|
+
},
|
|
36
|
+
{ type: "part-finish", path: [0] },
|
|
37
|
+
];
|
|
38
|
+
const stream = new ReadableStream<AssistantStreamChunk>({
|
|
39
|
+
start(controller) {
|
|
40
|
+
for (const chunk of chunks) controller.enqueue(chunk);
|
|
41
|
+
controller.close();
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const response = AssistantStream.toResponse(stream, new PlainTextEncoder());
|
|
46
|
+
await expect(response.text()).resolves.toBe("Hi");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("sends plain text headers without the data-stream protocol marker", () => {
|
|
50
|
+
const headers = new PlainTextEncoder().headers;
|
|
51
|
+
expect(headers.get("Content-Type")).toBe("text/plain; charset=utf-8");
|
|
52
|
+
expect(headers.has("x-vercel-ai-data-stream")).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { AssistantStreamEncoder } from "../AssistantStream";
|
|
2
2
|
import type { AssistantStreamChunk } from "../AssistantStreamChunk";
|
|
3
|
+
import {
|
|
4
|
+
AssistantMetaTransformStream,
|
|
5
|
+
type AssistantMetaStreamChunk,
|
|
6
|
+
} from "../utils/stream/AssistantMetaTransformStream";
|
|
3
7
|
import { AssistantTransformStream } from "../utils/stream/AssistantTransformStream";
|
|
4
8
|
import { PipeableTransformStream } from "../utils/stream/PipeableTransformStream";
|
|
5
9
|
|
|
@@ -9,43 +13,20 @@ export class PlainTextEncoder
|
|
|
9
13
|
{
|
|
10
14
|
headers = new Headers({
|
|
11
15
|
"Content-Type": "text/plain; charset=utf-8",
|
|
12
|
-
"x-vercel-ai-data-stream": "v1",
|
|
13
16
|
});
|
|
14
17
|
|
|
15
18
|
constructor() {
|
|
16
19
|
super((readable) => {
|
|
17
|
-
const transform = new TransformStream<
|
|
20
|
+
const transform = new TransformStream<AssistantMetaStreamChunk, string>({
|
|
18
21
|
transform(chunk, controller) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
case "text-delta":
|
|
22
|
-
controller.enqueue(chunk.textDelta);
|
|
23
|
-
break;
|
|
24
|
-
|
|
25
|
-
case "part-start":
|
|
26
|
-
case "part-finish":
|
|
27
|
-
case "step-start":
|
|
28
|
-
case "step-finish":
|
|
29
|
-
case "message-finish":
|
|
30
|
-
case "error":
|
|
31
|
-
break;
|
|
32
|
-
|
|
33
|
-
default: {
|
|
34
|
-
const unsupportedType:
|
|
35
|
-
| "tool-call-args-text-finish"
|
|
36
|
-
| "data"
|
|
37
|
-
| "annotations"
|
|
38
|
-
| "tool-call-begin"
|
|
39
|
-
| "tool-call-delta"
|
|
40
|
-
| "result"
|
|
41
|
-
| "update-state" = type;
|
|
42
|
-
throw new Error(`unsupported chunk type: ${unsupportedType}`);
|
|
43
|
-
}
|
|
22
|
+
if (chunk.type === "text-delta" && chunk.meta.type === "text") {
|
|
23
|
+
controller.enqueue(chunk.textDelta);
|
|
44
24
|
}
|
|
45
25
|
},
|
|
46
26
|
});
|
|
47
27
|
|
|
48
28
|
return readable
|
|
29
|
+
.pipeThrough(new AssistantMetaTransformStream())
|
|
49
30
|
.pipeThrough(transform)
|
|
50
31
|
.pipeThrough(new TextEncoderStream());
|
|
51
32
|
});
|