zerochat 7.3.0__py3-none-any.whl

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 (130) hide show
  1. zerochat-7.3.0.dist-info/METADATA +70 -0
  2. zerochat-7.3.0.dist-info/RECORD +130 -0
  3. zerochat-7.3.0.dist-info/WHEEL +5 -0
  4. zerochat-7.3.0.dist-info/entry_points.txt +2 -0
  5. zerochat-7.3.0.dist-info/licenses/LICENSE +34 -0
  6. zerochat-7.3.0.dist-info/top_level.txt +2 -0
  7. zerochat.py +2004 -0
  8. zerochat_runtime/__init__.py +1 -0
  9. zerochat_runtime/assets/css/base.css +199 -0
  10. zerochat_runtime/assets/css/components/composer.css +1258 -0
  11. zerochat_runtime/assets/css/components/debug.css +777 -0
  12. zerochat_runtime/assets/css/components/header.css +97 -0
  13. zerochat_runtime/assets/css/components/icons.css +39 -0
  14. zerochat_runtime/assets/css/components/markdown.css +685 -0
  15. zerochat_runtime/assets/css/components/messages.css +473 -0
  16. zerochat_runtime/assets/css/components/modals.css +1319 -0
  17. zerochat_runtime/assets/css/components/sidebar.css +622 -0
  18. zerochat_runtime/assets/css/components/tools.css +1223 -0
  19. zerochat_runtime/assets/css/layout.css +171 -0
  20. zerochat_runtime/assets/css/print.css +120 -0
  21. zerochat_runtime/assets/css/styles.css +18 -0
  22. zerochat_runtime/assets/css/theme-overrides.css +555 -0
  23. zerochat_runtime/assets/css/tokens.css +165 -0
  24. zerochat_runtime/assets/help/architecture.html +247 -0
  25. zerochat_runtime/assets/help/composer.html +134 -0
  26. zerochat_runtime/assets/help/conversations.html +105 -0
  27. zerochat_runtime/assets/help/debug.html +108 -0
  28. zerochat_runtime/assets/help/en/architecture.html +247 -0
  29. zerochat_runtime/assets/help/en/composer.html +134 -0
  30. zerochat_runtime/assets/help/en/conversations.html +104 -0
  31. zerochat_runtime/assets/help/en/debug.html +105 -0
  32. zerochat_runtime/assets/help/en/gemini-free.html +39 -0
  33. zerochat_runtime/assets/help/en/index.html +191 -0
  34. zerochat_runtime/assets/help/en/learning.html +71 -0
  35. zerochat_runtime/assets/help/en/mcp.html +143 -0
  36. zerochat_runtime/assets/help/en/openrouter-free.html +39 -0
  37. zerochat_runtime/assets/help/en/profiles.html +173 -0
  38. zerochat_runtime/assets/help/en/rag.html +130 -0
  39. zerochat_runtime/assets/help/en/reasoning-telemetry.html +108 -0
  40. zerochat_runtime/assets/help/en/tools-agent.html +133 -0
  41. zerochat_runtime/assets/help/en/webllm.html +355 -0
  42. zerochat_runtime/assets/help/gemini-free.html +39 -0
  43. zerochat_runtime/assets/help/help.css +604 -0
  44. zerochat_runtime/assets/help/help.js +51 -0
  45. zerochat_runtime/assets/help/index.html +194 -0
  46. zerochat_runtime/assets/help/learning.html +71 -0
  47. zerochat_runtime/assets/help/mcp.html +143 -0
  48. zerochat_runtime/assets/help/openrouter-free.html +39 -0
  49. zerochat_runtime/assets/help/profiles.html +174 -0
  50. zerochat_runtime/assets/help/rag.html +130 -0
  51. zerochat_runtime/assets/help/reasoning-telemetry.html +111 -0
  52. zerochat_runtime/assets/help/tools-agent.html +133 -0
  53. zerochat_runtime/assets/help/webllm.html +359 -0
  54. zerochat_runtime/assets/js/agent-core.js +1553 -0
  55. zerochat_runtime/assets/js/api.js +844 -0
  56. zerochat_runtime/assets/js/app.js +2558 -0
  57. zerochat_runtime/assets/js/attachments.js +220 -0
  58. zerochat_runtime/assets/js/charts.js +338 -0
  59. zerochat_runtime/assets/js/chat-engine.js +566 -0
  60. zerochat_runtime/assets/js/config-store.js +207 -0
  61. zerochat_runtime/assets/js/context-manager.js +584 -0
  62. zerochat_runtime/assets/js/conversation-service.js +484 -0
  63. zerochat_runtime/assets/js/cookies.js +688 -0
  64. zerochat_runtime/assets/js/data-reset-service.js +136 -0
  65. zerochat_runtime/assets/js/debug.js +508 -0
  66. zerochat_runtime/assets/js/defaults.js +16 -0
  67. zerochat_runtime/assets/js/export.js +179 -0
  68. zerochat_runtime/assets/js/file-parser.js +2088 -0
  69. zerochat_runtime/assets/js/generation-controller.js +417 -0
  70. zerochat_runtime/assets/js/i18n.js +1483 -0
  71. zerochat_runtime/assets/js/icons.js +156 -0
  72. zerochat_runtime/assets/js/ingestionEngine.js +436 -0
  73. zerochat_runtime/assets/js/markdown.js +588 -0
  74. zerochat_runtime/assets/js/mcp.js +1271 -0
  75. zerochat_runtime/assets/js/message-turns.js +62 -0
  76. zerochat_runtime/assets/js/profile-backup.js +118 -0
  77. zerochat_runtime/assets/js/profile-export-bundle.js +481 -0
  78. zerochat_runtime/assets/js/profile-repository.js +213 -0
  79. zerochat_runtime/assets/js/providers-webllm.js +486 -0
  80. zerochat_runtime/assets/js/providers.js +1560 -0
  81. zerochat_runtime/assets/js/rag-index.js +295 -0
  82. zerochat_runtime/assets/js/rag-service.js +530 -0
  83. zerochat_runtime/assets/js/rag-ui.js +997 -0
  84. zerochat_runtime/assets/js/ragStorage.js +676 -0
  85. zerochat_runtime/assets/js/sandbox.js +449 -0
  86. zerochat_runtime/assets/js/state.js +704 -0
  87. zerochat_runtime/assets/js/storage-db.js +144 -0
  88. zerochat_runtime/assets/js/tool-cards.js +330 -0
  89. zerochat_runtime/assets/js/tool-security.js +653 -0
  90. zerochat_runtime/assets/js/tools/README.md +50 -0
  91. zerochat_runtime/assets/js/tools/builtin/agent-checkpoint.tool.js +212 -0
  92. zerochat_runtime/assets/js/tools/builtin/download-pdf.tool.js +111 -0
  93. zerochat_runtime/assets/js/tools/builtin/execute-javascript.tool.js +219 -0
  94. zerochat_runtime/assets/js/tools/builtin/fetch-web-page.tool.js +112 -0
  95. zerochat_runtime/assets/js/tools/builtin/list-documents.tool.js +117 -0
  96. zerochat_runtime/assets/js/tools/builtin/read-knowledge-chunk.tool.js +129 -0
  97. zerochat_runtime/assets/js/tools/builtin/read-knowledge-image.tool.js +95 -0
  98. zerochat_runtime/assets/js/tools/builtin/render-chart.tool.js +90 -0
  99. zerochat_runtime/assets/js/tools/builtin/search-knowledge-base.tool.js +124 -0
  100. zerochat_runtime/assets/js/tools/builtin/search-web.tool.js +125 -0
  101. zerochat_runtime/assets/js/tools/tool-manifest.js +53 -0
  102. zerochat_runtime/assets/js/tools/tool-runtime.js +77 -0
  103. zerochat_runtime/assets/js/ui-composer.js +319 -0
  104. zerochat_runtime/assets/js/ui-conversation.js +655 -0
  105. zerochat_runtime/assets/js/ui-dialogs.js +132 -0
  106. zerochat_runtime/assets/js/ui-generation-status.js +119 -0
  107. zerochat_runtime/assets/js/ui-inspector.js +804 -0
  108. zerochat_runtime/assets/js/ui-mcp.js +604 -0
  109. zerochat_runtime/assets/js/ui-profiles.js +824 -0
  110. zerochat_runtime/assets/js/ui-reasoning.js +146 -0
  111. zerochat_runtime/assets/js/ui-settings.js +825 -0
  112. zerochat_runtime/assets/js/ui-shell.js +215 -0
  113. zerochat_runtime/assets/js/ui-sidebar.js +429 -0
  114. zerochat_runtime/assets/js/ui-telemetry.js +404 -0
  115. zerochat_runtime/assets/js/ui-transfer.js +262 -0
  116. zerochat_runtime/assets/js/utils.js +216 -0
  117. zerochat_runtime/assets/js/vendor/orama.browser.js +11 -0
  118. zerochat_runtime/assets/js/web-browser.js +569 -0
  119. zerochat_runtime/assets/js/web-search.js +519 -0
  120. zerochat_runtime/assets/manifest.webmanifest +20 -0
  121. zerochat_runtime/assets/services/dummy_mcp/dummy_mcp_server.py +47 -0
  122. zerochat_runtime/assets/services/dummy_mcp/service.json +22 -0
  123. zerochat_runtime/assets/services/lsp/installer.json +9 -0
  124. zerochat_runtime/assets/services/lsp/service.json +22 -0
  125. zerochat_runtime/assets/services/memory/installer.json +9 -0
  126. zerochat_runtime/assets/services/memory/service.json +24 -0
  127. zerochat_runtime/assets/services/playwright/installer.json +10 -0
  128. zerochat_runtime/assets/services/playwright/service.json +39 -0
  129. zerochat_runtime/assets/sw.js +164 -0
  130. zerochat_runtime/assets/zerochat.html +671 -0
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Controlador del Ciclo de Vida de Generación e Inferencia (ZeroChat).
3
+ * Orquesta el envío de mensajes, streaming SSE, llamadas a herramientas,
4
+ * control de aborto, captura inmutable de configuración y finalización atómica.
5
+ */
6
+ (function (root, factory) {
7
+ if (typeof exports === 'object' && typeof module !== 'undefined') {
8
+ module.exports = factory();
9
+ } else {
10
+ root.ChatGenerationController = factory();
11
+ }
12
+ }(typeof self !== 'undefined' ? self : this, function () {
13
+ 'use strict';
14
+
15
+ function resolveDep(globalName, relPath) {
16
+ if (typeof window !== 'undefined' && window[globalName]) return window[globalName];
17
+ if (typeof globalThis !== 'undefined' && globalThis[globalName]) return globalThis[globalName];
18
+ if (typeof require !== 'undefined') {
19
+ try { return require(relPath); } catch (_) {}
20
+ }
21
+ return null;
22
+ }
23
+
24
+ function getState() { return resolveDep('ChatState', './state.js'); }
25
+ function getI18n() { return resolveDep('ChatI18n', './i18n.js'); }
26
+ function getIcons() { return resolveDep('ChatIcons', './icons.js'); }
27
+ function getAPI(options = {}) { return options.api || resolveDep('ChatAPI', './api.js'); }
28
+ function getEngine() { return resolveDep('ChatEngine', './chat-engine.js'); }
29
+ function getProfiles() { return resolveDep('ChatProfileRepository', './profile-repository.js'); }
30
+ function getAttachments() { return resolveDep('ChatAttachments', './attachments.js'); }
31
+ function getConversationService() { return resolveDep('ChatConversationService', './conversation-service.js'); }
32
+ function getUIConversation() { return resolveDep('ChatUIConversation', './ui-conversation.js'); }
33
+
34
+ function t(key, params) {
35
+ const I18n = getI18n();
36
+ return I18n?.t ? I18n.t(key, params) : key;
37
+ }
38
+
39
+ function getMsgIcon(name, size = 12) {
40
+ const Icons = getIcons();
41
+ if (Icons && Icons.has && Icons.has(name)) {
42
+ return Icons.get(name, { size, className: 'ui-icon' });
43
+ }
44
+ return '';
45
+ }
46
+
47
+ let currentAbortController = null;
48
+
49
+ function getCurrentAbortController() {
50
+ return currentAbortController;
51
+ }
52
+
53
+ function isGenerating() {
54
+ const State = getState();
55
+ return State?.get?.('streaming')?.isGenerating === true || currentAbortController !== null;
56
+ }
57
+
58
+ function handleStopGeneration() {
59
+ if (currentAbortController) {
60
+ currentAbortController.abort();
61
+ }
62
+ }
63
+
64
+ function finishGeneration(options = {}) {
65
+ const { skipSave = false, error = null, elements = {} } = options;
66
+ const State = getState();
67
+ const UIConversation = getUIConversation();
68
+
69
+ if (typeof options.removeTypingIndicator === 'function') {
70
+ options.removeTypingIndicator();
71
+ } else if (UIConversation?.removeTypingIndicator) {
72
+ UIConversation.removeTypingIndicator();
73
+ }
74
+
75
+ if (typeof options.clearGenerationStatus === 'function') {
76
+ options.clearGenerationStatus();
77
+ }
78
+
79
+ if (State?.set) {
80
+ if (error) {
81
+ State.set('streaming', { isGenerating: false, status: 'error', error: String(error) });
82
+ } else {
83
+ State.set('streaming', { isGenerating: false, status: 'idle', error: null });
84
+ }
85
+ }
86
+
87
+ if (elements.btnSend) elements.btnSend.disabled = false;
88
+ if (elements.btnStopStream) elements.btnStopStream.style.display = 'none';
89
+
90
+ currentAbortController = null;
91
+
92
+ if (elements.userInput) {
93
+ try { elements.userInput.focus(); } catch (_) {}
94
+ }
95
+
96
+ if (!skipSave && typeof options.saveCurrentSession === 'function') {
97
+ try {
98
+ options.saveCurrentSession();
99
+ } catch (saveErr) {
100
+ console.warn('[ZeroChat] Error al guardar sesión en finishGeneration:', saveErr);
101
+ }
102
+ }
103
+
104
+ if (typeof options.scrollToBottom === 'function') {
105
+ options.scrollToBottom();
106
+ } else if (UIConversation?.scrollToBottom) {
107
+ UIConversation.scrollToBottom(elements.messagesList);
108
+ }
109
+ }
110
+
111
+ function updateStatsDisplay(statsContainer, stats, options = {}) {
112
+ if (!stats || !statsContainer) return;
113
+ statsContainer.style.display = 'inline-flex';
114
+ const clockSvg = getMsgIcon('clock', 11);
115
+ const zapSvg = getMsgIcon('zap', 11);
116
+ const docSvg = getMsgIcon('file-text', 11);
117
+ const dbSvg = getMsgIcon('database', 11);
118
+
119
+ const cacheHtml = (stats.cachedTokens && stats.cachedTokens > 0)
120
+ ? `<span class="stat-sep stat-sep-4">•</span><span class="stat-item stat-item-cache" title="${t('stat_cache_title')}">${dbSvg} <span>${t('stat_cache_tokens', { tokens: stats.cachedTokens })}</span></span>`
121
+ : '';
122
+ statsContainer.innerHTML = `
123
+ <span class="stat-item stat-item-ttft" title="${t('stat_ttft_title')}">${clockSvg} <span>${t('stat_ttft', { sec: stats.ttftSec })}</span></span>
124
+ <span class="stat-sep stat-sep-1">•</span>
125
+ <span class="stat-item stat-item-speed" title="${t('stat_speed_title')}">${zapSvg} <span>${t('stat_speed', { speed: stats.tokensPerSec })}</span></span>
126
+ <span class="stat-sep stat-sep-2">•</span>
127
+ <span class="stat-item stat-item-total" title="${t('stat_total_time_title')}">${clockSvg} <span>${t('stat_total_time', { sec: stats.totalSec })}</span></span>
128
+ <span class="stat-sep stat-sep-3">•</span>
129
+ <span class="stat-item stat-item-tokens" title="${t('stat_tokens_title')}">${docSvg} <span>${t('stat_tokens', { tokens: stats.tokens })}</span></span>${cacheHtml}
130
+ `;
131
+
132
+ if (typeof options.updateConnectionTokensBadge === 'function') {
133
+ options.updateConnectionTokensBadge(stats);
134
+ }
135
+ }
136
+
137
+ async function handleSendMessage(options = {}) {
138
+ const elements = options.elements || {};
139
+ const State = getState();
140
+ const Attachments = getAttachments();
141
+ const API = getAPI(options);
142
+ const Engine = getEngine();
143
+ const Profiles = getProfiles();
144
+ const UIConversation = getUIConversation();
145
+ const ConversationService = getConversationService();
146
+
147
+ const rawText = (elements.userInput ? elements.userInput.value : (options.text || '')).trim();
148
+ const currentFiles = Attachments?.getFiles ? Attachments.getFiles() : [];
149
+ if ((!rawText && currentFiles.length === 0) || State?.isConversationBusy?.()) return;
150
+
151
+ const runtimeConfig = typeof options.getRuntimeConfig === 'function' ? options.getRuntimeConfig() : {};
152
+
153
+ const { fullPrompt, displayText, imageAttachments } = Attachments?.buildAttachmentsPayload
154
+ ? Attachments.buildAttachmentsPayload(rawText, currentFiles)
155
+ : { fullPrompt: rawText, displayText: rawText, imageAttachments: [] };
156
+
157
+ const appendUser = typeof options.appendUserMessage === 'function'
158
+ ? options.appendUserMessage
159
+ : (UIConversation?.appendUserMessage
160
+ ? (text, origPrompt, imgs) => UIConversation.appendUserMessage(elements.messagesList, elements.welcomeBanner, { text, originalPrompt: origPrompt, attachedImages: imgs }, options)
161
+ : () => 'msg_usr_' + Date.now());
162
+
163
+ const userMsgId = appendUser(displayText, rawText, imageAttachments);
164
+ const historyEntry = { id: userMsgId, role: 'user', content: fullPrompt };
165
+ if (imageAttachments.length > 0) {
166
+ historyEntry.images = imageAttachments;
167
+ }
168
+ if (State?.appendMessage) {
169
+ State.appendMessage(historyEntry);
170
+ }
171
+
172
+ const getCurrentSessionId = typeof options.getCurrentSessionId === 'function'
173
+ ? options.getCurrentSessionId
174
+ : (() => State?.getActiveSessionId?.() || '');
175
+ const generationSessionId = getCurrentSessionId();
176
+ let generationError = null;
177
+
178
+ if (elements.userInput) elements.userInput.value = '';
179
+ if (typeof options.clearAttachedFiles === 'function') options.clearAttachedFiles();
180
+ if (typeof options.autoResizeTextarea === 'function') options.autoResizeTextarea();
181
+ if (typeof options.closeReasoningMenu === 'function') options.closeReasoningMenu();
182
+
183
+ if (State?.set) {
184
+ State.set('streaming', { isGenerating: true, status: 'streaming', error: null });
185
+ }
186
+ if (typeof options.setGenerationStatus === 'function') {
187
+ options.setGenerationStatus({ phase: 'generating' });
188
+ }
189
+
190
+ currentAbortController = new AbortController();
191
+
192
+ if (typeof options.showTypingIndicator === 'function') {
193
+ options.showTypingIndicator();
194
+ } else if (UIConversation?.showTypingIndicator) {
195
+ UIConversation.showTypingIndicator(elements.messagesList);
196
+ }
197
+
198
+ const createPlaceholder = typeof options.createAssistantMessagePlaceholder === 'function'
199
+ ? options.createAssistantMessagePlaceholder
200
+ : (UIConversation?.createAssistantMessagePlaceholder
201
+ ? (id) => UIConversation.createAssistantMessagePlaceholder(elements.messagesList, id, options)
202
+ : () => ({ wrapper: null, row: null, content: null, actions: null, btnCopy: null, statsContainer: null, msgId: 'ast_' + Date.now() }));
203
+
204
+ const { wrapper, row, content, actions, btnCopy, statsContainer, msgId: assistantMsgId } = createPlaceholder();
205
+
206
+ if (typeof options.removeTypingIndicator === 'function') {
207
+ options.removeTypingIndicator();
208
+ } else if (UIConversation?.removeTypingIndicator) {
209
+ UIConversation.removeTypingIndicator();
210
+ }
211
+
212
+ const attachListeners = typeof options.attachListenersToContainer === 'function'
213
+ ? options.attachListenersToContainer
214
+ : (el => UIConversation?.attachListenersToContainer ? UIConversation.attachListenersToContainer(el) : null);
215
+
216
+ const scrollToBottom = typeof options.scrollToBottom === 'function'
217
+ ? options.scrollToBottom
218
+ : (() => UIConversation?.scrollToBottom ? UIConversation.scrollToBottom(elements.messagesList) : null);
219
+
220
+ if (!API?.streamChatCompletion) {
221
+ if (row?.classList?.add) row.classList.add('message-error');
222
+ if (content) content.innerHTML = 'Error: Chat API module not loaded.';
223
+ finishGeneration({ error: 'Error: Chat API module not loaded.', elements, ...options });
224
+ return;
225
+ }
226
+
227
+ if (!runtimeConfig.model || runtimeConfig.model.trim() === '') {
228
+ if (row?.classList?.add) row.classList.add('message-error');
229
+ if (content) {
230
+ content.innerHTML = `
231
+ <div style="display:flex; align-items:flex-start; gap:0.5rem;">
232
+ <span style="flex-shrink: 0; display: inline-flex; align-items: center; color: var(--error, #ef4444);">${getMsgIcon('alert-triangle', 18)}</span>
233
+ <div>
234
+ <strong>${t('err_no_model_title')}</strong>
235
+ <p style="margin-top: 0.25rem;">${t('err_no_model_desc', { url: runtimeConfig.apiUrl })}</p>
236
+ </div>
237
+ </div>
238
+ `;
239
+ }
240
+ if (actions) actions.style.display = 'inline-flex';
241
+ finishGeneration({ error: t('err_no_model_title'), elements, ...options });
242
+ return;
243
+ }
244
+
245
+ const activeRagBranchIds = Array.isArray(runtimeConfig.activeRagBranchIds)
246
+ ? runtimeConfig.activeRagBranchIds
247
+ : (runtimeConfig.activeRagBranchId ? [runtimeConfig.activeRagBranchId] : []);
248
+ const activeRagBranchId = activeRagBranchIds[0] || runtimeConfig.activeRagBranchId || '';
249
+
250
+ const RagService = resolveDep('ChatRagService', './rag-service.js');
251
+ if (activeRagBranchIds.length > 0 && RagService?.buildRagSystemContext) {
252
+ if (typeof options.setGenerationStatus === 'function') {
253
+ options.setGenerationStatus({ phase: 'rag', text: t('generation_status_rag') });
254
+ }
255
+ try {
256
+ const ragContext = await RagService.buildRagSystemContext(activeRagBranchIds, {
257
+ isCheckpointEnabled: !!(runtimeConfig.enabledTools && runtimeConfig.enabledTools.agent_checkpoint),
258
+ lang: runtimeConfig.language || 'es'
259
+ });
260
+ if (State?.set) State.set('agent', { loopWarning: false, ragSystemContext: ragContext });
261
+ } catch (err) {
262
+ console.warn('Error al cargar contexto inicial de RAG:', err);
263
+ if (typeof options.addDebugLog === 'function') {
264
+ options.addDebugLog('warning', `[RAG] Error al cargar contexto inicial: ${err?.message || String(err)}`);
265
+ }
266
+ if (State?.set) State.set('agent', { loopWarning: false, ragSystemContext: '' });
267
+ }
268
+ } else {
269
+ if (State?.set) State.set('agent', { loopWarning: false, ragSystemContext: '' });
270
+ }
271
+
272
+ const currentRagSystemContext = State?.get ? (State.get('agent')?.ragSystemContext || '') : '';
273
+
274
+ try {
275
+ const runner = Engine || resolveDep('ChatEngine', './chat-engine.js');
276
+ const activeProfile = Profiles?.load ? await Profiles.load(runtimeConfig.activeProfile?.id) : null;
277
+ const getChatHistory = typeof options.getChatHistory === 'function' ? options.getChatHistory : (() => State?.getMessages?.() || []);
278
+
279
+ const loopResult = await runner.executeAgentTurnLoop({
280
+ apiUrl: runtimeConfig.apiUrl,
281
+ apiType: runtimeConfig.apiType,
282
+ apiKey: activeProfile?.settings?.apiKey || '',
283
+ model: runtimeConfig.model,
284
+ temperature: runtimeConfig.temperature,
285
+ reasoningEffort: runtimeConfig.reasoningEffort || 'medium',
286
+ reasoningTransport: runtimeConfig.reasoningTransport || 'auto',
287
+ maxAgentTurns: runtimeConfig.maxAgentTurns ? Number(runtimeConfig.maxAgentTurns) : 15,
288
+ chatHistory: getChatHistory(),
289
+ appConfig: runtimeConfig,
290
+ assistantMsgId: assistantMsgId,
291
+ activeRagBranchId: activeRagBranchId,
292
+ activeRagBranchIds: activeRagBranchIds,
293
+ currentRagSystemContext: currentRagSystemContext,
294
+ signal: currentAbortController.signal,
295
+ container: content,
296
+
297
+ onBeforeRequest: runtimeConfig.enableDebugMessages ? async function ({ endpoint, headers, payload }) {
298
+ if (typeof options.openDebugInterceptorModal === 'function') {
299
+ return await options.openDebugInterceptorModal({ endpoint, headers, payload });
300
+ }
301
+ return { endpoint, headers, payload };
302
+ } : null,
303
+
304
+ onReasoningChunk: function (chunk) {
305
+ if (getCurrentSessionId() !== generationSessionId) return;
306
+ if (typeof options.addDebugLog === 'function') options.addDebugLog('thinking', chunk);
307
+ if (typeof options.setDebugStatus === 'function') options.setDebugStatus('streaming', t('debug_status_thinking'));
308
+ },
309
+
310
+ onGenerationStatus: function (status) {
311
+ if (getCurrentSessionId() !== generationSessionId) return;
312
+ if (typeof options.setGenerationStatus === 'function') options.setGenerationStatus(status);
313
+ },
314
+
315
+ onLog: function (type, txt) {
316
+ if (getCurrentSessionId() !== generationSessionId) return;
317
+ if (typeof options.addDebugLog === 'function') options.addDebugLog(type, txt);
318
+ },
319
+
320
+ onStats: function (stats) {
321
+ if (getCurrentSessionId() !== generationSessionId) return;
322
+ updateStatsDisplay(statsContainer, stats, options);
323
+ },
324
+
325
+ onChunk: function ({ turnIndex, fullText, delta, stats }) {
326
+ if (getCurrentSessionId() !== generationSessionId) return;
327
+ if (stats) updateStatsDisplay(statsContainer, stats, options);
328
+ scrollToBottom();
329
+ },
330
+
331
+ scrollToBottom: () => scrollToBottom(),
332
+ attachListeners: (el) => attachListeners(el)
333
+ });
334
+
335
+ if (getCurrentSessionId() !== generationSessionId) {
336
+ console.warn('[ZeroChat] Inferencia descartada por cambio de sesión.');
337
+ return;
338
+ }
339
+
340
+ if (loopResult && Array.isArray(loopResult.chatHistory) && State?.replaceMessages) {
341
+ State.replaceMessages(loopResult.chatHistory);
342
+ if (typeof options.setAssistantGroupMessageIds === 'function') {
343
+ options.setAssistantGroupMessageIds(wrapper, getChatHistory());
344
+ } else if (ConversationService?.setAssistantGroupMessageIds) {
345
+ ConversationService.setAssistantGroupMessageIds(wrapper, getChatHistory());
346
+ }
347
+ }
348
+
349
+ if (loopResult && loopResult.cancelled) {
350
+ if (wrapper && !wrapper.querySelector?.('.agentic-turn-block') && wrapper.parentNode) {
351
+ wrapper.parentNode.removeChild(wrapper);
352
+ }
353
+ if (typeof options.setDebugStatus === 'function') options.setDebugStatus('idle');
354
+ return;
355
+ }
356
+
357
+ if (loopResult && loopResult.loopDetected) {
358
+ if (State?.set) {
359
+ State.set('agent', { loopWarning: true });
360
+ }
361
+ if (typeof options.setDebugStatus === 'function') {
362
+ options.setDebugStatus('error', t('debug_status_loop_detected'));
363
+ }
364
+ if (typeof options.addDebugLog === 'function') {
365
+ options.addDebugLog('error', t('agent_loop_warning_notice'));
366
+ }
367
+ } else if (loopResult && loopResult.error) {
368
+ if (currentAbortController && currentAbortController.signal.aborted) {
369
+ return;
370
+ }
371
+ if (typeof options.setDebugStatus === 'function') options.setDebugStatus('error', t('debug_status_error'));
372
+ if (typeof options.addDebugLog === 'function') options.addDebugLog('error', loopResult.error.message || String(loopResult.error));
373
+ UIConversation.renderConnectionError({ row, content, actions },
374
+ loopResult.error.message || String(loopResult.error), runtimeConfig.apiUrl);
375
+ return;
376
+ }
377
+
378
+ if (loopResult && loopResult.stats) {
379
+ updateStatsDisplay(statsContainer, loopResult.stats, options);
380
+ if (typeof options.updateConnectionTokensBadge === 'function') {
381
+ options.updateConnectionTokensBadge(loopResult.stats, loopResult.contextDiagnostics, { forcePopover: true });
382
+ }
383
+ }
384
+
385
+ if (actions) actions.style.display = 'inline-flex';
386
+ UIConversation.bindMessageCopy(btnCopy, () => loopResult?.accumulatedMarkdown || loopResult?.finalAssistantText || '');
387
+
388
+ if (typeof options.setDebugStatus === 'function' && !loopResult?.loopDetected) {
389
+ options.setDebugStatus('done', t('debug_status_done'));
390
+ }
391
+ } catch (err) {
392
+ console.error('[ZeroChat] Error durante inferencia agéntica:', err);
393
+ const isAborted = Boolean(currentAbortController && currentAbortController.signal.aborted) || err?.name === 'AbortError';
394
+ if (!isAborted) {
395
+ generationError = err?.message || String(err);
396
+ if (typeof options.setDebugStatus === 'function') options.setDebugStatus('error', t('debug_status_error'));
397
+ if (typeof options.addDebugLog === 'function') options.addDebugLog('error', generationError);
398
+ UIConversation.renderConnectionError({ row, content, actions }, generationError, runtimeConfig.apiUrl);
399
+ }
400
+ } finally {
401
+ finishGeneration({
402
+ skipSave: getCurrentSessionId() !== generationSessionId,
403
+ error: generationError,
404
+ elements,
405
+ ...options
406
+ });
407
+ }
408
+ }
409
+
410
+ return {
411
+ handleSendMessage,
412
+ finishGeneration,
413
+ handleStopGeneration,
414
+ isGenerating,
415
+ getCurrentAbortController
416
+ };
417
+ }));