ephia-protocol

中文文档
1.2.0发布于 2026/11/03TypeScriptES Module

Contrat universel EphiaAudioEvent — types et schémas

原始使用文档
文档目录17 节
  1. Rôle et consommateurs autorisés
  2. Principe fondamental
  3. À quoi sert ce dossier ?
  4. Enveloppe commune (tous les événements)
  5. Règles du protocole
  6. Règle d'atomicité des fusions
  7. Événements par catégorie
  8. Session
  9. Audio / VAD
  10. Pipeline segment (dual)
  11. Transcription — temps réel
  12. Transcription — cycle de vie segment
  13. Document
  14. Flux normal d'un segment
  15. Exemple de payload committed
  16. Messages client → backend
  17. Usage

ephia-protocol

Contrat universel EphiaAudioEvent — le cœur du système.

Rôle et consommateurs autorisés

ephia-protocol est le contrat wire interne d'Ephia : types, schemas Zod, fixtures et changelog protocolaire. Voir docs/wire-contract.md et docs/protocol-versioning.md pour le détail des invariants actuels (protocole v3 : segment.canonical, commandSeq, ephiaWireServerEventSchema).

Il est consommé directement par :

  • @ephia/sdk (packages/ephia-sdk) — seul runtime client autorisé à interpréter le wire ;
  • les tests de contrat de ce package ;
  • le backend Python via les fixtures JSON de src/fixtures/.

Il n'est pas une API cliente. Les apps ne doivent pas importer les schemas wire (ephiaWireServerEventSchema, etc.) pour parser ou appliquer la dictée directement — elles consomment uniquement @ephia/sdk, qui réexporte les types publics nécessaires.

Note : le reste de ce README (sections « Enveloppe commune », « Événements par catégorie », exemple transcript.segment.committed) décrit un modèle d'événements antérieur au protocole v3 actuel et n'a pas été mis à jour dans le cadre de cette PR — à corriger séparément.

Principe fondamental

Protocol > Framework

Ce package est indépendant. Il ne dépend ni du SDK, ni de React, ni de LiveKit, ni du backend.

Il contient :

  • types TypeScript des événements publics
  • schémas Zod pour validation runtime
  • messages client
  • états de session
  • codes d'erreur
  • version du protocole (3 wire / "3.0" REST capabilities)

À quoi sert ce dossier ?

packages/protocol est le contrat partagé du produit audio. Il évite que le backend et le SDK évoluent avec des définitions d'événements divergentes.

Concrètement :

  • le backend doit émettre des payloads JSON conformes aux schémas de ce package
  • le SDK importe les types pour typer ses reducers, handlers et callbacks
  • le SDK valide chaque événement reçu avec ephiaAudioEventSchema.safeParse()
  • les tests peuvent vérifier la compatibilité backend/SDK à partir d'une seule source de vérité

Le backend Python n'importe pas le code TypeScript directement. La contrainte est contractuelle : les noms d'événements, champs obligatoires et formats de payload doivent rester compatibles avec les schémas Zod publiés ici.

Enveloppe commune (tous les événements)

{
  "protocolVersion": 3,
  "eventId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionId": "sess_abc",
  "type": "transcript.segment.committed",
  "seq": 42,
  "timestampMs": 1716200000000,
  "payload": { "..." },
  "meta": { "provider": "voxtral-small", "latencyMs": 340 }
}
  • seq : strictement croissant par session
  • eventId : UUID unique
  • timestampMs : timestamp serveur
  • sessionId : obligatoire sur tous les events
  • meta : optionnel — diagnostics, latences, modes

Règles du protocole

  • Aucun preview/stable ne doit écraser un segment déjà committed
  • committed insère la version courante du texte de façon atomique
  • reviewing signale une review SMART sans changer le texte
  • corrected remplace exactement le segment committed correspondant
  • review.rejected termine une review sans changement de texte
  • Ordre événementiel déterministe par seq

Règle d'atomicité des fusions

transcript.segment.committed est l'événement atomique d'application documentaire.

Lorsqu'un segment absorbe d'autres segments, le payload mergedWith contient les IDs absorbés. Le client doit appliquer le commit final et la suppression des absorbés dans une seule transaction visuelle atomique — jamais en deux étapes séparées.

segment.absorbed est un événement de lifecycle/debug. Il est émis après transcript.segment.committed correspondant. Il ne doit jamais provoquer de suppression visuelle avant le commit fusionné.

Événements par catégorie

Session

TypePayload clésDescription
session.startedclientType, mode, languageSession initiée
session.readyroomNameRoom LiveKit prête, dictée possible
session.stt_readybufferedFramesFlushedSTT initialisé (lazy init terminé)
session.paused—Session mise en pause
session.closedreasonSession terminée (user_stop|disconnect|error|timeout)
session.errorcode, message, recoverableErreur session
session.pong—Réponse à un ping keepalive
session.context.resetcontextOnly, reasonContexte backend réinitialisé
session.catchup.startedtrackName, durationMsCatchup audio démarré
session.catchup.completedsegmentCount, durationMsCatchup terminé
session.catchup.failedreasonCatchup échoué

Audio / VAD

TypePayload clésDescription
audio.vad.starttimestampMsVoix détectée
audio.vad.endtimestampMs, durationMsFin de voix

Pipeline segment (dual)

TypePayload clésDescription
segment.openedsegmentId, segmentSeq, startMsSegment audio ouvert
segment.audio.closedsegmentId, endMs, reasonPartie audio du segment terminée
segment.droppedsegmentId, reasonSegment silencieux/bruit, ignoré
segment.absorbedsegmentId, absorbedIntoSegment fusionné dans un autre

Transcription — temps réel

TypePayload clésDescription
transcript.previewsegmentId, segmentSeq, text, revisionTexte en cours (peut se répéter)
transcript.preview.stablesegmentId, segmentSeq, text, revisionPartie stable du preview

Transcription — cycle de vie segment

TypePayload clésDescription
transcript.segment.openedsegmentId, segmentSeq, startedAtMsDébut segment (pipeline legacy)
transcript.segment.processingsegmentId, durationMsFinalisation en cours
transcript.segment.completedsegmentId, providerFinalisation terminée
transcript.segment.readysegmentId, text, tagsTexte finalisé (bridge visuel avant commit)
transcript.segment.committedsegmentId, text, tags, mergedWith?Texte définitif — à appliquer atomiquement
transcript.segment.reviewingsegmentId, tagsReview SMART en cours
transcript.segment.correctedsegmentId, textCorrection review appliquée
transcript.segment.revisedsegmentId, newText, oldTextRévision post-commit
transcript.segment.review.queuedsegmentIdReview mise en attente
transcript.segment.review.rejectedsegmentId, reason, textReview terminée sans correction
transcript.segment.review.failedsegmentId, errorErreur review
transcript.segment.errorsegmentId, code, message, recoverableErreur finalisation

Document

TypePayload clésDescription
document.patchops[]Opérations structurelles sur le document

Flux normal d'un segment

segment.opened
  → transcript.preview         (texte interim, 1..N fois)
  → transcript.preview.stable  (texte stable, 0..N fois)
  → segment.audio.closed       (audio terminé, traitement en cours)
  → transcript.segment.committed  ← appliquer atomiquement
  → [transcript.segment.reviewing
     → transcript.segment.corrected | transcript.segment.review.rejected]

Exemple de payload committed

{
  "protocolVersion": 3,
  "eventId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionId": "sess_abc",
  "type": "transcript.segment.committed",
  "seq": 42,
  "timestampMs": 1716200000000,
  "payload": {
    "segmentId": "seg_12",
    "segmentSeq": 12,
    "text": "Le foie est de taille normale.",
    "source": "voxtral-small",
    "tags": [{ "tag": "anatomy", "word": "foie" }]
  },
  "meta": {
    "provider": "voxtral-small",
    "latencyMs": 820,
    "audioDurationMs": 2300
  }
}

Messages client → backend

MessageEnvoyé viaDescription
{ type: "session.pause" }data_channelMettre en pause
{ type: "session.resume" }data_channelReprendre
{ type: "session.reset", payload: { contextOnly: true } }data_channelReset contexte
{ type: "catchup.start", payload: { catchupId, source, sampleRate, channels, bytesTotal, durationMs, clientStartedAtMs?, clientEndedAtMs?, catchupSeq?, operationId?, startSample?, endSample? } }data_channelDémarrer un burst PCM16 mono 16 kHz catchup ; startSample/endSample lient le prefix à audio.live.anchor
{ type: "catchup.chunk", payload: { catchupId, chunkIndex, offsetBytes, dataBase64 } }data_channelEnvoyer un chunk catchup fiable et ordonné
{ type: "catchup.end", payload: { catchupId, chunkCount, bytesTotal } }data_channelFinaliser le burst catchup
{ type: "catchup.abort", payload: { catchupId, reason } }data_channelAnnuler le burst catchup
ephia.session.pauseRPCIdentique pause (mode useRpcCommands)
ephia.session.resumeRPCIdentique resume (mode useRpcCommands)
ephia.session.resetRPCIdentique reset (mode useRpcCommands)

Usage

import {
  ephiaAudioEventSchema,
  type EphiaAudioEvent,
  type TranscriptSegmentCommittedEvent,
  PROTOCOL_VERSION,  // 4
} from "ephia-protocol";

// Validation runtime
const parsed = ephiaAudioEventSchema.safeParse(raw);
if (!parsed.success) {
  console.warn("Invalid event:", parsed.error.issues);
  return;
}

// Discrimination par type
const event: EphiaAudioEvent = parsed.data;
if (event.type === "transcript.segment.committed") {
  const { segmentId, text, tags, mergedWith } = event.payload;
  applyToEditor(segmentId, text, { absorbed: mergedWith ?? [] });
}

版本与发布记录

ephia-protocol 共发布 3 个版本,首次发布于 2026/07/17;过去 12 个月发布了 3 个版本。

版本发布日期距今
1.2.0latest2026/11/03今天
1.1.02026/10/2212 天前
1.0.02026/07/173 个月前

关于 ephia-protocol 的常见问题

ephia-protocol 是什么?

Contrat universel EphiaAudioEvent - types et sch忧郁

如何安装 ephia-protocol?

在项目目录运行 npm install ephia-protocol,或使用 pnpm add / yarn add / bun add。

ephia-protocol 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

ephia-protocol 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

ephia-protocol 可以商用吗?

ephia-protocol 使用 未声明 许可。package.json 没有声明许可证。没有许可证的代码默认保留全部权利,商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括,不构成法律意见,请以许可原文为准。

ephia-protocol 还在维护吗?

最新版本 1.2.0 发布于 2026/11/03(今天);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/07/17。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。