import { extensionName } from "../../utils/settings.js"; import { AgentManager } from "./agent-manager.js"; import { characters, this_chid, saveSettingsDebounced } from "/script.js"; import { world_names } from "/scripts/world-info.js"; import { getApiConfig, setApiConfig, testConnection, fetchModels } from "./api.js"; import { tools } from "./tools.js"; const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`; let isInitialized = false; let agentManager = null; let previousCharData = {}; let previousWorldData = {}; export async function openAutoCharCardWindow() { toastr.info("该功能正在开发,尚未完成,请耐心等待。"); return; if ($('#acc-window').length > 0) { $('#acc-window').show(); return; } if (!$('#acc-style').length) { $('') .attr('id', 'acc-style') .attr('rel', 'stylesheet') .attr('type', 'text/css') .attr('href', `${extensionFolderPath}/assets/auto-char-card/style.css`) .appendTo('head'); } try { const htmlContent = await $.get(`${extensionFolderPath}/assets/auto-char-card/index.html`); $('body').append(htmlContent); bindEvents(); agentManager = new AgentManager(); try { populateDropdowns(); loadApiSettings(); } catch (dataError) { console.error('[Amily2 AutoCharCard] Failed to load data:', dataError); toastr.warning('数据加载部分失败,请检查控制台。'); } isInitialized = true; console.log('[Amily2 AutoCharCard] Window initialized.'); } catch (error) { console.error('[Amily2 AutoCharCard] Failed to initialize window:', error); toastr.error(`无法加载自动构建器界面: ${error.message}`); $('#acc-window').remove(); } } function populateDropdowns() { const charSelect = $('#acc-target-char'); charSelect.empty().append(''); charSelect.append(''); characters.forEach((char, index) => { if (char) { const option = $(''); worldSelect.append(''); world_names.forEach(name => { worldSelect.append($(''); try { const models = await fetchModels(apiUrl, apiKey); select.empty().append(''); if (models.length === 0) { select.append(''); } else { models.forEach(model => { select.append(new Option(model, model)); }); toastr.success(`成功获取 ${models.length} 个模型`); } } catch (error) { console.error(`[AutoCharCard] Failed to fetch models for ${role}:`, error); toastr.error(`获取模型失败: ${error.message}`); select.empty().append(''); } finally { btn.prop('disabled', false).html(originalIcon); } }; $('#acc-executor-refresh-models').on('click', () => handleRefreshModels('executor')); $('#acc-reviewer-refresh-models').on('click', () => handleRefreshModels('reviewer')); $('#acc-executor-test').on('click', async function() { const btn = $(this); btn.prop('disabled', true).text('测试中...'); const success = await testConnection('executor'); btn.prop('disabled', false).text('测试连接'); if (success) toastr.success('模型 A 连接成功'); else toastr.error('模型 A 连接失败'); }); $('#acc-reviewer-test').on('click', async function() { const btn = $(this); btn.prop('disabled', true).text('测试中...'); const success = await testConnection('reviewer'); btn.prop('disabled', false).text('测试连接'); if (success) toastr.success('模型 B 连接成功'); else toastr.error('模型 B 连接失败'); }); } async function handleSendMessage() { const input = $('#acc-user-input'); const message = input.val().trim(); if (!message) return; if (!agentManager) { toastr.error('Agent 未初始化'); return; } const selectedCharId = $('#acc-target-char').val(); const selectedWorld = $('#acc-target-world').val(); if (!selectedCharId && selectedCharId !== '0') { toastr.warning('请先选择一个目标角色(或选择新建)'); return; } addMessage('user', message); input.val(''); $('#acc-send-btn').prop('disabled', true); $('#acc-status-indicator').removeClass('status-idle').addClass('status-working').text('工作中...'); try { agentManager.setContext(selectedCharId, selectedWorld); await agentManager.handleUserMessage( message, (content, role) => { addMessage(role, content); }, (toolName, args) => { updatePreview(toolName, args); } ); } catch (error) { console.error('Agent Error:', error); addMessage('system', `发生错误: ${error.message}`); } finally { $('#acc-send-btn').prop('disabled', false); $('#acc-status-indicator').removeClass('status-working').addClass('status-idle').text('空闲'); } } function addMessage(role, content) { const stream = $('#acc-chat-stream'); let displayContent = content; if (role === 'executor') { const tools = [ 'read_world_info', 'write_world_info_entry', 'create_world_book', 'read_character_card', 'update_character_card', 'edit_character_text', 'manage_first_message', 'use_tool' ]; const regex = new RegExp(`<(${tools.join('|')})>[\\s\\S]*?<\\/\\1>`, 'g'); displayContent = content.replace(regex, '').trim(); if (!displayContent) { displayContent = "(正在执行操作...)"; } } const escapedContent = displayContent .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); const formattedContent = escapedContent.replace(/\n/g, '
'); const msgDiv = $('
').addClass(`acc-message ${role}`); const avatarDiv = $('
').addClass('acc-avatar'); if (role === 'user') { avatarDiv.html(''); } else if (role === 'assistant') { avatarDiv.html(''); } else if (role === 'executor') { avatarDiv.html(''); } else if (role === 'system') { avatarDiv.html(''); } const contentDiv = $('
').addClass('acc-message-content'); msgDiv.append(avatarDiv); msgDiv.append(contentDiv); stream.append(msgDiv); if (role === 'assistant') { let i = 0; const speed = 2; const chunkSize = 5; function typeWriter() { if (i < formattedContent.length) { let chunk = ""; let count = 0; while (count < chunkSize && i < formattedContent.length) { if (formattedContent.charAt(i) === '<') { const tagEnd = formattedContent.indexOf('>', i); if (tagEnd !== -1) { chunk += formattedContent.substring(i, tagEnd + 1); i = tagEnd + 1; } else { chunk += formattedContent.charAt(i); i++; } } else { chunk += formattedContent.charAt(i); i++; } count++; } contentDiv.html(contentDiv.html() + chunk); stream.scrollTop(stream[0].scrollHeight); setTimeout(typeWriter, speed); } } typeWriter(); } else { contentDiv.html(formattedContent); stream.scrollTop(stream[0].scrollHeight); } } async function updatePreview(toolName, args) { const container = $('#acc-preview-container'); if (toolName === 'update_character_card' || toolName === 'edit_character_text') { const chid = args.chid !== undefined ? args.chid : $('#acc-target-char').val(); if (chid !== undefined) { const charData = await tools.read_character_card({ chid }); const char = JSON.parse(charData); let html = `

角色预览: ${char.name}

`; const fields = ['description', 'personality', 'first_mes', 'scenario']; fields.forEach(field => { const oldVal = previousCharData[field] || ''; const newVal = char[field] || ''; let contentHtml = newVal; if (oldVal !== newVal) { contentHtml = `
${newVal}
`; if (oldVal) { contentHtml += ``; } } html += `
${field}:
${contentHtml}
`; }); container.html(html); previousCharData = char; } } else if (toolName === 'write_world_info_entry') { const bookName = args.book_name || $('#acc-target-world').val(); if (bookName) { const entriesData = await tools.read_world_info({ book_name: bookName }); const entries = JSON.parse(entriesData); let html = `

世界书预览: ${bookName}

`; entries.forEach(entry => { let isModified = false; if (args.entries) { const modifiedEntries = Array.isArray(args.entries) ? args.entries : [args.entries]; isModified = modifiedEntries.some(e => e.key === entry.key || (Array.isArray(entry.keys) && entry.keys.includes(e.key))); } const contentClass = isModified ? 'diff-added' : ''; html += `
Key: ${Array.isArray(entry.keys) ? entry.keys.join(', ') : entry.key}
Content:
${entry.content}
`; }); container.html(html); } } }