ryanmiyazato commited on
Commit
afe0c05
·
verified ·
1 Parent(s): 46ab765

Upload 7 files

Browse files
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ embeddings_glossary.json filter=lfs diff=lfs merge=lfs -text
2
+ embeddings_quality.json filter=lfs diff=lfs merge=lfs -text
README (1).md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: DustLookGradio
3
+ emoji: 🌖
4
+ colorFrom: green
5
+ colorTo: red
6
+ sdk: gradio
7
+ sdk_version: 5.49.1
8
+ app_file: app.py
9
+ pinned: false
10
+ license: unknown
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json, html, numpy as np, torch, gradio as gr
2
+ from sentence_transformers import SentenceTransformer
3
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer, BitsAndBytesConfig
4
+ from threading import Thread
5
+
6
+
7
+ class Config:
8
+ EMBEDDINGS_FILE = "embeddings_quality.json"
9
+ MODEL_ID = "HuggingFaceTB/SmolLM2-135M-Instruct"
10
+ TOP_K = 5
11
+ SIM_THRESHOLD = 0.36
12
+ MAX_NEW_TOKENS = 512
13
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
14
+
15
+
16
+ cfg = Config()
17
+
18
+
19
+ def safe_strip(x: str) -> str:
20
+ return x.replace("\n", " ").replace("\r", " ").strip() if isinstance(x, str) else ""
21
+
22
+
23
+ def load_entries(path):
24
+ with open(path, "r", encoding="utf-8") as f:
25
+ data = json.load(f)
26
+ entries = []
27
+ for v in data.values():
28
+ m = v.get("metadata", {})
29
+ title = m.get("title") or v.get("title") or ""
30
+ definition = m.get("definition") or v.get("definition") or m.get("content") or ""
31
+ source = m.get("source") or v.get("source") or ""
32
+ emb = np.array(v.get("embedding", []), dtype=np.float32)
33
+ if emb.size == 0:
34
+ continue
35
+ emb = emb / np.linalg.norm(emb)
36
+ entries.append({
37
+ "title": safe_strip(title),
38
+ "definition": safe_strip(definition),
39
+ "source": safe_strip(source),
40
+ "embedding": emb
41
+ })
42
+ vectors = np.stack([e["embedding"] for e in entries])
43
+ return entries, vectors
44
+
45
+
46
+ def init_models():
47
+ """Initialize all models and load data"""
48
+ # 1. Load embedding model for semantic search
49
+ embed_model = SentenceTransformer("all-MiniLM-L6-v2", device=cfg.DEVICE)
50
+
51
+ # 2. Load tokenizer and model for text generation
52
+ tokenizer = AutoTokenizer.from_pretrained(cfg.MODEL_ID)
53
+
54
+ # Add pad token if missing
55
+ if tokenizer.pad_token is None:
56
+ tokenizer.pad_token = tokenizer.eos_token
57
+
58
+ # Load model without quantization to avoid bitsandbytes issues
59
+ model = AutoModelForCausalLM.from_pretrained(
60
+ cfg.MODEL_ID,
61
+ device_map="auto",
62
+ torch_dtype=torch.float16 if cfg.DEVICE == "cuda" else torch.float32
63
+ )
64
+
65
+ # 3. Load entries and vectors
66
+ entries, vectors = load_entries(cfg.EMBEDDINGS_FILE)
67
+
68
+ return embed_model, tokenizer, model, entries, vectors
69
+
70
+
71
+ embed_model, tokenizer, model, entries, vectors = init_models()
72
+
73
+
74
+ def search_chunks(query, top_k=cfg.TOP_K, batch_size=512):
75
+ """Memory-efficient cosine similarity search."""
76
+ import heapq
77
+
78
+ # Encode query as normalized float32 vector
79
+ qv = embed_model.encode([query], normalize_embeddings=True,
80
+ convert_to_numpy=True).astype("float32")[0]
81
+
82
+ # Use a small max-heap to store the best results
83
+ heap = [] # stores (-similarity, index)
84
+
85
+ n = len(entries)
86
+ for start in range(0, n, batch_size):
87
+ end = min(start + batch_size, n)
88
+ # Instead of dotting all vectors, dot only a slice
89
+ sims = np.dot(vectors[start:end], qv)
90
+ for j, s in enumerate(sims):
91
+ if s < cfg.SIM_THRESHOLD:
92
+ continue
93
+ heapq.heappush(heap, (-s, start + j))
94
+ if len(heap) > top_k:
95
+ heapq.heappop(heap) # maintain top_k only
96
+
97
+ # Convert heap to sorted list (descending order)
98
+ results = [(-s, entries[i]) for s, i in sorted(heap)]
99
+ return [(e, float(s)) for s, e in results]
100
+
101
+
102
+ def build_context(entries, tokenizer, max_tokens=1500):
103
+ ctx, t = [], 0
104
+ for e in entries:
105
+ txt = f"{e['title']}: {e['definition']}\n"
106
+ tok = tokenizer.encode(txt, add_special_tokens=False)
107
+ if t + len(tok) > max_tokens:
108
+ break
109
+ ctx.append(txt)
110
+ t += len(tok)
111
+ return "\n".join(ctx)
112
+
113
+
114
+ def generate_answer(question, context_entries):
115
+ ctx_text = build_context(context_entries, tokenizer)
116
+ prompt = f"""<|im_start|>system
117
+ You are an expert on fighting game terminology. Use only the CONTEXT below to answer the QUESTION clearly using english language and proper structure.
118
+ <|im_end|>
119
+ <|im_start|>user
120
+ CONTEXT:
121
+ {ctx_text}
122
+
123
+ QUESTION: {question}
124
+ <|im_end|>
125
+ <|im_start|>assistant
126
+ """
127
+ inputs = tokenizer(prompt, return_tensors="pt").to(cfg.DEVICE)
128
+ streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
129
+ kwargs = dict(
130
+ **inputs,
131
+ max_new_tokens=cfg.MAX_NEW_TOKENS,
132
+ temperature=0.5,
133
+ top_p=0.9,
134
+ do_sample=True,
135
+ pad_token_id=tokenizer.eos_token_id,
136
+ eos_token_id=tokenizer.eos_token_id,
137
+ streamer=streamer
138
+ )
139
+ Thread(target=model.generate, kwargs=kwargs).start()
140
+ partial = ""
141
+ for token in streamer:
142
+ partial += token
143
+ yield partial
144
+
145
+
146
+ def qa_pipeline(question):
147
+ """Unified pipeline that streams search results first, then LLM answer."""
148
+ results = search_chunks(question)
149
+
150
+ # Build top results HTML immediately
151
+ if not results:
152
+ top_html = "<p>No relevant entries found.</p>"
153
+ yield top_html, "Your question is out of scope."
154
+ return
155
+
156
+ html_out = "<h4>Top Relevant Entries:</h4>"
157
+ for i, (e, s) in enumerate(results, 1):
158
+ html_out += f"<details open><summary><b>[{i}] (score: {s:.3f}) {html.escape(e['title'])}</b></summary><p>{html.escape(e['definition'][:500])}</p><p><i>{html.escape(e['source'])}</i></p></details>"
159
+
160
+ # Yield search results immediately
161
+ yield html_out, ""
162
+
163
+ # Then stream the LLM response
164
+ entries_only = [r[0] for r in results]
165
+ partial = ""
166
+ for token in generate_answer(question, entries_only):
167
+ partial = token
168
+ yield html_out, partial
169
+
170
+
171
+ with gr.Blocks(title="Fighting Game Glossary QA") as demo:
172
+ gr.Markdown("## 🎮 Fighting Game Glossary QA\nAsk about any fighting game term.")
173
+ q = gr.Textbox(label="Ask a question:", placeholder="e.g., What is a Roman Cancel?")
174
+ top = gr.HTML(label="Top Matches")
175
+ out = gr.Textbox(label="LLM Answer", lines=15, interactive=False, show_copy_button=True)
176
+ btn = gr.Button("Search & Answer")
177
+
178
+ # Use a single click event that streams both outputs
179
+ btn.click(fn=qa_pipeline, inputs=q, outputs=[top, out], queue=True)
180
+
181
+ demo.queue().launch()
embeddings_glossary.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f9522ed71ee174349ba2ef3789f0db9a85f832f630b5c2db64be8db569425dcb
3
+ size 11349754
embeddings_quality.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:acdebed75d66a96d60a877262026e832faa3eae141a351d824b4226a03fc6afe
3
+ size 330053950
gitattributes ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ embeddings_all.json filter=lfs diff=lfs merge=lfs -text
init_models.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from sentence_transformers import SentenceTransformer
3
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer, BitsAndBytesConfig
4
+
5
+
6
+ from projeto.app import cfg, load_entries
7
+
8
+
9
+ def debug_json_structure(path):
10
+ """Debug para ver a estrutura real do JSON"""
11
+ with open(path, "r", encoding="utf-8") as f:
12
+ data = json.load(f)
13
+
14
+ print("🔍 DEBUG DA ESTRUTURA DO JSON:")
15
+
16
+ # Ver as primeiras 3 chaves para ver a estrutura completa
17
+ for i, (key, value) in enumerate(list(data.items())[:3]):
18
+ print(f"\n--- Chave {i + 1}: '{key}' ---")
19
+ print(f"Tipo do valor: {type(value)}")
20
+ if isinstance(value, dict):
21
+ print(f"Campos: {list(value.keys())}")
22
+ for k, v in value.items():
23
+ if k == "embedding":
24
+ print(f" {k}: [lista com {len(v) if isinstance(v, list) else '?'} elementos]")
25
+ else:
26
+ print(f" {k}: {str(v)[:100]}{'...' if len(str(v)) > 100 else ''}")
27
+ print("---")
28
+
29
+ # Procurar especificamente por "Faust" para ver sua estrutura
30
+ print("\n🔍 PROCURANDO POR 'Faust' NO JSON:")
31
+ faust_found = False
32
+ for key, value in data.items():
33
+ if "Faust" in key or (
34
+ isinstance(value, dict) and "Faust" in str(value.get('title', '')) + str(value.get('term', ''))):
35
+ print(f"Encontrado Faust na chave: '{key}'")
36
+ print(f"Estrutura: {value}")
37
+ faust_found = True
38
+ break
39
+
40
+ if not faust_found:
41
+ print("Faust não encontrado nas primeiras verificações")
42
+
43
+ def init_models():
44
+ """Initialize all models and load data"""
45
+ # 1. Load embedding model for semantic search
46
+ embed_model = SentenceTransformer("all-MiniLM-L6-v2", device=cfg.DEVICE)
47
+
48
+ # 2. Load tokenizer and model for text generation
49
+ tokenizer = AutoTokenizer.from_pretrained(cfg.MODEL_ID)
50
+
51
+ # Add pad token if missing
52
+ if tokenizer.pad_token is None:
53
+ tokenizer.pad_token = tokenizer.eos_token
54
+
55
+ # Load model without quantization to avoid bitsandbytes issues
56
+ model = AutoModelForCausalLM.from_pretrained(
57
+ cfg.MODEL_ID,
58
+ device_map="auto",
59
+ torch_dtype=torch.float16 if cfg.DEVICE == "cuda" else torch.float32
60
+ )
61
+
62
+ # 3. Load entries and vectors
63
+ print("Carregando embeddings...")
64
+ entries, vectors = load_entries(cfg.EMBEDDINGS_FILE)
65
+
66
+ if len(entries) == 0:
67
+ raise Exception("Nenhuma entrada foi carregada! Verifique o arquivo de embeddings.")
68
+
69
+ return embed_model, tokenizer, model, entries, vectors
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ bitsandbytes
2
+ torch>=2.0.0
3
+ transformers>=4.30.0
4
+ sentence-transformers>=2.2.0
5
+ gradio>=4.0.0
6
+ accelerate>=0.20.0
7
+ numpy>=1.21.0