@aigne/core 1.14.0 → 1.15.0
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 +12 -0
- package/lib/cjs/agents/ai-agent.js +2 -3
- package/lib/cjs/agents/team-agent.js +1 -1
- package/lib/cjs/aigne/context.js +1 -1
- package/lib/cjs/prompt/template.js +1 -1
- package/lib/cjs/server/server.js +2 -3
- package/lib/cjs/utils/stream-utils.d.ts +0 -1
- package/lib/cjs/utils/stream-utils.js +19 -25
- package/lib/dts/utils/stream-utils.d.ts +0 -1
- package/lib/esm/agents/ai-agent.js +2 -3
- package/lib/esm/agents/team-agent.js +2 -2
- package/lib/esm/aigne/context.js +2 -2
- package/lib/esm/prompt/template.js +1 -1
- package/lib/esm/server/server.js +2 -3
- package/lib/esm/utils/stream-utils.d.ts +0 -1
- package/lib/esm/utils/stream-utils.js +19 -24
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
* rename @aigne/core-next to @aigne/core ([3a81009](https://github.com/AIGNE-io/aigne-framework/commit/3a8100962c81813217b687ae28e8de604419c622))
|
|
23
23
|
* use text resource from MCP correctly ([8b9eba8](https://github.com/AIGNE-io/aigne-framework/commit/8b9eba83352ec096a2a5d4f410d4c4bde7420bce))
|
|
24
24
|
|
|
25
|
+
## [1.15.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.14.0...core-v1.15.0) (2025-05-15)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* optimize the stability of the model and ci ([#119](https://github.com/AIGNE-io/aigne-framework/issues/119)) ([de93887](https://github.com/AIGNE-io/aigne-framework/commit/de938879452a8be82a198dda0eda1eb9fcbb0474))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Bug Fixes
|
|
34
|
+
|
|
35
|
+
* **core:** response.headers.toJSON is not a function ([#121](https://github.com/AIGNE-io/aigne-framework/issues/121)) ([4609ba6](https://github.com/AIGNE-io/aigne-framework/commit/4609ba645e6b8fe8d76ecd475cd2d7817483a4bd))
|
|
36
|
+
|
|
25
37
|
## [1.14.0](https://github.com/AIGNE-io/aigne-framework/compare/core-v1.13.0...core-v1.14.0) (2025-05-12)
|
|
26
38
|
|
|
27
39
|
|
|
@@ -7,7 +7,6 @@ const memory_js_1 = require("../memory/memory.js");
|
|
|
7
7
|
const chat_model_js_1 = require("../models/chat-model.js");
|
|
8
8
|
const prompt_builder_js_1 = require("../prompt/prompt-builder.js");
|
|
9
9
|
const template_js_1 = require("../prompt/template.js");
|
|
10
|
-
const stream_utils_js_1 = require("../utils/stream-utils.js");
|
|
11
10
|
const type_utils_js_1 = require("../utils/type-utils.js");
|
|
12
11
|
const agent_js_1 = require("./agent.js");
|
|
13
12
|
const types_js_1 = require("./types.js");
|
|
@@ -209,7 +208,7 @@ class AIAgent extends agent_js_1.Agent {
|
|
|
209
208
|
for (;;) {
|
|
210
209
|
const modelOutput = {};
|
|
211
210
|
const stream = await context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
|
|
212
|
-
for await (const value of
|
|
211
|
+
for await (const value of stream) {
|
|
213
212
|
if (value.delta.text?.text) {
|
|
214
213
|
yield { delta: { text: { [outputKey]: value.delta.text.text } } };
|
|
215
214
|
}
|
|
@@ -281,7 +280,7 @@ class AIAgent extends agent_js_1.Agent {
|
|
|
281
280
|
if (!tool)
|
|
282
281
|
throw new Error(`Tool not found: ${call.function.name}`);
|
|
283
282
|
const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true });
|
|
284
|
-
yield*
|
|
283
|
+
yield* stream;
|
|
285
284
|
}
|
|
286
285
|
}
|
|
287
286
|
exports.AIAgent = AIAgent;
|
|
@@ -119,7 +119,7 @@ class TeamAgent extends agent_js_1.Agent {
|
|
|
119
119
|
const newAgents = [];
|
|
120
120
|
for (const agent of agents) {
|
|
121
121
|
const [o, transferToAgent] = await context.invoke(agent, { ...input, ...output }, { returnActiveAgent: true, streaming: true });
|
|
122
|
-
for await (const chunk of
|
|
122
|
+
for await (const chunk of o) {
|
|
123
123
|
yield chunk;
|
|
124
124
|
(0, stream_utils_js_1.mergeAgentResponseChunk)(output, chunk);
|
|
125
125
|
}
|
package/lib/cjs/aigne/context.js
CHANGED
|
@@ -175,7 +175,7 @@ class AIGNEContextInternal {
|
|
|
175
175
|
for (;;) {
|
|
176
176
|
const result = {};
|
|
177
177
|
const stream = await activeAgent.invoke(input, context, { streaming: true });
|
|
178
|
-
for await (const value of
|
|
178
|
+
for await (const value of stream) {
|
|
179
179
|
if (value.delta.text) {
|
|
180
180
|
yield { delta: { text: value.delta.text } };
|
|
181
181
|
}
|
|
@@ -92,7 +92,7 @@ class ToolMessageTemplate extends ChatMessageTemplate {
|
|
|
92
92
|
constructor(content, toolCallId, name) {
|
|
93
93
|
super("tool", typeof content === "string"
|
|
94
94
|
? content
|
|
95
|
-
: (0, type_utils_js_1.tryOrThrow)(() => JSON.stringify(content), `Failed to stringify tool content. toolCallId: ${toolCallId}, content: ${(0, node_util_1.inspect)(content)}`), name);
|
|
95
|
+
: (0, type_utils_js_1.tryOrThrow)(() => JSON.stringify(content, (_, value) => typeof value === "bigint" ? value.toString() : value), `Failed to stringify tool content. toolCallId: ${toolCallId}, content: ${(0, node_util_1.inspect)(content)}`), name);
|
|
96
96
|
this.toolCallId = toolCallId;
|
|
97
97
|
}
|
|
98
98
|
format(variables) {
|
package/lib/cjs/server/server.js
CHANGED
|
@@ -9,7 +9,6 @@ const content_type_1 = __importDefault(require("content-type"));
|
|
|
9
9
|
const raw_body_1 = __importDefault(require("raw-body"));
|
|
10
10
|
const zod_1 = require("zod");
|
|
11
11
|
const event_stream_js_1 = require("../utils/event-stream.js");
|
|
12
|
-
const stream_utils_js_1 = require("../utils/stream-utils.js");
|
|
13
12
|
const type_utils_js_1 = require("../utils/type-utils.js");
|
|
14
13
|
const error_js_1 = require("./error.js");
|
|
15
14
|
/**
|
|
@@ -158,11 +157,11 @@ class AIGNEServer {
|
|
|
158
157
|
*/
|
|
159
158
|
async _writeResponse(response, res) {
|
|
160
159
|
try {
|
|
161
|
-
res.writeHead(response.status, response.headers.
|
|
160
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
162
161
|
res.flushHeaders();
|
|
163
162
|
if (!response.body)
|
|
164
163
|
throw new Error("Response body is empty");
|
|
165
|
-
for await (const chunk of
|
|
164
|
+
for await (const chunk of response.body) {
|
|
166
165
|
res.write(chunk);
|
|
167
166
|
// Support for express with compression middleware
|
|
168
167
|
if ("flush" in res && typeof res.flush === "function") {
|
|
@@ -18,5 +18,4 @@ export declare function readableStreamToArray<T>(stream: ReadableStream<T>, opti
|
|
|
18
18
|
export declare function readableStreamToArray<T>(stream: ReadableStream<T>, options?: {
|
|
19
19
|
catchError?: false;
|
|
20
20
|
}): Promise<T[]>;
|
|
21
|
-
export declare function readableStreamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterable<T>;
|
|
22
21
|
export declare function stringToAgentResponseStream(str: string, key?: "text" | typeof MESSAGE_KEY | string): AgentResponseStream<Message>;
|
|
@@ -12,7 +12,6 @@ exports.isAsyncGenerator = isAsyncGenerator;
|
|
|
12
12
|
exports.arrayToAgentProcessAsyncGenerator = arrayToAgentProcessAsyncGenerator;
|
|
13
13
|
exports.arrayToReadableStream = arrayToReadableStream;
|
|
14
14
|
exports.readableStreamToArray = readableStreamToArray;
|
|
15
|
-
exports.readableStreamToAsyncIterator = readableStreamToAsyncIterator;
|
|
16
15
|
exports.stringToAgentResponseStream = stringToAgentResponseStream;
|
|
17
16
|
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
|
|
18
17
|
const agent_js_1 = require("../agents/agent.js");
|
|
@@ -42,7 +41,7 @@ function mergeAgentResponseChunk(output, chunk) {
|
|
|
42
41
|
async function agentResponseStreamToObject(stream) {
|
|
43
42
|
const json = {};
|
|
44
43
|
if (stream instanceof ReadableStream) {
|
|
45
|
-
for await (const value of
|
|
44
|
+
for await (const value of stream) {
|
|
46
45
|
mergeAgentResponseChunk(json, value);
|
|
47
46
|
}
|
|
48
47
|
}
|
|
@@ -92,22 +91,26 @@ function onAgentResponseStreamEnd(stream, callback, options) {
|
|
|
92
91
|
return new ReadableStream({
|
|
93
92
|
async pull(controller) {
|
|
94
93
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
while (true) {
|
|
95
|
+
const { value, done } = await reader.read();
|
|
96
|
+
if (done) {
|
|
97
|
+
const result = await callback(json);
|
|
98
|
+
if (result && !(0, fast_deep_equal_1.default)(result, json)) {
|
|
99
|
+
let chunk = { delta: { json: result } };
|
|
100
|
+
if (options?.processChunk)
|
|
101
|
+
chunk = options.processChunk(chunk);
|
|
102
|
+
controller.enqueue(chunk);
|
|
103
|
+
}
|
|
104
|
+
controller.close();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
mergeAgentResponseChunk(json, value);
|
|
97
108
|
const chunk = options?.processChunk ? options.processChunk(value) : value;
|
|
98
|
-
if (!(0, agent_js_1.isEmptyChunk)(chunk))
|
|
109
|
+
if (!(0, agent_js_1.isEmptyChunk)(chunk)) {
|
|
99
110
|
controller.enqueue(chunk);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
const result = await callback(json);
|
|
104
|
-
if (result && !(0, fast_deep_equal_1.default)(result, json)) {
|
|
105
|
-
let chunk = { delta: { json: result } };
|
|
106
|
-
if (options?.processChunk)
|
|
107
|
-
chunk = options.processChunk(chunk);
|
|
108
|
-
controller.enqueue(chunk);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
109
113
|
}
|
|
110
|
-
controller.close();
|
|
111
114
|
}
|
|
112
115
|
catch (error) {
|
|
113
116
|
controller.error(options?.errorCallback?.(error) ?? error);
|
|
@@ -147,7 +150,7 @@ function arrayToReadableStream(chunks) {
|
|
|
147
150
|
async function readableStreamToArray(stream, options) {
|
|
148
151
|
const result = [];
|
|
149
152
|
try {
|
|
150
|
-
for await (const value of
|
|
153
|
+
for await (const value of stream) {
|
|
151
154
|
result.push(value);
|
|
152
155
|
}
|
|
153
156
|
}
|
|
@@ -158,15 +161,6 @@ async function readableStreamToArray(stream, options) {
|
|
|
158
161
|
}
|
|
159
162
|
return result;
|
|
160
163
|
}
|
|
161
|
-
async function* readableStreamToAsyncIterator(stream) {
|
|
162
|
-
const reader = stream.getReader();
|
|
163
|
-
while (true) {
|
|
164
|
-
const { value, done } = await reader.read();
|
|
165
|
-
if (done)
|
|
166
|
-
break;
|
|
167
|
-
yield value;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
164
|
function stringToAgentResponseStream(str, key = "text") {
|
|
171
165
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "word" });
|
|
172
166
|
const segments = segmenter.segment(str);
|
|
@@ -18,5 +18,4 @@ export declare function readableStreamToArray<T>(stream: ReadableStream<T>, opti
|
|
|
18
18
|
export declare function readableStreamToArray<T>(stream: ReadableStream<T>, options?: {
|
|
19
19
|
catchError?: false;
|
|
20
20
|
}): Promise<T[]>;
|
|
21
|
-
export declare function readableStreamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterable<T>;
|
|
22
21
|
export declare function stringToAgentResponseStream(str: string, key?: "text" | typeof MESSAGE_KEY | string): AgentResponseStream<Message>;
|
|
@@ -4,7 +4,6 @@ import { MemoryAgent } from "../memory/memory.js";
|
|
|
4
4
|
import { ChatModel } from "../models/chat-model.js";
|
|
5
5
|
import { MESSAGE_KEY, PromptBuilder } from "../prompt/prompt-builder.js";
|
|
6
6
|
import { AgentMessageTemplate, ToolMessageTemplate } from "../prompt/template.js";
|
|
7
|
-
import { readableStreamToAsyncIterator } from "../utils/stream-utils.js";
|
|
8
7
|
import { checkArguments, isEmpty } from "../utils/type-utils.js";
|
|
9
8
|
import { Agent, agentOptionsSchema, } from "./agent.js";
|
|
10
9
|
import { isTransferAgentOutput } from "./types.js";
|
|
@@ -206,7 +205,7 @@ export class AIAgent extends Agent {
|
|
|
206
205
|
for (;;) {
|
|
207
206
|
const modelOutput = {};
|
|
208
207
|
const stream = await context.invoke(model, { ...modelInput, messages: modelInput.messages.concat(toolCallMessages) }, { streaming: true });
|
|
209
|
-
for await (const value of
|
|
208
|
+
for await (const value of stream) {
|
|
210
209
|
if (value.delta.text?.text) {
|
|
211
210
|
yield { delta: { text: { [outputKey]: value.delta.text.text } } };
|
|
212
211
|
}
|
|
@@ -278,6 +277,6 @@ export class AIAgent extends Agent {
|
|
|
278
277
|
if (!tool)
|
|
279
278
|
throw new Error(`Tool not found: ${call.function.name}`);
|
|
280
279
|
const stream = await context.invoke(tool, { ...call.function.arguments, ...input }, { streaming: true });
|
|
281
|
-
yield*
|
|
280
|
+
yield* stream;
|
|
282
281
|
}
|
|
283
282
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mergeAgentResponseChunk
|
|
1
|
+
import { mergeAgentResponseChunk } from "../utils/stream-utils.js";
|
|
2
2
|
import { isEmpty } from "../utils/type-utils.js";
|
|
3
3
|
import { Agent, } from "./agent.js";
|
|
4
4
|
/**
|
|
@@ -116,7 +116,7 @@ export class TeamAgent extends Agent {
|
|
|
116
116
|
const newAgents = [];
|
|
117
117
|
for (const agent of agents) {
|
|
118
118
|
const [o, transferToAgent] = await context.invoke(agent, { ...input, ...output }, { returnActiveAgent: true, streaming: true });
|
|
119
|
-
for await (const chunk of
|
|
119
|
+
for await (const chunk of o) {
|
|
120
120
|
yield chunk;
|
|
121
121
|
mergeAgentResponseChunk(output, chunk);
|
|
122
122
|
}
|
package/lib/esm/aigne/context.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Agent, } from "../agents/agent.js";
|
|
|
5
5
|
import { isTransferAgentOutput, transferAgentOutputKey } from "../agents/types.js";
|
|
6
6
|
import { UserAgent } from "../agents/user-agent.js";
|
|
7
7
|
import { createMessage } from "../prompt/prompt-builder.js";
|
|
8
|
-
import { agentResponseStreamToObject, asyncGeneratorToReadableStream, onAgentResponseStreamEnd,
|
|
8
|
+
import { agentResponseStreamToObject, asyncGeneratorToReadableStream, onAgentResponseStreamEnd, } from "../utils/stream-utils.js";
|
|
9
9
|
import { checkArguments, isNil, omitBy, } from "../utils/type-utils.js";
|
|
10
10
|
import { MessageQueue, toMessagePayload, } from "./message-queue.js";
|
|
11
11
|
import { newEmptyContextUsage } from "./usage.js";
|
|
@@ -168,7 +168,7 @@ class AIGNEContextInternal {
|
|
|
168
168
|
for (;;) {
|
|
169
169
|
const result = {};
|
|
170
170
|
const stream = await activeAgent.invoke(input, context, { streaming: true });
|
|
171
|
-
for await (const value of
|
|
171
|
+
for await (const value of stream) {
|
|
172
172
|
if (value.delta.text) {
|
|
173
173
|
yield { delta: { text: value.delta.text } };
|
|
174
174
|
}
|
|
@@ -80,7 +80,7 @@ export class ToolMessageTemplate extends ChatMessageTemplate {
|
|
|
80
80
|
constructor(content, toolCallId, name) {
|
|
81
81
|
super("tool", typeof content === "string"
|
|
82
82
|
? content
|
|
83
|
-
: tryOrThrow(() => JSON.stringify(content), `Failed to stringify tool content. toolCallId: ${toolCallId}, content: ${inspect(content)}`), name);
|
|
83
|
+
: tryOrThrow(() => JSON.stringify(content, (_, value) => typeof value === "bigint" ? value.toString() : value), `Failed to stringify tool content. toolCallId: ${toolCallId}, content: ${inspect(content)}`), name);
|
|
84
84
|
this.toolCallId = toolCallId;
|
|
85
85
|
}
|
|
86
86
|
format(variables) {
|
package/lib/esm/server/server.js
CHANGED
|
@@ -3,7 +3,6 @@ import contentType from "content-type";
|
|
|
3
3
|
import getRawBody from "raw-body";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { AgentResponseStreamSSE } from "../utils/event-stream.js";
|
|
6
|
-
import { readableStreamToAsyncIterator } from "../utils/stream-utils.js";
|
|
7
6
|
import { checkArguments, isRecord, tryOrThrow } from "../utils/type-utils.js";
|
|
8
7
|
import { ServerError } from "./error.js";
|
|
9
8
|
/**
|
|
@@ -152,11 +151,11 @@ export class AIGNEServer {
|
|
|
152
151
|
*/
|
|
153
152
|
async _writeResponse(response, res) {
|
|
154
153
|
try {
|
|
155
|
-
res.writeHead(response.status, response.headers.
|
|
154
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
156
155
|
res.flushHeaders();
|
|
157
156
|
if (!response.body)
|
|
158
157
|
throw new Error("Response body is empty");
|
|
159
|
-
for await (const chunk of
|
|
158
|
+
for await (const chunk of response.body) {
|
|
160
159
|
res.write(chunk);
|
|
161
160
|
// Support for express with compression middleware
|
|
162
161
|
if ("flush" in res && typeof res.flush === "function") {
|
|
@@ -18,5 +18,4 @@ export declare function readableStreamToArray<T>(stream: ReadableStream<T>, opti
|
|
|
18
18
|
export declare function readableStreamToArray<T>(stream: ReadableStream<T>, options?: {
|
|
19
19
|
catchError?: false;
|
|
20
20
|
}): Promise<T[]>;
|
|
21
|
-
export declare function readableStreamToAsyncIterator<T>(stream: ReadableStream<T>): AsyncIterable<T>;
|
|
22
21
|
export declare function stringToAgentResponseStream(str: string, key?: "text" | typeof MESSAGE_KEY | string): AgentResponseStream<Message>;
|
|
@@ -26,7 +26,7 @@ export function mergeAgentResponseChunk(output, chunk) {
|
|
|
26
26
|
export async function agentResponseStreamToObject(stream) {
|
|
27
27
|
const json = {};
|
|
28
28
|
if (stream instanceof ReadableStream) {
|
|
29
|
-
for await (const value of
|
|
29
|
+
for await (const value of stream) {
|
|
30
30
|
mergeAgentResponseChunk(json, value);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -76,22 +76,26 @@ export function onAgentResponseStreamEnd(stream, callback, options) {
|
|
|
76
76
|
return new ReadableStream({
|
|
77
77
|
async pull(controller) {
|
|
78
78
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
while (true) {
|
|
80
|
+
const { value, done } = await reader.read();
|
|
81
|
+
if (done) {
|
|
82
|
+
const result = await callback(json);
|
|
83
|
+
if (result && !equal(result, json)) {
|
|
84
|
+
let chunk = { delta: { json: result } };
|
|
85
|
+
if (options?.processChunk)
|
|
86
|
+
chunk = options.processChunk(chunk);
|
|
87
|
+
controller.enqueue(chunk);
|
|
88
|
+
}
|
|
89
|
+
controller.close();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
mergeAgentResponseChunk(json, value);
|
|
81
93
|
const chunk = options?.processChunk ? options.processChunk(value) : value;
|
|
82
|
-
if (!isEmptyChunk(chunk))
|
|
94
|
+
if (!isEmptyChunk(chunk)) {
|
|
83
95
|
controller.enqueue(chunk);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
const result = await callback(json);
|
|
88
|
-
if (result && !equal(result, json)) {
|
|
89
|
-
let chunk = { delta: { json: result } };
|
|
90
|
-
if (options?.processChunk)
|
|
91
|
-
chunk = options.processChunk(chunk);
|
|
92
|
-
controller.enqueue(chunk);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
|
-
controller.close();
|
|
95
99
|
}
|
|
96
100
|
catch (error) {
|
|
97
101
|
controller.error(options?.errorCallback?.(error) ?? error);
|
|
@@ -131,7 +135,7 @@ export function arrayToReadableStream(chunks) {
|
|
|
131
135
|
export async function readableStreamToArray(stream, options) {
|
|
132
136
|
const result = [];
|
|
133
137
|
try {
|
|
134
|
-
for await (const value of
|
|
138
|
+
for await (const value of stream) {
|
|
135
139
|
result.push(value);
|
|
136
140
|
}
|
|
137
141
|
}
|
|
@@ -142,15 +146,6 @@ export async function readableStreamToArray(stream, options) {
|
|
|
142
146
|
}
|
|
143
147
|
return result;
|
|
144
148
|
}
|
|
145
|
-
export async function* readableStreamToAsyncIterator(stream) {
|
|
146
|
-
const reader = stream.getReader();
|
|
147
|
-
while (true) {
|
|
148
|
-
const { value, done } = await reader.read();
|
|
149
|
-
if (done)
|
|
150
|
-
break;
|
|
151
|
-
yield value;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
149
|
export function stringToAgentResponseStream(str, key = "text") {
|
|
155
150
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "word" });
|
|
156
151
|
const segments = segmenter.segment(str);
|