一个将 Fitten Code 聊天请求转换为 OpenAI Chat Completions 风格接口的轻量代理服务。
- 提供
/v1/models和/v1/chat/completions兼容接口 - 内置 Web UI 聊天界面(访问
http://localhost:3000即可使用) - 支持非流式响应
- 支持 SSE 流式响应
- 支持
messages[].content字符串与分段数组输入 - 支持单张图片输入
- 支持 tool_calls / function calling 协议转换(上游 XML
<function_calls>→ OpenAI 标准tool_calls) - 统一错误结构,便于调用方处理
- 已支持:文本输入、流式输出、内容分段数组、单图输入
- 已支持:上游 XML
<function_calls>自动转换为 OpenAI 标准tool_calls(非流式 + 流式增量格式) - 已支持:请求体中的
assistant.tool_calls与role: "tool"消息整理进聊天输入 - 明确不支持:同一条消息中的多图输入
- 暂未实现:代理自己发起并执行 tools / function calling(2api 只做协议转换,执行由接入方 agent 完成)
- 暂未实现:embeddings 等扩展能力
当同一条消息包含多张图片时,服务会返回:
- HTTP 400
error.code = "multiple_images_not_supported"
npm install先复制环境变量示例文件,再填入自己的 Fitten 账号凭据:
# Linux / macOS
cp .env.example .env
# Windows
copy .env.example .env编辑 .env,填入 FITTEN_USERNAME 和 FITTEN_PASSWORD,然后启动:
npm start默认监听地址:http://localhost:3000
项目支持使用 Docker 容器化部署,镜像已发布到 GitHub Container Registry (GHCR)。
# 拉取最新镜像
docker pull ghcr.io/bobotechnology/fitten-code-2api:latest
# 运行容器
docker run -d \
--name fitten-api \
-p 3000:3000 \
-e FITTEN_USERNAME=your_username \
-e FITTEN_PASSWORD=your_password \
ghcr.io/bobotechnology/fitten-code-2api:latest# 构建镜像
docker build -t fitten-code-2api .
# 运行容器
docker run -d \
--name fitten-api \
-p 3000:3000 \
-e FITTEN_USERNAME=your_username \
-e FITTEN_PASSWORD=your_password \
fitten-code-2api创建 docker-compose.yml 文件:
version: '3.8'
services:
fitten-api:
image: ghcr.io/bobotechnology/fitten-code-2api:latest
container_name: fitten-api
ports:
- "3000:3000"
environment:
- FITTEN_USERNAME=your_username
- FITTEN_PASSWORD=your_password
- PORT=3000
restart: unless-stopped启动服务:
docker-compose up -d注意: 运行容器时必须设置 FITTEN_USERNAME 和 FITTEN_PASSWORD 环境变量。
项目内置了一个轻量级的 Web UI 聊天界面,启动服务后可以直接在浏览器中使用:
- 访问地址:
http://localhost:3000 - 支持流式输出显示
- 支持多轮对话
- 支持设置 API 地址、API Key、温度参数等
- 对话记录保存在浏览器本地存储
适合快速测试和调试 API 接口,也可以作为简单的聊天客户端使用。
完整示例见 .env.example。
PORT:服务端口,默认3000FITTEN_BASE_URL:Fitten 服务地址,默认https://fc.fittenlab.cnDEFAULT_MODEL:默认模型名,默认fitten-codeFITTEN_USER_AGENT:上游请求使用的 User-Agent;留空时使用内置浏览器 UAFITTEN_REQUEST_TIMEOUT_MS:上游请求超时,默认120000FITTEN_ACCESS_TOKEN_REFRESH_MARGIN_MS:access token 提前续期窗口,默认60000MAX_IMAGE_BYTES:单张图片大小上限,默认5MBFITTEN_USERNAME:Fitten 登录账号FITTEN_PASSWORD:Fitten 登录密码
GET /GET /v1/models返回当前可用模型列表(标准 OpenAI 格式):
{
"object": "list",
"data": [
{
"id": "fitten-code",
"object": "model",
"created": 1700000000,
"owned_by": "fitten"
}
]
}POST /v1/chat/completions
Content-Type: application/json{
"model": "fitten-code",
"messages": [
{
"role": "system",
"content": "请完全使用中文回答。"
},
{
"role": "user",
"content": "你好,请只回复 ok"
}
],
"stream": false
}{
"model": "fitten-code",
"messages": [
{
"role": "user",
"content": "请分三行介绍一下这个代理的作用"
}
],
"stream": true,
"stream_options": {
"include_usage": true
}
}POST /v1/chat/completions
Content-Type: application/json
{
"model": "fitten-code",
"messages": [
{ "role": "system", "content": "你是助手" },
{ "role": "user", "content": "帮我查一下当前目录" }
],
"stream": false
}如果上游返回 XML <function_calls>,2api 会自动转成 OpenAI 标准 tool_calls 格式:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "我来帮你查一下。",
"tool_calls": [{
"index": 0,
"id": "xml_tool_call_xxx_0",
"type": "function",
"function": {
"name": "run_terminal",
"arguments": "{\"command\":\"ls -la\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}流式请求同样支持 tool_calls 转换。当上游返回 XML <function_calls> 时,最后一个 SSE chunk 的 finish_reason 为 tool_calls,并包含 tool_calls 字段:
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"xml_tool_call_xxx_0","type":"function","function":{"name":"run_terminal","arguments":"{\"command\":\"ls -la\"}"}}]},"finish_reason":"tool_calls"}]}{
"model": "fitten-code",
"messages": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "请描述这张图片里的主要内容"
},
{
"type": "input_image",
"image_url": {
"url": "https://example.com/demo.png"
}
}
]
}
],
"stream": false
}如果单条消息里同时放入多张图片,服务会直接返回 multiple_images_not_supported。
服务会先完成 Fitten 登录和会话准备,再将请求转换后发送到上游聊天接口,最后返回 OpenAI 风格响应。
非流式返回结构示例:
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1710000000,
"model": "fitten-code",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "ok"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 94,
"completion_tokens": 2,
"total_tokens": 96
}
}当 stream: true 时,返回 OpenAI 风格的 SSE 数据流。
图片输入仅支持单张图片,接受以下格式:
data:image/...;base64,...http://...https://...
限制说明:
- 单条消息最多一张图片
- 单张图片默认不超过
5MB - 非图片内容、空响应或不支持的协议会返回明确错误码
当前版本已知限制如下:
- 当前只实现
GET /v1/models和POST /v1/chat/completions - 默认会在缺失 system message 时自动补一条
请完全使用中文回答。 - 不支持同一条消息中的多图输入
- 不支持 embeddings
- 不支持文件上传
- 不支持跨实例共享会话缓存;当前 session cache 为进程内内存缓存
scripts/check-*.js主要是本地联调脚本,不属于离线可重复的单元测试- 不支持代理自己发起和执行 tools / function calling(2api 只做协议转换,执行由接入方 agent 完成)
服务会尽量返回统一的 OpenAI 风格错误结构:
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "image_too_large",
"param": "messages.content.image_url"
}
}目前文档中明确列出的常见错误码包括:
multiple_images_not_supported:同一条消息中出现多张图片image_too_large:图片超过MAX_IMAGE_BYTES限制invalid_image_data_url:data:image/...Base64 数据不合法unsupported_image_url:图片 URL 不是data:image/...或http/httpsremote_image_fetch_failed:远程图片拉取失败remote_image_not_image:远程 URL 返回的内容不是图片remote_image_empty:远程图片响应为空invalid_credentials:Fitten 登录失败或账号密码无效login_failed:Fitten 登录接口返回了非预期失败invalid_upstream_json:上游返回了无法解析的 JSONinvalid_login_payload:登录成功响应缺少必要字段refresh_token_invalid:refresh token 无效refresh_user_not_found:refresh token 对应用户不存在refresh_access_token_failed:刷新 access token 失败refresh_token_unavailable:当前 session 中没有可用 refresh tokenupstream_timeout:上游请求超时client_closed_request:客户端提前断开连接upstream_unauthorized:上游返回 401upstream_request_failed:上游请求返回非预期状态码empty_upstream_response:上游返回了空响应internal_error:服务内部错误invalid_request:请求参数不合法
仓库提供了一组本地联调脚本,主要用于回归聊天主链路、图片边界与 tool_calls 协议转换。常用命令:
npm test
npm run check:proxy
npm run check:stream
npm run check:tool-calls
npm run check:content-array
npm run check:markdown
npm run check:newlines
npm run check:errors
npm run check:image-input
npm run check:image-stream
npm run check:image-cleanup
npm run check:image-boundaries
npm run check:all其中 npm test 当前主要做基础语法检查,已经覆盖 index.js、src/openai-request.js、src/message-content.js、src/streaming.js、src/tool-calls.js、src/inputs.js、src/helpers.js、src/fitten-payloads.js、src/session.js、src/errors.js 和 src/parse-xml-tool-calls.js。
脚本分类说明:
基础功能测试:
check-proxy.js:基础代理功能测试check-stream.js:流式输出测试check-tool-calls.js:XML<function_calls>→ OpenAItool_calls协议转换(非流式 + 流式)check-content-array.js:内容分段数组输入测试check-markdown.js:Markdown 格式处理测试check-newlines.js:换行符处理测试check-errors.js:错误码测试
图片相关测试:
check-image-input.js:单图输入测试check-image-stream.js:流式图片响应测试check-image-cleanup.js:图片清理逻辑测试check-image-boundaries.js:图片边界条件测试check-image-size-limit.js:图片大小限制测试check-multi-image-rejected.js:多图输入拒绝测试- [
check-invalid-image-data-url.js](scripts/check-invalid-image-data-url.js:1):无效 Base64 图片数据测试 - [
check-unsupported-image-url.js](scripts/check-unsupported-image-url.js:1):不支持的 URL 格式测试
远程图片测试:
- [
check-remote-image.js](scripts/check-remote-image.js:1):远程图片拉取测试 - [
check-remote-image-size-limit.js](scripts/check-remote-image-size-limit.js:1):远程图片大小限制测试 - [
check-remote-image-not-image.js](scripts/check-remote-image-not-image.js:1):远程 URL 非图片内容测试 - [
check-remote-image-empty.js](scripts/check-remote-image-empty.js:1):远程图片空响应测试
这些脚本依赖真实的 Fitten 凭据和外部网络环境,适合作为本地集成验证。
.
├─ index.js 入口:路由定义、请求重试与响应组装
├─ package.json
├─ README.md
├─ public/ Web UI 聊天界面
│ ├─ index.html 主页面
│ ├─ app.js 前端逻辑
│ └─ style.css 样式文件
├─ src/
│ ├─ errors.js 公共错误
│ ├─ fitten-payloads.js 上游请求载荷与元数据构建
│ ├─ helpers.js 工具函数
│ ├─ inputs.js Fitten 输入格式与工具描述构建
│ ├─ message-content.js 消息归一化与图片处理
│ ├─ openai-request.js OpenAI 请求解析
│ ├─ parse-xml-tool-calls.js XML <function_calls> 解析器
│ ├─ session.js 会话管理与 token 刷新
│ ├─ streaming.js 流式响应处理
│ └─ tool-calls.js OpenAI tool_calls 格式转换
└─ scripts/
└─ check-*.js 本地联调脚本
项目配置了自动化的 Docker 镜像构建和发布流程,使用 GitHub Actions 实现 CI/CD。
- 推送到
main分支:自动构建并推送 Docker 镜像到 GHCR - 创建版本标签(
v*):构建并打上版本号标签(如v1.0.0,v1.0) - Pull Request:仅测试构建,不推送镜像
- ✅ 多平台支持:同时构建
linux/amd64和linux/arm64架构镜像 - ✅ 智能标签:
latest/main:最新版本v1.0.0/v1.0:语义化版本标签abc1234:Git SHA 短哈希
- ✅ 构建缓存:使用 GitHub Actions 缓存加速构建过程
- ✅ 安全认证:自动使用
GITHUB_TOKEN登录 GHCR,无需手动配置凭据
Docker 镜像发布到 GitHub Container Registry (GHCR):
ghcr.io/bobotechnology/fitten-code-2api:<tag>可用标签示例:
ghcr.io/bobotechnology/fitten-code-2api:latestghcr.io/bobotechnology/fitten-code-2api:mainghcr.io/bobotechnology/fitten-code-2api:v1.0.0
访问仓库的 Actions 页面查看构建历史和状态:
https://github.com/bobotechnology/fitten-code-2api/actions
# 创建并推送版本标签,会自动触发带版本号的镜像构建
git tag v1.0.0
git push origin v1.0.0如果仓库是私有的,拉取镜像需要先登录 GHCR:
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin其中 $GITHUB_TOKEN 是你的 GitHub Personal Access Token(需要 read:packages 权限)。