安装
npm install @boldvideo/bold-js
快速启动
import { createClient } from '@boldvideo/bold-js';
const bold = createClient('your-api-key');
// Fetch videos
const videos = await bold.videos.list();
// AI-powered recommendations
const recs = await bold.ai.recommendations({
topics: ['sales', 'negotiation'],
stream: false
});
console.log(recs.guidance);
API 参考
视频
// List latest videos (default: 12)
const videos = await bold.videos.list();
// With limit (backwards compatible)
const videos = await bold.videos.list(20);
// With filters
const videos = await bold.videos.list({
limit: 20,
tag: 'sales',
collectionId: 'col_123',
viewerId: 'viewer_123' // Include watch progress
});
// Paginated index (uses /videos endpoint)
const videos = await bold.videos.list({
page: 2,
tag: 'sales',
collectionId: 'col_123'
});
// Get a single video by ID or slug
const video = await bold.videos.get('video-id');
const videoBySlug = await bold.videos.get('my-video-slug');
// Search videos
const results = await bold.videos.search('pricing strategies');
安全播放
视频 playbackPolicy: 'signed' 需要一个播放符号。 赤马流
和图像 URL 为这些视频返回 403 。 使用独立的助手代替
手建 Mux URL 时:
import { createClient, streamUrl, muxPlayerProps, thumbnailUrl } from '@boldvideo/bold-js';
const bold = createClient('YOUR_API_KEY'); // Keep your tenant API key server-side.
const { data: video } = await bold.videos.get('video-id');
const src = streamUrl(video); // For an HLS player.
const playerProps = muxPlayerProps(video); // Spread into <MuxPlayer {...playerProps} />.
const poster = thumbnailUrl(video, { width: 640, height: 360, time: 0, fitMode: 'crop' });
streamUrl 保存 API 的 URL, 返回 Mux HLS URL, 并附带任何已提供的内容
playbackToken。 。 。 . muxPlayerProps 返回刚刚 { playbackId } 公共录像;
已签名的视频还可获得回放/音标和明确的海报
从 thumbnail。 。 。 . thumbnailUrl 为公共视频构建 Mux 图像 URL 。 签名
视频返回存储的公众 thumbnail 不变 忽略所有选项数字 :
无法使用查询参数大小或查找已签名的 Mux 图像 .
帮助者接受 Video、引用、搜索来源、建议或自己的
带有所需骆驼Case字段的对象(PlaybackSource(a) 载有下列资料的大赦国际消息来源:
即使没有政策,标志也被视为签名;明确的公共政策需要
玩家道具和缩略图优先。 URL 帮助返回 undefined 当
缺少所需的 URL、 播放 ID 或签名缩略图; 玩家道具省略
字段。 它们的格式是提供的数据,而不是验证证书:没有签名的视频
符号不能通过构建的赤网址播放。 对于一个没有缩略图或
故事板符号,重排完整视频以获取可用的字段.
最后一个 12小时。 。 。 . Video.playbackTokenExpiresAt 是可选的无效
ISO8601 倍贴. 重新调试视频 bold.videos.get() 以新的迹象发誓;
没有刷新端点或自动刷新定时器。 脚本字段无效
公共视频。 不登录标志或标志化的 URL .
播放列表
// List all playlists
const playlists = await bold.playlists.list();
// Get a single playlist with videos
const playlist = await bold.playlists.get('playlist-id');
设置
// Fetch channel settings, menus, and featured playlists
const settings = await bold.settings();
// Access portal hero configuration
if (settings.portal.hero.type === 'custom') {
// Render custom hero section
}
// Menu items with external link handling
settings.menuItems.forEach(item => {
// item.blank: true opens in a new window/tab
// item.isExt: true indicates an external URL
// item.icon: optional icon path (can be null)
});
查看器 API
管理外部用户并跟踪其视频监视进度. 理想的课程平台 整合与粗体视频.
查看器管理
// Create a viewer (e.g., when user signs up)
const { data: viewer } = await bold.viewers.create({
name: 'John Doe',
externalId: 'user_123', // Your platform's user ID
email: '[email protected]',
traits: { plan: 'pro', company_name: 'Acme Inc' }
});
// Find viewer by external ID (common for syncing users)
const { data: viewer } = await bold.viewers.lookup({ externalId: 'user_123' });
// Or find by email
const { data: viewer } = await bold.viewers.lookup({ email: '[email protected]' });
// Update viewer
await bold.viewers.update(viewer.id, {
traits: { plan: 'enterprise' } // Note: traits are replaced, not merged
});
// List all viewers
const { data: viewers } = await bold.viewers.list();
进展跟踪
// Save progress as video plays (call every 5-10 seconds)
await bold.viewers.saveProgress(viewerId, videoId, {
currentTime: 120, // seconds
duration: 600 // total video duration
});
// Mark video complete by setting currentTime = duration
await bold.viewers.saveProgress(viewerId, videoId, {
currentTime: 600,
duration: 600
});
// Get progress for a specific video
const { data: progress } = await bold.viewers.getProgress(viewerId, videoId);
console.log(`${progress.percentage}% complete`);
// List all progress for a viewer (e.g., for a course dashboard)
const { data: progress, meta } = await bold.viewers.listProgress(viewerId, {
collectionId: 'course-collection-id', // Filter to a course
completed: false // Only in-progress videos
});
console.log(`Completed ${meta.completed} of ${meta.total} videos`);
通知首选项
阅读或部分更新查看器的电子邮件并按下通知频道 租户 API 键客户端 :
const preferences = await bold.notifications.getPreferences('viewer-uuid');
console.log(preferences.channels.email, preferences.channels.push);
// Only push is changed; the existing email preference is preserved.
await bold.notifications.updatePreferences('viewer-uuid', {
channels: { push: true }
});
会话管理
使用 BOLD 会话管理作为无头密码共享预防层. 您的应用程序仍然拥有登录,会员,产品访问,以及员工/管理人员检查. 只有在您的应用决定了查看者可以访问保护后才能拨打 BOLD 内容.
运行时会话
运行时会话呼叫使用客户的上游JWT,而不是BOLD API键. 这个 当上游JWT已经存在时,工厂对于浏览器/本地运行时间代码是安全的 该运行时间.
import { createAuthClient } from '@boldvideo/bold-js';
const auth = createAuthClient({
tenantSlug: 'hrtu',
upstreamJwt: outsetaJwt
});
const result = await auth.sessions.create({
deviceId: browserDeviceId,
platform: 'web',
userAgent: navigator.userAgent
});
let sessionId: string | undefined;
if ('challengeRequired' in result) {
// Show an email-code challenge UI, then verify the code.
const verified = await auth.challenges.verify(result.challengeId, '123456');
sessionId = verified.sessionId ?? undefined;
} else if (result.sessionId) {
sessionId = result.sessionId;
}
if (sessionId) {
localStorage.setItem('bold_session_id', sessionId);
}
if (sessionId) {
// Register the session for push notifications.
await auth.notifications.registerDevice(sessionId, {
provider: 'expo', // 'expo' | 'fcm' | 'apns'
token: expoPushToken
});
const verification = await auth.sessions.verify(sessionId);
if (!verification.valid) {
// session_not_found, session_revoked, or session_expired
}
}
对于刷新过的JWT,通过每个调用:
await auth.sessions.verify(sessionId, { upstreamJwt: freshJwt });
观众自我管理方法需要政策支持:
const { data: sessions } = await auth.sessions.list();
await auth.sessions.revoke(sessionId);
await auth.sessions.revokeOthers(currentSessionId);
当账户没有会话管理时, 设备注册 返回已输入错误 :
import { SessionManagementUnavailableError } from '@boldvideo/bold-js';
try {
await auth.notifications.registerDevice(sessionId, {
provider: 'fcm',
token: fcmToken
});
} catch (error) {
if (error instanceof SessionManagementUnavailableError) {
console.log(error.status, error.code, error.retryable);
}
}
服务器系统会话管理
客户服务器可以检查 BOLD 设备状态,撤销会话,并管理 每查看器设备限制 超标或豁免 上游外部ID使用 普通 BOLD 租户 API 密钥。 保留此 API 密钥服务器侧 。 别放 浏览器代码中的 BOLD 租户密钥或管理密钥 .
import { createClient } from '@boldvideo/bold-js';
const bold = createClient(process.env.BOLD_TENANT_API_KEY!);
const { data: state } =
await bold.sessionManagement.getViewerSessionManagementStateByExternalId(
outsetaPersonUid
);
if (state.canUpdateDeviceLimitOverride) {
await bold.sessionManagement.setViewerDeviceLimitOverrideByExternalId(
outsetaPersonUid,
5
);
await bold.sessionManagement.clearViewerDeviceLimitOverrideByExternalId(
outsetaPersonUid
);
}
if (state.canUpdateSessionManagementExemption) {
await bold.sessionManagement.setViewerSessionManagementExemptionByExternalId(
outsetaPersonUid,
true
);
}
const { data: sessions } =
await bold.sessionManagement.listViewerSessionsByExternalId(outsetaPersonUid);
for (const session of sessions) {
if (session.travelVerdict === 'impossible_travel') {
// Bold flagged this sign-in as physically implausible.
}
}
await bold.sessionManagement.revokeAllViewerSessionsByExternalId(
outsetaPersonUid,
{ reason: 'outseta:membership_changed' }
);
setViewerDeviceLimitOverrideByExternalId 需要一个正的安全整数.
使用 clearViewerDeviceLimitOverrideByExternalId 明确清除.
每届会议的项目都包含 travelVerdict: 'impossible_travel' | null - 粗体货
权威的不可能旅行信号,由这些管理员范围显示
届会管理方法。 这是一个 仅标签: SDK 永远不会返回
坐标,生IP,或导出距离/速度. 没错 null 当
签入时没有标出标记(以及字段前的后端).
服务器侧会话管理方法无法更新 JWT 配置, 账户 特性标记、会话 TTL 或电子邮件模板设置。 这些仍然是BOLD操作员 控制系统.
社区 API
以文章、评论和反应建立社区特色。 所有写操作都需要一个 viewerId (表演动作的观众).
员额
// List posts (optionally filter by category)
const { data: posts } = await bold.community.posts.list({
category: 'announcements',
limit: 20,
offset: 0,
viewerId: 'viewer-uuid' // Include viewerReacted in response
});
// Get a single post with comments
const { data: post } = await bold.community.posts.get('post-id', 'viewer-uuid');
// Create a post and notify viewers by their customer external IDs.
const { data: newPost, mentions } = await bold.community.posts.create('viewer-uuid', {
content: 'Hello community! **Markdown** supported.',
category: 'general',
mentions: ['customer-user-123']
});
console.log(mentions?.skipped); // malformed or unresolved external IDs
// Update a post (owner or admin only)
await bold.community.posts.update('viewer-uuid', 'post-id', {
content: 'Updated content'
});
// Delete a post (owner or admin only)
await bold.community.posts.delete('viewer-uuid', 'post-id');
// Pin or unpin a post (server only). Use a community moderation key: a
// single-purpose key that can pin and unpin posts and nothing else, so it is
// safe to keep in your server's environment. Ask Bold support to issue one.
// Check that the acting member may moderate first; authorship is not enough.
const admin = createClient(process.env.BOLD_API_KEY, {
adminApiKey: process.env.BOLD_COMMUNITY_MODERATION_KEY,
});
const { data: pinned } = await admin.community.posts.setPinned('post-id', true);
console.log(pinned.pinned, pinned.pinnedAt); // re-pinning keeps the original pinnedAt
await admin.community.posts.setPinned('post-id', false);
// React to a post (toggle like/unlike)
const reaction = await bold.community.posts.react('viewer-uuid', 'post-id');
console.log(reaction.reacted, reaction.reactionsCount);
评论
// Create a comment on a post
const { data: comment } = await bold.community.comments.create(
'viewer-uuid',
'post-id',
{ content: 'Great post!', mentions: ['customer-user-456'] }
);
// Reply to a comment (nested)
const { data: reply } = await bold.community.comments.create(
'viewer-uuid',
'post-id',
{ content: 'I agree!', parentId: 'parent-comment-id' }
);
// Delete a comment (owner or admin only)
await bold.community.comments.delete('viewer-uuid', 'comment-id');
// React to a comment (toggle)
const reaction = await bold.community.comments.react('viewer-uuid', 'comment-id');
// Read nested reaction state on comments/replies (viewerId enables viewerHasReacted)
const { data: post } = await bold.community.posts.get('post-id', 'viewer-uuid');
for (const thread of post.comments.items ?? []) {
console.log(thread.createdAt, thread.reactions.count, thread.reactions.viewerHasReacted);
}
引用收件箱
const { data: mentionItems, meta } = await bold.community.mentions.list(
'viewer-uuid',
{ page: 1, pageSize: 20 }
);
const { data: unread } =
await bold.community.mentions.unreadCount('viewer-uuid');
await bold.community.mentions.markRead('viewer-uuid', {
ids: mentionItems.map((mention) => mention.id)
});
// Or mark every unread mention as read.
await bold.community.mentions.markRead('viewer-uuid', { all: true });
可选 mentions 只有在创建员额/评论时才返回结果
请求包括: mentions 阵列。 提供最多25个粗体工艺
外部 ID ; 格式不正确、 未解决、 或顶级以外的 ID 出现在 skipped, (中文(简体) ),
重复和自我记忆则默默地被省略。 从未跳过的身份
失败内容创建 .
提及 excerpt 价值观是简单、无卫生的内容。 逃脱或消毒
它们在渲染为 HTML 之前.
AI 方法
聊天、搜索和建议都支持流(默认)和非流模式.
语音会议
在不管理WebRTC,麦克风音轨或字幕片段的情况下,与单一视频交谈.
语音需要账户的AI和语音功能,视频记录以及浏览器
在安全上下文(HTTPS,或用于开发的本地主机)中带有麦克风许可.
工厂在服务器渲染过程中同步且安全可调用;仅限 start()
使用浏览器 API. 不需要框架或额外的依赖.
测试准确公布的 npm 释放的操作员专用页面,请参见 语音接收装置。 。 。 . 其记录的合成检查不能确定真正的提供者或Safari/iPhone的接受.
使用账户设置来决定是否显示语音控制 。 失踪或失实 值保持语音隐藏;当会话开始时,经纪人仍然强制访问.
const settings = await bold.settings();
const voiceEnabled = settings.data.account.voice?.enabled === true;
const session = bold.ai.voice.createSession({
videoId: 'lesson-slug',
viewer: 'customer-123', // Optional viewer UUID or external ID
onStatus: (status) => { statusLabel.textContent = status; },
onCaptions: (turns) => {
// Each turn has a stable id, speaker ('user' or 'assistant'), text,
// startedAt/updatedAt in milliseconds, and seekable timestamp segments.
captions.textContent = turns.map(turn => `${turn.speaker}: ${turn.text}`).join('\n');
},
onEnded: (reason) => console.log('Voice ended:', reason),
onError: (error) => { errorLabel.textContent = error.message; },
});
// Start directly from a user gesture. Handle rejection even when using onError.
startButton.onclick = () => { void session.start().catch(() => {}); };
muteButton.onclick = () => session.setMuted(!session.muted);
endButton.onclick = () => { void session.end(); };
// Optional animation-frame polling: normalized RMS amplitudes, not volume settings.
const { input, output } = session.getAudioLevels();
// In your component unmount / video replacement callback, call session.dispose().
createSession(options) 返回 a 单用途 VoiceSession。创建新手柄
重新连接。 经纪人的请求发生在里面 start(),在麦克风许可后
和社民党谈判;来电者不供应社民党. 可选 viewerProfile 接受一个
字符串或字符串、数字和布尔值的对象,不改变而转发
它的钥匙。 查看器配置需要账户的查看器特性.
- 只读状态 :
status, (中文(简体) ),sessionId(直到经纪人回复为止),muted。 。 。 . - 状态 :
idle → connecting → live → ending → ended; 远程关闭可能去 直接转到ended。 。 。 . start(): Promise<void>解析何时存在,或何时取消end()编号 :dispose()。 。 。 . 检查session.status如果后续代码需要实时连接。 设置失败 拒绝和呼叫onError。并行开始分享一次尝试.setMuted(boolean)本地禁用麦克风音轨, 包括启动前的设定 .setPlaybackState({ playing, currentTime })哑声麦克风和辅助音频 视频播放时 恢复观众的麦克风哑弹选择, 与助理分享视频位置。 调用游戏,暂停,结束,和 寻求事件(不是每次时间更新).currentTime必须是有限、非负性的 秒; 上下文以整秒精度被去除。 启动前更新为 立即应用到新媒体上;只有最新的上下文在直播时发送. 结束后的电话被忽略 。 此方法不操作您的视频播放器 .end(): Promise<void>立即停止麦克风/音频,最多三秒钟 以接近确认。 它是一能的;完成并不确认最后账单.dispose()默默地立即释放出一切,包括在设置或结束期间. SDK也处理上pagehide;它无法检测框架卸载.- 最终原因 :
user, (中文(简体) ),idle, (中文(简体) ),time, (中文(简体) ),server, (中文(简体) ),connection, (中文(简体) ),error。 。 。 .
连接设置有60秒的最后期限 经麦克风许可后; 取消 并及时解决未解决的许可问题,并停止任何给予的麦克风音轨 等会 对付费的会话创建没有自动重复。 SDK执行 服务器在页面运行时的最大持续时间和闲置限制。 可听到的投入/产出或 笔录 dertas 和视频回放计数为活动; 输入的无声音频帧不计数 . 暂停视频重新启动闲置窗口; 回放从未延长最长持续时间 。 浏览器计时器 可以被节制或中止,因此后端限制仍然对计费具有权威性.
经纪人的失败是 VoiceAPIError 可选实例 status, (中文(简体) ), code, (中文(简体) ), retryable, (中文(简体) ),
retryAfter (raw HTTP 头条), resetsOn,以及 originalError。这包括
voice_allowance_exceeded (402), (中文(简体) ), voice_session_limit (409个)和 rate_limited (429) (中文(简体) ).
麦克风、回放和WebRTC故障是普通的 Error 实例。 提供者错误
事件调用 onError 但不要自动结束一个活的会话.
说明和寻找按钮. 1500米以内同一发言者产生的碎片
同一回合,包括发言者重叠时. turn.segments 包含
{ type: 'text', text } 财务报告和审定财务报表 { type: 'timestamp', text, seconds } 条目。 显示 a
时间戳作为按钮,其点击设置您的视频播放器 currentTime 改为 seconds。 。 。 .
两者 m:ss 财务报告和审定财务报表 h:mm:ss 获得支持,包括: 0:00。说明文本不可信;
使用文本节点或您框架的逃避文本渲染, 而不是 innerHTML。 。 。 .
纯洁的助手 appendVoiceCaption(turns, speaker, delta, now) 财务报告和审定财务报表
splitVoiceTimestamps(text) 也被导出。 SDK不控制你的视频
玩家或注入特定应用程序的问候.
如果提供者要求代表团,SDK用固定的英语回复
只有录音记录: " 只有这种录像记录。 从它的回答,
或者说视频没有覆盖它。” 此协议响应也用非英语发送
届会; VoiceSessionOptions 不显示语言或指令覆盖.
反应清净. 保持手柄在回放, 创建它的效果,从 一个点击处理器。 为每个挂载/视频创建新手柄( 包括严格模式) 效果重放 ; 不要以效果开始麦克风捕获 .
const voiceRef = useRef<VoiceSession | null>(null);
useEffect(() => {
const session = bold.ai.voice.createSession({
videoId,
onStatus: setStatus,
onCaptions: setCaptions, // useState<readonly VoiceCaptionTurn[]>([])
onError: (error) => setError(error.message),
});
voiceRef.current = session;
return () => {
session.dispose();
voiceRef.current = null;
};
}, [bold, videoId]); // Keep the bold client stable across renders.
// In a button handler:
const startVoice = () => { void voiceRef.current?.start().catch(() => {}); };
导入 VoiceSession 财务报告和审定财务报表 VoiceCaptionTurn 作为类型从 @boldvideo/bold-js,以及
反应钩从 react。根据需要,将显示的状态/标题重置到视频更改上.
聊天
整个图书馆的谈话AI 深入的QQA 在你的整个视频库.
// Streaming (default)
const stream = await bold.ai.chat({ prompt: 'How do I price my SaaS?' });
for await (const event of stream) {
if (event.type === 'text_delta') process.stdout.write(event.delta);
if (event.type === 'sources') console.log('Sources:', event.sources);
}
// Non-streaming
const response = await bold.ai.chat({
prompt: 'What are the best closing techniques?',
stream: false
});
console.log(response.content);
选项 :
| 参数 | 类型 | 说明 |
|---|---|---|
prompt | string | 用户的问题( 需要) |
stream | boolean | true (默认)SSE, false 给 JSON 的 |
videoId | string | 如果提供的话, 视频的范围 而不是整个库 |
currentTime | number | 当前播放位置( 只使用 videoId(中文(简体) ) |
conversationId | string | 继续现有对话的通行证 |
collectionId | string | 过滤到特定收藏 |
tags | string[] | 通过标签过滤 |
建议
获得基于话题的AI动力视频建议——理想的个性化学习路径,考试准备,以及内容发现.
// Streaming (default)
const stream = await bold.ai.recommendations({
topics: ['contract law', 'ethics', 'client management'],
});
for await (const event of stream) {
if (event.type === 'recommendations') {
event.recommendations.forEach(rec => {
console.log(`${rec.topic}:`);
rec.videos.forEach(v => console.log(` - ${v.title} (${v.relevance})`));
});
}
if (event.type === 'text_delta') {
process.stdout.write(event.delta); // AI guidance
}
}
// Non-streaming
const response = await bold.ai.recommendations({
topics: ['sales', 'marketing'],
stream: false
});
console.log(response.guidance);
console.log(response.recommendations);
选项 :
| 参数 | 类型 | 说明 |
|---|---|---|
topics | string[] | 寻找(需要)内容的主题 |
stream | boolean | true (默认)SSE, false 给 JSON 的 |
limit | number | 每个主题的最大视频( 默认: 5, 最大: 20) |
collectionId | string | 过滤到特定收藏 |
tags | string[] | 通过标签过滤 |
includeGuidance | boolean | 包含 AI 学习路径描述( 默认: 真) |
context | AIContextMessage[] | 先前的对话开始后续 |
搜索
以AI生成的简短摘要快速语义搜索.
const stream = await bold.ai.search({
prompt: 'pricing strategies',
limit: 10
});
for await (const event of stream) {
if (event.type === 'sources') {
console.log(`Found ${event.sources.length} results`);
}
}
AI 交互跟踪
chat (全图书馆或录像机), ask, (中文(简体) ), coach,以及 search 接受一个
选项 requestId UUID, 发送为 request_id。每次故意生成一次
用户动作并在重试此动作时重新使用。 新问题、后续行动或
故意重复搜索获得一个新的UUID. SDK 不生成ID或重试
自动请求.
search 还接受 searchMode: 'preview' | 'settled'编号 search_mode。 。 。 .
关闭它保存服务器的 settled 默认。 使用 preview 临时
搜索和 settled 最终提交的搜索文件; 处理最后提交的文件
以它自己的UUID作为新的行动.
const action = {
prompt: 'pricing strategies',
requestId: crypto.randomUUID(),
searchMode: 'settled' as const,
stream: false as const,
};
const response = await bold.ai.search(action);
// If retrying this action, pass the same action.requestId.
console.log(response.interactionId);
贾森 AIResponse 和SSE 组合键 message_complete 事件曝光
interactionId?: string | null (自 interaction_id) (中文(简体) ). 搜索预览
可能返回 null; 旧服务器可能会省略. 这些ID支持 分析
仅复制,而不是回放或模式调用分解。 持久性
是最佳和同步的: 接收ID是 不是一个持久的承认。 。 。 .
这不会改变 videos.search(),该调用 /videos?query=…; SDK 会的
不覆盖单独的关键字 /search 终点.
聊天,视频聊天, ask编号 :coach,而AI搜索也接受可选归属:
channel: 'portal' | 'embed' | 'api' | 'mcp' | 'unknown', (中文(简体) ), clientName: string,以及
clientVersion: string。这些是作为顶级发送的 channel, (中文(简体) ), client_name,以及
client_version字段中省略;后端正常丢失或
频道值为错误 unknown。归属不影响 requestId
相去相减. 设定 channel: 'portal' 明确在端口服务器代理; 这些
标签是分析元数据,而不是授权信号.
视频扫描聊天
通过传递来谈论一个特定的视频 videoId只使用视频的笔录作为上下文.
const stream = await bold.ai.chat({
videoId: 'video-id',
prompt: 'What is discussed at the 5 minute mark?'
});
for await (const event of stream) {
if (event.type === 'text_delta') process.stdout.write(event.delta);
}
// With playback context (coming soon)
const stream = await bold.ai.chat({
videoId: 'video-id',
prompt: 'What does she mean by that?',
currentTime: 847 // seconds
});
获取对话历史
通过 ID 获取对话以显示消息历史 :
const conversation = await bold.ai.getConversation('550e8400-e29b-41d4-a716-446655440000');
console.log(`Created: ${conversation.createdAt}`);
for (const msg of conversation.messages) {
console.log(`${msg.role}: ${msg.content}`);
}
答题率
使用可选 messageId 从非流出 AIResponse 或流线
message_complete 用于评分助手回答的事件 :
const answer = await bold.ai.chat({ prompt: 'How do I get started?', stream: false });
if (answer.messageId) {
const feedback = await bold.ai.rateMessage(answer.messageId, {
rating: 'down',
reasons: ['too_short'],
note: 'Please include an example.',
});
await bold.ai.clearRating(feedback.messageId);
}
rating 实值 'up' 或者说 'down'可选 reasons 接受 'wrong', (中文(简体) ),
'not_in_video', (中文(简体) ), 'too_short', (中文(简体) ), 'off_topic',以及 'other'· ; note 是可选的.
rateMessage 返回时 MessageFeedback 与 messageId, (中文(简体) ), rating, (中文(简体) ), reasons, (中文(简体) ),
和无效 note。 。 。 . clearRating 解析无值。 都抛出
AIAPIError 和404状态,当消息不在呼叫者的账户上.
多回合对话
使用 context 后续问题参数:
const first = await bold.ai.search({
prompt: 'How do indie designers find clients?',
stream: false
});
const followUp = await bold.ai.search({
prompt: 'What about cold outreach specifically?',
context: first.context,
stream: false
});
对话启动器
获取对话启动提示, 可用时按收藏解决 返回账户一级启动人,否则:
// Account-level starters (no collectionIds)
const { data: starters } = await bold.conversationStarters.list();
// Resolve starters for one or more collections
const { data: starters } = await bold.conversationStarters.list({
collectionIds: ['collection-uuid-1', 'collection-uuid-2']
});
starters.forEach((starter) => {
console.log(starter.text, starter.source, starter.collectionId);
});
分析
明确AI 约定(浏览器或服务器)
bold.trackEngagement(options: TrackEngagementOptions): Promise<void> 发送
POST /api/v1/event 独立于遗产 trackEvent 和它的油门.
导入 TrackEngagementOptions 从 @boldvideo/bold-js。 。 。 .
| SDK 字段 | 电线场 | 含义 |
|---|---|---|
event | n | source_open 或者说 video_progress |
interactionId | interaction_id | AI 响应中所需的交互 UUID |
openId | playback_id | 每个有意打开的源头都需要新的UUID; 不是一个 Mux 播放回放Id |
videoId | vid | 所需的源视频 ID |
viewer? | viewer | 现有查看器 UUID 或外部 ID |
watchedSeconds | watched_seconds | 所需经费 video_progress,缺席 source_open; 累计过期 为此打开播放时长秒 |
// On a deliberate source open, skip if the response has no interaction ID.
if (response.interactionId) {
const open = {
interactionId: response.interactionId,
openId: crypto.randomUUID(),
videoId: source.videoId,
};
void bold.trackEngagement({ ...open, event: 'source_open' });
// Later, after the caller has measured 12.5 seconds spent playing:
void bold.trackEngagement({ ...open, event: 'video_progress', watchedSeconds: 12.5 });
}
source_open 只建立开放; 进步需要开放。 维护
计数器,不包括暂停/缓冲时间。 不发送绝对
玩家头姿势 寻找三角洲 或每个样本增量 重排/重排
进度使用相同的 openId;后端保留了累积的最大. 生成
新鲜的 openId 当用户故意再次打开源时.
呼叫是最佳的 并且安全 开火和忘记: 运输错误被抓住, 并且不使用自动重试或共享节流阀。 一项坚定的承诺(包括 HTTP 202)为 不是一个持久的承认后端会验证房客,观众, 和源属性;它重复捕获/公开比赛最多30秒 不创建占位符交互。 服务器代理必须获得 API 密钥和 来自信任的租户/会话背景的浏览器引用,而不是浏览器的主张.
遗留的播放和页面浏览
跟踪视频事件和页面浏览分析.
// Track video events (play, pause, complete, etc.)
bold.trackEvent({
type: 'play',
videoId: 'video-id',
timestamp: 0
});
// Track page views
bold.trackPageView({
path: '/videos/my-video',
referrer: document.referrer
});
类型脚本
所有类型都导出为全类型脚本支持 :
import type {
Video,
Playlist,
Settings,
Portal,
PortalHero,
MenuItem,
AIEvent,
AIResponse,
ChatOptions,
SearchOptions,
RecommendationsOptions,
RecommendationsResponse,
Recommendation,
Conversation,
ConversationMessage,
Source,
Viewer,
ViewerProgress,
ViewerLookupParams,
ListProgressOptions,
ListVideosOptions,
ListVideosLatestOptions,
ListVideosIndexOptions,
NotificationChannels,
NotificationPreferencesResponse,
NotificationChannelUpdate,
UpdateNotificationPreferencesData,
NotificationProvider,
RegisterDeviceData,
RegisterDeviceResponse,
// Community API
Post,
PostAuthor,
Comment,
ReactionResponse,
Mention,
MentionSkippedReport,
CommunityPostCreateResponse,
CommunityCommentCreateResponse,
ListPostsOptions,
ListMentionsOptions,
MentionsUnreadCountResponse,
MarkMentionsReadData,
MarkMentionsReadResponse,
CreatePostData,
UpdatePostData,
CreateCommentData,
// Conversation Starters
ConversationStarter,
ConversationStarterSource,
ListConversationStartersOptions
} from '@boldvideo/bold-js';
从v1.7.x移徙
断开: 现在所有响应类型都使用骆驼 Case
所有API响应 (videos, playlist, setters, AI)现在被转换为使用平庸的TypeScript/JavaScript命名:
// Before (v1.7.x and earlier)
video.playback_id
video.published_at
video.stream_url
video.meta_data
settings.featured_playlists
settings.menu_items
settings.theme_config
playlist.is_private
// After (v1.8.0)
video.playbackId
video.publishedAt
video.streamUrl
video.metaData
settings.featuredPlaylists
settings.menuItems
settings.themeConfig
playlist.isPrivate
方法变化
| 旧 | 新设 | 页:1 |
|---|---|---|
bold.ai.ask(opts) | bold.ai.chat(opts) | ask() 还在工作,但已经贬值 |
bold.ai.coach(opts) | bold.ai.chat(opts) | coach() 还在工作,但已经贬值 |
bold.ai.chat(videoId, opts) | bold.ai.chat({ videoId, ...opts }) | 过 videoId 选项中 |
bold.ai.recommend(opts) | bold.ai.recommendations(opts) | recommend() 还在工作,但已经贬值 |
类型重命名
| 旧类型 | 新类型 |
|---|---|
AskOptions | ChatOptions |
RecommendOptions | RecommendationsOptions |
RecommendResponse | RecommendationsResponse |
旧类型仍然作为后向相容性的别名被导出.
相关链接
捐款
见 捐款额 详细介绍如何为该项目作出贡献.
警卫
见 警卫 安全政策和报告脆弱性.
许可证
麻省理工学院