kingkont 0.10.5 → 0.10.7

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.
package/lib/providers.js CHANGED
@@ -170,7 +170,13 @@ async function chatiumWait(s, taskId, opts = {}) {
170
170
  }
171
171
  return d;
172
172
  }
173
- if (d.status === 'failed') throw new Error(d.error || 'chatium task failed');
173
+ if (d.status === 'failed') {
174
+ // Префиксуем чтобы было понятно что упало именно на стороне Chatium
175
+ // (а не локально). Без префикса юзер видит только cryptic JS-error
176
+ // от @start/sdk и думает что дело в renderer'е.
177
+ const msg = d.error || d.failedReason || 'failed';
178
+ throw new Error(`Chatium task failed: ${String(msg).slice(0, 400)}`);
179
+ }
174
180
  await new Promise(r => setTimeout(r, 1500));
175
181
  }
176
182
  throw new Error(`chatium poll timeout after ${timeoutMs / 1000}s`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kingkont",
3
- "version": "0.10.5",
3
+ "version": "0.10.7",
4
4
  "description": "KingKont · Chatium — нод-редактор сцен с AI-генерацией (картинки/видео/голос/SFX/музыка/текст)",
5
5
  "main": "main.js",
6
6
  "bin": {
package/renderer/chat.js CHANGED
@@ -294,12 +294,32 @@
294
294
  }
295
295
 
296
296
  // ============== LLM call + tool-loop ==============
297
+ // Превращаем messages в transcript-style prompt — работает независимо
298
+ // от того поддерживает ли backend multi-turn messages-формат. Если
299
+ // multi-turn вызывает 502 на Chatium (как было), transcript всё равно
300
+ // работает через стандартный prompt+system путь.
301
+ function messagesToPrompt(messages) {
302
+ const lines = [];
303
+ for (const m of messages) {
304
+ const role = m.role === 'assistant' ? 'ASSISTANT' : m.role === 'user' ? 'USER' : m.role.toUpperCase();
305
+ const content = typeof m.content === 'string'
306
+ ? m.content
307
+ : Array.isArray(m.content)
308
+ ? m.content.map(b => typeof b?.text === 'string' ? b.text : '').join('\n')
309
+ : '';
310
+ lines.push(`${role}: ${content}`);
311
+ }
312
+ lines.push('ASSISTANT:'); // подсказываем модели что её очередь
313
+ return lines.join('\n\n');
314
+ }
315
+
297
316
  async function callLLM(messages, system) {
317
+ const prompt = messagesToPrompt(messages);
298
318
  const body = {
299
- messages, system,
319
+ prompt, system,
300
320
  model: 'anthropic/claude-sonnet-4.6',
301
321
  };
302
- console.log('[chat] → POST /api/text', { messages: messages.length, hasSystem: !!system });
322
+ console.log('[chat] → POST /api/text', { promptLen: prompt.length, hasSystem: !!system });
303
323
  const r = await fetch('/api/text', {
304
324
  method: 'POST',
305
325
  headers: { 'Content-Type': 'application/json' },
@@ -316,20 +336,18 @@
316
336
  }
317
337
 
318
338
  // Превращает internal history → массив для /api/text. Используем
319
- // Anthropic-формат content-blocks (`[{type:'text', text:...}]`), потому
320
- // что Chatium-side @start/sdk требует именно его — со строкой content
321
- // падает «Cannot read properties of undefined (reading 'length')».
322
- // OpenRouter этот формат тоже принимает (универсальный путь).
339
+ // string-content (OpenAI-style) — это формат который понимают обе
340
+ // backend-имплементации (Chatium @start/sdk и OpenRouter напрямую).
341
+ // Раньше пробовали Anthropic content-blocks `[{type:'text', text}]`
342
+ // но это вызывало «Cannot read properties of undefined (reading 'length')»
343
+ // в @start/sdk. String-content универсальнее.
323
344
  function historyToMessages() {
324
345
  const out = [];
325
346
  for (const m of history) {
326
347
  if (m.role === 'system') continue;
327
348
  const content = String(m.content || '');
328
- if (!content) continue; // Anthropic не любит пустые messages
329
- out.push({
330
- role: m.role,
331
- content: [{ type: 'text', text: content }],
332
- });
349
+ if (!content) continue; // пустые messages не отправляем
350
+ out.push({ role: m.role, content });
333
351
  }
334
352
  return out;
335
353
  }
@@ -644,6 +644,31 @@
644
644
  $('newCloudProject')?.addEventListener('click', createNewCloudProject);
645
645
  $('openCloudProjects')?.addEventListener('click', openCloudProjectsModal);
646
646
  $('saveProjectCloud')?.addEventListener('click', saveCloudProject);
647
+ // ПКМ на «☁ Сохранить на сервер» — расширенное меню: «Сохранить как
648
+ // шаблон» (доступно ВСЕМ типам проектов, включая cloud — реализация
649
+ // в templates.js работает через filmHandle, cloudFs-shim тоже подходит).
650
+ $('saveProjectCloud')?.addEventListener('contextmenu', e => {
651
+ e.preventDefault();
652
+ e.stopPropagation();
653
+ const menu = document.getElementById('nodeMenu');
654
+ if (!menu) return;
655
+ menu.innerHTML = '';
656
+ const add = (label, fn) => {
657
+ const b = document.createElement('button');
658
+ b.textContent = label;
659
+ b.addEventListener('click', () => { menu.classList.add('hidden'); fn(); });
660
+ menu.appendChild(b);
661
+ };
662
+ add('☁ Сохранить на сервер', saveCloudProject);
663
+ add('💾 Сохранить как шаблон…', () => {
664
+ if (typeof saveCurrentProjectAsTemplate === 'function') saveCurrentProjectAsTemplate();
665
+ else alert('saveCurrentProjectAsTemplate недоступен');
666
+ });
667
+ if (typeof positionFloatingMenu === 'function') {
668
+ positionFloatingMenu(menu, e.clientX, e.clientY);
669
+ }
670
+ setTimeout(() => document.addEventListener('mousedown', () => menu.classList.add('hidden'), { once: true }), 0);
671
+ });
647
672
  setCloudButtonsVisibility();
648
673
  // Переинициализируем видимость кнопок раз в 5 сек — для случая когда юзер
649
674
  // login/logout в Chatium через настройки (нет других сигналов).
package/server.js CHANGED
@@ -166,7 +166,11 @@ async function handleText(req, res) {
166
166
  const body = await readJson(req);
167
167
  const r = await providers.generateText({ ...body, settings: getSettings() });
168
168
  send(res, 200, { text: r.text, model: r.model, cost: r.cost }, { 'X-Provider': r.provider });
169
- } catch (e) { sendError(res, e, 502); }
169
+ } catch (e) {
170
+ // Печатаем stack — иначе на клиенте не понятно где упало.
171
+ console.error('[/api/text] error:', e.stack || e.message || e);
172
+ sendError(res, e, 502);
173
+ }
170
174
  }
171
175
 
172
176
  async function handleTts(req, res) {