sixfingerdev commited on
Commit
b3e1f6a
·
verified ·
1 Parent(s): c314317

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +976 -579
app.py CHANGED
@@ -1,622 +1,1019 @@
1
- # app.py - Sixfinger Groq Backend (Allam-2-7B Eklendi)
2
- import json
3
- import os
4
- from datetime import datetime
5
- from flask import Flask, request, jsonify, Response
6
- from groq import Groq
7
- import traceback
8
 
9
  app = Flask(__name__)
10
- app.config['JSON_AS_ASCII'] = False
11
-
12
- # ========== CONFIGURATION ==========
13
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
14
- PORT = int(os.getenv("PORT", 7860))
15
-
16
- if not GROQ_API_KEY:
17
- raise ValueError("❌ GROQ_API_KEY environment variable gerekli!")
18
-
19
- groq_client = Groq(api_key=GROQ_API_KEY)
20
-
21
- # ========== MODEL CATEGORIES ==========
22
-
23
- # FREE PLAN MODELS (Yüksek limitli)
24
- FREE_MODELS = {
25
- 'llama-8b-instant': {
26
- 'id': 'llama-3.1-8b-instant',
27
- 'description': 'Llama 3.1 8B Instant (Ultra Fast)',
28
- 'rpm': 30,
29
- 'rpd': 14400, # ⭐ EN YÜKSEK
30
- 'tpm': 14400,
31
- 'tpd': 6000000,
32
- 'size': '8B',
33
- 'speed': '⚡⚡⚡',
34
- 'plan_required': 'free',
35
- 'language': 'Multilingual'
36
- },
37
- 'allam-2-7b': {
38
- 'id': 'allam-2-7b',
39
- 'description': 'Allam 2 7B (Arabic/Turkish Optimized)', # ✅ EKLENDI
40
- 'rpm': 30,
41
- 'rpd': 300,
42
- 'tpm': 7000,
43
- 'tpd': 60000,
44
- 'size': '7B',
45
- 'speed': '⚡⚡',
46
- 'plan_required': 'free',
47
- 'language': 'Arabic/Turkish'
48
- }
49
  }
50
 
51
- # PAID PLAN MODELS (Güçlü ama düşük limit)
52
- PAID_MODELS = {
53
- 'llama-70b': {
54
- 'id': 'llama-3.3-70b-versatile',
55
- 'description': 'Llama 3.3 70B Versatile (Powerful)',
56
- 'rpm': 30,
57
- 'rpd': 1000,
58
- 'tpm': 1000,
59
- 'tpd': 12000000,
60
- 'size': '70B',
61
- 'speed': '⚡⚡',
62
- 'plan_required': 'starter',
63
- 'language': 'Multilingual'
64
- },
65
- 'qwen3-32b': {
66
- 'id': 'qwen/qwen3-32b',
67
- 'description': 'Qwen3 32B (Türkçe Optimized)',
68
- 'rpm': 60,
69
- 'rpd': 1000,
70
- 'tpm': 1000,
71
- 'tpd': 6000000,
72
- 'size': '32B',
73
- 'speed': '⚡⚡',
74
- 'plan_required': 'starter',
75
- 'language': 'Turkish/Chinese'
76
- },
77
- 'gpt-oss-120b': {
78
- 'id': 'openai/gpt-oss-120b',
79
- 'description': 'GPT OSS 120B (Giant)',
80
- 'rpm': 30,
81
- 'rpd': 1000,
82
- 'tpm': 1000,
83
- 'tpd': 8000000,
84
- 'size': '120B',
85
- 'speed': '⚡⚡',
86
- 'plan_required': 'pro',
87
- 'language': 'Multilingual'
88
- },
89
- 'llama-maverick-17b': {
90
- 'id': 'meta-llama/llama-4-maverick-17b-128e-instruct',
91
- 'description': 'Llama 4 Maverick 17B (Latest)',
92
- 'rpm': 30,
93
- 'rpd': 1000,
94
- 'tpm': 1000,
95
- 'tpd': 6000000,
96
- 'size': '17B',
97
- 'speed': '⚡⚡',
98
- 'plan_required': 'starter',
99
- 'language': 'Multilingual'
100
- },
101
- 'llama-scout-17b': {
102
- 'id': 'meta-llama/llama-4-scout-17b-16e-instruct',
103
- 'description': 'Llama 4 Scout 17B (Fast)',
104
- 'rpm': 30,
105
- 'rpd': 1000,
106
- 'tpm': 1000,
107
- 'tpd': 30000000, # Çok yüksek token limit!
108
- 'size': '17B',
109
- 'speed': '⚡⚡⚡',
110
- 'plan_required': 'starter',
111
- 'language': 'Multilingual'
112
- },
113
- 'gpt-oss-20b': {
114
- 'id': 'openai/gpt-oss-20b',
115
- 'description': 'GPT OSS 20B (Compact)',
116
- 'rpm': 30,
117
- 'rpd': 1000,
118
- 'tpm': 1000,
119
- 'tpd': 8000000,
120
- 'size': '20B',
121
- 'speed': '⚡⚡',
122
- 'plan_required': 'starter',
123
- 'language': 'Multilingual'
124
- },
125
- 'kimi-k2': {
126
- 'id': 'moonshotai/kimi-k2-instruct',
127
- 'description': 'Kimi K2 Instruct (Chinese)',
128
- 'rpm': 60,
129
- 'rpd': 1000,
130
- 'tpm': 1000,
131
- 'tpd': 10000000,
132
- 'size': 'Unknown',
133
- 'speed': '⚡⚡',
134
- 'plan_required': 'pro',
135
- 'language': 'Chinese/Multilingual'
136
- }
137
  }
138
 
139
- # TÜM MODELLER
140
- ALL_MODELS = {**FREE_MODELS, **PAID_MODELS}
 
 
 
 
 
141
 
142
- # DEFAULT MODEL PRIORITY (fallback için)
143
- MODEL_PRIORITY = [
144
- # FREE (önce en yüksek limitli)
145
- 'llama-8b-instant', # 14,400 RPD (FREE için ana)
146
- 'allam-2-7b', # 300 RPD (FREE için yedek)
147
-
148
- # PAID (güçlüden zayıfa)
149
- 'llama-70b', # 70B (en güçlü genel amaçlı)
150
- 'gpt-oss-120b', # 120B (giant)
151
- 'qwen3-32b', # 32B (Türkçe)
152
- 'llama-scout-17b', # 17B (hızlı + yüksek token limit)
153
- 'llama-maverick-17b', # 17B (son model)
154
- 'gpt-oss-20b', # 20B
155
- 'kimi-k2' # Chinese
156
- ]
157
-
158
- # ========== PLAN - MODEL MAPPING ==========
159
- PLAN_ALLOWED_MODELS = {
160
- 'free': [
161
- 'llama-8b-instant', # Ana model (14.4K/gün)
162
- 'allam-2-7b' # Yedek/alternatif (300/gün)
163
- ],
164
- 'starter': [
165
- 'llama-8b-instant',
166
- 'allam-2-7b',
167
- 'qwen3-32b', # Türkçe için
168
- 'llama-70b', # Güçlü model
169
- 'llama-maverick-17b',
170
- 'llama-scout-17b',
171
- 'gpt-oss-20b'
172
- ],
173
- 'pro': [
174
- 'llama-8b-instant',
175
- 'allam-2-7b',
176
- 'qwen3-32b',
177
- 'llama-70b',
178
- 'llama-maverick-17b',
179
- 'llama-scout-17b',
180
- 'gpt-oss-20b',
181
- 'gpt-oss-120b', # Giant model
182
- 'kimi-k2' # Chinese model
183
- ],
184
- 'plus': list(ALL_MODELS.keys()) # Tüm modeller
185
- }
186
-
187
- # ========== STATISTICS ==========
188
- stats = {
189
- 'total_requests': 0,
190
- 'successful_requests': 0,
191
- 'failed_requests': 0,
192
- 'model_usage': {},
193
- 'model_failures': {},
194
- 'fallback_count': 0,
195
- 'start_time': datetime.utcnow()
196
- }
197
-
198
- # ========== HELPER FUNCTIONS ==========
199
-
200
- def get_allowed_models(user_plan='free', preferred_model=None):
201
- """Kullanıcının planına göre izinli modelleri döndür"""
202
- allowed = PLAN_ALLOWED_MODELS.get(user_plan, ['llama-8b-instant'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
- # Preferred model varsa ve izinliyse öncelikli yap
205
- if preferred_model and preferred_model in allowed:
206
- models = [preferred_model] + [m for m in allowed if m != preferred_model]
207
- else:
208
- # MODEL_PRIORITY'ye göre sırala
209
- models = [m for m in MODEL_PRIORITY if m in allowed]
210
 
211
- return models
 
 
 
212
 
213
- def try_model(model_key, messages, max_tokens, temperature, top_p, stream=False):
214
- """Bir model'i dene"""
215
- if model_key not in ALL_MODELS:
216
- return None, f"Unknown model: {model_key}"
217
-
218
- model_info = ALL_MODELS[model_key]
219
- model_id = model_info['id']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
- try:
222
- if stream:
223
- response = groq_client.chat.completions.create(
224
- model=model_id,
225
- messages=messages,
226
- max_tokens=max_tokens,
227
- temperature=temperature,
228
- top_p=top_p,
229
- stream=True
230
- )
231
 
232
- stats['model_usage'][model_key] = stats['model_usage'].get(model_key, 0) + 1
233
- return response, None
234
- else:
235
- response = groq_client.chat.completions.create(
236
- model=model_id,
237
- messages=messages,
238
- max_tokens=max_tokens,
239
- temperature=temperature,
240
- top_p=top_p
241
- )
242
 
243
- stats['model_usage'][model_key] = stats['model_usage'].get(model_key, 0) + 1
244
- return response, None
245
-
246
- except Exception as e:
247
- error_msg = str(e)
248
- stats['model_failures'][model_key] = stats['model_failures'].get(model_key, 0) + 1
249
-
250
- print(f"❌ Model {model_key} failed: {error_msg}")
251
-
252
- if 'rate_limit' in error_msg.lower() or 'rate limit' in error_msg.lower():
253
- return None, "Rate limit exceeded"
254
- elif 'quota' in error_msg.lower():
255
- return None, "Quota exceeded"
256
- elif 'timeout' in error_msg.lower():
257
- return None, "Timeout"
258
- else:
259
- return None, f"Error: {error_msg[:150]}"
260
-
261
- def format_messages(prompt, system_prompt=None, history=None):
262
- """Format messages"""
263
- messages = []
264
-
265
- if system_prompt:
266
- messages.append({"role": "system", "content": system_prompt})
267
- else:
268
- # Default system prompt
269
- messages.append({
270
- "role": "system",
271
- "content": "Sen yardımsever ve bilgili bir AI asistanısın. Türkçe'yi mükemmel kullanırsın."
272
- })
273
-
274
- if history:
275
- messages.extend(history)
276
-
277
- messages.append({"role": "user", "content": prompt})
278
-
279
- return messages
280
-
281
- # ========== ROUTES ==========
282
-
283
- @app.route('/')
284
- def index():
285
- """API Documentation"""
286
- uptime = datetime.utcnow() - stats['start_time']
287
- uptime_str = str(uptime).split('.')[0]
288
 
289
- return jsonify({
290
- 'name': 'Sixfinger Groq Backend',
291
- 'version': '4.0.1',
292
- 'status': 'online',
293
- 'provider': 'Groq',
294
- 'uptime': uptime_str,
295
- 'models': {
296
- 'free': [
297
- {'key': k, 'rpd': v['rpd'], 'language': v['language']}
298
- for k, v in FREE_MODELS.items()
299
- ],
300
- 'paid': [
301
- {'key': k, 'rpd': v['rpd'], 'plan': v['plan_required']}
302
- for k, v in PAID_MODELS.items()
303
- ],
304
- 'total': len(ALL_MODELS)
305
- },
306
- 'stats': {
307
- 'total_requests': stats['total_requests'],
308
- 'successful': stats['successful_requests'],
309
- 'failed': stats['failed_requests'],
310
- 'success_rate': f"{(stats['successful_requests'] / max(stats['total_requests'], 1) * 100):.2f}%",
311
- 'fallback_count': stats['fallback_count']
312
- },
313
- 'endpoints': {
314
- 'chat': 'POST /api/chat',
315
- 'chat_stream': 'POST /api/chat/stream',
316
- 'models': 'GET /api/models',
317
- 'stats': 'GET /api/stats',
318
- 'health': 'GET /health'
319
- },
320
- 'headers': {
321
- 'X-Model': 'Preferred model key (optional)',
322
- 'X-User-Plan': 'User plan: free, starter, pro, plus (default: free)'
323
- }
324
- })
325
 
326
- @app.route('/api/models')
327
- def list_models():
328
- """List all models with details"""
329
- return jsonify({
330
- 'free_models': [
331
- {
332
- 'key': key,
333
- 'model_id': info['id'],
334
- 'description': info['description'],
335
- 'size': info['size'],
336
- 'speed': info['speed'],
337
- 'language': info['language'],
338
- 'limits': {
339
- 'rpm': info['rpm'],
340
- 'rpd': info['rpd'],
341
- 'tpm': info['tpm'],
342
- 'tpd': info['tpd']
343
- },
344
- 'usage_count': stats['model_usage'].get(key, 0),
345
- 'failure_count': stats['model_failures'].get(key, 0)
346
  }
347
- for key, info in FREE_MODELS.items()
348
- ],
349
- 'paid_models': [
350
- {
351
- 'key': key,
352
- 'model_id': info['id'],
353
- 'description': info['description'],
354
- 'size': info['size'],
355
- 'speed': info['speed'],
356
- 'language': info['language'],
357
- 'plan_required': info['plan_required'],
358
- 'limits': {
359
- 'rpm': info['rpm'],
360
- 'rpd': info['rpd'],
361
- 'tpm': info['tpm'],
362
- 'tpd': info['tpd']
363
- },
364
- 'usage_count': stats['model_usage'].get(key, 0),
365
- 'failure_count': stats['model_failures'].get(key, 0)
 
 
 
 
 
 
 
 
 
 
 
 
366
  }
367
- for key, info in PAID_MODELS.items()
368
- ],
369
- 'plan_permissions': PLAN_ALLOWED_MODELS
370
- })
371
-
372
- @app.route('/api/chat', methods=['POST'])
373
- def chat():
374
- """Chat endpoint (non-streaming)"""
375
- stats['total_requests'] += 1
376
-
377
- try:
378
- data = request.json
379
- if not data or 'prompt' not in data:
380
- stats['failed_requests'] += 1
381
- return jsonify({'error': 'prompt required'}), 400
382
-
383
- # Request parameters
384
- prompt = data['prompt']
385
- max_tokens = min(data.get('max_tokens', 1000), 4000)
386
- temperature = min(max(data.get('temperature', 0.7), 0.0), 2.0)
387
- top_p = min(max(data.get('top_p', 0.9), 0.1), 1.0)
388
- system_prompt = data.get('system_prompt')
389
- history = data.get('history', [])
390
-
391
- # Model selection
392
- preferred_model = request.headers.get('X-Model') or data.get('model')
393
- user_plan = request.headers.get('X-User-Plan', 'free').lower()
394
 
395
- # Validate plan
396
- if user_plan not in PLAN_ALLOWED_MODELS:
397
- user_plan = 'free'
 
 
398
 
399
- # Get allowed models
400
- models_to_try = get_allowed_models(user_plan, preferred_model)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
 
402
- # Format messages
403
- messages = format_messages(prompt, system_prompt, history)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
 
405
- # Try models
406
- attempts = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
- for i, model_key in enumerate(models_to_try):
409
- if i >= 5: # Max 5 attempts
410
- break
 
 
411
 
412
- print(f"🔄 Trying model {i+1}/{min(5, len(models_to_try))}: {model_key}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
414
- response, error = try_model(model_key, messages, max_tokens, temperature, top_p)
 
 
 
415
 
416
- attempts.append({
417
- 'model': model_key,
418
- 'success': response is not None,
419
- 'error': error
420
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
- if response:
423
- content = response.choices[0].message.content
424
- model_info = ALL_MODELS[model_key]
425
-
426
- stats['successful_requests'] += 1
427
- if i > 0:
428
- stats['fallback_count'] += 1
429
-
430
- result = {
431
- 'response': content,
432
- 'model': model_info['id'],
433
- 'model_key': model_key,
434
- 'model_size': model_info['size'],
435
- 'model_language': model_info['language'],
436
- 'attempts': i + 1,
437
- 'usage': {
438
- 'prompt_tokens': response.usage.prompt_tokens,
439
- 'completion_tokens': response.usage.completion_tokens,
440
- 'total_tokens': response.usage.total_tokens
441
- },
442
- 'parameters': {
443
- 'max_tokens': max_tokens,
444
- 'temperature': temperature,
445
- 'top_p': top_p
446
- }
447
- }
448
-
449
- if i > 0:
450
- result['fallback_attempts'] = attempts
451
 
452
- return jsonify(result)
453
-
454
- else:
455
- print(f" {model_key} failed: {error}")
456
- continue
457
-
458
- stats['failed_requests'] += 1
459
-
460
- return jsonify({
461
- 'error': 'All models failed',
462
- 'attempts': attempts,
463
- 'user_plan': user_plan,
464
- 'models_tried': [a['model'] for a in attempts]
465
- }), 503
466
-
467
- except Exception as e:
468
- stats['failed_requests'] += 1
469
- return jsonify({
470
- 'error': str(e),
471
- 'traceback': traceback.format_exc()
472
- }), 500
473
-
474
- @app.route('/api/chat/stream', methods=['POST'])
475
- def chat_stream():
476
- """Chat endpoint (streaming)"""
477
- stats['total_requests'] += 1
478
-
479
- try:
480
- data = request.json
481
- if not data or 'prompt' not in data:
482
- return jsonify({'error': 'prompt required'}), 400
483
-
484
- # Request parameters
485
- prompt = data['prompt']
486
- max_tokens = min(data.get('max_tokens', 1000), 4000)
487
- temperature = min(max(data.get('temperature', 0.7), 0.0), 2.0)
488
- top_p = min(max(data.get('top_p', 0.9), 0.1), 1.0)
489
- system_prompt = data.get('system_prompt')
490
- history = data.get('history', [])
491
 
492
- # Model selection
493
- preferred_model = request.headers.get('X-Model') or data.get('model')
494
- user_plan = request.headers.get('X-User-Plan', 'free').lower()
495
-
496
- if user_plan not in PLAN_ALLOWED_MODELS:
497
- user_plan = 'free'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
 
499
- # Get allowed models
500
- models_to_try = get_allowed_models(user_plan, preferred_model)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
- # Format messages
503
- messages = format_messages(prompt, system_prompt, history)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
 
505
- def generate():
506
- for i, model_key in enumerate(models_to_try):
507
- if i >= 5:
508
- break
509
-
510
- yield f"data: {json.dumps({'info': f'Trying model: {model_key}'}, ensure_ascii=False)}\n\n"
511
-
512
- stream_response, error = try_model(model_key, messages, max_tokens, temperature, top_p, stream=True)
513
-
514
- if stream_response:
515
- try:
516
- for chunk in stream_response:
517
- if chunk.choices[0].delta.content:
518
- text = chunk.choices[0].delta.content
519
- yield f"data: {json.dumps({'text': text}, ensure_ascii=False)}\n\n"
520
-
521
- stats['successful_requests'] += 1
522
- if i > 0:
523
- stats['fallback_count'] += 1
524
-
525
- model_info = ALL_MODELS[model_key]
526
- yield f"data: {json.dumps({'done': True, 'model': model_info['id'], 'model_key': model_key, 'attempts': i+1})}\n\n"
527
- return
528
-
529
- except Exception as e:
530
- yield f"data: {json.dumps({'warning': f'Stream error: {str(e)}'}, ensure_ascii=False)}\n\n"
531
- continue
532
-
533
- else:
534
- yield f"data: {json.dumps({'warning': f'{model_key} failed: {error}'}, ensure_ascii=False)}\n\n"
535
- continue
536
 
537
- stats['failed_requests'] += 1
538
- yield f"data: {json.dumps({'error': 'All models failed'})}\n\n"
 
 
 
 
 
 
539
 
540
- return Response(generate(), mimetype='text/event-stream')
541
-
542
- except Exception as e:
543
- stats['failed_requests'] += 1
544
- return jsonify({'error': str(e)}), 500
545
-
546
- @app.route('/health')
547
- def health():
548
- """Health check"""
549
- uptime = datetime.utcnow() - stats['start_time']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
550
 
551
- return jsonify({
552
- 'status': 'ok',
553
- 'uptime_seconds': int(uptime.total_seconds()),
554
- 'total_requests': stats['total_requests'],
555
- 'success_rate': f"{(stats['successful_requests'] / max(stats['total_requests'], 1) * 100):.2f}%",
556
- 'timestamp': datetime.utcnow().isoformat()
557
- })
558
 
559
- @app.route('/api/stats')
560
- def api_stats():
561
- """Detailed statistics"""
562
- uptime = datetime.utcnow() - stats['start_time']
563
-
564
- return jsonify({
565
- 'uptime_seconds': int(uptime.total_seconds()),
566
- 'total_requests': stats['total_requests'],
567
- 'successful_requests': stats['successful_requests'],
568
- 'failed_requests': stats['failed_requests'],
569
- 'success_rate': f"{(stats['successful_requests'] / max(stats['total_requests'], 1) * 100):.2f}%",
570
- 'fallback_count': stats['fallback_count'],
571
- 'model_usage': stats['model_usage'],
572
- 'model_failures': stats['model_failures'],
573
- 'timestamp': datetime.utcnow().isoformat()
574
- })
575
 
576
- # Error handlers
577
- @app.errorhandler(404)
578
- def not_found(e):
579
- return jsonify({'error': 'Not found'}), 404
580
-
581
- @app.errorhandler(500)
582
- def internal_error(e):
583
- return jsonify({'error': 'Internal error', 'detail': str(e)}), 500
584
-
585
- # CORS
586
- @app.after_request
587
- def after_request(response):
588
- response.headers.add('Access-Control-Allow-Origin', '*')
589
- response.headers.add('Access-Control-Allow-Headers', 'Content-Type,X-API-Key,X-Model,X-User-Plan')
590
- response.headers.add('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')
591
- return response
592
 
593
- # ========== MAIN ==========
 
 
 
 
 
 
594
 
595
- if __name__ == '__main__':
596
- print("\n" + "=" * 70)
597
- print("🚀 SIXFINGER GROQ BACKEND v4.0.1")
598
- print("=" * 70)
599
- print(f"✅ Groq API Key: {GROQ_API_KEY[:20]}...")
600
- print(f"📡 Port: {PORT}")
601
- print("=" * 70)
 
 
 
 
602
 
603
- print("\n🆓 FREE PLAN MODELS:")
604
- for key, info in FREE_MODELS.items():
605
- print(f" • {key}: {info['description']}")
606
- print(f" RPD: {info['rpd']:,} | TPD: {info['tpd']:,} | Language: {info['language']}")
607
 
608
- print("\n💎 PAID PLAN MODELS:")
609
- for key, info in PAID_MODELS.items():
610
- print(f" • {key}: {info['description']}")
611
- print(f" Plan: {info['plan_required']}+ | RPD: {info['rpd']:,} | Language: {info['language']}")
 
 
 
 
 
 
612
 
613
- print("\n📊 PLAN PERMISSIONS:")
614
- for plan, models in PLAN_ALLOWED_MODELS.items():
615
- print(f" • {plan.upper()}: {len(models)} modeller - {', '.join(models[:3])}...")
616
 
617
- print("\n" + "=" * 70)
618
- print(" Server ready!")
619
- print("📖 API Docs: http://0.0.0.0:7860")
620
- print("=" * 70 + "\n")
621
 
622
- app.run(host='0.0.0.0', port=PORT, debug=False, threaded=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ # Flask tabanlı Kayra Türkçe Dil Modeli Test Uygulaması
3
+ # sixfingerdev/kayra-1 ve sixfingerdev/kayra-1-exp modellerini test etmenizi sağlar.
4
+
5
+ from flask import Flask, request, jsonify, render_template_string
6
+ import torch
7
+ from transformers import AutoModelForCausalLM, AutoTokenizer
8
 
9
  app = Flask(__name__)
10
+
11
+ # ==================== CSS STİLLERİ ====================
12
+ STYLES = """
13
+ :root {
14
+ --primary: #2c3e50;
15
+ --secondary: #3498db;
16
+ --accent: #e74c3c;
17
+ --success: #27ae60;
18
+ --warning: #f39c12;
19
+ --light: #ecf0f1;
20
+ --dark: #1a252f;
21
+ --gray: #95a5a6;
22
+ --white: #ffffff;
23
+ --shadow: 0 4px 20px rgba(0,0,0,0.1);
24
+ --radius: 12px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
 
27
+ * {
28
+ margin: 0;
29
+ padding: 0;
30
+ box-sizing: border-box;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
+ body {
34
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
35
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
36
+ min-height: 100vh;
37
+ color: var(--dark);
38
+ line-height: 1.6;
39
+ }
40
 
41
+ .navbar {
42
+ background: var(--dark);
43
+ padding: 1rem 2rem;
44
+ display: flex;
45
+ justify-content: space-between;
46
+ align-items: center;
47
+ box-shadow: var(--shadow);
48
+ }
49
+
50
+ .navbar h1 {
51
+ color: var(--white);
52
+ font-size: 1.5rem;
53
+ font-weight: 600;
54
+ }
55
+
56
+ .nav-links {
57
+ display: flex;
58
+ gap: 1.5rem;
59
+ }
60
+
61
+ .nav-links a {
62
+ color: var(--light);
63
+ text-decoration: none;
64
+ font-weight: 500;
65
+ padding: 0.5rem 1rem;
66
+ border-radius: 6px;
67
+ transition: all 0.3s ease;
68
+ }
69
+
70
+ .nav-links a:hover,
71
+ .nav-links a.active {
72
+ background: var(--secondary);
73
+ color: var(--white);
74
+ }
75
+
76
+ .container {
77
+ max-width: 1000px;
78
+ margin: 2rem auto;
79
+ padding: 0 1rem;
80
+ }
81
+
82
+ .card {
83
+ background: var(--white);
84
+ border-radius: var(--radius);
85
+ box-shadow: var(--shadow);
86
+ padding: 2rem;
87
+ margin-bottom: 1.5rem;
88
+ }
89
+
90
+ .card-header {
91
+ border-bottom: 2px solid var(--light);
92
+ padding-bottom: 1rem;
93
+ margin-bottom: 1.5rem;
94
+ }
95
+
96
+ .card-header h2 {
97
+ color: var(--primary);
98
+ font-size: 1.5rem;
99
+ }
100
+
101
+ .card-header p {
102
+ color: var(--gray);
103
+ margin-top: 0.5rem;
104
+ }
105
+
106
+ label {
107
+ display: block;
108
+ font-weight: 600;
109
+ color: var(--primary);
110
+ margin-bottom: 0.5rem;
111
+ }
112
+
113
+ textarea {
114
+ width: 100%;
115
+ height: 140px;
116
+ padding: 1rem;
117
+ border: 2px solid var(--light);
118
+ border-radius: 8px;
119
+ font-size: 1rem;
120
+ font-family: inherit;
121
+ resize: vertical;
122
+ transition: border-color 0.3s ease;
123
+ }
124
+
125
+ textarea:focus {
126
+ outline: none;
127
+ border-color: var(--secondary);
128
+ }
129
+
130
+ select {
131
+ width: 100%;
132
+ padding: 0.8rem 1rem;
133
+ border: 2px solid var(--light);
134
+ border-radius: 8px;
135
+ font-size: 1rem;
136
+ font-family: inherit;
137
+ background: var(--white);
138
+ cursor: pointer;
139
+ margin-bottom: 1rem;
140
+ }
141
+
142
+ select:focus {
143
+ outline: none;
144
+ border-color: var(--secondary);
145
+ }
146
+
147
+ .btn {
148
+ display: inline-block;
149
+ padding: 1rem 2rem;
150
+ font-size: 1rem;
151
+ font-weight: 600;
152
+ border: none;
153
+ border-radius: 8px;
154
+ cursor: pointer;
155
+ transition: all 0.3s ease;
156
+ width: 100%;
157
+ text-align: center;
158
+ }
159
+
160
+ .btn-primary {
161
+ background: linear-gradient(135deg, var(--secondary), #2980b9);
162
+ color: var(--white);
163
+ }
164
+
165
+ .btn-primary:hover {
166
+ transform: translateY(-2px);
167
+ box-shadow: 0 6px 20px rgba(52, 152, 219, 0.4);
168
+ }
169
+
170
+ .btn-primary:disabled {
171
+ background: var(--gray);
172
+ cursor: not-allowed;
173
+ transform: none;
174
+ }
175
+
176
+ .form-group {
177
+ margin-bottom: 1.5rem;
178
+ }
179
+
180
+ .response-box {
181
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
182
+ border-radius: var(--radius);
183
+ padding: 1.5rem;
184
+ margin-top: 1.5rem;
185
+ display: none;
186
+ }
187
+
188
+ .response-box.active {
189
+ display: block;
190
+ }
191
+
192
+ .response-header {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 0.5rem;
196
+ margin-bottom: 1rem;
197
+ padding-bottom: 0.5rem;
198
+ border-bottom: 1px solid var(--gray);
199
+ }
200
+
201
+ .model-badge {
202
+ background: var(--secondary);
203
+ color: var(--white);
204
+ padding: 0.3rem 0.8rem;
205
+ border-radius: 20px;
206
+ font-size: 0.85rem;
207
+ font-weight: 600;
208
+ }
209
+
210
+ .response-content {
211
+ white-space: pre-wrap;
212
+ font-size: 1rem;
213
+ }
214
+
215
+ .response-content strong {
216
+ color: var(--primary);
217
+ }
218
+
219
+ .loading {
220
+ text-align: center;
221
+ padding: 2rem;
222
+ color: var(--gray);
223
+ display: none;
224
+ }
225
+
226
+ .loading.active {
227
+ display: block;
228
+ }
229
+
230
+ .spinner {
231
+ width: 40px;
232
+ height: 40px;
233
+ border: 4px solid var(--light);
234
+ border-top: 4px solid var(--secondary);
235
+ border-radius: 50%;
236
+ animation: spin 1s linear infinite;
237
+ margin: 0 auto 1rem;
238
+ }
239
+
240
+ @keyframes spin {
241
+ 0% { transform: rotate(0deg); }
242
+ 100% { transform: rotate(360deg); }
243
+ }
244
+
245
+ .error-box {
246
+ background: #fdeaea;
247
+ border: 1px solid var(--accent);
248
+ color: var(--accent);
249
+ padding: 1rem;
250
+ border-radius: 8px;
251
+ margin-top: 1rem;
252
+ }
253
+
254
+ .info-section {
255
+ margin-bottom: 2rem;
256
+ }
257
+
258
+ .info-section h3 {
259
+ color: var(--primary);
260
+ margin-bottom: 1rem;
261
+ padding-bottom: 0.5rem;
262
+ border-bottom: 2px solid var(--secondary);
263
+ }
264
+
265
+ .stats-grid {
266
+ display: grid;
267
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
268
+ gap: 1rem;
269
+ margin: 1rem 0;
270
+ }
271
+
272
+ .stat-card {
273
+ background: linear-gradient(135deg, var(--light), #dfe6e9);
274
+ padding: 1.5rem;
275
+ border-radius: 8px;
276
+ text-align: center;
277
+ }
278
+
279
+ .stat-value {
280
+ font-size: 2rem;
281
+ font-weight: 700;
282
+ color: var(--secondary);
283
+ }
284
+
285
+ .stat-label {
286
+ color: var(--gray);
287
+ font-size: 0.9rem;
288
+ margin-top: 0.3rem;
289
+ }
290
+
291
+ table {
292
+ width: 100%;
293
+ border-collapse: collapse;
294
+ margin: 1rem 0;
295
+ }
296
+
297
+ th, td {
298
+ padding: 0.8rem;
299
+ text-align: left;
300
+ border-bottom: 1px solid var(--light);
301
+ }
302
+
303
+ th {
304
+ background: var(--primary);
305
+ color: var(--white);
306
+ }
307
+
308
+ tr:hover {
309
+ background: var(--light);
310
+ }
311
+
312
+ .badge {
313
+ display: inline-block;
314
+ padding: 0.2rem 0.6rem;
315
+ border-radius: 4px;
316
+ font-size: 0.8rem;
317
+ font-weight: 600;
318
+ }
319
+
320
+ .badge-success {
321
+ background: #d4edda;
322
+ color: var(--success);
323
+ }
324
+
325
+ .badge-danger {
326
+ background: #f8d7da;
327
+ color: var(--accent);
328
+ }
329
+
330
+ .badge-warning {
331
+ background: #fff3cd;
332
+ color: #856404;
333
+ }
334
+
335
+ .badge-info {
336
+ background: #d1ecf1;
337
+ color: #0c5460;
338
+ }
339
+
340
+ .code-block {
341
+ background: var(--dark);
342
+ color: #a6e22e;
343
+ padding: 1rem;
344
+ border-radius: 8px;
345
+ overflow-x: auto;
346
+ font-family: 'Consolas', 'Monaco', monospace;
347
+ font-size: 0.9rem;
348
+ margin: 1rem 0;
349
+ }
350
+
351
+ .highlight {
352
+ background: linear-gradient(135deg, #fff3cd, #ffeeba);
353
+ padding: 1rem;
354
+ border-radius: 8px;
355
+ border-left: 4px solid var(--warning);
356
+ margin: 1rem 0;
357
+ }
358
+
359
+ .comparison-table tr:nth-child(even) {
360
+ background: #f8f9fa;
361
+ }
362
+
363
+ .footer {
364
+ text-align: center;
365
+ padding: 2rem;
366
+ color: var(--white);
367
+ opacity: 0.8;
368
+ }
369
+
370
+ .footer a {
371
+ color: var(--white);
372
+ }
373
+
374
+ @media (max-width: 768px) {
375
+ .navbar {
376
+ flex-direction: column;
377
+ gap: 1rem;
378
+ }
379
 
380
+ .stats-grid {
381
+ grid-template-columns: 1fr;
382
+ }
 
 
 
383
 
384
+ .two-column {
385
+ grid-template-columns: 1fr;
386
+ }
387
+ }
388
 
389
+ .two-column {
390
+ display: grid;
391
+ grid-template-columns: 1fr 1fr;
392
+ gap: 1rem;
393
+ }
394
+ """
395
+
396
+ # ==================== ANA SAYFA HTML ====================
397
+ INDEX_HTML = """
398
+ <!DOCTYPE html>
399
+ <html lang="tr">
400
+ <head>
401
+ <meta charset="UTF-8">
402
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
403
+ <title>Kayra - Türkçe Dil Modeli Test</title>
404
+ <style>{{ styles }}</style>
405
+ </head>
406
+ <body>
407
+ <nav class="navbar">
408
+ <h1>Kayra Türkçe Dil Modeli</h1>
409
+ <div class="nav-links">
410
+ <a href="/" class="active">Test</a>
411
+ <a href="/info">Bilgi</a>
412
+ </div>
413
+ </nav>
414
 
415
+ <div class="container">
416
+ <div class="card">
417
+ <div class="card-header">
418
+ <h2>Model Test Arayüzü</h2>
419
+ <p>Küçük ölçekli Türkçe dil modellerini karşılaştırmalı olarak test edin.</p>
420
+ </div>
 
 
 
 
421
 
422
+ <div class="form-group">
423
+ <label for="prompt">Soru veya mesajınız:</label>
424
+ <textarea id="prompt" placeholder="Örnek: Türkiye'nin başkenti neresidir?"></textarea>
425
+ </div>
 
 
 
 
 
 
426
 
427
+ <div class="form-group">
428
+ <label for="model">Model seçin:</label>
429
+ <select id="model">
430
+ <option value="stable">kayra-1 (Stable - Instruction Tuned)</option>
431
+ <option value="exp">kayra-1-exp (Deneysel - Sadece Pretrained)</option>
432
+ </select>
433
+ </div>
434
+
435
+ <button class="btn btn-primary" id="submitBtn" onclick="generate()">
436
+ Gönder ve Cevap Al
437
+ </button>
438
+
439
+ <div class="loading" id="loading">
440
+ <div class="spinner"></div>
441
+ <p>Yanıt oluşturuluyor, lütfen bekleyin...</p>
442
+ </div>
443
+
444
+ <div class="response-box" id="responseBox">
445
+ <div class="response-header">
446
+ <span>Model:</span>
447
+ <span class="model-badge" id="modelBadge"></span>
448
+ </div>
449
+ <div class="response-content" id="responseContent"></div>
450
+ </div>
451
+
452
+ <div class="error-box" id="errorBox" style="display:none;"></div>
453
+ </div>
454
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
 
456
+ <div class="footer">
457
+ <p>Kayra - Sıfırdan Türkçe ile eğitilmiş GPT modeli</p>
458
+ <p><a href="https://huggingface.co/sixfingerdev" target="_blank">Hugging Face</a></p>
459
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
 
461
+ <script>
462
+ async function generate() {
463
+ const prompt = document.getElementById("prompt").value.trim();
464
+ const model = document.getElementById("model").value;
465
+ const responseBox = document.getElementById("responseBox");
466
+ const loading = document.getElementById("loading");
467
+ const errorBox = document.getElementById("errorBox");
468
+ const submitBtn = document.getElementById("submitBtn");
469
+
470
+ if (!prompt) {
471
+ errorBox.textContent = "Lütfen bir mesaj girin!";
472
+ errorBox.style.display = "block";
473
+ responseBox.classList.remove("active");
474
+ return;
 
 
 
 
 
 
475
  }
476
+
477
+ errorBox.style.display = "none";
478
+ responseBox.classList.remove("active");
479
+ loading.classList.add("active");
480
+ submitBtn.disabled = true;
481
+
482
+ try {
483
+ const response = await fetch("/generate", {
484
+ method: "POST",
485
+ headers: { "Content-Type": "application/json" },
486
+ body: JSON.stringify({ prompt, model })
487
+ });
488
+
489
+ const data = await response.json();
490
+
491
+ if (data.error) {
492
+ errorBox.textContent = "Hata: " + data.error;
493
+ errorBox.style.display = "block";
494
+ } else {
495
+ document.getElementById("modelBadge").textContent = data.model;
496
+ document.getElementById("responseContent").innerHTML =
497
+ "<strong>Soru:</strong> " + escapeHtml(data.prompt) +
498
+ "\\n\\n<strong>Cevap:</strong>\\n" + escapeHtml(data.response);
499
+ responseBox.classList.add("active");
500
+ }
501
+ } catch (err) {
502
+ errorBox.textContent = "Bağlantı hatası: " + err.message;
503
+ errorBox.style.display = "block";
504
+ } finally {
505
+ loading.classList.remove("active");
506
+ submitBtn.disabled = false;
507
  }
508
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
509
 
510
+ function escapeHtml(text) {
511
+ const div = document.createElement('div');
512
+ div.textContent = text;
513
+ return div.innerHTML;
514
+ }
515
 
516
+ document.getElementById("prompt").addEventListener("keydown", function(e) {
517
+ if (e.ctrlKey && e.key === "Enter") {
518
+ generate();
519
+ }
520
+ });
521
+ </script>
522
+ </body>
523
+ </html>
524
+ """
525
+
526
+ # ==================== BİLGİ SAYFASI HTML ====================
527
+ INFO_HTML = """
528
+ <!DOCTYPE html>
529
+ <html lang="tr">
530
+ <head>
531
+ <meta charset="UTF-8">
532
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
533
+ <title>Kayra - Model Bilgileri</title>
534
+ <style>{{ styles }}</style>
535
+ </head>
536
+ <body>
537
+ <nav class="navbar">
538
+ <h1>Kayra Türkçe Dil Modeli</h1>
539
+ <div class="nav-links">
540
+ <a href="/">Test</a>
541
+ <a href="/info" class="active">Bilgi</a>
542
+ </div>
543
+ </nav>
544
+
545
+ <div class="container">
546
+ <!-- Genel Bakış -->
547
+ <div class="card">
548
+ <div class="card-header">
549
+ <h2>Kayra Nedir?</h2>
550
+ <p>Sıfırdan Türkçe ile eğitilmiş deneysel GPT modelleri</p>
551
+ </div>
552
+
553
+ <p>Kayra, Türkçe dil işleme araştırmaları için geliştirilmiş, sıfırdan eğitilmiş
554
+ küçük ölçekli GPT tabanlı dil modelleridir. Bu projede iki farklı model bulunmaktadır:</p>
555
+
556
+ <div class="stats-grid">
557
+ <div class="stat-card">
558
+ <div class="stat-value">85M</div>
559
+ <div class="stat-label">Parametre</div>
560
+ </div>
561
+ <div class="stat-card">
562
+ <div class="stat-value">500K</div>
563
+ <div class="stat-label">Eğitim Dokümanı</div>
564
+ </div>
565
+ <div class="stat-card">
566
+ <div class="stat-value">42.7</div>
567
+ <div class="stat-label">Validation PPL</div>
568
+ </div>
569
+ <div class="stat-card">
570
+ <div class="stat-value">MIT</div>
571
+ <div class="stat-label">Lisans</div>
572
+ </div>
573
+ </div>
574
+ </div>
575
 
576
+ <!-- Model Karşılaştırması -->
577
+ <div class="card">
578
+ <div class="card-header">
579
+ <h2>Model Karşılaştırması</h2>
580
+ </div>
581
+
582
+ <table>
583
+ <thead>
584
+ <tr>
585
+ <th>Özellik</th>
586
+ <th>kayra-1 (Stable)</th>
587
+ <th>kayra-1-exp (Deneysel)</th>
588
+ </tr>
589
+ </thead>
590
+ <tbody>
591
+ <tr>
592
+ <td>Tür</td>
593
+ <td>Instruction-tuned</td>
594
+ <td>Sadece Pretrained</td>
595
+ </tr>
596
+ <tr>
597
+ <td>Kullanım</td>
598
+ <td>Soru-Cevap formatında</td>
599
+ <td>Metin tamamlama</td>
600
+ </tr>
601
+ <tr>
602
+ <td>Stabilite</td>
603
+ <td><span class="badge badge-success">Stabil</span></td>
604
+ <td><span class="badge badge-warning">Deneysel</span></td>
605
+ </tr>
606
+ <tr>
607
+ <td>Prompt Formatı</td>
608
+ <td>### Soru: ... ### Cevap:</td>
609
+ <td>Düz metin</td>
610
+ </tr>
611
+ <tr>
612
+ <td>Önerilen Kullanım</td>
613
+ <td>Genel test ve demo</td>
614
+ <td>Araştırma ve analiz</td>
615
+ </tr>
616
+ </tbody>
617
+ </table>
618
+ </div>
619
 
620
+ <!-- Mimari Detaylar -->
621
+ <div class="card">
622
+ <div class="card-header">
623
+ <h2>Teknik Mimari</h2>
624
+ </div>
625
+
626
+ <table>
627
+ <tbody>
628
+ <tr><td><strong>Model Türü</strong></td><td>Decoder-only Transformer (GPT-style)</td></tr>
629
+ <tr><td><strong>Katman Sayısı</strong></td><td>10</td></tr>
630
+ <tr><td><strong>Hidden Size</strong></td><td>640</td></tr>
631
+ <tr><td><strong>Attention Heads</strong></td><td>10</td></tr>
632
+ <tr><td><strong>FFN Size</strong></td><td>2560</td></tr>
633
+ <tr><td><strong>Vocabulary</strong></td><td>32,000 BPE tokens</td></tr>
634
+ <tr><td><strong>Context Length</strong></td><td>512 tokens</td></tr>
635
+ <tr><td><strong>Toplam Parametre</strong></td><td>~85 milyon</td></tr>
636
+ </tbody>
637
+ </table>
638
+ </div>
639
 
640
+ <!-- Eğitim Verisi -->
641
+ <div class="card">
642
+ <div class="card-header">
643
+ <h2>Eğitim Verisi</h2>
644
+ </div>
645
 
646
+ <table>
647
+ <thead>
648
+ <tr>
649
+ <th>Kaynak</th>
650
+ <th>Doküman Sayısı</th>
651
+ <th>Açıklama</th>
652
+ </tr>
653
+ </thead>
654
+ <tbody>
655
+ <tr>
656
+ <td>Wikipedia TR</td>
657
+ <td>~170,000</td>
658
+ <td>Türkçe Vikipedi makaleleri</td>
659
+ </tr>
660
+ <tr>
661
+ <td>mC4 Turkish</td>
662
+ <td>~330,000</td>
663
+ <td>Common Crawl web dokümanları</td>
664
+ </tr>
665
+ <tr>
666
+ <td><strong>Toplam</strong></td>
667
+ <td><strong>~500,000</strong></td>
668
+ <td>MinHash LSH ile dedupe edilmiş</td>
669
+ </tr>
670
+ </tbody>
671
+ </table>
672
+ </div>
673
+
674
+ <!-- Hallucination Problemi -->
675
+ <div class="card">
676
+ <div class="card-header">
677
+ <h2>Neden Küçük Modeller Yanlış Bilgi Üretir?</h2>
678
+ </div>
679
 
680
+ <div class="highlight">
681
+ <strong>Temel Bulgu:</strong> Loss azalması, gerçek bilgi doğruluğu anlamına gelmez.
682
+ (loss down != factual accuracy up)
683
+ </div>
684
 
685
+ <div class="info-section">
686
+ <h3>Eğitim Sürecinde Gözlemlenen Davranış</h3>
687
+ <table class="comparison-table">
688
+ <thead>
689
+ <tr>
690
+ <th>Step</th>
691
+ <th>Val Loss</th>
692
+ <th>Val PPL</th>
693
+ <th>Üretilen Başkent</th>
694
+ <th>Doğru mu?</th>
695
+ </tr>
696
+ </thead>
697
+ <tbody>
698
+ <tr>
699
+ <td>1000</td>
700
+ <td>5.98</td>
701
+ <td>397.3</td>
702
+ <td>Ankara</td>
703
+ <td><span class="badge badge-success">Evet</span></td>
704
+ </tr>
705
+ <tr>
706
+ <td>3000</td>
707
+ <td>3.94</td>
708
+ <td>51.7</td>
709
+ <td>Ankara</td>
710
+ <td><span class="badge badge-success">Evet</span></td>
711
+ </tr>
712
+ <tr>
713
+ <td>5000</td>
714
+ <td>4.02</td>
715
+ <td>56.2</td>
716
+ <td>Rastgele şehir</td>
717
+ <td><span class="badge badge-danger">Hayır</span></td>
718
+ </tr>
719
+ <tr>
720
+ <td>6500</td>
721
+ <td>3.90</td>
722
+ <td>49.6</td>
723
+ <td>Bolu</td>
724
+ <td><span class="badge badge-danger">Hayır</span></td>
725
+ </tr>
726
+ <tr>
727
+ <td>7500</td>
728
+ <td>3.83</td>
729
+ <td>46.1</td>
730
+ <td>Konya</td>
731
+ <td><span class="badge badge-danger">Hayır</span></td>
732
+ </tr>
733
+ <tr>
734
+ <td>9000</td>
735
+ <td>3.75</td>
736
+ <td>42.7</td>
737
+ <td>Ankara (bazen)</td>
738
+ <td><span class="badge badge-warning">Belirsiz</span></td>
739
+ </tr>
740
+ </tbody>
741
+ </table>
742
+ </div>
743
 
744
+ <div class="two-column">
745
+ <div class="info-section">
746
+ <h3>Modelin Başarıyla Öğrendikleri</h3>
747
+ <ul style="margin-left: 1.5rem;">
748
+ <li>Dilbilgisi (Türkçe morfoloji)</li>
749
+ <li>Cümle yapısı (syntax)</li>
750
+ <li>Stil (resmi/gündelik ton eşleştirme)</li>
751
+ <li>Bağlam tutarlılığı (konu tutarlılığı)</li>
752
+ <li>Örüntü eşleme (Wikipedia tarzında metin)</li>
753
+ </ul>
754
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
 
756
+ <div class="info-section">
757
+ <h3>Modelin Öğrenemedikleri</h3>
758
+ <ul style="margin-left: 1.5rem;">
759
+ <li>Gerçek temeli: "Ankara = başkent" deterministik kuralı</li>
760
+ <li>Mantıksal tutarlılık: Aynı prompt aynı gerçeği vermeli</li>
761
+ <li>Bilgi geri çağırma: Güvenilir bilgi hatırlaması</li>
762
+ <li>Gerçek vs örüntü: Doğruluğu olabilirlikten ayırt etme</li>
763
+ </ul>
764
+ </div>
765
+ </div>
766
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
 
768
+ <!-- Model Boyutu Karşılaştırması -->
769
+ <div class="card">
770
+ <div class="card-header">
771
+ <h2>Model Boyutu ve Gerçeklik İlişkisi</h2>
772
+ </div>
773
+
774
+ <table>
775
+ <thead>
776
+ <tr>
777
+ <th>Model</th>
778
+ <th>Parametre</th>
779
+ <th>Gerçeklik Güvenilirliği</th>
780
+ </tr>
781
+ </thead>
782
+ <tbody>
783
+ <tr>
784
+ <td>Kayra (bu model)</td>
785
+ <td>85M</td>
786
+ <td><span class="badge badge-danger">Düşük - hallucination yaygın</span></td>
787
+ </tr>
788
+ <tr>
789
+ <td>GPT-2 Small</td>
790
+ <td>124M</td>
791
+ <td><span class="badge badge-danger">Düşük - benzer sorunlar</span></td>
792
+ </tr>
793
+ <tr>
794
+ <td>GPT-2 Medium</td>
795
+ <td>355M</td>
796
+ <td><span class="badge badge-warning">Orta - hala güvenilmez</span></td>
797
+ </tr>
798
+ <tr>
799
+ <td>GPT-3</td>
800
+ <td>175B</td>
801
+ <td><span class="badge badge-success">İyi tutarlılık</span></td>
802
+ </tr>
803
+ <tr>
804
+ <td>GPT-4</td>
805
+ <td>~1.7T + RLHF</td>
806
+ <td><span class="badge badge-success">Güvenilir</span></td>
807
+ </tr>
808
+ <tr>
809
+ <td>GPT-5</td>
810
+ <td>~10T (tahmini)</td>
811
+ <td><span class="badge badge-info">Çok yüksek (beklenen)</span></td>
812
+ </tr>
813
+ </tbody>
814
+ </table>
815
+
816
+ <div class="highlight">
817
+ <strong>Sonuç:</strong> 85M parametre dil örüntülerini öğrenir, bilgi tabanı oluşturmaz.
818
+ Gerçek bilgi güvenilirliği için milyarlarca parametre ve RLHF/DPO gibi hizalama teknikleri gerekir.
819
+ </div>
820
+ </div>
821
 
822
+ <!-- Kullanım Örneği -->
823
+ <div class="card">
824
+ <div class="card-header">
825
+ <h2>Kod Örneği</h2>
826
+ </div>
827
+
828
+ <div class="code-block">
829
+ <pre>from transformers import AutoModelForCausalLM, AutoTokenizer
830
+
831
+ model = AutoModelForCausalLM.from_pretrained(
832
+ "sixfingerdev/kayra-1-exp",
833
+ trust_remote_code=True # ONEMLI!
834
+ )
835
+ tokenizer = AutoTokenizer.from_pretrained("sixfingerdev/kayra-1-exp")
836
+
837
+ prompt = "Türkiye'nin başkenti"
838
+ inputs = tokenizer(prompt, return_tensors="pt")
839
+
840
+ outputs = model.generate(
841
+ inputs.input_ids,
842
+ max_new_tokens=100,
843
+ temperature=0.8,
844
+ top_k=50,
845
+ repetition_penalty=1.2,
846
+ do_sample=True
847
+ )
848
+
849
+ print(tokenizer.decode(outputs[0], skip_special_tokens=True))</pre>
850
+ </div>
851
+ </div>
852
 
853
+ <!-- Uygun Kullanım Alanları -->
854
+ <div class="card">
855
+ <div class="card-header">
856
+ <h2>Kullanım Alanları</h2>
857
+ </div>
858
+
859
+ <div class="two-column">
860
+ <div>
861
+ <h4 style="color: var(--success); margin-bottom: 0.5rem;">Önerilen Kullanım</h4>
862
+ <ul style="margin-left: 1.5rem;">
863
+ <li>Türkçe NLP limitasyonları araştırması</li>
864
+ <li>Pretraining baseline karşılaştırmaları</li>
865
+ <li>Hallucination örüntü çalışmaları</li>
866
+ <li>Eğitim amaçlı demonstrasyonlar</li>
867
+ <li>LLM başarısızlık modlarını anlama</li>
868
+ </ul>
869
+ </div>
870
+ <div>
871
+ <h4 style="color: var(--accent); margin-bottom: 0.5rem;">Önerilmeyen Kullanım</h4>
872
+ <ul style="margin-left: 1.5rem;">
873
+ <li>Production uygulamaları</li>
874
+ <li>Gerçek soru-cevap sistemleri</li>
875
+ <li>Bilgi erişim sistemleri</li>
876
+ <li>Eğitim içeriği üretimi</li>
877
+ <li>Doğruluk gerektiren her iş</li>
878
+ </ul>
879
+ </div>
880
+ </div>
881
+ </div>
882
 
883
+ <!-- Gelecek Planları -->
884
+ <div class="card">
885
+ <div class="card-header">
886
+ <h2>Gelecek: Kayra-v2</h2>
887
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888
 
889
+ <ul style="margin-left: 1.5rem;">
890
+ <li><strong>Daha büyük model:</strong> 350M-750M parametre</li>
891
+ <li><strong>Daha iyi tokenizer:</strong> NFC Unicode normalization</li>
892
+ <li><strong>Instruction tuning:</strong> 10K doğrulanmış QA çifti</li>
893
+ <li><strong>Alignment:</strong> Gerçeklik için RLHF veya DPO</li>
894
+ <li><strong>Değerlendirme:</strong> Gerçek kontrol benchmark'ları</li>
895
+ </ul>
896
+ </div>
897
 
898
+ <!-- Lisans ve Atıf -->
899
+ <div class="card">
900
+ <div class="card-header">
901
+ <h2>Lisans ve Atıf</h2>
902
+ </div>
903
+
904
+ <p><strong>Lisans:</strong> MIT License - Ticari ve akademik kullanım serbesttir.</p>
905
+
906
+ <div class="code-block">
907
+ <pre>@misc{kayra2024hallucination,
908
+ title={Why Small Turkish GPTs Hallucinate Facts: An Experimental 85M Model},
909
+ author={sixfingerdev},
910
+ year={2024},
911
+ publisher={HuggingFace},
912
+ howpublished={\\url{https://huggingface.co/sixfingerdev/kayra-1-exp}},
913
+ note={Research on loss-factuality divergence in low-resource language models}
914
+ }</pre>
915
+ </div>
916
+
917
+ <div class="highlight" style="margin-top: 1rem;">
918
+ <strong>Uyarı:</strong> Bu model bilerek kusurlarıyla birlikte paylaşılmıştır.
919
+ Küçük LM'lerin neden hallucination yaptığını gösteren bir öğrenme kaynağı olarak
920
+ hizmet eder, production aracı olarak değil.
921
+ </div>
922
+ </div>
923
+ </div>
924
 
925
+ <div class="footer">
926
+ <p>Kayra - Türkçe'yi Yaratan Zeka</p>
927
+ <p><a href="https://huggingface.co/sixfingerdev" target="_blank">Hugging Face</a></p>
928
+ </div>
929
+ </body>
930
+ </html>
931
+ """
932
 
933
+ # ==================== MODEL YÜKLEME ====================
934
+ print("Modeller yükleniyor... Bu biraz sürebilir (özellikle ilk seferde).")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
935
 
936
+ model_stable = AutoModelForCausalLM.from_pretrained(
937
+ "sixfingerdev/kayra-1",
938
+ trust_remote_code=True,
939
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
940
+ device_map="auto"
941
+ )
942
+ tokenizer_stable = AutoTokenizer.from_pretrained("sixfingerdev/kayra-1")
 
 
 
 
 
 
 
 
 
943
 
944
+ model_exp = AutoModelForCausalLM.from_pretrained(
945
+ "sixfingerdev/kayra-1-exp",
946
+ trust_remote_code=True,
947
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
948
+ device_map="auto"
949
+ )
950
+ tokenizer_exp = AutoTokenizer.from_pretrained("sixfingerdev/kayra-1-exp")
951
 
952
+ print("Modeller başarıyla yüklendi!")
953
+
954
+ # ==================== YARDIMCI FONKSİYONLAR ====================
955
+ def generate_response(model, tokenizer, prompt, max_new_tokens=150):
956
+ if model == model_stable:
957
+ formatted_prompt = f"### Soru: {prompt}\n\n### Cevap:"
958
+ else:
959
+ formatted_prompt = prompt
960
+
961
+ inputs = tokenizer(formatted_prompt, return_tensors="pt")
962
+ inputs = inputs.to(model.device)
963
 
964
+ if "token_type_ids" in inputs:
965
+ inputs.pop("token_type_ids")
 
 
966
 
967
+ with torch.no_grad():
968
+ outputs = model.generate(
969
+ **inputs,
970
+ max_new_tokens=max_new_tokens,
971
+ temperature=0.7,
972
+ do_sample=True,
973
+ top_p=0.9,
974
+ repetition_penalty=1.3,
975
+ pad_token_id=tokenizer.eos_token_id
976
+ )
977
 
978
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
979
 
980
+ if model == model_stable:
981
+ if "### Cevap:" in response:
982
+ response = response.split("### Cevap:")[-1].strip()
 
983
 
984
+ return response
985
+
986
+ # ==================== ROUTE'LAR ====================
987
+ @app.route("/")
988
+ def index():
989
+ return render_template_string(INDEX_HTML, styles=STYLES)
990
+
991
+ @app.route("/info")
992
+ def info():
993
+ return render_template_string(INFO_HTML, styles=STYLES)
994
+
995
+ @app.route("/generate", methods=["POST"])
996
+ def generate():
997
+ data = request.json
998
+ prompt = data.get("prompt", "").strip()
999
+ model_choice = data.get("model", "stable")
1000
+
1001
+ if not prompt:
1002
+ return jsonify({"error": "Lütfen bir soru veya mesaj girin."})
1003
+
1004
+ if model_choice == "stable":
1005
+ response = generate_response(model_stable, tokenizer_stable, prompt)
1006
+ model_name = "kayra-1 (Stable)"
1007
+ else:
1008
+ response = generate_response(model_exp, tokenizer_exp, prompt)
1009
+ model_name = "kayra-1-exp (Deneysel)"
1010
+
1011
+ return jsonify({
1012
+ "model": model_name,
1013
+ "prompt": prompt,
1014
+ "response": response
1015
+ })
1016
+
1017
+ # ==================== UYGULAMA BAŞLATMA ====================
1018
+ if __name__ == "__main__":
1019
+ app.run(host="0.0.0.0", port=5000, debug=True)