| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- const config = require('./config');
- const REQUEST_TIMEOUT_MS = 120000;
- const REQUEST_TIMEOUT_IMG_MS = 180000;
- module.exports.REQUEST_TIMEOUT_MS = REQUEST_TIMEOUT_MS;
- module.exports.REQUEST_TIMEOUT_IMG_MS = REQUEST_TIMEOUT_IMG_MS;
- /**
- * run(action, ...args, options?)
- * options: { model?: string, timeoutMs?: number } 可选;未传 model 时各 request 使用 config 默认。
- * 例:run('text2text', '你好', { model: 'gpt-4o-mini' })
- * run('img2text', prompt, imageUrl, { model: 'gpt-4o', timeoutMs: 60000 })
- * run('img2text', prompt, [urlScreen, urlTpl], { model: 'gpt-5.4' }) // 多图,如 img-center ROI
- */
- function request(path, body, timeoutMs) {
- const baseUrl = (config.BASE_URL || '').replace(/\/$/, '');
- const url = path.startsWith('http') ? path : `${baseUrl}/${path.replace(/^\//, '')}`;
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- return fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${config.API_KEY || ''}`
- },
- body: JSON.stringify(body),
- signal: controller.signal
- })
- .then((res) => {
- clearTimeout(timeoutId);
- return res.json().catch(() => ({})).then((data) => {
- if (!res.ok) throw new Error(data.error?.message || data.error || `HTTP ${res.status}`);
- return data;
- });
- })
- .catch((e) => {
- clearTimeout(timeoutId);
- if (e.name === 'AbortError') throw new Error(`请求超时 (${timeoutMs / 1000} 秒)`);
- throw e;
- });
- }
- /** images/edits 等:multipart,勿设 Content-Type(由 fetch 带 boundary) */
- function requestMultipart (subPath, formData, timeoutMs) {
- const baseUrl = (config.BASE_URL || '').replace(/\/$/, '');
- const url = subPath.startsWith('http') ? subPath : `${baseUrl}/${String(subPath).replace(/^\//, '')}`;
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- return fetch(url, {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${config.API_KEY || ''}`,
- },
- body: formData,
- signal: controller.signal,
- })
- .then((res) => {
- clearTimeout(timeoutId);
- return res.json().catch(() => ({})).then((data) => {
- if (!res.ok) throw new Error(data.error?.message || data.error || `HTTP ${res.status}`);
- return data;
- });
- })
- .catch((e) => {
- clearTimeout(timeoutId);
- if (e.name === 'AbortError') throw new Error(`请求超时 (${timeoutMs / 1000} 秒)`);
- throw e;
- });
- }
- function doubaoRequest(path, body, timeoutMs) {
- const baseUrl = (config.DOUBAO_BASE_URL || '').replace(/\/$/, '');
- const url = path.startsWith('http') ? path : `${baseUrl}/${path.replace(/^\//, '')}`;
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
- return fetch(url, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${config.DOUBAO_API_KEY || ''}`
- },
- body: JSON.stringify(body),
- signal: controller.signal
- })
- .then((res) => {
- clearTimeout(timeoutId);
- return res.json().catch(() => ({})).then((data) => {
- if (!res.ok) throw new Error(data.error?.message || data.error || `HTTP ${res.status}`);
- return data;
- });
- })
- .catch((e) => {
- clearTimeout(timeoutId);
- if (e.name === 'AbortError') throw new Error(`请求超时 (${timeoutMs / 1000} 秒)`);
- throw e;
- });
- }
- const text2text = require('./request/text2text');
- const img2text = require('./request/img2text');
- const text2img = require('./request/text2img');
- const img2img = require('./request/img2img');
- const REQ = { text2text, img2text, text2img, img2img };
- /** 最后一个参数可为 { model?: string, timeoutMs?: number },未传 model 则用各 request 内默认 */
- function popRunOptions (args) {
- const last = args[args.length - 1];
- if (
- last != null &&
- typeof last === 'object' &&
- !Array.isArray(last) &&
- (Object.prototype.hasOwnProperty.call(last, 'model') ||
- Object.prototype.hasOwnProperty.call(last, 'timeoutMs'))
- ) {
- return { options: args.pop(), rest: args };
- }
- return { options: {}, rest: args };
- }
- async function run (action, ...args) {
- const { options, rest } = popRunOptions(args);
- const callArgs = rest;
- if (action.startsWith('doubao_')) {
- return await doRequest(action.substring(7), true, callArgs, options);
- }
- return await doRequest(action, false, callArgs, options);
- }
- /** 从 request 模块取参数,在 ai.js 内发请求 */
- async function doRequest (action, isDoubao, args, options = {}) {
- const req = REQ[action];
- if (!req) return { success: false, error: 'Unknown action: ' + action };
- if (isDoubao && (!config.DOUBAO_MODEL || !config.DOUBAO_MODEL.trim())) {
- return { success: false, error: '豆包未配置:请在 nodejs/ai/config.js 中设置 DOUBAO_MODEL 为你在火山引擎控制台创建的模型接入点 ID(endpoint ID),或设置环境变量 DOUBAO_MODEL' };
- }
- const path = req.path;
- const modelOverride =
- options.model != null && String(options.model).trim() !== ''
- ? String(options.model).trim()
- : undefined;
- const timeoutMs =
- options.timeoutMs != null && Number.isFinite(Number(options.timeoutMs))
- ? Number(options.timeoutMs)
- : req.timeoutMs;
- try {
- if (action === 'img2img' && !isDoubao && typeof req.buildFormData === 'function') {
- const form = req.buildFormData(args[0], args[1], modelOverride);
- const data = await requestMultipart(path, form, timeoutMs);
- return { success: true, data };
- }
- let body;
- if (action === 'text2text') {
- body = isDoubao
- ? req.getDoubaoBody(args[0], modelOverride)
- : req.getBody(args[0], modelOverride);
- } else if (action === 'text2img') {
- body = isDoubao
- ? req.getDoubaoBody(args[0], args[1], modelOverride)
- : req.getBody(args[0], args[1], modelOverride);
- } else {
- body = isDoubao
- ? req.getDoubaoBody(args[0], args[1], modelOverride)
- : req.getBody(args[0], args[1], modelOverride);
- }
- const data = isDoubao
- ? await doubaoRequest(path, body, timeoutMs)
- : await request(path, body, timeoutMs);
- return { success: true, data };
- } catch (e) {
- const msg = e && (e.message || String(e));
- const cause = e && e.cause && (e.cause.message || String(e.cause));
- const err = cause ? `${msg} (${cause})` : msg;
- return { success: false, error: err || '网络请求失败' };
- }
- }
- module.exports.run = run;
- module.exports.request = request;
- module.exports.REQUEST_TIMEOUT_MS = REQUEST_TIMEOUT_MS;
- module.exports.REQUEST_TIMEOUT_IMG_MS = REQUEST_TIMEOUT_IMG_MS;
|