KOHAMI Docs
Guides

Data channels

Le back-end et la tablette échangent des messages JSON via trois familles de topics sur le DataChannel LiveKit. Chaque message est validé par un schéma Zod exporté depuis @kohami/shared.

FamilleDirectionExemples de topics
kohami.widget.databack vers tabletteweather:result, news:result, quiz:question, vision:result, assistance:session_started
kohami.user.*tablette vers backkohami.user.alert_button_pressed, user:weather, user:request_quiz
kohami.agent.eventsback vers tabletteturn_state, tool_invoked, stt_fallback_active, onboarding_completed

Le transport LiveKit est reliable par défaut (ordre préservé par publisher et par topic). Le back-end publie séquentiellement (résultat d'abord, puis tool-open). La tablette peut donc se reposer sur l'ordre.

Souscription côté tablette

datachannel-subscribe.ts
import { Room, RoomEvent } from "livekit-client";
import type {
  AvcSuspicionDetectedEvent,
  NewsResultEvent,
  QuizQuestionEvent,
  TurnStateEvent,
  WeatherResultEvent,
} from "@kohami/shared";

type AnyKohamiEvent =
  | WeatherResultEvent
  | NewsResultEvent
  | QuizQuestionEvent
  | AvcSuspicionDetectedEvent
  | TurnStateEvent;

function handleDataPacket(payload: Uint8Array): void {
  const raw = new TextDecoder().decode(payload);
  const event = JSON.parse(raw) as AnyKohamiEvent;

  switch (event.kind) {
    case "weather:result":
      renderWeatherCard(event);
      break;
    case "news:result":
      renderNewsList(event);
      break;
    case "quiz:question":
      renderQuiz(event);
      break;
    case "turn_state":
      updateAvatarSpeakingState(event);
      break;
    default:
      console.warn("Unknown event kind", event);
  }
}

room.on(RoomEvent.DataReceived, (payload) => {
  handleDataPacket(payload);
});

Publication côté tablette (inbound)

datachannel-publish.ts
async function publishUserAction(
  room: Room,
  event: { kind: string; [key: string]: unknown }
): Promise<void> {
  const payload = new TextEncoder().encode(JSON.stringify(event));
  await room.localParticipant.publishData(payload, {
    reliable: true,
    topic: "kohami.user.alert_button_pressed",
  });
}

Validez systématiquement le payload avec le schéma Zod correspondant avant publication :

import { UserWeatherInboundSchema } from "@kohami/shared";

const validated = UserWeatherInboundSchema.parse({
  kind: "user:weather",
  location: { lat: 48.8566, lon: 2.3522 },
});

Topics par famille

Outbound kohami.widget.data

Voir le catalogue exhaustif dans Events catalogue. Format général :

{
  kind: "<topic-name>",      // discriminant string
  // payload spécifique au topic
}

Inbound kohami.user.*

Le préfixe kohami.user.* regroupe toutes les actions initiées par le senior depuis la tablette. Liste actuelle :

  • kohami.user.alert_button_pressed, bouton d'alerte pressé
  • user:weather, tap sur la card météo
  • user:news, tap sur la card news
  • user:request_quiz ou user:request_guess, lancement d'un mini-jeu cognitif
  • user:answer_quiz ou user:answer_guess, réponse soumise (incluant isCorrect)
  • user:appointment_acknowledged, accusé de réception d'un rendez-vous
  • user:medication_taken ou user:medication_snoozed, gestion d'un rappel médicament
  • user:onboarding_recap_acknowledged, fin de l'étape de récap onboarding

Agent events kohami.agent.events

Le voice-worker publie des événements de cycle de vie :

  • turn_state, état du tour (agent_thinking, agent_speaking, agent_idle)
  • tool_invoked, un outil a été appelé (getCurrentWeather, contactAidant, etc.)
  • tool_call ou tool_result, début ou fin d'un appel d'outil avec payload
  • onboarding_completed, fin de l'extraction d'onboarding par tour
  • stt_fallback_active, tts_fallback_active, llm_fallback_active, bascule en mode dégradé
  • wake_word_detected, détection du mot d'éveil (si activé)
  • error, frame d'erreur (voir Gestion d'erreurs)

Tous les schémas sont exportés depuis @kohami/shared via AgentEventSchema (discriminated union).

Ordering garanti

Le back-end publie les events dans un ordre strict pour les widgets :

  1. Le résultat d'abord (weather:result, news:result, quiz:question, etc.)
  2. Le tool-open ensuite (kohami.tool.open avec name restreint à l'enum WIDGET_TOOL_NAMES)

Le transport reliable de LiveKit garantit l'ordre per-publisher, ce qui suffit côté tablette pour afficher la card avant de la mettre au premier plan.