3ssem0 commited on
Commit
e5c82f8
·
1 Parent(s): d72be96

faster-AI-1CPU

Browse files
Files changed (4) hide show
  1. Dockerfile +3 -2
  2. Procfile +2 -1
  3. code_commenter_improved.py +78 -31
  4. github_utils.py +5 -1
Dockerfile CHANGED
@@ -31,6 +31,7 @@ COPY --chown=user . .
31
  EXPOSE 7860
32
 
33
  # Command to run the application using Gunicorn for better stability on HF
34
- # -w 2: Two workers to keep memory usage balanced
35
  # -k uvicorn.workers.UvicornWorker: Use Uvicorn for FastAPI
36
- CMD ["gunicorn", "-w", "2", "-k", "uvicorn.workers.UvicornWorker", "main:app", "--bind", "0.0.0.0:7860", "--timeout", "120"]
 
 
31
  EXPOSE 7860
32
 
33
  # Command to run the application using Gunicorn for better stability on HF
34
+ # -w 1: Single worker to focus all CPU power and save memory
35
  # -k uvicorn.workers.UvicornWorker: Use Uvicorn for FastAPI
36
+ CMD ["gunicorn", "-w", "1", "-k", "uvicorn.workers.UvicornWorker", "main:app", "--bind", "0.0.0.0:7860", "--timeout", "120"]
37
+
Procfile CHANGED
@@ -1 +1,2 @@
1
- web: gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:$PORT
 
 
1
+ web: gunicorn -w 1 -k uvicorn.workers.UvicornWorker main:app --bind 0.0.0.0:$PORT
2
+
code_commenter_improved.py CHANGED
@@ -189,6 +189,33 @@ def _invoke_ai(codet5_pipeline, prompt, max_new_tokens=60):
189
  return ""
190
 
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  def _truncate_to_tokens(text: str, max_tokens: int = 400) -> str:
193
  """Approximate token limit by word count (1 token ~ 0.75 words)."""
194
  words = text.split()
@@ -231,12 +258,18 @@ def _strip_class_attrs(tag) -> str:
231
 
232
 
233
  def _annotate_html(soup, codet5_pipeline, comment_level):
234
- """Three-layer HTML annotator: Structural / Classes / Events."""
 
 
235
  event_attrs = [
236
  "onclick", "onsubmit", "onchange", "oninput",
237
  "onload", "onkeydown", "onmouseover",
238
  ]
239
 
 
 
 
 
240
  for tag in soup.find_all(True):
241
  has_event = any(tag.get(ea) for ea in event_attrs)
242
  is_educational_target = tag.name in _TAG_PEDAGOGY
@@ -245,25 +278,54 @@ def _annotate_html(soup, codet5_pipeline, comment_level):
245
  if not (is_educational_target or has_id_or_class or has_event):
246
  continue
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  role = tag.name.upper()
249
  structural_parts = []
 
250
  pedagogy = _TAG_PEDAGOGY.get(tag.name)
251
  if pedagogy:
252
  structural_parts.append(pedagogy)
253
 
 
254
  if tag.name == "img":
255
  src = tag.get("src", "")
256
  alt = tag.get("alt", "")
257
  if src:
258
- structural_parts.append(
259
- f"source: {src if len(src) <= 60 else src[:57] + '...'}"
260
- )
261
  if alt:
262
  structural_parts.append(f'alt text: "{alt}"')
263
  else:
264
- structural_parts.append(
265
- 'no alt text (needs alt="description" for accessibility)'
266
- )
267
  if tag.get("width"):
268
  structural_parts.append(f"width: {tag.get('width')}px")
269
  elif tag.name == "a" and tag.get("href"):
@@ -286,25 +348,13 @@ def _annotate_html(soup, codet5_pipeline, comment_level):
286
  )
287
  structural_parts.append(f"loads {lib} from CDN")
288
 
 
289
  final_summary = ""
290
- should_run_model = (
291
- (tag.name in _SEMANTIC_TAGS or tag.get("id"))
292
- and codet5_pipeline is not None
293
- and comment_level in ("detailed", "educational")
294
- )
295
- if should_run_model:
296
- # Enforce the "WHAT, WHY, IMPACT" pedagogy.
297
- # We explicitly tell it NOT to repeat the tag name to avoid the "Header: Header" redundancy.
298
- prompt = (
299
- f"Explain this web element for a beginner: WHAT it does, "
300
- f"WHY it is placed here in context, and what would "
301
- f"BREAK or happen to the visitor if it were removed. "
302
- f"IMPORTANT: DO NOT start your answer with the word '{tag.name}'. "
303
- f"Context: {_strip_class_attrs(tag)}"
304
- )
305
- raw = _invoke_ai(codet5_pipeline, prompt, max_new_tokens=100)
306
  final_summary = _sanitize_ai_text(raw, tag.name)
307
 
 
308
  indicator = ""
309
  if tag.get("id"):
310
  indicator = f"#{tag.get('id')}"
@@ -323,6 +373,7 @@ def _annotate_html(soup, codet5_pipeline, comment_level):
323
  tag.insert_before(soup.new_string("\n"))
324
  tag.insert_before(Comment(full_structural_comment))
325
 
 
326
  if tag.get("class"):
327
  c_val = tag.get("class")
328
  c_str = " ".join(c_val) if isinstance(c_val, list) else c_val
@@ -333,20 +384,16 @@ def _annotate_html(soup, codet5_pipeline, comment_level):
333
  parts = explained.split("\n<!-- WARNING:")
334
  tag.insert_before(Comment(f" CLASSES: {parts[0]} "))
335
  tag.insert_before(soup.new_string("\n"))
336
- tag.insert_before(
337
- Comment(
338
- f" WARNING:{parts[1].replace('-->', '').strip()} "
339
- )
340
- )
341
  else:
342
  tag.insert_before(Comment(f" CLASSES: {explained} "))
343
 
 
344
  for ea in event_attrs:
345
  if tag.get(ea):
346
  tag.insert_before(soup.new_string("\n"))
347
- tag.insert_before(
348
- Comment(f" EVENT: triggers {tag.get(ea)} on {ea[2:]} ")
349
- )
350
 
351
 
352
  CSS_PROP_COMMENTS = {
 
189
  return ""
190
 
191
 
192
+ def _invoke_ai_batch(codet5_pipeline, prompts, max_new_tokens=100, batch_size=8):
193
+ """Efficiently run multiple prompts through the transformer pipeline in a single batch."""
194
+ if codet5_pipeline is None or not prompts:
195
+ return [""] * len(prompts)
196
+
197
+ try:
198
+ # Pipeline handles list of strings and batching automatically
199
+ results = codet5_pipeline(
200
+ prompts,
201
+ max_new_tokens=max_new_tokens,
202
+ do_sample=False,
203
+ batch_size=batch_size
204
+ )
205
+
206
+ outputs = []
207
+ for res in results:
208
+ if isinstance(res, list):
209
+ outputs.append(res[0].get("generated_text", "").strip())
210
+ else:
211
+ outputs.append(res.get("generated_text", "").strip())
212
+ return outputs
213
+ except Exception as e:
214
+ logger.warning(f"Batch AI inference failed: {e}")
215
+ return [""] * len(prompts)
216
+
217
+
218
+
219
  def _truncate_to_tokens(text: str, max_tokens: int = 400) -> str:
220
  """Approximate token limit by word count (1 token ~ 0.75 words)."""
221
  words = text.split()
 
258
 
259
 
260
  def _annotate_html(soup, codet5_pipeline, comment_level):
261
+ """Three-layer HTML annotator: Structural / Classes / Events.
262
+ Uses batch inference for performance.
263
+ """
264
  event_attrs = [
265
  "onclick", "onsubmit", "onchange", "oninput",
266
  "onload", "onkeydown", "onmouseover",
267
  ]
268
 
269
+ # PASS 1: Identify targets and collect prompts
270
+ targets = []
271
+ prompts = []
272
+
273
  for tag in soup.find_all(True):
274
  has_event = any(tag.get(ea) for ea in event_attrs)
275
  is_educational_target = tag.name in _TAG_PEDAGOGY
 
278
  if not (is_educational_target or has_id_or_class or has_event):
279
  continue
280
 
281
+ should_run_ai = (
282
+ (tag.name in _SEMANTIC_TAGS or tag.get("id"))
283
+ and codet5_pipeline is not None
284
+ and comment_level in ("detailed", "educational")
285
+ )
286
+
287
+ prompt = None
288
+ if should_run_ai:
289
+ prompt = (
290
+ f"Explain this web element for a beginner: WHAT it does, "
291
+ f"WHY it is placed here in context, and what would "
292
+ f"BREAK or happen to the visitor if it were removed. "
293
+ f"IMPORTANT: DO NOT start your answer with the word '{tag.name}'. "
294
+ f"Context: {_strip_class_attrs(tag)}"
295
+ )
296
+ prompts.append(prompt)
297
+
298
+ targets.append({
299
+ "tag": tag,
300
+ "has_event": has_event,
301
+ "prompt_idx": len(prompts) - 1 if prompt else -1
302
+ })
303
+
304
+ # BATCH AI INVOCATION
305
+ ai_outputs = []
306
+ if prompts:
307
+ ai_outputs = _invoke_ai_batch(codet5_pipeline, prompts, max_new_tokens=100)
308
+
309
+ # PASS 2: Apply annotations
310
+ for target in targets:
311
+ tag = target["tag"]
312
  role = tag.name.upper()
313
  structural_parts = []
314
+
315
  pedagogy = _TAG_PEDAGOGY.get(tag.name)
316
  if pedagogy:
317
  structural_parts.append(pedagogy)
318
 
319
+ # Attribute-aware annotations
320
  if tag.name == "img":
321
  src = tag.get("src", "")
322
  alt = tag.get("alt", "")
323
  if src:
324
+ structural_parts.append(f"source: {src if len(src) <= 60 else src[:57] + '...'}")
 
 
325
  if alt:
326
  structural_parts.append(f'alt text: "{alt}"')
327
  else:
328
+ structural_parts.append('no alt text (needs alt="description" for accessibility)')
 
 
329
  if tag.get("width"):
330
  structural_parts.append(f"width: {tag.get('width')}px")
331
  elif tag.name == "a" and tag.get("href"):
 
348
  )
349
  structural_parts.append(f"loads {lib} from CDN")
350
 
351
+ # Apply AI summary
352
  final_summary = ""
353
+ if target["prompt_idx"] != -1 and target["prompt_idx"] < len(ai_outputs):
354
+ raw = ai_outputs[target["prompt_idx"]]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  final_summary = _sanitize_ai_text(raw, tag.name)
356
 
357
+ # Construct final comment
358
  indicator = ""
359
  if tag.get("id"):
360
  indicator = f"#{tag.get('id')}"
 
373
  tag.insert_before(soup.new_string("\n"))
374
  tag.insert_before(Comment(full_structural_comment))
375
 
376
+ # Classes annotation
377
  if tag.get("class"):
378
  c_val = tag.get("class")
379
  c_str = " ".join(c_val) if isinstance(c_val, list) else c_val
 
384
  parts = explained.split("\n<!-- WARNING:")
385
  tag.insert_before(Comment(f" CLASSES: {parts[0]} "))
386
  tag.insert_before(soup.new_string("\n"))
387
+ tag.insert_before(Comment(f" WARNING:{parts[1].replace('-->', '').strip()} "))
 
 
 
 
388
  else:
389
  tag.insert_before(Comment(f" CLASSES: {explained} "))
390
 
391
+ # Events annotation
392
  for ea in event_attrs:
393
  if tag.get(ea):
394
  tag.insert_before(soup.new_string("\n"))
395
+ tag.insert_before(Comment(f" EVENT: triggers {tag.get(ea)} on {ea[2:]} "))
396
+
 
397
 
398
 
399
  CSS_PROP_COMMENTS = {
github_utils.py CHANGED
@@ -97,9 +97,13 @@ def get_repository(username, repo_name, token):
97
  repo = g.get_repo(f"{username}/{repo_name}")
98
  return repo
99
  except Exception as e:
100
- print(f"Repository not found: {e}")
 
 
 
101
  return None
102
 
 
103
  def update_file_in_repo(repo, file_path, content, commit_message="Update file"):
104
  """
105
  Update an existing file in the repository.
 
97
  repo = g.get_repo(f"{username}/{repo_name}")
98
  return repo
99
  except Exception as e:
100
+ if "404" in str(e):
101
+ print(f"Info: Repository '{repo_name}' not found on GitHub. A new one will be created.")
102
+ else:
103
+ print(f"GitHub Repository lookup error: {e}")
104
  return None
105
 
106
+
107
  def update_file_in_repo(repo, file_path, content, commit_message="Update file"):
108
  """
109
  Update an existing file in the repository.