Documentation Index
Fetch the complete documentation index at: https://docs.sf-voice.sh/llms.txt
Use this file to discover all available pages before exploring further.
@sf-voice/media SDK 为 TypeScript 应用封装了 sf-voice media API。
使用它可以提交媒体资产、等待索引,并使用你自己的面向客户的资产 ID 在视频、
音频和转录文本维度中进行搜索。
摄取资产
通过 URL、S3 键或浏览器/文件缓冲区提交媒体。
保留你的 ID
传入你自己的 asset_id,以便结果能映射回你的系统。
限定搜索范围
使用 asset_class 或 asset_ids,确保搜索绑定到正确的客户或分组。
SDK 仅暴露 sf-voice 的术语。与具体提供方相关的映射在后端进行。
第一次使用 sf-voice media?
在使用 SDK 参考之前,先从产品概览开始。
pnpm add @sf-voice/media@latest
创建客户端
import { SfVoiceMedia } from "@sf-voice/media";
const client = new SfVoiceMedia({
baseUrl: "https://api.sf-voice.com",
apiKey: process.env.SF_VOICE_API_KEY!,
timeoutMs: 30_000,
});
| 选项 | 必填 | 说明 |
|---|
baseUrl | 是 | sf-voice media API 的基础 URL。 |
apiKey | 是 | 以 X-API-Key 请求头发送的 API 密钥。 |
timeoutMs | 否 | 每次请求的超时时间(毫秒)。默认为 30_000。 |
核心字段
| 字段 | 说明 |
|---|
asset_id | 你为媒体资产指定的唯一 ID。摄取时必填。 |
asset_class | 可选的逻辑分组,例如客户、工作区、项目或代码仓库。用于限定搜索范围。 |
types | 可选的待索引或搜索的媒体维度:"video"、"audio"、"transcript"。 |
metadata | 可选的扁平键/值元数据。值必须为字符串、数字或布尔值。 |
threshold | 可选的最小搜索分数,范围 0.0 到 1.0。值越高,返回的结果越少,但置信度越高。 |
选择 asset_class
使用 asset_class 表示你的产品默认应在其中进行搜索的边界。
对大多数面向客户的产品而言,这就是终端客户、工作区、项目、
代码仓库或集合。
asset_class 的良好示例:
customer_acme
workspace_123
project_onboarding
repo_mobile_app
对面向客户的工作流,不要依赖全局搜索,除非允许每位用户都搜索所有已索引的资产。
端到端流程
提交一个资产
const ingest = await client.ingest({
source: "url",
asset_id: "video_123",
asset_class: "customer_acme",
url: "https://example.com/recording.mp4",
media_type: "video",
types: ["video", "audio", "transcript"],
metadata: {
title: "product demo",
customer_id: "acme",
},
});
等待索引
const task = await client.pollTask(ingest.task_id, {
intervalMs: 2_000,
timeoutMs: 120_000,
});
if (task.status === "failed") {
throw new Error(task.error ?? "ingest task failed");
}
搜索已索引的媒体
const search = await client.search({
query: "where does the customer mention pricing?",
asset_class: "customer_acme",
types: ["transcript"],
threshold: 0.7,
limit: 10,
});
console.log(search.results);
浏览器与服务端使用
只要运行时提供 fetch、FormData 和 Blob,SDK 即可工作。
| 运行时 | 推荐的摄取来源 |
|---|
| 浏览器应用 | source: "file",用于从文件输入直接上传。 |
| 后端服务 | 媒体已存储时使用 source: "url" 或 source: "s3"。 |
| Worker 运行时 | 根据可用的请求体 API,使用 source: "url" 或 source: "file"。 |
对于大文件,建议先将媒体上传到你的存储层,然后通过 URL 或 S3 键摄取。
ingest(request) 提交媒体进行索引,并立即返回一个任务 ID。
通用字段:
| 字段 | 必填 | 说明 |
|---|
asset_id | 是 | 你为资产指定的唯一 ID。 |
asset_class | 否 | 资产的逻辑分组。后续搜索可将其作为范围。 |
media_type | 否 | "video" 或 "audio"。 |
types | 否 | 要索引的维度:"video"、"audio"、"transcript"。 |
metadata | 否 | 用于你自己关联用途的扁平键/值元数据。 |
来源相关字段:
| 来源 | 必填字段 | 说明 |
|---|
url | url | 从公开或后端可访问的 URL 摄取。 |
s3 | s3_key | 从已连接到 sf-voice 的 S3 键摄取。 |
file | file、filename | 直接上传 Blob、ArrayBuffer 或 Uint8Array。 |
const ingest = await client.ingest({
source: "url",
asset_id: "video_123",
asset_class: "customer_acme",
url: "https://example.com/recording.mp4",
media_type: "video",
types: ["video", "audio", "transcript"],
});
type IngestResponse = {
asset_id: string;
task_id: string;
status: "pending";
};
{
"asset_id": "video_123",
"task_id": "task_abc123",
"status": "pending"
}
任务与轮询
使用 getTask(taskId) 获取一次任务状态。使用 pollTask(taskId, options)
等待任务进入 "ready" 或 "failed" 状态。
const task = await client.pollTask("task_abc123", {
intervalMs: 2_000,
timeoutMs: 120_000,
});
type Task = {
task_id: string;
asset_id: string;
asset_class?: string;
types: Array<"video" | "audio" | "transcript">;
status: "pending" | "indexing" | "ready" | "failed";
error?: string;
created_at: string;
completed_at?: string;
};
search(request) 使用自然语言搜索已索引的媒体。
对面向客户的搜索,优先使用 asset_class 或 asset_ids。仅在确实希望搜索所有资产时才使用 scope: "all"。
搜索有三种范围模式:
| 模式 | 示例 | 适用场景 |
|---|
asset_class | { asset_class: "customer_acme" } | 搜索一个客户、工作区、项目或集合。 |
asset_ids | { asset_ids: ["video_123"] } | 搜索一组已知的资产。 |
scope: "all" | { scope: "all" } | 有意搜索所有已索引的资产。 |
| 字段 | 必填 | 说明 |
|---|
query | 是 | 自然语言搜索查询。 |
types | 否 | 要搜索的维度:"video"、"audio"、"transcript"。 |
asset_ids | 否 | 将搜索限定到特定客户资产 ID。 |
asset_class | 否 | 将搜索限定到一个逻辑分组。建议用于按客户限定的搜索。 |
scope | 否 | 仅在有意搜索所有资产时设置为 "all"。 |
threshold | 否 | 最低匹配分数,范围 0.0 到 1.0。默认为 API 默认值。 |
page | 否 | 页码。 |
limit | 否 | 每页最大结果数。 |
const search = await client.search({
query: "refund policy",
asset_class: "customer_acme",
types: ["transcript"],
});
type SearchResponse = {
results: Array<{
asset_id: string;
score: number;
start_ms: number;
end_ms: number;
match_type: "video" | "audio" | "transcript";
thumbnail_url?: string;
}>;
page_info: {
total: number;
page: number;
limit: number;
next_page_token?: string;
};
};
{
"results": [
{
"asset_id": "video_123",
"score": 0.84,
"start_ms": 42000,
"end_ms": 58000,
"match_type": "transcript",
"thumbnail_url": "https://api.sf-voice.com/assets/video_123/thumb.jpg"
}
],
"page_info": {
"total": 1,
"page": 1,
"limit": 10
}
}
start_ms 和 end_ms 是源媒体内的毫秒偏移量。使用它们可以让播放器
直接跳转到匹配时刻。
const assets = await client.listAssets({ page: 1, limit: 20 });
const asset = await client.getAsset("video_123");
await client.deleteAsset("video_123");
type Asset = {
asset_id: string;
asset_class?: string;
media_type: "video" | "audio";
source_type: "url" | "s3" | "file";
types: Array<"video" | "audio" | "transcript">;
status: "pending" | "indexing" | "ready" | "failed";
metadata?: Record<string, string | number | boolean>;
duration_ms?: number;
created_at: string;
updated_at: string;
};
每个非 2xx 的 API 响应都会抛出 SfVoiceMediaError。
import { SfVoiceMediaError } from "@sf-voice/media";
try {
await client.search({
query: "pricing",
asset_class: "customer_acme",
});
} catch (error) {
if (error instanceof SfVoiceMediaError) {
console.error(error.code);
console.error(error.status);
console.error(error.message);
}
throw error;
}
当单次 HTTP 请求超过客户端超时时,会抛出 SfVoiceMediaRequestTimeoutError。
当 pollTask 在任务到达 "ready" 或 "failed" 之前超过其轮询超时时,会抛出
SfVoiceMediaPollTimeoutError。