A team can connect Dify, Cursor, and a backend service to an LLM in an afternoon. Keeping those connections consistent for months is harder. The practical solution is to create a small provider adapter before every tool invents its own Base URL, API Key, and model name rules.
This article shows how to use Vector Engine as an OpenAI-compatible API gateway and wrap it as a tiny LLM API provider layer in Node.js. The same contract can then guide Dify and Cursor configuration, while giving engineers a clear place to debug model_not_found.
Registration URL: https://api.vectorengine.cn/register?aff=Igym
Adapter goal
The adapter has four jobs:
| Job | Why it helps |
|---|---|
| Read Base URL from configuration | Avoid hard-coded provider URLs in business code |
| Read API Key from secret storage | Keep keys out of source control |
| Read model name from configuration | Make model changes reviewable |
| Normalize errors | Turn provider failures into useful debugging signals |
Vector Engine uses the OpenAI-compatible Base URL pattern:
https://api.vectorengine.cn/v1
Use that value for OpenAI SDK clients, Dify provider settings, and Cursor custom endpoint settings.
Node.js adapter
Create llmProvider.js:
import OpenAI from "openai";
function required(name) {
const value = process.env[name];
if (!value) throw new Error(`${name} is required`);
return value;
}
const client = new OpenAI({
apiKey: required("VECTOR_ENGINE_API_KEY"),
baseURL: process.env.VECTOR_ENGINE_BASE_URL || "https://api.vectorengine.cn/v1",
});
export async function generateText(messages, options = {}) {
try {
const response = await client.chat.completions.create({
model: options.model || required("VECTOR_ENGINE_MODEL"),
messages,
temperature: options.temperature ?? 0.2,
});
return response.choices[0]?.message?.content ?? "";
} catch (error) {
const message = String(error?.message || error);
if (message.includes("model_not_found")) {
throw new Error("model_not_found: check the configured model name and key permission");
}
throw error;
}
}
Then call it from a route or job:
import { generateText } from "./llmProvider.js";
const answer = await generateText([
{ role: "system", content: "Answer with a concise engineering note." },
{ role: "user", content: "Summarize today's failed LLM checks." },
]);
console.log(answer);
Environment file for local testing
Use an explicit local environment file:
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn/v1
VECTOR_ENGINE_API_KEY=replace-with-local-test-key
VECTOR_ENGINE_MODEL=replace-with-enabled-model-id
The point is not only convenience. It gives reviewers a stable place to inspect provider behavior before a deployment.
Dify mapping
After the Node.js adapter works, map the same fields into Dify:
| Adapter field | Dify field |
|---|---|
VECTOR_ENGINE_BASE_URL |
Base URL |
VECTOR_ENGINE_API_KEY |
API Key |
VECTOR_ENGINE_MODEL |
Model |
If Dify fails but Node.js works, compare the three values. Most problems are still configuration drift, not framework behavior.
Cursor mapping
Cursor should use a dedicated API Key, but the same provider shape:
| Cursor field | Value |
|---|---|
| Custom endpoint type | OpenAI-compatible |
| Base URL | https://api.vectorengine.cn/v1 |
| API Key | A key created for Cursor |
| Model | The model ID used for coding assistance |
This makes Cursor easy to isolate if a key needs rotation or usage investigation.
Debug table
| Failure | Likely cause | Action |
|---|---|---|
model_not_found in Node.js only |
Wrong environment variable | Print the configured model name in a non-secret startup check |
model_not_found in every tool |
Model not enabled or typo reused everywhere | Confirm the exact enabled model ID |
| Dify request path error | Full endpoint pasted into Base URL | Use only the /v1 Base URL |
| Cursor works but backend fails | Different key or deployment secret | Compare key owner and deployment environment |
| Usage is hard to explain | One key shared by many tools | Split keys by Dify, Cursor, backend, and experiments |
Rollout pattern
Start with the Node.js adapter, then configure Dify, then configure Cursor. After each step, record the Base URL, API Key owner, and model name. With Vector Engine behind that consistent provider adapter, each new LLM tool joins the same operating model instead of creating another exception.
增加更多 LLM 工具前,先为向量引擎写一个小型 Node.js Provider Adapter
一个团队可以在一个下午把 Dify、Cursor 和后端服务接到 LLM 上。真正困难的是几个月后仍然保持这些连接一致。更稳妥的做法,是在每个工具各自定义 Base URL、API Key 和模型名规则之前,先写一个小型 provider adapter。
本文展示如何把向量引擎作为 OpenAI 兼容的 API中转站,并在 Node.js 中封装成一个很小的 LLM API provider layer。相同的契约也可以指导 Dify 和 Cursor 配置,并让工程师有一个清楚的位置排查 model_not_found。
注册地址:https://api.vectorengine.cn/register?aff=Igym
Adapter 目标
这个 adapter 只做四件事:
| 目标 | 作用 |
|---|---|
| 从配置读取 Base URL | 避免业务代码里写死 provider 地址 |
| 从密钥系统读取 API Key | 避免 key 进入代码仓库 |
| 从配置读取 model name | 让模型变更可以被 review |
| 统一错误信息 | 把 provider 失败转成可排查的信号 |
向量引擎使用 OpenAI-compatible Base URL 模式:
https://api.vectorengine.cn/v1
这个值可以用于 OpenAI SDK 客户端、Dify provider 配置,也可以用于 Cursor 自定义 endpoint。
Node.js adapter
创建 llmProvider.js:
import OpenAI from "openai";
function required(name) {
const value = process.env[name];
if (!value) throw new Error(`${name} is required`);
return value;
}
const client = new OpenAI({
apiKey: required("VECTOR_ENGINE_API_KEY"),
baseURL: process.env.VECTOR_ENGINE_BASE_URL || "https://api.vectorengine.cn/v1",
});
export async function generateText(messages, options = {}) {
try {
const response = await client.chat.completions.create({
model: options.model || required("VECTOR_ENGINE_MODEL"),
messages,
temperature: options.temperature ?? 0.2,
});
return response.choices[0]?.message?.content ?? "";
} catch (error) {
const message = String(error?.message || error);
if (message.includes("model_not_found")) {
throw new Error("model_not_found: check the configured model name and key permission");
}
throw error;
}
}
然后在路由或任务里调用:
import { generateText } from "./llmProvider.js";
const answer = await generateText([
{ role: "system", content: "Answer with a concise engineering note." },
{ role: "user", content: "Summarize today's failed LLM checks." },
]);
console.log(answer);
本地测试环境变量
本地使用明确的环境文件:
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.cn/v1
VECTOR_ENGINE_API_KEY=replace-with-local-test-key
VECTOR_ENGINE_MODEL=replace-with-enabled-model-id
这不仅是为了方便开发,也是为了让 review 时能稳定检查 provider 行为。
Dify 映射
Node.js adapter 跑通之后,再把同样的字段映射到 Dify:
| Adapter 字段 | Dify 字段 |
|---|---|
VECTOR_ENGINE_BASE_URL |
Base URL |
VECTOR_ENGINE_API_KEY |
API Key |
VECTOR_ENGINE_MODEL |
Model |
如果 Dify 失败而 Node.js 正常,就对比这三个值。大多数问题仍然是配置漂移,而不是框架本身。
Cursor 映射
Cursor 建议使用单独的 API Key,但 provider 形状保持一致:
| Cursor 字段 | 值 |
|---|---|
| Custom endpoint type | OpenAI-compatible |
| Base URL | https://api.vectorengine.cn/v1 |
| API Key | 专门给 Cursor 创建的 key |
| Model | 用于编码辅助的模型 ID |
这样当需要轮换 key 或分析用量时,Cursor 可以被单独隔离。
排错表
| 故障 | 可能原因 | 操作 |
|---|---|---|
只有 Node.js 返回 model_not_found
|
环境变量错误 | 在非敏感启动检查中打印当前模型名 |
所有工具都返回 model_not_found
|
模型未开通或错误拼写被复用 | 确认准确的可用模型 ID |
| Dify 出现请求路径错误 | 把完整接口地址填进 Base URL | 只使用 /v1 层级的 Base URL |
| Cursor 正常但后端失败 | key 或部署密钥不同 | 对比 key 归属和部署环境 |
| 用量很难解释 | 多个工具共用同一个 key | 按 Dify、Cursor、后端和实验拆分 key |
推进方式
先跑通 Node.js adapter,再配置 Dify,然后配置 Cursor。每完成一步,都记录 Base URL、API Key 归属和模型名。把向量引擎API中转站放在这个一致的 provider adapter 后面,新的 LLM 工具就能加入同一套运行模型,而不是再增加一个例外。
向量引擎中转站适合放在这样的工程边界里:配置明确、key 分离、模型可审计、故障可对比。这样团队接入 API中转站时,更容易把注意力放在业务效果和运行质量上。
Top comments (0)