multiAI Summary Pending

web-ai-prompt-api

Chrome's built-in Prompt API implementation guide. Use Gemini Nano locally in browser for AI features - session management, multimodal input, structured output, streaming, Chrome Extensions. Reference for all Prompt API development.

231 stars

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/web-ai-prompt-api/SKILL.md --create-dirs "https://raw.githubusercontent.com/aiskillstore/marketplace/main/skills/ajv009/web-ai-prompt-api/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/web-ai-prompt-api/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How web-ai-prompt-api Compares

Feature / Agentweb-ai-prompt-apiStandard Approach
Platform SupportmultiLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Chrome's built-in Prompt API implementation guide. Use Gemini Nano locally in browser for AI features - session management, multimodal input, structured output, streaming, Chrome Extensions. Reference for all Prompt API development.

Which AI agents support this skill?

This skill is compatible with multi.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Chrome Prompt API Reference

Complete implementation guide for Chrome's built-in Prompt API using Gemini Nano.

## Hardware & OS Requirements

**OS:** Windows 10/11, macOS 13+ (Ventura+), Linux, ChromeOS (Platform 16389.0.0+) on Chromebook Plus
**Storage:** 22 GB free (model downloaded separately)
**GPU:** >4 GB VRAM OR **CPU:** 16 GB RAM + 4 cores
**Network:** Unmetered connection for download
**Chrome:** 138+ (Extensions in stable, Web in origin trial)

**Not supported:** Mobile (Android, iOS), non-Chromebook Plus ChromeOS

Check model size: `chrome://on-device-internals`
Model removed if storage <10 GB after download.

## Language Support

From Chrome 140: English, Spanish, Japanese (input/output)

## Availability Check

```javascript
const availability = await LanguageModel.availability();
// Returns: "unavailable" | "downloadable" | "downloading" | "available"
```

**CRITICAL:** Always pass same options to `availability()` as you use in `create()`. Some models don't support certain modalities/languages.

## Model Parameters

```javascript
await LanguageModel.params();
// { defaultTopK: 3, maxTopK: 128, defaultTemperature: 1, maxTemperature: 2 }
```

## Session Creation

### Basic Session

```javascript
const session = await LanguageModel.create();
```

### With Custom Parameters

```javascript
const params = await LanguageModel.params();
const session = await LanguageModel.create({
  temperature: Math.min(params.defaultTemperature * 1.2, 2.0),
  topK: params.defaultTopK
});
```

### With Download Monitoring

```javascript
const session = await LanguageModel.create({
  monitor(m) {
    m.addEventListener('downloadprogress', (e) => {
      console.log(`Downloaded ${e.loaded * 100}%`);
    });
  }
});
```

### With AbortSignal

```javascript
const controller = new AbortController();
stopButton.onclick = () => controller.abort();

const session = await LanguageModel.create({
  signal: controller.signal
});
```

## Initial Prompts (Context Setting)

```javascript
const session = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'What is the capital of Italy?' },
    { role: 'assistant', content: 'The capital of Italy is Rome.' },
    { role: 'user', content: 'What language is spoken there?' },
    { role: 'assistant', content: 'The official language is Italian.' }
  ]
});
```

**Use cases:**
- Resume stored sessions after browser restart
- N-shot prompting
- Set assistant personality/tone
- Provide conversation history

## Multimodal Input (Origin Trial)

### expectedInputs & expectedOutputs

```javascript
const session = await LanguageModel.create({
  expectedInputs: [
    {
      type: "text",  // or "image", "audio"
      languages: ["en" /* system */, "ja" /* user prompt */]
    }
  ],
  expectedOutputs: [
    { type: "text", languages: ["ja"] }
  ]
});
```

**Input types:** text, image, audio
**Output types:** text only

Throws `NotSupportedError` if unsupported modality.

### Using with Images/Audio

```javascript
const session = await LanguageModel.create({
  initialPrompts: [{
    role: 'system',
    content: 'Analyze images for patterns.'
  }],
  expectedInputs: [{ type: 'image' }]
});

// Append image
await session.append([{
  role: 'user',
  content: [
    { type: 'text', value: 'Analyze this image' },
    { type: 'image', value: fileInput.files[0] }
  ]
}]);
```

## Prompting Methods

### Non-Streaming (Short Responses)

```javascript
const result = await session.prompt('Write me a haiku!');
console.log(result);
```

### Streaming (Long Responses)

```javascript
const stream = session.promptStreaming('Write me a long poem!');
for await (const chunk of stream) {
  console.log(chunk);  // Partial results as they arrive
}
```

### With AbortSignal

```javascript
const controller = new AbortController();
stopButton.onclick = () => controller.abort();

const result = await session.prompt('Write a poem', {
  signal: controller.signal
});
```

## Response Constraints (Structured Output)

Pass JSON Schema to get predictable JSON responses:

```javascript
const schema = {
  type: "object",
  properties: {
    hashtags: {
      type: "array",
      maxItems: 3,
      items: {
        type: "string",
        pattern: "^#[^\\s#]+$"
      }
    }
  },
  required: ["hashtags"],
  additionalProperties: false
};

const result = await session.prompt(
  `Generate hashtags for: ${post}`,
  { responseConstraint: schema }
);

const data = JSON.parse(result);  // Guaranteed valid JSON
```

**Simple boolean example:**

```javascript
const schema = { "type": "boolean" };
const result = await session.prompt(
  `Is this about pottery?\n\n${text}`,
  { responseConstraint: schema }
);
console.log(JSON.parse(result));  // true or false
```

**Measure input quota usage:**

```javascript
const usage = session.measureInputUsage({ responseConstraint: schema });
```

**Omit schema from input quota:**

```javascript
const result = await session.prompt(
  `Summarize as JSON { rating } with 0-5 number:`,
  {
    responseConstraint: schema,
    omitResponseConstraintInput: true
  }
);
```

## Response Prefixes

Guide model output format by prefilling assistant response:

```javascript
const result = await session.prompt([
  { role: 'user', content: 'Create a TOML character sheet' },
  { role: 'assistant', content: '```toml\n', prefix: true }
]);
// Model continues from "```toml\n"
```

## append() Method

Add messages to session without immediate response (useful for multimodal):

```javascript
await session.append([
  {
    role: 'user',
    content: [
      { type: 'text', value: 'First context message' },
      { type: 'image', value: imageFile }
    ]
  }
]);

// Later, prompt with accumulated context
const result = await session.prompt('Analyze the images');
```

Promise fulfills when validated and appended.

## Session Management

### Check Quota

```javascript
console.log(`${session.inputUsage}/${session.inputQuota}`);
const remaining = session.inputQuota - session.inputUsage;
```

When quota exceeded, oldest messages lost from context.

### Clone Session

Clones inherit parameters, initial prompts, and history:

```javascript
const mainSession = await LanguageModel.create({
  initialPrompts: [{ role: 'system', content: 'Speak like a pirate' }]
});

const clone1 = await mainSession.clone();
const clone2 = await mainSession.clone({ signal: controller.signal });

// Independent conversations with same setup
await clone1.prompt('Tell me a joke about parrots');
await clone2.prompt('Tell me a joke about treasure');
```

**Use for:** Parallel conversations, "what if" scenarios, resource efficiency

### Restore Session from localStorage

```javascript
let sessionData = getFromLocalStorage(uuid) || {
  initialPrompts: [],
  topK: (await LanguageModel.params()).defaultTopK,
  temperature: (await LanguageModel.params()).defaultTemperature
};

const session = await LanguageModel.create(sessionData);

// Track conversation
const stream = session.promptStreaming(prompt);
let result = '';
for await (const chunk of stream) {
  result = chunk;
}

sessionData.initialPrompts.push(
  { role: 'user', content: prompt },
  { role: 'assistant', content: result }
);

localStorage.setItem(uuid, JSON.stringify(sessionData));
```

### Destroy Session

```javascript
session.destroy();
// Frees resources, aborts ongoing execution
// Session unusable after destroy
```

**Best practice:** Keep one empty session alive to keep model loaded. Destroy only when truly done.

## User Activation Requirement

Model download requires user interaction:

```javascript
if (navigator.userActivation.isActive) {
  const session = await LanguageModel.create();
}
```

**Sticky activation events:** click, tap, keydown, mousedown

## Permission Policy & iframes

**Default:** Top-level windows + same-origin iframes only

**Grant cross-origin iframe access:**

```html
<iframe src="https://cross-origin.example.com/"
        allow="language-model">
</iframe>
```

**Not available in Web Workers** (permission policy complexity)

## Local Development (localhost)

1. Go to `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input`
2. Select **Enabled**
3. Relaunch Chrome
4. Open DevTools, type: `await LanguageModel.availability();`
5. Should return `"available"`

**Troubleshoot:**
- Restart Chrome
- Check `chrome://on-device-internals` → Model Status tab
- Wait for model download to complete

## Chrome Extensions

Available in Chrome 138+ stable for extensions.

**Remove expired origin trial permissions:**

```json
{
  "permissions": ["aiLanguageModelOriginTrial"]  // REMOVE THIS
}
```

Register for current origin trial if needed.

## Best Practices

**Session management:**
- Clone sessions to preserve initial prompts
- Use AbortController to let users stop responses
- Track inputUsage to warn before quota exceeded
- Destroy unused sessions to free memory
- Keep one empty session alive to keep model ready

**Performance:**
- Use streaming for long responses
- append() messages in advance for multimodal
- Monitor download progress, inform users
- Check availability before creating sessions

**Structured output:**
- Use JSON Schema for predictable responses
- LLMs good at generating schemas from descriptions
- Validate with JSON Schema validators
- Use prefix for format guidance

**Context preservation:**
- Store conversation in localStorage for restoration
- Use initialPrompts for session context
- Clone for parallel conversations with same context

## Error Handling

```javascript
try {
  const session = await LanguageModel.create();
  const result = await session.prompt('Hello');
} catch (err) {
  if (err.name === 'AbortError') {
    // User stopped generation
  } else if (err.name === 'NotSupportedError') {
    // Unsupported modality/language
  } else {
    console.error(err.name, err.message);
  }
}
```

## Key Limitations

- Desktop only (no mobile)
- 22 GB storage required
- Unmetered network for download
- No Web Workers support
- Output is text-only (input can be multimodal)
- Context window has token limits
- Model removed if storage <10 GB after download

## Use Cases

- AI-powered search on page content
- Content classification/filtering
- Event extraction from pages
- Contact information extraction
- Chatbots with conversation memory
- Offline AI features
- Privacy-sensitive data processing
- Content summarization
- Translation assistance
- Image/audio analysis (multimodal)