Wayfinder6 commited on
Commit
8d34f0c
·
verified ·
1 Parent(s): fe1bb8d

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +3 -3
  2. app.py +41 -35
  3. requirements.txt +1 -1
README.md CHANGED
@@ -2,7 +2,7 @@
2
  title: WOOF
3
  emoji: "\U0001F415"
4
  colorFrom: yellow
5
- colorTo: yellow
6
  sdk: gradio
7
  sdk_version: "5.34.2"
8
  app_file: app.py
@@ -46,9 +46,9 @@ Same dad. Daughter has dyslexia. Mom has dementia. Dog has opinions about grass.
46
 
47
  ## The Stack
48
 
49
- - **Model:** MiniCPM5-1B (OpenBMB) via llama.cpp1B parameters
50
  - **Framework:** Gradio
51
- - **Cloud APIs:** Zero. Your dog's medical history stays private.
52
 
53
  ## Important
54
 
 
2
  title: WOOF
3
  emoji: "\U0001F415"
4
  colorFrom: yellow
5
+ colorTo: green
6
  sdk: gradio
7
  sdk_version: "5.34.2"
8
  app_file: app.py
 
46
 
47
  ## The Stack
48
 
49
+ - **Model:** NVIDIA Nemotron 3 Nano Omni via NIM API multimodal (text + vision)
50
  - **Framework:** Gradio
51
+ - **Fallback:** Rule-based analysis when API is unavailable
52
 
53
  ## Important
54
 
app.py CHANGED
@@ -13,31 +13,24 @@ their stomach hurts but their output can.
13
  """
14
 
15
  import gradio as gr
16
- # from llama_cpp import Llama
17
- from llama_cpp.llama_chat_format import MiniCPMv26ChatHandler
18
-
19
- # --- Model ---
20
- # MiniCPM-V for vision (can analyze images) — OpenBMB sponsor model
21
- # Falls back to text-only analysis if vision model unavailable
22
-
23
- MODEL_REPO = "openbmb/MiniCPM5-1B-128k-GGUF"
24
- MODEL_FILE = "minicpm5-1b-128k-q4_k_m.gguf"
25
-
26
- print("Loading model...")
27
- try:
28
- llm = Llama.from_pretrained(
29
- repo_id=MODEL_REPO,
30
- filename=MODEL_FILE,
31
- n_ctx=2048,
32
- n_threads=4,
33
- verbose=False,
34
  )
35
- MODEL_LOADED = False
36
- print("Model loaded.")
37
- except Exception as e:
38
- print(f"Model failed to load: {e}")
39
- MODEL_LOADED = False
40
- llm = None
41
 
42
 
43
  # --- Dog Health Knowledge Base ---
@@ -115,7 +108,7 @@ FUN FACT: [one actually interesting dog gut fact to lighten the mood]"""
115
  def analyze_output(description, output_type, dog_size, photo):
116
  """Analyze dog output from description and/or photo."""
117
  if not description and not photo:
118
- return """<div class="wf-empty">Describe what you found or upload a photo. No judgment here — we've all been there at 6am.</div>"""
119
 
120
  # Check for known toxic items in description
121
  alerts = []
@@ -133,28 +126,41 @@ def analyze_output(description, output_type, dog_size, photo):
133
  if key in lower:
134
  color_match = info
135
 
136
- # Build context
137
  context_parts = []
138
  if output_type: context_parts.append(f"Type: {output_type}")
139
  if dog_size: context_parts.append(f"Dog size: {dog_size}")
140
  context = " | ".join(context_parts)
 
 
141
 
142
- if MODEL_LOADED and llm:
 
143
  try:
144
- prompt = f"My dog's {output_type or 'poop'}: {description or 'see photo'}"
145
- if context: prompt += f"\n({context})"
146
-
147
- response = llm.create_chat_completion(
 
 
 
 
 
 
 
 
 
148
  messages=[
149
  {"role": "system", "content": SYSTEM_PROMPT},
150
- {"role": "user", "content": prompt}
151
  ],
152
  max_tokens=600,
153
  temperature=0.3,
154
  )
155
- raw = response["choices"][0]["message"]["content"].strip()
156
  return format_analysis(raw, alerts, color_match)
157
- except:
 
158
  return format_analysis(fallback_analysis(description, output_type), alerts, color_match)
159
  else:
160
  return format_analysis(fallback_analysis(description, output_type), alerts, color_match)
@@ -430,7 +436,7 @@ with gr.Blocks(css=CUSTOM_CSS, title="WOOF", theme=gr.themes.Soft()) as app:
430
  gr.HTML("""
431
  <div style="text-align:center; padding: 16px 0 4px; color: #CCC; font-size: 0.7em; font-family: 'Quicksand', sans-serif; line-height: 1.8;">
432
  Not a substitute for veterinary care. When in doubt, call your vet.<br>
433
- 1B parameters. No internet needed. Your dog's business stays private.<br>
434
  <span style="color:#8B6914;">Heuremen — Build Small Hackathon 2026</span>
435
  </div>
436
  """)
 
13
  """
14
 
15
  import gradio as gr
16
+ import os
17
+ import io
18
+ import base64
19
+ from openai import OpenAI
20
+
21
+ # --- NVIDIA Nemotron Nano Omni (sponsor model) ---
22
+ NVIDIA_API_KEY = os.environ.get("NVIDIA_API_KEY", "")
23
+ NEMOTRON_MODEL = "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning"
24
+
25
+ nvidia_client = None
26
+ if NVIDIA_API_KEY:
27
+ nvidia_client = OpenAI(
28
+ base_url="https://integrate.api.nvidia.com/v1",
29
+ api_key=NVIDIA_API_KEY,
 
 
 
 
30
  )
31
+ print("NVIDIA Nemotron connected.")
32
+ else:
33
+ print("No NVIDIA_API_KEY running in fallback mode.")
 
 
 
34
 
35
 
36
  # --- Dog Health Knowledge Base ---
 
108
  def analyze_output(description, output_type, dog_size, photo):
109
  """Analyze dog output from description and/or photo."""
110
  if not description and not photo:
111
+ return """<div class="wf-empty">Describe what you found or upload a photo. No judgment here.</div>"""
112
 
113
  # Check for known toxic items in description
114
  alerts = []
 
126
  if key in lower:
127
  color_match = info
128
 
129
+ # Build the prompt
130
  context_parts = []
131
  if output_type: context_parts.append(f"Type: {output_type}")
132
  if dog_size: context_parts.append(f"Dog size: {dog_size}")
133
  context = " | ".join(context_parts)
134
+ user_text = f"My dog's {output_type or 'poop'}: {description or 'see photo'}"
135
+ if context: user_text += f"\n({context})"
136
 
137
+ # Try Nemotron
138
+ if nvidia_client:
139
  try:
140
+ content = [{"type": "text", "text": user_text}]
141
+
142
+ if photo:
143
+ buffered = io.BytesIO()
144
+ photo.save(buffered, format="JPEG", quality=85)
145
+ b64 = base64.b64encode(buffered.getvalue()).decode()
146
+ content.append({
147
+ "type": "image_url",
148
+ "image_url": {"url": f"data:image/jpeg;base64,{b64}"}
149
+ })
150
+
151
+ response = nvidia_client.chat.completions.create(
152
+ model=NEMOTRON_MODEL,
153
  messages=[
154
  {"role": "system", "content": SYSTEM_PROMPT},
155
+ {"role": "user", "content": content}
156
  ],
157
  max_tokens=600,
158
  temperature=0.3,
159
  )
160
+ raw = response.choices[0].message.content.strip()
161
  return format_analysis(raw, alerts, color_match)
162
+ except Exception as e:
163
+ print(f"Nemotron error: {e}")
164
  return format_analysis(fallback_analysis(description, output_type), alerts, color_match)
165
  else:
166
  return format_analysis(fallback_analysis(description, output_type), alerts, color_match)
 
436
  gr.HTML("""
437
  <div style="text-align:center; padding: 16px 0 4px; color: #CCC; font-size: 0.7em; font-family: 'Quicksand', sans-serif; line-height: 1.8;">
438
  Not a substitute for veterinary care. When in doubt, call your vet.<br>
439
+ Powered by NVIDIA Nemotron Nano Omni<br>
440
  <span style="color:#8B6914;">Heuremen — Build Small Hackathon 2026</span>
441
  </div>
442
  """)
requirements.txt CHANGED
@@ -1,3 +1,3 @@
1
  gradio>=4.0
2
- # llama-cpp-python
3
  Pillow
 
1
  gradio>=4.0
2
+ openai
3
  Pillow