npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@owllayer/adapter-livekit

v0.4.0

Published

OwlLayer Adapter - LiveKit optional runtime foundation

Readme

@owllayer/adapter-livekit

npm version npm downloads License: MIT TypeScript strict Node.js 22

Adapter optionnel pour utiliser LiveKit avec OwlLayer.

Cette doc repond a la question principale : comment brancher l'adapter LiveKit dans une application OwlLayer existante.

Ce que fournit le package

@owllayer/adapter-livekit fournit quatre briques serveur :

  • GeminiLiveAdapter : adapter live pour OwlLayerServer, compatible avec le contrat LiveAdapter de @owllayer/core.
  • GeminiTTSService : service tts compatible avec le contrat TTSService de @owllayer/core.
  • LiveKitRoomTokenService / createLiveKitRoomToken : generation de tokens de room LiveKit cote serveur.
  • OwlLayerLiveKitAgentBridge : bridge optionnel pour relier une AgentSession LiveKit au pipeline tools de OwlLayer.

LiveKit reste optionnel. @owllayer/server ne l'importe pas directement. Votre application importe cet adapter uniquement quand elle veut activer LiveKit.

Catalogue de modeles et voix Gemini

GEMINI_LIVE_VOICES et GEMINI_TTS_VOICES / GEMINI_TTS_VOICE_INFOS listent les 30 voix Gemini documentees, avec le meme genre et les memes langues que GEMINI_VOICES de @owllayer/adapter-google (FR-009). Toute chaine reste acceptee ; les constantes sont recommandees.

  • GEMINI_LIVE_MODELS : modeles Gemini Live actifs, avec role et statut ; DEFAULT_GEMINI_LIVE_MODEL ('gemini-2.5-flash-native-audio-preview-12-2025') : le plugin Google de LiveKit applique les nouveaux tools en reprenant la session, et seul Gemini 2.5 les prend ; Gemini 3.x Live garde les tools de l'ouverture.
  • GEMINI_TTS_MODELS : modeles Gemini TTS ; DEFAULT_GEMINI_TTS_MODEL inchange.
  • GEMINI_LANGUAGES (['multilingual']) et geminiSupportsLanguage(id, language) : toutes les entrees Gemini sont multilingues.
  • isKnownGeminiModel(id, role?) / isKnownGeminiVoice(id) : sans jamais lever.
  • GEMINI_DEPRECATED_MODELS / getGeminiDeprecatedModel(id) : modeles Gemini deprecies ou retires (par exemple l'ancien alias Live gemini-2.5-flash-native-audio-preview). Construire GeminiLiveAdapter ou GeminiTTSService avec l'un d'eux — constante ou chaine libre — journalise un avertissement nommant le remplacant ; la valeur configuree est toujours utilisee.
  • GEMINI_CATALOG_VERIFIED_AT : date de derniere verification par rapport a la documentation officielle Google.

Installation

pnpm add @owllayer/adapter-livekit

Pour le client React qui rejoint une room LiveKit :

pnpm add livekit-client

livekit-client est un peer dependency optionnel de @owllayer/react.

Variables serveur

Ces variables restent cote serveur :

LIVEKIT_URL=wss://your-livekit-host
LIVEKIT_API_KEY=lk_api_key
LIVEKIT_API_SECRET=lk_api_secret

# Provider Gemini utilise par l'implementation actuelle.
GOOGLE_API_KEY=google_api_key

# Optionnel si vous utilisez Vertex AI.
GOOGLE_GENAI_USE_VERTEXAI=false
GOOGLE_CLOUD_PROJECT=
GOOGLE_CLOUD_LOCATION=

# Origines autorisees pour l'endpoint token.
OWLLAYER_LIVEKIT_ALLOWED_ORIGINS=http://localhost:5173,https://app.example.com

Ne placez jamais LIVEKIT_API_SECRET, LIVEKIT_API_KEY ou GOOGLE_API_KEY dans le frontend. Le navigateur recoit seulement un token de room court genere par votre serveur.

Usage 1 - Brancher Gemini Live via LiveKit dans OwlLayerServer

GeminiLiveAdapter s'utilise comme les autres adapters live (GoogleLiveAdapter, OpenAILiveAdapter) : il se passe dans l'option live du serveur.

import 'dotenv/config';
import { OwlLayerServer } from '@owllayer/server';
import { GoogleAdapter, GOOGLE_DEFAULT_TEXT_MODEL } from '@owllayer/adapter-google';
import { GeminiLiveAdapter, DEFAULT_GEMINI_LIVE_VOICE } from '@owllayer/adapter-livekit';

const server = new OwlLayerServer({
  llm: new GoogleAdapter({
    apiKey: process.env.GOOGLE_API_KEY!,
    model: GOOGLE_DEFAULT_TEXT_MODEL,
    systemPrompt: 'Tu es un assistant OwlLayer.',
  }),

  live: new GeminiLiveAdapter({
    apiKey: process.env.GOOGLE_API_KEY!,
    voice: DEFAULT_GEMINI_LIVE_VOICE,
    systemPrompt: 'Tu es un assistant vocal OwlLayer. Reponds court.',
  }),

  port: 3001,
  path: '/owllayer',
  client: {
    requireApiKey: true,
  },
});

server.addApiKey(process.env.OWLLAYER_API_KEY!);

server.tool(
  'get_order_status',
  {
    description: 'Retourne le statut d une commande.',
    parameters: {
      type: 'object',
      properties: {
        orderId: { type: 'string' },
      },
      required: ['orderId'],
    },
  },
  async ({ orderId }) => {
    return { orderId, status: 'processing' };
  }
);

server.listen(() => {
  console.log('OwlLayer ready on http://localhost:3001');
});

Dans ce mode, OwlLayer continue de gerer :

  • les sessions ;
  • les API keys ;
  • les server tools ;
  • les client tools exposes par les SDK frontend ;
  • HITL ;
  • les TOOL_CALL / TOOL_RESULT.

L'adapter LiveKit traduit seulement les evenements provider vers le contrat LiveAdapter de OwlLayer.

Usage 2 - Brancher Gemini TTS via LiveKit

Si vous voulez uniquement utiliser le TTS Gemini via LiveKit, passez GeminiTTSService dans tts.

import { OwlLayerServer } from '@owllayer/server';
import { GoogleAdapter, GOOGLE_DEFAULT_TEXT_MODEL } from '@owllayer/adapter-google';
import { GeminiTTSService, DEFAULT_GEMINI_TTS_VOICE } from '@owllayer/adapter-livekit';

const server = new OwlLayerServer({
  llm: new GoogleAdapter({
    apiKey: process.env.GOOGLE_API_KEY!,
    model: GOOGLE_DEFAULT_TEXT_MODEL,
  }),
  tts: new GeminiTTSService({
    apiKey: process.env.GOOGLE_API_KEY!,
    defaultVoice: DEFAULT_GEMINI_TTS_VOICE,
  }),
  port: 3001,
  path: '/owllayer',
});

Ce mode ne cree pas de room LiveKit. Il transforme du texte en audio et laisse OwlLayer transporter la reponse.

Usage 3 - Donner une room LiveKit au client React

Le client ne doit pas connaitre les secrets LiveKit. Il appelle un endpoint serveur qui verifie la session OwlLayer puis genere un token.

Endpoint token cote serveur

Le demo-server fournit un exemple complet avec createLiveKitTokenRequestHandler.

import { createServer } from 'http';
import { OwlLayerServer } from '@owllayer/server';
import {
  createLiveKitRoomToken,
  isLiveKitServerEnvConfigured,
  resolveLiveKitRuntimeConfig,
} from '@owllayer/adapter-livekit';

const httpServer = createServer();
const server = new OwlLayerServer({
  llm,
  server: httpServer,
  path: '/owllayer',
  client: { requireApiKey: true },
});

httpServer.on('request', async (req, res) => {
  const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
  if (url.pathname !== '/owllayer/livekit/token') {
    return;
  }

  if (req.method !== 'POST') {
    res.writeHead(405, { Allow: 'POST' });
    res.end();
    return;
  }

  if (!isLiveKitServerEnvConfigured(process.env)) {
    res.writeHead(503, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'livekit_not_configured' }));
    return;
  }

  const body = await readJson(req);
  const sessionId = String(body.sessionId ?? '');
  const apiKey = readBearerApiKey(req);

  if (!apiKey || !server.isAgentBridgeSessionOwnedByApiKey(sessionId, apiKey)) {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'owllayer_session_not_found' }));
    return;
  }

  const snapshot = await server.getAgentBridgeSessionSnapshot(sessionId);
  if (!snapshot) {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'owllayer_session_not_found' }));
    return;
  }

  const token = await createLiveKitRoomToken(
    {
      sessionId: snapshot.sessionId,
      roomName: body.roomName,
      participantName: body.participantName,
      ttlSeconds: 300,
    },
    { config: resolveLiveKitRuntimeConfig({}, process.env) }
  );

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(token));
});

Dans votre application, gardez la meme logique de securite que le demo-server :

  • verifier l'API key OwlLayer du client ;
  • verifier que la session demandee appartient a cette API key ;
  • appliquer une allowlist CORS pour vos domaines deployes ;
  • limiter le TTL du token ;
  • ne jamais mettre de secret dans les metadata LiveKit.

Client React

Le hook useOwlLayerLiveKitRoom se branche dans une app React deja connectee a OwlLayer.

import { OwlLayerProvider, useAgent, useOwlLayerLiveKitRoom } from '@owllayer/react';

function VoiceRoomButton() {
  const { sessionId, agentState } = useAgent();
  const room = useOwlLayerLiveKitRoom({
    tokenEndpoint: 'http://localhost:3001/owllayer/livekit/token',
    apiKey: import.meta.env.VITE_OWLLAYER_API_KEY,
    autoConnect: false,
    disconnectOnUnmount: true,
    disconnectOnOwlLayerDisconnect: true,
    microphoneEnabledOnConnect: true,
  });

  if (!sessionId || agentState === 'disconnected') {
    return null;
  }

  return (
    <button
      type="button"
      disabled={room.status === 'requesting-token' || room.status === 'connecting'}
      onClick={() => room.isConnected ? room.disconnect() : void room.connect()}
    >
      {room.isConnected ? 'Quitter la room' : 'Rejoindre la room'}
    </button>
  );
}

export function App() {
  return (
    <OwlLayerProvider
      endpoint="ws://localhost:3001/owllayer"
      apiKey={import.meta.env.VITE_OWLLAYER_API_KEY}
    >
      <VoiceRoomButton />
      {/* Vos composants OwlLayer et vos tools React restent inchanges. */}
    </OwlLayerProvider>
  );
}

Le hook demande automatiquement un token avec le sessionId OwlLayer courant. Il connecte ensuite livekit-client avec livekitUrl et token.

Usage 4 - Bridge AgentSession LiveKit vers les tools OwlLayer

OwlLayerLiveKitAgentBridge sert quand vous utilisez une AgentSession LiveKit et voulez que ses tool calls reviennent dans OwlLayer.

import { OwlLayerLiveKitAgentBridge } from '@owllayer/adapter-livekit';

const bridge = new OwlLayerLiveKitAgentBridge({
  toolExecutor: (toolCall, context) =>
    server.routeAgentBridgeToolCall(context.sessionId, toolCall),
});

const snapshot = await server.getAgentBridgeSessionSnapshot(sessionId);
if (snapshot) {
  await bridge.start(snapshot);
}

Le trajet attendu est :

  1. AgentSession appelle un tool.
  2. Le bridge convertit l'appel en LLMToolCall OwlLayer.
  3. OwlLayerServer route l'appel via ToolRouter et HITL.
  4. Si le tool est cote client, OwlLayerClient l'execute dans l'interface montee.
  5. Le resultat revient au bridge puis au provider LiveKit.

Cette partie est utile pour les agents LiveKit avances. Pour un usage simple audio/realtime, commencez par GeminiLiveAdapter et useOwlLayerLiveKitRoom.

Exemple demo existant

Dans ce repo :

  • apps/demo-server/src/server.ts montre un serveur OwlLayer avec endpoint /owllayer/livekit/token.
  • apps/demo-server/src/livekitTokenEndpoint.ts montre la verification API key + session avant creation du token.
  • apps/demo-react/src/components/LiveKitRoomButton.tsx montre le bouton React qui rejoint/quitte la room.

Limites actuelles

  • Gemini est le provider LiveKit implemente aujourd'hui, mais l'architecture reste ouverte a d'autres providers LiveKit.
  • La telephonie/SIP n'est pas encore implementee.
  • Les updates de tools mid-session dependent du provider LiveKit. Quand ce n'est pas supporte, OwlLayer doit appliquer les nouveaux tools a la prochaine session.
  • Le dashboard affiche seulement de l'etat operationnel redige. Il ne doit pas afficher de token, secret, contexte brut, args de tools ou resultats de tools.

License

MIT © OwlLayer