Jan2000 commited on
Commit
ceebe88
·
unverified ·
1 Parent(s): d47fc8a

Create api.js

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