aigencydev commited on
Commit
b530bea
·
verified ·
1 Parent(s): 131ceb7

Add abuse mitigation: 10 msg/min, 50 msg/session, 2000 char limit, 5000 daily global cap

Browse files
Files changed (1) hide show
  1. app.py +99 -12
app.py CHANGED
@@ -15,6 +15,10 @@ from __future__ import annotations
15
 
16
  import io
17
  import os
 
 
 
 
18
 
19
  import gradio as gr
20
  import requests
@@ -31,6 +35,33 @@ DEMO_BANNER = (
31
  "128B parameters · 278K context · KVKK-resident"
32
  )
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  PLACEHOLDER_MSG = (
35
  "🔒 The interactive chat is being activated.\n\n"
36
  "While the demo is finalised, you can already:\n"
@@ -105,31 +136,75 @@ def send_with_image(chat_id: str, message: str, image: Image.Image) -> str:
105
  return r.json().get("message", "")
106
 
107
 
108
- # ── Gradio handler with session-scoped chat_id ─────────────────────
109
- def chat(prompt, image, history, chat_id_state):
 
 
 
 
110
  history = history or []
111
  chat_id = chat_id_state or ""
 
 
112
 
113
  if not prompt.strip():
114
- return history, "", chat_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  if not API_TOKEN:
117
  history.append((prompt, PLACEHOLDER_MSG))
118
- return history, "", chat_id
119
 
 
120
  try:
121
  if not chat_id:
122
- # Open a new conversation; the seed message can be the user's first prompt
123
  if image is None:
124
  cid, answer = new_chat(prompt)
125
  chat_id = cid
126
  else:
127
- # newChat doesn't accept attachments — open with a brief seed, then send image
128
  cid, _ = new_chat("Bir görsel inceleyeceksin.")
129
  chat_id = cid
130
  answer = send_with_image(chat_id, prompt, image)
131
  else:
132
- # Continue existing conversation
133
  if image is None:
134
  answer = send_message(chat_id, prompt)
135
  else:
@@ -137,12 +212,13 @@ def chat(prompt, image, history, chat_id_state):
137
  except Exception as e:
138
  answer = f"Error: {e}"
139
 
 
140
  history.append((prompt, answer))
141
- return history, "", chat_id
142
 
143
 
144
  def reset_session():
145
- return [], "", ""
146
 
147
 
148
  def make_leaderboard():
@@ -175,6 +251,8 @@ Responses are served live by the production API.
175
  with gr.Blocks(title="AIGENCY V4 Demo", theme=gr.themes.Soft()) as demo:
176
  gr.Markdown(f"# AIGENCY V4\n\n*{DEMO_BANNER}*")
177
  chat_id_state = gr.State("")
 
 
178
 
179
  with gr.Tab("Chat"):
180
  with gr.Row():
@@ -195,9 +273,18 @@ with gr.Blocks(title="AIGENCY V4 Demo", theme=gr.themes.Soft()) as demo:
195
  with gr.Row():
196
  send = gr.Button("Send", variant="primary")
197
  clear = gr.Button("New conversation")
198
- send.click(chat, [msg, img, chatbot, chat_id_state], [chatbot, msg, chat_id_state])
199
- msg.submit(chat, [msg, img, chatbot, chat_id_state], [chatbot, msg, chat_id_state])
200
- clear.click(reset_session, [], [chatbot, msg, chat_id_state])
 
 
 
 
 
 
 
 
 
201
 
202
  with gr.Tab("Benchmark Leaderboard"):
203
  gr.Markdown(
 
15
 
16
  import io
17
  import os
18
+ import time
19
+ import threading
20
+ from collections import deque
21
+ from datetime import datetime, timezone
22
 
23
  import gradio as gr
24
  import requests
 
35
  "128B parameters · 278K context · KVKK-resident"
36
  )
37
 
38
+ # ── Abuse mitigation knobs ─────────────────────────────────────────
39
+ RATE_PER_MIN_PER_SESSION = int(os.environ.get("RATE_PER_MIN", "10"))
40
+ MAX_PER_SESSION = int(os.environ.get("MAX_PER_SESSION", "50"))
41
+ MAX_PROMPT_CHARS = int(os.environ.get("MAX_PROMPT_CHARS", "2000"))
42
+ DAILY_CAP_GLOBAL = int(os.environ.get("DAILY_CAP", "5000"))
43
+
44
+ # ── Global daily counter (thread-safe) ─────────────────────────────
45
+ _global_lock = threading.Lock()
46
+ _global_counter = {"date": "", "count": 0}
47
+
48
+
49
+ def _utc_today() -> str:
50
+ return datetime.now(timezone.utc).strftime("%Y-%m-%d")
51
+
52
+
53
+ def _check_and_inc_daily() -> tuple[bool, int]:
54
+ """Increment the global daily counter; return (allowed, current_count)."""
55
+ today = _utc_today()
56
+ with _global_lock:
57
+ if _global_counter["date"] != today:
58
+ _global_counter["date"] = today
59
+ _global_counter["count"] = 0
60
+ if _global_counter["count"] >= DAILY_CAP_GLOBAL:
61
+ return False, _global_counter["count"]
62
+ _global_counter["count"] += 1
63
+ return True, _global_counter["count"]
64
+
65
  PLACEHOLDER_MSG = (
66
  "🔒 The interactive chat is being activated.\n\n"
67
  "While the demo is finalised, you can already:\n"
 
136
  return r.json().get("message", "")
137
 
138
 
139
+ # ── Gradio handler with session-scoped chat_id + rate limiting ────
140
+ def chat(prompt, image, history, chat_id_state, ts_log_state, count_state):
141
+ """
142
+ ts_log_state: deque of recent timestamps (sliding 60s window) for per-minute rate
143
+ count_state: total messages in this session
144
+ """
145
  history = history or []
146
  chat_id = chat_id_state or ""
147
+ ts_log = ts_log_state or deque()
148
+ count = count_state or 0
149
 
150
  if not prompt.strip():
151
+ return history, "", chat_id, ts_log, count
152
+
153
+ # 1) Prompt length limit
154
+ if len(prompt) > MAX_PROMPT_CHARS:
155
+ history.append((prompt[:200] + " […]", (
156
+ f"⚠️ Prompt too long ({len(prompt)} chars). "
157
+ f"Max {MAX_PROMPT_CHARS} chars per message in this demo. "
158
+ f"For longer contexts, use the production API at aigency.dev."
159
+ )))
160
+ return history, "", chat_id, ts_log, count
161
+
162
+ # 2) Per-session total
163
+ if count >= MAX_PER_SESSION:
164
+ history.append((prompt, (
165
+ f"⚠️ Session limit reached ({MAX_PER_SESSION} messages). "
166
+ f"Refresh the page to start a new session, "
167
+ f"or contact info@e-cloud.web.tr · ai@aigency.dev for production access."
168
+ )))
169
+ return history, "", chat_id, ts_log, count
170
+
171
+ # 3) Per-minute rate (sliding window)
172
+ now = time.time()
173
+ while ts_log and now - ts_log[0] > 60:
174
+ ts_log.popleft()
175
+ if len(ts_log) >= RATE_PER_MIN_PER_SESSION:
176
+ wait = int(60 - (now - ts_log[0]))
177
+ history.append((prompt, (
178
+ f"⚠️ Slow down — max {RATE_PER_MIN_PER_SESSION} messages/minute. "
179
+ f"Try again in {wait}s."
180
+ )))
181
+ return history, "", chat_id, ts_log, count
182
+
183
+ # 4) Global daily cap
184
+ allowed, daily_count = _check_and_inc_daily()
185
+ if not allowed:
186
+ history.append((prompt, (
187
+ f"⚠️ The demo has reached today's global limit ({DAILY_CAP_GLOBAL} requests). "
188
+ f"It resets at 00:00 UTC. For uninterrupted access, contact "
189
+ f"info@e-cloud.web.tr · ai@aigency.dev."
190
+ )))
191
+ return history, "", chat_id, ts_log, count
192
 
193
  if not API_TOKEN:
194
  history.append((prompt, PLACEHOLDER_MSG))
195
+ return history, "", chat_id, ts_log, count
196
 
197
+ # 5) Actual API call
198
  try:
199
  if not chat_id:
 
200
  if image is None:
201
  cid, answer = new_chat(prompt)
202
  chat_id = cid
203
  else:
 
204
  cid, _ = new_chat("Bir görsel inceleyeceksin.")
205
  chat_id = cid
206
  answer = send_with_image(chat_id, prompt, image)
207
  else:
 
208
  if image is None:
209
  answer = send_message(chat_id, prompt)
210
  else:
 
212
  except Exception as e:
213
  answer = f"Error: {e}"
214
 
215
+ ts_log.append(now)
216
  history.append((prompt, answer))
217
+ return history, "", chat_id, ts_log, count + 1
218
 
219
 
220
  def reset_session():
221
+ return [], "", "", deque(), 0
222
 
223
 
224
  def make_leaderboard():
 
251
  with gr.Blocks(title="AIGENCY V4 Demo", theme=gr.themes.Soft()) as demo:
252
  gr.Markdown(f"# AIGENCY V4\n\n*{DEMO_BANNER}*")
253
  chat_id_state = gr.State("")
254
+ ts_log_state = gr.State(lambda: deque())
255
+ count_state = gr.State(0)
256
 
257
  with gr.Tab("Chat"):
258
  with gr.Row():
 
273
  with gr.Row():
274
  send = gr.Button("Send", variant="primary")
275
  clear = gr.Button("New conversation")
276
+ gr.Markdown(
277
+ f"*Demo limits: {MAX_PROMPT_CHARS} chars/message · "
278
+ f"{RATE_PER_MIN_PER_SESSION} msg/min · "
279
+ f"{MAX_PER_SESSION} msg/session · "
280
+ f"{DAILY_CAP_GLOBAL} requests/day globally. "
281
+ f"For unlimited production access: info@e-cloud.web.tr · ai@aigency.dev*"
282
+ )
283
+ chat_inputs = [msg, img, chatbot, chat_id_state, ts_log_state, count_state]
284
+ chat_outputs = [chatbot, msg, chat_id_state, ts_log_state, count_state]
285
+ send.click(chat, chat_inputs, chat_outputs)
286
+ msg.submit(chat, chat_inputs, chat_outputs)
287
+ clear.click(reset_session, [], [chatbot, msg, chat_id_state, ts_log_state, count_state])
288
 
289
  with gr.Tab("Benchmark Leaderboard"):
290
  gr.Markdown(