Writing Assistance APIs

Draft Community Group Report,

More details about this document
This version:
https://webmachinelearning.github.io/writing-assistance-apis
Issue Tracking:
GitHub
Editor:
Domenic Denicola (Google)

Abstract

The summarizer, writer, and rewriter APIs provide high-level interfaces to call on a browser or operating system’s built-in language model to help with writing tasks.

Status of this document

This specification was published by the Web Machine Learning Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

For now, see the explainer.

2. The summarizer API

[Exposed=Window, SecureContext]
interface Summarizer {
  static Promise<Summarizer> create(optional SummarizerCreateOptions options = {});
  static Promise<Availability> availability(optional SummarizerCreateCoreOptions options = {});

  Promise<DOMString> summarize(
    DOMString input,
    optional SummarizerSummarizeOptions options = {}
  );
  ReadableStream summarizeStreaming(
    DOMString input,
    optional SummarizerSummarizeOptions options = {}
  );

  readonly attribute DOMString sharedContext;
  readonly attribute SummarizerType type;
  readonly attribute SummarizerFormat format;
  readonly attribute SummarizerLength length;

  readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
  readonly attribute FrozenArray<DOMString>? expectedContextLanguages;
  readonly attribute DOMString? outputLanguage;

  Promise<double> measureInputUsage(
    DOMString input,
    optional SummarizerSummarizeOptions options = {}
  );
  readonly attribute unrestricted double inputQuota;
};
Summarizer includes DestroyableModel;

dictionary SummarizerCreateCoreOptions {
  SummarizerType type = "key-points";
  SummarizerFormat format = "markdown";
  SummarizerLength length = "short";

  sequence<DOMString> expectedInputLanguages;
  sequence<DOMString> expectedContextLanguages;
  DOMString outputLanguage;
};

dictionary SummarizerCreateOptions : SummarizerCreateCoreOptions {
  AbortSignal signal;
  CreateMonitorCallback monitor;

  DOMString sharedContext;
};

dictionary SummarizerSummarizeOptions {
  AbortSignal signal;
  DOMString context;
};

enum SummarizerType { "tl;dr", "teaser", "key-points", "headline" };
enum SummarizerFormat { "plain-text", "markdown" };
enum SummarizerLength { "short", "medium", "long" };

2.1. Creation

The static create(options) method steps are:
  1. Return the result of creating an AI model object given options, "summarizer", validate and canonicalize summarizer options, computing summarizer options availability, download the summarizer model, initialize the summarizer model, and create a summarizer object.

To validate and canonicalize summarizer options given a SummarizerCreateCoreOptions options, perform the following steps. They mutate options in place to canonicalize and deduplicate language tags, and throw a TypeError if any are invalid.
  1. Validate and canonicalize language tags given options and "expectedInputLanguages".

  2. Validate and canonicalize language tags given options and "expectedContextLanguages".

  3. Validate and canonicalize language tags given options and "outputLanguage".

To download the summarizer model, given a SummarizerCreateCoreOptions options:
  1. Assert: these steps are running in parallel.

  2. Initiate the download process for everything the user agent needs to summarize text according to options. This could include a base AI model, fine-tunings for specific languages or option values, or other resources.

  3. If the download process cannot be started for any reason, then return false.

  4. Return true.

To initialize the summarizer model, given a SummarizerCreateOptions options:
  1. Assert: these steps are running in parallel.

  2. Perform any necessary initialization operations for the AI model backing the user agent’s summarization capabilities.

    This could include loading the model into memory, loading options["sharedContext"] into the model’s context window, or loading any fine-tunings necessary to support the other options expressed by options.

  3. If initialization failed because the process of loading options resulted in using up all of the model’s input quota, then:

    1. Let requested be the amount of input usage needed to encode options. The encoding of options as input is implementation-defined.

      This could be the amount of tokens needed to represent these options in a language model tokenization scheme, possibly with prompt engineering. Or it could be 0, if the implementation plans to send the options to the underlying model with every summarize operation.

    2. Let quota be the maximum input quota that the user agent supports for encoding options.

    3. Assert: requested is greater than quota. (That is how we reached this error branch.)

    4. Return a quota exceeded error information whose requested is requested and quota is quota.

  4. If initialization failed for any other reason, then return a DOMException error information whose name is "OperationError" and whose details contain appropriate detail.

  5. Return null.

To create a summarizer object, given a realm realm and a SummarizerCreateOptions options:
  1. Assert: these steps are running on realm’s surrounding agent’s event loop.

  2. Let inputQuota be the amount of input quota that is available to the user agent for future summarization operations. (This value is implementation-defined, and may be +∞ if there are no specific limits beyond, e.g., the user’s memory, or the limits of JavaScript strings.)

    For implementations that do not have infinite quota, this will generally vary for each Summarizer instance, depending on how much input quota was used by encoding options. See this note on that encoding.

  3. Return a new Summarizer object, created in realm, with

    shared context

    options["sharedContext"] if it exists; otherwise null

    summary type

    options["type"]

    summary format

    options["format"]

    summary length

    options["length"]

    expected input languages

    the result of creating a frozen array given options["expectedInputLanguages"] if it is not empty; otherwise null

    expected context languages

    the result of creating a frozen array given options["expectedContextLanguages"] if it is not empty; otherwise null

    output language

    options["outputLanguage"] if it exists; otherwise null

    input quota

    inputQuota

2.2. Availability

The static availability(options) method steps are:
  1. Return the result of computing AI model availability given options, "summarizer", validate and canonicalize summarizer options, and compute summarizer options availability.

To compute summarizer options availability given a SummarizerCreateCoreOptions options, perform the following steps. They return either an Availability value or null, and they mutate options in place to update language tags to their best-fit matches.
  1. Assert: this algorithm is running in parallel.

  2. Let availability be the summarizer non-language options availability given options["type"], options["format"], and options["length"].

  3. Let triple be the summarizer language availabilities triple.

  4. If triple is null, then return null.

  5. Let inputLanguageAvailability be the result of computing language availability given options["expectedInputLanguages"] and triple’s input languages.

  6. Let contextLanguagesAvailability be the result of computing language availability given options["expectedContextLanguages"] and triple’s context languages.

  7. Let outputLanguagesList be « options["outputLanguage"] ».

  8. Let outputLanguageAvailability be the result of computing language availability given outputLanguagesList and triple’s output languages.

  9. Set options["outputLanguage"] to outputLanguagesList[0].

  10. Return the minimum availability given « availability, inputLanguageAvailability, contextLanguagesAvailability, outputLanguageAvailability ».

The summarizer non-language options availability, given a SummarizerType type, SummarizerFormat format, and a SummarizerLength length, is given by the following steps. They return an Availability value or null.
  1. Assert: this algorithm is running in parallel.

  2. If there is some error attempting to determine whether the user agent supports summarizing text, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null.

  3. If the user agent supports summarizing text into the type of summary described by type, in the format described by format, and with the length guidance given by length without performing any downloading operations, then return "available".

  4. If the user agent believes it can summarize text according to type, format, and length, but only after finishing a download (e.g., of an AI model or fine-tuning) that is already ongoing, then return "downloadable".

  5. If the user agent believes it can summarize text according to type, format, and length, but only after performing a download (e.g., of an AI model or fine-tuning), then return "downloadable".

  6. Otherwise, return "unavailable".

The summarizer language availabilities triple is given by the following steps. They return a language availabilities triple or null.
  1. Assert: this algorithm is running in parallel.

  2. If there is some error attempting to determine whether the user agent supports summarizing text, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null.

  3. Return a language availabilities triple with:

    input languages

    the result of getting the language availabilities partition given the purpose of summarizing text written in that language

    context languages

    the result of getting the language availabilities partition given the purpose of summarizing text using web-developer provided context information written in that language

    output languages

    the result of getting the language availabilities partition given the purpose of producing text summaries in that language

A common setup seen in today’s software is to support two types of written Chinese: "traditional Chinese" and "simplified Chinese". Let’s suppose that the user agent supports summarizing text written in traditional Chinese with no downloads, and simplified Chinese after a download.

One way this could be implemented would be for summarizer language availabilities triple to return that "zh-Hant" is in the input languages["available"] set, and "zh" and "zh-Hans" are in the input languages["downloadable"] set. This return value conforms to the requirements of the language tag set completeness rules, in ensuring that "zh" is present. Per the "should"-level guidance, the implementation has determined that "zh" belongs in the set of downloadable input languages, with "zh-Hans", instead of in the set of available input languages, with "zh-Hant".

Combined with the use of LookupMatchingLocaleByBestFit, this means availability() will give the following answers:

function a(languageTag) {
  return Summarizer.availability({
    expectedInputLanguages: [languageTag]
  });
}

await a("zh") === "downloadable";
await a("zh-Hant") === "available";
await a("zh-Hans") === "downloadable";

await a("zh-TW") === "available";      // zh-TW will best-fit to zh-Hant
await a("zh-HK") === "available";      // zh-HK will best-fit to zh-Hant
await a("zh-CN") === "downloadable";   // zh-CN will best-fit to zh-Hans

await a("zh-BR") === "downloadable";   // zh-BR will best-fit to zh
await a("zh-Kana") === "downloadable"; // zh-Kana will best-fit to zh

2.3. The Summarizer class

Every Summarizer has a shared context, a string-or-null, set during creation.

Every Summarizer has a summary type, a SummarizerType, set during creation.

Every Summarizer has a summary format, a SummarizerFormat, set during creation.

Every Summarizer has a summary length, a SummarizerLength, set during creation.

Every Summarizer has an expected input 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 Summarizer has an output language, a string or null, set during creation.

Every Summarizer has a input quota, a number, set during creation.


The sharedContext getter steps are to return this’s shared context.

The type getter steps are to return this’s summary type.

The format getter steps are to return this’s summary format.

The length getter steps are to return this’s summary length.

The expectedInputLanguages getter steps are to return this’s expected input languages.

The expectedContextLanguages getter steps are to return this’s expected context languages.

The outputLanguage getter steps are to return this’s output language.

The inputQuota getter steps are to return this’s input quota.


The summarize(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let operation be an algorithm step which takes arguments chunkProduced, done, error, and stopProducing, and summarizes input given this’s shared context, context, this’s summary type, this’s summary format, this’s summary length, this’s output language, this’s input quota, chunkProduced, done, error, and stopProducing.

  3. Return the result of getting an aggregated AI model result given this, options, and operation.

The summarizeStreaming(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let operation be an algorithm step which takes arguments chunkProduced, done, error, and stopProducing, and summarizes input given this’s shared context, context, this’s summary type, this’s summary format, this’s summary length, this’s output language, this’s input quota, chunkProduced, done, error, and stopProducing.

  3. Return the result of getting a streaming AI model result given this, options, and operation.

The measureInputUsage(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let measureUsage be an algorithm step which takes argument stopMeasuring, and returns the result of measuring summarizer input usage given input, this’s shared context, context, this’s summary type, this’s summary format, this’s summary length, this’s output language, and stopMeasuring.

  3. Return the result of measuring AI model input usage given this, options, and measureUsage.

2.4. Summarization

2.4.1. The algorithm

To summarize given:

perform the following steps:

  1. Assert: this algorithm is running in parallel.

  2. Let requested be the result of measuring summarizer input usage given input, sharedContext, context, type, format, length, outputLanguage, and stopProducing.

  3. If requested is null, then return.

  4. If requested is an error information, then:

    1. Perform error given requested.

    2. Return.

  5. Assert: requested is a number.

  6. If requested is greater than inputQuota, then:

    1. Let errorInfo be a quota exceeded error information with a requested of requested and a quota of inputQuota.

    2. Perform error given errorInfo.

    3. Return.

    In reality, we expect that implementations will check the input usage against the quota as part of the same call into the model as the summarization itself. The steps are only separated in the specification for ease of understanding.

  7. In an implementation-defined manner, subject to the following guidelines, begin the processs of summarizing input into a string.

    If they are non-null, sharedContext and context should be used to aid in the summarization by providing context on how the web developer wishes the input to be summarized.

    If input is the empty string, or otherwise consists of no summarizable content (e.g., only contains whitespace, or control characters), then the resulting summary should be the empty string. In such cases, sharedContext, context, type, format, length, and outputLanguage should be ignored.

    The summarization should conform to the guidance given by type, format, and length, in the definitions of each of their enumeration values.

    If outputLanguage is non-null, the summarization should be in that language. Otherwise, it should be in the language of input (which might not match that of context or sharedContext). If input contains multiple languages, or the language of input cannot be detected, then either the output language is implementation-defined, or the implementation may treat this as an error, per the guidance in § 2.4.4 Errors.

  8. While true:

    1. Wait for the next chunk of summarization data to be produced, for the summarization process to finish, or for the result of calling stopProducing to become true.

    2. If such a chunk is successfully produced:

      1. Let it be represented as a string chunk.

      2. Perform chunkProduced given chunk.

    3. Otherwise, if the summarization process has finished:

      1. Perform done.

      2. Break.

    4. Otherwise, if stopProducing returns true, then break.

    5. Otherwise, if an error occurred during summarization:

      1. Let the error be represented as error information errorInfo according to the guidance in § 2.4.4 Errors.

      2. Perform error given errorInfo.

      3. Break.

2.4.2. Usage

To measure summarizer input usage, given:

perform the following steps:

  1. Assert: this algorithm is running in parallel.

  2. 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 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.

    If an error occurs during this process, then return an appropriate DOMException error information according to the guidance in § 2.4.4 Errors.

  3. 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 input quota is +∞). Otherwise, it must be positive and should be roughly proportional to the 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 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.

    If during this process stopMeasuring starts returning true, then instead return null.

    If an error occurs during this process, then instead return an appropriate DOMException error information according to the guidance in § 2.4.4 Errors.

2.4.3. 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 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.

SummarizerType values
Value Meaning
"tl;dr"

The summary should be short and to the point, providing a quick overview of the input, suitable for a busy reader.

"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"

The summary should extract the most important points from the input, presented as a bulleted list.

"headline"

The summary should effectively contain the main point of the input in a single sentence, in the format of an article headline.

SummarizerLength values
Value Meaning
"short"

The guidance is dependent on the value of SummarizerType:

"tl;dr"
"teaser"

The summary should fit within 1 sentence.

"key-points"

The summary should consist of no more than 3 bullet points.

"headline"

The summary should use no more than 12 words.

"medium"

The guidance is dependent on the value of SummarizerType:

"tl;dr"
"teaser"

The summary should fit within 1 short paragraph.

"key-points"

The summary should consist of no more than 5 bullet points.

"headline"

The summary should use no more than 17 words.

"long"

The guidance is dependent on the value of SummarizerType:

"tl;dr"
"teaser"

The summary should fit within 1 paragraph.

"key-points"

The summary should consist of no more than 7 bullet points.

"headline"

The summary should use no more than 22 words.

SummarizerFormat values
Value Meaning
"plain-text"

The summary should not contain any formatting or markup language.

"markdown"

The summary should be formatted using the Markdown markup language, ideally as valid CommonMark. [COMMONMARK]

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.

2.4.4. Errors

When summarization fails, the following possible reasons may be surfaced to the web developer. This table lists the possible DOMException names and the cases in which an implementation should use them:

DOMException name Scenarios
"NotAllowedError"

Summarization is disabled by user choice or user agent policy.

"NotReadableError"

The summarization output was filtered by the user agent, e.g., because it was detected to be harmful, inaccurate, or nonsensical.

"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 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 create().

The 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"

All other scenarios, or if the user agent would prefer not to disclose the failure reason.

This table does not give the complete list of exceptions that can be surfaced by the summarizer API. It only contains those which can come from certain implementation-defined steps.

2.5. Permissions policy integration

Access to the summarizer API is gated behind the policy-controlled feature "summarizer", which has a default allowlist of 'self'.

3. The writer API

[Exposed=Window, SecureContext]
interface Writer {
  static Promise<Writer> create(optional WriterCreateOptions options = {});
  static Promise<Availability> availability(optional WriterCreateCoreOptions options = {});

  Promise<DOMString> write(
    DOMString input,
    optional WriterWriteOptions options = {}
  );
  ReadableStream writeStreaming(
    DOMString input,
    optional WriterWriteOptions options = {}
  );

  readonly attribute DOMString sharedContext;
  readonly attribute WriterTone tone;
  readonly attribute WriterFormat format;
  readonly attribute WriterLength length;

  readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
  readonly attribute FrozenArray<DOMString>? expectedContextLanguages;
  readonly attribute DOMString? outputLanguage;

  Promise<double> measureInputUsage(
    DOMString input,
    optional WriterWriteOptions options = {}
  );
  readonly attribute unrestricted double inputQuota;
};
Writer includes DestroyableModel;

dictionary WriterCreateCoreOptions {
  WriterTone tone = "neutral";
  WriterFormat format = "markdown";
  WriterLength length = "short";

  sequence<DOMString> expectedInputLanguages;
  sequence<DOMString> expectedContextLanguages;
  DOMString outputLanguage;
};

dictionary WriterCreateOptions : WriterCreateCoreOptions {
  AbortSignal signal;
  CreateMonitorCallback monitor;

  DOMString sharedContext;
};

dictionary WriterWriteOptions {
  DOMString context;
  AbortSignal signal;
};

enum WriterTone { "formal", "neutral", "casual" };
enum WriterFormat { "plain-text", "markdown" };
enum WriterLength { "short", "medium", "long" };

3.1. Creation

The static create(options) method steps are:
  1. Return the result of creating an AI model object given options, "writer", validate and canonicalize writer options, computing writer options availability, download the writer model, initialize the writer model, and create a writer object.

To validate and canonicalize writer options given a WriterCreateCoreOptions options, perform the following steps. They mutate options in place to canonicalize and deduplicate language tags, and throw a TypeError if any are invalid.
  1. Validate and canonicalize language tags given options and "expectedInputLanguages".

  2. Validate and canonicalize language tags given options and "expectedContextLanguages".

  3. Validate and canonicalize language tags given options and "outputLanguage".

To download the writer model, given a WriterCreateCoreOptions options:
  1. Assert: these steps are running in parallel.

  2. Initiate the download process for everything the user agent needs to write text according to options. This could include a base AI model, fine-tunings for specific languages or option values, or other resources.

  3. If the download process cannot be started for any reason, then return false.

  4. Return true.

To initialize the writer model, given a WriterCreateOptions options:
  1. Assert: these steps are running in parallel.

  2. Perform any necessary initialization operations for the AI model backing the user agent’s writing capabilities.

    This could include loading the model into memory, loading options["sharedContext"] into the model’s context window, or loading any fine-tunings necessary to support the other options expressed by options.

  3. If initialization failed because the process of loading options resulted in using up all of the model’s input quota, then:

    1. Let requested be the amount of input usage needed to encode options. The encoding of options as input is implementation-defined.

      This could be the amount of tokens needed to represent these options in a language model tokenization scheme, possibly with prompt engineering. Or it could be 0, if the implementation plans to send the options to the underlying model with every write operation.

    2. Let quota be the maximum input quota that the user agent supports for encoding options.

    3. Assert: requested is greater than quota. (That is how we reached this error branch.)

    4. Return a quota exceeded error information whose requested is requested and quota is quota.

  4. If initialization failed for any other reason, then return a DOMException error information whose name is "OperationError" and whose details contain appropriate detail.

  5. Return null.

To create a writer object, given a realm realm and a WriterCreateOptions options:
  1. Assert: these steps are running on realm’s surrounding agent’s event loop.

  2. Let inputQuota be the amount of input quota that is available to the user agent for future writing operations. (This value is implementation-defined, and may be +∞ if there are no specific limits beyond, e.g., the user’s memory, or the limits of JavaScript strings.)

  3. Return a new Writer object, created in realm, with

    shared context

    options["sharedContext"] if it exists; otherwise null

    tone

    options["tone"]

    format

    options["format"]

    length

    options["length"]

    expected input languages

    the result of creating a frozen array given options["expectedInputLanguages"] if it is not empty; otherwise null

    expected context languages

    the result of creating a frozen array given options["expectedContextLanguages"] if it is not empty; otherwise null

    output language

    options["outputLanguage"] if it exists; otherwise null

    input quota

    inputQuota

3.2. Availability

The static availability(options) method steps are:
  1. Return the result of computing AI model availability given options, "writer", validate and canonicalize writer options, and compute writer options availability.

To compute writer options availability given a WriterCreateCoreOptions options, perform the following steps. They return either an Availability value or null, and they mutate options in place to update language tags to their best-fit matches.
  1. Assert: this algorithm is running in parallel.

  2. Let availability be the writer non-language options availability given options["tone"], options["format"], and options["length"].

  3. Let triple be the writer language availabilities triple.

  4. If triple is null, then return null.

  5. Let inputLanguageAvailability be the result of computing language availability given options["expectedInputLanguages"] and triple’s input languages.

  6. Let contextLanguagesAvailability be the result of computing language availability given options["expectedContextLanguages"] and triple’s context languages.

  7. Let outputLanguagesList be « options["outputLanguage"] ».

  8. Let outputLanguageAvailability be the result of computing language availability given outputLanguagesList and triple’s output languages.

  9. Set options["outputLanguage"] to outputLanguagesList[0].

  10. Return the minimum availability given « availability, inputLanguageAvailability, contextLanguagesAvailability, outputLanguageAvailability ».

The writer non-language options availability, given a WriterTone tone, WriterFormat format, and a WriterLength length, is given by the following steps. They return an Availability value or null.
  1. Assert: this algorithm is running in parallel.

  2. If there is some error attempting to determine whether the user agent supports writing text, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null.

  3. If the user agent supports writing text with the tone described by tone, in the format described by format, and with the length guidance given by length without performing any downloading operations, then return "available".

  4. If the user agent believes it can write text according to tone, format, and length, but only after finishing a download (e.g., of an AI model or fine-tuning) that is already ongoing, then return "downloadable".

  5. If the user agent believes it can write text according to tone, format, and length, but only after performing a download (e.g., of an AI model or fine-tuning), then return "downloadable".

  6. Otherwise, return "unavailable".

The writer language availabilities triple is given by the following steps. They return a language availabilities triple or null.
  1. Assert: this algorithm is running in parallel.

  2. If there is some error attempting to determine whether the user agent supports writing text, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null.

  3. Return a language availabilities triple with:

    input languages

    the result of getting the language availabilities partition given the purpose of writing text based on input in that language

    context languages

    the result of getting the language availabilities partition given the purpose of writing text using web-developer provided context information written in that language

    output languages

    the result of getting the language availabilities partition given the purpose of producing written text in that language

3.3. The Writer class

Every Writer has a shared context, a string-or-null, set during creation.

Every Writer has a tone, a WriterTone, set during creation.

Every Writer has a format, a WriterFormat, set during creation.

Every Writer has a length, a WriterLength, set during creation.

Every Writer has an expected input languages, a FrozenArray<DOMString> or null, set during creation.

Every Writer has an expected context languages, a FrozenArray<DOMString> or null, set during creation.

Every Writer has an output language, a string or null, set during creation.

Every Writer has a input quota, a number, set during creation.


The sharedContext getter steps are to return this’s shared context.

The tone getter steps are to return this’s tone.

The format getter steps are to return this’s format.

The length getter steps are to return this’s length.

The expectedInputLanguages getter steps are to return this’s expected input languages.

The expectedContextLanguages getter steps are to return this’s expected context languages.

The outputLanguage getter steps are to return this’s output language.

The inputQuota getter steps are to return this’s input quota.


The write(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let operation be an algorithm step which takes arguments chunkProduced, done, error, and stopProducing, and writes given input, this’s shared context, context, this’s tone, this’s format, this’s length, this’s output language, this’s input quota, chunkProduced, done, error, and stopProducing.

  3. Return the result of getting an aggregated AI model result given this, options, and operation.

The writeStreaming(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let operation be an algorithm step which takes arguments chunkProduced, done, error, and stopProducing, and writes given input, this’s shared context, context, this’s tone, this’s format, this’s length, this’s output language, this’s input quota, chunkProduced, done, error, and stopProducing.

  3. Return the result of getting a streaming AI model result given this, options, and operation.

The measureInputUsage(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let measureUsage be an algorithm step which takes argument stopMeasuring, and returns the result of measuring writer input usage given input, this’s shared context, context, this’s tone, this’s format, this’s length, this’s output language, and stopMeasuring.

  3. Return the result of measuring AI model input usage given this, options, and measureUsage.

3.4. Writing

3.4.1. The algorithm

To write given:

perform the following steps:

  1. Assert: this algorithm is running in parallel.

  2. Let requested be the result of measuring writer input usage given input, sharedContext, context, tone, format, length, outputLanguage, and stopProducing.

  3. If requested is null, then return.

  4. If requested is an error information, then:

    1. Perform error given requested.

    2. Return.

  5. Assert: requested is a number.

  6. If requested is greater than inputQuota, then:

    1. Let errorInfo be a quota exceeded error information with a requested of requested and a quota of inputQuota.

    2. Perform error given errorInfo.

    3. Return.

  7. In an implementation-defined manner, subject to the following guidelines, begin the processs of writing to a string, based on the writing task specified in input.

    If they are non-null, sharedContext and context should be used to aid in the writing by providing context on how the web developer wishes the writing task to be performed.

    If input is the empty string, then the resulting text should be the empty string.

    The written output should conform to the guidance given by tone, format, and length, in the definitions of each of their enumeration values.

    If outputLanguage is non-null, the writing should be in that language. Otherwise, it should be in the language of input (which might not match that of context or sharedContext). If input contains multiple languages, or the language of input cannot be detected, then either the output language is implementation-defined, or the implementation may treat this as an error, per the guidance in § 3.4.4 Errors.

  8. While true:

    1. Wait for the next chunk of written text to be produced, for the writing process to finish, or for the result of calling stopProducing to become true.

    2. If such a chunk is successfully produced:

      1. Let it be represented as a string chunk.

      2. Perform chunkProduced given chunk.

    3. Otherwise, if the writing process has finished:

      1. Perform done.

      2. Break.

    4. Otherwise, if stopProducing returns true, then break.

    5. Otherwise, if an error occurred during writing:

      1. Let the error be represented as error information errorInfo according to the guidance in § 3.4.4 Errors.

      2. Perform error given errorInfo.

      3. Break.

3.4.2. Usage

To measure writer input usage, given:

perform the following steps:

  1. Assert: this algorithm is running in parallel.

  2. Let inputToModel be the implementation-defined string that would be sent to the underlying model in order to write given input, sharedContext, context, tone, format, length, and outputLanguage.

    If during this process stopMeasuring starts returning true, then return null.

    If an error occurs during this process, then return an appropriate DOMException error information according to the guidance in § 3.4.4 Errors.

  3. 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 writing process (i.e., if the input quota is +∞). Otherwise, it must be positive and should be roughly proportional to the length of inputToModel.

    If during this process stopMeasuring starts returning true, then instead return null.

    If an error occurs during this process, then instead return an appropriate DOMException error information according to the guidance in § 3.4.4 Errors.

3.4.3. Options

The write 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 WriterTone, WriterFormat, and WriterLength enumerations.

This section gives normative guidance on how the implementation of write should use each enumeration value to guide the writing process.

WriterTone values
Value Meaning
"formal"

The writing should use formal language, employing precise terminology, avoiding contractions and slang, and maintaining a professional tone suitable for academic, business, or official contexts.

"neutral"

The writing should use a balanced, moderate tone that is neither overly formal nor casual, suitable for general audiences and informational contexts.

"casual"

The writing should use conversational language, potentially including contractions, colloquialisms, and a more relaxed, friendly tone suitable for informal communication.

WriterLength values
Value Meaning
"short"

The writing should be concise and to the point, using no more than 100 words.

"medium"

The writing should be moderately detailed, using no more than 300 words.

"long"

The writing should be in-depth and thorough, using no more than 500 words.

WriterFormat values
Value Meaning
"plain-text"

The writing should not contain any formatting or markup language.

"markdown"

The writing should be formatted using the Markdown markup language, ideally as valid CommonMark. [COMMONMARK]

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.

3.4.4. Errors

When writing fails, the following possible reasons may be surfaced to the web developer. This table lists the possible DOMException names and the cases in which an implementation should use them:

DOMException name Scenarios
"NotAllowedError"

Writing is disabled by user choice or user agent policy.

"NotReadableError"

The writing output was filtered by the user agent, e.g., because it was detected to be harmful, offensive, or nonsensical.

"NotSupportedError"

The input writing prompt provided, 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 create().

The writing 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 create().

The 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"

All other scenarios, or if the user agent would prefer not to disclose the failure reason.

This table does not give the complete list of exceptions that can be surfaced by the writer API. It only contains those which can come from certain implementation-defined steps.

3.5. Permissions policy integration

Access to the writer API is gated behind the policy-controlled feature "writer", which has a default allowlist of 'self'.

4. The rewriter API

[Exposed=Window, SecureContext]
interface Rewriter {
  static Promise<Rewriter> create(optional RewriterCreateOptions options = {});
  static Promise<Availability> availability(optional RewriterCreateCoreOptions options = {});

  Promise<DOMString> rewrite(
    DOMString input,
    optional RewriterRewriteOptions options = {}
  );
  ReadableStream rewriteStreaming(
    DOMString input,
    optional RewriterRewriteOptions options = {}
  );

  readonly attribute DOMString sharedContext;
  readonly attribute RewriterTone tone;
  readonly attribute RewriterFormat format;
  readonly attribute RewriterLength length;

  readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
  readonly attribute FrozenArray<DOMString>? expectedContextLanguages;
  readonly attribute DOMString? outputLanguage;

  Promise<double> measureInputUsage(
    DOMString input,
    optional RewriterRewriteOptions options = {}
  );
  readonly attribute unrestricted double inputQuota;
};
Rewriter includes DestroyableModel;

dictionary RewriterCreateCoreOptions {
  RewriterTone tone = "as-is";
  RewriterFormat format = "as-is";
  RewriterLength length = "as-is";

  sequence<DOMString> expectedInputLanguages;
  sequence<DOMString> expectedContextLanguages;
  DOMString outputLanguage;
};

dictionary RewriterCreateOptions : RewriterCreateCoreOptions {
  AbortSignal signal;
  CreateMonitorCallback monitor;

  DOMString sharedContext;
};

dictionary RewriterRewriteOptions {
  DOMString context;
  AbortSignal signal;
};

enum RewriterTone { "as-is", "more-formal", "more-casual" };
enum RewriterFormat { "as-is", "plain-text", "markdown" };
enum RewriterLength { "as-is", "shorter", "longer" };

4.1. Creation

The static create(options) method steps are:
  1. Return the result of creating an AI model object given options, "rewriter", validate and canonicalize rewriter options, computing rewriter options availability, download the rewriter model, initialize the rewriter model, and create a rewriter object.

To validate and canonicalize rewriter options given a RewriterCreateCoreOptions options, perform the following steps. They mutate options in place to canonicalize and deduplicate language tags, and throw a TypeError if any are invalid.
  1. Validate and canonicalize language tags given options and "expectedInputLanguages".

  2. Validate and canonicalize language tags given options and "expectedContextLanguages".

  3. Validate and canonicalize language tags given options and "outputLanguage".

To download the rewriter model, given a RewriterCreateCoreOptions options:
  1. Assert: these steps are running in parallel.

  2. Initiate the download process for everything the user agent needs to rewrite text according to options. This could include a base AI model, fine-tunings for specific languages or option values, or other resources.

  3. If the download process cannot be started for any reason, then return false.

  4. Return true.

To initialize the rewriter model, given a RewriterCreateOptions options:
  1. Assert: these steps are running in parallel.

  2. Perform any necessary initialization operations for the AI model backing the user agent’s rewriting capabilities.

    This could include loading the model into memory, loading options["sharedContext"] into the model’s context window, or loading any fine-tunings necessary to support the other options expressed by options.

  3. If initialization failed because the process of loading options resulted in using up all of the model’s input quota, then:

    1. Let requested be the amount of input usage needed to encode options. The encoding of options as input is implementation-defined.

      This could be the amount of tokens needed to represent these options in a language model tokenization scheme, possibly with prompt engineering. Or it could be 0, if the implementation plans to send the options to the underlying model with every rewrite operation.

    2. Let quota be the maximum input quota that the user agent supports for encoding options.

    3. Assert: requested is greater than quota. (That is how we reached this error branch.)

    4. Return a quota exceeded error information whose requested is requested and quota is quota.

  4. If initialization failed for any other reason, then return a DOMException error information whose name is "OperationError" and whose details contain appropriate detail.

  5. Return null.

To create a rewriter object, given a realm realm and a RewriterCreateOptions options:
  1. Assert: these steps are running on realm’s surrounding agent’s event loop.

  2. Let inputQuota be the amount of input quota that is available to the user agent for future rewriting operations. (This value is implementation-defined, and may be +∞ if there are no specific limits beyond, e.g., the user’s memory, or the limits of JavaScript strings.)

  3. Return a new Rewriter object, created in realm, with

    shared context

    options["sharedContext"] if it exists; otherwise null

    tone

    options["tone"]

    format

    options["format"]

    length

    options["length"]

    expected input languages

    the result of creating a frozen array given options["expectedInputLanguages"] if it is not empty; otherwise null

    expected context languages

    the result of creating a frozen array given options["expectedContextLanguages"] if it is not empty; otherwise null

    output language

    options["outputLanguage"] if it exists; otherwise null

    input quota

    inputQuota

4.2. Availability

The static availability(options) method steps are:
  1. Return the result of computing AI model availability given options, "rewriter", validate and canonicalize rewriter options, and compute rewriter options availability.

To compute rewriter options availability given a RewriterCreateCoreOptions options, perform the following steps. They return either an Availability value or null, and they mutate options in place to update language tags to their best-fit matches.
  1. Assert: this algorithm is running in parallel.

  2. Let availability be the rewriter non-language options availability given options["tone"], options["format"], and options["length"].

  3. Let triple be the rewriter language availabilities triple.

  4. If triple is null, then return null.

  5. Let inputLanguageAvailability be the result of computing language availability given options["expectedInputLanguages"] and triple’s input languages.

  6. Let contextLanguagesAvailability be the result of computing language availability given options["expectedContextLanguages"] and triple’s context languages.

  7. Let outputLanguagesList be « options["outputLanguage"] ».

  8. Let outputLanguageAvailability be the result of computing language availability given outputLanguagesList and triple’s output languages.

  9. Set options["outputLanguage"] to outputLanguagesList[0].

  10. Return the minimum availability given « availability, inputLanguageAvailability, contextLanguagesAvailability, outputLanguageAvailability ».

The rewriter non-language options availability, given a RewriterTone tone, RewriterFormat format, and a RewriterLength length, is given by the following steps. They return an Availability value or null.
  1. Assert: this algorithm is running in parallel.

  2. If there is some error attempting to determine whether the user agent supports rewriting text, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null.

  3. If the user agent supports rewriting text with the tone modification described by tone, in the format described by format, and with the length modification given by length without performing any downloading operations, then return "available".

  4. If the user agent believes it can rewrite text according to tone, format, and length, but only after finishing a download (e.g., of an AI model or fine-tuning) that is already ongoing, then return "downloadable".

  5. If the user agent believes it can rewrite text according to tone, format, and length, but only after performing a download (e.g., of an AI model or fine-tuning), then return "downloadable".

  6. Otherwise, return "unavailable".

The rewriter language availabilities triple is given by the following steps. They return a language availabilities triple or null.
  1. Assert: this algorithm is running in parallel.

  2. If there is some error attempting to determine whether the user agent supports rewriting text, which the user agent believes to be transient (such that re-querying could stop producing such an error), then return null.

  3. Return a language availabilities triple with:

    input languages

    the result of getting the language availabilities partition given the purpose of rewriting text written in that language

    context languages

    the result of getting the language availabilities partition given the purpose of rewriting text using web-developer provided context information written in that language

    output languages

    the result of getting the language availabilities partition given the purpose of producing rewritten text in that language

4.3. The Rewriter class

Every Rewriter has a shared context, a string-or-null, set during creation.

Every Rewriter has a tone, a RewriterTone, set during creation.

Every Rewriter has a format, a RewriterFormat, set during creation.

Every Rewriter has a length, a RewriterLength, set during creation.

Every Rewriter has an expected input languages, a FrozenArray<DOMString> or null, set during creation.

Every Rewriter has an expected context languages, a FrozenArray<DOMString> or null, set during creation.

Every Rewriter has an output language, a string or null, set during creation.

Every Rewriter has a input quota, a number, set during creation.


The sharedContext getter steps are to return this’s shared context.

The tone getter steps are to return this’s tone.

The format getter steps are to return this’s format.

The length getter steps are to return this’s length.

The expectedInputLanguages getter steps are to return this’s expected input languages.

The expectedContextLanguages getter steps are to return this’s expected context languages.

The outputLanguage getter steps are to return this’s output language.

The inputQuota getter steps are to return this’s input quota.


The rewrite(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let operation be an algorithm step which takes arguments chunkProduced, done, error, and stopProducing, and rewrites input given this’s shared context, context, this’s tone, this’s format, this’s length, this’s output language, this’s input quota, chunkProduced, done, error, and stopProducing.

  3. Return the result of getting an aggregated AI model result given this, options, and operation.

The rewriteStreaming(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let operation be an algorithm step which takes arguments chunkProduced, done, error, and stopProducing, and rewrites input given this’s shared context, context, this’s tone, this’s format, this’s length, this’s output language, this’s input quota, chunkProduced, done, error, and stopProducing.

  3. Return the result of getting a streaming AI model result given this, options, and operation.

The measureInputUsage(input, options) method steps are:
  1. Let context be options["context"] if it exists; otherwise null.

  2. Let measureUsage be an algorithm step which takes argument stopMeasuring, and returns the result of measuring rewriter input usage given input, this’s shared context, context, this’s tone, this’s format, this’s length, this’s output language, and stopMeasuring.

  3. Return the result of measuring AI model input usage given this, options, and measureUsage.

4.4. Rewriting

4.4.1. The algorithm

To rewrite given:

perform the following steps:

  1. Assert: this algorithm is running in parallel.

  2. Let requested be the result of measuring rewriter input usage given input, sharedContext, context, tone, format, length, outputLanguage, and stopProducing.

  3. If requested is null, then return.

  4. If requested is an error information, then:

    1. Perform error given requested.

    2. Return.

  5. Assert: requested is a number.

  6. If requested is greater than inputQuota, then:

    1. Let errorInfo be a quota exceeded error information with a requested of requested and a quota of inputQuota.

    2. Perform error given errorInfo.

    3. Return.

  7. In an implementation-defined manner, subject to the following guidelines, begin the processs of rewriting input into a string.

    If they are non-null, sharedContext and context should be used to aid in the rewriting by providing context on how the web developer wishes the rewriting task to be performed.

    If input is the empty string, then the resulting text should be the empty string.

    The rewritten output should conform to the guidance given by tone, format, and length, in the definitions of each of their enumeration values.

    If outputLanguage is non-null, the rewritten output text should be in that language. Otherwise, it should be in the language of input (which might not match that of context or sharedContext). If input contains multiple languages, or the language of input cannot be detected, then either the output language is implementation-defined, or the implementation may treat this as an error, per the guidance in § 4.4.4 Errors.

  8. While true:

    1. Wait for the next chunk of rewritten text to be produced, for the rewriting process to finish, or for the result of calling stopProducing to become true.

    2. If such a chunk is successfully produced:

      1. Let it be represented as a string chunk.

      2. Perform chunkProduced given chunk.

    3. Otherwise, if the rewriting process has finished:

      1. Perform done.

      2. Break.

    4. Otherwise, if stopProducing returns true, then break.

    5. Otherwise, if an error occurred during rewriting:

      1. Let the error be represented as error information errorInfo according to the guidance in § 4.4.4 Errors.

      2. Perform error given errorInfo.

      3. Break.

4.4.2. Usage

To measure rewriter input usage, given:

perform the following steps:

  1. Assert: this algorithm is running in parallel.

  2. Let inputToModel be the implementation-defined string that would be sent to the underlying model in order to rewrite given input, sharedContext, context, tone, format, length, and outputLanguage.

    If during this process stopMeasuring starts returning true, then return null.

    If an error occurs during this process, then return an appropriate DOMException error information according to the guidance in § 4.4.4 Errors.

  3. 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 rewriting process (i.e., if the input quota is +∞). Otherwise, it must be positive and should be roughly proportional to the length of inputToModel.

    If during this process stopMeasuring starts returning true, then instead return null.

    If an error occurs during this process, then instead return an appropriate DOMException error information according to the guidance in § 4.4.4 Errors.

4.4.3. Options

The rewrite 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 RewriterTone, RewriterFormat, and RewriterLength enumerations.

This section gives normative guidance on how the implementation of rewrite should use each enumeration value to guide the rewriting process.

RewriterTone values
Value Meaning
"as-is"

The rewriting should preserve the tone of the original text.

"more-formal"

The rewriting should make the text more formal than the original, using more precise terminology, avoiding contractions and slang, and employing a more professional tone suitable for academic, business, or official contexts.

"more-casual"

The rewriting should make the text more casual than the original, using more conversational language, potentially including contractions, colloquialisms, and a more relaxed, friendly tone suitable for informal communication.

RewriterLength values
Value Meaning
"as-is"

The rewriting should aim to preserve the approximate length of the original text.

"shorter"

The rewriting should make the text more concise than the original, omitting or shortening as necessary such that the end result is shorter.

"longer"

The rewriting should expand on the original text, providing more details or elaboration such that the end result is longer.

RewriterFormat values
Value Meaning
"as-is"

The rewriting should preserve the format of the original text.

"plain-text"

The rewriting should convert the text to plain text, removing any formatting or markup language that may be present in the original.

"markdown"

The rewriting should format the text using the Markdown markup language, ideally as valid CommonMark, converting from whatever format the original text was in. [COMMONMARK]

As with all "should"-level guidance, user agents might not conform perfectly to these.

4.4.4. Errors

When rewriting fails, the following possible reasons may be surfaced to the web developer. This table lists the possible DOMException names and the cases in which an implementation should use them:

DOMException name Scenarios
"NotAllowedError"

Rewriting is disabled by user choice or user agent policy.

"NotReadableError"

The rewriting output was filtered by the user agent, e.g., because it was detected to be harmful, offensive, or nonsensical.

"NotSupportedError"

The input to be rewritten, 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 create().

The rewriting 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 create().

The 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"

All other scenarios, or if the user agent would prefer not to disclose the failure reason.

This table does not give the complete list of exceptions that can be surfaced by the rewriter API. It only contains those which can come from certain implementation-defined steps.

4.5. Permissions policy integration

Access to the rewriter API is gated behind the policy-controlled feature "rewriter", which has a default allowlist of 'self'.

5. Shared infrastructure

5.1. Common APIs

[Exposed=Window, SecureContext]
interface CreateMonitor : EventTarget {
  attribute EventHandler ondownloadprogress;
};

callback CreateMonitorCallback = undefined (CreateMonitor monitor);

enum Availability {
  "unavailable",
  "downloadable",
  "downloading",
  "available"
};

interface mixin DestroyableModel {
  undefined destroy();
};

The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by all CreateMonitor objects:

Event handler Event handler event type
ondownloadprogress downloadprogress

Every interface including the DestroyableModel interface mixin has a destruction abort controller, an AbortController, set by the initialize as a destroyable algorithm.

The destruction abort controller is only used internally, as a way of tracking calls to destroy(). Since it is easy to combine multiple AbortSignals 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 destroy().

To initialize as a destroyable an DestroyableModel object destroyable:
  1. Let controller be a new AbortController created in destroyable’s relevant realm.

  2. Set controller’s signal to a new AbortSignal created in destroyable’s relevant realm.

  3. Set destroyable’s destruction abort controller to controller.

The destroy() method steps are to destroy this given a new "AbortError" DOMException.

To destroy an DestroyableModel destroyable, given a JavaScript value reason:
  1. Signal abort given destroyable’s destruction abort controller and reason.

  2. The user agent should release any resources associated with destroyable, such as AI models loaded to support its operation, as long as those resources are not needed for other ongoing operations.

5.2. Creation

To create an AI model object given:

perform the following steps:

  1. Let realm be the current realm.

  2. Assert: realm’s global object is a Window object.

  3. Let document be realm’s global object’s associated Document.

  4. If document is not fully active, then return a promise rejected with an "InvalidStateError" DOMException.

  5. Perform validateAndCanonicalizeOptions given options. If this throws an exception e, catch it, and return a promise rejected with e.

    This can mutate options.

  6. If options["signal"] exists and is aborted, then return a promise rejected with options["signal"]'s abort reason.

  7. If document is not allowed to use permissionsPolicyFeature, then return a promise rejected with a "NotAllowedError" DOMException.

  8. Let fireProgressEvent be an algorithm taking two arguments that does nothing.

  9. If options["monitor"] exists, then:

    1. Let monitor be a new CreateMonitor created in realm.

    2. Invoke options["monitor"] with « monitor » and "rethrow".

      If this throws an exception e, catch it, and return a promise rejected with e.

    3. Set fireProgressEvent to an algorithm taking argument loaded, which performs the following steps:

      1. Assert: this algorithm is running in parallel.

      2. Queue a global task on the AI task source given realm’s global object to perform the following steps:

        1. Fire an event named downloadprogress at monitor, using ProgressEvent, with the loaded attribute initialized to loaded, the total attribute initialized to 1, and the lengthComputable attribute initialized to true.

  10. Let abortedDuringDownload be false.

    This variable will be written to from the event loop, but read from in parallel.

  11. If options["signal"] exists, then add the following abort steps to options["signal"]:

    1. Set abortedDuringDownload to true.

  12. Let promise be a new promise created in realm.

  13. In parallel:

    1. Let availability be the result of performing getAvailability given options.

      This can mutate options.

    2. Switch on availability:

    null
    1. Reject promise with an "UnknownError" DOMException.

    2. Abort these steps.

    "unavailable"
    1. Reject promise with a "NotSupportedError" DOMException.

    2. Abort these steps.

    "available"
    1. Initialize and return an AI model object given promise, options, fireProgressEvent, initialize, and create.

    "downloading"
    "downloadable"
    1. If availability is "downloadable", then:

      1. Let startDownloadResult be the result of performing startDownload given options.

      2. If startDownloadResult is false, then:

        1. Queue a global task on the AI task source given realm’s global object to reject promise with a "NetworkError" DOMException.

        2. Abort these steps.

    2. Run the following steps, but abort when abortedDuringDownload becomes true:

      1. Wait for the total number of bytes to be downloaded to become determined, and let that number be totalBytes.

        This number must be equal to the number of bytes that the user agent needs to download at the present time, not including any that have already been downloaded.

        For example, if another tab has started the download and it is 90% finished, and the user agent is planning to share the model across all tabs, then totalBytes will be 10% of the size of the model, not 100% of the size of the model.

        This prevents the web developer-perceived progress from suddenly jumping from 0% to 90%, and then taking a long time to go from 90% to 100%. It also provides some protection against the (admittedly not very powerful) fingerprinting vector of measuring the current download progress across multiple sites.

      2. Let lastProgressFraction be 0.

      3. Let lastProgressTime be the monotonic clock’s unsafe current time.

      4. Perform fireProgressEvent given 0.

      5. While true:

        1. If downloading has failed, then:

          1. Queue a global task on the AI task source given realm’s global object to reject promise with a "NetworkError" DOMException.

          2. Abort these steps.

        2. Let bytesSoFar be the number of bytes downloaded so far.

        3. Assert: bytesSoFar is greater than or equal to 0, and less than or equal to totalBytes.

        4. If the monotonic clock’s unsafe current time minus lastProgressTime is greater than 50 ms, or bytesSoFar equals totalBytes, then:

          1. Let rawProgressFraction be bytesSoFar divided by totalBytes.

          2. Let progressFraction be floor(rawProgressFraction × 65,536) ÷ 65,536.

            We use a fraction, instead of firing a progress event with the number of bytes downloaded, to avoid giving precise information about the size of the model or other material being downloaded.

            progressFraction is calculated from rawProgressFraction to give a precision of one part in 216. This ensures that over most internet speeds and with most model sizes, the loaded value will be different from the previous one that was fired ~50 milliseconds ago.

            Full calculation

            Assume a 5 GiB download size, and a 20 Mbps download speed (chosen as a number on the lower range from this source). Then, downloading 5 GiB will take:

            5  GiB × 2 30  bytes GiB × 8  bits bytes ÷ 20 × 10 6  bits s × 1000  ms s ÷ 50  ms interval = 49,950  intervals

            Rounding up to the nearest power of two gives a conservative estimate of 65,536 fifty millisecond intervals, so we want to give progress to 1 part in 216.

          3. If progressFraction is not equal to lastProgressFraction, then perform fireProgressEvent given progressFraction.

          4. If bytesSoFar equals totalBytes, then break.

            Since this is the only non-failure exit condition for the loop, we will never miss firing a downloadprogress event for the 100% mark.

          5. Set lastProgressFraction to progressFraction.

          6. Set lastProgressTime to the monotonic clock’s unsafe current time.

    3. If aborted, then:

      1. Queue a global task on the AI task source given realm’s global object to perform the following steps:

        1. Assert: options["signal"] is aborted.

        2. Reject promise with options["signal"]'s abort reason.

      2. Abort these steps.

    4. Initialize and return an AI model object given promise, options, a no-op algorithm, initialize, and create.

  14. Return promise.

To initialize and return an AI model object given a Promise promise, an ordered map options, and algorithms fireProgressEvent, initialize, and create:
  1. Assert: these steps are running in parallel.

  2. Perform fireProgressEvent given 0.

  3. Perform fireProgressEvent given 1.

  4. Let result be the result of performing initialize given options.

  5. Queue a global task on the AI task source given promise’s relevant global object to perform the following steps:

    1. If options["signal"] exists and is aborted, then:

      1. Reject promise with options["signal"]'s abort reason.

      2. Abort these steps.

      This check is necessary in case any code running on the event loop caused the AbortSignal to become aborted before this task ran.

    2. If result is an error information, then:

      1. Reject promise with the result of converting error information into an exception object given result.

      2. Abort these steps.

    3. Let model be the result of performing create given promise’s relevant global object and options.

    4. Assert: model implements an interface that includes DestroyableModel.

    5. Initialize as a destroyable model.

    6. If options["signal"] exists, then add the following abort steps to options["signal"]:

      1. Destroy model given options["signal"]'s abort reason.

    7. Resolve promise with model.

5.3. Obtaining results and usage

To get an aggregated AI model result given an DestroyableModel modelObject, an ordered map options, and an algorithm operation:
  1. Let global be modelObject’s relevant global object.

  2. Assert: global is a Window object.

  3. If global’s associated Document is not fully active, then return a promise rejected with an "InvalidStateError" DOMException.

  4. Let signals be « modelObject’s destruction abort controller’s signal ».

  5. If options["signal"] exists, then append it to signals.

  6. Let compositeSignal be the result of creating a dependent abort signal given signals using AbortSignal and modelObject’s relevant realm.

  7. If compositeSignal is aborted, then return a promise rejected with compositeSignal’s abort reason.

  8. Let abortedDuringOperation be false.

    This variable will be written to from the event loop, but read from in parallel.

  9. Add the following abort steps to compositeSignal:

    1. Set abortedDuringOperation to true.

  10. Let promise be a new promise created in modelObject’s relevant realm.

  11. In parallel:

    1. Let result be the empty string.

    2. Let chunkProduced be the following steps given a string chunk:

      1. Queue a global task on the AI task source given global to perform the following steps:

        1. If abortedDuringOperation is true, then reject promise with compositeSignal’s abort reason.

        2. Otherwise, append chunk to result.

    3. Let done be the following steps:

      1. Queue a global task on the AI task source given |global to perform the following steps:

        1. If abortedDuringOperation is true, then reject promise with compositeSignal’s abort reason.

        2. Otherwise, resolve promise with result.

    4. Let error be the following steps given error information errorInfo:

      1. Queue a global task on the AI task source given global to perform the following steps:

        1. If abortedDuringOperation is true, then reject promise with compositeSignal’s abort reason.

        2. Otherwise, reject promise with the result converting error information into an exception object given errorInfo.

    5. Let stopProducing be the following steps:

      1. Return abortedDuringOperation.

    6. Perform operation given chunkProduced, done, error, and stopProducing.

  12. Return promise.

To get a streaming AI model result given an DestroyableModel modelObject, an ordered map options, and an algorithm operation:
  1. Let global be modelObject’s relevant global object.

  2. Assert: global is a Window object.

  3. If global’s associated Document is not fully active, then throw an "InvalidStateError" DOMException.

  4. Let signals be « modelObject’s destruction abort controller’s signal ».

  5. If options["signal"] exists, then append it to signals.

  6. Let compositeSignal be the result of creating a dependent abort signal given signals using AbortSignal and modelObject’s relevant realm.

  7. If compositeSignal is aborted, then return a promise rejected with compositeSignal’s abort reason.

  8. Let abortedDuringOperation be false.

    This variable will be written to from the event loop, but read from in parallel.

  9. Add the following abort steps to compositeSignal:

    1. Set abortedDuringOperation to true.

  10. Let stream be a new ReadableStream created in modelObject’s relevant realm.

  11. Let canceledDuringOperation be false.

    This variable tracks web developer stream cancelations via stream.cancel(), which are not surfaced as errors. It will be written to from the event loop, but sometimes read from in parallel.

  12. Set up stream with cancelAlgorithm set to the following steps (ignoring the reason argument):

    1. Set canceledDuringOperation to true.

  13. In parallel:

    1. Let chunkProduced be the following steps given a string chunk:

      1. Queue a global task on the AI task source given global to perform the following steps:

        1. If abortedDuringOperation is true, then error stream with compositeSignal’s abort reason.

        2. Otherwise, enqueue chunk into stream.

    2. Let done be the following steps:

      1. Queue a global task on the AI task source given global to perform the following steps:

        1. If abortedDuringOperation is true, then error stream with compositeSignal’s abort reason.

        2. Otherwise, close stream.

    3. Let error be the following steps given error information errorInfo:

      1. Queue a global task on the AI task source given global to perform the following steps:

        1. If abortedDuringOperation is true, then error stream with compositeSignal’s abort reason.

        2. Otherwise, error stream with the result of converting error information into an exception object given errorInfo.

    4. Let stopProducing be the following steps:

      1. If either abortedDuringOperation or canceledDuringOperation are true, then return true.

      2. Return false.

    5. Perform operation given chunkProduced, done, error, and stopProducing.

  14. Return stream.

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 fully active, then return a promise rejected with an "InvalidStateError" DOMException.

  2. Let signals be « modelObject’s destruction abort controller’s signal ».

  3. If options["signal"] exists, then append it to signals.

  4. Let compositeSignal be the result of creating a dependent abort signal given signals using AbortSignal and modelObject’s relevant realm.

  5. If compositeSignal is aborted, then return a promise rejected with compositeSignal’s abort reason.

  6. Let abortedDuringMeasurement be false.

    This variable will be written to from the event loop, but read from in parallel.

  7. Add the following abort steps to compositeSignal:

    1. Set abortedDuringMeasurement to true.

  8. Let promise be a new promise created in modelObject’s relevant realm.

  9. In parallel:

    1. Let stopMeasuring be the following steps:

      1. Return abortedDuringMeasurement.

    2. Let result be the result of performing measure given stopMeasuring.

    3. Queue a global task on the AI task source given modelObject’s relevant global object to perform the following steps:

      1. If abortedDuringMeasurement is true, then reject promise with compositeSignal’s abort reason.

      2. Otherwise, if result is an error information, then reject promise with the result converting error information into an exception object given result.

      3. Otherwise,

        1. Assert: result is a number. (It is not null, since in that case abortedDuringMeasurement would have been true.)

        2. Resolve promise with result.

  10. Return promise.

5.4. Language tags

To validate and canonicalize language tags given a ordered map options and a string key, perform the following steps. They mutate options in place to canonicalize and deduplicate language tags found in options[key], and throw a TypeError if any are invalid.
  1. Assert: options[key] exists.

  2. If options[key] is a string, then set options[key] to the result of validating and canonicalizing a single language tag given options[key].

  3. Otherwise:

    1. Assert: options[key] either does not exist, or it is a list of strings.

    2. Let languageTags be an empty ordered set.

    3. If options[key] exists, then for each languageTag of options[key]:

      1. If IsStructurallyValidLanguageTag(languageTag) is false, then throw a TypeError.

      2. Append the result of validating and canonicalizing a single language tag to languageTags.

    4. Set options[key] to languageTags.

To validate and canonicalize a single language tag given a string potentialLanguageTag:
  1. If IsStructurallyValidLanguageTag(potentialLanguageTag) is false, then throw a TypeError.

  2. Return CanonicalizeUnicodeLocaleId(potentialLanguageTag).

A set of Unicode canonicalized locale identifiers languageTags meets the language tag set completeness rules if for every item languageTag of languageTags, if languageTag has more than one subtag, then languageTags must also contain a less narrow language tag with the same language subtag and a strict subset of the same following subtags (i.e., omitting one or more).

This definition is intended to align with that of [[AvailableLocales]] in ECMAScript Internationalization API Specification. [ECMA-402]

This means that if an implementation supports summarization of "de-DE" input text, it will also count as supporting "de" input text.

The converse direction is supported not by the language tag set completeness rules, but instead by the use of LookupMatchingLocaleByBestFit, which ensures that if an implementation supports summarizing "de" input text, it also counts as supporting summarization of "de-CH", "de-Latn-CH", etc.

5.5. Availability

To compute AI model availability given options, a policy-controlled feature permissionsPolicyFeature, an algorithm validate, and an algorithm compute:
  1. Let global be the current global object.

  2. Assert: global is a Window object.

  3. Let document be global’s associated Document.

  4. If document is not fully active, then return a promise rejected with an "InvalidStateError" DOMException.

  5. Perform validate given options.

  6. If document is not allowed to use permissionsPolicyFeature, then return a promise resolved with "unavailable".

  7. Let promise be a new promise created in global’s realm.

  8. In parallel:

    1. Let availability be the result of compute given options.

    2. 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.

      2. Otherwise, resolve promise with availability.

The minimum availability given a list of Availability-or-null values availabilities is:
  1. If availabilities contains null, then return null.

  2. If availabilities contains "unavailable", then return "unavailable".

  3. If availabilities contains "downloading", then return "downloading".

  4. If availabilities contains "downloadable", then return "downloadable".

  5. Return "available".

5.6. Language availability

A language availabilities partition is a map whose keys are "downloading", "downloadable", or "available", and whose values are sets of strings representing Unicode canonicalized locale identifiers. [ECMA-402]

A language availabilities triple is a struct with the following items:

To get the language availabilities partition given a description purpose of the purpose for which we’re checking language availability:
  1. Let partition be «[ "available" → an empty set, "downloading" → an empty set, "downloadable" → an empty set ]».

  2. 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. Append languageTag to partition["available"].

  3. 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. Append languageTag to partition["downloading"].

  4. 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. Append languageTag to partition["downloadable"].

  5. Assert: partition["available"], partition["downloading"], and partition["downloadable"] are disjoint.

  6. If the union of partition["available"], partition["downloading"], and partition["downloadable"] does not meet the language tag set completeness rules, then:

    1. Let missingLanguageTags be the set of missing language tags necessary for that union to meet the language tag set completeness rules.

    2. For each languageTag of missingLanguageTags:

      1. Append languageTag to one of the three sets. Which of the sets to append to is implementation-defined, and should be guided by considerations similar to that of LookupMatchingLocaleByBestFit in terms of keeping "best fallback languages" together.

    3. Return partition.

To compute language availability given an ordered set of strings requestedLanguages and a language availabilities partition partition, perform the following steps. They return an Availability value, and they mutate requestedLanguages in place to update language tags to their best-fit matches.
  1. Let availability be "available".

  2. For each language of requestedLanguages:

    1. For each availabilityToCheck of « "available", "downloading", "downloadable" »:

      1. Let languagesWithThisAvailability be partition[availabilityToCheck].

      2. Let bestMatch be LookupMatchingLocaleByBestFit(languagesWithThisAvailability, « language »).

      3. If bestMatch is not undefined, then:

        1. Replace language with bestMatch.[[locale]] in requestedLanguages.

        2. Set availability to the minimum availability given availability and availabilityToCheck.

        3. Break.

    2. Return "unavailable".

  3. Return availability.

5.7. Errors

An error information is used to communicate error information from in parallel to the event loop. It is either a quota exceeded error information or a DOMException error information.

A DOMException error information is a struct with the following items:

name

a string that will be used for the DOMException’s name.

details

other information necessary to create a useful DOMException for the web developer. (Typically, just an exception message.)

A quota exceeded error information is a struct with the following items:

requested

a number that will be used for the QuotaExceededError’s requested.

quota

a number that will be used for the QuotaExceededError’s quota.

The parts of this specification related to quota exceeded errors assume that whatwg/webidl#1465 will be merged.

To convert error information into an exception object, given an error information errorInfo:
  1. If errorInfo is a DOMException error information, then return a new DOMException with name given by errorInfo’s name, using errorInfo’s details to populate the message appropriately.

  2. Otherwise:

    1. Assert: error is a quota exceeded error information.

    2. Return a new QuotaExceededError whose requested is error’s requested and quota is error’s quota.

5.8. Task source

Tasks queued by this specification use the AI task source.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[COMMONMARK]
CommonMark Spec. URL: https://spec.commonmark.org/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMA-402]
ECMAScript Internationalization API Specification. URL: https://tc39.es/ecma402/
[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[STREAMS]
Adam Rice; et al. Streams Standard. Living Standard. URL: https://streams.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[XHR]
Anne van Kesteren. XMLHttpRequest Standard. Living Standard. URL: https://xhr.spec.whatwg.org/

IDL Index

[Exposed=Window, SecureContext]
interface Summarizer {
  static Promise<Summarizer> create(optional SummarizerCreateOptions options = {});
  static Promise<Availability> availability(optional SummarizerCreateCoreOptions options = {});

  Promise<DOMString> summarize(
    DOMString input,
    optional SummarizerSummarizeOptions options = {}
  );
  ReadableStream summarizeStreaming(
    DOMString input,
    optional SummarizerSummarizeOptions options = {}
  );

  readonly attribute DOMString sharedContext;
  readonly attribute SummarizerType type;
  readonly attribute SummarizerFormat format;
  readonly attribute SummarizerLength length;

  readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
  readonly attribute FrozenArray<DOMString>? expectedContextLanguages;
  readonly attribute DOMString? outputLanguage;

  Promise<double> measureInputUsage(
    DOMString input,
    optional SummarizerSummarizeOptions options = {}
  );
  readonly attribute unrestricted double inputQuota;
};
Summarizer includes DestroyableModel;

dictionary SummarizerCreateCoreOptions {
  SummarizerType type = "key-points";
  SummarizerFormat format = "markdown";
  SummarizerLength length = "short";

  sequence<DOMString> expectedInputLanguages;
  sequence<DOMString> expectedContextLanguages;
  DOMString outputLanguage;
};

dictionary SummarizerCreateOptions : SummarizerCreateCoreOptions {
  AbortSignal signal;
  CreateMonitorCallback monitor;

  DOMString sharedContext;
};

dictionary SummarizerSummarizeOptions {
  AbortSignal signal;
  DOMString context;
};

enum SummarizerType { "tl;dr", "teaser", "key-points", "headline" };
enum SummarizerFormat { "plain-text", "markdown" };
enum SummarizerLength { "short", "medium", "long" };

[Exposed=Window, SecureContext]
interface Writer {
  static Promise<Writer> create(optional WriterCreateOptions options = {});
  static Promise<Availability> availability(optional WriterCreateCoreOptions options = {});

  Promise<DOMString> write(
    DOMString input,
    optional WriterWriteOptions options = {}
  );
  ReadableStream writeStreaming(
    DOMString input,
    optional WriterWriteOptions options = {}
  );

  readonly attribute DOMString sharedContext;
  readonly attribute WriterTone tone;
  readonly attribute WriterFormat format;
  readonly attribute WriterLength length;

  readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
  readonly attribute FrozenArray<DOMString>? expectedContextLanguages;
  readonly attribute DOMString? outputLanguage;

  Promise<double> measureInputUsage(
    DOMString input,
    optional WriterWriteOptions options = {}
  );
  readonly attribute unrestricted double inputQuota;
};
Writer includes DestroyableModel;

dictionary WriterCreateCoreOptions {
  WriterTone tone = "neutral";
  WriterFormat format = "markdown";
  WriterLength length = "short";

  sequence<DOMString> expectedInputLanguages;
  sequence<DOMString> expectedContextLanguages;
  DOMString outputLanguage;
};

dictionary WriterCreateOptions : WriterCreateCoreOptions {
  AbortSignal signal;
  CreateMonitorCallback monitor;

  DOMString sharedContext;
};

dictionary WriterWriteOptions {
  DOMString context;
  AbortSignal signal;
};

enum WriterTone { "formal", "neutral", "casual" };
enum WriterFormat { "plain-text", "markdown" };
enum WriterLength { "short", "medium", "long" };

[Exposed=Window, SecureContext]
interface Rewriter {
  static Promise<Rewriter> create(optional RewriterCreateOptions options = {});
  static Promise<Availability> availability(optional RewriterCreateCoreOptions options = {});

  Promise<DOMString> rewrite(
    DOMString input,
    optional RewriterRewriteOptions options = {}
  );
  ReadableStream rewriteStreaming(
    DOMString input,
    optional RewriterRewriteOptions options = {}
  );

  readonly attribute DOMString sharedContext;
  readonly attribute RewriterTone tone;
  readonly attribute RewriterFormat format;
  readonly attribute RewriterLength length;

  readonly attribute FrozenArray<DOMString>? expectedInputLanguages;
  readonly attribute FrozenArray<DOMString>? expectedContextLanguages;
  readonly attribute DOMString? outputLanguage;

  Promise<double> measureInputUsage(
    DOMString input,
    optional RewriterRewriteOptions options = {}
  );
  readonly attribute unrestricted double inputQuota;
};
Rewriter includes DestroyableModel;

dictionary RewriterCreateCoreOptions {
  RewriterTone tone = "as-is";
  RewriterFormat format = "as-is";
  RewriterLength length = "as-is";

  sequence<DOMString> expectedInputLanguages;
  sequence<DOMString> expectedContextLanguages;
  DOMString outputLanguage;
};

dictionary RewriterCreateOptions : RewriterCreateCoreOptions {
  AbortSignal signal;
  CreateMonitorCallback monitor;

  DOMString sharedContext;
};

dictionary RewriterRewriteOptions {
  DOMString context;
  AbortSignal signal;
};

enum RewriterTone { "as-is", "more-formal", "more-casual" };
enum RewriterFormat { "as-is", "plain-text", "markdown" };
enum RewriterLength { "as-is", "shorter", "longer" };

[Exposed=Window, SecureContext]
interface CreateMonitor : EventTarget {
  attribute EventHandler ondownloadprogress;
};

callback CreateMonitorCallback = undefined (CreateMonitor monitor);

enum Availability {
  "unavailable",
  "downloadable",
  "downloading",
  "available"
};

interface mixin DestroyableModel {
  undefined destroy();
};