Polyfill e Fallback para a Prompt API — Guia Completo
A Prompt API roda só no Chrome 148+ e Edge. Pra atingir todos os usuários — incluindo Firefox, Safari e dispositivos sem hardware adequado — existe um polyfill oficial mantido pelo time do Chrome que implementa a mesma API sobre backends cloud ou locais.
Na prática, o polyfill garante que seu código funciona de forma idêntica independente do suporte nativo do navegador.
O que é o polyfill oficial
O prompt-api-polyfill é um pacote npm que implementa fielmente a especificação da Prompt API. Quando o navegador não suporta a API nativamente, o polyfill redireciona as chamadas pra um backend configurável:
- Backend cloud: Manda requisições pra APIs como Gemini, OpenAI ou Anthropic
- Backend local: Usa Transformers.js pra rodar um modelo localmente no browser via WebGPU
Instalação
npm install prompt-api-polyfill
Backends disponíveis
Backend cloud
O backend cloud redireciona chamadas para APIs de IA na nuvem. Dados são enviados para o provedor escolhido:
| Provedor | Modelo padrão | Custo |
|---|---|---|
| Gemini | Gemini Flash | ~$0.50/1M tokens (input) |
| OpenAI | GPT-4o-mini | ~$0.15/1M tokens (input) |
| Anthropic | Claude 3.5 Haiku | Variável |
Backend local (Transformers.js)
O backend local usa Transformers.js com WebGPU para executar modelos diretamente no navegador. Funciona como a API nativa (on-device), mas:
- Não compartilha modelo entre origens (cada site baixa seu próprio)
- O download pode ser maior dependendo do modelo escolhido
- Funciona em qualquer navegador com WebGPU
Configuração
Crie um arquivo .env.json com as credenciais do backend escolhido:
Configuração para Gemini (cloud)
{
"BACKEND": "gemini",
"API_KEY": "sua-api-key-do-google-ai-studio",
"MODEL_NAME": "gemini-2.0-flash-lite"
}
Configuração para OpenAI (cloud)
{
"BACKEND": "openai",
"API_KEY": "sk-sua-api-key-openai",
"MODEL_NAME": "gpt-4o-mini"
}
Configuração para backend local
{
"BACKEND": "local",
"MODEL_NAME": "Qwen/Qwen2.5-0.5B-Instruct"
}
Uso no código
O polyfill é importado e registrado antes de usar a API:
import { polyfill } from 'prompt-api-polyfill';
// Registra o polyfill (só atua se API nativa não existir)
await polyfill({
backend: 'gemini',
apiKey: 'sua-api-key',
modelName: 'gemini-2.0-flash-lite'
});
// Código idêntico à API nativa
const session = await LanguageModel.create();
const resposta = await session.prompt('Hello, world!');
Progressive Enhancement Pattern
O padrão recomendado é usar a API nativa quando disponível e o polyfill como fallback:
async function inicializarIA() {
// 1. Tentar API nativa primeiro
if ('LanguageModel' in window) {
const status = await LanguageModel.availability({
expectedInputs: [{ type: 'text', languages: ['en'] }],
expectedOutputs: [{ type: 'text', languages: ['en'] }]
});
if (status !== 'unavailable') {
console.log('Usando Prompt API nativa (on-device)');
return await LanguageModel.create();
}
}
// 2. Fallback para polyfill com backend cloud
console.log('API nativa indisponível. Usando polyfill cloud.');
const { polyfill } = await import('prompt-api-polyfill');
await polyfill({
backend: 'gemini',
apiKey: import.meta.env.VITE_GEMINI_API_KEY,
modelName: 'gemini-2.0-flash-lite'
});
return await LanguageModel.create();
}
// Uso — código idêntico independente do backend
const session = await inicializarIA();
const resposta = await session.prompt('Explain recursion.');
Feature Detection completa
class AIService {
constructor() {
this.mode = null; // 'native', 'polyfill-cloud', 'polyfill-local', 'unavailable'
this.session = null;
}
async detectar() {
// Nível 1: API nativa com modelo disponível
if ('LanguageModel' in window) {
const status = await LanguageModel.availability({
expectedInputs: [{ type: 'text', languages: ['en'] }],
expectedOutputs: [{ type: 'text', languages: ['en'] }]
});
if (status === 'available' || status === 'downloading' || status === 'downloadable') {
this.mode = 'native';
return this.mode;
}
}
// Nível 2: Polyfill local (se WebGPU disponível)
if ('gpu' in navigator) {
this.mode = 'polyfill-local';
return this.mode;
}
// Nível 3: Polyfill cloud (sempre funciona com internet)
if (navigator.onLine) {
this.mode = 'polyfill-cloud';
return this.mode;
}
// Sem opções
this.mode = 'unavailable';
return this.mode;
}
async inicializar() {
await this.detectar();
switch (this.mode) {
case 'native':
this.session = await LanguageModel.create({
monitor: (m) => m.addEventListener('downloadprogress', (e) => {
this.onProgress?.(e.loaded);
})
});
break;
case 'polyfill-local':
const { polyfill: pLocal } = await import('prompt-api-polyfill');
await pLocal({ backend: 'local', modelName: 'Qwen/Qwen2.5-0.5B-Instruct' });
this.session = await LanguageModel.create();
break;
case 'polyfill-cloud':
const { polyfill: pCloud } = await import('prompt-api-polyfill');
await pCloud({
backend: 'gemini',
apiKey: this.apiKey,
modelName: 'gemini-2.0-flash-lite'
});
this.session = await LanguageModel.create();
break;
default:
throw new Error('IA não disponível neste ambiente');
}
return this;
}
async prompt(texto, opcoes) {
return this.session.prompt(texto, opcoes);
}
getInfoMode() {
const info = {
'native': { label: 'On-device (Gemini Nano)', privacidade: 'máxima', custo: 'grátis' },
'polyfill-local': { label: 'Local (WebGPU)', privacidade: 'alta', custo: 'grátis' },
'polyfill-cloud': { label: 'Cloud', privacidade: 'limitada', custo: 'por uso' }
};
return info[this.mode];
}
}
Considerações de UX
Informar o usuário sobre o modo ativo
const ai = new AIService();
ai.apiKey = 'sua-key';
await ai.inicializar();
const info = ai.getInfoMode();
document.getElementById('ai-badge').textContent = info.label;
document.getElementById('ai-privacidade').textContent = `Privacidade: ${info.privacidade}`;
Indicar quando dados saem do dispositivo
Se o polyfill cloud está ativo, o usuário deve saber que seus dados serão processados externamente:
if (ai.mode === 'polyfill-cloud') {
mostrarAviso(
'A IA está usando processamento em nuvem. ' +
'Seus dados serão enviados para servidores externos.'
);
}
Oferecer escolha ao usuário
async function inicializarComEscolha() {
const temNativo = 'LanguageModel' in window &&
(await LanguageModel.availability({})) !== 'unavailable';
if (temNativo) {
// Usa nativo sem perguntar
return await LanguageModel.create();
}
// Perguntar ao usuário
const escolha = await mostrarDialogo({
titulo: 'Como deseja usar a IA?',
opcoes: [
{ id: 'cloud', label: 'Nuvem (rápido, requer internet)', icon: '☁️' },
{ id: 'local', label: 'Local (privado, download ~500MB)', icon: '💻' },
{ id: 'nenhum', label: 'Não usar IA', icon: '❌' }
]
});
if (escolha === 'nenhum') return null;
const { polyfill } = await import('prompt-api-polyfill');
await polyfill({
backend: escolha === 'cloud' ? 'gemini' : 'local',
apiKey: escolha === 'cloud' ? API_KEY : undefined,
modelName: escolha === 'cloud' ? 'gemini-2.0-flash-lite' : 'Qwen/Qwen2.5-0.5B-Instruct'
});
return await LanguageModel.create();
}
Firebase AI Logic como alternativa de produção
Para produção, o Google recomenda usar o Firebase AI Logic Hybrid SDK que oferece:
- Fallback automático entre on-device e cloud
- Gerenciamento de API keys via Firebase
- Controle de custos e rate limiting
- Monitoramento e analytics
// Firebase AI Logic — alternativa de produção ao polyfill
import { initializeApp } from 'firebase/app';
import { getAI } from 'firebase/ai';
const app = initializeApp(firebaseConfig);
const ai = getAI(app);
// Usa Prompt API nativa quando disponível, Gemini API como fallback
Quando usar o polyfill vs. implementar seu próprio fallback
| Cenário | Recomendação |
|---|---|
| Protótipo/demo rápido | Polyfill |
| Produção com controle de custos | Firebase AI Logic |
| Precisa de modelo específico | Implementar fallback próprio |
| Precisa funcionar 100% offline | Backend local do polyfill |
| Empresa com contrato cloud | Fallback próprio para seu provedor |
Perguntas frequentes
O polyfill tem a mesma API que a Prompt API nativa?
Sim. O polyfill é spec-compliant — implementa a mesma interface. Código escrito para a API nativa funciona sem alteração com o polyfill.
O polyfill funciona offline?
Com backend local (Transformers.js), sim — após o download do modelo. Com backend cloud, não.
Quanto custa usar o polyfill com backend cloud?
Depende do provedor. Gemini Flash custa ~$0.50/1M tokens de input. GPT-4o-mini custa ~$0.15/1M tokens de input. O custo é por requisição processada.
O polyfill suporta multimodal (imagem/áudio)?
Depende do backend configurado. Backends cloud que suportam multimodal (Gemini, GPT-4o) funcionam. O backend local com Transformers.js pode ter limitações dependendo do modelo escolhido.
Posso usar o polyfill em produção?
O polyfill é experimental. Para produção, o Google recomenda o Firebase AI Logic Hybrid SDK como solução mais robusta e gerenciável.