@mast-ai/built-in-ai 0.1.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/LICENSE +193 -0
- package/README.md +48 -0
- package/dist/BuiltInAIAdapter.d.ts +31 -0
- package/dist/BuiltInAIAdapter.js +151 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +8 -0
- package/dist/tools/detectLanguage.d.ts +38 -0
- package/dist/tools/detectLanguage.js +82 -0
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/proofread.d.ts +47 -0
- package/dist/tools/proofread.js +85 -0
- package/dist/tools/summarize.d.ts +42 -0
- package/dist/tools/summarize.js +132 -0
- package/dist/tools/translate.d.ts +45 -0
- package/dist/tools/translate.js +97 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.js +3 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship made available under
|
|
36
|
+
the License, as indicated by a copyright notice that is included in
|
|
37
|
+
or attached to the work (an example is provided in the Appendix below).
|
|
38
|
+
|
|
39
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
40
|
+
form, that is based on (or derived from) the Work and for which the
|
|
41
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
42
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
43
|
+
of this License, Derivative Works shall not include works that remain
|
|
44
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
45
|
+
the Work and Derivative Works thereof.
|
|
46
|
+
|
|
47
|
+
"Contribution" shall mean, as submitted to the Licensor for inclusion
|
|
48
|
+
in the Work by the copyright owner or by an individual or Legal Entity
|
|
49
|
+
authorized to submit on behalf of the copyright owner. For the purposes
|
|
50
|
+
of this definition, "submitted" means any form of electronic, verbal,
|
|
51
|
+
or written communication sent to the Licensor or its representatives,
|
|
52
|
+
including but not limited to communication on electronic mailing lists,
|
|
53
|
+
source code control systems, and issue tracking systems that are managed
|
|
54
|
+
by, or on behalf of, the Licensor for the purpose of discussing and
|
|
55
|
+
improving the Work, but excluding communication that is conspicuously
|
|
56
|
+
marked or designated in writing by the copyright owner as "Not a
|
|
57
|
+
Contribution."
|
|
58
|
+
|
|
59
|
+
"Contributor" shall mean Licensor and any Legal Entity on behalf of
|
|
60
|
+
whom a Contribution has been received by the Licensor and included
|
|
61
|
+
within the Work.
|
|
62
|
+
|
|
63
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
64
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
65
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
66
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
67
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
68
|
+
Work and such Derivative Works in Source or Object form.
|
|
69
|
+
|
|
70
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
71
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
72
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
73
|
+
(except as stated in this section) patent license to make, have made,
|
|
74
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
75
|
+
where such license applies only to those patent claims licensable
|
|
76
|
+
by such Contributor that are necessarily infringed by their
|
|
77
|
+
Contribution(s) alone or by the combination of their Contribution(s)
|
|
78
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
79
|
+
institute patent litigation against any entity (including a cross-claim
|
|
80
|
+
or counterclaim in a lawsuit) alleging that the Work or any patent
|
|
81
|
+
claim embodied in the Work constitutes direct or contributory patent
|
|
82
|
+
infringement, then any patent licenses granted to You under this
|
|
83
|
+
License for that Work shall terminate as of the date such litigation
|
|
84
|
+
is filed.
|
|
85
|
+
|
|
86
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
87
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
88
|
+
modifications, and in Source or Object form, provided that You
|
|
89
|
+
meet the following conditions:
|
|
90
|
+
|
|
91
|
+
(a) You must give any other recipients of the Work or Derivative
|
|
92
|
+
Works a copy of this License; and
|
|
93
|
+
|
|
94
|
+
(b) You must cause any modified files to carry prominent notices
|
|
95
|
+
stating that You changed the files; and
|
|
96
|
+
|
|
97
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
98
|
+
that You distribute, all copyright, patent, trademark, and
|
|
99
|
+
attribution notices from the Source form of the Work,
|
|
100
|
+
excluding those notices that do not pertain to any part of
|
|
101
|
+
the Derivative Works; and
|
|
102
|
+
|
|
103
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
104
|
+
distribution, You must include a readable copy of the
|
|
105
|
+
attribution notices contained within such NOTICE file, in
|
|
106
|
+
at least one of the following places: within a NOTICE text
|
|
107
|
+
file distributed as part of the Derivative Works; within
|
|
108
|
+
the Source form or documentation, if provided along with the
|
|
109
|
+
Derivative Works; or, within a display generated by the
|
|
110
|
+
Derivative Works, if and wherever such third-party notices
|
|
111
|
+
normally appear. The contents of the NOTICE file are for
|
|
112
|
+
informational purposes only and do not modify the License.
|
|
113
|
+
You may add Your own attribution notices within Derivative
|
|
114
|
+
Works that You distribute, alongside or in addition to the
|
|
115
|
+
NOTICE text from the Work, provided that such additional
|
|
116
|
+
attribution notices cannot be construed as modifying the License.
|
|
117
|
+
|
|
118
|
+
You may add Your own license statement for Your modifications and
|
|
119
|
+
may provide additional grant of rights to use, copy, modify, merge,
|
|
120
|
+
publish, distribute, sublicense, and/or sell copies of the
|
|
121
|
+
Contribution, and to permit persons to whom the Contribution is
|
|
122
|
+
furnished to do so.
|
|
123
|
+
|
|
124
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
125
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
126
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
127
|
+
this License, without any additional terms or conditions.
|
|
128
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
129
|
+
the terms of any separate license agreement you may have executed
|
|
130
|
+
with Licensor regarding such Contributions.
|
|
131
|
+
|
|
132
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
133
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
134
|
+
except as required for reasonable and customary use in describing the
|
|
135
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
136
|
+
|
|
137
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
138
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
139
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
140
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
141
|
+
implied, including, without limitation, any conditions of
|
|
142
|
+
TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
143
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
144
|
+
appropriateness of using or reproducing the Work and assume any
|
|
145
|
+
risks associated with Your exercise of permissions under this License.
|
|
146
|
+
|
|
147
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
148
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
149
|
+
unless required by applicable law (such as deliberate and grossly
|
|
150
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
151
|
+
liable to You for damages, including any direct, indirect, special,
|
|
152
|
+
incidental, or exemplary damages of any character arising as a
|
|
153
|
+
result of this License or out of the use or inability to use the
|
|
154
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
155
|
+
workstop, computer failure or malfunction, or all other commercial
|
|
156
|
+
damages or losses), even if such Contributor has been advised of the
|
|
157
|
+
possibility of such damages.
|
|
158
|
+
|
|
159
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
160
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
161
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
162
|
+
or other liability obligations and/or rights consistent with this
|
|
163
|
+
License. However, in accepting such obligations, You may act only
|
|
164
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
165
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
166
|
+
defend, and hold each Contributor harmless for any liability
|
|
167
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
168
|
+
of your accepting any such warranty or additional liability.
|
|
169
|
+
|
|
170
|
+
END OF TERMS AND CONDITIONS
|
|
171
|
+
|
|
172
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
173
|
+
|
|
174
|
+
To apply the Apache License to your work, attach the following
|
|
175
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
176
|
+
replaced with your own identifying information. (Don't include
|
|
177
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
178
|
+
comment syntax for the programming language used. Source files
|
|
179
|
+
may also indicate a copyright notice separately from the text below.
|
|
180
|
+
|
|
181
|
+
Copyright 2026 Andre Cipriani Bandarra
|
|
182
|
+
|
|
183
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
184
|
+
you may not use this file except in compliance with the License.
|
|
185
|
+
You may obtain a copy of the License at
|
|
186
|
+
|
|
187
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
188
|
+
|
|
189
|
+
Unless required by applicable law or agreed to in writing, software
|
|
190
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
191
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
192
|
+
See the License for the specific language governing permissions and
|
|
193
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @mast-ai/built-in-ai
|
|
2
|
+
|
|
3
|
+
On-device inference for [MAST](https://github.com/andreban/mast-ai) via Chrome's [built-in AI](https://developer.chrome.com/docs/ai/built-in) (Prompt API / Gemini Nano). No server, no API key, no network round trip.
|
|
4
|
+
|
|
5
|
+
Also exposes browser AI capabilities as ready-to-register MAST tools: summarization, translation, language detection, and proofreading.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @mast-ai/core @mast-ai/built-in-ai
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Chrome with the Prompt API and related built-in AI APIs enabled.
|
|
14
|
+
|
|
15
|
+
## Adapter
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { AgentRunner, ToolRegistry, createAgent } from '@mast-ai/core';
|
|
19
|
+
import { BuiltInAIAdapter, checkAvailability } from '@mast-ai/built-in-ai';
|
|
20
|
+
|
|
21
|
+
const availability = await checkAvailability();
|
|
22
|
+
if (availability !== 'available') {
|
|
23
|
+
throw new Error(`Prompt API not ready: ${availability}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const adapter = new BuiltInAIAdapter();
|
|
27
|
+
const runner = new AgentRunner(adapter, new ToolRegistry());
|
|
28
|
+
|
|
29
|
+
const agent = createAgent({ name: 'LocalAssistant', instructions: '...', tools: [] });
|
|
30
|
+
const result = await runner.run(agent, 'Hello!');
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Built-in tools
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { ToolRegistry } from '@mast-ai/core';
|
|
37
|
+
import { addAllBuiltInAITools } from '@mast-ai/built-in-ai';
|
|
38
|
+
|
|
39
|
+
const registry = new ToolRegistry();
|
|
40
|
+
addAllBuiltInAITools(registry);
|
|
41
|
+
// Registers: summarize, translate, detectLanguage, proofread
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or register them individually: `SummarizeTool`, `TranslateTool`, `DetectLanguageTool`, `ProofreadTool`.
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
Apache-2.0. Copyright 2026 Andre Cipriani Bandarra.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { LlmAdapter, AdapterRequest, AdapterResponse, AdapterStreamChunk } from '@mast-ai/core';
|
|
2
|
+
/** Options for {@link BuiltInAIAdapter}. */
|
|
3
|
+
export interface BuiltInAIAdapterOptions {
|
|
4
|
+
/** Called during model download with bytes loaded and total bytes. */
|
|
5
|
+
onDownloadProgress?: (progress: {
|
|
6
|
+
loaded: number;
|
|
7
|
+
total: number;
|
|
8
|
+
}) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* LlmAdapter backed by the browser's Prompt API (on-device model).
|
|
12
|
+
*
|
|
13
|
+
* Limitation: tool calling is not supported. The Prompt API has no native
|
|
14
|
+
* mechanism for structured tool invocation — `toolCalls` will always be `[]`.
|
|
15
|
+
*/
|
|
16
|
+
export declare class BuiltInAIAdapter implements LlmAdapter {
|
|
17
|
+
private readonly options;
|
|
18
|
+
private cachedSession;
|
|
19
|
+
private cachedHistory;
|
|
20
|
+
/** @param options - Optional configuration such as a download-progress callback. */
|
|
21
|
+
constructor(options?: BuiltInAIAdapterOptions);
|
|
22
|
+
/** {@inheritDoc LlmAdapter.generate} */
|
|
23
|
+
generate(request: AdapterRequest): Promise<AdapterResponse>;
|
|
24
|
+
/** {@inheritDoc LlmAdapter.generateStream} */
|
|
25
|
+
generateStream(request: AdapterRequest): AsyncIterable<AdapterStreamChunk>;
|
|
26
|
+
private acquireSession;
|
|
27
|
+
private isCacheHit;
|
|
28
|
+
private invalidateCache;
|
|
29
|
+
}
|
|
30
|
+
/** Returns the availability of the browser's on-device language model via `LanguageModel.availability()`. */
|
|
31
|
+
export declare function checkAvailability(): Promise<import("./types.js").LanguageModelAvailability>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { AdapterError } from '@mast-ai/core';
|
|
4
|
+
/**
|
|
5
|
+
* LlmAdapter backed by the browser's Prompt API (on-device model).
|
|
6
|
+
*
|
|
7
|
+
* Limitation: tool calling is not supported. The Prompt API has no native
|
|
8
|
+
* mechanism for structured tool invocation — `toolCalls` will always be `[]`.
|
|
9
|
+
*/
|
|
10
|
+
export class BuiltInAIAdapter {
|
|
11
|
+
options;
|
|
12
|
+
cachedSession = null;
|
|
13
|
+
cachedHistory = [];
|
|
14
|
+
/** @param options - Optional configuration such as a download-progress callback. */
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
/** {@inheritDoc LlmAdapter.generate} */
|
|
19
|
+
async generate(request) {
|
|
20
|
+
if (request.tools.length > 0) {
|
|
21
|
+
console.warn('BuiltInAIAdapter: tool calling is not supported — tools will never be invoked.');
|
|
22
|
+
}
|
|
23
|
+
const { session, lastMessage } = await this.acquireSession(request);
|
|
24
|
+
try {
|
|
25
|
+
const input = messageToString(lastMessage);
|
|
26
|
+
const text = await session.prompt(input, { signal: request.signal });
|
|
27
|
+
this.cachedHistory = [
|
|
28
|
+
...request.messages,
|
|
29
|
+
{ role: 'assistant', content: { type: 'text', text } },
|
|
30
|
+
];
|
|
31
|
+
return { text, toolCalls: [] };
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
this.invalidateCache();
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** {@inheritDoc LlmAdapter.generateStream} */
|
|
39
|
+
async *generateStream(request) {
|
|
40
|
+
if (request.tools.length > 0) {
|
|
41
|
+
console.warn('BuiltInAIAdapter: tool calling is not supported — tools will never be invoked.');
|
|
42
|
+
}
|
|
43
|
+
const { session, lastMessage } = await this.acquireSession(request);
|
|
44
|
+
try {
|
|
45
|
+
const input = messageToString(lastMessage);
|
|
46
|
+
const stream = session.promptStreaming(input, { signal: request.signal });
|
|
47
|
+
const reader = stream.getReader();
|
|
48
|
+
let fullText = '';
|
|
49
|
+
try {
|
|
50
|
+
while (true) {
|
|
51
|
+
const { done, value } = await reader.read();
|
|
52
|
+
if (done)
|
|
53
|
+
break;
|
|
54
|
+
fullText += value;
|
|
55
|
+
yield { type: 'text_delta', delta: value };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
reader.releaseLock();
|
|
60
|
+
}
|
|
61
|
+
this.cachedHistory = [
|
|
62
|
+
...request.messages,
|
|
63
|
+
{ role: 'assistant', content: { type: 'text', text: fullText } },
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
this.invalidateCache();
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async acquireSession(request) {
|
|
72
|
+
if (request.messages.length === 0) {
|
|
73
|
+
throw new AdapterError('Request must contain at least one message.');
|
|
74
|
+
}
|
|
75
|
+
const lastMessage = request.messages[request.messages.length - 1];
|
|
76
|
+
const history = request.messages.slice(0, -1);
|
|
77
|
+
if (this.isCacheHit(history)) {
|
|
78
|
+
return { session: this.cachedSession, lastMessage };
|
|
79
|
+
}
|
|
80
|
+
this.invalidateCache();
|
|
81
|
+
if (typeof LanguageModel === 'undefined') {
|
|
82
|
+
throw new AdapterError('Prompt API is not supported in this browser.');
|
|
83
|
+
}
|
|
84
|
+
const availability = await LanguageModel.availability();
|
|
85
|
+
if (availability === 'unavailable') {
|
|
86
|
+
throw new AdapterError('Built-in AI model is unavailable on this device.');
|
|
87
|
+
}
|
|
88
|
+
if (availability === 'after-download' || availability === 'downloading') {
|
|
89
|
+
throw new AdapterError(`Built-in AI model is not ready (status: "${availability}"). Wait for the model to finish downloading.`);
|
|
90
|
+
}
|
|
91
|
+
const initialPrompts = buildInitialPrompts(request.system, history);
|
|
92
|
+
const session = await LanguageModel.create({
|
|
93
|
+
signal: request.signal,
|
|
94
|
+
initialPrompts,
|
|
95
|
+
monitor: this.options.onDownloadProgress
|
|
96
|
+
? (m) => {
|
|
97
|
+
m.addEventListener('downloadprogress', (e) => {
|
|
98
|
+
const evt = e;
|
|
99
|
+
this.options.onDownloadProgress({ loaded: evt.loaded, total: evt.total });
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
: undefined,
|
|
103
|
+
});
|
|
104
|
+
if (session.contextUsage >= session.contextWindow) {
|
|
105
|
+
session.destroy();
|
|
106
|
+
throw new AdapterError("Conversation history exceeds the model's context window.");
|
|
107
|
+
}
|
|
108
|
+
this.cachedSession = session;
|
|
109
|
+
this.cachedHistory = history;
|
|
110
|
+
return { session, lastMessage };
|
|
111
|
+
}
|
|
112
|
+
isCacheHit(history) {
|
|
113
|
+
if (!this.cachedSession)
|
|
114
|
+
return false;
|
|
115
|
+
// cachedHistory = all prior messages + last assistant response.
|
|
116
|
+
// history = all messages except the new user turn = cachedHistory when cache is warm.
|
|
117
|
+
if (history.length !== this.cachedHistory.length)
|
|
118
|
+
return false;
|
|
119
|
+
if (history.length === 0)
|
|
120
|
+
return true;
|
|
121
|
+
const last = history[history.length - 1];
|
|
122
|
+
const cachedLast = this.cachedHistory[this.cachedHistory.length - 1];
|
|
123
|
+
return JSON.stringify(last) === JSON.stringify(cachedLast);
|
|
124
|
+
}
|
|
125
|
+
invalidateCache() {
|
|
126
|
+
this.cachedSession?.destroy();
|
|
127
|
+
this.cachedSession = null;
|
|
128
|
+
this.cachedHistory = [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function buildInitialPrompts(system, history) {
|
|
132
|
+
const prompts = [];
|
|
133
|
+
if (system) {
|
|
134
|
+
prompts.push({ role: 'system', content: system });
|
|
135
|
+
}
|
|
136
|
+
for (const msg of history) {
|
|
137
|
+
prompts.push({ role: msg.role, content: messageToString(msg) });
|
|
138
|
+
}
|
|
139
|
+
return prompts;
|
|
140
|
+
}
|
|
141
|
+
function messageToString(message) {
|
|
142
|
+
if (message.content.type === 'text') {
|
|
143
|
+
return message.content.text;
|
|
144
|
+
}
|
|
145
|
+
// tool_calls and tool_result have no text representation for the Prompt API
|
|
146
|
+
return '';
|
|
147
|
+
}
|
|
148
|
+
/** Returns the availability of the browser's on-device language model via `LanguageModel.availability()`. */
|
|
149
|
+
export async function checkAvailability() {
|
|
150
|
+
return LanguageModel.availability();
|
|
151
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { BuiltInAIAdapter, checkAvailability } from './BuiltInAIAdapter.js';
|
|
2
|
+
export type { BuiltInAIAdapterOptions } from './BuiltInAIAdapter.js';
|
|
3
|
+
export type { LanguageModelAvailability } from './types.js';
|
|
4
|
+
export { SummarizeTool } from './tools/summarize.js';
|
|
5
|
+
export type { SummarizeToolOptions, SummarizeArgs } from './tools/summarize.js';
|
|
6
|
+
export { DetectLanguageTool } from './tools/detectLanguage.js';
|
|
7
|
+
export type { DetectLanguageToolOptions, DetectLanguageArgs } from './tools/detectLanguage.js';
|
|
8
|
+
export { TranslateTool } from './tools/translate.js';
|
|
9
|
+
export type { TranslateToolOptions, TranslateArgs } from './tools/translate.js';
|
|
10
|
+
export { ProofreadTool } from './tools/proofread.js';
|
|
11
|
+
export type { ProofreadToolOptions, ProofreadArgs, ProofreadCorrection, ProofreadResult, } from './tools/proofread.js';
|
|
12
|
+
export { addAllBuiltInAITools } from './tools/index.js';
|
|
13
|
+
export type { AddAllBuiltInAIToolsOptions } from './tools/index.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
export { BuiltInAIAdapter, checkAvailability } from './BuiltInAIAdapter.js';
|
|
4
|
+
export { SummarizeTool } from './tools/summarize.js';
|
|
5
|
+
export { DetectLanguageTool } from './tools/detectLanguage.js';
|
|
6
|
+
export { TranslateTool } from './tools/translate.js';
|
|
7
|
+
export { ProofreadTool } from './tools/proofread.js';
|
|
8
|
+
export { addAllBuiltInAITools } from './tools/index.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Tool, ToolDefinition, ToolContext, ToolRegistry } from '@mast-ai/core';
|
|
2
|
+
import type { LanguageDetectionResult } from '../types.js';
|
|
3
|
+
/** Arguments passed by the model when invoking the `detectLanguage` tool. */
|
|
4
|
+
export interface DetectLanguageArgs {
|
|
5
|
+
/** The text whose language should be detected. */
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
/** Options for {@link DetectLanguageTool}. */
|
|
9
|
+
export interface DetectLanguageToolOptions {
|
|
10
|
+
/** Called during model download with bytes loaded and total bytes. */
|
|
11
|
+
onDownloadProgress?: (progress: {
|
|
12
|
+
loaded: number;
|
|
13
|
+
total: number;
|
|
14
|
+
}) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* {@link Tool} that identifies the language of a piece of text using the
|
|
18
|
+
* browser's Language Detector API.
|
|
19
|
+
*
|
|
20
|
+
* Use {@link DetectLanguageTool.addToRegistry} to register the tool — direct
|
|
21
|
+
* construction is not recommended because the session is created asynchronously.
|
|
22
|
+
*/
|
|
23
|
+
export declare class DetectLanguageTool implements Tool<DetectLanguageArgs, LanguageDetectionResult> {
|
|
24
|
+
private session;
|
|
25
|
+
/**
|
|
26
|
+
* Registers a `DetectLanguageTool` instance into `registry` once the
|
|
27
|
+
* underlying Language Detector session is ready.
|
|
28
|
+
*
|
|
29
|
+
* Throws immediately if the Language Detector API is unsupported or unavailable.
|
|
30
|
+
* The tool is silently skipped if background session creation fails.
|
|
31
|
+
*/
|
|
32
|
+
static addToRegistry(registry: ToolRegistry, options?: DetectLanguageToolOptions): Promise<void>;
|
|
33
|
+
/** {@inheritDoc Tool.definition} */
|
|
34
|
+
definition(): ToolDefinition;
|
|
35
|
+
/** {@inheritDoc Tool.call} */
|
|
36
|
+
call(args: DetectLanguageArgs, context: ToolContext): Promise<LanguageDetectionResult>;
|
|
37
|
+
private acquireSession;
|
|
38
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { AdapterError } from '@mast-ai/core';
|
|
4
|
+
/**
|
|
5
|
+
* {@link Tool} that identifies the language of a piece of text using the
|
|
6
|
+
* browser's Language Detector API.
|
|
7
|
+
*
|
|
8
|
+
* Use {@link DetectLanguageTool.addToRegistry} to register the tool — direct
|
|
9
|
+
* construction is not recommended because the session is created asynchronously.
|
|
10
|
+
*/
|
|
11
|
+
export class DetectLanguageTool {
|
|
12
|
+
session = null;
|
|
13
|
+
/**
|
|
14
|
+
* Registers a `DetectLanguageTool` instance into `registry` once the
|
|
15
|
+
* underlying Language Detector session is ready.
|
|
16
|
+
*
|
|
17
|
+
* Throws immediately if the Language Detector API is unsupported or unavailable.
|
|
18
|
+
* The tool is silently skipped if background session creation fails.
|
|
19
|
+
*/
|
|
20
|
+
static async addToRegistry(registry, options) {
|
|
21
|
+
if (typeof LanguageDetector === 'undefined') {
|
|
22
|
+
throw new AdapterError('Language Detector API is not supported in this browser.');
|
|
23
|
+
}
|
|
24
|
+
const availability = await LanguageDetector.availability();
|
|
25
|
+
if (availability === 'unavailable') {
|
|
26
|
+
throw new AdapterError('Language Detector API is unavailable on this device.');
|
|
27
|
+
}
|
|
28
|
+
const tool = new DetectLanguageTool();
|
|
29
|
+
const monitor = options?.onDownloadProgress
|
|
30
|
+
? (m) => {
|
|
31
|
+
m.addEventListener('downloadprogress', (e) => {
|
|
32
|
+
const evt = e;
|
|
33
|
+
options.onDownloadProgress({ loaded: evt.loaded, total: evt.total });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
: undefined;
|
|
37
|
+
LanguageDetector.create({ monitor })
|
|
38
|
+
.then((session) => {
|
|
39
|
+
tool.session = session;
|
|
40
|
+
registry.register(tool);
|
|
41
|
+
})
|
|
42
|
+
.catch(() => {
|
|
43
|
+
// Background creation failed — tool remains unregistered.
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** {@inheritDoc Tool.definition} */
|
|
47
|
+
definition() {
|
|
48
|
+
return {
|
|
49
|
+
name: 'detectLanguage',
|
|
50
|
+
description: 'Detect the language of a piece of text. ' +
|
|
51
|
+
"Returns the most likely BCP 47 language tag (e.g. 'en', 'fr', 'ja') " +
|
|
52
|
+
'and a confidence score between 0 and 1.',
|
|
53
|
+
parameters: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
text: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: 'The text whose language should be detected.',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
required: ['text'],
|
|
62
|
+
},
|
|
63
|
+
scope: 'read',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** {@inheritDoc Tool.call} */
|
|
67
|
+
async call(args, context) {
|
|
68
|
+
if (typeof LanguageDetector === 'undefined') {
|
|
69
|
+
throw new AdapterError('Language Detector API is not supported in this browser.');
|
|
70
|
+
}
|
|
71
|
+
const session = await this.acquireSession(context);
|
|
72
|
+
const results = await session.detect(args.text, { signal: context.signal });
|
|
73
|
+
return results[0] ?? { detectedLanguage: null, confidence: 0 };
|
|
74
|
+
}
|
|
75
|
+
async acquireSession(context) {
|
|
76
|
+
if (this.session) {
|
|
77
|
+
return this.session;
|
|
78
|
+
}
|
|
79
|
+
this.session = await LanguageDetector.create({ signal: context.signal });
|
|
80
|
+
return this.session;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ToolRegistry } from '@mast-ai/core';
|
|
2
|
+
/** Options for {@link addAllBuiltInAITools}. */
|
|
3
|
+
export interface AddAllBuiltInAIToolsOptions {
|
|
4
|
+
/** Called during model download with the tool name and bytes loaded / total bytes. */
|
|
5
|
+
onDownloadProgress?: (tool: string, progress: {
|
|
6
|
+
loaded: number;
|
|
7
|
+
total: number;
|
|
8
|
+
}) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Convenience helper that registers all available built-in AI tools into `registry`.
|
|
12
|
+
*
|
|
13
|
+
* Uses `Promise.allSettled` internally so a single unavailable tool does not
|
|
14
|
+
* prevent the others from being registered.
|
|
15
|
+
*/
|
|
16
|
+
export declare function addAllBuiltInAITools(registry: ToolRegistry, options?: AddAllBuiltInAIToolsOptions): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { SummarizeTool } from './summarize.js';
|
|
4
|
+
import { DetectLanguageTool } from './detectLanguage.js';
|
|
5
|
+
import { TranslateTool } from './translate.js';
|
|
6
|
+
import { ProofreadTool } from './proofread.js';
|
|
7
|
+
/**
|
|
8
|
+
* Convenience helper that registers all available built-in AI tools into `registry`.
|
|
9
|
+
*
|
|
10
|
+
* Uses `Promise.allSettled` internally so a single unavailable tool does not
|
|
11
|
+
* prevent the others from being registered.
|
|
12
|
+
*/
|
|
13
|
+
export async function addAllBuiltInAITools(registry, options) {
|
|
14
|
+
await Promise.allSettled([
|
|
15
|
+
SummarizeTool.addToRegistry(registry, {
|
|
16
|
+
onDownloadProgress: options?.onDownloadProgress
|
|
17
|
+
? (p) => options.onDownloadProgress('summarize', p)
|
|
18
|
+
: undefined,
|
|
19
|
+
}),
|
|
20
|
+
DetectLanguageTool.addToRegistry(registry, {
|
|
21
|
+
onDownloadProgress: options?.onDownloadProgress
|
|
22
|
+
? (p) => options.onDownloadProgress('detectLanguage', p)
|
|
23
|
+
: undefined,
|
|
24
|
+
}),
|
|
25
|
+
TranslateTool.addToRegistry(registry, {
|
|
26
|
+
onDownloadProgress: options?.onDownloadProgress
|
|
27
|
+
? (p) => options.onDownloadProgress('translate', p)
|
|
28
|
+
: undefined,
|
|
29
|
+
}),
|
|
30
|
+
ProofreadTool.addToRegistry(registry, {
|
|
31
|
+
onDownloadProgress: options?.onDownloadProgress
|
|
32
|
+
? (p) => options.onDownloadProgress('proofread', p)
|
|
33
|
+
: undefined,
|
|
34
|
+
}),
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Tool, ToolDefinition, ToolContext, ToolRegistry } from '@mast-ai/core';
|
|
2
|
+
import type { ProofreadCorrection, ProofreadResult } from '../types.js';
|
|
3
|
+
export type { ProofreadCorrection, ProofreadResult };
|
|
4
|
+
/** Arguments passed by the model when invoking the `proofread` tool. */
|
|
5
|
+
export interface ProofreadArgs {
|
|
6
|
+
/** The text to proofread. */
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
/** Options for {@link ProofreadTool}. */
|
|
10
|
+
export interface ProofreadToolOptions {
|
|
11
|
+
/** Called during model download with bytes loaded / total. */
|
|
12
|
+
onDownloadProgress?: (progress: {
|
|
13
|
+
loaded: number;
|
|
14
|
+
total: number;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* {@link Tool} that checks text for spelling and grammar errors using the
|
|
19
|
+
* browser's Proofreader API.
|
|
20
|
+
*
|
|
21
|
+
* Use {@link ProofreadTool.addToRegistry} to register the tool — direct
|
|
22
|
+
* construction is not recommended.
|
|
23
|
+
*
|
|
24
|
+
* Session creation strategy:
|
|
25
|
+
* - `"available"` — session is created eagerly in `addToRegistry` and reused.
|
|
26
|
+
* - `"downloadable"` / `"downloading"` — session is created lazily on the first
|
|
27
|
+
* `call()`, which must occur within a user-gesture context (browser requirement
|
|
28
|
+
* for triggering a model download).
|
|
29
|
+
*/
|
|
30
|
+
export declare class ProofreadTool implements Tool<ProofreadArgs, ProofreadResult> {
|
|
31
|
+
private readonly options?;
|
|
32
|
+
private session;
|
|
33
|
+
constructor(options?: ProofreadToolOptions | undefined);
|
|
34
|
+
/**
|
|
35
|
+
* Registers a `ProofreadTool` instance into `registry`.
|
|
36
|
+
*
|
|
37
|
+
* Throws if the Proofreader API global is absent or reports `"unavailable"`.
|
|
38
|
+
* When the model is already available, the session is created immediately.
|
|
39
|
+
* When a download is required, session creation is deferred to the first `call()`.
|
|
40
|
+
*/
|
|
41
|
+
static addToRegistry(registry: ToolRegistry, options?: ProofreadToolOptions): Promise<void>;
|
|
42
|
+
/** {@inheritDoc Tool.definition} */
|
|
43
|
+
definition(): ToolDefinition;
|
|
44
|
+
/** {@inheritDoc Tool.call} */
|
|
45
|
+
call(args: ProofreadArgs, context: ToolContext): Promise<ProofreadResult>;
|
|
46
|
+
private buildMonitor;
|
|
47
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { AdapterError } from '@mast-ai/core';
|
|
4
|
+
/**
|
|
5
|
+
* {@link Tool} that checks text for spelling and grammar errors using the
|
|
6
|
+
* browser's Proofreader API.
|
|
7
|
+
*
|
|
8
|
+
* Use {@link ProofreadTool.addToRegistry} to register the tool — direct
|
|
9
|
+
* construction is not recommended.
|
|
10
|
+
*
|
|
11
|
+
* Session creation strategy:
|
|
12
|
+
* - `"available"` — session is created eagerly in `addToRegistry` and reused.
|
|
13
|
+
* - `"downloadable"` / `"downloading"` — session is created lazily on the first
|
|
14
|
+
* `call()`, which must occur within a user-gesture context (browser requirement
|
|
15
|
+
* for triggering a model download).
|
|
16
|
+
*/
|
|
17
|
+
export class ProofreadTool {
|
|
18
|
+
options;
|
|
19
|
+
session = null;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Registers a `ProofreadTool` instance into `registry`.
|
|
25
|
+
*
|
|
26
|
+
* Throws if the Proofreader API global is absent or reports `"unavailable"`.
|
|
27
|
+
* When the model is already available, the session is created immediately.
|
|
28
|
+
* When a download is required, session creation is deferred to the first `call()`.
|
|
29
|
+
*/
|
|
30
|
+
static async addToRegistry(registry, options) {
|
|
31
|
+
if (typeof Proofreader === 'undefined') {
|
|
32
|
+
throw new AdapterError('Proofreader API is not supported in this browser.');
|
|
33
|
+
}
|
|
34
|
+
const availability = await Proofreader.availability();
|
|
35
|
+
if (availability === 'unavailable') {
|
|
36
|
+
throw new AdapterError('Proofreader API is not available on this device.');
|
|
37
|
+
}
|
|
38
|
+
const tool = new ProofreadTool(options);
|
|
39
|
+
if (availability === 'available') {
|
|
40
|
+
tool.session = await Proofreader.create();
|
|
41
|
+
}
|
|
42
|
+
// For "downloadable"/"downloading", session creation is deferred to call().
|
|
43
|
+
registry.register(tool);
|
|
44
|
+
}
|
|
45
|
+
/** {@inheritDoc Tool.definition} */
|
|
46
|
+
definition() {
|
|
47
|
+
return {
|
|
48
|
+
name: 'proofread',
|
|
49
|
+
description: 'Check a piece of text for spelling and grammar errors using an on-device AI model. ' +
|
|
50
|
+
'Returns a list of corrections, each with the problematic span and the corrected replacement.',
|
|
51
|
+
parameters: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
text: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'The text to proofread.',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
required: ['text'],
|
|
60
|
+
},
|
|
61
|
+
scope: 'read',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** {@inheritDoc Tool.call} */
|
|
65
|
+
async call(args, context) {
|
|
66
|
+
if (typeof Proofreader === 'undefined') {
|
|
67
|
+
throw new AdapterError('Proofreader API is not supported in this browser.');
|
|
68
|
+
}
|
|
69
|
+
if (!this.session) {
|
|
70
|
+
this.session = await Proofreader.create({ monitor: this.buildMonitor() });
|
|
71
|
+
}
|
|
72
|
+
return this.session.proofread(args.text, { signal: context.signal });
|
|
73
|
+
}
|
|
74
|
+
buildMonitor() {
|
|
75
|
+
if (!this.options?.onDownloadProgress)
|
|
76
|
+
return undefined;
|
|
77
|
+
const cb = this.options.onDownloadProgress;
|
|
78
|
+
return (m) => {
|
|
79
|
+
m.addEventListener('downloadprogress', (e) => {
|
|
80
|
+
const evt = e;
|
|
81
|
+
cb({ loaded: evt.loaded, total: evt.total });
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Tool, ToolDefinition, ToolContext, ToolRegistry } from '@mast-ai/core';
|
|
2
|
+
import type { SummarizerType, SummarizerFormat, SummarizerLength } from '../types.js';
|
|
3
|
+
/** Arguments passed by the model when invoking the `summarize` tool. */
|
|
4
|
+
export interface SummarizeArgs {
|
|
5
|
+
/** The full text to summarize. */
|
|
6
|
+
text: string;
|
|
7
|
+
type?: SummarizerType;
|
|
8
|
+
format?: SummarizerFormat;
|
|
9
|
+
length?: SummarizerLength;
|
|
10
|
+
/** Optional hint to guide the summarization (e.g. `"meeting transcript"`). */
|
|
11
|
+
context?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Options for {@link SummarizeTool}. */
|
|
14
|
+
export interface SummarizeToolOptions {
|
|
15
|
+
/** Called during model download with bytes loaded and total bytes. */
|
|
16
|
+
onDownloadProgress?: (progress: {
|
|
17
|
+
loaded: number;
|
|
18
|
+
total: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* {@link Tool} that condenses text using the browser's Summarizer API.
|
|
23
|
+
*
|
|
24
|
+
* Use {@link SummarizeTool.addToRegistry} to register the tool — direct
|
|
25
|
+
* construction is not recommended because the session is created asynchronously.
|
|
26
|
+
*/
|
|
27
|
+
export declare class SummarizeTool implements Tool<SummarizeArgs, string> {
|
|
28
|
+
private cached;
|
|
29
|
+
/**
|
|
30
|
+
* Registers a `SummarizeTool` instance into `registry` once the underlying
|
|
31
|
+
* Summarizer session is ready.
|
|
32
|
+
*
|
|
33
|
+
* Throws immediately if the Summarizer API is unsupported or unavailable.
|
|
34
|
+
* The tool is silently skipped if background session creation fails.
|
|
35
|
+
*/
|
|
36
|
+
static addToRegistry(registry: ToolRegistry, options?: SummarizeToolOptions): Promise<void>;
|
|
37
|
+
/** {@inheritDoc Tool.definition} */
|
|
38
|
+
definition(): ToolDefinition;
|
|
39
|
+
/** {@inheritDoc Tool.call} */
|
|
40
|
+
call(args: SummarizeArgs, context: ToolContext): Promise<string>;
|
|
41
|
+
private acquireSession;
|
|
42
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { AdapterError } from '@mast-ai/core';
|
|
4
|
+
/**
|
|
5
|
+
* {@link Tool} that condenses text using the browser's Summarizer API.
|
|
6
|
+
*
|
|
7
|
+
* Use {@link SummarizeTool.addToRegistry} to register the tool — direct
|
|
8
|
+
* construction is not recommended because the session is created asynchronously.
|
|
9
|
+
*/
|
|
10
|
+
export class SummarizeTool {
|
|
11
|
+
cached = null;
|
|
12
|
+
/**
|
|
13
|
+
* Registers a `SummarizeTool` instance into `registry` once the underlying
|
|
14
|
+
* Summarizer session is ready.
|
|
15
|
+
*
|
|
16
|
+
* Throws immediately if the Summarizer API is unsupported or unavailable.
|
|
17
|
+
* The tool is silently skipped if background session creation fails.
|
|
18
|
+
*/
|
|
19
|
+
static async addToRegistry(registry, options) {
|
|
20
|
+
if (typeof Summarizer === 'undefined') {
|
|
21
|
+
throw new AdapterError('Summarizer API is not supported in this browser.');
|
|
22
|
+
}
|
|
23
|
+
const availability = await Summarizer.availability();
|
|
24
|
+
if (availability === 'unavailable') {
|
|
25
|
+
throw new AdapterError('Summarizer API is unavailable on this device.');
|
|
26
|
+
}
|
|
27
|
+
const tool = new SummarizeTool();
|
|
28
|
+
const monitor = options?.onDownloadProgress
|
|
29
|
+
? (m) => {
|
|
30
|
+
m.addEventListener('downloadprogress', (e) => {
|
|
31
|
+
const evt = e;
|
|
32
|
+
options.onDownloadProgress({ loaded: evt.loaded, total: evt.total });
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
: undefined;
|
|
36
|
+
// Return immediately — session creation (including any download) happens in the background.
|
|
37
|
+
// The tool is registered once the session is ready.
|
|
38
|
+
Summarizer.create({ monitor })
|
|
39
|
+
.then((session) => {
|
|
40
|
+
tool.cached = { session, type: undefined, format: undefined, length: undefined };
|
|
41
|
+
registry.register(tool);
|
|
42
|
+
})
|
|
43
|
+
.catch(() => {
|
|
44
|
+
// Background creation failed — tool remains unregistered.
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** {@inheritDoc Tool.definition} */
|
|
48
|
+
definition() {
|
|
49
|
+
return {
|
|
50
|
+
name: 'summarize',
|
|
51
|
+
description: 'Condense a long piece of text into a shorter form. ' +
|
|
52
|
+
'Use when the user asks to summarize, shorten, or extract the key points of a document, ' +
|
|
53
|
+
'article, or any other lengthy content.',
|
|
54
|
+
parameters: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
text: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'The full text to summarize.',
|
|
60
|
+
},
|
|
61
|
+
type: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
enum: ['key-points', 'tldr', 'teaser', 'headline'],
|
|
64
|
+
description: 'Shape of the summary. ' +
|
|
65
|
+
"'key-points' returns a bullet-point list of the main ideas (default). " +
|
|
66
|
+
"'tldr' returns a short paragraph capturing the essence. " +
|
|
67
|
+
"'teaser' returns an engaging excerpt meant to entice reading the full text. " +
|
|
68
|
+
"'headline' returns a single-sentence title.",
|
|
69
|
+
},
|
|
70
|
+
format: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
enum: ['plain-text', 'markdown'],
|
|
73
|
+
description: "Output format. Use 'markdown' when the result will be rendered; " +
|
|
74
|
+
"'plain-text' otherwise. Defaults to 'plain-text'.",
|
|
75
|
+
},
|
|
76
|
+
length: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
enum: ['short', 'medium', 'long'],
|
|
79
|
+
description: 'Target length of the summary relative to the input. ' + "Defaults to 'medium'.",
|
|
80
|
+
},
|
|
81
|
+
context: {
|
|
82
|
+
type: 'string',
|
|
83
|
+
description: "Optional hint to guide the summarization, e.g. 'scientific paper', " +
|
|
84
|
+
"'meeting transcript', or 'news article for a general audience'.",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
required: ['text'],
|
|
88
|
+
},
|
|
89
|
+
scope: 'read',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** {@inheritDoc Tool.call} */
|
|
93
|
+
async call(args, context) {
|
|
94
|
+
if (typeof Summarizer === 'undefined') {
|
|
95
|
+
throw new AdapterError('Summarizer API is not supported in this browser.');
|
|
96
|
+
}
|
|
97
|
+
const session = await this.acquireSession(args, context);
|
|
98
|
+
return session.summarize(args.text, {
|
|
99
|
+
context: args.context,
|
|
100
|
+
signal: context.signal,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async acquireSession(args, context) {
|
|
104
|
+
if (this.cached &&
|
|
105
|
+
this.cached.type === args.type &&
|
|
106
|
+
this.cached.format === args.format &&
|
|
107
|
+
this.cached.length === args.length) {
|
|
108
|
+
return this.cached.session;
|
|
109
|
+
}
|
|
110
|
+
const old = this.cached;
|
|
111
|
+
this.cached = null;
|
|
112
|
+
let session;
|
|
113
|
+
try {
|
|
114
|
+
session = await Summarizer.create({
|
|
115
|
+
type: args.type,
|
|
116
|
+
format: args.format,
|
|
117
|
+
length: args.length,
|
|
118
|
+
signal: context.signal,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
old?.session.destroy();
|
|
123
|
+
}
|
|
124
|
+
this.cached = {
|
|
125
|
+
session,
|
|
126
|
+
type: args.type,
|
|
127
|
+
format: args.format,
|
|
128
|
+
length: args.length,
|
|
129
|
+
};
|
|
130
|
+
return session;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Tool, ToolDefinition, ToolContext, ToolRegistry } from '@mast-ai/core';
|
|
2
|
+
/** Arguments passed by the model when invoking the `translate` tool. */
|
|
3
|
+
export interface TranslateArgs {
|
|
4
|
+
/** The text to translate. */
|
|
5
|
+
text: string;
|
|
6
|
+
/** BCP 47 language tag of the source language (e.g. `"en"`). */
|
|
7
|
+
sourceLanguage: string;
|
|
8
|
+
/** BCP 47 language tag of the target language (e.g. `"fr"`). */
|
|
9
|
+
targetLanguage: string;
|
|
10
|
+
}
|
|
11
|
+
/** Options for {@link TranslateTool}. */
|
|
12
|
+
export interface TranslateToolOptions {
|
|
13
|
+
/** Called during model download for a new language pair with the pair and bytes loaded / total. */
|
|
14
|
+
onDownloadProgress?: (progress: {
|
|
15
|
+
loaded: number;
|
|
16
|
+
total: number;
|
|
17
|
+
sourceLanguage: string;
|
|
18
|
+
targetLanguage: string;
|
|
19
|
+
}) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* {@link Tool} that translates text between languages using the browser's
|
|
23
|
+
* Translator API.
|
|
24
|
+
*
|
|
25
|
+
* Use {@link TranslateTool.addToRegistry} to register the tool — direct
|
|
26
|
+
* construction is not recommended. Sessions are created lazily per language
|
|
27
|
+
* pair on first use and cached for subsequent calls.
|
|
28
|
+
*/
|
|
29
|
+
export declare class TranslateTool implements Tool<TranslateArgs, string> {
|
|
30
|
+
private readonly options?;
|
|
31
|
+
private sessions;
|
|
32
|
+
constructor(options?: TranslateToolOptions | undefined);
|
|
33
|
+
/**
|
|
34
|
+
* Registers a `TranslateTool` instance into `registry`.
|
|
35
|
+
*
|
|
36
|
+
* Throws immediately if the Translator API global is absent. No session is
|
|
37
|
+
* created at registration time — sessions are created lazily in `call()`.
|
|
38
|
+
*/
|
|
39
|
+
static addToRegistry(registry: ToolRegistry, options?: TranslateToolOptions): Promise<void>;
|
|
40
|
+
/** {@inheritDoc Tool.definition} */
|
|
41
|
+
definition(): ToolDefinition;
|
|
42
|
+
/** {@inheritDoc Tool.call} */
|
|
43
|
+
call(args: TranslateArgs, context: ToolContext): Promise<string>;
|
|
44
|
+
private acquireSession;
|
|
45
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Copyright 2026 Andre Cipriani Bandarra
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { AdapterError } from '@mast-ai/core';
|
|
4
|
+
/**
|
|
5
|
+
* {@link Tool} that translates text between languages using the browser's
|
|
6
|
+
* Translator API.
|
|
7
|
+
*
|
|
8
|
+
* Use {@link TranslateTool.addToRegistry} to register the tool — direct
|
|
9
|
+
* construction is not recommended. Sessions are created lazily per language
|
|
10
|
+
* pair on first use and cached for subsequent calls.
|
|
11
|
+
*/
|
|
12
|
+
export class TranslateTool {
|
|
13
|
+
options;
|
|
14
|
+
sessions = new Map();
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Registers a `TranslateTool` instance into `registry`.
|
|
20
|
+
*
|
|
21
|
+
* Throws immediately if the Translator API global is absent. No session is
|
|
22
|
+
* created at registration time — sessions are created lazily in `call()`.
|
|
23
|
+
*/
|
|
24
|
+
static async addToRegistry(registry, options) {
|
|
25
|
+
if (typeof Translator === 'undefined') {
|
|
26
|
+
throw new AdapterError('Translator API is not supported in this browser.');
|
|
27
|
+
}
|
|
28
|
+
registry.register(new TranslateTool(options));
|
|
29
|
+
}
|
|
30
|
+
/** {@inheritDoc Tool.definition} */
|
|
31
|
+
definition() {
|
|
32
|
+
return {
|
|
33
|
+
name: 'translate',
|
|
34
|
+
description: 'Translate a piece of text from one language to another using an on-device AI model. ' +
|
|
35
|
+
"Languages are specified as BCP 47 tags (e.g. 'en', 'fr', 'ja').",
|
|
36
|
+
parameters: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
text: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'The text to translate.',
|
|
42
|
+
},
|
|
43
|
+
sourceLanguage: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'BCP 47 language tag of the source language (e.g. "en").',
|
|
46
|
+
},
|
|
47
|
+
targetLanguage: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'BCP 47 language tag of the target language (e.g. "fr").',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ['text', 'sourceLanguage', 'targetLanguage'],
|
|
53
|
+
},
|
|
54
|
+
scope: 'read',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** {@inheritDoc Tool.call} */
|
|
58
|
+
async call(args, context) {
|
|
59
|
+
if (typeof Translator === 'undefined') {
|
|
60
|
+
throw new AdapterError('Translator API is not supported in this browser.');
|
|
61
|
+
}
|
|
62
|
+
const session = await this.acquireSession(args.sourceLanguage, args.targetLanguage, context);
|
|
63
|
+
return session.translate(args.text, { signal: context.signal });
|
|
64
|
+
}
|
|
65
|
+
async acquireSession(sourceLanguage, targetLanguage, context) {
|
|
66
|
+
const key = `${sourceLanguage}:${targetLanguage}`;
|
|
67
|
+
const cached = this.sessions.get(key);
|
|
68
|
+
if (cached) {
|
|
69
|
+
return cached;
|
|
70
|
+
}
|
|
71
|
+
const availability = await Translator.availability({ sourceLanguage, targetLanguage });
|
|
72
|
+
if (availability === 'unavailable') {
|
|
73
|
+
throw new AdapterError(`Translation from ${sourceLanguage} to ${targetLanguage} is not available on this device.`);
|
|
74
|
+
}
|
|
75
|
+
const monitor = this.options?.onDownloadProgress
|
|
76
|
+
? (m) => {
|
|
77
|
+
m.addEventListener('downloadprogress', (e) => {
|
|
78
|
+
const evt = e;
|
|
79
|
+
this.options.onDownloadProgress({
|
|
80
|
+
loaded: evt.loaded,
|
|
81
|
+
total: evt.total,
|
|
82
|
+
sourceLanguage,
|
|
83
|
+
targetLanguage,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
: undefined;
|
|
88
|
+
const session = await Translator.create({
|
|
89
|
+
sourceLanguage,
|
|
90
|
+
targetLanguage,
|
|
91
|
+
signal: context.signal,
|
|
92
|
+
monitor,
|
|
93
|
+
});
|
|
94
|
+
this.sessions.set(key, session);
|
|
95
|
+
return session;
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Availability status of the on-device language model returned by `LanguageModel.availability()`.
|
|
3
|
+
*
|
|
4
|
+
* - `"readily"` — model is ready to use immediately.
|
|
5
|
+
* - `"after-download"` — model must be downloaded before use.
|
|
6
|
+
* - `"downloading"` — download is in progress.
|
|
7
|
+
* - `"unavailable"` — not supported on this device or browser.
|
|
8
|
+
*/
|
|
9
|
+
export type LanguageModelAvailability = 'readily' | 'after-download' | 'downloading' | 'unavailable';
|
|
10
|
+
/** A single message passed to `LanguageModel.create` as part of `initialPrompts`. */
|
|
11
|
+
export interface LanguageModelMessage {
|
|
12
|
+
role: 'user' | 'assistant' | 'system';
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
/** Options accepted by `LanguageModel.create`. */
|
|
16
|
+
export interface LanguageModelCreateOptions {
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
initialPrompts?: LanguageModelMessage[];
|
|
19
|
+
/** Callback invoked with a download progress monitor target. */
|
|
20
|
+
monitor?: (monitor: EventTarget) => void;
|
|
21
|
+
}
|
|
22
|
+
/** Options accepted by `LanguageModelSession.prompt` and `promptStreaming`. */
|
|
23
|
+
export interface LanguageModelPromptOptions {
|
|
24
|
+
signal?: AbortSignal;
|
|
25
|
+
}
|
|
26
|
+
/** A live session obtained from `LanguageModel.create`. */
|
|
27
|
+
export interface LanguageModelSession {
|
|
28
|
+
prompt(input: string, options?: LanguageModelPromptOptions): Promise<string>;
|
|
29
|
+
promptStreaming(input: string, options?: LanguageModelPromptOptions): ReadableStream<string>;
|
|
30
|
+
/** Number of tokens currently consumed in the session context. */
|
|
31
|
+
contextUsage: number;
|
|
32
|
+
/** Maximum number of tokens the session context can hold. */
|
|
33
|
+
contextWindow: number;
|
|
34
|
+
destroy(): void;
|
|
35
|
+
addEventListener(type: 'contextoverflow', listener: EventListener): void;
|
|
36
|
+
}
|
|
37
|
+
declare global {
|
|
38
|
+
const LanguageModel: {
|
|
39
|
+
availability(options?: Partial<LanguageModelCreateOptions>): Promise<LanguageModelAvailability>;
|
|
40
|
+
create(options?: LanguageModelCreateOptions): Promise<LanguageModelSession>;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Availability status of the on-device Summarizer API returned by `Summarizer.availability()`.
|
|
45
|
+
*
|
|
46
|
+
* - `"readily"` — ready to use immediately.
|
|
47
|
+
* - `"after-download"` — must be downloaded before use.
|
|
48
|
+
* - `"downloading"` — download is in progress.
|
|
49
|
+
* - `"unavailable"` — not supported on this device or browser.
|
|
50
|
+
*/
|
|
51
|
+
export type SummarizerAvailability = 'readily' | 'after-download' | 'downloading' | 'unavailable';
|
|
52
|
+
/** Shape of the summary output. */
|
|
53
|
+
export type SummarizerType = 'key-points' | 'tldr' | 'teaser' | 'headline';
|
|
54
|
+
/** Output format of the summary. */
|
|
55
|
+
export type SummarizerFormat = 'plain-text' | 'markdown';
|
|
56
|
+
/** Target length of the summary relative to the source text. */
|
|
57
|
+
export type SummarizerLength = 'short' | 'medium' | 'long';
|
|
58
|
+
/** Options accepted by `Summarizer.create`. */
|
|
59
|
+
export interface SummarizerCreateOptions {
|
|
60
|
+
type?: SummarizerType;
|
|
61
|
+
format?: SummarizerFormat;
|
|
62
|
+
length?: SummarizerLength;
|
|
63
|
+
/** Shared context prepended to every summarization call made on this session. */
|
|
64
|
+
sharedContext?: string;
|
|
65
|
+
signal?: AbortSignal;
|
|
66
|
+
/** Callback invoked with a download progress monitor target. */
|
|
67
|
+
monitor?: (monitor: EventTarget) => void;
|
|
68
|
+
}
|
|
69
|
+
/** Per-call options passed to `SummarizerSession.summarize` / `summarizeStreaming`. */
|
|
70
|
+
export interface SummarizerCallOptions {
|
|
71
|
+
/** Optional hint providing additional context for this specific call. */
|
|
72
|
+
context?: string;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}
|
|
75
|
+
/** A live session obtained from `Summarizer.create`. */
|
|
76
|
+
export interface SummarizerSession {
|
|
77
|
+
summarize(text: string, options?: SummarizerCallOptions): Promise<string>;
|
|
78
|
+
summarizeStreaming(text: string, options?: SummarizerCallOptions): ReadableStream<string>;
|
|
79
|
+
destroy(): void;
|
|
80
|
+
}
|
|
81
|
+
declare global {
|
|
82
|
+
const Summarizer: {
|
|
83
|
+
availability(options?: Partial<SummarizerCreateOptions>): Promise<SummarizerAvailability>;
|
|
84
|
+
create(options?: SummarizerCreateOptions): Promise<SummarizerSession>;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Availability status of the on-device Language Detector API.
|
|
89
|
+
*
|
|
90
|
+
* - `"readily"` — ready to use immediately.
|
|
91
|
+
* - `"after-download"` — must be downloaded before use.
|
|
92
|
+
* - `"downloading"` — download is in progress.
|
|
93
|
+
* - `"unavailable"` — not supported on this device or browser.
|
|
94
|
+
*/
|
|
95
|
+
export type LanguageDetectorAvailability = 'readily' | 'after-download' | 'downloading' | 'unavailable';
|
|
96
|
+
/** Options accepted by `LanguageDetector.create`. */
|
|
97
|
+
export interface LanguageDetectorCreateOptions {
|
|
98
|
+
signal?: AbortSignal;
|
|
99
|
+
/** Callback invoked with a download progress monitor target. */
|
|
100
|
+
monitor?: (monitor: EventTarget) => void;
|
|
101
|
+
}
|
|
102
|
+
/** A single language detection result. */
|
|
103
|
+
export interface LanguageDetectionResult {
|
|
104
|
+
/** BCP 47 language tag (e.g. `"en"`, `"fr"`), or `null` if undetermined. */
|
|
105
|
+
detectedLanguage: string | null;
|
|
106
|
+
/** Confidence score in the range [0, 1]. */
|
|
107
|
+
confidence: number;
|
|
108
|
+
}
|
|
109
|
+
/** Per-call options passed to `LanguageDetectorSession.detect`. */
|
|
110
|
+
export interface LanguageDetectorCallOptions {
|
|
111
|
+
signal?: AbortSignal;
|
|
112
|
+
}
|
|
113
|
+
/** A live session obtained from `LanguageDetector.create`. */
|
|
114
|
+
export interface LanguageDetectorSession {
|
|
115
|
+
detect(text: string, options?: LanguageDetectorCallOptions): Promise<LanguageDetectionResult[]>;
|
|
116
|
+
destroy(): void;
|
|
117
|
+
}
|
|
118
|
+
declare global {
|
|
119
|
+
const LanguageDetector: {
|
|
120
|
+
availability(options?: Partial<LanguageDetectorCreateOptions>): Promise<LanguageDetectorAvailability>;
|
|
121
|
+
create(options?: LanguageDetectorCreateOptions): Promise<LanguageDetectorSession>;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Availability status of the on-device Translator API for a given language pair.
|
|
126
|
+
*
|
|
127
|
+
* - `"readily"` — ready to use immediately.
|
|
128
|
+
* - `"after-download"` — must be downloaded before use.
|
|
129
|
+
* - `"downloading"` — download is in progress.
|
|
130
|
+
* - `"unavailable"` — not supported on this device or browser.
|
|
131
|
+
*/
|
|
132
|
+
export type TranslatorAvailability = 'readily' | 'after-download' | 'downloading' | 'unavailable';
|
|
133
|
+
/** Options for checking Translator availability for a specific language pair. */
|
|
134
|
+
export interface TranslatorAvailabilityOptions {
|
|
135
|
+
sourceLanguage: string;
|
|
136
|
+
targetLanguage: string;
|
|
137
|
+
}
|
|
138
|
+
/** Options accepted by `Translator.create`. */
|
|
139
|
+
export interface TranslatorCreateOptions {
|
|
140
|
+
sourceLanguage: string;
|
|
141
|
+
targetLanguage: string;
|
|
142
|
+
signal?: AbortSignal;
|
|
143
|
+
/** Callback invoked with a download progress monitor target. */
|
|
144
|
+
monitor?: (monitor: EventTarget) => void;
|
|
145
|
+
}
|
|
146
|
+
/** Per-call options passed to `TranslatorSession.translate`. */
|
|
147
|
+
export interface TranslatorCallOptions {
|
|
148
|
+
signal?: AbortSignal;
|
|
149
|
+
}
|
|
150
|
+
/** A live session obtained from `Translator.create`. */
|
|
151
|
+
export interface TranslatorSession {
|
|
152
|
+
translate(text: string, options?: TranslatorCallOptions): Promise<string>;
|
|
153
|
+
destroy(): void;
|
|
154
|
+
}
|
|
155
|
+
declare global {
|
|
156
|
+
const Translator: {
|
|
157
|
+
availability(options: TranslatorAvailabilityOptions): Promise<TranslatorAvailability>;
|
|
158
|
+
create(options: TranslatorCreateOptions): Promise<TranslatorSession>;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Availability status of the on-device Proofreader API.
|
|
163
|
+
*
|
|
164
|
+
* - `"readily"` — ready to use immediately.
|
|
165
|
+
* - `"after-download"` — must be downloaded before use.
|
|
166
|
+
* - `"downloading"` — download is in progress.
|
|
167
|
+
* - `"unavailable"` — not supported on this device or browser.
|
|
168
|
+
*/
|
|
169
|
+
export type ProofreaderAvailability = 'available' | 'downloadable' | 'downloading' | 'unavailable';
|
|
170
|
+
/** Options accepted by `Proofreader.create`. */
|
|
171
|
+
export interface ProofreaderCreateOptions {
|
|
172
|
+
signal?: AbortSignal;
|
|
173
|
+
/** Callback invoked with a download progress monitor target. */
|
|
174
|
+
monitor?: (monitor: EventTarget) => void;
|
|
175
|
+
}
|
|
176
|
+
/** Per-call options passed to `ProofreaderSession.proofread`. */
|
|
177
|
+
export interface ProofreaderCallOptions {
|
|
178
|
+
signal?: AbortSignal;
|
|
179
|
+
}
|
|
180
|
+
/** A single correction returned by the Proofreader API. */
|
|
181
|
+
export interface ProofreadCorrection {
|
|
182
|
+
/** The corrected replacement text. */
|
|
183
|
+
correction: string;
|
|
184
|
+
/** Start index of the error span in the original input (inclusive). */
|
|
185
|
+
startIndex: number;
|
|
186
|
+
/** End index of the error span in the original input (exclusive). */
|
|
187
|
+
endIndex: number;
|
|
188
|
+
}
|
|
189
|
+
/** The result returned by `ProofreaderSession.proofread`. */
|
|
190
|
+
export interface ProofreadResult {
|
|
191
|
+
/** The input text with all corrections applied. */
|
|
192
|
+
correctedInput: string;
|
|
193
|
+
/** Individual corrections found in the input. */
|
|
194
|
+
corrections: ProofreadCorrection[];
|
|
195
|
+
}
|
|
196
|
+
/** A live session obtained from `Proofreader.create`. */
|
|
197
|
+
export interface ProofreaderSession {
|
|
198
|
+
proofread(text: string, options?: ProofreaderCallOptions): Promise<ProofreadResult>;
|
|
199
|
+
destroy(): void;
|
|
200
|
+
}
|
|
201
|
+
declare global {
|
|
202
|
+
const Proofreader: {
|
|
203
|
+
availability(): Promise<ProofreaderAvailability>;
|
|
204
|
+
create(options?: ProofreaderCreateOptions): Promise<ProofreaderSession>;
|
|
205
|
+
};
|
|
206
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mast-ai/built-in-ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/andreban/mast-ai.git",
|
|
14
|
+
"directory": "packages/built-in-ai"
|
|
15
|
+
},
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"registry": "https://registry.npmjs.org",
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
22
|
+
"build": "npm run clean && tsc",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"test": "vitest",
|
|
26
|
+
"lint": "eslint src",
|
|
27
|
+
"format": "prettier --write src"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@mast-ai/core": "^0.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "~6.0.2",
|
|
34
|
+
"vitest": "^4.1.4"
|
|
35
|
+
}
|
|
36
|
+
}
|