@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-sdkPeer 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_id、room_id、user_id、user_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 Sessionsdk.runtime.create/sdk.runtime.start/sdk.runtime.stopsdk.client.connect/sdk.client.connection:底层@vivix-ai/ivi-sdk-tsWebSocket 连接与聚合消息计数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-ts 的 IviClientConfig,需要直接传入通用 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 的连接规则:
- 使用
clientConfig.transport - 若
clientConfig.transport为空,则不创建 runtime - 浏览器 LiveKit 场景可传
LiveKitBrowserTransport - WebSocket、测试 fake transport 或其它 transport 也通过同一个
transport字段传入
方式 C:使用 useIviSessionRuntime 创建会话并管理 runtime
当希望把 createIVISession 也放进 SDK 接入流程时,可以使用 useIviSessionRuntime。它会在挂载后创建 IVI Session、构造 WebSocket runtime,并在 autoStart 为 true 时自动启动。
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的创建autoStart为true时自动调用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 状态变化。runtime 为 null 时返回 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 包含 id、role、lifecycle、status、text、transcript、displayText、timestamp、updatedAt、content、item、source、responseId、itemId。
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)。mediaType 为 null 时不持久化。
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 组件包括 IVIStageView、IVITrackSlot、IVITrtcPlayer、IVILivekitPlayer 与 IVISubtitleOverlay;当前公开导出的 React hooks 包括 useManagedIviRuntime、useIviSessionRuntime、useRuntimeState、useIviStageView 与 useIviSubtitles。
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"> | — | 字幕自定义配置,可传 roles、maxItems、useModelStreamingTranscript、hideCompletedAfterMs、onSubtitleCompleted 等 |
| 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_url、token,以及可选 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
音量控制浮层组件,由 IVITrackSlot 在 showVolumeControl 开启时内部使用;当前不从包根公开导出。
| 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.created、session.updated、session.endedsession.stream.*、session.streams.list.responseresponse.*response.output_text.*response.output_audio_transcript.*session.stage.updated、session.stage.get.responsesession.track.*、session.tracks.list.responsesession.source.*、session.sources.list.responsesession.character.*、session.characters.list.responsesession.environment.*、session.environments.list.responsesession.object.*、session.objects.list.responseconversation.list.responseconversation.item.added、conversation.item.doneerror
未识别类型会降级为 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。
