Prompt API: Add overview for Prompt API - #43848
Conversation
pepelsbey
left a comment
There was a problem hiding this comment.
Thank you for contributing! It looks good overall, but there are a few missing parts and consistency with the API landing template I would suggest addressing.
Missing required sections:
- Examples
- Browser compatibility
See the API landing page template
I would also suggest applying some of these changes to other branches/PRs.
|
@pepelsbey, please take another look. |
|
Thank you for accepting my suggestions! I also pointed out in the review that some of the required sections are missing. I would suggest taking a closer look at the template for that type of page. |
jmedley
left a comment
There was a problem hiding this comment.
@hamishwillee please take another look
|
@jmedley please have a closer look at my review. Essential parts of the reference are still missing from this PR. Thank you! |
7ff9b3f to
16d544f
Compare
|
@pepelsbey @jmedley I have pulled in all the docs into this one PR via cherry pick and rebase. Then ran a claude spelling and grammar check, followed by prettier. Should be reviewable now. @pepelsbey Do you want me involved in the review? I'm back Friday now - could look then. |
|
@hamishwillee I can't find anything else to resolve. Please take another look. |
| console.log(`${session.contextUsage}/${session.contextWindow}`); | ||
| ``` | ||
|
|
||
| When a method call such as {{domxref("LanguageModel.prompt()", "prompt()")}} or {{domxref("LanguageModel.promptStreaming()", "promptStreaming()")}} would exceed the remaining number of tokens in the context window, a `QuotaExceededError` {{domxref("DOMException")}} is thrown and the {{domxref("LanguageModel.contextoverflow_event", "contextoverflow")}} event fires. |
There was a problem hiding this comment.
Another case of "is thrown" - errors are rejected promises. Can you do a search/replace check?
| }); | ||
| ``` | ||
|
|
||
| After a `LanguageModel` has been created, you can release its assigned resources and stop any further activity by calling its {{domxref("LanguageModel.destroy()")}} method. You are encouraged to do this after you've finished with the object as it can consume a lot of resources. |
There was a problem hiding this comment.
Pedantic, but you can't destroy what doesn't exist. Something like
| After a `LanguageModel` has been created, you can release its assigned resources and stop any further activity by calling its {{domxref("LanguageModel.destroy()")}} method. You are encouraged to do this after you've finished with the object as it can consume a lot of resources. | |
| When you have finished with a {{domxref("LanguageModel")}} you can release its assigned resources and stop any further activity by calling its {{domxref("LanguageModel.destroy()")}} method. You are encouraged to do this when you finish your session, as it can consume a lot of resources. |
| const promptOutput = document.querySelector(".prompt-output"); | ||
| ``` | ||
|
|
||
| Next, we create a global `session` variable to hold our session. Because using the API requires transient activation, we populate `session` inside a `focus` event handler on the `<textarea>`. When the user focuses the `<textarea>`, we first check whether the API is supported; if not, we print a non-support message and `return` early. Next, we check whether `session` already has a value assigned (we don't want to create a new session each time). If not, we run the `init()` function, which generates a `LanguageModel` instance using the custom `getSession()` function defined later on. |
There was a problem hiding this comment.
FWIW You're checking API support based on LanguageModel being defined on the window. That doesn't need transient activation.
This matters a little, because my first thought was "Oh, you have to interact to display support, that's a flaw in the API design"
| - `assistant` | ||
| - : Inputs that are written from the point of view of the AI assistant, which mainly serve to provide context/history, and further shape how the model responds. These are commonly used for [preserving sessions](/en-US/docs/Web/API/Prompt_API/Preserving_sessions) and [few-shot prompts](#few-shot_prompts). | ||
| - `system` | ||
| - : Global inputs from the overall system that give the model instructions on how to respond. If a `system` input is included, it must come first in the provided inputs, otherwise an exception is thrown. As a result, `system` inputs are usually only included as [initial prompts](#providing_initial_prompts_during_session_creation). |
There was a problem hiding this comment.
Is an exception thrown, and is "as a result" true. Maybe
| - : Global inputs from the overall system that give the model instructions on how to respond. If a `system` input is included, it must come first in the provided inputs, otherwise an exception is thrown. As a result, `system` inputs are usually only included as [initial prompts](#providing_initial_prompts_during_session_creation). | |
| - : Global inputs from the overall system that give the model instructions on how to respond. If a `system` input is included, it must come first in the provided inputs, otherwise the returned promise will reject with an exception. `system` inputs are usually only included as [initial prompts](#providing_initial_prompts_during_session_creation). |
| ]); | ||
| ``` | ||
|
|
||
| You don't need this form unless you are providing the assistant with `image` and/or `audio` inputs (see [multimodal prompts](/en-US/docs/Web/API/Prompt_API/Multimodal)). |
There was a problem hiding this comment.
Should it be:
| You don't need this form unless you are providing the assistant with `image` and/or `audio` inputs (see [multimodal prompts](/en-US/docs/Web/API/Prompt_API/Multimodal)). | |
| You don't need this form unless you are providing the assistant with `image` and/or `audio` inputs (see [multimodal prompts](/en-US/docs/Web/API/Prompt_API/Multimodal)): |
| ]); | ||
| ``` | ||
|
|
||
| However, you could rewrite the previous multiple-user-input example into this form, which includes both messages in a single input object. You might find this version easier to understand: |
There was a problem hiding this comment.
I don't feel like "however" follows here, and it is easy to forget which example we're talking about (previous). Maybe this helps.
| However, you could rewrite the previous multiple-user-input example into this form, which includes both messages in a single input object. You might find this version easier to understand: | |
| Note that you could rewrite the first example, which had multiple user input objects in an array, in the same form. This then includes both messages in a single input object, which you may find easier to understand: |
|
|
||
| ## Few-shot prompts | ||
|
|
||
| A few-shot prompt is a set of `user` role and `assistant` role input pairs passed as an example to the API to train it how to respond to a particular type of input, before asking it to complete a similar task. |
There was a problem hiding this comment.
Long sentence. Maybe
| A few-shot prompt is a set of `user` role and `assistant` role input pairs passed as an example to the API to train it how to respond to a particular type of input, before asking it to complete a similar task. | |
| A few-shot prompt is a set of `user` role and `assistant` role input pairs passed as an example to the API — in order to train it how to respond to a particular type of input before asking it to complete a similar task. |
|
|
||
| A few-shot prompt is a set of `user` role and `assistant` role input pairs passed as an example to the API to train it how to respond to a particular type of input, before asking it to complete a similar task. | ||
|
|
||
| The following example shows how to use a few-shot prompt to ask the API for a specific task (French translation) to be delivered in a specific format, before providing some examples to help it learn the correct output format. |
There was a problem hiding this comment.
Awkward. This reads better, but might not capture everything you want.
| The following example shows how to use a few-shot prompt to ask the API for a specific task (French translation) to be delivered in a specific format, before providing some examples to help it learn the correct output format. | |
| The following example shows how to use a few-shot prompt to request a French translation in a specific format, providing sample inputs and outputs to demonstrate the expected structure. |
|
|
||
| ## Adding response constraints | ||
|
|
||
| The `prompt()` and {{domxref("LanguageModel.promptStreaming", "promptStreaming()")}} methods both accept a [`responseConstraint`](/en-US/docs/Web/API/LanguageModel/prompt#responseconstraint) option that takes as its value a [JSON Schema](https://json-schema.org/) object defining the precise format the assistant's responses should be delivered in. This delivers more precise results than just asking the API to respond in a particular way via a `system` prompt, for example. |
There was a problem hiding this comment.
Too much "precise". How about
| The `prompt()` and {{domxref("LanguageModel.promptStreaming", "promptStreaming()")}} methods both accept a [`responseConstraint`](/en-US/docs/Web/API/LanguageModel/prompt#responseconstraint) option that takes as its value a [JSON Schema](https://json-schema.org/) object defining the precise format the assistant's responses should be delivered in. This delivers more precise results than just asking the API to respond in a particular way via a `system` prompt, for example. | |
| The `prompt()` and {{domxref("LanguageModel.promptStreaming", "promptStreaming()")}} methods both accept a [`responseConstraint`](/en-US/docs/Web/API/LanguageModel/prompt#responseconstraint) option that takes as its value a [JSON Schema](https://json-schema.org/) object that defines the exact format expected for the assistant's responses. This delivers more controlled results than just asking the API to respond in a particular way via a `system` prompt. |
|
|
||
| ### JavaScript | ||
|
|
||
| We create a `session` variable to hold our session. Because using the API requires [transient activation](/en-US/docs/Glossary/Transient_activation), we populate `session` inside a `focus` event handler on the `<input>` element. When the user focuses the `<input>`, we first check whether the API is supported; if not, we print a non-support message and `return` early. Next, we check whether `session` already has a value assigned (we don't want to create a new session each time). If not, we run the `init()` function, which generates a `LanguageModel` instance using the custom `getSession()` function. |
There was a problem hiding this comment.
Same comment on transien activation - you don't need it for the non-support mesage
|
|
||
| ## Adding the prompt history to the session | ||
|
|
||
| Next, we create a `session` variable to hold our session. Because using the API requires [transient activation](/en-US/docs/Glossary/Transient_activation), we populate `session` inside a `focus` event handler on the `<textarea>`. When the user focuses the `<textarea>`, we first check whether the API is supported; if not, we print a non-support message and `return` early. Next, we check whether `session` already has a value assigned (we don't want to create a new session each time). If not, we run the `init()` function, which generates a `LanguageModel` instance using the custom `getSession()` function. We pass `getSession()` the `promptHistory` variable from earlier to add the saved history to the session on creation. |
There was a problem hiding this comment.
Same comment on transient activation - you don't need it for the non-support mesage
| <label for="url">Choose image from your local files:</label> | ||
| <input type="file" id="inputElem" accept="image/*" /> | ||
| </div> | ||
| <button type="submit" id="submit">Submit query</button |
There was a problem hiding this comment.
Example is good, but selecting the image does not update the image in the UI - that doesn't happen until you submit query. It just feels very odd as a UI.
If it were me I'd update the image shown as soon as it is selected and clear any text in the prompt response.
| - {{httpheader("Permissions-Policy")}}; the {{httpheader("Permissions-Policy/language-model", "language-model")}} directive | ||
| - : Controls access to the prompt functionality. | ||
| Where a policy specifically disallows its use, the {{domxref("LanguageModel.availability_static", "LanguageModel.availability()")}} static method will return `unavailable`, and any attempts to call other `LanguageModel` methods will fail with a `NotAllowedError` {{domxref("DOMException")}}. | ||
|
|
There was a problem hiding this comment.
CreateMonitor is shown in the sidebar but not listed here - it should be as a related interface. It should also be updated to note that it can be used with LanguageModel.
| ## Events | ||
|
|
||
| - {{domxref("LanguageModel.contextoverflow_event", "contextoverflow")}} | ||
| - : Fired when a `prompt()`, `promptStreaming()`, or `append()` call would exceed the context window size. |
There was a problem hiding this comment.
| - : Fired when a `prompt()`, `promptStreaming()`, or `append()` call would exceed the context window size. | |
| - : Fired when a `prompt()`, `promptStreaming()`, or `append()` call exceeds the context window size. |
|
|
||
| This value increases every time you call {{domxref("LanguageModel.prompt()", "prompt()")}}, {{domxref("LanguageModel.promptStreaming()", "promptStreaming()")}}, or {{domxref("LanguageModel.append()", "append()")}}. | ||
|
|
||
| Compare `contextUsage` with {{domxref("LanguageModel.contextWindow")}} to determine how much space remains. When `contextUsage` would exceed `contextWindow`, subsequent method calls throw a `QuotaExceededError` and the {{domxref("LanguageModel.contextoverflow_event", "contextoverflow")}} event fires. |
There was a problem hiding this comment.
| Compare `contextUsage` with {{domxref("LanguageModel.contextWindow")}} to determine how much space remains. When `contextUsage` would exceed `contextWindow`, subsequent method calls throw a `QuotaExceededError` and the {{domxref("LanguageModel.contextoverflow_event", "contextoverflow")}} event fires. | |
| Compare `contextUsage` with {{domxref("LanguageModel.contextWindow")}} to determine how many tokens remain. When `contextUsage` would exceed `contextWindow`, subsequent method calls throw a `QuotaExceededError` and the {{domxref("LanguageModel.contextoverflow_event", "contextoverflow")}} event fires. |
|
|
||
| The **`contextWindow`** read-only property of the {{domxref("LanguageModel")}} interface returns the total number of context window tokens available for this session. It is set when the session is created and does not change during the session's lifetime. | ||
|
|
||
| Compare `contextWindow` against {{domxref("LanguageModel.contextUsage", "contextUsage")}} to determine how much space remains. Use {{domxref("LanguageModel.measureContextUsage()", "measureContextUsage()")}} to estimate how many tokens a new prompt would consume before sending it. |
There was a problem hiding this comment.
See https://github.com/mdn/content/pull/43848/changes#r3688399412 - "space" isn't quite the best word.
| const promptText = "Let me ask you an interesting question..."; | ||
|
|
||
| async function contextAvailable(promptText) { | ||
| const remaining = session.contextWindow - session.contextUsage; |
There was a problem hiding this comment.
I'm OK with this as an example, but FWIW you might not want to run the next code if the value back is Infinity
|
|
||
| The **`availability()`** static method of the {{domxref("LanguageModel")}} interface returns a status identifier indicating whether the browser's language model supports a given set of configuration options, without creating a session or triggering a download. | ||
|
|
||
| Use `availability()` before calling {{domxref("LanguageModel.create_static", "LanguageModel.create()")}} to determine whether the desired configuration is supported. This avoids initiating a session (and spending tokens) only to have it fail, and lets you provide a meaningful fallback to users when the configuration is not supported. |
There was a problem hiding this comment.
We don't care about spending tokens at this point because we don't have a session yet. The problem with the terminology is it makes people think that there is a cost in the same way as a subscription. Should check that is purged everywhere.
| Use `availability()` before calling {{domxref("LanguageModel.create_static", "LanguageModel.create()")}} to determine whether the desired configuration is supported. This avoids initiating a session (and spending tokens) only to have it fail, and lets you provide a meaningful fallback to users when the configuration is not supported. | |
| Use `availability()` before calling {{domxref("LanguageModel.create_static", "LanguageModel.create()")}} to determine whether the desired configuration is supported. This avoids initiating a session only to have it fail, and lets you provide a meaningful fallback to users when the configuration is not supported. |
| {{APIRef("Prompt API")}}{{SecureContext_Header}} | ||
|
|
||
| The **`create()`** static method of the {{domxref("LanguageModel")}} interface constructs a new {{domxref("LanguageModel")}} instance, automatically downloading the corresponding model data if it is not already available. | ||
|
|
There was a problem hiding this comment.
IF this is the actual gated method, it should include a standard note about transient activation.
The method should also be added to https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/User_activation#transient_activation - in the list of gated methods.
| ## See also | ||
|
|
||
| - {{domxref("LanguageModel.availability_static", "LanguageModel.availability()")}} | ||
| - [Prompt API](/en-US/docs/Web/API/Prompt_API) |
There was a problem hiding this comment.
Perhaps also /Prompt_API/Adding_context - I was going to suggest link from the syntax on the roles.
| - `tools` | ||
| - : An array of objects representing tools available to the AI. | ||
| Each object can include the following properties: | ||
| - `name` | ||
| - : A string giving the tool a unique name the model uses to refer to it when issuing a tool call. | ||
| - `description` | ||
| - : A string describing what the tool does. The model uses this description to decide if and when to invoke the tool. | ||
| - `inputSchema` | ||
| - : A [JSON Schema](https://json-schema.org/) that describes the tool's input parameters. The model uses this schema to construct the arguments it passes to the tool's `execute` function. | ||
| - `execute` | ||
| - : A callback function that the user agent invokes when the model calls this tool. Its arguments are specific to the model being used. It must return a {{jsxref("Promise")}} that resolves with a {{jsxref("String")}} representing the tool's result. |
There was a problem hiding this comment.
I don't remember any guide on this topic.
There was a problem hiding this comment.
Though there is an example below.
| ### Exceptions | ||
|
|
||
| - `AbortError` {{domxref("DOMException")}} | ||
| - : Thrown if the operation was aborted via the `signal` option. |
|
|
||
| ### Monitoring download progress | ||
|
|
||
| This code shows how you can monitor the download progress of a model. |
There was a problem hiding this comment.
For all of these do you think a guide link might be useful?
| - `"user"` | ||
| - : A message from the user, which the API should respond to. | ||
| - `"assistant"` | ||
| - : Inputs that come from the point of view of the AI assistant, which mainly serve to provide context/history, and further shape how the model responds. |
There was a problem hiding this comment.
While this is much better than before because it clarifies the "inputs" it might be better as (everywhere):
| - : Inputs that come from the point of view of the AI assistant, which mainly serve to provide context/history, and further shape how the model responds. | |
| - : Inputs that provide context for the AI assistant, such as its persona or the format of its responses. These mainly serve to provide context/history, and further shape how the model responds. |
I'd no longer block on the absence of a link to more information, but it would be better if we had that.
| - : The result of a tool invocation. | ||
| - `value` | ||
| - : The content of the message. If the `type` is `text`, this is always a string. If the `type` is `audio` or `image`, the `value` can be one of several different object types; see [What data types are accepted?](/en-US/docs/Web/API/Prompt_API/Multimodal#what_data_types_are_accepted). | ||
| - `prefix` {{optional_inline}} |
There was a problem hiding this comment.
Do/should we have an example of this?
| try { | ||
| await session.append(veryLargeDocument); | ||
| } catch (err) { | ||
| if (err.name === "QuotaExceededError") { | ||
| console.warn("Document too large."); | ||
| session = checkpoint; | ||
| } | ||
| } |
There was a problem hiding this comment.
Ah, I guess this is why you use the term throw - all the promise code is wrapped.
|
|
||
| ### Cloning to retry after a context overflow | ||
|
|
||
| This example uses a checkpoint and rollback pattern to save the state of a session before attempting to append a large amount of data. Cloning the session before calling `append()` ensures that the app has a way to restore to a safe state before attempting something risky. |
There was a problem hiding this comment.
Perhaps
| This example uses a checkpoint and rollback pattern to save the state of a session before attempting to append a large amount of data. Cloning the session before calling `append()` ensures that the app has a way to restore to a safe state before attempting something risky. | |
| This example uses a checkpoint and rollback pattern to save the state of a session before attempting to append a large amount of data. Cloning the session before calling `append()` allows the app to restore the state if the context window is exceeded. |
|
|
||
| {{APIRef("Prompt API")}}{{SeeCompatTable}}{{securecontext_header}} | ||
|
|
||
| The **`destroy()`** method of the {{domxref("LanguageModel")}} interface releases the resources assigned to the `LanguageModel` instance it is called on and stops any further activity on it. This means that any ongoing and subsequent method calls made on the `LanguageModel` will reject with an `AbortError`. |
There was a problem hiding this comment.
| The **`destroy()`** method of the {{domxref("LanguageModel")}} interface releases the resources assigned to the `LanguageModel` instance it is called on and stops any further activity on it. This means that any ongoing and subsequent method calls made on the `LanguageModel` will reject with an `AbortError`. | |
| The **`destroy()`** method of the {{domxref("LanguageModel")}} interface releases the resources assigned to the `LanguageModel` instance it is called on and stops any further activity on it. Any ongoing and subsequent method calls made on the `LanguageModel` will reject with an `AbortError`. |
|
|
||
| The **`promptStreaming()`** method of the {{domxref("LanguageModel")}} interface sends input to the language model and returns a {{domxref("ReadableStream")}} that delivers the model's response incrementally as it is generated. | ||
|
|
||
| This is useful for displaying responses to users in real time, for long outputs, or for any scenario where perceived latency should be minimized. Consume the stream using `for await...of` or by attaching a reader via {{domxref("ReadableStream.getReader()")}}. |
There was a problem hiding this comment.
| This is useful for displaying responses to users in real time, for long outputs, or for any scenario where perceived latency should be minimized. Consume the stream using `for await...of` or by attaching a reader via {{domxref("ReadableStream.getReader()")}}. | |
| This is useful for displaying responses to users incrementally for outputs that take a long time to complete, or for any scenario where perceived latency should be minimized. Consume the stream using `for await...of` or by attaching a reader via {{domxref("ReadableStream.getReader()")}}. |
|
|
||
| try { | ||
| for await (const chunk of stream) { | ||
| console.log(chunk); |
There was a problem hiding this comment.
FWIW and in other cases where you show streaming, console logging is not great because you end up with so many small chunks. If you want someone to copy/paste this into dev tools to run then fine, but to show a more natural usage I'd suggest maybe writing to a log_append() function or similar.
I.e. not a blocker, just a thought from having tried copying the example in the guide.
Description
The Prompt API is coming out of Beta in Chrome 148. Since its easier to review smaller PRs, I'm submitting the interfaces one at a time.
And I have other parts of this API queued up: https://github.com/jmedley/content/branches
Motivation
This is being done so that API documentation is available when Chrome 148 ships.
Additional details
See https://chromestatus.com/feature/5134603979063296
Related issues and pull requests
First part of resolving issue #43755