LucasLooTan commited on
Commit
0d9b6c2
·
1 Parent(s): 9b576b6

fix: threading.Lock on LandmarkExtractor + composer prompt format match

Browse files

Two deep-check IMPORTANTs:
- B.F2 / A.F4: Gradio multi-threaded server can race the lazy
initialisation of _extractor_singleton; concurrent first-clicks
could build two MediaPipe instances. threading.Lock with
double-checked-locking pattern fixes it.
- C.F6: composer user prompt previously sent ', '.join(signs) while
the system prompt teaches the model with Python list syntax. Switch
user prompt to repr(list(signs)) so format matches examples — should
improve the 'My name is Lucas' hero demo.

signbridge/composer/sentence.py CHANGED
@@ -110,7 +110,9 @@ def compose_sentence(signs: Sequence[str]) -> str:
110
  return ""
111
 
112
  client, model = _resolve_client()
113
- user_prompt = "ASL signs: " + ", ".join(signs)
 
 
114
 
115
  if client is None:
116
  return _naive_join(signs)
 
110
  return ""
111
 
112
  client, model = _resolve_client()
113
+ # System prompt examples use Python-list syntax (e.g. ["L","U","C","A","S"]);
114
+ # match that format here so the LLM sees inputs and examples consistently.
115
+ user_prompt = f"ASL signs: {list(signs)!r}"
116
 
117
  if client is None:
118
  return _naive_join(signs)
signbridge/space.py CHANGED
@@ -17,6 +17,7 @@ from __future__ import annotations
17
 
18
  import logging
19
  import os
 
20
  from dataclasses import dataclass, field
21
 
22
  import gradio as gr
@@ -108,13 +109,22 @@ def _recognize(frame: np.ndarray) -> tuple[str, float]:
108
 
109
 
110
  _extractor_singleton: LandmarkExtractor | None = None
 
111
 
112
 
113
  def _shared_extractor() -> LandmarkExtractor:
 
 
 
 
 
114
  global _extractor_singleton
115
- if _extractor_singleton is None:
116
- _extractor_singleton = LandmarkExtractor()
117
- return _extractor_singleton
 
 
 
118
 
119
 
120
  def _capture_sign(
 
17
 
18
  import logging
19
  import os
20
+ import threading
21
  from dataclasses import dataclass, field
22
 
23
  import gradio as gr
 
109
 
110
 
111
  _extractor_singleton: LandmarkExtractor | None = None
112
+ _extractor_lock = threading.Lock()
113
 
114
 
115
  def _shared_extractor() -> LandmarkExtractor:
116
+ """Return the lazy-loaded MediaPipe Holistic extractor.
117
+
118
+ Double-checked locking so concurrent first-call requests under
119
+ Gradio's worker threads don't race and build two extractors.
120
+ """
121
  global _extractor_singleton
122
+ if _extractor_singleton is not None:
123
+ return _extractor_singleton
124
+ with _extractor_lock:
125
+ if _extractor_singleton is None:
126
+ _extractor_singleton = LandmarkExtractor()
127
+ return _extractor_singleton
128
 
129
 
130
  def _capture_sign(