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

@vivix-ai/ivi-frontend-sdk

v0.3.12

Published

A frontend SDK scaffold for IVI platform.

Readme

IVI Frontend SDK

用于前端应用集成 IVI 实时能力的 TypeScript SDK,包含:

  • 基于 @vivix-ai/ivi-sdk-ts 的 runtime 协调层
  • 通过 API 网关创建 IVI Session 的 HTTP 辅助方法
  • 面向舞台渲染的 React 组件与 hooks
  • 基于 OpenTelemetry API 的链路 span(SDK 不内置 exporter/provider)

安装依赖

当前包发布到 npm 官方仓库,包名为 @vivix-ai/ivi-frontend-sdk

安装 SDK:

# npm
npm install @vivix-ai/ivi-frontend-sdk

# pnpm
pnpm add @vivix-ai/ivi-frontend-sdk

# yarn
yarn add @vivix-ai/ivi-frontend-sdk

安装后 package.json 中会出现类似条目:

{
  "dependencies": {
    "@vivix-ai/ivi-frontend-sdk": "^0.3.0"
  }
}

更新到最新版本

# npm
npm update @vivix-ai/ivi-frontend-sdk

# pnpm
pnpm update @vivix-ai/ivi-frontend-sdk

# yarn
yarn up @vivix-ai/ivi-frontend-sdk

Peer Dependencies

React 为可选 peer dependency,仅使用 React 组件时需要:

{
  "react": "^18.0.0 || ^19.0.0",
  "react-dom": "^18.0.0 || ^19.0.0"
}

如果只使用 runtime 能力、不接入 React 组件与 hooks,可忽略本文中的 React 用法部分。


快速开始

1)创建 client + runtime

import { IviClient } from "@vivix-ai/ivi-sdk-ts";
import { IviRuntimeCoordinator } from "@vivix-ai/ivi-frontend-sdk";
import { LiveKitBrowserTransport } from "@vivix-ai/ivi-sdk-ts/transports/livekit-browser";

const client = new IviClient({
  transport: new LiveKitBrowserTransport({
    url: "wss://livekit.example.com",
    token: "livekit_access_token"
  }),
  onParseError: (error, raw) => {
    console.error("parse error", error, raw);
  }
});

const runtime = new IviRuntimeCoordinator(client, {
  syncStageOnSessionCreated: true,    // 默认 true,session 建立后自动同步 stage
  onSessionEnded: (event) => {
    console.log("session ended", event);
  }
});

await runtime.start();

也可以使用 IviFrontendSdk 创建 client/runtime:

import { IviFrontendSdk } from "@vivix-ai/ivi-frontend-sdk";
import { LiveKitBrowserTransport } from "@vivix-ai/ivi-sdk-ts/transports/livekit-browser";

const sdk = new IviFrontendSdk();
const runtime = sdk.createRuntimeCoordinator(
  {
    transport: new LiveKitBrowserTransport({
      url: "wss://livekit.example.com",
      token: "livekit_access_token"
    })
  },
  {
    syncStageOnSessionCreated: true
  }
);

await runtime.start();

1.1)通过 API 网关创建 IVI Session 并启动 runtime

如果业务侧已经接入 API 网关,可直接使用 SDK 内置的 createIVISession HTTP 封装。该方法会调用 POST /v1/ivi/sessions,拿到 ivi_session_id / endpoint 后创建 WebSocket runtime。

当创建会话响应里包含 prebuild_trtc_info 时,SDK 会把它转换成一个临时的 bootstrap source,并在 websocket 真实 stage/track/source 事件到达前,提前通过 IVITrackSlot slot="main" 展示画面。真实 source ready 后会自动接管,业务 UI 不需要区分“快速开播”和“常规开播”。

import {
  createIVISession,
  createRuntimeFromSession
} from "@vivix-ai/ivi-frontend-sdk";

const session = await createIVISession(
  {
    idempotencyKey: crypto.randomUUID(),
    streamType: "TRTC",
    iviVersion: "dev",
    sessionParameters: {
      resolution: "720p"
    },
    prebuiltCharacters: [
      {
        characterId: "character_claire",
        characterPayload: characterJson
      }
    ],
    prebuiltStream: {
      streamId: "stream_claire",
      mode: "character",
      streamConfig: {
        scene_description: "Claire on stage",
        character_ids: ["character_claire"],
        realtime_cvi_config: { enabled: false }
      }
    }
  },
  {
    baseUrl: "https://api.example.com",
    headers: async () => ({
      Authorization: `Bearer ${token}`
    })
  }
);

const { runtime } = createRuntimeFromSession(session, {
  fastStart: true,
  bootstrapSlot: "main",
  websocket: {
    query: {
      x_auth_jwt: authJwt
    },
    protocols: authJwt ? ["ivi", authJwt] : undefined
  }
});

await runtime.start();

也可以一步完成创建 session 与 runtime:

import { createIVISessionRuntime } from "@vivix-ai/ivi-frontend-sdk";

const { session, runtime } = await createIVISessionRuntime(request, {
  http: {
    baseUrl: "https://api.example.com",
    headers: { Authorization: `Bearer ${token}` }
  },
  fastStart: true,
  bootstrapSlot: "main"
});

await runtime.start();

createIVISession 返回值会保留原始 response,同时提供 SDK 归一化字段:

| 字段 | 类型 | 说明 | |------|------|------| | iviSessionId | string | 会话 ID,对应后端 ivi_session_id | | endpoint | string | IVI realtime websocket 地址 | | trtcInfo / livekitInfo | — | 会话级播放/传输信息(如果后端返回) | | prebuildTrtcInfo | — | 后端 prebuild_trtc_info 的 camelCase 视图 | | prebuiltPlayback | — | SDK 归一化后的快速开播播放源;存在时可用于 bootstrap source | | raw | unknown | 原始 HTTP response body | | requestBody | Record<string, unknown> | 实际发给网关的 snake_case JSON |

IviCreateIVISessionRequest 使用 camelCase 字段,SDK 会在发送前转换为 CP transcoder 需要的 snake_case JSON:

| SDK 字段 | HTTP 字段 | |----------|-----------| | idempotencyKey | idempotency_key | | streamType | stream_type | | iviVersion | ivi_version | | sessionParameters | session_parameters | | prebuiltCharacters | prebuilt_characters | | prebuiltStream | prebuilt_stream | | enableDecoderPublish | enable_decoder_publish | | sessionRecording | session_recording |

浏览器侧不应依赖 TRTC secret_key。SDK 在归一化 TRTC 信息时会丢弃 secret_key / secretKey,播放器只使用拉流所需的 app_idroom_iduser_iduser_sig

1.2)OpenTelemetry 链路观测

SDK 只依赖 @opentelemetry/api 并创建 spans,不配置上报目的地。业务应用只要在入口处配置自己的 OpenTelemetry provider/exporter,随后把 telemetry 透传给 SDK 即可;未配置 provider 时这些调用会自动 no-op。

import { context, trace } from "@opentelemetry/api";
import { createIVISessionRuntime } from "@vivix-ai/ivi-frontend-sdk";

const telemetry = {
  tracer: trace.getTracer("my-live-page"),
  context: context.active(), // 可接上按钮点击、页面路由等外部父链路
  attributes: {
    "app.feature": "ivi-live"
  }
};

const { runtime } = await createIVISessionRuntime(request, {
  http: {
    baseUrl: "https://api.example.com",
    headers: { Authorization: `Bearer ${token}` }
  },
  telemetry,
  fastStart: true,
  bootstrapSlot: "main"
});

await runtime.start();

会被串联的关键 spans 包括:

  • sdk.session.create:HTTP 创建 IVI Session
  • sdk.runtime.create / sdk.runtime.start / sdk.runtime.stop
  • sdk.client.connect / sdk.client.connection:底层 @vivix-ai/ivi-sdk-ts WebSocket 连接与聚合消息计数
  • sdk.media.trtc.join_room / sdk.media.livekit.join_room:媒体进房耗时
  • sdk.media.trtc.waiting_remote_video_available / sdk.media.trtc.start_remote_video / sdk.media.trtc.remote_video_rendered:TRTC 远端视频事件等待、开始渲染、渲染完成等首屏关键点
  • sdk.media.livekit.*:LiveKit 远端视频可用等首屏关键点

SDK span 不会写入 token、user_sig、TRTC secret_key 或完整业务 payload。低基数字段可以放在 telemetry.attributes 中统一附加。

使用自定义 WebSocket 运行时(Node.js / 自定义环境):

import WebSocket from "ws"; // Node.js 环境
import { IviClient } from "@vivix-ai/ivi-sdk-ts";
import { WebSocketTransport } from "@vivix-ai/ivi-sdk-ts/transports/websocket";
import { IviRuntimeCoordinator } from "@vivix-ai/ivi-frontend-sdk";

const client = new IviClient({
  transport: new WebSocketTransport({
    url: "wss://your-domain/v1/realtime",
    sessionId: "sess_xxx",
    socketFactory: (url, protocols) => new WebSocket(url, protocols)
  }),
  onParseError: (error, raw) => {
    console.error("parse error", error, raw);
  }
});

const runtime = new IviRuntimeCoordinator(client);
await runtime.start();

IviFrontendClientConfig 等同于 @vivix-ai/ivi-sdk-tsIviClientConfig,需要直接传入通用 transport

| 参数 | 类型 | 说明 | |------|------|------| | transport | IviRealtimeTransport | 通用 transport,直接透传给 @vivix-ai/ivi-sdk-ts;浏览器 LiveKit 场景可传 LiveKitBrowserTransport | | onParseError | (error, rawMessage) => void | 事件解析失败回调 | | onLog | (entry) => void | IviClient 链路日志回调,包含发送、transport 状态和重连链路 | | telemetry | IviClientTelemetryOptions | OpenTelemetry 配置;通常由上层 SDK 透传 | | reconnect | IviClientReconnectOptions | 自动重连配置;默认启用,指数退避 | | sendWaitTimeoutMs | number | 发送时等待连接恢复的超时,默认 30000 ms,<=0 表示无限等待 |

2)订阅状态与事件

const unlistenState = runtime.onStateChange((state) => {
  console.log(state.status, state.stage, state.tracks.size, state.sources.size);
});

const unlistenEvent = runtime.onEvent((event, state) => {
  console.log("event:", event.type, "runtime:", state.status);
});

const unlistenTrtcEvent = runtime.onTrtcEvent((event) => {
  console.log("trtc event:", event.sourceId, event.type, event.payload);
});

// 业务结束时
unlistenState();
unlistenEvent();
unlistenTrtcEvent();
runtime.stop();

也可以在创建 runtime 时通过 onTrtcEvent 配置项统一接收 TRTC SDK 原始事件:

const runtime = new IviRuntimeCoordinator(client, {
  trtcAIDenoiser: {
    enabled: true,
    mode: "normal" // 也可使用 "far_field_reduction"
  },
  onTrtcEvent: (event) => {
    // event.type: "remote_video_available" | "remote_video_unavailable" | "remote_audio_available" | "remote_audio_unavailable"
    // event.rawType: TRTC SDK 原始事件名
    // event.payload: TRTC SDK 原始事件 payload
  }
});

trtcAIDenoiser 默认开启,传 false 可关闭;也可以传 { enabled, mode, assetsPath } 调整 TRTC SDK 的 AIDenoiser 插件参数。

3)发送用户文本并触发模型回复

const result = await runtime.sendUserTextAndTriggerResponse({
  streamId: "stream_001",
  text: "你好",
  callbacks: {
    onConversationItemAdded: (event) => console.log("item added"),
    onConversationItemDone: (event) => console.log("item done"),
    onConversationItemReady: ({ itemId }) => console.log("item ready:", itemId),
    onResponseCreated: (event) => console.log("response created")
  }
});
console.log("完成:", result.itemId, result.streamId);

4)上报 source 播放完成

runtime.sendSessionSourcePlaybackCompleted("source_001", "track_001");

5)runtime 状态字段

IviRuntimeState 包含:

| 字段 | 类型 | 说明 | |------|------|------| | status | "idle" \| "connecting" \| "syncing" \| "running" \| "stopped" | 生命周期状态 | | session | IviRealtimeSessionConfig \| null | 当前会话(来自 @vivix-ai/ivi-sdk-ts) | | stage | IviStageComposition \| null | 当前 stage(slot → track 映射,来自 @vivix-ai/ivi-sdk-ts) | | tracks | Map<string, IviTrack> | 所有 track | | sources | Map<string, IviRuntimeSource> | 所有 source(含 status: created \| ready \| failed) | | conversationItems | Map<string, IviRuntimeConversationItem> | 对话条目映射 | | conversations | IviRuntimeConversationItem[] | 对话有序列表 | | bootstrap | IviRuntimeBootstrapViewState \| null | prebuild 快速开播的临时 slot/track/source 绑定状态 |


React 接入

方式 A:传入外部 runtime

import { IviClient } from "@vivix-ai/ivi-sdk-ts";
import { IviRuntimeCoordinator, IVIStageView, IVITrackSlot, useIviStageView } from "@vivix-ai/ivi-frontend-sdk";
import { LiveKitBrowserTransport } from "@vivix-ai/ivi-sdk-ts/transports/livekit-browser";

// 初始化 runtime(参考上方 Core 用法)
const client = new IviClient({
  transport: new LiveKitBrowserTransport({
    url: "wss://livekit.example.com",
    token: "livekit_access_token"
  })
});
const runtime = new IviRuntimeCoordinator(client);
await runtime.start();

function StageDebug() {
  const { slotBindings } = useIviStageView();
  return <pre>{slotBindings.map((x) => `${x.slot} → ${x.trackId} → ${x.sourceId ?? "null"}`).join("\n")}</pre>;
}

export function StagePage() {
  return (
    <IVIStageView runtime={runtime}>
      {({ slotBindings }) => (
        <div>
          {slotBindings.map((binding) => (
            <IVITrackSlot key={binding.slot} slot={binding.slot} />
          ))}
          <StageDebug />
        </div>
      )}
    </IVIStageView>
  );
}

方式 B:使用 useManagedIviRuntime 管理 runtime

import { IVIStageView, IVITrackSlot, useManagedIviRuntime } from "@vivix-ai/ivi-frontend-sdk";
import { LiveKitBrowserTransport } from "@vivix-ai/ivi-sdk-ts/transports/livekit-browser";

export function AutoStagePage({ livekitToken }: { livekitToken: string }) {
  const runtime = useManagedIviRuntime({
    clientConfig: {
      transport: new LiveKitBrowserTransport({
        url: "wss://livekit.example.com",
        token: livekitToken
      })
    },
    onLog: (entry) => {
      // 统一收集 client / runtime / TRTC / LiveKit 的 IVI 链路日志
      console.log(entry.tag, entry.message, entry.data);
    },
    onRuntimeInitError: (err) => console.error("runtime init failed", err)
  });

  return (
    <IVIStageView runtime={runtime} onRuntimeEvent={(event) => console.log("event:", event.type)}>
      <IVITrackSlot slot="main" showVolumeControl showSubtitle />
    </IVIStageView>
  );
}

useManagedIviRuntime 的连接规则:

  1. 使用 clientConfig.transport
  2. clientConfig.transport 为空,则不创建 runtime
  3. 浏览器 LiveKit 场景可传 LiveKitBrowserTransport
  4. WebSocket、测试 fake transport 或其它 transport 也通过同一个 transport 字段传入

方式 C:使用 useIviSessionRuntime 创建会话并管理 runtime

当希望把 createIVISession 也放进 SDK 接入流程时,可以使用 useIviSessionRuntime。它会在挂载后创建 IVI Session、构造 WebSocket runtime,并在 autoStarttrue 时自动启动。

import {
  IVIStageView,
  IVITrackSlot,
  useIviSessionRuntime,
  type IviCreateIVISessionRequest
} from "@vivix-ai/ivi-frontend-sdk";

export function FastStartStagePage({
  request,
  token,
  authJwt
}: {
  request: IviCreateIVISessionRequest;
  token: string;
  authJwt?: string;
}) {
  const { runtime, session, status, error } = useIviSessionRuntime({
    request,
    http: {
      baseUrl: "https://api.example.com",
      headers: {
        Authorization: `Bearer ${token}`
      }
    },
    fastStart: true,
    bootstrapSlot: "main",
    websocket: {
      query: {
        x_auth_jwt: authJwt
      },
      protocols: authJwt ? ["ivi", authJwt] : undefined
    }
  });

  if (error) return <div>{error.message}</div>;

  return (
    <IVIStageView runtime={runtime}>
      <IVITrackSlot slot="main" showVolumeControl showSubtitle />
      <span>{status}</span>
      <span>{session?.iviSessionId}</span>
    </IVIStageView>
  );
}

request 变化会重新创建 session/runtime;业务侧应使用 useMemo 或稳定引用控制重启时机。


React Hooks

useManagedIviRuntime

function useManagedIviRuntime(config: IviManagedRuntimeConfig): IviRuntimeCoordinator | null;

负责根据 clientConfig 创建并管理一个 IviRuntimeCoordinator。该 hook 会处理:

  • IviClient / IviRuntimeCoordinator 的创建
  • autoStarttrue 时自动调用 runtime.start()
  • 卸载时自动 runtime.stop()
  • 初始化错误通过 onRuntimeInitError 上抛
  • 通过 onLog 统一收集 IviClient、runtime 状态变更、TRTC / LiveKit 管理器等 IVI 链路日志

IviManagedRuntimeConfig 关键字段:

| 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | clientConfig | Partial<IviFrontendClientConfig> | — | IviClient 配置;transport 为空时不创建 runtime | | autoStart | boolean | true | 是否自动启动 runtime | | runtimeConfig | IviRuntimeCoordinatorConfig | — | 透传给 IviRuntimeCoordinator | | clientConfig.telemetry / runtimeConfig.telemetry | IviFrontendTelemetryOptions | — | OpenTelemetry 透传;两处使用同一个配置即可串联 | | onRuntimeInitError | (error) => void | — | 初始化或启动失败回调 | | onLog | (entry) => void | — | 统一日志回调,覆盖 [IVI-SEND][IVI-TRANSPORT][IVI-RECONNECT][IVI-EVT][IVI-STATE][IVI-TRTC][IVI-LIVEKIT] |

onLog 的统一入参格式:

interface IviManagedRuntimeLogEntry {
  level: "info" | "warn" | "error";
  source: "client" | "runtime";
  tag: string;          // 例如 "[IVI-TRANSPORT]"、"[IVI-STATE]"
  message: string;      // 带 tag 的可读日志
  args: unknown[];      // 接近原 console 调用的参数
  data?: unknown;       // 结构化数据
}

useIviSessionRuntime

function useIviSessionRuntime(config: UseIviSessionRuntimeConfig): UseIviSessionRuntimeResult;

负责把 createIVISession、WebSocket transport 和 IviRuntimeCoordinator 组合起来。适合希望通过 API 网关创建会话,并在 prebuild 成功时自动快速开播的 React 接入。

UseIviSessionRuntimeConfig 关键字段:

| 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | request | IviCreateIVISessionRequest \| null \| undefined | — | 创建会话请求;为空时不创建 runtime | | http | IviCreateIVISessionHttpOptions | — | HTTP 网关配置,包含 baseUrl / url / headers / fetch | | fastStart | boolean | true | 是否启用 prebuild bootstrap source | | bootstrapSlot | string | "main" | 快速开播临时绑定的 slot | | bootstrapTrackId | string | "__ivi_bootstrap_track" | 快速开播临时 track id | | websocket | IviRuntimeWebSocketOptions | — | websocket 地址、query、protocols、socketFactory | | runtimeConfig | IviRuntimeCoordinatorConfig | — | 透传给 runtime | | clientConfig | Omit<IviClientConfig, "transport"> | — | 透传给 IviClient 的非 transport 配置 | | telemetry | IviFrontendTelemetryOptions | — | 同时串联创建 session、runtime 和底层 WebSocket spans | | enabled | boolean | true | 是否启用 hook | | autoStart | boolean | true | 是否创建后自动调用 runtime.start() | | onCreated | (session) => void | — | HTTP 创建会话成功回调 | | onRuntimeReady | (runtime, client) => void | — | runtime 构造完成回调 | | onError | (error) => void | — | 创建或启动失败回调 |

返回值:

| 字段 | 类型 | 说明 | |------|------|------| | status | "idle" \| "creating" \| "connecting" \| "running" \| "error" \| "stopped" | hook 生命周期状态 | | session | IviCreateIVISessionResult \| null | 创建会话结果 | | runtime | IviRuntimeCoordinator \| null | runtime 实例 | | client | IviClient \| null | 底层 realtime client | | error | Error \| null | 最近一次错误 |

useIviStageView

function useIviStageView(): IviStageViewContextValue;

读取最近 IVIStageView 提供的 Context,必须在 IVIStageView 子树内使用。

返回值 IviStageViewContextValue

| 字段 | 类型 | 说明 | |------|------|------| | runtime | IviRuntimeCoordinator \| null | 当前 runtime 实例 | | state | IviRuntimeState | 实时状态快照 | | slotTrackMap | Map<string, string> | slot → trackId 映射 | | slotBindings | IviStageSlotBinding[] | 完整绑定信息数组 |

function MyOverlay() {
  const { state, slotBindings } = useIviStageView();
  return <div>{state.conversations.length} 条对话 | {slotBindings.length} 个绑定</div>;
}

useRuntimeState

function useRuntimeState(runtime: IviRuntimeCoordinator | null): IviRuntimeState;

订阅 runtime 状态变化。runtimenull 时返回 EMPTY_RUNTIME_STATE

useIviSubtitles

function useIviSubtitles(
  runtime: IviRuntimeCoordinator | null,
  options?: IviUseSubtitlesOptions
): IviSubtitleItem[];

基于 runtime 的 conversation / response 事件维护字幕队列,不负责渲染。字幕进入完成态后默认 3000ms 自动移除,可通过 hideCompletedAfterMs={false} 关闭,或通过 onSubtitleCompleted 自定义保留/删除策略。默认从 conversation 条目聚合字幕;当 roles 包含 "model" 且开启 useModelStreamingTranscript 时,model 字幕改为使用 response.output_audio_transcript.* 事件,response.output_audio_transcript.done 只更新文本,只有 response.done 才表示这一轮 model 字幕结束。

| Option | 类型 | 默认值 | 说明 | |--------|------|--------|------| | roles | IviSubtitleRole \| IviSubtitleRole[] | "user" | 要收集的发言人角色 | | maxItems | number | 2 | 最多保留的字幕条数,超过后清理最旧条目 | | useModelStreamingTranscript | boolean | false | 当 roles 包含 "model" 时,model 字幕是否改用 response.output_audio_transcript.* 事件 | | hideCompletedAfterMs | number \| false | 3000 | 字幕进入完成态后多久自动移除;传 false 保留到被 maxItems 裁剪 | | onSubtitleCompleted | (item, context) => "keep" \| "remove" \| { removeAfterMs } \| void \| Promise<...> | — | 自定义完成态清理策略,优先于 hideCompletedAfterMs |

IviSubtitleItem 包含 idrolelifecyclestatustexttranscriptdisplayTexttimestampupdatedAtcontentitemsourceresponseIditemId

function CustomSubtitles({ runtime }: { runtime: IviRuntimeCoordinator | null }) {
  const subtitles = useIviSubtitles(runtime, {
    roles: ["user", "model"],
    maxItems: 5,
    useModelStreamingTranscript: true,
    onSubtitleCompleted: (item) => (
      item.role === "model" ? { removeAfterMs: 4000 } : { removeAfterMs: 2500 }
    )
  });

  return (
    <div>
      {subtitles.map((item) => (
        <span key={item.id}>{item.displayText}</span>
      ))}
    </div>
  );
}

以下 hooks 为 IVITrackSlot 内部实现,当前不属于包根公开导出 API。

useVolumeMemory

function useVolumeMemory(mediaType: IviMediaVolumeType | null): [number, (v: number) => void];

按媒体类型("trtc" | "livekit" | "video" | "hls")在 localStorage 中持久化音量(0–100)。mediaTypenull 时不持久化。

useApplyVolumeToSlot

function useApplyVolumeToSlot(
  containerRef: RefObject<HTMLElement | null>,
  volume: number,
  enabled: boolean,
  activeSourceId?: string | null
): void;

将音量同步应用到容器内带 data-ivi-slot-role="active" 属性的 <video> / <audio> 元素,通过 MutationObserver 监听异步插入的媒体(如 TRTC / LiveKit 远端流)。


React 组件

当前公开导出的 React 组件包括 IVIStageViewIVITrackSlotIVITrtcPlayerIVILivekitPlayerIVISubtitleOverlay;当前公开导出的 React hooks 包括 useManagedIviRuntimeuseIviSessionRuntimeuseRuntimeStateuseIviStageViewuseIviSubtitles

IVIStageView

顶层舞台容器,负责消费 runtime 并通过 Context 注入状态。

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | children | ReactNode \| (ctx) => ReactNode | — | 子节点或 render-prop | | runtime | IviRuntimeCoordinator \| null | — | 要消费的 runtime,由业务方或 useManagedIviRuntime 提供 | | onBindingsChange | (bindings) => void | — | slot 绑定变化回调 | | onRuntimeEvent | (event) => void | — | runtime 事件回调 | | className / style | — | — | 容器样式 |

IVITrackSlot

按 slot 或 trackId 绑定,自动根据 source 类型渲染对应媒体播放器。

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | slot | string | — | 通过 slot 名称绑定(与 stage composition 对应) | | trackId | string | — | 直接绑定 trackId | | emptyFallback | ReactNode | — | 无 source 时的兜底内容 | | renderTrtc | (ctx: IviTrackSlotRenderContext) => ReactNode | — | 自定义 TRTC 渲染 | | renderLivekit | (ctx: IviTrackSlotRenderContext) => ReactNode | — | 自定义 LiveKit 远端流渲染 | | renderMedia | (ctx: IviTrackSlotRenderContext) => ReactNode | — | 自定义非 TRTC / LiveKit 媒体渲染 | | fitStrategy | "contain" \| "cover" \| "auto" | — | 媒体适配策略 | | adaptToSourceSize | boolean | — | 是否根据 source 尺寸自适应 | | showVolumeControl | boolean | — | 是否显示音量控制浮层 | | volumeControlProps | — | — | 音量控制自定义配置 | | showSubtitle | boolean | — | 是否显示字幕浮层 | | subtitleProps | Omit<IVISubtitleOverlayProps, "runtime"> | — | 字幕自定义配置,可传 rolesmaxItemsuseModelStreamingTranscripthideCompletedAfterMsonSubtitleCompleted 等 | | trtcPlayerProps | — | — | TRTC 播放器自定义配置 | | livekitPlayerProps | — | — | LiveKit 播放器自定义配置 | | videoProps / imageProps | — | — | 透传给原生 <video> / <img> | | className / style | — | — | 容器样式 |

IviTrackSlotRenderContext

interface IviTrackSlotRenderContext {
  slot: string;
  track: IviTrack;
  source: IviRuntimeSource;
  isPreloading?: boolean;
}

以下远端播放器可由 IVITrackSlot 默认使用,也可在业务侧按需直接使用。

IVITrtcPlayer

TRTC 远端流播放器,自动以 audience 身份入会并订阅远端流。

| Prop | 类型 | 说明 | |------|------|------| | trtc | IviSourcePlaybackTrtc | TRTC 拉流配置 | | sourceId | string | source ID | | runtime | IviRuntimeCoordinator | runtime 实例 | | loadingFallback | ReactNode | 加载中兜底 | | errorFallback | ReactNode | 错误兜底 | | muted | boolean | 是否静音 | | trtcAIDenoiser | boolean \| IviRuntimeTrtcAIDenoiserOptions | 独立使用 IVITrtcPlayer 时的 TRTC AIDenoiser 配置,默认开启 | | className / style | — | 容器样式 |

IVILivekitPlayer

LiveKit 远端音视频播放器,挂载后加入房间并订阅远端 tracks。ivi-frontend-sdk 只负责拉取和播放远端音视频流,不再发布用户麦克风;麦克风采集 / ASR 数据链路请使用 @vivix-ai/ivi-sdk-ts/transports/livekit-browser 或业务侧 transport。

| Prop | 类型 | 说明 | |------|------|------| | livekit | LiveKit playback 配置 | 包含 ws_urltoken,以及可选 room / identity | | sourceId | string | source ID | | runtime | IviRuntimeCoordinator | runtime 实例 | | loadingFallback | ReactNode | 加载中兜底 | | errorFallback | ReactNode | 错误兜底 | | muted | boolean | 是否静音 | | className / style | — | 容器样式 |

IVIHlsVideo(内部组件)

HLS 流播放器(基于 hls.js),由 IVITrackSlot 内部用于 .m3u8 地址播放;当前不从包根公开导出。

| Prop | 类型 | 说明 | |------|------|------| | url | string | HLS 播放地址 | | videoProps | — | 透传给 <video> | | style | — | 样式 | | aggressivePreload | boolean | 激进预加载模式 | | paused | boolean | 是否暂停 |

隐藏原生控件(包括进度条):SDK 未提供单独隐藏进度条的开关,HTML5 <video> 也不支持只隐藏 timeline。可通过 videoProps.controls = false 关闭整套原生控件,适用于 IVITrackSlot

<IVITrackSlot slot="main" videoProps={{ controls: false }} />

若需“保留播放/音量,仅隐藏进度条”,请在上层自行渲染自定义控制栏。

IVIFlvVideo

FLV 流播放器(基于 xgplayer + xgplayer-flv),用于 .flv 地址播放。IVITrackSlot 会按 URL 后缀自动选用。辅助函数 isFlvUrl(url) 可判断 URL 是否为 flv。

使用前需安装 peer 依赖:

npm install xgplayer xgplayer-flv

| Prop | 类型 | 说明 | |------|------|------| | url | string | FLV 播放地址 | | videoProps | — | 透传 muted / controls / onEnded 等语义 | | style | — | 容器样式 | | isLive | boolean | 是否直播,默认 true | | paused | boolean | 是否暂停(standby 预加载) |

直播 FLV 通常不会触发 ended,切换 source 时请使用定时模式而非「播放完毕」。

IVIVolumeControl

音量控制浮层组件,由 IVITrackSlotshowVolumeControl 开启时内部使用;当前不从包根公开导出。

| Prop | 类型 | 说明 | |------|------|------| | volume | number | 当前音量 0–100 | | onVolumeChange | (volume: number) => void | 音量变化回调 | | renderSpeakerIcon | (volume: number) => ReactNode | 自定义喇叭图标 | | colors | IVIVolumeControlColors | 颜色配置 | | className / style | — | 样式 |

IVISubtitleOverlay

预制字幕浮层组件,内部通过 useIviSubtitles 监听 runtime 的 conversation / response 事件并展示字幕。

| Prop | 类型 | 默认值 | 说明 | |------|------|--------|------| | runtime | IviRuntimeCoordinator \| null | — | 字幕来源 runtime | | roles | IviSubtitleRole \| IviSubtitleRole[] | "user" | 要展示的发言人角色 | | maxItems | number | 2 | 最多保留的字幕条数,超过后清理最旧条目 | | maxVisible | number | — | 已废弃,兼容旧字段;请使用 maxItems | | useModelStreamingTranscript | boolean | false | 当 roles 包含 "model" 时,model 字幕是否改用 response.output_audio_transcript.* 事件 | | hideCompletedAfterMs | number \| false | 3000 | 字幕进入完成态后多久自动移除;传 false 关闭自动移除 | | onSubtitleCompleted | (item, context) => "keep" \| "remove" \| { removeAfterMs } \| void \| Promise<...> | — | 自定义完成态清理策略,优先于 hideCompletedAfterMs | | subtitleStyle | IVISubtitleOverlayStyle | — | 样式配置 | | className / style | — | — | 样式 |


主动发送事件(IviClient 便捷方法)

IviClient 内置以下发送方法(自动补齐 event_id):

| 方法 | 说明 | |------|------| | sendSessionStageGet() | 请求当前 stage | | sendSessionTracksList() | 请求 track 列表 | | sendSessionSourcesList() | 请求 source 列表 | | sendSessionStreamsList() | 请求 stream 列表 | | sendSessionSourcePlaybackCompleted(sourceId, trackId?) | 上报 source 播放完成 | | sendConversationList() | 请求对话列表 | | sendConversationItemCreate(item) | 创建对话条目 | | sendUserText(text, itemId?) | 发送用户文本 | | sendResponseCreate(streamId?, response?) | 触发模型回复;streamId 可选,省略时使用 session 级配置 | | sendResponseCreateByItemId(itemId, streamId?, response?) | 基于条目 ID 触发模型回复 |


已解析事件范围

parseIviEvent 已支持并有测试覆盖:

  • session.createdsession.updatedsession.ended
  • session.stream.*session.streams.list.response
  • response.*
  • response.output_text.*
  • response.output_audio_transcript.*
  • session.stage.updatedsession.stage.get.response
  • session.track.*session.tracks.list.response
  • session.source.*session.sources.list.response
  • session.character.*session.characters.list.response
  • session.environment.*session.environments.list.response
  • session.object.*session.objects.list.response
  • conversation.list.response
  • conversation.item.addedconversation.item.done
  • error

未识别类型会降级为 UnknownIviEvent,避免解析阶段直接中断。

语义约束

  • session:一次 IVI 体验的最外层作用域;
  • stage:展示编排层,维护 slot → track
  • track:展示播控通道,维护 active/next source 引用;
  • source:可被 track 播放的媒体资产(含 playback 信息);
  • stream:实时推理通道,属于生成层概念,不等价于展示层 track。

当前已知边界

  • 底层 IviClient 默认启用自动重连,并支持发送时等待连接恢复;但当前不内置应用层心跳;
  • 不内置事件去重缓存;
  • 不提供 Web Component 入口;
  • 对外以 TypeScript/React API 为主。

本地开发

npm install
npm run typecheck
npm run build
npm run test

版本提升与打 tag

仓库提供版本提升脚本,执行前要求工作区干净:

npm run release:version -- 0.3.0

脚本会同步更新 package.json / package-lock.json,提交:

chore: update package version to 0.3.0

并创建 tag:v0.3.0