diff --git a/README.md b/README.md
index 6dbe922..a791428 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ Both of these potential goals could pose challenges to interoperability, so we w
All three APIs share the same format: create a summarizer/writer/rewriter object customized as necessary, and call its appropriate method:
```js
-const summarizer = await ai.summarizer.create({
+const summarizer = await Summarizer.create({
sharedContext: "An article from the Daily Economic News magazine",
type: "headline",
length: "short"
@@ -93,7 +93,7 @@ const summary = await summarizer.summarize(articleEl.textContent, {
```
```js
-const writer = await ai.writer.create({
+const writer = await Writer.create({
tone: "formal"
});
@@ -103,7 +103,7 @@ const result = await writer.write(
```
```js
-const rewriter = await ai.rewriter.create({
+const rewriter = await Rewriter.create({
sharedContext: "A review for the Flux Capacitor 3000 from TimeMachines Inc."
});
@@ -117,7 +117,7 @@ const result = await rewriter.rewrite(reviewEl.textContent, {
All three of the APIs support streaming output, via counterpart methods `summarizeStreaming()` / `writeStreaming()` / `rewriteStreaming()` that return `ReadableStream`s of strings. A sample usage would be:
```js
-const writer = await ai.writer.create({ tone: "formal", length: "long" });
+const writer = await Writer.create({ tone: "formal", length: "long" });
const stream = await writer.writeStreaming(
"A draft for an inquiry to my bank about how to enable wire transfers on my account"
@@ -133,7 +133,7 @@ for (const chunk of stream) {
A created summarizer/writer/rewriter object can be used multiple times. **The only shared state is the initial configuration options**; the inputs do not build on each other. (See more discussion [below](#one-shot-functions-instead-of-summarizer--writer--rewriter-objects).)
```js
-const summarizer = await ai.summarize.create({ type: "tl;dr" });
+const summarizer = await Summarizer.create({ type: "tl;dr" });
const reviewSummaries = await Promise.all(
Array.from(
@@ -150,7 +150,7 @@ The default behavior for the summarizer/writer/rewriter objects assumes that the
It's better practice, if possible, to supply the `create()` method with information about the expected languages in use. This allows the implementation to download any necessary supporting material, such as fine-tunings or safety-checking models, and to immediately reject the promise returned by `create()` if the web developer needs to use languages that the browser is not capable of supporting:
```js
-const summarizer = await ai.summarize.create({
+const summarizer = await Summarizer.create({
type: "key-points",
expectedInputLanguages: ["ja", "ko"],
expectedContextLanguages: ["en", "ja", "ko"],
@@ -189,7 +189,7 @@ Whenever any API call fails due to too-large input, it is rejected with a `Quota
This allows detecting failures due to overlarge inputs and giving clear feedback to the user, with code such as the following:
```js
-const summarizer = await ai.summarizer.create();
+const summarizer = await Summarizer.create();
try {
console.log(await summarizer.summarize(potentiallyLargeInput));
@@ -205,16 +205,16 @@ try {
Note that all of the following methods can reject (or error the relevant stream) with this type of exception:
-* `ai.summarizer.create()`, if `sharedContext` is too large;
+* `Summarizer.create()`, if `sharedContext` is too large;
-* `ai.summarizer.summarize()`/`summarizeStreaming()`, if the combination of the creation-time `sharedContext`, the current method call's `input` argument, and the current method call's `context` is too large;
+* `summarize()`/`summarizeStreaming()`, if the combination of the creation-time `sharedContext`, the current method call's `input` argument, and the current method call's `context` is too large;
* Similarly for writer creation / writing, and rewriter creation / rewriting.
In some cases, instead of providing errors after the fact, the developer needs to be able to communicate to the user how close they are to the limit. For this, they can use the `inputQuota` property and the `measureInputUsage()` method on the summarizer/writer/rewriter objects:
```js
-const rewriter = await ai.rewriter.create();
+const rewriter = await Rewriter.create();
meterEl.max = rewriter.inputQuota;
textbox.addEventListener("input", () => {
@@ -264,7 +264,7 @@ An example usage is the following:
```js
const options = { type: "teaser", expectedInputLanguages: ["ja"] };
-const availability = await ai.summarizer.availability(options);
+const availability = await Summarizer.availability(options);
if (availability !== "unavailable") {
// We're good! Let's do the summarization using the built-in API.
@@ -272,7 +272,7 @@ if (availability !== "unavailable") {
console.log("Sit tight, we need to do some downloading...");
}
- const summarizer = await ai.summarizer.create(options);
+ const summarizer = await Summarizer.create(options);
console.log(await summarizer.summarize(articleEl.textContent));
} else {
// Either the API overall, or the combination of teaser + Japanese input, is not available.
@@ -286,7 +286,7 @@ if (availability !== "unavailable") {
For cases where using the API is only possible after a download, you can monitor the download progress (e.g. in order to show your users a progress bar) using code such as the following:
```js
-const writer = await ai.writer.create({
+const writer = await Writer.create({
...otherOptions,
monitor(m) {
m.addEventListener("downloadprogress", e => {
@@ -322,7 +322,7 @@ Each API comes equipped with a couple of `signal` options that accept `AbortSign
const controller = new AbortController();
stopButton.onclick = () => controller.abort();
-const rewriter = await ai.rewriter.create({ signal: controller.signal });
+const rewriter = await Rewriter.create({ signal: controller.signal });
await rewriter.rewrite(document.body.textContent, { signal: controller.signal });
```
@@ -393,12 +393,6 @@ Although the APIs contain support for streaming output, they don't support strea
However, we believe that streaming input would not be a good fit for these APIs. Attempting to summarize or rewrite input as more input streams in will likely result in multiple wasteful rounds of revision. The underlying language model technology does not support streaming input, so the implementation would be buffering the input stream anyway, then repeatedly feeding new versions of the buffered text to the language model. If a developer wants to achieve such results, they can do so themselves, at the cost of writing code which makes the wastefulness of the operation more obvious. Developers can also customize such code, e.g. by only asking for new summaries every 5 seconds (or whatever interval makes the most sense for their use case).
-### Alternative API spellings
-
-In [the TAG review of the translation and language detection APIs](https://github.com/w3ctag/design-reviews/issues/948), some TAG members suggested slightly different patterns than the `ai.something.create()` pattern, such as `AISomething.create()` or `Something.create()`.
-
-We are open to such surface-level tweaks to the API entry points, and intend to gather more data from web developers on what they find more understandable and clear.
-
### Directly exposing a "prompt API"
The same team that is working on these APIs is also prototyping an experimental [prompt API](https://github.com/webmachinelearning/prompt-api/). A natural question is how these efforts related. Couldn't one easily accomplish summarization/writing/rewriting by directly prompting a language model, thus making these higher-level APIs redundant?
diff --git a/index.bs b/index.bs
index b8bca9d..5eeff2c 100644
--- a/index.bs
+++ b/index.bs
@@ -29,6 +29,8 @@ urlPrefix: https://tc39.es/ecma402/; spec: ECMA-402
urlPrefix: https://tc39.es/ecma262/; spec: ECMA-262
type: abstract-op
text: floor; url: eqn-floor
+ type: dfn
+ text: current realm; url: current-realm
urlPrefix: https://whatpr.org/webidl/1465.html; spec: WEBIDL
type: interface
text: QuotaExceededError; url: quotaexceedederror
@@ -53,39 +55,28 @@ For now, see the [explainer](https://github.com/webmachinelearning/writing-assis
The [=AIDestroyable/destruction abort controller=] is only used internally, as a way of tracking calls to {{AIDestroyable/destroy()}}. Since it is easy to combine multiple {{AbortSignal}}s using [=create a dependent abort signal=], this lets us centralize handling of any {{AbortSignal}} the web developer provides to specific method calls, with any calls to {{AIDestroyable/destroy()}}.
+
The [=DestroyableModel/destruction abort controller=] is only used internally, as a way of tracking calls to {{DestroyableModel/destroy()}}. Since it is easy to combine multiple {{AbortSignal}}s using [=create a dependent abort signal=], this lets us centralize handling of any {{AbortSignal}} the web developer provides to specific method calls, with any calls to {{DestroyableModel/destroy()}}.
The
summarizer language availabilities are given by the following steps. They return a [=language availabilities=] or null.
@@ -421,9 +379,9 @@ All of these [=struct/items=] are [=maps=] from {{AIAvailability}} values to [=s
-
The {{AISummarizer}} class
+
The {{Summarizer}} class
-Every {{AISummarizer}} has a
shared context, a [=string=]-or-null, set during creation.
+Every {{Summarizer}} has a
shared context, a [=string=]-or-null, set during creation.
-Every {{AISummarizer}} has a
summary type, an {{AISummarizerType}}, set during creation.
+Every {{Summarizer}} has a
summary type, an {{SummarizerType}}, set during creation.
-Every {{AISummarizer}} has a
summary format, an {{AISummarizerFormat}}, set during creation.
+Every {{Summarizer}} has a
summary format, an {{SummarizerFormat}}, set during creation.
-Every {{AISummarizer}} has a
summary length, an {{AISummarizerLength}}, set during creation.
+Every {{Summarizer}} has a
summary length, an {{SummarizerLength}}, set during creation.
-Every {{AISummarizer}} has an
expected input languages, a
{{FrozenArray}}<{{DOMString}}> or null, set during creation.
+Every {{Summarizer}} has an
expected input languages, a
{{FrozenArray}}<{{DOMString}}> or null, set during creation.
-Every {{AISummarizer}} has an
expected context languages, a
{{FrozenArray}}<{{DOMString}}> or null, set during creation.
+Every {{Summarizer}} has an
expected context languages, a
{{FrozenArray}}<{{DOMString}}> or null, set during creation.
-Every {{AISummarizer}} has an
output language, a [=string=] or null, set during creation.
+Every {{Summarizer}} has an
output language, a [=string=] or null, set during creation.
-Every {{AISummarizer}} has a
input quota, a number, set during creation.
+Every {{Summarizer}} has a
input quota, a number, set during creation.
-The
sharedContext getter steps are to return [=this=]'s [=AISummarizer/shared context=].
+The
sharedContext getter steps are to return [=this=]'s [=Summarizer/shared context=].
-The
type getter steps are to return [=this=]'s [=AISummarizer/summary type=].
+The
type getter steps are to return [=this=]'s [=Summarizer/summary type=].
-The
format getter steps are to return [=this=]'s [=AISummarizer/summary format=].
+The
format getter steps are to return [=this=]'s [=Summarizer/summary format=].
-The
length getter steps are to return [=this=]'s [=AISummarizer/summary length=].
+The
length getter steps are to return [=this=]'s [=Summarizer/summary length=].
-The
expectedInputLanguages getter steps are to return [=this=]'s [=AISummarizer/expected input languages=].
+The
expectedInputLanguages getter steps are to return [=this=]'s [=Summarizer/expected input languages=].
-The
expectedContextLanguages getter steps are to return [=this=]'s [=AISummarizer/expected context languages=].
+The
expectedContextLanguages getter steps are to return [=this=]'s [=Summarizer/expected context languages=].
-The
outputLanguage getter steps are to return [=this=]'s [=AISummarizer/output language=].
+The
outputLanguage getter steps are to return [=this=]'s [=Summarizer/output language=].
-The
inputQuota getter steps are to return [=this=]'s [=AISummarizer/input quota=].
+The
inputQuota getter steps are to return [=this=]'s [=Summarizer/input quota=].
- The summarize(|input|, |options|) method steps are:
+ The summarize(|input|, |options|) method steps are:
- 1. Let |context| be |options|["{{AISummarizerSummarizeOptions/context}}"] if it [=map/exists=]; otherwise null.
+ 1. Let |context| be |options|["{{SummarizerSummarizeOptions/context}}"] if it [=map/exists=]; otherwise null.
- 1. Let |operation| be an algorithm step which takes arguments |chunkProduced|, |done|, |error|, and |stopProducing|, and [=summarizes=] |input| given [=this=]'s [=AISummarizer/shared context=], |context|, [=this=]'s [=AISummarizer/summary type=], [=this=]'s [=AISummarizer/summary format=], [=this=]'s [=AISummarizer/summary length=], [=this=]'s [=AISummarizer/output language=], [=this=]'s [=AISummarizer/input quota=], |chunkProduced|, |done|, |error|, and |stopProducing|.
+ 1. Let |operation| be an algorithm step which takes arguments |chunkProduced|, |done|, |error|, and |stopProducing|, and [=summarizes=] |input| given [=this=]'s [=Summarizer/shared context=], |context|, [=this=]'s [=Summarizer/summary type=], [=this=]'s [=Summarizer/summary format=], [=this=]'s [=Summarizer/summary length=], [=this=]'s [=Summarizer/output language=], [=this=]'s [=Summarizer/input quota=], |chunkProduced|, |done|, |error|, and |stopProducing|.
1. Return the result of [=getting an aggregated AI model result=] given [=this=], |options|, and |operation|.
- The summarizeStreaming(|input|, |options|) method steps are:
+ The summarizeStreaming(|input|, |options|) method steps are:
- 1. Let |context| be |options|["{{AISummarizerSummarizeOptions/context}}"] if it [=map/exists=]; otherwise null.
+ 1. Let |context| be |options|["{{SummarizerSummarizeOptions/context}}"] if it [=map/exists=]; otherwise null.
- 1. Let |operation| be an algorithm step which takes arguments |chunkProduced|, |done|, |error|, and |stopProducing|, and [=summarizes=] |input| given [=this=]'s [=AISummarizer/shared context=], |context|, [=this=]'s [=AISummarizer/summary type=], [=this=]'s [=AISummarizer/summary format=], [=this=]'s [=AISummarizer/summary length=], [=this=]'s [=AISummarizer/output language=], [=this=]'s [=AISummarizer/input quota=], |chunkProduced|, |done|, |error|, and |stopProducing|.
+ 1. Let |operation| be an algorithm step which takes arguments |chunkProduced|, |done|, |error|, and |stopProducing|, and [=summarizes=] |input| given [=this=]'s [=Summarizer/shared context=], |context|, [=this=]'s [=Summarizer/summary type=], [=this=]'s [=Summarizer/summary format=], [=this=]'s [=Summarizer/summary length=], [=this=]'s [=Summarizer/output language=], [=this=]'s [=Summarizer/input quota=], |chunkProduced|, |done|, |error|, and |stopProducing|.
1. Return the result of [=getting a streaming AI model result=] given [=this=], |options|, and |operation|.
- The measureInputUsage(|input|, |options|) method steps are:
+ The measureInputUsage(|input|, |options|) method steps are:
- 1. Let |context| be |options|["{{AISummarizerSummarizeOptions/context}}"] if it [=map/exists=]; otherwise null.
+ 1. Let |context| be |options|["{{SummarizerSummarizeOptions/context}}"] if it [=map/exists=]; otherwise null.
- 1. Let |measureUsage| be an algorithm step which takes argument |stopMeasuring|, and returns the result of [=measuring summarizer input usage=] given |input|, [=this=]'s [=AISummarizer/shared context=], |context|, [=this=]'s [=AISummarizer/summary type=], [=this=]'s [=AISummarizer/summary format=], [=this=]'s [=AISummarizer/summary length=], [=this=]'s [=AISummarizer/output language=], and |stopMeasuring|.
+ 1. Let |measureUsage| be an algorithm step which takes argument |stopMeasuring|, and returns the result of [=measuring summarizer input usage=] given |input|, [=this=]'s [=Summarizer/shared context=], |context|, [=this=]'s [=Summarizer/summary type=], [=this=]'s [=Summarizer/summary format=], [=this=]'s [=Summarizer/summary length=], [=this=]'s [=Summarizer/output language=], and |stopMeasuring|.
1. Return the result of [=measuring AI model input usage=] given [=this=], |options|, and |measureUsage|.
@@ -523,11 +481,11 @@ The
inputQuota getter steps are to retur
* a [=string=] |input|,
* a [=string=]-or-null |sharedContext|,
* a [=string=]-or-null |context|,
- * an {{AISummarizerType}} |type|,
- * an {{AISummarizerFormat}} |format|,
- * an {{AISummarizerLength}} |length|,
+ * an {{SummarizerType}} |type|,
+ * an {{SummarizerFormat}} |format|,
+ * an {{SummarizerLength}} |length|,
* a [=string=]-or-null |outputLanguage|,
- * a [=number=] |inputQuota|,
+ * a number |inputQuota|,
* an algorithm |chunkProduced| that takes a [=string=] and returns nothing,
* an algorithm |done| that takes no arguments and returns nothing,
* an algorithm |error| that takes [=error information=] and returns nothing, and
@@ -604,9 +562,9 @@ The
inputQuota getter steps are to retur
* a [=string=] |input|,
* a [=string=]-or-null |sharedContext|,
* a [=string=]-or-null |context|,
- * an {{AISummarizerType}} |type|,
- * an {{AISummarizerFormat}} |format|,
- * an {{AISummarizerLength}} |length|,
+ * an {{SummarizerType}} |type|,
+ * an {{SummarizerFormat}} |format|,
+ * an {{SummarizerLength}} |length|,
* a [=string=]-or-null |outputLanguage|, and
* an algorithm |stopMeasuring| that takes no arguments and returns a boolean,
@@ -616,7 +574,7 @@ The
inputQuota getter steps are to retur
1. Let |inputToModel| be the [=implementation-defined=] string that would be sent to the underlying model in order to [=summarize=] given |input|, |sharedContext|, |context|, |type|, |format|, |length|, and |outputLanguage|.
-
This might be something similar to the concatenation of |input| and |context|, if all of the other options were loaded into the model during initialization, and so the input usage for those was already accounted for when computing the [=AISummarizer/input quota=]. Or it might consist of more, if the options are sent along with every summarization call, or if there is a per-summarization wrapper prompt of some sort.
+
This might be something similar to the concatenation of |input| and |context|, if all of the other options were loaded into the model during initialization, and so the input usage for those was already accounted for when computing the [=Summarizer/input quota=]. Or it might consist of more, if the options are sent along with every summarization call, or if there is a per-summarization wrapper prompt of some sort.
If during this process |stopMeasuring| starts returning true, then return null.
@@ -624,7 +582,7 @@ The inputQuota getter steps are to retur
1. Return the amount of input usage needed to represent |inputToModel| when given to the underlying model. The exact calculation procedure is [=implementation-defined=], subject to the following constraints.
- The returned input usage must be nonnegative and finite. It must be 0, if there are no usage quotas for the summarization process (i.e., if the [=AISummarizer/input quota=] is +∞). Otherwise, it must be positive and should be roughly proportional to the [=string/length=] of |inputToModel|.
+ The returned input usage must be nonnegative and finite. It must be 0, if there are no usage quotas for the summarization process (i.e., if the [=Summarizer/input quota=] is +∞). Otherwise, it must be positive and should be roughly proportional to the [=string/length=] of |inputToModel|.
This might be the number of tokens needed to represent |input| in a language model tokenization scheme, or it might be |input|'s [=string/length=]. It could also be some variation of these which also counts the usage of any prefixes or suffixes necessary to give to the model.
@@ -635,82 +593,82 @@ The inputQuota getter steps are to retur
Options
-The [=summarize=] algorithm's details are [=implementation-defined=], as they are expected to be powered by an AI model. However, it is intended to be controllable by the web developer through the {{AISummarizerType}}, {{AISummarizerFormat}}, and {{AISummarizerLength}} enumerations.
+The [=summarize=] algorithm's details are [=implementation-defined=], as they are expected to be powered by an AI model. However, it is intended to be controllable by the web developer through the {{SummarizerType}}, {{SummarizerFormat}}, and {{SummarizerLength}} enumerations.
This section gives normative guidance on how the implementation of [=summarize=] should use each enumeration value to guide the summarization process.
- {{AISummarizerType}} values
+ {{SummarizerType}} values
| Value
| Meaning
|
- | "tl;dr"
+ | "tl;dr"
|
The summary should be short and to the point, providing a quick overview of the input, suitable for a busy reader.
|
- | "teaser"
+ | "teaser"
|
The summary should focus on the most interesting or intriguing parts of the input, designed to draw the reader in to read more.
|
- | "key-points"
+ | "key-points"
|
The summary should extract the most important points from the input, presented as a bulleted list.
|
- | "headline"
+ | "headline"
|
The summary should effectively contain the main point of the input in a single sentence, in the format of an article headline.
|
- {{AISummarizerLength}} values
+ {{SummarizerLength}} values
| Value
| Meaning
|
- | "short"
+ | "short"
|
- The guidance is dependent on the value of {{AISummarizerType}}:
+ The guidance is dependent on the value of {{SummarizerType}}:
- : "{{AISummarizerType/tl;dr}}"
- : "{{AISummarizerType/teaser}}"
+ : "{{SummarizerType/tl;dr}}"
+ : "{{SummarizerType/teaser}}"
:: The summary should fit within 1 sentence.
- : "{{AISummarizerType/key-points}}"
+ : "{{SummarizerType/key-points}}"
:: The summary should consist of no more than 3 bullet points.
- : "{{AISummarizerType/headline}}"
+ : "{{SummarizerType/headline}}"
:: The summary should use no more than 12 words.
|
- | "medium"
+ | "medium"
|
- The guidance is dependent on the value of {{AISummarizerType}}:
+ The guidance is dependent on the value of {{SummarizerType}}:
- : "{{AISummarizerType/tl;dr}}"
- : "{{AISummarizerType/teaser}}"
+ : "{{SummarizerType/tl;dr}}"
+ : "{{SummarizerType/teaser}}"
:: The summary should fit within 1 short paragraph.
- : "{{AISummarizerType/key-points}}"
+ : "{{SummarizerType/key-points}}"
:: The summary should consist of no more than 5 bullet points.
- : "{{AISummarizerType/headline}}"
+ : "{{SummarizerType/headline}}"
:: The summary should use no more than 17 words.
|
- | "long"
+ | "long"
|
- The guidance is dependent on the value of {{AISummarizerType}}:
+ The guidance is dependent on the value of {{SummarizerType}}:
- : "{{AISummarizerType/tl;dr}}"
- : "{{AISummarizerType/teaser}}"
+ : "{{SummarizerType/tl;dr}}"
+ : "{{SummarizerType/teaser}}"
:: The summary should fit within 1 paragraph.
- : "{{AISummarizerType/key-points}}"
+ : "{{SummarizerType/key-points}}"
:: The summary should consist of no more than 7 bullet points.
- : "{{AISummarizerType/headline}}"
+ : "{{SummarizerType/headline}}"
:: The summary should use no more than 22 words.
|
@@ -718,18 +676,18 @@ This section gives normative guidance on how the implementation of [=summarize=]
As with all "should"-level guidance, user agents might not conform perfectly to these. Especially in the case of counting words, it's expected that language models might not conform perfectly.
- {{AISummarizerFormat}} values
+ {{SummarizerFormat}} values
| Value
| Meaning
|
- | "plain-text"
+ | "plain-text"
|
The summary should not contain any formatting or markup language.
|
- | "markdown"
+ | "markdown"
|
The summary should be formatted using the Markdown markup language, ideally as valid CommonMark. [[!COMMONMARK]]
|
@@ -755,11 +713,11 @@ When summarization fails, the following possible reasons may be surfaced to the
| "{{NotSupportedError}}"
|
- The input to be summarized, or the context to be provided, was in a language that the user agent does not support, or was not provided properly in the call to {{AISummarizerFactory/create()}}.
+ The input to be summarized, or the context to be provided, was in a language that the user agent does not support, or was not provided properly in the call to {{Summarizer/create()}}.
- The summarization output ended up being in a language that the user agent does not support (e.g., because the user agent has not performed sufficient quality control tests on that output language), or was not provided properly in the call to {{AISummarizerFactory/create()}}.
+ The summarization output ended up being in a language that the user agent does not support (e.g., because the user agent has not performed sufficient quality control tests on that output language), or was not provided properly in the call to {{Summarizer/create()}}.
- The {{AISummarizerCreateCoreOptions/outputLanguage}} option was not set, and the language of the input text could not be determined, so the user agent did not have a good output language default available.
+ The {{SummarizerCreateCoreOptions/outputLanguage}} option was not set, and the language of the input text could not be determined, so the user agent did not have a good output language default available.
|
| "{{UnknownError}}"
|
@@ -774,20 +732,23 @@ Just IDL for now; full spec coming!
[Exposed=(Window,Worker), SecureContext]
-interface AIWriterFactory {
- Promise create(optional AIWriterCreateOptions options = {});
- Promise availability(optional AIWriterCreateCoreOptions options = {});
-};
+interface Writer {
+ static Promise create(optional WriterCreateOptions options = {});
+ static Promise availability(optional WriterCreateCoreOptions options = {});
-[Exposed=(Window,Worker), SecureContext]
-interface AIWriter {
- Promise write(DOMString writingTask, optional AIWriterWriteOptions options = {});
- ReadableStream writeStreaming(DOMString writingTask, optional AIWriterWriteOptions options = {});
+ Promise write(
+ DOMString input,
+ optional WriterWriteOptions options = {}
+ );
+ ReadableStream writeStreaming(
+ DOMString input,
+ optional WriterWriteOptions options = {}
+ );
readonly attribute DOMString sharedContext;
- readonly attribute AIWriterTone tone;
- readonly attribute AIWriterFormat format;
- readonly attribute AIWriterLength length;
+ readonly attribute WriterTone tone;
+ readonly attribute WriterFormat format;
+ readonly attribute WriterLength length;
readonly attribute FrozenArray? expectedInputLanguages;
readonly attribute FrozenArray? expectedContextLanguages;
@@ -795,37 +756,37 @@ interface AIWriter {
Promise measureInputUsage(
DOMString input,
- optional AIWriterWriteOptions options = {}
+ optional WriterWriteOptions options = {}
);
readonly attribute unrestricted double inputQuota;
};
-AIWriter includes AIDestroyable;
+Writer includes DestroyableModel;
-dictionary AIWriterCreateCoreOptions {
- AIWriterTone tone = "neutral";
- AIWriterFormat format = "markdown";
- AIWriterLength length = "short";
+dictionary WriterCreateCoreOptions {
+ WriterTone tone = "neutral";
+ WriterFormat format = "markdown";
+ WriterLength length = "short";
sequence expectedInputLanguages;
sequence expectedContextLanguages;
DOMString outputLanguage;
};
-dictionary AIWriterCreateOptions : AIWriterCreateCoreOptions {
+dictionary WriterCreateOptions : WriterCreateCoreOptions {
AbortSignal signal;
- AICreateMonitorCallback monitor;
+ CreateMonitorCallback monitor;
DOMString sharedContext;
};
-dictionary AIWriterWriteOptions {
+dictionary WriterWriteOptions {
DOMString context;
AbortSignal signal;
};
-enum AIWriterTone { "formal", "neutral", "casual" };
-enum AIWriterFormat { "plain-text", "markdown" };
-enum AIWriterLength { "short", "medium", "long" };
+enum WriterTone { "formal", "neutral", "casual" };
+enum WriterFormat { "plain-text", "markdown" };
+enum WriterLength { "short", "medium", "long" };
The rewriter API
@@ -834,20 +795,23 @@ Just IDL for now; full spec coming!
[Exposed=(Window,Worker), SecureContext]
-interface AIRewriterFactory {
- Promise create(optional AIRewriterCreateOptions options = {});
- Promise availability(optional AIRewriterCreateCoreOptions options = {});
-};
+interface Rewriter {
+ static Promise create(optional RewriterCreateOptions options = {});
+ static Promise availability(optional RewriterCreateCoreOptions options = {});
-[Exposed=(Window,Worker), SecureContext]
-interface AIRewriter {
- Promise rewrite(DOMString input, optional AIRewriterRewriteOptions options = {});
- ReadableStream rewriteStreaming(DOMString input, optional AIRewriterRewriteOptions options = {});
+ Promise rewrite(
+ DOMString input,
+ optional RewriterRewriteOptions options = {}
+ );
+ ReadableStream rewriteStreaming(
+ DOMString input,
+ optional RewriterRewriteOptions options = {}
+ );
readonly attribute DOMString sharedContext;
- readonly attribute AIRewriterTone tone;
- readonly attribute AIRewriterFormat format;
- readonly attribute AIRewriterLength length;
+ readonly attribute RewriterTone tone;
+ readonly attribute RewriterFormat format;
+ readonly attribute RewriterLength length;
readonly attribute FrozenArray? expectedInputLanguages;
readonly attribute FrozenArray? expectedContextLanguages;
@@ -855,37 +819,37 @@ interface AIRewriter {
Promise measureInputUsage(
DOMString input,
- optional AIRewriterRewriteOptions options = {}
+ optional RewriterRewriteOptions options = {}
);
readonly attribute unrestricted double inputQuota;
};
-AIRewriter includes AIDestroyable;
+Rewriter includes DestroyableModel;
-dictionary AIRewriterCreateCoreOptions {
- AIRewriterTone tone = "as-is";
- AIRewriterFormat format = "as-is";
- AIRewriterLength length = "as-is";
+dictionary RewriterCreateCoreOptions {
+ RewriterTone tone = "as-is";
+ RewriterFormat format = "as-is";
+ RewriterLength length = "as-is";
sequence expectedInputLanguages;
sequence expectedContextLanguages;
DOMString outputLanguage;
};
-dictionary AIRewriterCreateOptions : AIRewriterCreateCoreOptions {
+dictionary RewriterCreateOptions : RewriterCreateCoreOptions {
AbortSignal signal;
- AICreateMonitorCallback monitor;
+ CreateMonitorCallback monitor;
DOMString sharedContext;
};
-dictionary AIRewriterRewriteOptions {
+dictionary RewriterRewriteOptions {
DOMString context;
AbortSignal signal;
};
-enum AIRewriterTone { "as-is", "more-formal", "more-casual" };
-enum AIRewriterFormat { "as-is", "plain-text", "markdown" };
-enum AIRewriterLength { "as-is", "shorter", "longer" };
+enum RewriterTone { "as-is", "more-formal", "more-casual" };
+enum RewriterFormat { "as-is", "plain-text", "markdown" };
+enum RewriterLength { "as-is", "shorter", "longer" };
Shared infrastructure
@@ -895,20 +859,30 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
To create an AI model object given:
- * a [=ECMAScript/realm=] |realm|,
* an [=ordered map=] |options|,
- * an algorithm |getAvailability| taking an [=ordered map=] and returning an {{AIAvailability}} or null,
+ * an algorithm |validateAndCanonicalizeOptions| taking an [=ordered map=] and returning nothing,
+ * an algorithm |getAvailability| taking an [=ordered map=] and returning an {{Availability}} or null,
* an algorithm |startDownload| taking an [=ordered map=] and returning a boolean,
* an algorithm |initialize| taking an [=ordered map=] and returning an error information or null, and
* an algorithm |create| taking a [=ECMAScript/realm=] and an [=ordered map=] and returning a Web IDL object representing the model,
perform the following steps:
+ 1. Let |realm| be the [=current realm=].
+
+ 1. If |realm|'s [=realm/global object=] is a {{Window}} whose [=associated Document=] is not [=Document/fully active=], then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
+
+ 1. If |options|["`signal`"] [=map/exists=] and is [=AbortSignal/aborted=], then return [=a promise rejected with=] |options|["`signal`"]'s [=AbortSignal/abort reason=].
+
+ 1. Perform |validateAndCanonicalizeOptions| given |options|. If this throws an exception |e|, catch it, and return [=a promise rejected with=] |e|.
+
+ This can mutate |options|.
+
1. Let |fireProgressEvent| be an algorithm taking two arguments that does nothing.
1. If |options|["`monitor`"] [=map/exists=], then:
- 1. Let |monitor| be a [=new=] {{AICreateMonitor}} created in |realm|.
+ 1. Let |monitor| be a [=new=] {{CreateMonitor}} created in |realm|.
1. [=Invoke=] |options|["`monitor`"] with « |monitor| » and "`rethrow`".
@@ -920,7 +894,7 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
1. [=Queue a global task=] on the [=AI task source=] given |realm|'s [=realm/global object=] to perform the following steps:
- 1. [=Fire an event=] named {{AICreateMonitor/downloadprogress}} at |monitor|, using {{ProgressEvent}}, with the {{ProgressEvent/loaded}} attribute initialized to |loaded|, the {{ProgressEvent/total}} attribute initialized to 1, and the {{ProgressEvent/lengthComputable}} attribute initialized to true.
+ 1. [=Fire an event=] named {{CreateMonitor/downloadprogress}} at |monitor|, using {{ProgressEvent}}, with the {{ProgressEvent/loaded}} attribute initialized to |loaded|, the {{ProgressEvent/total}} attribute initialized to 1, and the {{ProgressEvent/lengthComputable}} attribute initialized to true.
This assumes whatwg/xhr#394 is merged so that passing non-integer values for {{ProgressEvent/loaded}} works as expected.
@@ -949,20 +923,20 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
1. Abort these steps.
- : "{{AIAvailability/unavailable}}"
+ : "{{Availability/unavailable}}"
::
1. [=Reject=] |promise| with a "{{NotSupportedError}}" {{DOMException}}.
1. Abort these steps.
- : "{{AIAvailability/available}}"
+ : "{{Availability/available}}"
::
1. [=Initialize and return an AI model object=] given |promise|, |options|, |fireProgressEvent|, |initialize|, and |create|.
- : "{{AIAvailability/downloading}}"
- : "{{AIAvailability/downloadable}}"
+ : "{{Availability/downloading}}"
+ : "{{Availability/downloadable}}"
::
- 1. If |availability| is "{{AIAvailability/downloadable}}", then let |startDownloadResult| be the result of performing |startDownload| given |options|.
+ 1. If |availability| is "{{Availability/downloadable}}", then let |startDownloadResult| be the result of performing |startDownload| given |options|.
1. If |startDownloadResult| is false, then:
@@ -1099,7 +1073,7 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
1. If |bytesSoFar| equals |totalBytes|, then [=iteration/break=].
- Since this is the only non-failure exit condition for the loop, we will never miss firing a {{AICreateMonitor/downloadprogress}} event for the 100% mark.
+ Since this is the only non-failure exit condition for the loop, we will never miss firing a {{CreateMonitor/downloadprogress}} event for the 100% mark.
1. Set |lastProgressFraction| to |progressFraction|.
@@ -1150,13 +1124,13 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
1. Let |model| be the result of performing |create| given |promise|'s [=relevant global object=] and |options|.
- 1. [=Assert=]: |model| [=implements=] an [=interface=] that [=interface/includes=] {{AIDestroyable}}.
+ 1. [=Assert=]: |model| [=implements=] an [=interface=] that [=interface/includes=] {{DestroyableModel}}.
1. [=Initialize as a destroyable=] |model|.
1. If |options|["`signal`"] [=map/exists=], then [=AbortSignal/add|add the following abort steps=] to |options|["`signal`"]:
- 1. [=AIDestroyable/Destroy=] |model| given |options|["`signal`"]'s [=AbortSignal/abort reason=].
+ 1. [=DestroyableModel/Destroy=] |model| given |options|["`signal`"]'s [=AbortSignal/abort reason=].
1. [=Resolve=] |promise| with |model|.
@@ -1164,11 +1138,11 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
Obtaining results and usage
- To get an aggregated AI model result given an {{AIDestroyable}} |modelObject|, an [=ordered map=] |options|, and an algorithm |operation|:
+ To get an aggregated AI model result given an {{DestroyableModel}} |modelObject|, an [=ordered map=] |options|, and an algorithm |operation|:
1. If |modelObject|'s [=relevant global object=] is a {{Window}} whose [=associated Document=] is not [=Document/fully active=], then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
- 1. Let |signals| be « |modelObject|'s [=AIDestroyable/destruction abort controller=]'s [=AbortController/signal=] ».
+ 1. Let |signals| be « |modelObject|'s [=DestroyableModel/destruction abort controller=]'s [=AbortController/signal=] ».
1. If |options|["`signal`"] [=map/exists=], then [=set/append=] it to |signals|.
@@ -1224,13 +1198,13 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
- To get a streaming AI model result given an {{AIDestroyable}} |modelObject|, an [=ordered map=] |options|, and an algorithm |operation|:
+ To get a streaming AI model result given an {{DestroyableModel}} |modelObject|, an [=ordered map=] |options|, and an algorithm |operation|:
1. If |modelObject|'s [=relevant global object=] is a {{Window}} whose [=associated Document=] is not [=Document/fully active=], then throw an "{{InvalidStateError}}" {{DOMException}}.
- 1. Let |signals| be « |modelObject|'s [=AIDestroyable/destruction abort controller=]'s [=AbortController/signal=] ».
+ 1. Let |signals| be « |modelObject|'s [=DestroyableModel/destruction abort controller=]'s [=AbortController/signal=] ».
- 1. If |options|["{{AISummarizerSummarizeOptions/signal}}"] [=map/exists=], then [=set/append=] it to |signals|.
+ 1. If |options|["{{SummarizerSummarizeOptions/signal}}"] [=map/exists=], then [=set/append=] it to |signals|.
1. Let |compositeSignal| be the result of [=creating a dependent abort signal=] given |signals| using {{AbortSignal}} and |modelObject|'s [=relevant realm=].
@@ -1292,11 +1266,11 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
- To measure AI model input usage given an {{AIDestroyable}} |modelObject|, an [=ordered map=] |options|, and an algorithm |measure|:
+ To measure AI model input usage given an {{DestroyableModel}} |modelObject|, an [=ordered map=] |options|, and an algorithm |measure|:
1. If |modelObject|'s [=relevant global object=] is a {{Window}} whose [=associated Document=] is not [=Document/fully active=], then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
- 1. Let |signals| be « |modelObject|'s [=AIDestroyable/destruction abort controller=]'s [=AbortController/signal=] ».
+ 1. Let |signals| be « |modelObject|'s [=DestroyableModel/destruction abort controller=]'s [=AbortController/signal=] ».
1. If |options|["`signal`"] [=map/exists=], then [=set/append=] it to |signals|.
@@ -1372,23 +1346,23 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
To get language availabilities given a description |purpose| of the purpose for which we're checking language availability:
- 1. Let |availabilities| be «[ "{{AIAvailability/available}}" → an empty [=set=], "{{AIAvailability/downloading}}" → an empty [=set=], "{{AIAvailability/downloadable}}" → an empty [=set=] ]».
+ 1. Let |availabilities| be «[ "{{Availability/available}}" → an empty [=set=], "{{Availability/downloading}}" → an empty [=set=], "{{Availability/downloadable}}" → an empty [=set=] ]».
1. [=list/For each=] human language |languageTag|, represented as a [=Unicode canonicalized locale identifier=], for which the user agent supports |purpose|, without performing any downloading operations:
- 1. [=set/Append=] |languageTag| to |availabilities|["{{AIAvailability/available}}"].
+ 1. [=set/Append=] |languageTag| to |availabilities|["{{Availability/available}}"].
1. [=list/For each=] human language |languageTag|, represented as a [=Unicode canonicalized locale identifier=], for which the user agent is currently downloading material (e.g., an AI model or fine-tuning) to support |purpose|:
- 1. [=set/Append=] |languageTag| to |availabilities|["{{AIAvailability/downloading}}"].
+ 1. [=set/Append=] |languageTag| to |availabilities|["{{Availability/downloading}}"].
1. [=list/For each=] human language |languageTag|, represented as a [=Unicode canonicalized locale identifier=], for which the user agent believes it can support |purpose|, but only after performing a not-currently-ongoing download (e.g., of an AI model or fine-tuning):
- 1. [=set/Append=] |languageTag| to |availabilities|["{{AIAvailability/downloadable}}"].
+ 1. [=set/Append=] |languageTag| to |availabilities|["{{Availability/downloadable}}"].
- 1. [=Assert=]: |availabilities|["{{AIAvailability/available}}"], |availabilities|["{{AIAvailability/downloading}}"], and |availabilities|["{{AIAvailability/downloadable}}"] are disjoint.
+ 1. [=Assert=]: |availabilities|["{{Availability/available}}"], |availabilities|["{{Availability/downloading}}"], and |availabilities|["{{Availability/downloadable}}"] are disjoint.
- 1. If the [=set/union=] of |availabilities|["{{AIAvailability/available}}"], |availabilities|["{{AIAvailability/downloading}}"], and |availabilities|["{{AIAvailability/downloadable}}"] does not meet the [=language tag set completeness rules=], then:
+ 1. If the [=set/union=] of |availabilities|["{{Availability/available}}"], |availabilities|["{{Availability/downloading}}"], and |availabilities|["{{Availability/downloadable}}"] does not meet the [=language tag set completeness rules=], then:
1. Let |missingLanguageTags| be the [=set=] of missing language tags necessary to meet the [=language tag set completeness rules=].
@@ -1414,17 +1388,39 @@ enum AIRewriterLength { "as-is", "shorter", "longer" };
Availability
- The minimum availability given a [=list=] of {{AIAvailability}}-or-null values |availabilities| is:
+ To compute AI model availability given |options|, an algorithm |validate|, and an algorithm |compute|:
+
+ 1. Let |global| be the [=current global object=].
+
+ 1. If |global| is a {{Window}} whose [=associated Document=] is not [=Document/fully active=], then return [=a promise rejected with=] an "{{InvalidStateError}}" {{DOMException}}.
+
+ 1. Perform |validate| given |options|.
+
+ 1. Let |promise| be [=a new promise=] created in the [=current realm=].
+
+ 1. [=In parallel=]:
+
+ 1. Let |availability| be the result of |compute| given |options|.
+
+ 1. [=Queue a global task=] on the [=AI task source=] given |global| to perform the following steps:
+
+ 1. If |availability| is null, then [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}.
+
+ 1. Otherwise, [=resolve=] |promise| with |availability|.
+
+
+
+ The minimum availability given a [=list=] of {{Availability}}-or-null values |availabilities| is:
1. If |availabilities| [=list/contains=] null, then return null.
- 1. If |availabilities| [=list/contains=] "{{AIAvailability/unavailable}}", then return "{{AIAvailability/unavailable}}".
+ 1. If |availabilities| [=list/contains=] "{{Availability/unavailable}}", then return "{{Availability/unavailable}}".
- 1. If |availabilities| [=list/contains=] "{{AIAvailability/downloading}}", then return "{{AIAvailability/downloading}}".
+ 1. If |availabilities| [=list/contains=] "{{Availability/downloading}}", then return "{{Availability/downloading}}".
- 1. If |availabilities| [=list/contains=] "{{AIAvailability/downloadable}}", then return "{{AIAvailability/downloadable}}".
+ 1. If |availabilities| [=list/contains=] "{{Availability/downloadable}}", then return "{{Availability/downloadable}}".
- 1. Return "{{AIAvailability/available}}".
+ 1. Return "{{Availability/available}}".
Errors
@@ -1441,9 +1437,9 @@ A DOMException error information is a [=struct=] with the foll
A quota exceeded error information is a [=struct=] with the following [=struct/items=]:
: requested
-:: a [=number=] that will be used for the {{QuotaExceededError}}'s [=QuotaExceededError/requested=].
+:: a number that will be used for the {{QuotaExceededError}}'s [=QuotaExceededError/requested=].
: quota
-:: a [=number=] that will be used for the {{QuotaExceededError}}'s [=QuotaExceededError/quota=].
+:: a number that will be used for the {{QuotaExceededError}}'s [=QuotaExceededError/quota=].
The parts of this specification related to quota exceeded errors assume that whatwg/webidl#1465 will be merged.
|