TypeScript API 参考

必填配置

本页所有 TypeScript 示例都使用应用启动时已校验的 API Key:

const apiKey = process.env.JOY_TOKEN_API_KEY;
if (!apiKey) throw new Error("JOY_TOKEN_API_KEY is required");

导入

import { JoyTokenClient, JoyTokenAPIError } from "@joytoken/client-sdk-ts";

JoyTokenClient

const joytoken = new JoyTokenClient({
apiKey,
apiBaseUrl: process.env.JOY_TOKEN_API_BASE_URL,
timeoutMs: 60_000,
});
选项用途
apiKey请求认证,默认读取 JOY_TOKEN_API_KEY
apiBaseUrl模型和价格 API Base URL,默认读取 JOY_TOKEN_API_BASE_URL,否则使用 https://api.joytokens.ai
openAIBaseUrlOpenAI 兼容接口 Base URL,默认读取 JOY_TOKEN_OPENAI_BASE_URL,否则使用 <apiBaseUrl>/openai/v1
anthropicBaseUrl已弃用的源码兼容选项;Messages 仍使用 Chat Completions 入口
anthropicVersion已弃用的源码兼容选项;SDK 不发送 Anthropic 请求头
timeoutMs完整请求和流式消费超时,默认 60_000;传入 0 可禁用
maxRetries针对 HTTP 429、5xx 和传输错误的自动重试次数;默认 0;设置正数即显式开启
fetch自定义 fetch 实现
defaultHeaders添加到每个请求的默认请求头;SDK 认证请求头优先

模型调用、模型元数据和价格接口必须配置 apiKey。缺少 Key 时,SDK 会在发送请求前直接抛出明确错误。models.list() 是唯一无需鉴权的目录方法。

同一个 JoyTokenClient 无需全局协议开关,即可使用 Chat Completions、Responses 和 Anthropic 兼容 Messages。Chat 与 Responses 使用 Gateway 原生 OpenAI 入口;Messages 由 SDK 本地转换并通过 Chat Completions 发送。

兼容接口方法

使用 JOY_TOKEN_OPENAI_BASE_URL 配置 openAIBaseUrl

chat.completions.create

await joytoken.chat.completions.create({
model: "auto",
messages: [{ role: "user", content: "Hello" }],
});

调用 POST /openai/v1/chat/completions,返回非流式 Chat Completions 响应。

chat.completions.stream

for await (const chunk of joytoken.chat.completions.stream({
model: "auto",
messages: [{ role: "user", content: "Hello" }],
})) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
}

调用同一接口,使用 SSE 读取流式 chunks。

responses.create

const response = await joytoken.responses.create({
model: "auto",
input: "用一句话解释 JoyToken。",
});
console.log(response.output?.[0]?.content?.[0]?.text);

调用 POST /openai/v1/responses,返回非流式 Responses 响应。input 支持字符串或 message input items。

responses.stream

for await (const event of joytoken.responses.stream({
model: "auto",
input: "Say hello",
})) {
if (event.type === "response.output_text.delta") {
process.stdout.write(event.delta ?? "");
}
}

读取 Responses SSE 事件,包括 response.output_text.deltaresponse.completed

images.generate

const image = await joytoken.images.generate({
model: "auto",
prompt: "A neon JoyToken logo on a black background",
size: "1024x1024",
});
console.log(image.data[0]?.url ?? image.data[0]?.b64_json);

调用 POST /openai/v1/images/generationsmodelprompt 为必填字段。图片生成仅支持非流式响应:images.generate 会在完整 JSON 响应返回后完成,不会返回 AsyncIterable 或 SSE 事件。不要传 stream: true。生成结果位于 data,如有 JoyToken 路由和计费信息则位于 metadata

images.edit

const edited = await joytoken.images.edit({
model: "auto",
image: "https://picsum.photos/512/512",
prompt: "Add a neon JoyToken logo in the top-right corner",
size: "1024x1024",
});
console.log(edited.data[0]?.url ?? edited.data[0]?.b64_json);

调用 POST /openai/v1/images/editspromptimage 为必填字段,model 必须为 "auto"image 接受单个 http(s) URL 或 base64 data URI,也可传入数组以同时编辑多张图片。图片编辑仅支持非流式响应:images.edit 会在完整 JSON 响应返回后完成,不会返回 AsyncIterable 或 SSE 事件。不要传 stream: true。输出默认为 b64_json,仅当需要托管 URL 时才设置 response_format: "url"。编辑结果位于 data,如有 JoyToken 路由和计费信息则位于 metadata

Tools 工具参考

Client 工具选项

选项默认值行为
tools未配置Client 注册的声明与 handler
defaultLocalToolstrue仅在不存在用户工具时启用 7 个本地默认工具
defaultBuiltinToolsfalse为 Responses 显式启用默认 web_search_preview
toolMaxSteps8返回最后响应前最多执行的工具轮数
excludedDefaultTools[]只从 SDK 默认集合移除名称
fileWorkspace当前目录本地文件工具沙箱根目录
shellWorkspace当前目录本地 shell 工作根目录
filePermission缺失 / 拒绝file_write 单次审批
shellPermission缺失 / 拒绝shell 单次审批

工具执行方法

原语 createstream 不会执行请求级或 Client 注册工具。仅当不存在任何用户工具来源时,create() 才可能自动执行 SDK 默认工具闭环。

chat.completions

方法返回值handler 执行
create(request)Promise<ChatCompletionResponse>用户/注册工具:否;SDK 默认:选中时自动
stream(request)AsyncIterable<ChatCompletionChunk>
run(request)Promise<ChatCompletionResponse>
executeTools(request)等同 run
runStream(request, options?)Promise<ChatCompletionResponse>是,模型轮次使用流式请求
executeToolsStream(request, options?)等同 runStream

responses

Responses 方法发送原生扁平工具声明,并接收原生 output item 与 SSE 事件。

方法返回值handler 执行
create(request)Promise<Response>用户/注册工具:否;SDK 默认:选中时自动
stream(request)AsyncIterable<ResponseStreamEvent>
run(request)Promise<Response>
executeTools(request)等同 run
runStream(request, options?)Promise<Response>
executeToolsStream(request, options?)等同 runStream

Response.output_text 是所有 output_text 内容的拼接。工具循环追加 function_call_output 输入项。Hosted tools 保持原生 Responses 形状。

messages

Messages 方法对外暴露 Anthropic 兼容输入、输出、tool_use 内容块与流式事件,内部请求 Gateway Chat Completions。

方法返回值handler 执行
create(request)Promise<MessageResponse>用户/注册工具:否;SDK 默认:选中时自动
stream(request)AsyncIterable<MessageStreamEvent>
run(request)Promise<MessageResponse>
executeTools(request)等同 run
runStream(request, options?)Promise<MessageResponse>
executeToolsStream(request, options?)等同 runStream

Anthropic tool_choice 映射如下:

MessagesChat
{ type: "auto" }"auto"
{ type: "any" }"required"
{ type: "tool", name }指定函数
{ type: "none" }"none"

工具类型与辅助函数

import {
calculator,
dateTime,
defineTool,
fileRead,
fileSearch,
fileWrite,
listDir,
shell,
type Tool,
type ToolCall,
type ToolCallResult,
type ToolRunStreamOptions,
} from "@joytoken/client-sdk-ts";

defineTool() 原样返回传入工具并保留泛型输入/输出类型。Tool 包含 name、可选 description、JSON Schema parameters,以及可选 execute(input, context) handler。

ToolCall.extra_content?: Record<string, unknown> 保存不透明的厂商扩展数据。SDK 会在非流式和流式 Chat 续轮、Responses function_call item、Messages tool_use block 以及工具 handler 上下文中保留该字段。Gemini thought_signature 只是其中一个示例;类型和透传逻辑不绑定任何厂商。

应用自行实现工具循环时,必须复用完整返回的 ToolCall、Responses function_call 或 Messages tool_use item,不能只用 idnamearguments 重建。

const options: ToolRunStreamOptions = {
onTextDelta: (delta) => process.stdout.write(delta),
onToolResult: (result: ToolCallResult) => {
console.log(result.tool_name, result.is_error);
},
onOrchestrationEvent: (event) => {
if (event.type === "plan") console.log("plan", event.plan.length);
else console.log("stage", event.task_id, event.final);
},
};

显式执行器收到没有匹配注册 handler 的工具调用时,会生成结构化 tool_handler_not_found 工具结果;绝不会回退到 SDK 同名默认实现。

工具优先级

request.tools !== undefined
→ 完整使用 request.tools(包括 [])
→ 显式执行器只能使用同名 Client handler
否则存在 Client tools
→ 完整使用 Client tools
→ 仅显式执行器执行
否则
→ 注入 SDK 默认工具
→ 允许默认自动闭环

Responses hosted file_search 必须包含非空 vector_store_ids,SDK 不会自动创建 ID。本地 function file_search 使用 { type: "function", name: "file_search", ... },两者属于不同工具。

models.list

const models = await joytoken.models.list({ locale: "zh" });

调用无需鉴权的 GET /api/v1/models 接口。需要英文描述时传 { locale: "en" };不传 locale 时接口默认返回英文描述。SDK 保留 HTTP 响应层级,目录项位于 models.data.models

models.meta

const metadata = await joytoken.models.meta();

携带已配置的 API Key 调用 GET /api/v1/models/meta,返回档位、SKU、能力标签、行业包、提供商和目录更新时间。

pricing.retrieve

const pricing = await joytoken.pricing.retrieve();

携带已配置的 API Key 调用 GET /api/v1/pricing,返回面向客户的档位兑换价格元数据。

编排(Orchestration)

当网关为 model: "auto" 的一轮请求规划并执行多个子任务时,SDK 会聚合结果,而不是暴露原始 JSON 负载。

import {
ORCHESTRATION_FINAL_TASK_ID,
type OrchestrationResult,
type OrchestrationInfo,
type OrchestrationEvent,
} from "@joytoken/client-sdk-ts";
载体形态说明
ChatCompletionResponse.orchestration?OrchestrationResult聚合后的 plan,以及按到达顺序排列的全部子任务 stages
ChatCompletionChunk.orchestration?OrchestrationInfo逐块元数据:task_idtask_seqtask_statustitlephase,规划块上还有 plan
runStreamonOrchestrationEvent(event: OrchestrationEvent) => void触发一次 plan 事件,并在每次子任务切换时触发 stage 事件;非编排轮次不会触发
ORCHESTRATION_FINAL_TASK_ID"__final__"面向用户答案阶段的哨兵 task_id

非流式 create() 仍在 choices[0].message.content 中返回最终答案文本,拆解则挂在 orchestration 上。流式 chunk 包含中间子任务文本,因此请按 chunk.orchestration?.task_id === ORCHESTRATION_FINAL_TASK_ID 过滤以只渲染最终回复,或使用 runStream——其 onTextDelta 已只发出最终答案文本。

JoyTokenAPIError

try {
await joytoken.chat.completions.create({
model: "auto",
messages: [{ role: "user", content: "你好" }],
});
} catch (error) {
if (error instanceof JoyTokenAPIError) {
console.error(error.status, error.requestId, error.body);
}
}
字段用途
statusHTTP 状态码
requestId请求 ID
body错误响应体
context模型调用可选的请求阶段诊断信息

编排执行失败时,可能返回 HTTP 200 但 body 或 SSE 流中携带错误信封({ "error": { ... }, "choices": [] })。SDK 会在非流式和流式的 Chat 路径上都检测该信封,并抛出同样的 JoyTokenAPIError,网关错误对象通过 body 暴露。