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:

ProvedorModelo padrãoCusto
GeminiGemini Flash~$0.50/1M tokens (input)
OpenAIGPT-4o-mini~$0.15/1M tokens (input)
AnthropicClaude 3.5 HaikuVariá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árioRecomendação
Protótipo/demo rápidoPolyfill
Produção com controle de custosFirebase AI Logic
Precisa de modelo específicoImplementar fallback próprio
Precisa funcionar 100% offlineBackend local do polyfill
Empresa com contrato cloudFallback 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.


Referências