sara.mesquita commited on
Commit
284102c
·
1 Parent(s): e0c97ee

Changes nemotron model

Browse files
Files changed (6) hide show
  1. README.md +10 -11
  2. app.py +34 -1
  3. core/ai.py +75 -15
  4. index.html +1 -1
  5. setup-git.sh +1 -1
  6. static/app.js +19 -0
README.md CHANGED
@@ -25,7 +25,7 @@ tags:
25
  - cosine-similarity
26
  models:
27
  - meta-llama/Llama-3.2-11B-Vision-Instruct
28
- - nvidia/Nemotron-Mini-4B-Instruct
29
  - sentence-transformers/all-MiniLM-L6-v2
30
  ---
31
 
@@ -64,7 +64,7 @@ flowchart TD
64
  A["📷 Photo from phone"] --> B["🤖 Vision AI\nLlama-3.2-11B-Vision\n(NVIDIA NIM / HF)"]
65
  B -->|"is it an animal?"| C{Animal detected?}
66
  C -->|"No"| ERR["❌ Error message\n'No dog or cat identified'"]
67
- C -->|"Yes"| D["📝 Structured JSON\nNemotron Mini 4B Instruct structures\nspecies · breed · color · condition · marks"]
68
  D --> E["🔢 Semantic embedding\nall-MiniLM-L6-v2 · 384 dim\n(local, 22M params)"]
69
  E --> F{Cosine similarity\n≥ 0.80?}
70
  F -->|"Yes — known animal"| G["➕ New sighting\nadded to existing animal"]
@@ -106,14 +106,14 @@ The map uses color to signal urgency: **🟢 green** = dog · **🟠 orange** =
106
 
107
  | Component | Model / Library | Where it runs |
108
  |---|---|---|
109
- | Visual identification | **Llama-3.2-11B-Vision-Instruct** (11B) via NVIDIA NIM (or HF Serverless) | remote API |
110
- | Text reasoning / JSON | **Nemotron Mini 4B Instruct** via NVIDIA NIM | remote API |
111
  | Semantic embedding | **all-MiniLM-L6-v2** (384 dim, 22M params) | CPU, local |
112
  | Animal matching | Cosine similarity (threshold 0.80) | CPU, local |
113
  | Database | SQLite | local / persistent |
114
  | Frontend | SPA via `gradio.Server` + Leaflet.js + Lucide Icons | browser |
115
 
116
- **Total parameters running locally: ~22M** (MiniLM). The vision and reasoning models run via serverless API — nothing heavy on the Space machine. **Every model used is well under the 32B hackathon cap:** Llama-3.2-11B-Vision (11B), Nemotron Mini 4B Instruct (4B) and MiniLM-L6-v2 (22M).
117
 
118
  ---
119
 
@@ -136,8 +136,7 @@ This submission earns the following hackathon bonus quests:
136
 
137
  - 🎨 **Off-Brand** — Fully custom interface via `gradio.Server`, no default Gradio components visible. SPA frontend with Leaflet.js for the map, Lucide Icons, and custom design.
138
  - 📡 **Sharing is Caring** — Full traces of every sighting (photo → AI analysis → embedding → matching result) are automatically published as a dataset on the Hub via `/admin/push-traces`.
139
- - 🤏 **Tiny Titan** — The only on-device model is `all-MiniLM-L6-v2` (22M params, far under 4B): all animal matching runs locally on a tiny embedding model.
140
- - 🟩 **NVIDIA / Nemotron** — Structured identification is normalized by `Nemotron-Mini-4B-Instruct` via NVIDIA NIM — small (4B), well under the 32B cap.
141
 
142
  ---
143
 
@@ -148,7 +147,7 @@ git clone https://huggingface.co/spaces/build-small-hackathon/viralata-mapper
148
  cd viralata-mapper
149
  pip install -r requirements.txt
150
 
151
- # Recommended: NVIDIA NIM powers both vision (Llama-3.2-11B-Vision) and Nemotron Mini 4B
152
  NVIDIA_API_KEY=nvapi_... python app.py
153
 
154
  # Alternative: vision via HuggingFace Serverless
@@ -170,7 +169,7 @@ On first run the app seeds the database with demo animals around Brasília so th
170
 
171
  | Secret | Description |
172
  |---|---|
173
- | `NVIDIA_API_KEY` | NVIDIA NIM key — powers **both** vision (Llama-3.2-11B-Vision) and Nemotron Mini 4B normalization. A single key is enough. |
174
  | `HF_TOKEN` | Optional — vision via HF Serverless if you don't use NVIDIA NIM |
175
  | `MATCH_THRESHOLD` | Optional. Similarity threshold. Default: `0.80` |
176
  | `HF_DATASET_ID` | Optional. Hub dataset for trace publishing (e.g. `org/dataset-name`) |
@@ -223,7 +222,7 @@ Set up a **Persistent Storage Bucket** in the Space and mount it at `/data/` so
223
  ## Credits
224
 
225
  - **[Meta](https://ai.meta.com/llama/)** — Llama 3.2 11B Vision Instruct (Llama 3.2 Community License).
226
- - **[NVIDIA](https://build.nvidia.com/)** — Nemotron Mini 4B Instruct via NVIDIA NIM.
227
  - **[Hugging Face](https://huggingface.co/)** — `sentence-transformers/all-MiniLM-L6-v2`, Serverless Inference, Space hosting.
228
  - **[Leaflet.js](https://leafletjs.com/)** — interactive map.
229
  - **[Gradio](https://gradio.app/)** — `gradio.Server` for the custom frontend.
@@ -233,7 +232,7 @@ Set up a **Persistent Storage Bucket** in the Space and mount it at `/data/` so
233
 
234
  ## License
235
 
236
- MIT for the application code. Models follow their own licenses — see the Meta (Llama 3.2) and NVIDIA (Nemotron Mini 4B Instruct) model cards for full terms.
237
 
238
  ---
239
 
 
25
  - cosine-similarity
26
  models:
27
  - meta-llama/Llama-3.2-11B-Vision-Instruct
28
+ - nvidia/nemotron-3-nano-30b-a3b
29
  - sentence-transformers/all-MiniLM-L6-v2
30
  ---
31
 
 
64
  A["📷 Photo from phone"] --> B["🤖 Vision AI\nLlama-3.2-11B-Vision\n(NVIDIA NIM / HF)"]
65
  B -->|"is it an animal?"| C{Animal detected?}
66
  C -->|"No"| ERR["❌ Error message\n'No dog or cat identified'"]
67
+ C -->|"Yes"| D["🧠 Nemotron 3 Nano (brain)\nreasons over the description \nstructured JSON: species · breed · color · condition · marks"]
68
  D --> E["🔢 Semantic embedding\nall-MiniLM-L6-v2 · 384 dim\n(local, 22M params)"]
69
  E --> F{Cosine similarity\n≥ 0.80?}
70
  F -->|"Yes — known animal"| G["➕ New sighting\nadded to existing animal"]
 
106
 
107
  | Component | Model / Library | Where it runs |
108
  |---|---|---|
109
+ | Visual perception | **Llama-3.2-11B-Vision-Instruct** (11B) via NVIDIA NIM (or HF Serverless) | remote API |
110
+ | Reasoning brain (structuring + animal stories) | **Nemotron 3 Nano** (`nvidia/nemotron-3-nano-30b-a3b`, MoE — 3B active) via NVIDIA NIM | remote API |
111
  | Semantic embedding | **all-MiniLM-L6-v2** (384 dim, 22M params) | CPU, local |
112
  | Animal matching | Cosine similarity (threshold 0.80) | CPU, local |
113
  | Database | SQLite | local / persistent |
114
  | Frontend | SPA via `gradio.Server` + Leaflet.js + Lucide Icons | browser |
115
 
116
+ **Total parameters running locally: ~22M** (MiniLM). Vision and the Nemotron brain run via serverless NVIDIA NIM — nothing heavy on the Space machine. **The hackathon cap is per model, not summed** ([per the FAQ](https://build-small-hackathon-field-guide.hf.space/#faq)), and every model is under 32B: Llama-3.2-11B-Vision (11B), Nemotron 3 Nano (30B total but only **3B active** per call, served serverless) and MiniLM-L6-v2 (22M).
117
 
118
  ---
119
 
 
136
 
137
  - 🎨 **Off-Brand** — Fully custom interface via `gradio.Server`, no default Gradio components visible. SPA frontend with Leaflet.js for the map, Lucide Icons, and custom design.
138
  - 📡 **Sharing is Caring** — Full traces of every sighting (photo → AI analysis → embedding → matching result) are automatically published as a dataset on the Hub via `/admin/push-traces`.
139
+ - 🟩 **NVIDIA / Nemotron** — **Nemotron 3 Nano** (`nvidia/nemotron-3-nano-30b-a3b`, MoE with 3B active) is the reasoning brain of the app: it structures every animal identification into the canonical record that drives matching, **and** writes each animal's story shown on its profile. The vision model only perceives the photo — Nemotron does the thinking.
 
140
 
141
  ---
142
 
 
147
  cd viralata-mapper
148
  pip install -r requirements.txt
149
 
150
+ # Recommended: NVIDIA NIM powers both vision (Llama-3.2-11B-Vision) and the Nemotron 3 Nano brain
151
  NVIDIA_API_KEY=nvapi_... python app.py
152
 
153
  # Alternative: vision via HuggingFace Serverless
 
169
 
170
  | Secret | Description |
171
  |---|---|
172
+ | `NVIDIA_API_KEY` | NVIDIA NIM key — powers **both** vision (Llama-3.2-11B-Vision) and the Nemotron 3 Nano brain (structuring + stories). A single key is enough. |
173
  | `HF_TOKEN` | Optional — vision via HF Serverless if you don't use NVIDIA NIM |
174
  | `MATCH_THRESHOLD` | Optional. Similarity threshold. Default: `0.80` |
175
  | `HF_DATASET_ID` | Optional. Hub dataset for trace publishing (e.g. `org/dataset-name`) |
 
222
  ## Credits
223
 
224
  - **[Meta](https://ai.meta.com/llama/)** — Llama 3.2 11B Vision Instruct (Llama 3.2 Community License).
225
+ - **[NVIDIA](https://build.nvidia.com/)** — Nemotron 3 Nano (`nvidia/nemotron-3-nano-30b-a3b`) via NVIDIA NIM.
226
  - **[Hugging Face](https://huggingface.co/)** — `sentence-transformers/all-MiniLM-L6-v2`, Serverless Inference, Space hosting.
227
  - **[Leaflet.js](https://leafletjs.com/)** — interactive map.
228
  - **[Gradio](https://gradio.app/)** — `gradio.Server` for the custom frontend.
 
232
 
233
  ## License
234
 
235
+ MIT for the application code. Models follow their own licenses — see the Meta (Llama 3.2) and NVIDIA (Nemotron 3 Nano / NVIDIA Open Model License) model cards for full terms.
236
 
237
  ---
238
 
app.py CHANGED
@@ -46,6 +46,10 @@ def _photo_url(photo_path: str) -> str:
46
  # In-memory session store for analyze → confirm two-step flow
47
  _pending: dict[str, dict] = {}
48
 
 
 
 
 
49
  app = Server()
50
 
51
  # Serve photos as static files at /photos/...
@@ -97,7 +101,36 @@ async def get_animal(animal_id: int):
97
  s["photo_url"] = _photo_url(s.get("photo_path") or "")
98
  for h in detail.get("help_events", []):
99
  h["photo_url"] = _photo_url(h.get("photo_path") or "")
100
- detail.get("animal", {}).pop("embedding", None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  return JSONResponse(content=detail)
102
 
103
 
 
46
  # In-memory session store for analyze → confirm two-step flow
47
  _pending: dict[str, dict] = {}
48
 
49
+ # Cache da historia gerada pelo Nemotron, por (animal_id, n_eventos).
50
+ # Re-gera so quando ha avistamento/ajuda novo; evita chamada de API a cada visita.
51
+ _story_cache: dict[tuple[int, int], str] = {}
52
+
53
  app = Server()
54
 
55
  # Serve photos as static files at /photos/...
 
101
  s["photo_url"] = _photo_url(s.get("photo_path") or "")
102
  for h in detail.get("help_events", []):
103
  h["photo_url"] = _photo_url(h.get("photo_path") or "")
104
+ animal = detail.get("animal", {})
105
+ animal.pop("embedding", None)
106
+
107
+ # Historia do animal redigida pelo Nemotron 3 Nano (cerebro do app).
108
+ # Cacheada por (id, n_eventos) e degrada para "" sem quebrar o perfil.
109
+ try:
110
+ sightings = detail.get("sightings", [])
111
+ help_events = detail.get("help_events", [])
112
+ n_events = len(sightings) + len(help_events)
113
+ cache_key = (animal_id, n_events)
114
+ if cache_key in _story_cache:
115
+ detail["story"] = _story_cache[cache_key]
116
+ else:
117
+ desc_obj = json.loads(animal.get("description") or "{}")
118
+ location_hint = next(
119
+ (f"Lat {s['lat']:.3f}, Lng {s['lng']:.3f}"
120
+ for s in sightings if s.get("lat") and s.get("lng")),
121
+ "",
122
+ )
123
+ story = ai.generate_story(
124
+ desc_obj,
125
+ sighting_count=len(sightings),
126
+ help_count=len(help_events),
127
+ location_hint=location_hint,
128
+ )
129
+ _story_cache[cache_key] = story
130
+ detail["story"] = story
131
+ except Exception:
132
+ detail["story"] = ""
133
+
134
  return JSONResponse(content=detail)
135
 
136
 
core/ai.py CHANGED
@@ -1,17 +1,22 @@
1
  """
2
- ai.py — Visao + estruturacao de texto, ambas via NVIDIA NIM (ou HF como alternativa).
3
 
4
- Arquitetura (todos os modelos < 32B):
5
- - Visao : meta/llama-3.2-11b-vision-instruct (11B, NVIDIA NIM) -> NVIDIA_API_KEY
6
- (ou meta-llama/Llama-3.2-11B-Vision-Instruct via HF, se so houver HF_TOKEN)
7
- - Texto : nvidia/nemotron-mini-4b-instruct (4B, NVIDIA NIM) -> NVIDIA_API_KEY
8
- - Embed : sentence-transformers/all-MiniLM-L6-v2 (22M, local/CPU)
 
 
 
 
 
 
 
9
 
10
  Fluxo: o Llama-3.2-11B-Vision enxerga a foto e descreve o animal (texto livre —
11
- modelos de visao raramente cospem JSON limpo). Em seguida o Nemotron Mini 4B
12
- Instruct (texto puro) LE essa descricao e EXTRAI o JSON estrito (especie, raca da
13
- lista permitida, cor, condicao, frase de matching). Cada modelo no que e bom:
14
- Llama ve, Nemotron estrutura.
15
 
16
  Basta a NVIDIA_API_KEY: ela serve tanto para a visao quanto para o Nemotron.
17
  Sem NVIDIA_API_KEY mas com HF_TOKEN, a visao usa o HF Serverless e a estruturacao
@@ -21,7 +26,8 @@ Variaveis de ambiente:
21
  NVIDIA_API_KEY — chave NVIDIA NIM (free tier em build.nvidia.com)
22
  HF_TOKEN — token HuggingFace (alternativa de visao via HF Serverless)
23
  NVIDIA_VISION_MODEL — override do modelo de visao NIM (default meta/llama-3.2-11b-vision-instruct)
24
- NVIDIA_MODEL — override do modelo de texto NIM (default nvidia/nemotron-mini-4b-instruct)
 
25
  HF_VISION_MODEL — override do modelo de visao HF (default meta-llama/Llama-3.2-11B-Vision-Instruct)
26
  """
27
  import base64
@@ -37,7 +43,7 @@ log = logging.getLogger(__name__)
37
 
38
  _HF_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct" # id HF Serverless
39
  _NIM_VISION_MODEL = "meta/llama-3.2-11b-vision-instruct" # id NVIDIA NIM (11B, free)
40
- _NIM_TEXT_MODEL = "nvidia/nemotron-mini-4b-instruct" # id NVIDIA NIM (4B, free)
41
 
42
  _DOG_BREEDS = (
43
  "SRD, Labrador Retriever, Golden Retriever, German Shepherd, Bulldog, French Bulldog, "
@@ -210,11 +216,16 @@ class AnimalAI:
210
  return self._fallback()
211
 
212
  def _structure_with_nemotron(self, raw_text: str) -> dict:
213
- """Extrai JSON estrito da descricao livre da visao usando Nemotron Mini 4B Instruct."""
214
  try:
215
  resp = self.nim_client.chat.completions.create(
216
  model=self.nim_text_model,
217
- messages=[{"role": "user", "content": EXTRACT_PROMPT + raw_text}],
 
 
 
 
 
218
  max_tokens=500,
219
  temperature=0.2,
220
  top_p=0.9,
@@ -223,7 +234,7 @@ class AnimalAI:
223
  out = (msg.content or "") or (getattr(msg, "reasoning_content", "") or "")
224
  parsed = self._extract_json(out)
225
  if parsed is not None:
226
- log.info("Nemotron Mini 4B estruturou o JSON.")
227
  parsed["_ai_success"] = True
228
  return parsed
229
  log.warning("Nemotron nao retornou JSON — tentando parse direto da visao")
@@ -237,6 +248,55 @@ class AnimalAI:
237
  return direct
238
  return self._fallback()
239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  def get_embedding(self, description: dict) -> list:
241
  """Embedding da descricao. Aleatorio se IA falhou (evita falsos matches)."""
242
  if not description.get("_ai_success", True):
 
1
  """
2
+ ai.py — Visao + raciocinio/estruturacao de texto, ambas via NVIDIA NIM (ou HF como alternativa).
3
 
4
+ Arquitetura (cada modelo individualmente < 32B — teto e por modelo, nao somado):
5
+ - Visao : meta/llama-3.2-11b-vision-instruct (11B, NVIDIA NIM) -> NVIDIA_API_KEY
6
+ (ou meta-llama/Llama-3.2-11B-Vision-Instruct via HF, se so houver HF_TOKEN)
7
+ - Cerebro : nvidia/nemotron-3-nano-30b-a3b (MoE, 3B ATIVOS, NVIDIA NIM) -> NVIDIA_API_KEY
8
+ - Embed : sentence-transformers/all-MiniLM-L6-v2 (22M, local/CPU)
9
+
10
+ O Nemotron 3 Nano e o nucleo do app: alem de extrair o JSON estrito a partir da
11
+ descricao livre da visao (especie, raca da lista permitida, cor, condicao, frase de
12
+ matching), ele tambem REDIGE a "historia" de cada animal exibida no perfil
13
+ (generate_story). E um modelo de reasoning MoE (30B totais / 3B ativos por chamada,
14
+ 256k de contexto) servido na API hospedada da NVIDIA — so 3B ativos o tornam rapido
15
+ e barato, e o total de 30B fica abaixo do teto de 32B por modelo do hackathon.
16
 
17
  Fluxo: o Llama-3.2-11B-Vision enxerga a foto e descreve o animal (texto livre —
18
+ modelos de visao raramente cospem JSON limpo). Em seguida o Nemotron 3 Nano LE essa
19
+ descricao, raciocina e estrutura. Cada modelo no que e bom: Llama ve, Nemotron pensa.
 
 
20
 
21
  Basta a NVIDIA_API_KEY: ela serve tanto para a visao quanto para o Nemotron.
22
  Sem NVIDIA_API_KEY mas com HF_TOKEN, a visao usa o HF Serverless e a estruturacao
 
26
  NVIDIA_API_KEY — chave NVIDIA NIM (free tier em build.nvidia.com)
27
  HF_TOKEN — token HuggingFace (alternativa de visao via HF Serverless)
28
  NVIDIA_VISION_MODEL — override do modelo de visao NIM (default meta/llama-3.2-11b-vision-instruct)
29
+ NVIDIA_MODEL — override do cerebro de texto NIM (default nvidia/nemotron-3-nano-30b-a3b;
30
+ aponte para um Nemotron 3 Nano 4B self-hosted se rodar local)
31
  HF_VISION_MODEL — override do modelo de visao HF (default meta-llama/Llama-3.2-11B-Vision-Instruct)
32
  """
33
  import base64
 
43
 
44
  _HF_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct" # id HF Serverless
45
  _NIM_VISION_MODEL = "meta/llama-3.2-11b-vision-instruct" # id NVIDIA NIM (11B, free)
46
+ _NIM_TEXT_MODEL = "nvidia/nemotron-3-nano-30b-a3b" # id NVIDIA NIM (Nemotron 3 Nano MoE, 3B ativos)
47
 
48
  _DOG_BREEDS = (
49
  "SRD, Labrador Retriever, Golden Retriever, German Shepherd, Bulldog, French Bulldog, "
 
216
  return self._fallback()
217
 
218
  def _structure_with_nemotron(self, raw_text: str) -> dict:
219
+ """Extrai JSON estrito da descricao livre da visao usando Nemotron 3 Nano."""
220
  try:
221
  resp = self.nim_client.chat.completions.create(
222
  model=self.nim_text_model,
223
+ # "detailed thinking off": convencao do Nemotron 3 p/ suprimir traços de
224
+ # reasoning e devolver so a resposta — mantem o JSON limpo e a chamada barata.
225
+ messages=[
226
+ {"role": "system", "content": "detailed thinking off"},
227
+ {"role": "user", "content": EXTRACT_PROMPT + raw_text},
228
+ ],
229
  max_tokens=500,
230
  temperature=0.2,
231
  top_p=0.9,
 
234
  out = (msg.content or "") or (getattr(msg, "reasoning_content", "") or "")
235
  parsed = self._extract_json(out)
236
  if parsed is not None:
237
+ log.info("Nemotron 3 Nano estruturou o JSON.")
238
  parsed["_ai_success"] = True
239
  return parsed
240
  log.warning("Nemotron nao retornou JSON — tentando parse direto da visao")
 
248
  return direct
249
  return self._fallback()
250
 
251
+ def generate_story(self, animal: dict, sighting_count: int,
252
+ help_count: int = 0, location_hint: str = "") -> str:
253
+ """Redige uma breve historia do animal (1-2 frases) com o Nemotron 3 Nano.
254
+
255
+ E o cerebro do app dando voz ao perfil: a partir dos dados estruturados e do
256
+ historico, escreve uma narrativa curta em portugues para a pagina do animal.
257
+ Retorna "" se o Nemotron nao estiver disponivel ou falhar (o frontend so
258
+ mostra a historia quando ela existe — degrada sem quebrar).
259
+ """
260
+ if self.nim_client is None:
261
+ return ""
262
+
263
+ desc = animal if isinstance(animal, dict) else {}
264
+ facts = {
265
+ "species": desc.get("species", ""),
266
+ "breed": desc.get("breed_estimate", ""),
267
+ "color": desc.get("primary_color", ""),
268
+ "condition": desc.get("condition", ""),
269
+ "marks": desc.get("distinctive_marks", []),
270
+ "sightings": sighting_count,
271
+ "help_events": help_count,
272
+ "location_hint": location_hint,
273
+ }
274
+ prompt = (
275
+ "You write a SHORT story (1-2 sentences, max 40 words) in BRAZILIAN PORTUGUESE "
276
+ "for the public profile of a stray animal being tracked by a community map. "
277
+ "Warm, factual, no invented details. Output ONLY the sentence(s), no preamble.\n"
278
+ "FACTS (JSON):\n" + json.dumps(facts, ensure_ascii=False)
279
+ )
280
+ try:
281
+ resp = self.nim_client.chat.completions.create(
282
+ model=self.nim_text_model,
283
+ messages=[
284
+ {"role": "system", "content": "detailed thinking off"},
285
+ {"role": "user", "content": prompt},
286
+ ],
287
+ max_tokens=160,
288
+ temperature=0.6,
289
+ top_p=0.9,
290
+ )
291
+ msg = resp.choices[0].message
292
+ text = (msg.content or "").strip()
293
+ # Modelo de reasoning pode emitir bloco <think>...</think> — remove.
294
+ text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL).strip()
295
+ return text
296
+ except Exception as e:
297
+ log.warning("Nemotron generate_story falhou: %s", e)
298
+ return ""
299
+
300
  def get_embedding(self, description: dict) -> list:
301
  """Embedding da descricao. Aleatorio se IA falhou (evita falsos matches)."""
302
  if not description.get("_ai_success", True):
index.html CHANGED
@@ -581,7 +581,7 @@
581
  <h3>Tech stack</h3>
582
  <div class="stack-list">
583
  <div class="stack-row"><span class="stack-label">Vision AI</span><span class="stack-value">Llama 3.2 11B Vision Instruct</span></div>
584
- <div class="stack-row"><span class="stack-label">Embeddings</span><span class="stack-value"> NVIDIA Nemotron Mini 4B Instruct</span></div>
585
  <div class="stack-row"><span class="stack-label">Matching</span><span class="stack-value">all-MiniLM-L6-v2</span></div>
586
  <div class="stack-row"><span class="stack-label">Map</span><span class="stack-value">Leaflet.js + OSM</span></div>
587
  <div class="stack-row"><span class="stack-label">Frontend</span><span class="stack-value">gr.Server SPA</span></div>
 
581
  <h3>Tech stack</h3>
582
  <div class="stack-list">
583
  <div class="stack-row"><span class="stack-label">Vision AI</span><span class="stack-value">Llama 3.2 11B Vision Instruct</span></div>
584
+ <div class="stack-row"><span class="stack-label">Reasoning brain</span><span class="stack-value">NVIDIA Nemotron 3 Nano</span></div>
585
  <div class="stack-row"><span class="stack-label">Matching</span><span class="stack-value">all-MiniLM-L6-v2</span></div>
586
  <div class="stack-row"><span class="stack-label">Map</span><span class="stack-value">Leaflet.js + OSM</span></div>
587
  <div class="stack-row"><span class="stack-label">Frontend</span><span class="stack-value">gr.Server SPA</span></div>
setup-git.sh CHANGED
@@ -37,7 +37,7 @@ git add .
37
  git commit -m "feat: initial Animal Visto app
38
 
39
  - Gradio UI mobile-first com mapa Leaflet
40
- - Integração NVIDIA NIM (Nemotron Nano VL)
41
  - Matching por cosine similarity (sentence-transformers)
42
  - SQLite persistente via HF Storage Bucket"
43
 
 
37
  git commit -m "feat: initial Animal Visto app
38
 
39
  - Gradio UI mobile-first com mapa Leaflet
40
+ - Integração NVIDIA NIM (Nemotron 3 Nano + Llama 3.2 Vision)
41
  - Matching por cosine similarity (sentence-transformers)
42
  - SQLite persistente via HF Storage Bucket"
43
 
static/app.js CHANGED
@@ -323,6 +323,25 @@
323
  const statusText = daysSince === 0 ? 'Seen today' : daysSince === 1 ? 'Seen yesterday' : `Seen ${daysSince} days ago`;
324
  document.getElementById('profile-status-text').textContent = statusText;
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  // Identification
327
  document.getElementById('prof-species').innerHTML = `${svgIcon(isDog?'dog':'cat',16)} ${isDog ? 'Dog' : 'Cat'}`;
328
  document.getElementById('prof-breed').textContent = breed;
 
323
  const statusText = daysSince === 0 ? 'Seen today' : daysSince === 1 ? 'Seen yesterday' : `Seen ${daysSince} days ago`;
324
  document.getElementById('profile-status-text').textContent = statusText;
325
 
326
+ // História redigida pelo Nemotron 3 Nano (cérebro do app). Aparece só quando existe.
327
+ const story = (data.story || '').trim();
328
+ let storyEl = document.getElementById('profile-story');
329
+ const statusEl = document.getElementById('profile-status-text');
330
+ if (story) {
331
+ if (!storyEl) {
332
+ storyEl = document.createElement('p');
333
+ storyEl.id = 'profile-story';
334
+ storyEl.style.cssText = 'margin:.5rem 0 0;font-style:italic;opacity:.85;line-height:1.4;';
335
+ if (statusEl && statusEl.parentNode) {
336
+ statusEl.parentNode.insertBefore(storyEl, statusEl.nextSibling);
337
+ }
338
+ }
339
+ storyEl.textContent = story;
340
+ storyEl.style.display = '';
341
+ } else if (storyEl) {
342
+ storyEl.style.display = 'none';
343
+ }
344
+
345
  // Identification
346
  document.getElementById('prof-species').innerHTML = `${svgIcon(isDog?'dog':'cat',16)} ${isDog ? 'Dog' : 'Cat'}`;
347
  document.getElementById('prof-breed').textContent = breed;