@smart-cloud/ai-kit-ui 1.3.15 → 1.4.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/dist/index.cjs +19 -9
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +19 -9
- package/package.json +26 -23
- package/src/ai-feature/AiFeature.tsx +98 -8
- package/src/ai-feature/chunked-features.ts +254 -0
- package/src/ai-feature/chunking-utils.ts +211 -0
- package/tsup.config.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smart-cloud/ai-kit-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -19,51 +19,54 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@emotion/cache": "^11.14.0",
|
|
21
21
|
"@emotion/react": "^11.14.0",
|
|
22
|
-
"@mantine/colors-generator": "^8.3.
|
|
23
|
-
"@smart-cloud/ai-kit-core": "^1.3
|
|
24
|
-
"@smart-cloud/wpsuite-core": "^2.2.
|
|
25
|
-
"@tabler/icons-react": "^3.
|
|
22
|
+
"@mantine/colors-generator": "^8.3.16",
|
|
23
|
+
"@smart-cloud/ai-kit-core": "^1.4.3",
|
|
24
|
+
"@smart-cloud/wpsuite-core": "^2.2.10",
|
|
25
|
+
"@tabler/icons-react": "^3.40.0",
|
|
26
26
|
"chroma-js": "^3.2.0",
|
|
27
27
|
"react-markdown": "^10.1.0",
|
|
28
|
+
"rehype-parse": "^9.0.1",
|
|
28
29
|
"rehype-raw": "^7.0.0",
|
|
30
|
+
"rehype-remark": "^10.0.0",
|
|
29
31
|
"rehype-sanitize": "^6.0.0",
|
|
30
32
|
"rehype-stringify": "^10.0.1",
|
|
31
33
|
"remark-gfm": "^4.0.1",
|
|
32
34
|
"remark-parse": "^11.0.0",
|
|
33
35
|
"remark-rehype": "^11.1.2",
|
|
36
|
+
"remark-stringify": "^11.0.0",
|
|
34
37
|
"unified": "^11.0.5"
|
|
35
38
|
},
|
|
36
39
|
"peerDependencies": {
|
|
37
|
-
"@mantine/core": "^8.3.
|
|
38
|
-
"@mantine/hooks": "^8.3.
|
|
39
|
-
"@mantine/modals": "^8.3.
|
|
40
|
-
"@wordpress/data": "^10.
|
|
41
|
-
"aws-amplify": "^6.16.
|
|
40
|
+
"@mantine/core": "^8.3.16",
|
|
41
|
+
"@mantine/hooks": "^8.3.16",
|
|
42
|
+
"@mantine/modals": "^8.3.16",
|
|
43
|
+
"@wordpress/data": "^10.41.0",
|
|
44
|
+
"aws-amplify": "^6.16.3",
|
|
42
45
|
"react": "^18.3.1",
|
|
43
46
|
"react-dom": "^18.3.1"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"@eslint/js": "^10.0.1",
|
|
47
|
-
"@mantine/core": "^8.3.
|
|
48
|
-
"@mantine/hooks": "^8.3.
|
|
49
|
-
"@mantine/modals": "^8.3.
|
|
50
|
-
"@types/dom-chromium-ai": "^0.0.
|
|
51
|
-
"@types/jquery": "^
|
|
50
|
+
"@mantine/core": "^8.3.16",
|
|
51
|
+
"@mantine/hooks": "^8.3.16",
|
|
52
|
+
"@mantine/modals": "^8.3.16",
|
|
53
|
+
"@types/dom-chromium-ai": "^0.0.15",
|
|
54
|
+
"@types/jquery": "^4.0.0",
|
|
52
55
|
"@types/react": "^18.3.23",
|
|
53
56
|
"@types/react-dom": "^18.3.7",
|
|
54
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
55
|
-
"@typescript-eslint/parser": "^8.
|
|
56
|
-
"@wordpress/data": "^10.
|
|
57
|
-
"ajv": "^8.
|
|
58
|
-
"aws-amplify": "^6.16.
|
|
59
|
-
"eslint": "^10.0.
|
|
60
|
-
"globals": "^17.
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
58
|
+
"@typescript-eslint/parser": "^8.57.0",
|
|
59
|
+
"@wordpress/data": "^10.41.0",
|
|
60
|
+
"ajv": "^8.18.0",
|
|
61
|
+
"aws-amplify": "^6.16.3",
|
|
62
|
+
"eslint": "^10.0.3",
|
|
63
|
+
"globals": "^17.4.0",
|
|
61
64
|
"jquery": "^4.0.0",
|
|
62
65
|
"react": "^18.3.1",
|
|
63
66
|
"react-dom": "^18.3.1",
|
|
64
67
|
"tsup": "^8.5.1",
|
|
65
68
|
"typescript": "^5.9.3",
|
|
66
|
-
"typescript-eslint": "^8.
|
|
69
|
+
"typescript-eslint": "^8.57.0"
|
|
67
70
|
},
|
|
68
71
|
"exports": {
|
|
69
72
|
".": {
|
|
@@ -55,6 +55,12 @@ import {
|
|
|
55
55
|
|
|
56
56
|
import { translations } from "../i18n";
|
|
57
57
|
import { PoweredBy } from "../poweredBy";
|
|
58
|
+
import { shouldChunkInput } from "./chunking-utils";
|
|
59
|
+
import {
|
|
60
|
+
chunkedSummarize,
|
|
61
|
+
chunkedTranslate,
|
|
62
|
+
chunkedRewrite,
|
|
63
|
+
} from "./chunked-features";
|
|
58
64
|
import {
|
|
59
65
|
isBackendConfigured,
|
|
60
66
|
readDefaultOutputLanguage,
|
|
@@ -472,7 +478,31 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
|
|
|
472
478
|
}, [text, defaults]);
|
|
473
479
|
|
|
474
480
|
const canGenerate = useMemo(() => {
|
|
475
|
-
|
|
481
|
+
// If inputText is a function (async or sync getText), we can't determine
|
|
482
|
+
// if it has content without calling it. Assume it's valid if provided.
|
|
483
|
+
const input = inputText;
|
|
484
|
+
if (typeof input === "function") {
|
|
485
|
+
switch (mode) {
|
|
486
|
+
case "generateImageMetadata":
|
|
487
|
+
return Boolean(image);
|
|
488
|
+
case "translate":
|
|
489
|
+
// For translate, we need outputLanguage check, but can't check text without calling getText
|
|
490
|
+
return Boolean(
|
|
491
|
+
!outputLanguage || detectedLanguage !== outputLanguage,
|
|
492
|
+
);
|
|
493
|
+
case "summarize":
|
|
494
|
+
case "proofread":
|
|
495
|
+
case "rewrite":
|
|
496
|
+
case "write":
|
|
497
|
+
case "generatePostMetadata":
|
|
498
|
+
return true; // Assume getText will provide valid content
|
|
499
|
+
default:
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// If inputText is a string, check it directly
|
|
505
|
+
const text = input as string | undefined;
|
|
476
506
|
switch (mode) {
|
|
477
507
|
case "generateImageMetadata":
|
|
478
508
|
return Boolean(image);
|
|
@@ -507,8 +537,11 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
|
|
|
507
537
|
setError(null);
|
|
508
538
|
setGenerated(null);
|
|
509
539
|
|
|
540
|
+
const input = await inputText;
|
|
510
541
|
try {
|
|
511
|
-
|
|
542
|
+
// Support both sync and async getText functions
|
|
543
|
+
const text =
|
|
544
|
+
typeof input === "function" ? await Promise.resolve(input()) : input;
|
|
512
545
|
switch (mode) {
|
|
513
546
|
case "summarize": {
|
|
514
547
|
const res = await ai.run(async ({ signal, onStatus }) => {
|
|
@@ -524,13 +557,32 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
|
|
|
524
557
|
type: type as SummarizerType,
|
|
525
558
|
outputLanguage: outLang as SummarizeArgs["outputLanguage"],
|
|
526
559
|
};
|
|
527
|
-
|
|
560
|
+
|
|
561
|
+
const featureOptions: FeatureOptions = {
|
|
528
562
|
signal,
|
|
529
563
|
onStatus,
|
|
530
564
|
context,
|
|
531
565
|
modeOverride,
|
|
532
566
|
onDeviceTimeoutOverride: onDeviceTimeout,
|
|
533
|
-
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// Determine if we're using on-device mode
|
|
570
|
+
const isOnDevice =
|
|
571
|
+
modeOverride === "local-only" ||
|
|
572
|
+
(!modeOverride && context === "admin");
|
|
573
|
+
|
|
574
|
+
// Check if chunking is needed
|
|
575
|
+
if (shouldChunkInput(text!.trim(), "summarize", isOnDevice)) {
|
|
576
|
+
return await chunkedSummarize(
|
|
577
|
+
text!.trim(),
|
|
578
|
+
args,
|
|
579
|
+
featureOptions,
|
|
580
|
+
isOnDevice,
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Normal single-pass summarization
|
|
585
|
+
const out = await summarize(args, featureOptions);
|
|
534
586
|
return out.result;
|
|
535
587
|
});
|
|
536
588
|
setGenerated((res as never) ?? "");
|
|
@@ -614,13 +666,32 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
|
|
|
614
666
|
sourceLanguage: inputLang!,
|
|
615
667
|
targetLanguage: outLang,
|
|
616
668
|
};
|
|
617
|
-
|
|
669
|
+
|
|
670
|
+
const featureOptions: FeatureOptions = {
|
|
618
671
|
signal,
|
|
619
672
|
onStatus,
|
|
620
673
|
context,
|
|
621
674
|
modeOverride,
|
|
622
675
|
onDeviceTimeoutOverride: onDeviceTimeout,
|
|
623
|
-
}
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// Determine if we're using on-device mode
|
|
679
|
+
const isOnDevice =
|
|
680
|
+
modeOverride === "local-only" ||
|
|
681
|
+
(!modeOverride && context === "admin");
|
|
682
|
+
|
|
683
|
+
// Check if chunking is needed (both on-device quota and AWS Translate limit)
|
|
684
|
+
if (shouldChunkInput(text!.trim(), "translate", isOnDevice)) {
|
|
685
|
+
return await chunkedTranslate(
|
|
686
|
+
text!.trim(),
|
|
687
|
+
args,
|
|
688
|
+
featureOptions,
|
|
689
|
+
isOnDevice,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Normal single-pass translation
|
|
694
|
+
const out = await translate(args, featureOptions);
|
|
624
695
|
return out.result;
|
|
625
696
|
});
|
|
626
697
|
setGenerated((res as never) ?? "");
|
|
@@ -652,13 +723,32 @@ const AiFeatureBase: FC<AiFeatureProps & AiKitShellInjectedProps> = (props) => {
|
|
|
652
723
|
length: length as RewriterLength,
|
|
653
724
|
outputLanguage: outLang as RewriteArgs["outputLanguage"],
|
|
654
725
|
};
|
|
655
|
-
|
|
726
|
+
|
|
727
|
+
const featureOptions: FeatureOptions = {
|
|
656
728
|
signal,
|
|
657
729
|
onStatus,
|
|
658
730
|
context,
|
|
659
731
|
modeOverride,
|
|
660
732
|
onDeviceTimeoutOverride: onDeviceTimeout,
|
|
661
|
-
}
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// Determine if we're using on-device mode
|
|
736
|
+
const isOnDevice =
|
|
737
|
+
modeOverride === "local-only" ||
|
|
738
|
+
(!modeOverride && context === "admin");
|
|
739
|
+
|
|
740
|
+
// Check if chunking is needed
|
|
741
|
+
if (shouldChunkInput(text!.trim(), "rewrite", isOnDevice)) {
|
|
742
|
+
return await chunkedRewrite(
|
|
743
|
+
text!.trim(),
|
|
744
|
+
args,
|
|
745
|
+
featureOptions,
|
|
746
|
+
isOnDevice,
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Normal single-pass rewrite
|
|
751
|
+
const out = await rewrite(args, featureOptions);
|
|
662
752
|
return out.result;
|
|
663
753
|
});
|
|
664
754
|
setGenerated((res as never) ?? "");
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunked versions of AI features for handling large inputs
|
|
3
|
+
*
|
|
4
|
+
* These wrappers split large inputs into smaller chunks, process them
|
|
5
|
+
* sequentially, and combine the results.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
AiKitStatusEvent,
|
|
10
|
+
FeatureOptions,
|
|
11
|
+
SummarizeArgs,
|
|
12
|
+
SummarizeResult,
|
|
13
|
+
TranslateArgs,
|
|
14
|
+
TranslateResult,
|
|
15
|
+
RewriteArgs,
|
|
16
|
+
RewriteResult,
|
|
17
|
+
} from "@smart-cloud/ai-kit-core";
|
|
18
|
+
import { summarize, translate, rewrite } from "@smart-cloud/ai-kit-core";
|
|
19
|
+
import {
|
|
20
|
+
splitTextIntoChunks,
|
|
21
|
+
getChunkSize,
|
|
22
|
+
estimateTokenCount,
|
|
23
|
+
} from "./chunking-utils";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Chunked summarize implementation
|
|
27
|
+
*
|
|
28
|
+
* Strategy:
|
|
29
|
+
* 1. Split text into chunks
|
|
30
|
+
* 2. Summarize each chunk
|
|
31
|
+
* 3. If combined summaries are still large, recursively summarize them
|
|
32
|
+
* 4. Return final summary
|
|
33
|
+
*/
|
|
34
|
+
export async function chunkedSummarize(
|
|
35
|
+
text: string,
|
|
36
|
+
args: SummarizeArgs,
|
|
37
|
+
options: FeatureOptions,
|
|
38
|
+
isOnDevice: boolean,
|
|
39
|
+
recursionLevel: number = 0,
|
|
40
|
+
): Promise<SummarizeResult> {
|
|
41
|
+
const maxChunkSize = getChunkSize("summarize", isOnDevice);
|
|
42
|
+
const chunks = splitTextIntoChunks(text, maxChunkSize);
|
|
43
|
+
|
|
44
|
+
if (chunks.length === 1) {
|
|
45
|
+
// No chunking needed
|
|
46
|
+
return await summarize(args, options);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Prevent infinite recursion (max 2 levels)
|
|
50
|
+
if (recursionLevel >= 2) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
"Text is too large to summarize. Please try using backend mode or reduce the input size.",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Phase 1: Summarize each chunk
|
|
57
|
+
const chunkSummaries: string[] = [];
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
60
|
+
const chunkResult = await summarize(
|
|
61
|
+
{
|
|
62
|
+
...args,
|
|
63
|
+
text: chunks[i].text,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
...options,
|
|
67
|
+
onStatus: (e: AiKitStatusEvent) => {
|
|
68
|
+
if (options.onStatus) {
|
|
69
|
+
// Modify progress to reflect chunking
|
|
70
|
+
const baseProgress =
|
|
71
|
+
typeof e.progress === "number" ? e.progress : 0;
|
|
72
|
+
const chunkProgress = (i + baseProgress) / chunks.length;
|
|
73
|
+
|
|
74
|
+
options.onStatus({
|
|
75
|
+
...e,
|
|
76
|
+
message:
|
|
77
|
+
recursionLevel === 0
|
|
78
|
+
? `Summarizing part ${i + 1}/${chunks.length}...`
|
|
79
|
+
: `Combining summaries (${i + 1}/${chunks.length})...`,
|
|
80
|
+
progress: chunkProgress,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
chunkSummaries.push(chunkResult.result);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Phase 2: Combine summaries
|
|
91
|
+
const combinedSummaries = chunkSummaries.join("\n\n");
|
|
92
|
+
|
|
93
|
+
// Check if we need another round of summarization
|
|
94
|
+
if (estimateTokenCount(combinedSummaries) > maxChunkSize / 3.5) {
|
|
95
|
+
// Recursively summarize
|
|
96
|
+
return await chunkedSummarize(
|
|
97
|
+
combinedSummaries,
|
|
98
|
+
{
|
|
99
|
+
...args,
|
|
100
|
+
// Adjust length for recursive summarization
|
|
101
|
+
length: args.length === "short" ? "short" : "medium",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
...options,
|
|
105
|
+
onStatus: (e: AiKitStatusEvent) => {
|
|
106
|
+
if (options.onStatus) {
|
|
107
|
+
options.onStatus({
|
|
108
|
+
...e,
|
|
109
|
+
message: "Creating final summary...",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
isOnDevice,
|
|
115
|
+
recursionLevel + 1,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Final summarization
|
|
120
|
+
return await summarize(
|
|
121
|
+
{
|
|
122
|
+
...args,
|
|
123
|
+
text: combinedSummaries,
|
|
124
|
+
length: args.length === "short" ? "short" : "medium",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
...options,
|
|
128
|
+
onStatus: (e: AiKitStatusEvent) => {
|
|
129
|
+
if (options.onStatus) {
|
|
130
|
+
options.onStatus({
|
|
131
|
+
...e,
|
|
132
|
+
message: "Creating final summary...",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Chunked translate implementation
|
|
142
|
+
*
|
|
143
|
+
* Strategy:
|
|
144
|
+
* 1. Split text into chunks (respecting AWS Translate 10k char limit)
|
|
145
|
+
* 2. Translate each chunk sequentially
|
|
146
|
+
* 3. Join translated chunks
|
|
147
|
+
*/
|
|
148
|
+
export async function chunkedTranslate(
|
|
149
|
+
text: string,
|
|
150
|
+
args: TranslateArgs,
|
|
151
|
+
options: FeatureOptions,
|
|
152
|
+
isOnDevice: boolean,
|
|
153
|
+
): Promise<TranslateResult> {
|
|
154
|
+
const maxChunkSize = getChunkSize("translate", isOnDevice);
|
|
155
|
+
const chunks = splitTextIntoChunks(text, maxChunkSize);
|
|
156
|
+
|
|
157
|
+
if (chunks.length === 1) {
|
|
158
|
+
// No chunking needed
|
|
159
|
+
return await translate(args, options);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Translate each chunk sequentially
|
|
163
|
+
const translatedChunks: string[] = [];
|
|
164
|
+
|
|
165
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
166
|
+
const chunkResult = await translate(
|
|
167
|
+
{
|
|
168
|
+
...args,
|
|
169
|
+
text: chunks[i].text,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
...options,
|
|
173
|
+
onStatus: (e: AiKitStatusEvent) => {
|
|
174
|
+
if (options.onStatus) {
|
|
175
|
+
const baseProgress =
|
|
176
|
+
typeof e.progress === "number" ? e.progress : 0;
|
|
177
|
+
const chunkProgress = (i + baseProgress) / chunks.length;
|
|
178
|
+
|
|
179
|
+
options.onStatus({
|
|
180
|
+
...e,
|
|
181
|
+
message: `Translating part ${i + 1}/${chunks.length}...`,
|
|
182
|
+
progress: chunkProgress,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
translatedChunks.push(chunkResult.result);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Join with paragraph breaks to maintain structure
|
|
193
|
+
return {
|
|
194
|
+
result: translatedChunks.join("\n\n"),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Chunked rewrite implementation
|
|
200
|
+
*
|
|
201
|
+
* Strategy:
|
|
202
|
+
* 1. Split text into chunks
|
|
203
|
+
* 2. Rewrite each chunk sequentially
|
|
204
|
+
* 3. Join rewritten chunks
|
|
205
|
+
*/
|
|
206
|
+
export async function chunkedRewrite(
|
|
207
|
+
text: string,
|
|
208
|
+
args: RewriteArgs,
|
|
209
|
+
options: FeatureOptions,
|
|
210
|
+
isOnDevice: boolean,
|
|
211
|
+
): Promise<RewriteResult> {
|
|
212
|
+
const maxChunkSize = getChunkSize("rewrite", isOnDevice);
|
|
213
|
+
const chunks = splitTextIntoChunks(text, maxChunkSize);
|
|
214
|
+
|
|
215
|
+
if (chunks.length === 1) {
|
|
216
|
+
// No chunking needed
|
|
217
|
+
return await rewrite(args, options);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Rewrite each chunk sequentially
|
|
221
|
+
const rewrittenChunks: string[] = [];
|
|
222
|
+
|
|
223
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
224
|
+
const chunkResult = await rewrite(
|
|
225
|
+
{
|
|
226
|
+
...args,
|
|
227
|
+
text: chunks[i].text,
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
...options,
|
|
231
|
+
onStatus: (e: AiKitStatusEvent) => {
|
|
232
|
+
if (options.onStatus) {
|
|
233
|
+
const baseProgress =
|
|
234
|
+
typeof e.progress === "number" ? e.progress : 0;
|
|
235
|
+
const chunkProgress = (i + baseProgress) / chunks.length;
|
|
236
|
+
|
|
237
|
+
options.onStatus({
|
|
238
|
+
...e,
|
|
239
|
+
message: `Rewriting part ${i + 1}/${chunks.length}...`,
|
|
240
|
+
progress: chunkProgress,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
rewrittenChunks.push(chunkResult.result);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Join with paragraph breaks
|
|
251
|
+
return {
|
|
252
|
+
result: rewrittenChunks.join("\n\n"),
|
|
253
|
+
};
|
|
254
|
+
}
|