Natywne dla Supabase

Bramka AI wbudowana w Twoje Edge Functions.

Nie instalujesz dodatkowego pakietu. Weryfikujesz JWT Supabase i przekazujesz identyfikator użytkownika w jednym nagłówku, a każde wywołanie jest mierzone, rozliczane i rejestrowane dla tej osoby — bez tworzenia własnej warstwy pomiarowej.

supabase/functions/_shared/odnoga.ts
const BASE = "https://api.odnoga.com/v1";

import { createClient } from "jsr:@supabase/supabase-js@2";

export async function odnoga(req: Request) {
  const jwt = (req.headers.get("authorization") ?? "").split(" ")[1];
  // Verify the token — a decoded-but-unverified subject can be forged, and the
  // spend would land on someone else's account.
  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_ANON_KEY")!,
  );
  const endUser = jwt ? (await supabase.auth.getUser(jwt)).data.user?.id : undefined;

  return (path: string, body: unknown) =>
    fetch(BASE + path, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        Authorization: `Bearer ${Deno.env.get("ODNOGA_API_KEY")}`,
        ...(endUser ? { "x-odnoga-end-user": endUser } : {}),
      },
      body: JSON.stringify(body),
    });
}

// supabase/functions/chat/index.ts
Deno.serve(async (req) => {
  const ai = await odnoga(req);
  const r = await ai("/chat/completions", {
    model: "auto",
    messages: [{ role: "user", content: "Hi" }],
  });
  return new Response(r.body, { headers: { "content-type": "application/json" } });
});

Jeden sekret: supabase secrets set ODNOGA_API_KEY=sk_live_xxx

Co dostajesz

Dla środowiska Supabase

Helper do skopiowania

Jeden mały plik w _shared/. Bez pakietu, dodatkowego etapu budowania ani wersji do pilnowania.

end_user z JWT

Odczytujesz auth.uid() z nagłówka Authorization i wysyłasz jako x-odnoga-end-user. Każde wywołanie jest mierzone dla konkretnego użytkownika.

RAG nad pgvector

Embeddingi i czat w dwóch wywołaniach; supabase.rpc("match_documents") pomiędzy. Bez kleju.

Traceability dla edge functions

Każde wywołanie ma własny zapis: organizacja, użytkownik końcowy, wersja promptu, model, koszt i czas odpowiedzi.

Jeden sekret do ustawienia

supabase secrets set ODNOGA_API_KEY=… i rozwiązanie działa.

Streaming, obrazki, embeddingi

Te same OpenAI-compatible endpointy dla każdej modalności — /chat/completions, /embeddings, /images/generations.

Przepis

RAG z pgvector — od początku do końca

const ai = await odnoga(req);

const { data: emb } = await ai("/embeddings", {
  model: "text-embedding-3-small",
  input: question,
}).then((r) => r.json());

const { data: docs } = await supabase.rpc("match_documents", {
  query_embedding: emb[0].embedding,
  match_count: 5,
});

const r = await ai("/chat/completions", {
  model: "gpt-4o-mini",
  messages: [
    { role: "system", content: `Use:\n${docs.map(d => d.content).join("\n---\n")}` },
    { role: "user", content: question },
  ],
});
FAQ

Najczęstsze pytania

Czy to oficjalny projekt Supabase?

Nie — jesteśmy niezależną bramką. Integracja to zwykłe wywołanie HTTP z Twojej Edge Function, więc nic w Twoim projekcie nie zależy od tego, czy wydamy pakiet.

Czy jest pakiet npm lub JSR?

Publikowany SDK jest w przygotowaniu. Dziś integracja jest celowo bez pakietu: endpoint HTTP zgodny z OpenAI oraz jeden nagłówek. Każde SDK OpenAI działa po ustawieniu base_url na odnogę.

A co z row-level security?

odnoga nigdy nie dotyka Twoich tabel. Zapisuje identyfikator użytkownika końcowego, który wysyłasz, aby przypisać koszt. Twój klient Supabase i RLS nadal określają, jakie dane widzi użytkownik.

Czy mogę z przeglądarki (tokeny eut)?

Tak. Generujesz krótkotrwały token eut_ z edge function przez POST /v1/sessions, przekazujesz do przeglądarki i używasz jako bearer.

Uruchom AI w Edge Functions już dziś.