DEV Community

Jia
Jia

Posted on

Add a CI Smoke Job for Vector Engine Config Changes Before Dify and Cursor Rollouts

Local checks are useful, but they do not catch every configuration drift. A developer may test Vector Engine from one laptop while a CI environment still points at an old Base URL. A Dify workflow may be updated while a Node.js service keeps the previous model name. Cursor may work for an individual engineer while the shared API Key used by a service is missing.

For a team using Vector Engine as an OpenAI-compatible API gateway and LLM API provider layer, a small CI smoke job can catch those mismatches before they reach a shared environment. The job does not need to send a large prompt. It only needs to confirm that the configured Base URL, API Key, and model name still form a working route.

This DEV tutorial shows a practical Node.js script and a CI step that you can adapt for Dify, Cursor, and service-side rollouts.

What the smoke job should verify

The job should answer five questions:

  1. Is the Base URL shaped like the expected OpenAI-compatible endpoint?
  2. Is the API Key present in the CI secret store?
  3. Is the model name present and non-empty?
  4. Does the route accept a minimal chat completion request?
  5. If the call fails, can the job separate model_not_found from credential or endpoint mistakes?

Use the same environment variables across tools:

VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn/v1
VECTOR_ENGINE_API_KEY=${{ secrets.VECTOR_ENGINE_API_KEY }}
VECTOR_ENGINE_MODEL=your_model_name
Enter fullscreen mode Exit fullscreen mode

Registration URL: https://api.vectorengine.cn/register?aff=Igym

The Node.js smoke script

Create scripts/vector-engine-smoke.mjs:

const required = [
  "VECTOR_ENGINE_BASE_URL",
  "VECTOR_ENGINE_API_KEY",
  "VECTOR_ENGINE_MODEL"
];

for (const name of required) {
  if (!process.env[name]) {
    throw new Error(`Missing required environment variable: ${name}`);
  }
}

const baseURL = process.env.VECTOR_ENGINE_BASE_URL.replace(/\/$/, "");
const apiKey = process.env.VECTOR_ENGINE_API_KEY;
const model = process.env.VECTOR_ENGINE_MODEL;

if (!baseURL.endsWith("/v1")) {
  throw new Error(`Unexpected Base URL shape: ${baseURL}`);
}

const response = await fetch(`${baseURL}/chat/completions`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    authorization: `Bearer ${apiKey}`
  },
  body: JSON.stringify({
    model,
    messages: [
      { role: "user", content: "Return the word ready." }
    ],
    max_tokens: 16
  })
});

const text = await response.text();
let parsed;
try {
  parsed = JSON.parse(text);
} catch {
  parsed = { raw: text };
}

const errorCode = parsed?.error?.code || parsed?.code || "";
const errorMessage = parsed?.error?.message || parsed?.message || "";

if (response.ok) {
  console.log(JSON.stringify({
    ok: true,
    status: response.status,
    model,
    usage: parsed.usage || null
  }, null, 2));
  process.exit(0);
}

if (errorCode === "model_not_found" || /model_not_found/i.test(errorMessage)) {
  throw new Error(`Vector Engine route failed with model_not_found. Check model name: ${model}`);
}

if (response.status === 401) {
  throw new Error("Vector Engine returned 401. Check CI API Key secret and key scope.");
}

throw new Error(`Vector Engine smoke check failed with HTTP ${response.status}: ${text.slice(0, 500)}`);
Enter fullscreen mode Exit fullscreen mode

This script intentionally fails fast. It gives different messages for missing secrets, Base URL drift, API Key failure, and model_not_found.

Example CI workflow

Here is a minimal GitHub Actions job:

name: vector-engine-smoke

on:
  pull_request:
    paths:
      - "dify/**"
      - "cursor/**"
      - "src/llm/**"
      - "scripts/vector-engine-smoke.mjs"

jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Check Vector Engine route
        env:
          VECTOR_ENGINE_BASE_URL: https://api.vectorengine.cn/v1
          VECTOR_ENGINE_API_KEY: ${{ secrets.VECTOR_ENGINE_API_KEY }}
          VECTOR_ENGINE_MODEL: ${{ vars.VECTOR_ENGINE_MODEL }}
        run: node scripts/vector-engine-smoke.mjs
Enter fullscreen mode Exit fullscreen mode

The paths filter is optional, but it keeps the job focused. Run it when Dify exports, Cursor settings, or Node.js LLM integration files change.

How this helps during rollout

Without a smoke job, the team often discovers a broken route from a person using the tool. That creates vague bug reports like "Dify stopped working" or "Cursor cannot use the provider." The CI job gives a more specific signal before the change is merged:

CI error Likely owner
Missing VECTOR_ENGINE_API_KEY CI or platform owner
Unexpected Base URL shape Integration owner
401 Key owner
model_not_found Model route owner
5xx or timeout Provider-layer operator

The important point is that Vector Engine is shared infrastructure. When it sits between many tools and model routes, every change should leave evidence. A compact Node.js CI smoke job gives that evidence without turning the repository into a full observability project.

Keep Dify, Cursor, and services aligned

Dify, Cursor, and Node.js services can each hide configuration in a different place. A pull request may update one and forget another. Put the expected Base URL and model name in visible configuration files where possible, then let CI verify the live route.

That habit makes Vector Engine easier to operate as an LLM API provider layer. Developers still get the flexibility of an OpenAI-compatible API gateway, but the team gets a simple guardrail before shared tools break.


本地检查有价值,但它不能发现所有配置漂移。开发者可能在自己的电脑上测通了向量引擎,但 CI 环境仍然指向旧 Base URL。Dify 工作流可能已经更新,而 Node.js 服务还保留旧 model name。Cursor 对某个工程师可用,但服务使用的共享 API Key 可能缺失。

如果团队把向量引擎作为 OpenAI-compatible API gateway 和 LLM API provider layer,一个小型 CI smoke job 可以在配置进入共享环境之前发现这些不一致。这个 job 不需要发送很大的 prompt,只需要确认当前 Base URL、API Key 和 model name 能组成一条可用路由。

这篇 DEV 教程给出一个可复用的 Node.js 脚本和 CI 步骤,可用于 Dify、Cursor 和服务端迁移。

smoke job 应该验证什么

这个 job 需要回答五个问题:

  1. Base URL 是否符合预期的 OpenAI-compatible endpoint 形态。
  2. API Key 是否存在于 CI secret store。
  3. model name 是否存在且非空。
  4. 这条路由是否能接受最小 chat completion 请求。
  5. 如果调用失败,job 是否能区分 model_not_found、凭证错误和 endpoint 错误。

建议让工具共用同一组环境变量:

VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn/v1
VECTOR_ENGINE_API_KEY=${{ secrets.VECTOR_ENGINE_API_KEY }}
VECTOR_ENGINE_MODEL=your_model_name
Enter fullscreen mode Exit fullscreen mode

注册地址:https://api.vectorengine.cn/register?aff=Igym

Node.js smoke 脚本

创建 scripts/vector-engine-smoke.mjs

const required = [
  "VECTOR_ENGINE_BASE_URL",
  "VECTOR_ENGINE_API_KEY",
  "VECTOR_ENGINE_MODEL"
];

for (const name of required) {
  if (!process.env[name]) {
    throw new Error(`Missing required environment variable: ${name}`);
  }
}

const baseURL = process.env.VECTOR_ENGINE_BASE_URL.replace(/\/$/, "");
const apiKey = process.env.VECTOR_ENGINE_API_KEY;
const model = process.env.VECTOR_ENGINE_MODEL;

if (!baseURL.endsWith("/v1")) {
  throw new Error(`Unexpected Base URL shape: ${baseURL}`);
}

const response = await fetch(`${baseURL}/chat/completions`, {
  method: "POST",
  headers: {
    "content-type": "application/json",
    authorization: `Bearer ${apiKey}`
  },
  body: JSON.stringify({
    model,
    messages: [
      { role: "user", content: "Return the word ready." }
    ],
    max_tokens: 16
  })
});

const text = await response.text();
let parsed;
try {
  parsed = JSON.parse(text);
} catch {
  parsed = { raw: text };
}

const errorCode = parsed?.error?.code || parsed?.code || "";
const errorMessage = parsed?.error?.message || parsed?.message || "";

if (response.ok) {
  console.log(JSON.stringify({
    ok: true,
    status: response.status,
    model,
    usage: parsed.usage || null
  }, null, 2));
  process.exit(0);
}

if (errorCode === "model_not_found" || /model_not_found/i.test(errorMessage)) {
  throw new Error(`Vector Engine route failed with model_not_found. Check model name: ${model}`);
}

if (response.status === 401) {
  throw new Error("Vector Engine returned 401. Check CI API Key secret and key scope.");
}

throw new Error(`Vector Engine smoke check failed with HTTP ${response.status}: ${text.slice(0, 500)}`);
Enter fullscreen mode Exit fullscreen mode

这个脚本会快速失败。缺少 secret、Base URL 漂移、API Key 失败、model_not_found 都会给出不同信息。

CI workflow 示例

下面是一个最小 GitHub Actions job:

name: vector-engine-smoke

on:
  pull_request:
    paths:
      - "dify/**"
      - "cursor/**"
      - "src/llm/**"
      - "scripts/vector-engine-smoke.mjs"

jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Check Vector Engine route
        env:
          VECTOR_ENGINE_BASE_URL: https://api.vectorengine.cn/v1
          VECTOR_ENGINE_API_KEY: ${{ secrets.VECTOR_ENGINE_API_KEY }}
          VECTOR_ENGINE_MODEL: ${{ vars.VECTOR_ENGINE_MODEL }}
        run: node scripts/vector-engine-smoke.mjs
Enter fullscreen mode Exit fullscreen mode

paths filter 不是必须的,但它能让 job 更聚焦。建议在 Dify 导出文件、Cursor 设置、Node.js LLM 集成代码变化时运行。

迁移时它能解决什么

如果没有 smoke job,团队经常从工具使用者那里发现 broken route,于是问题单会变成很模糊的描述,比如 "Dify stopped working" 或 "Cursor cannot use the provider"。CI job 可以在合并之前给出更具体的信号:

CI 错误 可能负责人
缺少 VECTOR_ENGINE_API_KEY CI 或平台负责人
Base URL 形态不符合预期 集成负责人
401 Key 负责人
model_not_found 模型路由负责人
5xx 或 timeout provider layer 运维负责人

关键点在于,向量引擎是共享基础设施。当它位于多种工具和多条模型路由之间,每次变化都应该留下证据。一个紧凑的 Node.js CI smoke job 可以提供这种证据,而不需要把仓库改造成完整监控系统。

让 Dify、Cursor 和服务保持一致

Dify、Cursor 和 Node.js 服务会把配置藏在不同位置。一个 pull request 可能更新了其中一个,却漏掉另一个。能放在可见配置文件里的 Base URL 和 model name,就尽量放出来,再让 CI 验证真实路由。

这种习惯能让向量引擎、向量引擎API中转站、向量引擎中转站和 API中转站 更容易被团队共同维护。开发者仍然可以获得 OpenAI-compatible API gateway 的灵活性,同时团队也能在共享工具出问题之前获得一道简单护栏。

Top comments (0)