blaze-aura69 commited on
Commit
9f682aa
·
verified ·
1 Parent(s): 465ee72

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -111
app.py CHANGED
@@ -1,5 +1,6 @@
1
- from fastapi import FastAPI
2
  from fastapi.middleware.cors import CORSMiddleware
 
3
  from pydantic import BaseModel
4
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
5
  import torch
@@ -8,6 +9,7 @@ import httpx
8
  import time
9
  from concurrent.futures import ThreadPoolExecutor
10
  import hashlib
 
11
 
12
  app = FastAPI(title="Distributed M2M100 API")
13
 
@@ -47,6 +49,7 @@ SPACE_PREFIX = "trans"
47
  DISCOVERY_CHUNK_SIZE = 50
48
  CACHE_TTL = 86400 # Cache time-to-live: 1 day in seconds (24 * 60 * 60)
49
  MAX_CACHE_SIZE = 10000 # Maximum number of cached translations
 
50
 
51
  # =====================================================
52
  # GLOBALS
@@ -62,110 +65,35 @@ cooldown = {}
62
  # 🔥 Translation cache with TTL (NEW)
63
  translation_cache = {}
64
 
 
 
 
 
65
  # =====================================================
66
  # LANGUAGES
67
  # =====================================================
68
  SUPPORTED_LANGS = {
69
- "af": "Afrikaans",
70
- "am": "Amharic",
71
- "ar": "Arabic",
72
- "ast": "Asturian",
73
- "az": "Azerbaijani",
74
- "ba": "Bashkir",
75
- "be": "Belarusian",
76
- "bg": "Bulgarian",
77
- "bn": "Bengali",
78
- "br": "Breton",
79
- "bs": "Bosnian",
80
- "ca": "Catalan",
81
- "ceb": "Cebuano",
82
- "cs": "Czech",
83
- "cy": "Welsh",
84
- "da": "Danish",
85
- "de": "German",
86
- "el": "Greek",
87
- "en": "English",
88
- "es": "Spanish",
89
- "et": "Estonian",
90
- "fa": "Persian",
91
- "ff": "Fulah",
92
- "fi": "Finnish",
93
- "fr": "French",
94
- "fy": "Western Frisian",
95
- "ga": "Irish",
96
- "gd": "Scottish Gaelic",
97
- "gl": "Galician",
98
- "gu": "Gujarati",
99
- "ha": "Hausa",
100
- "he": "Hebrew",
101
- "hi": "Hindi",
102
- "hr": "Croatian",
103
- "ht": "Haitian Creole",
104
- "hu": "Hungarian",
105
- "hy": "Armenian",
106
- "id": "Indonesian",
107
- "ig": "Igbo",
108
- "ilo": "Ilocano",
109
- "is": "Icelandic",
110
- "it": "Italian",
111
- "ja": "Japanese",
112
- "jv": "Javanese",
113
- "ka": "Georgian",
114
- "kk": "Kazakh",
115
- "km": "Khmer",
116
- "kn": "Kannada",
117
- "ko": "Korean",
118
- "lb": "Luxembourgish",
119
- "lg": "Ganda",
120
- "ln": "Lingala",
121
- "lo": "Lao",
122
- "lt": "Lithuanian",
123
- "lv": "Latvian",
124
- "mg": "Malagasy",
125
- "mk": "Macedonian",
126
- "ml": "Malayalam",
127
- "mn": "Mongolian",
128
- "mr": "Marathi",
129
- "ms": "Malay",
130
- "my": "Myanmar",
131
- "ne": "Nepali",
132
- "nl": "Dutch",
133
- "no": "Norwegian",
134
- "ns": "Northern Sotho",
135
- "oc": "Occitan",
136
- "or": "Odia",
137
- "pa": "Punjabi",
138
- "pl": "Polish",
139
- "ps": "Pashto",
140
- "pt": "Portuguese",
141
- "ro": "Romanian",
142
- "ru": "Russian",
143
- "sd": "Sindhi",
144
- "si": "Sinhala",
145
- "sk": "Slovak",
146
- "sl": "Slovenian",
147
- "so": "Somali",
148
- "sq": "Albanian",
149
- "sr": "Serbian",
150
- "ss": "Swati",
151
- "su": "Sundanese",
152
- "sv": "Swedish",
153
- "sw": "Swahili",
154
- "ta": "Tamil",
155
- "th": "Thai",
156
- "tl": "Tagalog",
157
- "tn": "Tswana",
158
- "tr": "Turkish",
159
- "uk": "Ukrainian",
160
- "ur": "Urdu",
161
- "uz": "Uzbek",
162
- "vi": "Vietnamese",
163
- "wo": "Wolof",
164
- "xh": "Xhosa",
165
- "yi": "Yiddish",
166
- "yo": "Yoruba",
167
- "zh": "Chinese",
168
- "zu": "Zulu"
169
  }
170
 
171
  # =====================================================
@@ -252,6 +180,7 @@ def is_blocked(url):
252
  async def check_space(client, i):
253
  space_name = SPACE_PREFIX if i == 0 else f"{SPACE_PREFIX}{i}"
254
  url = f"https://{USERNAME}-{space_name}.hf.space"
 
255
  if is_blocked(url):
256
  return {
257
  "exists": True,
@@ -261,6 +190,7 @@ async def check_space(client, i):
261
  "current_space_status": "cooldown"
262
  }
263
  }
 
264
  try:
265
  response = await client.get(f"{url}/status")
266
  if response.status_code == 200:
@@ -317,6 +247,7 @@ async def discover_spaces_parallel():
317
  existing_spaces = []
318
  empty_space_url = None
319
  timeout = httpx.Timeout(5.0)
 
320
  async with httpx.AsyncClient(timeout=timeout) as client:
321
  start = 0
322
  while True:
@@ -325,7 +256,7 @@ async def discover_spaces_parallel():
325
  for i in range(start, start + DISCOVERY_CHUNK_SIZE)
326
  ]
327
  results = await asyncio.gather(*tasks)
328
-
329
  found_empty = False
330
  for result in results:
331
  if result["exists"]:
@@ -341,11 +272,12 @@ async def discover_spaces_parallel():
341
  empty_space_url = result["url"]
342
  found_empty = True
343
  break
344
-
345
  if found_empty:
346
  break
 
347
  start += DISCOVERY_CHUNK_SIZE
348
-
349
  return {
350
  "existing_spaces": existing_spaces,
351
  "empty_space_url": empty_space_url
@@ -372,10 +304,36 @@ async def status():
372
  }
373
 
374
  # =====================================================
375
- # LANGUAGES
376
  # =====================================================
377
  @app.get("/languages")
378
- def languages():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  return SUPPORTED_LANGS
380
 
381
  # =====================================================
@@ -384,7 +342,7 @@ def languages():
384
  @app.post("/translate")
385
  async def translate(req: TranslateRequest):
386
  global active_requests
387
-
388
  # Character limit check (without spaces)
389
  char_count_without_spaces = len(req.text.replace(" ", ""))
390
  if char_count_without_spaces > MAX_CHARACTERS:
@@ -394,7 +352,7 @@ async def translate(req: TranslateRequest):
394
  "max_characters": MAX_CHARACTERS,
395
  "received_characters": char_count_without_spaces
396
  }
397
-
398
  # Check cache first
399
  cached_result = get_from_cache(req.text, req.source_lang, req.target_lang)
400
  if cached_result is not None:
@@ -403,7 +361,7 @@ async def translate(req: TranslateRequest):
403
  "translated_text": cached_result,
404
  "from_cache": True
405
  }
406
-
407
  queue_size = request_queue.qsize()
408
  if active_requests >= MAX_ACTIVE_REQUESTS and queue_size >= MAX_QUEUE_SIZE:
409
  discovered = await discover_spaces_parallel()
@@ -411,11 +369,10 @@ async def translate(req: TranslateRequest):
411
  "status": "space_full",
412
  "empty_space_url": discovered["empty_space_url"]
413
  }
414
-
415
  await request_queue.put("req")
416
  async with lock:
417
  active_requests += 1
418
-
419
  try:
420
  loop = asyncio.get_running_loop()
421
  result = await loop.run_in_executor(
 
1
+ from fastapi import FastAPI, Request
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
  from pydantic import BaseModel
5
  from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
6
  import torch
 
9
  import time
10
  from concurrent.futures import ThreadPoolExecutor
11
  import hashlib
12
+ from collections import defaultdict
13
 
14
  app = FastAPI(title="Distributed M2M100 API")
15
 
 
49
  DISCOVERY_CHUNK_SIZE = 50
50
  CACHE_TTL = 86400 # Cache time-to-live: 1 day in seconds (24 * 60 * 60)
51
  MAX_CACHE_SIZE = 10000 # Maximum number of cached translations
52
+ MAX_RPS_LANGUAGES = 20000 # Maximum requests per second for /languages endpoint
53
 
54
  # =====================================================
55
  # GLOBALS
 
65
  # 🔥 Translation cache with TTL (NEW)
66
  translation_cache = {}
67
 
68
+ # 🔥 RPS tracking for /languages endpoint (NEW)
69
+ languages_request_times = []
70
+ languages_lock = asyncio.Lock()
71
+
72
  # =====================================================
73
  # LANGUAGES
74
  # =====================================================
75
  SUPPORTED_LANGS = {
76
+ "af": "Afrikaans", "am": "Amharic", "ar": "Arabic", "ast": "Asturian", "az": "Azerbaijani",
77
+ "ba": "Bashkir", "be": "Belarusian", "bg": "Bulgarian", "bn": "Bengali", "br": "Breton",
78
+ "bs": "Bosnian", "ca": "Catalan", "ceb": "Cebuano", "cs": "Czech", "cy": "Welsh",
79
+ "da": "Danish", "de": "German", "el": "Greek", "en": "English", "es": "Spanish",
80
+ "et": "Estonian", "fa": "Persian", "ff": "Fulah", "fi": "Finnish", "fr": "French",
81
+ "fy": "Western Frisian", "ga": "Irish", "gd": "Scottish Gaelic", "gl": "Galician",
82
+ "gu": "Gujarati", "ha": "Hausa", "he": "Hebrew", "hi": "Hindi", "hr": "Croatian",
83
+ "ht": "Haitian Creole", "hu": "Hungarian", "hy": "Armenian", "id": "Indonesian",
84
+ "ig": "Igbo", "ilo": "Ilocano", "is": "Icelandic", "it": "Italian", "ja": "Japanese",
85
+ "jv": "Javanese", "ka": "Georgian", "kk": "Kazakh", "km": "Khmer", "kn": "Kannada",
86
+ "ko": "Korean", "lb": "Luxembourgish", "lg": "Ganda", "ln": "Lingala", "lo": "Lao",
87
+ "lt": "Lithuanian", "lv": "Latvian", "mg": "Malagasy", "mk": "Macedonian",
88
+ "ml": "Malayalam", "mn": "Mongolian", "mr": "Marathi", "ms": "Malay", "my": "Myanmar",
89
+ "ne": "Nepali", "nl": "Dutch", "no": "Norwegian", "ns": "Northern Sotho",
90
+ "oc": "Occitan", "or": "Odia", "pa": "Punjabi", "pl": "Polish", "ps": "Pashto",
91
+ "pt": "Portuguese", "ro": "Romanian", "ru": "Russian", "sd": "Sindhi",
92
+ "si": "Sinhala", "sk": "Slovak", "sl": "Slovenian", "so": "Somali", "sq": "Albanian",
93
+ "sr": "Serbian", "ss": "Swati", "su": "Sundanese", "sv": "Swedish", "sw": "Swahili",
94
+ "ta": "Tamil", "th": "Thai", "tl": "Tagalog", "tn": "Tswana", "tr": "Turkish",
95
+ "uk": "Ukrainian", "ur": "Urdu", "uz": "Uzbek", "vi": "Vietnamese", "wo": "Wolof",
96
+ "xh": "Xhosa", "yi": "Yiddish", "yo": "Yoruba", "zh": "Chinese", "zu": "Zulu"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  }
98
 
99
  # =====================================================
 
180
  async def check_space(client, i):
181
  space_name = SPACE_PREFIX if i == 0 else f"{SPACE_PREFIX}{i}"
182
  url = f"https://{USERNAME}-{space_name}.hf.space"
183
+
184
  if is_blocked(url):
185
  return {
186
  "exists": True,
 
190
  "current_space_status": "cooldown"
191
  }
192
  }
193
+
194
  try:
195
  response = await client.get(f"{url}/status")
196
  if response.status_code == 200:
 
247
  existing_spaces = []
248
  empty_space_url = None
249
  timeout = httpx.Timeout(5.0)
250
+
251
  async with httpx.AsyncClient(timeout=timeout) as client:
252
  start = 0
253
  while True:
 
256
  for i in range(start, start + DISCOVERY_CHUNK_SIZE)
257
  ]
258
  results = await asyncio.gather(*tasks)
259
+
260
  found_empty = False
261
  for result in results:
262
  if result["exists"]:
 
272
  empty_space_url = result["url"]
273
  found_empty = True
274
  break
275
+
276
  if found_empty:
277
  break
278
+
279
  start += DISCOVERY_CHUNK_SIZE
280
+
281
  return {
282
  "existing_spaces": existing_spaces,
283
  "empty_space_url": empty_space_url
 
304
  }
305
 
306
  # =====================================================
307
+ # LANGUAGES WITH RPS LIMITING (UPDATED)
308
  # =====================================================
309
  @app.get("/languages")
310
+ async def languages(request: Request):
311
+ async with languages_lock:
312
+ current_time = time.time()
313
+
314
+ # Remove requests older than 1 second
315
+ global languages_request_times
316
+ languages_request_times = [
317
+ t for t in languages_request_times
318
+ if current_time - t < 1.0
319
+ ]
320
+
321
+ # Check if we've exceeded the RPS limit
322
+ if len(languages_request_times) >= MAX_RPS_LANGUAGES:
323
+ # Discover empty space URL for redirection
324
+ discovered = await discover_spaces_parallel()
325
+ return JSONResponse(
326
+ status_code=429,
327
+ content={
328
+ "status": "space_full",
329
+ "message": f"Rate limit exceeded. Maximum {MAX_RPS_LANGUAGES} requests per second allowed.",
330
+ "empty_space_url": discovered["empty_space_url"]
331
+ }
332
+ )
333
+
334
+ # Add current request timestamp
335
+ languages_request_times.append(current_time)
336
+
337
  return SUPPORTED_LANGS
338
 
339
  # =====================================================
 
342
  @app.post("/translate")
343
  async def translate(req: TranslateRequest):
344
  global active_requests
345
+
346
  # Character limit check (without spaces)
347
  char_count_without_spaces = len(req.text.replace(" ", ""))
348
  if char_count_without_spaces > MAX_CHARACTERS:
 
352
  "max_characters": MAX_CHARACTERS,
353
  "received_characters": char_count_without_spaces
354
  }
355
+
356
  # Check cache first
357
  cached_result = get_from_cache(req.text, req.source_lang, req.target_lang)
358
  if cached_result is not None:
 
361
  "translated_text": cached_result,
362
  "from_cache": True
363
  }
364
+
365
  queue_size = request_queue.qsize()
366
  if active_requests >= MAX_ACTIVE_REQUESTS and queue_size >= MAX_QUEUE_SIZE:
367
  discovered = await discover_spaces_parallel()
 
369
  "status": "space_full",
370
  "empty_space_url": discovered["empty_space_url"]
371
  }
372
+
373
  await request_queue.put("req")
374
  async with lock:
375
  active_requests += 1
 
376
  try:
377
  loop = asyncio.get_running_loop()
378
  result = await loop.run_in_executor(