freegate 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +8 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freegate",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Free multi-provider LLM gateway with automatic failover. One OpenAI-compatible endpoint routes to 25 free models (Groq, Mistral, Gemini, NIM, OpenRouter, ZAI, Cerebras, DeepSeek). Never pay for LLMs.",
5
5
  "license": "MIT",
6
6
  "bin": {
package/server.js CHANGED
@@ -134,18 +134,6 @@ async function handleChatCompletion(req, res, body) {
134
134
  }
135
135
  return false;
136
136
  });
137
- // Debug: log raw message keys if any mention image/attachment/file
138
- if (!hasImage && Array.isArray(body.messages)) {
139
- for (const m of body.messages) {
140
- if (m && typeof m === 'object' && (m.image || m.attachments || m.file || (Array.isArray(m.content) && m.content.some(c => c && c.image)))) {
141
- logger.info('Vision debug: обнаружено изображение в другом формате', {
142
- keys: Object.keys(m).slice(0, 8),
143
- contentTypes: Array.isArray(m.content) ? m.content.map(c => c && c.type) : null,
144
- });
145
- break;
146
- }
147
- }
148
- }
149
137
  if (hasImage) {
150
138
  // Two-stage pipeline: try vision providers in order until one extracts text.
151
139
  const visionChain = ['gemini-vision', 'nim-vision', 'deepseek-vision']
@@ -162,7 +150,11 @@ async function handleChatCompletion(req, res, body) {
162
150
  role: 'user',
163
151
  content: [
164
152
  { type: 'text', text: 'Распознай и извлеки ВЕСЬ текст с изображения (ошибка, код, сообщение). Верни только содержимое, без комментариев. Если это код — верни код как есть.' },
165
- ...(Array.isArray(body.messages) ? body.messages.flatMap((m) => (Array.isArray(m.content) ? m.content.filter((c) => c.type === 'image_url' || c.type === 'image') : [])) : []),
153
+ ...(Array.isArray(body.messages) ? body.messages.flatMap((m) => (Array.isArray(m.content) ? m.content.filter((c) => c && (c.type === 'image_url' || c.type === 'image' || c.type === 'input_image')).map((c) => {
154
+ // Normalize any image part to the universal image_url format
155
+ const url = c.image_url?.url || c.image?.url || (c.image && typeof c.image === 'string' ? c.image : null) || c.url;
156
+ return url ? { type: 'image_url', image_url: { url } } : null;
157
+ }).filter(Boolean) : [])) : []),
166
158
  ],
167
159
  }],
168
160
  max_tokens: 2000,
@@ -183,8 +175,9 @@ async function handleChatCompletion(req, res, body) {
183
175
  body = {
184
176
  ...body,
185
177
  messages: userMsgs.map((m) => {
186
- if (Array.isArray(m.content) && m.content.some((c) => c.type === 'image_url' || c.type === 'image')) {
187
- return { role: 'user', content: `${m.content.find((c) => c.type === 'text')?.text || ''}\n\n[Содержимое скриншота]\n${cleaned}` };
178
+ if (Array.isArray(m.content) && m.content.some((c) => c && (c.type === 'image_url' || c.type === 'image' || c.type === 'input_image'))) {
179
+ const textPart = m.content.find((c) => c && c.type === 'text')?.text || '';
180
+ return { role: 'user', content: `${textPart}\n\n[Содержимое скриншота]\n${cleaned}` };
188
181
  }
189
182
  return m;
190
183
  }),