DEV Community

Jia
Jia

Posted on

Build a Tool Profile Resolver Before Dify, Cursor, and Node.js Share Vector Engine

#ai

When Vector Engine becomes the shared OpenAI-compatible API gateway for several internal tools, the hard part is rarely the HTTP request itself. The hard part is keeping each tool honest about the same Base URL, API Key source, model name, timeout, and error expectations. Dify may store values in a provider form, Cursor may keep them in a local settings file, and a Node.js service may read them from environment variables.

This tutorial builds a small tool profile resolver in Node.js. It does not send prompts. It prints a sanitized profile for every client before traffic moves through the LLM API provider layer. The goal is to catch configuration drift before a developer sees model_not_found in production.

The profile shape

Use one profile per tool. Keep secrets out of the output.

const profiles = {
  dify: {
    tool: "Dify",
    baseURL: process.env.DIFY_BASE_URL,
    apiKeyName: "DIFY_VECTOR_ENGINE_KEY",
    model: process.env.DIFY_MODEL,
    timeoutMs: 45000
  },
  cursor: {
    tool: "Cursor",
    baseURL: process.env.CURSOR_BASE_URL,
    apiKeyName: "CURSOR_VECTOR_ENGINE_KEY",
    model: process.env.CURSOR_MODEL,
    timeoutMs: 30000
  },
  service: {
    tool: "Node.js service",
    baseURL: process.env.VECTOR_ENGINE_BASE_URL,
    apiKeyName: "VECTOR_ENGINE_API_KEY",
    model: process.env.VECTOR_ENGINE_MODEL,
    timeoutMs: 60000
  }
};
Enter fullscreen mode Exit fullscreen mode

For OpenAI-compatible clients, the Base URL should be the provider base:

VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn/v1
Enter fullscreen mode Exit fullscreen mode

Do not paste a full chat-completions endpoint into a field that expects a Base URL. That mistake often produces confusing 404s or a model_not_found message because the client appends another path segment.

The resolver

The resolver checks missing fields, unsafe paths, and model name consistency.

function normalizeBaseURL(value) {
  if (!value) return "";
  return value.replace(/\/+$/, "");
}

function resolveProfile(name, profile) {
  const baseURL = normalizeBaseURL(profile.baseURL);
  const errors = [];

  if (!baseURL) errors.push("missing Base URL");
  if (!profile.model) errors.push("missing model name");
  if (baseURL.endsWith("/chat/completions")) {
    errors.push("Base URL looks like a full endpoint");
  }

  return {
    name,
    tool: profile.tool,
    baseURL,
    apiKeyName: profile.apiKeyName,
    apiKeyPresent: Boolean(process.env[profile.apiKeyName]),
    model: profile.model || "",
    timeoutMs: profile.timeoutMs,
    errors
  };
}

const resolved = Object.entries(profiles).map(([name, profile]) =>
  resolveProfile(name, profile)
);

console.table(resolved.map(item => ({
  tool: item.tool,
  baseURL: item.baseURL,
  apiKeyName: item.apiKeyName,
  apiKeyPresent: item.apiKeyPresent,
  model: item.model,
  errors: item.errors.join("; ")
})));

if (resolved.some(item => item.errors.length > 0 || !item.apiKeyPresent)) {
  process.exitCode = 1;
}
Enter fullscreen mode Exit fullscreen mode

This output is safe to paste into an internal ticket because it shows the API Key variable name, not the key value.

What to compare in Dify and Cursor

Use the resolver output as a checklist:

Field Dify Cursor Node.js service
Base URL Provider form or custom model settings Custom OpenAI-compatible endpoint VECTOR_ENGINE_BASE_URL
API Key Secret field in the provider Local settings or environment bridge Server-side environment variable
Model name Model field in workflow config Model override in settings VECTOR_ENGINE_MODEL
Error owner Workflow owner Developer using the IDE Service owner

When model_not_found appears, do not rotate the key immediately. Check whether Dify, Cursor, and the Node.js service are using the same model name and the same Vector Engine route. A mismatched model field is easier to fix than a blind provider change.

Add one smoke request

After the resolver passes, run a short smoke request from the Node.js service profile.

async function smoke() {
  const baseURL = normalizeBaseURL(process.env.VECTOR_ENGINE_BASE_URL);
  const response = await fetch(`${baseURL}/chat/completions`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.VECTOR_ENGINE_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: process.env.VECTOR_ENGINE_MODEL,
      messages: [{ role: "user", content: "Return the word ok." }],
      temperature: 0
    })
  });

  const text = await response.text();
  if (!response.ok) {
    throw new Error(`Vector Engine smoke failed: ${response.status} ${text}`);
  }
  console.log("Vector Engine smoke passed");
}
Enter fullscreen mode Exit fullscreen mode

Keep this smoke request small. The resolver checks configuration, and the smoke request checks the path through the OpenAI-compatible API gateway.

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

Summary

A shared LLM API provider layer is easier to operate when tool settings are explicit. Before Dify, Cursor, and a Node.js service share Vector Engine, print the resolved Base URL, API Key source, model name, and timeout. That small step turns model_not_found from a vague user complaint into a concrete configuration diff.


在 Dify、Cursor 和 Node.js 共用向量引擎前,先写一个工具配置解析器

当向量引擎成为多个内部工具共用的 OpenAI 兼容入口时,真正麻烦的通常不是 HTTP 请求本身,而是每个工具是否都诚实地使用同一个 Base URL、API Key 来源、模型名称、超时设置和错误预期。Dify 可能把配置放在模型供应商表单里,Cursor 可能放在本地设置里,Node.js 服务则通常从环境变量读取。

这篇教程用 Node.js 写一个工具配置解析器。它不会发送业务提示词,只会在流量进入 LLM API provider layer 之前,打印每个客户端的脱敏配置。目标是在开发者遇到 model_not_found 之前发现配置漂移。

配置结构

每个工具维护一份 profile,不输出密钥明文。

const profiles = {
  dify: {
    tool: "Dify",
    baseURL: process.env.DIFY_BASE_URL,
    apiKeyName: "DIFY_VECTOR_ENGINE_KEY",
    model: process.env.DIFY_MODEL,
    timeoutMs: 45000
  },
  cursor: {
    tool: "Cursor",
    baseURL: process.env.CURSOR_BASE_URL,
    apiKeyName: "CURSOR_VECTOR_ENGINE_KEY",
    model: process.env.CURSOR_MODEL,
    timeoutMs: 30000
  },
  service: {
    tool: "Node.js service",
    baseURL: process.env.VECTOR_ENGINE_BASE_URL,
    apiKeyName: "VECTOR_ENGINE_API_KEY",
    model: process.env.VECTOR_ENGINE_MODEL,
    timeoutMs: 60000
  }
};
Enter fullscreen mode Exit fullscreen mode

对于 OpenAI 兼容客户端,Base URL 应该是服务基础地址,例如:

VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn/v1
Enter fullscreen mode Exit fullscreen mode

不要把完整的 chat-completions 接口填进只需要 Base URL 的字段。很多 404 或 model_not_found,其实是工具自动追加路径后变成了重复路径。

解析器代码

解析器检查缺失字段、错误路径和模型名称一致性。

function normalizeBaseURL(value) {
  if (!value) return "";
  return value.replace(/\/+$/, "");
}

function resolveProfile(name, profile) {
  const baseURL = normalizeBaseURL(profile.baseURL);
  const errors = [];

  if (!baseURL) errors.push("missing Base URL");
  if (!profile.model) errors.push("missing model name");
  if (baseURL.endsWith("/chat/completions")) {
    errors.push("Base URL looks like a full endpoint");
  }

  return {
    name,
    tool: profile.tool,
    baseURL,
    apiKeyName: profile.apiKeyName,
    apiKeyPresent: Boolean(process.env[profile.apiKeyName]),
    model: profile.model || "",
    timeoutMs: profile.timeoutMs,
    errors
  };
}
Enter fullscreen mode Exit fullscreen mode

输出结果可以贴进内部工单,因为它只展示 API Key 变量名,不展示实际 Key。

Dify 和 Cursor 要对照什么

可以把解析器输出当作检查表:

字段 Dify Cursor Node.js 服务
Base URL 供应商或自定义模型设置 自定义 OpenAI 兼容入口 VECTOR_ENGINE_BASE_URL
API Key 供应商密钥字段 本地设置或环境变量桥接 服务端环境变量
模型名称 工作流模型字段 设置里的模型覆盖项 VECTOR_ENGINE_MODEL
错误负责人 工作流负责人 使用 IDE 的开发者 服务负责人

如果出现 model_not_found,不要立刻轮换 Key。先确认 Dify、Cursor 和 Node.js 服务是否使用同一个模型名称,以及同一条向量引擎路由。模型字段错配通常比盲目更换供应商更容易处理。

再加一个小烟测

配置解析通过后,用 Node.js 服务 profile 发一个很短的 smoke request。

async function smoke() {
  const baseURL = normalizeBaseURL(process.env.VECTOR_ENGINE_BASE_URL);
  const response = await fetch(`${baseURL}/chat/completions`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.VECTOR_ENGINE_API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: process.env.VECTOR_ENGINE_MODEL,
      messages: [{ role: "user", content: "Return the word ok." }],
      temperature: 0
    })
  });

  const text = await response.text();
  if (!response.ok) {
    throw new Error(`Vector Engine smoke failed: ${response.status} ${text}`);
  }
  console.log("Vector Engine smoke passed");
}
Enter fullscreen mode Exit fullscreen mode

向量引擎API中转站适合统一管理多工具入口,但前提是配置能够被读回。向量引擎中转站不是让所有工具变成一个黑盒,而是让团队能把 API中转站 的 Base URL、Key 来源、模型名和错误责任写清楚。

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

小结

共享的 LLM API provider layer 只有在配置显式时才容易运维。让 Dify、Cursor 和 Node.js 服务共用向量引擎之前,先打印解析后的 Base URL、API Key 来源、模型名称和超时设置。这样 model_not_found 就不再是模糊投诉,而是可以定位到具体配置差异的工程信号。

Top comments (0)