Go API 参考

导入

import (
"context"
"errors"
"fmt"
"io"
"os"
joytoken "github.com/jd-opensource/joytoken-sdk-go"
)

NewClient

ctx := context.Background()
client := joytoken.NewClient(
joytoken.WithAPIKey(os.Getenv("JOY_TOKEN_API_KEY")),
joytoken.WithAPIBaseURL(os.Getenv("JOY_TOKEN_API_BASE_URL")),
)
选项用途
WithAPIKey请求认证
WithAPIBaseURLJoyToken 公共 Base URL,并派生 <base>/openai/v1 用于模型调用
WithOpenAIBaseURL显式覆盖派生出的模型 API Base URL
WithAnthropicBaseURL已废弃的 no-op,仅为源码兼容保留
WithAnthropicVersion已废弃的 no-op,仅为源码兼容保留
WithHTTPClient自定义 HTTP client
WithHeader添加默认请求头
WithTimeout完整请求和流式消费超时,默认 60 秒;传入非正值可禁用
WithMaxRetries启用瞬时错误重试;默认 0,因为模型 POST 不天然幂等
WithTools / WithToolHandler为显式 Run* 循环注册可执行工具;非空注册集整体替换默认工具
WithDefaultLocalTools开关内置本地工具集(calculatordatetimefile_readlist_dirfile_searchfile_writeshell);默认开启
WithDefaultBuiltinTools启用 Responses 托管 web_search_preview;默认关闭
WithoutDefaultTools按名排除特定默认工具(如 WithoutDefaultTools("shell", "file_write"));不影响显式注册的工具
WithFileWorkspace / WithShellWorkspace配置沙箱根目录;两者均默认为当前目录
WithFilePermissionfile_write 工具授予审批回调;未提供时 file_write 仍会声明,但执行期被拒绝
WithShellPermissionshell 工具授予审批回调;未提供时 shell 仍会声明,但执行期被拒绝

Gateway 只有一个 Chat Completions 模型入口。Responses 使用原生 /openai/v1/responses;Messages 在 SDK 本地与 Chat Completions 相互转换,不会发送 Anthropic HTTP 请求或 anthropic-version 请求头。

模型调用、模型元数据和价格接口必须配置 WithAPIKey。缺少 Key 时,这些方法会在发送请求前返回 joytoken.ErrMissingAPIKeyListModels 是唯一无需鉴权的目录方法。

方法选择

接口结构单次请求执行工具原始流式流式工具循环
ChatCreateChatCompletionRunChatCompletionStreamChatCompletionRunChatCompletionStream
ResponsesCreateResponseRunResponseStreamResponseRunResponseStream
MessagesCreateMessageRunMessageStreamMessageRunMessageStream

只有在请求级与 Client 注册工具都不存在时,基础方法才会自动运行 SDK 默认兜底工具。调用方工具必须使用显式 Run* 才会由 SDK 执行。

兼容接口方法

通常只需配置一次 WithAPIBaseURL。只有需要显式覆盖模型地址时才使用 WithOpenAIBaseURL

CreateChatCompletion

completion, err := client.CreateChatCompletion(ctx, joytoken.ChatCompletionRequest{
Model: joytoken.ModelAuto,
Messages: []joytoken.ChatMessage{
{Role: "user", Content: "Hello"},
},
Tools: []joytoken.ChatTool{},
})

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

StreamChatCompletion

stream, err := client.StreamChatCompletion(ctx, joytoken.ChatCompletionRequest{
Model: joytoken.ModelAuto,
Messages: []joytoken.ChatMessage{
{Role: "user", Content: "Hello"},
},
Tools: []joytoken.ChatTool{},
})
if err != nil {
return err
}
defer stream.Close()
chunk, err := stream.Recv()

使用 SSE 读取流式 chunks。返回 io.EOF 表示流结束。

CreateResponse

response, err := client.CreateResponse(ctx, joytoken.ResponseRequest{
Model: joytoken.ModelAuto,
Input: "用一句话解释 JoyToken。",
Tools: []joytoken.ResponseTool{},
})
if err != nil {
return err
}
fmt.Println(response.OutputText())

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

StreamResponse

stream, err := client.StreamResponse(ctx, joytoken.ResponseRequest{
Model: joytoken.ModelAuto,
Input: "Say hello",
Tools: []joytoken.ResponseTool{},
})
if err != nil {
return err
}
defer stream.Close()
for {
event, err := stream.Recv()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return err
}
fmt.Print(event.Delta)
}

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

GenerateImage

image, err := client.GenerateImage(ctx, joytoken.ImageGenerationRequest{
Model: joytoken.ModelAuto,
Prompt: "A neon JoyToken logo on a black background",
Size: "1024x1024",
})
if err != nil {
return err
}
if len(image.Data) == 0 {
return fmt.Errorf("图片响应不包含数据")
}
fmt.Println(image.Data[0].URL)

调用 POST /openai/v1/images/generationsModelPrompt 为必填字段。图片生成仅支持非流式响应:GenerateImage 会在完整 JSON 响应返回后一次性返回,不提供 SSE 流。不要传 stream: true。生成结果位于 Data,如有 JoyToken 路由和计费信息则位于 Metadata

EditImage

edited, err := client.EditImage(ctx, joytoken.ImageEditRequest{
Model: joytoken.ModelAuto,
Image: "https://picsum.photos/512/512",
Prompt: "Add a neon JoyToken logo in the top-right corner",
Size: "1024x1024",
})
if err != nil {
return err
}
if len(edited.Data) == 0 {
return fmt.Errorf("image response contained no data")
}
fmt.Println(edited.Data[0].URL)

调用 POST /openai/v1/images/editsPromptImage 为必填字段,Model 必须为 ModelAutoImage 接受单个 http(s) URL 或 base64 data URI,也可传入切片以同时编辑多张图片。图片编辑仅支持非流式响应:EditImage 会在完整 JSON 响应返回后一次性返回,不提供 SSE 流。不要传 stream: true。输出默认为 b64_json,仅当需要托管 URL 时才将 ResponseFormat 设置为 "url"。编辑结果位于 Data,如有 JoyToken 路由和计费信息则位于 Metadata

工具调用

工具所有权遵循同一套优先级:

  1. 非 nil 的请求 Tools(包含空切片)会原样复制,并关闭所有默认工具。
  2. 否则 Client 注册工具会整体替换默认工具。
  3. 只有前两者都不存在时,SDK 才注入并自动执行默认兜底工具。

Create* 与原始 Stream* 不会执行请求级或 Client 注册工具。显式 Run* 只执行同名 handler。基础 Create* 只会自动运行规则 3 中由 SDK 自有的默认工具。

注册工具

构造 Client 时使用 WithToolHandler(或 WithTools)注册工具。非空注册集会整体替换默认本地工具,不做合并。

client := joytoken.NewClient(
joytoken.WithAPIKey(os.Getenv("JOY_TOKEN_API_KEY")),
joytoken.WithAPIBaseURL(os.Getenv("JOY_TOKEN_API_BASE_URL")),
joytoken.WithToolHandler(
"get_weather",
"Get the current weather for a city.",
map[string]any{
"type": "object",
"properties": map[string]any{
"city": map[string]any{"type": "string"},
},
"required": []string{"city"},
},
func(ctx context.Context, input any, _ joytoken.ToolExecutionContext) (any, error) {
args, _ := input.(map[string]any)
return map[string]any{"city": args["city"], "tempC": 22}, nil
},
),
)

RunChatCompletion

result, err := client.RunChatCompletion(ctx, joytoken.ChatCompletionRequest{
Model: joytoken.ModelAuto,
Messages: []joytoken.ChatMessage{
{Role: "user", Content: "What is 2+2?"},
},
}, joytoken.RunChatOptions{MaxSteps: 8})
if err != nil {
return err
}
fmt.Println(result.FinalText, result.StoppedBy, result.FinishReason)

运行有轮数上限的 Chat Completions 工具循环。RunChatOptions.MaxSteps 限制轮数(为 0 时使用默认值 8)。返回 *RunChatResult,包含 FinalTextMessages、逐轮 StepsStoppedBy 以及与 Provider 无关的 FinishReason

RunResponse / RunMessage / 流式 Run

RunResponse 在 Responses 原生 items 上运行循环,RunMessage 在 Messages 兼容内容块上运行循环。RunChatCompletionStreamRunResponseStreamRunMessageStream 都支持文本增量和工具结果回调,并返回最终聚合响应。

ToolCall.ExtraContent 保存不透明的厂商扩展字段,序列化为 extra_content。SDK 会在非流式与流式续跑、Messages tool_use 转换、Responses function-call items 以及 Agent 循环中原样保留它。Gemini 的 google.thought_signature 即为一例。手写循环必须保留完整的返回工具调用,而非仅重建其标准字段。另有一个位于顶层的 thought_signature,对于像 Gemini 这类直接把它返回在 tool_call 顶层的厂商,SDK 将其暴露为 ToolCall.ThoughtSignature(序列化为 thought_signature),并在续跑轮次原样(verbatim)保留与回传。

后续轮次失败时,显式 Run* 方法会同时返回错误和非 nil 的部分结果。已完成的 Steps、工具结果以及累积的 MessagesInput 都可用于诊断与恢复。

请求级声明不含可执行 Go 代码。Run* 请求传入 Tools 时,只有 Client 上注册的同名 handler 可以执行。未知名称会产生结构化 tool_handler_not_found 结果并回传模型。

Responses 托管工具

WithDefaultBuiltinTools(true) 当前只启用 web_search_preview,默认关闭。托管 file_search 必须显式提供有效 vector_store_ids。不要同时发送 web_searchweb_search_20250305web_search_preview 等厂商别名;未知类型可能被 Gateway 拒绝。

应通过 Responses 结果中是否存在 web_search_call output item,确认托管搜索确实执行。

有副作用的工具受门控保护

默认本地工具集包含可自由运行的只读工具(file_readlist_dirfile_search),以及两个始终会声明给模型、但在执行时默认安全拒绝的副作用工具:file_writeshell。它们仅在你提供审批回调时才会运行。

client := joytoken.NewClient(
joytoken.WithAPIKey(os.Getenv("JOY_TOKEN_API_KEY")),
// 逐条批准或拒绝 shell 命令;未提供回调即拒绝所有调用。
joytoken.WithShellPermission(func(ctx context.Context, req joytoken.ShellPermissionRequest) (bool, error) {
return req.Command == "ls" && req.WorkingDir == "/tmp", nil
}),
joytoken.WithFilePermission(func(ctx context.Context, req joytoken.FilePermissionRequest) (bool, error) {
return false, nil // 拒绝所有写入
}),
)

ShellPermissionRequest 包含 CommandWorkingDirFilePermissionRequest 包含解析后的 Root。使用 WithShellWorkspaceWithFileWorkspace 配置根目录,便于宿主准确展示和限制执行位置。若想彻底移除某个工具而非只是门控,可按名称排除:

// 从默认集中移除 shell 和 file_write;只读工具保留。
joytoken.WithoutDefaultTools("shell", "file_write")

WithoutDefaultTools 只过滤默认工具——你通过 WithTools / WithToolHandler 注册的工具始终优先,永不会被移除。

循环的每个续轮都会保留已解析的工具声明,并且每个工具结果只追加一次。首个工具调用后,强制工具选择会放宽为 auto,让模型能够返回最终回答。默认循环上限为 8 轮。

ListModels

models, err := client.ListModelsWithOptions(ctx, joytoken.ListModelsOptions{
Locale: joytoken.ModelLocaleZH,
})

调用无需鉴权的 GET /api/v1/models 接口。ModelLocaleZH 返回中文描述,ModelLocaleEN 返回英文描述。ListModels(ctx) 不传 locale,使用接口默认的英文描述。SDK 保留 HTTP 响应层级,目录项位于 models.Data.Models

GetModelMeta

metadata, err := client.GetModelMeta(ctx)

携带已配置的 API Key 调用 GET /api/v1/models/meta,返回目录筛选元数据。

GetPricing

pricing, err := client.GetPricing(ctx)

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

ErrMissingAPIKey

if errors.Is(err, joytoken.ErrMissingAPIKey) {
// 重试前配置 WithAPIKey 或 JOY_TOKEN_API_KEY。
}

超时与重试策略

WithTimeout 覆盖完整非流式请求,或流被消费期间的完整生命周期。传非正值可禁用 SDK 超时。

WithMaxRetries(n) 使用带上限的指数退避、抖动和 Retry-After 支持,对 HTTP 429、5xx 与传输错误进行重试。默认值为 0。只有在可以接受重放非幂等模型 POST,以及潜在重复执行或计费时才应开启。

APIError

var apiErr *joytoken.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode, apiErr.RequestID, apiErr.Body)
}
字段用途
StatusCodeHTTP 状态码
RequestID请求 ID
ResponseHeaders响应头
Body错误响应体

ChatCompletionResponse.Metadata、Chat 流 metadata 与 Messages 适配层 metadata 会保留 Gateway 返回的路由和计费信息。排查时可记录 request ID 与停止原因,但不要记录 API Key 或未脱敏的敏感工具输入。