Skip to main content

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_classasset_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,
});
选项必填说明
baseUrlsf-voice media API 的基础 URL。
apiKeyX-API-Key 请求头发送的 API 密钥。
timeoutMs每次请求的超时时间(毫秒)。默认为 30_000

核心字段

字段说明
asset_id你为媒体资产指定的唯一 ID。摄取时必填。
asset_class可选的逻辑分组,例如客户、工作区、项目或代码仓库。用于限定搜索范围。
types可选的待索引或搜索的媒体维度:"video""audio""transcript"
metadata可选的扁平键/值元数据。值必须为字符串、数字或布尔值。
threshold可选的最小搜索分数,范围 0.01.0。值越高,返回的结果越少,但置信度越高。

选择 asset_class

使用 asset_class 表示你的产品默认应在其中进行搜索的边界。 对大多数面向客户的产品而言,这就是终端客户、工作区、项目、 代码仓库或集合。 asset_class 的良好示例:
  • customer_acme
  • workspace_123
  • project_onboarding
  • repo_mobile_app
对面向客户的工作流,不要依赖全局搜索,除非允许每位用户都搜索所有已索引的资产。

端到端流程

1

提交一个资产

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",
  },
});
2

等待索引

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");
}
3

搜索已索引的媒体

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);

浏览器与服务端使用

只要运行时提供 fetchFormDataBlob,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用于你自己关联用途的扁平键/值元数据。
来源相关字段:
来源必填字段说明
urlurl从公开或后端可访问的 URL 摄取。
s3s3_key从已连接到 sf-voice 的 S3 键摄取。
filefilefilename直接上传 BlobArrayBufferUint8Array

示例

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_classasset_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.01.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_msend_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