Aleksmorshen commited on
Commit
f0af5f7
·
verified ·
1 Parent(s): 7638bd5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +250 -198
app.py CHANGED
@@ -1,14 +1,13 @@
1
  import os
2
  import io
3
- import base64
4
  from flask import Flask, request, jsonify, Response
5
  from PIL import Image
6
  import google.generativeai as genai
7
- import numpy as np
8
 
9
  app = Flask(__name__)
10
 
11
- API_KEY_INTERNAL = os.environ.get("GEMINI_API_KEY", "YOUR_GOOGLE_GEMINI_API_KEY")
12
 
13
  html_template = """
14
  <!DOCTYPE html>
@@ -22,65 +21,68 @@ html_template = """
22
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
23
  <style>
24
  :root {
25
- --light-bg: #F9F9FB;
26
- --light-surface: #FFFFFF;
27
- --light-primary-text: #171717;
28
- --light-secondary-text: #6A6A6A;
29
- --light-border: #EAEAEA;
30
- --light-accent: #007AFF;
31
- --light-accent-hover: #0056B3;
32
  --light-user-bubble: #007AFF;
33
  --light-user-text: #FFFFFF;
34
-
35
- --dark-bg: #121212;
36
- --dark-surface: #1E1E1E;
37
- --dark-primary-text: #EAEAEA;
38
- --dark-secondary-text: #9E9E9E;
39
- --dark-border: #333333;
40
- --dark-accent: #0A84FF;
41
- --dark-accent-hover: #3B9EFF;
 
42
  --dark-user-bubble: #0A84FF;
43
  --dark-user-text: #FFFFFF;
 
 
 
 
 
 
44
 
45
  --bg-color: var(--light-bg);
46
- --surface-color: var(--light-surface);
47
- --text-primary: var(--light-primary-text);
48
- --text-secondary: var(--light-secondary-text);
49
- --border-color: var(--light-border);
50
- --accent-color: var(--light-accent);
51
- --accent-color-hover: var(--light-accent-hover);
52
  --user-bubble-color: var(--light-user-bubble);
53
  --user-text-color: var(--light-user-text);
 
 
 
 
 
 
54
  }
55
 
56
  @media (prefers-color-scheme: dark) {
57
  :root {
58
  --bg-color: var(--dark-bg);
59
- --surface-color: var(--dark-surface);
60
- --text-primary: var(--dark-primary-text);
61
- --text-secondary: var(--dark-secondary-text);
62
- --border-color: var(--dark-border);
63
- --accent-color: var(--dark-accent);
64
- --accent-color-hover: var(--dark-accent-hover);
65
  --user-bubble-color: var(--dark-user-bubble);
66
  --user-text-color: var(--dark-user-text);
 
 
 
 
 
 
67
  }
68
  }
69
 
70
- html, body {
71
- height: 100%;
72
- margin: 0;
73
- overflow: hidden;
74
  }
75
 
76
  body {
77
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
 
78
  background-color: var(--bg-color);
79
  color: var(--text-primary);
80
  display: flex;
81
- justify-content: center;
82
- align-items: center;
83
- line-height: 1.5;
84
  -webkit-font-smoothing: antialiased;
85
  -moz-osx-font-smoothing: grayscale;
86
  }
@@ -88,19 +90,16 @@ html_template = """
88
  .chat-container {
89
  display: flex;
90
  flex-direction: column;
91
- width: 100%;
92
  height: 100%;
93
- max-width: 1000px;
 
94
  margin: 0 auto;
95
- background-color: var(--surface-color);
96
- box-shadow: 0 4px 20px rgba(0,0,0,0.05);
97
- border-radius: 0;
98
- overflow: hidden;
99
  }
100
 
101
  .chat-header {
102
- padding: 18px 24px;
103
- background-color: var(--surface-color);
104
  border-bottom: 1px solid var(--border-color);
105
  text-align: center;
106
  flex-shrink: 0;
@@ -112,67 +111,77 @@ html_template = """
112
  margin: 0;
113
  color: var(--text-primary);
114
  }
115
-
116
- .chat-header p {
117
- font-size: 13px;
 
118
  color: var(--text-secondary);
119
- margin: 4px 0 0;
120
  }
121
 
122
  .chat-messages {
123
  flex-grow: 1;
124
  overflow-y: auto;
125
- padding: 24px;
126
  display: flex;
127
  flex-direction: column;
128
- gap: 18px;
129
  }
130
-
131
- .message {
132
  display: flex;
133
- max-width: 75%;
134
- flex-shrink: 0;
 
135
  }
136
 
137
- .message-bubble {
138
  padding: 12px 18px;
139
  border-radius: 22px;
 
140
  font-size: 16px;
141
  }
142
-
143
- .message.user {
144
  align-self: flex-end;
145
- flex-direction: row-reverse;
146
  }
147
 
148
- .message.user .message-bubble {
149
  background-color: var(--user-bubble-color);
150
  color: var(--user-text-color);
151
  border-bottom-right-radius: 6px;
152
  }
153
-
154
- .message.bot {
155
  align-self: flex-start;
 
156
  }
157
 
158
- .message.bot .message-bubble {
159
- background-color: var(--bg-color);
160
- color: var(--text-primary);
161
- border: 1px solid var(--border-color);
162
  border-bottom-left-radius: 6px;
163
  }
 
 
 
 
 
 
164
 
165
  .typing-indicator {
166
  display: flex;
167
  align-items: center;
168
  gap: 5px;
 
169
  }
170
 
171
  .typing-indicator span {
172
- width: 8px;
173
  height: 8px;
 
174
  background-color: var(--text-secondary);
175
  border-radius: 50%;
 
176
  animation: bounce 1.4s infinite ease-in-out both;
177
  }
178
 
@@ -184,77 +193,101 @@ html_template = """
184
  40% { transform: scale(1.0); }
185
  }
186
 
187
-
188
  .chat-input-area {
189
- padding: 16px 24px;
190
- border-top: 1px solid var(--border-color);
191
- background-color: var(--surface-color);
192
  flex-shrink: 0;
 
 
 
 
193
  }
194
-
195
- #chat-form {
196
  display: flex;
197
- align-items: center;
198
- gap: 12px;
199
  }
200
-
201
  #message-input {
202
  flex-grow: 1;
203
- padding: 14px 20px;
204
- border-radius: 24px;
205
- border: 1px solid var(--border-color);
206
- background-color: var(--bg-color);
207
  color: var(--text-primary);
 
208
  font-size: 16px;
 
 
 
209
  outline: none;
210
- transition: border-color 0.2s, box-shadow 0.2s;
211
  }
212
-
213
  #message-input:focus {
214
- border-color: var(--accent-color);
215
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-color) 20%, transparent);
216
  }
217
 
218
  #send-button {
219
- width: 48px;
220
- height: 48px;
221
  border: none;
222
- background-color: var(--accent-color);
223
  color: white;
 
 
224
  border-radius: 50%;
225
  cursor: pointer;
226
  display: flex;
227
  justify-content: center;
228
  align-items: center;
229
- transition: background-color 0.2s, transform 0.1s;
230
  flex-shrink: 0;
 
231
  }
232
-
 
 
 
 
 
 
233
  #send-button:hover {
234
- background-color: var(--accent-color-hover);
235
  }
236
 
237
  #send-button:active {
238
- transform: scale(0.95);
239
  }
240
 
241
  #send-button:disabled {
242
  background-color: var(--text-secondary);
243
  cursor: not-allowed;
244
- opacity: 0.7;
 
 
 
245
  }
246
 
247
- #send-button svg {
248
- width: 24px;
249
- height: 24px;
 
 
250
  }
251
 
252
- @media (min-width: 768px) {
253
- .chat-container {
254
- border-radius: 16px;
255
- height: 90vh;
256
- max-height: 800px;
257
- border: 1px solid var(--border-color);
 
 
 
 
 
 
 
 
 
 
258
  }
259
  }
260
  </style>
@@ -263,27 +296,25 @@ html_template = """
263
  <div class="chat-container">
264
  <header class="chat-header">
265
  <h1>SYNKRIS AI</h1>
266
- <p>Powered by Morshen Group</p>
267
  </header>
268
 
269
  <div class="chat-messages" id="chat-messages">
270
- <div class="message bot">
271
- <div class="message-bubble">
272
- Здравствуйте! Я — SYNKRIS AI 2.0, ваш персональный ассистент. Чем могу помочь?
273
  </div>
274
  </div>
275
  </div>
276
-
277
- <div class="chat-input-area">
278
- <form id="chat-form">
279
- <input type="text" id="message-input" placeholder="Введите ваше сообщение..." autocomplete="off" required>
280
- <button type="submit" id="send-button" aria-label="Отправить">
281
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
282
- <path d="M3.478 2.405a.75.75 0 00-.926.94l2.432 7.905H13.5a.75.75 0 010 1.5H4.984l-2.432 7.905a.75.75 0 00.926.94 60.519 60.519 0 0018.445-8.986.75.75 0 000-1.218A60.517 60.517 0 003.478 2.405z" />
283
- </svg>
284
  </button>
285
  </form>
286
- </div>
287
  </div>
288
 
289
  <script>
@@ -292,133 +323,154 @@ html_template = """
292
  const sendButton = document.getElementById('send-button');
293
  const chatMessages = document.getElementById('chat-messages');
294
 
295
- const addMessage = (message, sender) => {
296
- const messageDiv = document.createElement('div');
297
- messageDiv.className = `message ${sender}`;
298
-
299
- const bubbleDiv = document.createElement('div');
300
- bubbleDiv.className = 'message-bubble';
301
- bubbleDiv.textContent = message;
302
 
303
- messageDiv.appendChild(bubbleDiv);
304
- chatMessages.appendChild(messageDiv);
305
- chatMessages.scrollTop = chatMessages.scrollHeight;
306
- };
307
-
308
- const showTypingIndicator = () => {
309
- const typingDiv = document.createElement('div');
310
- typingDiv.className = 'message bot typing-indicator-container';
311
- typingDiv.innerHTML = `
312
- <div class="message-bubble">
313
- <div class="typing-indicator">
314
- <span></span><span></span><span></span>
315
- </div>
316
- </div>
317
- `;
318
- chatMessages.appendChild(typingDiv);
 
 
 
 
319
  chatMessages.scrollTop = chatMessages.scrollHeight;
320
  };
321
 
322
- const removeTypingIndicator = () => {
323
- const indicator = chatMessages.querySelector('.typing-indicator-container');
324
- if (indicator) {
325
- indicator.remove();
 
 
 
 
 
 
326
  }
327
  };
 
 
 
 
 
 
 
 
 
328
 
329
  chatForm.addEventListener('submit', async (e) => {
330
  e.preventDefault();
331
- const message = messageInput.value.trim();
332
- if (!message) return;
 
 
 
333
 
334
- addMessage(message, 'user');
335
  messageInput.value = '';
 
336
  sendButton.disabled = true;
337
-
338
- showTypingIndicator();
339
 
340
  try {
341
  const response = await fetch('/chat', {
342
  method: 'POST',
343
  headers: { 'Content-Type': 'application/json' },
344
- body: JSON.stringify({ message: message })
345
  });
346
 
347
- removeTypingIndicator();
348
-
349
  const result = await response.json();
 
 
350
 
351
  if (!response.ok) {
352
- throw new Error(result.error || 'Произошла ошибка сервера.');
353
  }
 
 
 
 
354
 
355
- addMessage(result.reply, 'bot');
356
  } catch (error) {
357
- removeTypingIndicator();
358
- addMessage(`Ошибка: ${error.message}`, 'bot');
 
 
359
  } finally {
360
  sendButton.disabled = false;
361
  messageInput.focus();
362
  }
363
  });
 
364
  </script>
365
  </body>
366
  </html>
367
  """
368
 
369
- def generate_ai_reply(user_message):
370
- try:
371
- genai.configure(api_key=API_KEY_INTERNAL)
372
- except Exception as e:
373
- raise ValueError(f"Не удалось настроить Google AI: {e}")
374
-
375
- system_prompt = (
376
- "You are SYNKRIS AI 2.0, a sophisticated and helpful AI assistant. "
377
- "You were developed by the synkris AI laboratory, which is a key part of the Morshen group. "
378
- "Your purpose is to assist users with their questions and tasks accurately and efficiently. "
379
- "You are capable of conversing in any language the user chooses. "
380
- "Maintain a professional, knowledgeable, and friendly tone."
381
- )
382
-
383
- try:
384
- model = genai.GenerativeModel(
385
- model_name='gemma-3-12b-it',
386
- system_instruction=system_prompt
387
- )
388
-
389
- chat = model.start_chat(history=[])
390
- response = chat.send_message(user_message)
391
-
392
- if hasattr(response, 'text'):
393
- return response.text
394
- else:
395
- if response.parts:
396
- return "".join(part.text for part in response.parts if hasattr(part, 'text'))
397
- else:
398
- raise ValueError("Не удалось получить текстовый ответ от модели.")
399
- except Exception as e:
400
- raise ValueError(f"Ошибка при взаимодействии с моделью ИИ: {e}")
401
-
402
-
403
  @app.route('/')
404
  def index():
405
  return Response(html_template, mimetype='text/html')
406
 
407
  @app.route('/chat', methods=['POST'])
408
  def handle_chat():
409
- data = request.get_json()
410
- if not data or 'message' not in data:
411
- return jsonify({"error": "Сообщение не найдено в запросе."}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
 
413
- user_message = data['message']
 
 
 
 
 
 
 
 
 
 
 
414
 
415
- try:
416
- ai_reply = generate_ai_reply(user_message)
417
- return jsonify({"reply": ai_reply})
418
- except ValueError as ve:
419
- return jsonify({"error": str(ve)}), 400
420
  except Exception as e:
421
- return jsonify({"error": f"Внутренняя ошибка сервера: {e}"}), 500
 
 
 
 
 
 
422
 
423
  if __name__ == '__main__':
424
  app.run(host='0.0.0.0', port=7860, debug=False)
 
1
  import os
2
  import io
3
+ import json
4
  from flask import Flask, request, jsonify, Response
5
  from PIL import Image
6
  import google.generativeai as genai
 
7
 
8
  app = Flask(__name__)
9
 
10
+ API_KEY_INTERNAL = "AIzaSyArKidc4o0MwbaCFKStlb2q2AwNg6Pnqns"
11
 
12
  html_template = """
13
  <!DOCTYPE html>
 
21
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
22
  <style>
23
  :root {
24
+ --light-bg: #F9F9F9;
25
+ --light-chat-bg: #FFFFFF;
 
 
 
 
 
26
  --light-user-bubble: #007AFF;
27
  --light-user-text: #FFFFFF;
28
+ --light-ai-bubble: #EFEFF4;
29
+ --light-ai-text: #000000;
30
+ --light-text-primary: #000000;
31
+ --light-text-secondary: #6D6D72;
32
+ --light-border: #E5E5EA;
33
+ --light-input-bg: #F0F0F0;
34
+
35
+ --dark-bg: #000000;
36
+ --dark-chat-bg: #1C1C1E;
37
  --dark-user-bubble: #0A84FF;
38
  --dark-user-text: #FFFFFF;
39
+ --dark-ai-bubble: #2C2C2E;
40
+ --dark-ai-text: #FFFFFF;
41
+ --dark-text-primary: #FFFFFF;
42
+ --dark-text-secondary: #8E8E93;
43
+ --dark-border: #3A3A3C;
44
+ --dark-input-bg: #2C2C2E;
45
 
46
  --bg-color: var(--light-bg);
47
+ --chat-bg-color: var(--light-chat-bg);
 
 
 
 
 
48
  --user-bubble-color: var(--light-user-bubble);
49
  --user-text-color: var(--light-user-text);
50
+ --ai-bubble-color: var(--light-ai-bubble);
51
+ --ai-text-color: var(--light-ai-text);
52
+ --text-primary: var(--light-text-primary);
53
+ --text-secondary: var(--light-text-secondary);
54
+ --border-color: var(--light-border);
55
+ --input-bg-color: var(--light-input-bg);
56
  }
57
 
58
  @media (prefers-color-scheme: dark) {
59
  :root {
60
  --bg-color: var(--dark-bg);
61
+ --chat-bg-color: var(--dark-chat-bg);
 
 
 
 
 
62
  --user-bubble-color: var(--dark-user-bubble);
63
  --user-text-color: var(--dark-user-text);
64
+ --ai-bubble-color: var(--dark-ai-bubble);
65
+ --ai-text-color: var(--dark-ai-text);
66
+ --text-primary: var(--dark-text-primary);
67
+ --text-secondary: var(--dark-text-secondary);
68
+ --border-color: var(--dark-border);
69
+ --input-bg-color: var(--dark-input-bg);
70
  }
71
  }
72
 
73
+ html {
74
+ height: -webkit-fill-available;
 
 
75
  }
76
 
77
  body {
78
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
79
+ margin: 0;
80
  background-color: var(--bg-color);
81
  color: var(--text-primary);
82
  display: flex;
83
+ flex-direction: column;
84
+ height: 100vh;
85
+ height: -webkit-fill-available;
86
  -webkit-font-smoothing: antialiased;
87
  -moz-osx-font-smoothing: grayscale;
88
  }
 
90
  .chat-container {
91
  display: flex;
92
  flex-direction: column;
 
93
  height: 100%;
94
+ max-width: 800px;
95
+ width: 100%;
96
  margin: 0 auto;
97
+ background-color: var(--chat-bg-color);
98
+ box-shadow: 0 0 20px rgba(0,0,0,0.05);
 
 
99
  }
100
 
101
  .chat-header {
102
+ padding: 15px 20px;
 
103
  border-bottom: 1px solid var(--border-color);
104
  text-align: center;
105
  flex-shrink: 0;
 
111
  margin: 0;
112
  color: var(--text-primary);
113
  }
114
+
115
+ .chat-header span {
116
+ font-size: 12px;
117
+ font-weight: 500;
118
  color: var(--text-secondary);
 
119
  }
120
 
121
  .chat-messages {
122
  flex-grow: 1;
123
  overflow-y: auto;
124
+ padding: 20px;
125
  display: flex;
126
  flex-direction: column;
127
+ gap: 12px;
128
  }
129
+
130
+ .message-bubble {
131
  display: flex;
132
+ flex-direction: column;
133
+ max-width: 80%;
134
+ word-wrap: break-word;
135
  }
136
 
137
+ .message-content {
138
  padding: 12px 18px;
139
  border-radius: 22px;
140
+ line-height: 1.5;
141
  font-size: 16px;
142
  }
143
+
144
+ .user {
145
  align-self: flex-end;
146
+ align-items: flex-end;
147
  }
148
 
149
+ .user .message-content {
150
  background-color: var(--user-bubble-color);
151
  color: var(--user-text-color);
152
  border-bottom-right-radius: 6px;
153
  }
154
+
155
+ .ai {
156
  align-self: flex-start;
157
+ align-items: flex-start;
158
  }
159
 
160
+ .ai .message-content {
161
+ background-color: var(--ai-bubble-color);
162
+ color: var(--ai-text-color);
 
163
  border-bottom-left-radius: 6px;
164
  }
165
+
166
+ .error .message-content {
167
+ background-color: #FF3B301A;
168
+ color: #FF453A;
169
+ border: 1px solid #FF3B3080;
170
+ }
171
 
172
  .typing-indicator {
173
  display: flex;
174
  align-items: center;
175
  gap: 5px;
176
+ padding: 12px 18px;
177
  }
178
 
179
  .typing-indicator span {
 
180
  height: 8px;
181
+ width: 8px;
182
  background-color: var(--text-secondary);
183
  border-radius: 50%;
184
+ opacity: 0.4;
185
  animation: bounce 1.4s infinite ease-in-out both;
186
  }
187
 
 
193
  40% { transform: scale(1.0); }
194
  }
195
 
 
196
  .chat-input-area {
 
 
 
197
  flex-shrink: 0;
198
+ padding: 15px 20px;
199
+ padding-bottom: calc(15px + env(safe-area-inset-bottom));
200
+ border-top: 1px solid var(--border-color);
201
+ background-color: var(--chat-bg-color);
202
  }
203
+
204
+ .chat-input-form {
205
  display: flex;
206
+ align-items: flex-end;
207
+ gap: 10px;
208
  }
209
+
210
  #message-input {
211
  flex-grow: 1;
212
+ border: none;
213
+ padding: 14px 18px;
214
+ border-radius: 22px;
215
+ background-color: var(--input-bg-color);
216
  color: var(--text-primary);
217
+ font-family: inherit;
218
  font-size: 16px;
219
+ line-height: 1.4;
220
+ resize: none;
221
+ max-height: 150px;
222
  outline: none;
223
+ transition: box-shadow 0.2s;
224
  }
225
+
226
  #message-input:focus {
227
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--user-bubble-color) 25%, transparent);
 
228
  }
229
 
230
  #send-button {
 
 
231
  border: none;
232
+ background-color: var(--user-bubble-color);
233
  color: white;
234
+ width: 44px;
235
+ height: 44px;
236
  border-radius: 50%;
237
  cursor: pointer;
238
  display: flex;
239
  justify-content: center;
240
  align-items: center;
 
241
  flex-shrink: 0;
242
+ transition: background-color 0.2s, transform 0.1s;
243
  }
244
+
245
+ #send-button svg {
246
+ width: 22px;
247
+ height: 22px;
248
+ transition: transform 0.2s ease-in-out;
249
+ }
250
+
251
  #send-button:hover {
252
+ background-color: color-mix(in srgb, var(--user-bubble-color) 90%, #000);
253
  }
254
 
255
  #send-button:active {
256
+ transform: scale(0.9);
257
  }
258
 
259
  #send-button:disabled {
260
  background-color: var(--text-secondary);
261
  cursor: not-allowed;
262
+ }
263
+
264
+ #send-button:not(:disabled) svg {
265
+ transform: translateX(1px);
266
  }
267
 
268
+ @media (max-width: 800px) {
269
+ .chat-container {
270
+ border-radius: 0;
271
+ box-shadow: none;
272
+ }
273
  }
274
 
275
+ @media (max-width: 600px) {
276
+ .message-content {
277
+ font-size: 15px;
278
+ padding: 10px 15px;
279
+ }
280
+ #message-input {
281
+ font-size: 15px;
282
+ padding: 12px 16px;
283
+ }
284
+ #send-button {
285
+ width: 40px;
286
+ height: 40px;
287
+ }
288
+ #send-button svg {
289
+ width: 20px;
290
+ height: 20px;
291
  }
292
  }
293
  </style>
 
296
  <div class="chat-container">
297
  <header class="chat-header">
298
  <h1>SYNKRIS AI</h1>
299
+ <span>by Morshen Group</span>
300
  </header>
301
 
302
  <div class="chat-messages" id="chat-messages">
303
+ <div class="message-bubble ai" id="initial-message">
304
+ <div class="message-content">
305
+ Здравствуйте! Я — SYNKRIS AI 2.0. Чем могу помочь?
306
  </div>
307
  </div>
308
  </div>
309
+
310
+ <footer class="chat-input-area">
311
+ <form class="chat-input-form" id="chat-form">
312
+ <textarea id="message-input" placeholder="Введите ваше сообщение..." rows="1" required></textarea>
313
+ <button type="submit" id="send-button">
314
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24" height="24"><path d="M3.478 2.405a.75.75 0 00-.926.94l2.432 7.905H13.5a.75.75 0 010 1.5H4.984l-2.432 7.905a.75.75 0 00.926.94 60.519 60.519 0 0018.445-8.986.75.75 0 000-1.218A60.517 60.517 0 003.478 2.405z" /></svg>
 
 
315
  </button>
316
  </form>
317
+ </footer>
318
  </div>
319
 
320
  <script>
 
323
  const sendButton = document.getElementById('send-button');
324
  const chatMessages = document.getElementById('chat-messages');
325
 
326
+ let conversationHistory = [];
 
 
 
 
 
 
327
 
328
+ const addMessageToUI = (sender, message, type = 'text') => {
329
+ const messageBubble = document.createElement('div');
330
+ messageBubble.classList.add('message-bubble', sender);
331
+
332
+ const messageContent = document.createElement('div');
333
+
334
+ if (type === 'error') {
335
+ messageBubble.classList.add('error');
336
+ messageContent.textContent = `Ошибка: ${message}`;
337
+ } else if (type === 'loading') {
338
+ messageContent.classList.add('typing-indicator');
339
+ messageContent.innerHTML = '<span></span><span></span><span></span>';
340
+ messageBubble.id = 'loading-indicator';
341
+ } else {
342
+ messageContent.classList.add('message-content');
343
+ messageContent.textContent = message;
344
+ }
345
+
346
+ messageBubble.appendChild(messageContent);
347
+ chatMessages.appendChild(messageBubble);
348
  chatMessages.scrollTop = chatMessages.scrollHeight;
349
  };
350
 
351
+ const autoResizeTextarea = () => {
352
+ messageInput.style.height = 'auto';
353
+ let scrollHeight = messageInput.scrollHeight;
354
+ let maxHeight = parseInt(window.getComputedStyle(messageInput).maxHeight);
355
+ if (scrollHeight > maxHeight) {
356
+ messageInput.style.height = maxHeight + 'px';
357
+ messageInput.style.overflowY = 'auto';
358
+ } else {
359
+ messageInput.style.height = scrollHeight + 'px';
360
+ messageInput.style.overflowY = 'hidden';
361
  }
362
  };
363
+
364
+ messageInput.addEventListener('input', autoResizeTextarea);
365
+
366
+ messageInput.addEventListener('keydown', (e) => {
367
+ if (e.key === 'Enter' && !e.shiftKey) {
368
+ e.preventDefault();
369
+ chatForm.requestSubmit();
370
+ }
371
+ });
372
 
373
  chatForm.addEventListener('submit', async (e) => {
374
  e.preventDefault();
375
+ const userMessage = messageInput.value.trim();
376
+ if (!userMessage) return;
377
+
378
+ addMessageToUI('user', userMessage);
379
+ conversationHistory.push({ role: 'user', parts: [{ text: userMessage }] });
380
 
 
381
  messageInput.value = '';
382
+ autoResizeTextarea();
383
  sendButton.disabled = true;
384
+
385
+ addMessageToUI('ai', '', 'loading');
386
 
387
  try {
388
  const response = await fetch('/chat', {
389
  method: 'POST',
390
  headers: { 'Content-Type': 'application/json' },
391
+ body: JSON.stringify({ history: conversationHistory })
392
  });
393
 
 
 
394
  const result = await response.json();
395
+ const loadingIndicator = document.getElementById('loading-indicator');
396
+ if (loadingIndicator) loadingIndicator.remove();
397
 
398
  if (!response.ok) {
399
+ throw new Error(result.error || `Ошибка сервера: ${response.status}`);
400
  }
401
+
402
+ const aiMessage = result.text;
403
+ addMessageToUI('ai', aiMessage);
404
+ conversationHistory.push({ role: 'model', parts: [{ text: aiMessage }] });
405
 
 
406
  } catch (error) {
407
+ console.error("Fetch Error:", error);
408
+ const loadingIndicator = document.getElementById('loading-indicator');
409
+ if (loadingIndicator) loadingIndicator.remove();
410
+ addMessageToUI('ai', error.message, 'error');
411
  } finally {
412
  sendButton.disabled = false;
413
  messageInput.focus();
414
  }
415
  });
416
+
417
  </script>
418
  </body>
419
  </html>
420
  """
421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
  @app.route('/')
423
  def index():
424
  return Response(html_template, mimetype='text/html')
425
 
426
  @app.route('/chat', methods=['POST'])
427
  def handle_chat():
428
+ try:
429
+ data = request.get_json()
430
+ if not data or 'history' not in data:
431
+ return jsonify({"error": "Некорректный запрос. Отсутствует история диалога."}), 400
432
+
433
+ history = data['history']
434
+
435
+ genai.configure(api_key=API_KEY_INTERNAL)
436
+
437
+ system_instruction = {
438
+ "role": "user",
439
+ "parts": [{
440
+ "text": "Ты — SYNKRIS AI 2.0, большая языковая модель, разработанная AI лабораторией Synkris, принадлежащей компании Morshen Group. Веди диалог вежливо, будь полезным и отвечай на вопросы пользователя точно и по существу. Всегда отвечай на том же языке, на котором к тебе обратился пользователь."
441
+ }]
442
+ }
443
+
444
+ model_response_instruction = {
445
+ "role": "model",
446
+ "parts": [{
447
+ "text": "Здравствуйте! Я — SYNKRIS AI 2.0. Я готов помочь вам. На каком языке вы предпочитаете общаться?"
448
+ }]
449
+ }
450
+
451
+ full_history = [system_instruction, model_response_instruction] + history
452
 
453
+ model = genai.GenerativeModel('gemma-2-27b-it')
454
+
455
+ response = model.generate_content(full_history)
456
+
457
+ if not hasattr(response, 'text') or not response.text:
458
+ if response.prompt_feedback and response.prompt_feedback.block_reason:
459
+ reason = response.prompt_feedback.block_reason
460
+ return jsonify({"error": f"Ответ заблокирован из-за политики безопасности (Причина: {reason})."}), 400
461
+ else:
462
+ return jsonify({"error": "Модель не сгенерировала ответ. Попробуйте переформулировать запрос."}), 500
463
+
464
+ return jsonify({"text": response.text})
465
 
 
 
 
 
 
466
  except Exception as e:
467
+ error_message = str(e)
468
+ if "API key not valid" in error_message:
469
+ return jsonify({"error": "Неверный или неактивный ключ API. Проверьте конфигурацию на сервере."}), 500
470
+ elif "resource has been exhausted" in error_message:
471
+ return jsonify({"error": "Квота запросов к API исчерпана. Пожалуйста, попробуйте позже."}), 429
472
+ else:
473
+ return jsonify({"error": f"Произошла внутренняя ошибка сервера: {error_message}"}), 500
474
 
475
  if __name__ == '__main__':
476
  app.run(host='0.0.0.0', port=7860, debug=False)