Git及dockerhub初体验:Nextchat小改进

众所周知nextchat项目(https://github.com/ChatGPTNextWeb/NextChat)的更新是很慢的。这么说其实并不严谨,在Github上它的代码库经常更新,但打包很“懒”,往往代码已经更新了很多轮,但dockerhub上的版本还是几个月之前的。

最让人难以接受的是对bug的反馈。诸如claude系列自从4.1版本开始就不再同时接受temperature和top_p参数了,在issue中提出了很久,一直无人问津。猜想可能大家都已经用上了自定义版本的nextchat,只有我这个“小白”还在依赖官方的docker吧。

曾经尝试过在vps上git clone整个项目后手动编译,但编译对服务器的CPU与内存要求较高,在小鸡上尝试几次均告失败。其实最好的方式是在自己的github上fork一个nextchat,然后在docker hub上创建自己的docker image。

恰逢ChatGPT Codex风头正劲,一直在琢磨着用Codex在Github上干点“大活”,捣腾Github也是迟早的事情,就拿Nextchat先试一试。

1、创建github与dockerhub帐号

这一步不再赘述,其实只需要创建github帐号即可,dockerhub可以直接使用github的凭证登录。

假设github上的帐号名称是mygitname。

2、Fork

进入nextchat的github项目主页,在Code页,右上角点击“Fork”,选择“Create a new fork”。输入合适的项目名称和描述,并勾选“Copy the main branch only”。点击Create fork,这一步将把nextchat的当前仓库中的main分支全部复制到自己的github帐户中。

3、定位并修改anthropic.ts代码

询问ChatGPT可知,Claude的参数传递应该在anthropic.ts中进行设置。对仓库中的文件进行简单分析,很快就能定位到关键代码位于:app/api/anthropic.ts。

进入该文件页面,点击右上角的“Edit this file”,将其中的req.body段稍作修改:

  // 读取 body 并做参数检查 / 过滤
  if (req.body) {
    try {
      const clonedBody = await req.text();
      const jsonBody = JSON.parse(clonedBody) as {
        model?: string;
        temperature?: number;
        top_p?: number;
      };

      const modelName = jsonBody.model?.toLowerCase() || "";

      // ---------- Claude 参数修正逻辑 ----------
      // 仅在 Claude 4.1 及之后版本应用过滤
      if (modelName.includes("claude")) {
        // 从 4.1 开始的所有模型都会触发参数冲突
        const needsFilter =
          /claude[-_]?(4(\.|-)|opus|sonnet)/i.test(modelName);
        if (
          needsFilter &&
          jsonBody.temperature !== undefined &&
          jsonBody.top_p !== undefined
        ) {
          delete jsonBody.top_p;
          console.log(
            "[Claude param fix] Removed top_p for model:",
            jsonBody.model,
          );
        }
      }

      // ---------- 模型白名单检查 ----------
      if (
        serverConfig.customModels &&
        isModelNotavailableInServer(
          serverConfig.customModels,
          jsonBody.model as string,
          ServiceProvider.Anthropic as string,
        )
      ) {
        return NextResponse.json(
          {
            error: true,
            message: `you are not allowed to use ${jsonBody?.model} model`,
          },
          { status: 403 },
        );
      }

      fetchOptions.body = JSON.stringify(jsonBody);
    } catch (e) {
      console.error("[Anthropic] filter", e);
    }
  }

使用正则表达式对Claude系列模型进行匹配,这就实现了4.1及以上版本时,仅传递temperature参数,删除top_p参数,解决了这个bug。

点击右上角“commit changes”保存修改。

4、生成dockerhub secret密钥

下一步是生成修改后的自定义docker image。为了做到这一点,首先需要生成dockerhub的密钥,并设置到github中,这才能实现从github往dockerhub的自动推送。

登录hub.docker.com,在右上角的个人帐户头像中,单击“Accout Settings”,点击左侧的“Personal access tokens”,然后点击Generate new token。这里注意,由于这个token将用于github推送docker,所以要赋予其全部权限(Read, Write, Delete)。描述建议就用nextchat,这样和项目对应起来。生成后,记录用户名并妥善保存token。注意,这个token的内容只会展示一次。

返回到github,进入自己的nextchat项目(https://github.com/mygitname/NextChat)中。进入“Settings”页,在左侧找到“Secrets and Variables” - “Actions”,依次添加两个secrets:DOCKER_USERNAME及DOCKER_TOKEN,分别对应在hub.docker.com上的用户名与刚刚生成的token。

5、创建docker空仓库

再回到https://hub.docker.com,点击Create a repository创建一个空仓库,名称就叫nextchat。由于之前我们假设我们的github用户名、docker用户名均为mygitname,那么,这个docker空仓库的名称就叫“mygitname/nextchat”。创建空仓库这一步是必要的,相当于为后续github action的推送指定了目的地。

6、使用github action创建并推送docker

回到github自己的nextchat项目(https://github.com/mygitname/NextChat)。点击“Actions”页,单击“New workflow”。可以看到github已经贴心地为我们准备好了docker image的模板。点击模板上的configue按钮,编辑其内容如下:

name: Docker Image CI

on:
  workflow_dispatch:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: docker.io/${{ secrets.DOCKER_USERNAME }}/nextchat:latest

这里引用了之前已经设置好的两个secret。

保存之后,在左侧会出现“Docker Image CI”的workflow。点击它,在右侧点击“Run workflow”,github就会自动开始创建并推送docker了。这个过程可能需要几分钟,之后会有成功完成的提示。

检查hub.docker.com,不出意外的话,自定义docker已经发布。那么,接下来就可以在vps上直接使用这个docker了。

7、后记:开源之殇

nextchat之所以如此表现是可以理解的。他们早就开始为企业提供付费服务,在这种情况下还继续开源已经是作贡献了。如果想要顺利地使用这份开源成果,就需要用户自己动手,或自己编译,或自己改改bug然后重新打包——这制造了一个门槛。这个门槛并不高,但它拦住了那些纯“伸手党”。从本质上讲,既然已经到了使用开源软件的程度,那么付出一点点学习成本是再正常不过的,因为天下没有完全免费的午餐,别人手把手教你的时间,也都是有成本的。

对我来说,下一步就是连接Codex和github——新的探险旅程就要开始了。