File size: 10,078 Bytes
ceebe88 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
// --- START OF FILE api.js ---
// static/js/api.js
import * as state from './state.js';
import * as db from './db.js';
import * as chatUI from './ui/chat.js';
import * as toolUI from './ui/tools.js';
// *** START: MODIFIED - بازگرداندن تابع به حالت استاندارد ***
// این تابع اکنون در صورت اتمام زمان با موفقیت resolve میشود و فقط در صورت لغو شدن reject میکند.
export function interruptibleTimeout(duration, signal) {
return new Promise((resolve, reject) => {
if (signal.aborted) {
return reject(new DOMException('Aborted', 'AbortError'));
}
const timeoutId = setTimeout(resolve, duration);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new DOMException('Aborted', 'AbortError'));
});
});
}
// *** END: MODIFIED ***
export async function getBrowserFingerprint() {
const components = [navigator.userAgent, navigator.language, screen.width + 'x' + screen.height, new Date().getTimezoneOffset()];
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic";
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069";
ctx.fillText("a1b2c3d4e5f6g7h8i9j0_!@#$%^&*()", 2, 15);
components.push(canvas.toDataURL());
} catch (e) {
components.push("canvas-error");
}
const fingerprintString = components.join('~~~');
let hash = 0;
for (let i = 0; i < fingerprintString.length; i++) {
const char = fingerprintString.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0;
}
return 'fp_' + Math.abs(hash).toString(16);
}
export async function convertTextToFile(content, format, buttonElement) {
chatUI.showLoadingOnButton(buttonElement, true);
try {
const convertFormData = new FormData();
convertFormData.append('content', content);
convertFormData.append('format', format);
const convertResponse = await fetch('https://texttopdf-5irq.onrender.com/', { method: 'POST', body: convertFormData });
if (!convertResponse.ok) throw new Error(`خطا در ارتباط با سرور تبدیل: ${convertResponse.statusText}`);
const fileBlob = await convertResponse.blob();
const fileName = `alpha-export-${Date.now()}.${format}`;
const uploadFormData = new FormData();
uploadFormData.append('image', fileBlob, fileName);
const uploadResponse = await fetch('https://www.aisada.ir/hamed/upload.php', { method: 'POST', body: uploadFormData });
if (!uploadResponse.ok) {
const errorText = await uploadResponse.text().catch(() => `HTTP ${uploadResponse.status}`);
throw new Error(`آپلود فایل ساخته شده به سرور شما ناموفق بود: ${errorText}`);
}
const uploadData = await uploadResponse.json();
if (uploadData.success && uploadData.url) {
window.parent.postMessage({ type: 'OPEN_EXTERNAL_URL', url: uploadData.url }, '*');
} else {
throw new Error(uploadData.message || 'پاسخ سرور آپلود شما پس از ساخت فایل، نامعتبر بود.');
}
} catch (error) {
console.error('خطا در فرآیند تبدیل و آپلود فایل:', error);
alert(`متاسفانه در آمادهسازی فایل برای باز کردن خطایی رخ داد: ${error.message}`);
} finally {
chatUI.showLoadingOnButton(buttonElement, false);
}
}
export async function processAndUploadFile(file) {
const readFileAsBase64 = (file) => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(',')[1]);
reader.onerror = (error) => reject(error);
reader.readAsDataURL(file);
});
try {
const [fileId, base64Data] = await Promise.all([db.storeFile(file), readFileAsBase64(file)]);
const blobUrl = URL.createObjectURL(file);
return { id: fileId, blobUrl, base64Data, name: file.name, mimeType: file.type };
} catch (error) {
console.error("خطا در پردازش و ذخیره فایل:", error);
throw error;
}
}
export async function getChatStream(conversationHistory, signal) {
const activeChat = state.getActiveChat();
const messagesForApi = conversationHistory
.filter(msg => !msg.isTemporary)
.map(msg => {
const apiMsg = { role: msg.role, parts: [] };
msg.parts.forEach(part => {
if (part.text) apiMsg.parts.push({ type: 'text', text: part.text });
if (part.base64Data && part.mimeType) apiMsg.parts.push({ base64Data: part.base64Data, mimeType: part.mimeType });
});
return apiMsg;
}).filter(msg => msg.parts.length > 0);
const bodyPayload = {
messages: messagesForApi,
id: 'chat_' + Date.now(),
trigger: 'submit-message',
show_thoughts: activeChat ? activeChat.showThoughts : false
};
const response = await fetch('/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
signal: signal,
body: JSON.stringify(bodyPayload),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`خطای شبکه: ${response.status} - ${errorText}`);
}
return response;
}
export async function readStreamAndDisplay(response, modelBubbleOuterDivElement) {
let fullBotResponse = "";
const activeChat = state.getActiveChat();
const activeTool = state.getActiveTool();
try {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop();
for (const event of events) {
if (event.startsWith('data: ')) {
const dataString = event.substring(6);
if (dataString.trim() === '[DONE]') break;
try {
const jsonData = JSON.parse(dataString);
if (jsonData.type === 'thought' && jsonData.content) {
chatUI.streamThought(jsonData.content, modelBubbleOuterDivElement);
} else if (jsonData.choices?.[0]?.delta?.content) {
const textChunk = jsonData.choices[0].delta.content;
fullBotResponse += textChunk;
if (activeTool === 'deep-think' || activeTool === 'reasoning') {
chatUI.streamFinalText(fullBotResponse, modelBubbleOuterDivElement);
} else {
const hasThoughtsUI = modelBubbleOuterDivElement.querySelector('.thinking-panel-wrapper');
if (hasThoughtsUI) chatUI.streamFinalText(fullBotResponse, modelBubbleOuterDivElement);
else chatUI.streamFreeWsChunk(modelBubbleOuterDivElement, fullBotResponse);
}
} else if (jsonData.type === 'error') {
throw new Error(jsonData.message || 'خطای ناشناخته از سرور');
}
} catch (e) {
console.warn('خطا در پردازش قطعه JSON:', dataString, e);
}
}
}
}
} catch (error) {
throw error;
} finally {
if (fullBotResponse) {
const finalMessageObject = {
role: 'assistant',
parts: [{ text: fullBotResponse }],
toolUsed: activeTool,
wasGeneratedWithThoughts: activeChat ? activeChat.showThoughts : false
};
const tempMsgIndex = activeChat.messages.findIndex(m => m.isTemporary);
if (tempMsgIndex > -1) activeChat.messages[tempMsgIndex] = finalMessageObject;
else activeChat.messages.push(finalMessageObject);
if (activeTool === 'deep-think') {
chatUI.finalizeFinalText(modelBubbleOuterDivElement, fullBotResponse);
toolUI.hideDeepThinkPanel(modelBubbleOuterDivElement);
} else if (activeTool === 'reasoning') {
chatUI.finalizeFinalText(modelBubbleOuterDivElement, fullBotResponse);
toolUI.hideReasoningPanel(modelBubbleOuterDivElement);
} else {
const hasThoughtsUI = modelBubbleOuterDivElement.querySelector('.thinking-panel-wrapper');
if (hasThoughtsUI) chatUI.finalizeFinalText(modelBubbleOuterDivElement, fullBotResponse);
else chatUI.finalizeFreeWsMessage(modelBubbleOuterDivElement, fullBotResponse);
}
chatUI.updateMessageActions(modelBubbleOuterDivElement, finalMessageObject, false, true);
} else {
const tempMsgIndex = activeChat.messages.findIndex(m => m.isTemporary);
if (tempMsgIndex > -1) {
if(!modelBubbleOuterDivElement.querySelector('.message-content')?.innerText.includes('متوقف شد')) {
activeChat.messages.splice(tempMsgIndex, 1);
const tempElement = document.getElementById('chat-window').querySelector(`.message-entry[data-index="${tempMsgIndex}"]`);
if (tempElement) tempElement.remove();
}
}
}
}
}
|