One Runtime, Several Interfaces

Explain this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Apply this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

The first Browser AI Observatory knew about one global object: LanguageModel.

That was enough to expose the common lifecycle. We could inspect availability, create a session, monitor model acquisition, stream output, cancel a request and record what happened.

Chrome also exposes narrower built-in AI capabilities. Instead of asking a general language-model session to perform an arbitrary task, an application can create a summarizer, writer, rewriter, proofreader, translator or language detector.

At first this can look like convenience syntax.

await session.prompt("Summarize this text: ...");

becomes:

await summarizer.summarize(text);

If that were the whole change, we could keep one Prompt API chapter and treat the others as wrappers.

But a task-specific API does something architecturally important: it moves part of the task contract from prompt prose into software structure.

general prompting

task + options + input encoded as language
             LanguageModel

task API

task selected by interface
options selected by fields
input supplied as data
       Summarizer

That changes what the browser knows, what the developer can validate and what our debugger should display.

This chapter adds all seven current surfaces to Browser AI Observatory without pretending they are identical.


1. The common shape

The built-in AI APIs share a recognizable lifecycle:

feature detection
availability(options)
create(options + monitor)
task operation(input)
result or stream

That common shape earns a shared adapter.

It lets the observatory implement capability inspection, download progress, timing, cancellation, trace identity and export once. If every panel tab reimplemented those mechanisms, differences in the debugger would be mistaken for differences in the APIs.

But the shared lifecycle stops before task meaning.

API Created object Primary operation Output shape
Prompt language-model session prompt() text
Summarizer summarizer summarize() text
Writer writer write() text
Rewriter rewriter rewrite() text
Proofreader proofreader proofread() corrected text plus corrections
Language Detector detector detect() ranked language candidates
Translator translator translate() translated text

The first four can stream generated text. Translation can stream as well. Proofreading and language detection return structured results rather than pretending every useful answer is prose.

An adapter should normalize mechanics while preserving meaning.


2. What the Prompt API leaves to us

A general prompt can express almost any text task:

const result = await session.prompt(`
Summarize the following passage as three Markdown bullet points.
Preserve the distinction between browser responsibility and application responsibility.

${text}
`);

The browser sees a sequence of instructions and data. The application must ensure that:

  • the input is separated safely from instructions;
  • the requested format is understood;
  • the number of points is respected;
  • important meaning is preserved;
  • unrelated text is not introduced;
  • the result can be parsed or rendered safely.

Some of those obligations remain with a task API. Summaries can still omit or distort information. Generated Markdown still needs safe rendering. The narrower interface does not turn probabilistic behavior into a deterministic function.

It does, however, make important intentions explicit:

const summarizer = await Summarizer.create({
  type: "key-points",
  format: "markdown",
  length: "short",
  preference: "auto",
  expectedInputLanguages: ["en"],
  expectedContextLanguages: ["en"],
  outputLanguage: "en"
});

The task, output form, approximate length, execution preference and language contract are fields. They can be inspected before execution and recorded without parsing a prompt string.

This is the first advantage of a task API:

A task API turns some behavioral intent into typed configuration.


3. Narrow interfaces create useful constraints

The Writer API offers tone, format, length and shared context. The Rewriter API offers related but different values because rewriting begins from existing text. The Translator API requires source and target languages. The Language Detector returns a ranking rather than a single unsupported claim of certainty.

These distinctions tell us what the platform thinks the operation is.

They also prevent nonsensical combinations from becoming ordinary usage. A translator does not need a temperature control. A proofreader should not be prompted to invent a new article. A summarizer can describe summary types directly rather than asking every application to phrase them consistently.

Narrowness buys three properties:

  1. Discoverability — supported options are part of the interface.
  2. Inspectability — a debugger can display semantic configuration without interpreting prose.
  3. Replaceability — the browser may choose an implementation suited to the declared task.

The price is reduced freedom. If the application needs an unusual operation outside the task contract, the Prompt API remains the escape hatch.


4. Do not assume one physical model

The title of this chapter says one runtime, not one set of weights.

Chrome describes built-in AI as browser-managed foundation and expert models. Some capabilities may share a foundation model. Translation and language detection can use task-specific models. Implementations can change as the browser evolves.

Our application can safely depend on the exposed capability contract.

It cannot safely infer that all API globals:

  • download the same asset;
  • have the same hardware requirements;
  • support the same languages;
  • share a context window;
  • have the same latency;
  • change together after an update.

The observatory therefore records apiId on every event. A generic event named session.create.finished is useful only if we can still tell which capability was created.

{
  "type": "session.create.finished",
  "data": {
    "apiId": "summarizer",
    "durationMs": 842.4
  }
}

Shared instrumentation should remove accidental differences, not meaningful ones.


5. Build a capability registry

The runnable extension now places the API-specific facts in one registry:

const DEFINITIONS = {
  prompt: {
    globalName: "LanguageModel",
    method: "prompt",
    streamingMethod: "promptStreaming",
    defaultOptions: {
      samplingMode: "most-predictable",
      expectedInputs: [{ type: "text", languages: ["en"] }],
      expectedOutputs: [{ type: "text", languages: ["en"] }]
    }
  },
  summarizer: {
    globalName: "Summarizer",
    method: "summarize",
    streamingMethod: "summarizeStreaming",
    defaultOptions: {
      type: "key-points",
      format: "markdown",
      length: "medium",
      preference: "auto",
      expectedInputLanguages: ["en"],
      expectedContextLanguages: ["en"],
      outputLanguage: "en"
    }
  }
};

The complete implementation includes Writer, Rewriter, Proofreader, Language Detector and Translator definitions in core/adapters.js.

The registry is data rather than a hierarchy of seven classes. These APIs vary primarily by global name, creation options and operation name. Introducing inheritance here would add more code without revealing more architecture.

The adapter performs the common mechanics:

export function createAPIAdapter(apiId, environment = globalThis) {
  const definition = getAPIDefinition(apiId);

  return {
    ...definition,
    id: apiId,

    exposed() {
      return Boolean(environment[definition.globalName]);
    },

    async availability(options = definition.defaultOptions) {
      const api = environment[definition.globalName];
      if (!api) return "unavailable";
      return api.availability(options);
    },

    async create(options, onProgress) {
      const api = environment[definition.globalName];
      return api.create({
        ...options,
        monitor(monitor) {
          monitor.addEventListener("downloadprogress", onProgress);
        }
      });
    }
  };
}

Passing environment makes the boundary testable. Production uses globalThis. Tests supply fake APIs and verify lifecycle behavior without claiming to emulate the model.


6. Run the seven-interface switchboard

Select Run with Browser AI from Chapter 05 to open:

/tools/ai/browser-ai-from-first-principles/05-chapter/

The new switchboard asks seven separate capability questions:

Interface Global Contract emphasis
Prompt LanguageModel Sampling, expected inputs and expected outputs
Summarizer Summarizer Type, format, length and language expectations
Writer Writer Tone, format, length and language expectations
Rewriter Rewriter Relationship to existing text plus output constraints
Proofreader Proofreader Correction-oriented structured results
Translator Translator Explicit source and target languages
Language Detector LanguageDetector Ranked language hypotheses

Click Inspect all interfaces. For every row, the experience first checks whether the named global is exposed. If it is, the page calls that interface’s availability(options) method with the exact option object displayed in the final column.

The matrix deliberately separates Exposed from Availability. A global can exist while a requested language or task configuration is unavailable. Conversely, an interface absent from this page is reported as not exposed rather than being collapsed into a vague browser-level “AI disabled” state.

Every inspection also enters the shared Observatory trace:

capability.inspect.started  { apiId: "summarizer" }
capability.inspect.finished {
  apiId: "summarizer",
  globalName: "Summarizer",
  exposed: true,
  availability: "available"
}

This produces a useful form of evidence without creating seven sessions or triggering seven acquisitions. The switchboard is a preflight instrument. Model preparation remains behind a later, explicit user action for the task the reader chooses to run.

The interface definitions live in the chapter’s experiment.json. They match the registry used by the extension, including samplingMode: "most-predictable" for Prompt API compatibility with speculative-decoding builds. Putting the options in inspectable data means the matrix can show what it asked rather than presenting availability as context-free truth.

Replay mode preserves the same rule. The reviewed September 2 trace contains one Prompt API capability result. The switchboard fills that row and labels the other six interfaces unobserved. It does not convert “not present in this trace” into “unavailable.”

This switchboard becomes the reusable substrate for the next three chapters. Chapter 06 can select Summarizer, Chapter 07 can compare Writer, Rewriter and Proofreader, and Chapter 08 can pair Language Detector with Translator. The shared lifecycle remains stable while the experiment contract changes.


7. Preserve output shape

Text operations fit one streaming path:

const stream = instance[streamingMethod](input, operationOptions);
let output = "";

for await (const chunk of stream) {
  output += chunk;
  onChunk(chunk);
}

Structured operations require a decision. The UI needs displayable text, while evaluation may need the original structure.

The first implementation serializes non-string results as formatted JSON:

function formatResult(value) {
  return typeof value === "string"
    ? value
    : JSON.stringify(value, null, 2);
}

This is acceptable for an experimental console because it preserves fields and makes export straightforward. A later typed result layer should retain the original value alongside its rendered representation. Otherwise a debugger can accidentally turn a numeric confidence into a string and make downstream analysis harder.

The important rule is:

Normalization must not erase the evidence needed to evaluate the task.


8. One panel, different experiments

The panel now contains:

  • an API selector;
  • editable creation options;
  • context and input fields;
  • capability inspection;
  • session creation and download progress;
  • run and stop controls;
  • streamed or structured output;
  • fixture evaluation;
  • a unified event trace;
  • JSON export.

Changing the selected API destroys the current adapter reference and requires a new session. This reflects the platform contract: creation-time parameters are not mutable preferences attached to one universal object.

function selectAPI(apiId) {
  const adapter = runtime.select(apiId);
  options.value = JSON.stringify(adapter.defaultOptions, null, 2);
  availability.textContent = "Unknown";
  sessionState.textContent = "None";
  run.disabled = true;
}

The panel does not silently reuse a Writer after the developer selects Rewriter. Visible state prevents a surprisingly common experimental mistake: believing one option changed when the program actually kept using an older configured instance.


9. Fixtures make comparison possible

A blank text box invites exploration. It does not produce comparable evidence.

The observatory includes a fixture for each task family. A fixture freezes:

API identity
creation options
context
input
automatic assertions
human-review rubric

For example, the summarization fixture requires at least three bullets and the presence of both “browser” and “application.” A human check asks whether the output avoids claiming that local execution is automatically private or faster.

{
  id: "summarizer-browser-runtime",
  apiId: "summarizer",
  options: {
    type: "key-points",
    format: "markdown",
    length: "short"
  },
  assertions: [
    { kind: "min-bullets", value: 3 },
    { kind: "contains", value: "browser" },
    { kind: "contains", value: "application" },
    {
      kind: "custom",
      rubric: "No claim that local execution is automatically private or faster."
    }
  ]
}

The structural assertions are deliberately modest. Lexical presence does not prove meaning was preserved. A human rubric is not quietly converted into a fake numeric check.

This gives each run three independent outcomes:

Outcome Question
Runtime Did the API operation complete?
Automatic fixture Did the output satisfy machine-checkable constraints?
Human review Did it preserve the intended meaning and quality?

One green result should never stand in for all three.


10. Compare contracts, not just prose

We can now run a controlled comparison:

Route A: Prompt API

Encode task, format, length and source text in a prompt.

Route B: Summarizer API

Encode task options in Summarizer.create() and pass the source text to summarize().

Record:

  • exposure and availability;
  • cold and warm creation time;
  • download events;
  • time to first chunk;
  • total duration;
  • output size;
  • structural checks;
  • human rubric;
  • configuration complexity;
  • failure mode.

The question is not “Which paragraph reads better once?”

It is:

Which contract produces a feature that is easier to specify, observe, validate and maintain across repeated cases?

A general prompt may win for a specialized task. A task API may win for predictable semantics and configuration. The experiment should be allowed to tell us.


11. What the deterministic tests prove

The extension includes Node tests using fake browser APIs.

They verify that:

  • all seven capability definitions are registered;
  • an absent global becomes operationally unavailable;
  • progress events pass through the adapter;
  • streaming chunks are concatenated in order;
  • structured proofreading results remain inspectable;
  • runtime events appear in lifecycle order;
  • metrics-only mode does not retain prompt or response content;
  • content mode retains text only when explicitly selected;
  • fixtures target known APIs;
  • automatic checks do not silently mark human rubrics as passed.

They do not verify summarization quality, language coverage or Chrome’s actual model behavior.

That division is intentional:

Node tests
  → our deterministic instrumentation and evaluation machinery

Chrome fixture runs
  → browser availability, performance and model behavior

Testing a fake model would give us a green suite and no evidence about the system the book is studying.


Conclusion

The built-in AI APIs share a lifecycle, but they do not share one meaning.

A common adapter can normalize exposure, availability, creation, download monitoring, streaming, cancellation and trace events. The registry must still preserve API identity, creation options, operation names and output shape.

Task APIs matter because they move some intent out of prompt prose and into inspectable software contracts. That can make a feature easier to configure and debug. It does not make the model deterministic or the output automatically correct.

Browser AI Observatory can now run the complete Part II experiment group through one panel. The Chapter 05 experience exposes the same registry as a live capability matrix, so readers can see which interfaces their browser actually offers and inspect the options behind every result. The next three chapters use that substrate rather than inventing isolated examples.

We begin with summarization because it exposes nearly every tension at once: extraction versus generation, structure versus meaning, speed versus capability, and a result that can look excellent while omitting the one fact the reader needed.


Sources and further reading

  1. Chrome for Developers, Built-in AI.
  2. Chrome for Developers, The Prompt API.
  3. Chrome for Developers, Summarize with built-in AI.
  4. Chrome for Developers, Writer API.
  5. Chrome for Developers, Rewriter API.
  6. Chrome for Developers, Proofreader API.
  7. Chrome for Developers, Translation with built-in AI.
  8. Chrome for Developers, Language detection with built-in AI.