Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| import pandas as pd | |
| def load_known_tokens(csv_path: str = "content/asl_app_data/asl_video_index_final_with_path_cleaned.csv") -> list[str]: | |
| """Load vocabulary from CSV — single words only for prompt constraint.""" | |
| try: | |
| df = pd.read_csv(csv_path) | |
| tokens = df['token'].dropna().str.upper().str.strip().unique().tolist() | |
| # Single words only — more useful for Gemma constraint | |
| single = sorted([t for t in tokens if ' ' not in t and len(t) > 1]) | |
| return single | |
| except Exception as e: | |
| print(f"⚠️ Could not load tokens from CSV: {e}") | |
| # Fallback: words we know we have | |
| return [ | |
| "HELLO", "PLEASE", "COME", "CLEAN", "YOU", "AND", "ANY", | |
| "ASK", "ANSWER", "ANOTHER", "ANYONE", "ANYTHING", "ANYWHERE", | |
| "APOLOGY", "APPRECIATE", "APPROACH", "ARMY", "AROUND", | |
| "ARRIVE", "ART", "ASSIST", "ATTEND", "ATTENTION", "ATTITUDE", | |
| "ANGER", "ANIMAL", "ANNOUNCE", "ANNOY", "ANNOYED", "ANXIOUS", | |
| "APART", "APARTMENT", "APPLE", "ARROGANT", "ASSOCIATE", | |
| "ASSUME", "ATTRACT", "AUNT" | |
| ] | |
| def build_gloss_sequence( | |
| transcript: str, | |
| model_name: str, | |
| max_tokens: int = 60, | |
| csv_path: str = "content/asl_app_data/asl_video_index_final_with_path_cleaned.csv" | |
| ) -> list[str]: | |
| """ | |
| Convert English transcript to ASL gloss tokens using Gemma. | |
| Uses constrained prompt to maximize hits against known vocabulary. | |
| Falls back to simple uppercase split if API fails. | |
| """ | |
| known_tokens = load_known_tokens(csv_path) | |
| # Take first 300 tokens for prompt — keep it under context limit | |
| vocab_sample = ', '.join(known_tokens[:300]) | |
| prompt = f"""You are an expert ASL (American Sign Language) gloss translator. | |
| STRICT RULES: | |
| 1. DROP all articles: a, an, the | |
| 2. DROP all linking verbs: is, are, was, were, am, be, been, being | |
| 3. DROP all prepositions: to, of, for, in, on, at, by, with, from, into | |
| 4. USE present tense root form only: CLEAN not CLEANING, DELAY not DELAYED, ARRIVE not ARRIVED | |
| 5. ONLY output words from this vocabulary list: {vocab_sample} | |
| 6. If a word has no match, find the CLOSEST SYNONYM from the vocabulary | |
| 7. Output ONLY uppercase tokens separated by spaces — no punctuation, no explanation | |
| EXAMPLES: | |
| English: "Hello, how are you doing today?" | |
| ASL Gloss: HELLO YOU | |
| English: "Please clean the dishes" | |
| ASL Gloss: PLEASE CLEAN | |
| English: "I am anxious about the appointment" | |
| ASL Gloss: ANXIOUS APPOINT | |
| English: "Can you come here and help me?" | |
| ASL Gloss: COME ASSIST | |
| English: "{transcript}" | |
| ASL Gloss:""" | |
| api_url = f"https://api-inference.huggingface.co/models/google/gemma-2b-it" | |
| headers = {"Authorization": f"Bearer {os.environ.get('HF_TOKEN')}"} | |
| payload = { | |
| "inputs": prompt, | |
| "parameters": { | |
| "max_new_tokens": max_tokens, | |
| "return_full_text": False, | |
| "temperature": 0.1, # Low temp = more deterministic/constrained | |
| "do_sample": False | |
| } | |
| } | |
| try: | |
| response = requests.post(api_url, headers=headers, json=payload, timeout=20) | |
| output = response.json() | |
| raw = "" | |
| if isinstance(output, list) and len(output) > 0: | |
| raw = output[0].get("generated_text", "").strip() | |
| elif isinstance(output, dict): | |
| raw = output.get("generated_text", "").strip() | |
| if not raw: | |
| print(f"⚠️ Empty Gemma response, falling back. Raw: {output}") | |
| return _simple_gloss(transcript) | |
| # Clean output — take only first line, strip non-alpha | |
| first_line = raw.split('\n')[0].strip() | |
| tokens = [ | |
| t.upper() for t in first_line.split() | |
| if t.isalpha() and t.upper() not in {"ASL", "GLOSS", "ENGLISH"} | |
| ] | |
| if not tokens: | |
| return _simple_gloss(transcript) | |
| print(f"✅ Gemma gloss: {tokens}") | |
| return tokens | |
| except Exception as e: | |
| print(f"⚠️ Gemma API failed: {e}. Falling back to simple gloss.") | |
| return _simple_gloss(transcript) | |
| def _simple_gloss(transcript: str) -> list[str]: | |
| """ | |
| Fallback: strip stopwords and return uppercase tokens. | |
| Used when Gemma API is unavailable. | |
| """ | |
| STOPWORDS = { | |
| "A", "AN", "THE", "IS", "ARE", "WAS", "WERE", "AM", "BE", "BEEN", | |
| "BEING", "TO", "OF", "FOR", "IN", "ON", "AT", "BY", "WITH", "FROM", | |
| "INTO", "AND", "BUT", "OR", "SO", "IF", "AS", "IT", "ITS", "THIS", | |
| "THAT", "THESE", "THOSE", "MY", "YOUR", "HIS", "HER", "OUR", "THEIR" | |
| } | |
| import re | |
| words = re.findall(r"[A-Za-z]+", transcript) | |
| return [w.upper() for w in words if w.upper() not in STOPWORDS] |