img2text.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. const config = require('../config');
  2. const PATH = 'chat/completions';
  3. /** 读图较慢,与 ai.js REQUEST_TIMEOUT_IMG_MS 对齐 */
  4. const TIMEOUT_MS = 180000;
  5. /**
  6. * 将单 URL 或 URL 数组规范为字符串数组(OpenAI 兼容:同一 user 消息里多段 image_url = 多图)
  7. */
  8. function normalizeImageUrls (imageUrlOrUrls) {
  9. if (Array.isArray(imageUrlOrUrls)) {
  10. return imageUrlOrUrls.map((u) => String(u || '').trim()).filter(Boolean);
  11. }
  12. const s = String(imageUrlOrUrls || '').trim();
  13. return s ? [s] : [];
  14. }
  15. function buildUserContent (prompt, imageUrlOrUrls) {
  16. const urls = normalizeImageUrls(imageUrlOrUrls);
  17. const content = [{ type: 'text', text: String(prompt || '') }];
  18. for (const url of urls) {
  19. content.push({
  20. type: 'image_url',
  21. image_url: { url, detail: 'high' },
  22. });
  23. }
  24. return content;
  25. }
  26. function resolveModel (modelOverride) {
  27. return (
  28. (modelOverride && String(modelOverride).trim()) ||
  29. (config.IMG2TEXT_MODEL && String(config.IMG2TEXT_MODEL).trim()) ||
  30. config.MODEL_NAME ||
  31. 'gpt-4o'
  32. );
  33. }
  34. /** 截图 ROI(img-center):默认读 config.IMG_CENTER_MODEL,与 img2text 默认解耦 */
  35. function resolveImgCenterModel (modelOverride) {
  36. return (
  37. (modelOverride && String(modelOverride).trim()) ||
  38. (config.IMG_CENTER_MODEL && String(config.IMG_CENTER_MODEL).trim()) ||
  39. (config.IMG2TEXT_MODEL && String(config.IMG2TEXT_MODEL).trim()) ||
  40. config.MODEL_NAME ||
  41. 'gpt-5.4'
  42. );
  43. }
  44. /**
  45. * OpenAI 兼容 POST /v1/chat/completions
  46. * @param {string} prompt
  47. * @param {string|string[]} imageUrlOrUrls 单张 data URL / https URL,或多张(多附件)
  48. * @param {string} [modelOverride]
  49. */
  50. function getBody (prompt, imageUrlOrUrls, modelOverride) {
  51. const urls = normalizeImageUrls(imageUrlOrUrls);
  52. if (urls.length === 0) {
  53. throw new Error('img2text: 至少提供一张图片(字符串 URL 或 URL 数组)');
  54. }
  55. const model = resolveModel(modelOverride);
  56. const content = buildUserContent(prompt, imageUrlOrUrls);
  57. return {
  58. model,
  59. messages: [{ role: 'user', content }],
  60. // 勿与 max_completion_tokens 同时传:部分聚合网关(如 ChatAnywhere)会报错
  61. max_tokens: 1024,
  62. stream: false,
  63. };
  64. }
  65. function getDoubaoBody (prompt, imageUrlOrUrls, modelOverride) {
  66. const urls = normalizeImageUrls(imageUrlOrUrls);
  67. if (urls.length === 0) {
  68. throw new Error('img2text: 至少提供一张图片');
  69. }
  70. const model =
  71. (modelOverride && String(modelOverride).trim()) || config.DOUBAO_MODEL;
  72. const content = buildUserContent(prompt, imageUrlOrUrls);
  73. return {
  74. model,
  75. messages: [{ role: 'user', content }],
  76. max_tokens: 1024,
  77. stream: false,
  78. };
  79. }
  80. module.exports = {
  81. path: PATH,
  82. getBody,
  83. getDoubaoBody,
  84. /** img-center:无 override 时用 config.IMG_CENTER_MODEL(环境变量 IMG_CENTER_MODEL) */
  85. resolveImgCenterModel,
  86. timeoutMs: TIMEOUT_MS,
  87. /** 测试/调试:看 content 结构 */
  88. buildUserContent,
  89. normalizeImageUrls,
  90. };