Skip to content

Commit 049ae17

Browse files
committed
feat: switch Gemma backend to Google AI Studio for Gemma 4 E4B
- Add google-generativeai to requirements.txt - Detect GOOGLE_API_KEY and init genai.GenerativeModel(gemma-4-e4b-it) - Fallback to featherless-ai gemma-3-12b-it if no Google key - Replace all _GEMMA_MODEL/_GEMMA_PROVIDER UI refs with _GEMMA_DISPLAY - Update WARN message to mention GOOGLE_API_KEY Space secret
1 parent 172b0bd commit 049ae17

2 files changed

Lines changed: 53 additions & 23 deletions

File tree

apps/hdp-physical-hf/app.py

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,48 @@
4343
sign_edt,
4444
)
4545

46-
# ── Gemma 4 via HF Inference API ─────────────────────────────────────────────
47-
# Default: google/gemma-4-4b-it on hf-inference (requires accepting license at
48-
# huggingface.co/google/gemma-4-4b-it with the account that owns HF_TOKEN).
49-
# Override via Space env vars if needed:
50-
# GEMMA_MODEL — e.g. google/gemma-4-12b-it
51-
# GEMMA_PROVIDER — e.g. featherless-ai
52-
_GEMMA_MODEL = os.environ.get("GEMMA_MODEL", "google/gemma-4-4b-it")
53-
_GEMMA_PROVIDER = os.environ.get("GEMMA_PROVIDER", "hf-inference")
46+
# ── Gemma 4 via Google AI Studio ─────────────────────────────────────────────
47+
# Gemma 4 (E4B) is a multimodal any-to-any model — not on HF serverless.
48+
# The only public inference path is Google AI Studio (free API key at
49+
# aistudio.google.com/apikey). Add key as GOOGLE_API_KEY Space secret.
50+
#
51+
# Fallback chain (checked in order):
52+
# 1. Google AI Studio — GOOGLE_API_KEY + GEMMA_MODEL (default: gemma-4-e4b-it)
53+
# 2. HF InferenceClient — HF_TOKEN + featherless-ai + gemma-3-12b-it
54+
_GEMMA_MODEL = os.environ.get("GEMMA_MODEL", "gemma-4-e4b-it")
55+
_GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
5456
_HF_TOKEN = os.environ.get("HF_TOKEN")
55-
_gemma_client = None
5657
_GEMMA_AVAILABLE = False
58+
_GEMMA_BACKEND = None # "google" | "hf"
59+
_gemma_google = None
60+
_gemma_hf = None
5761

5862
try:
59-
from huggingface_hub import InferenceClient
60-
if _HF_TOKEN:
61-
_gemma_client = InferenceClient(provider=_GEMMA_PROVIDER, api_key=_HF_TOKEN)
63+
import google.generativeai as genai
64+
if _GOOGLE_API_KEY:
65+
genai.configure(api_key=_GOOGLE_API_KEY)
66+
_gemma_google = genai.GenerativeModel(_GEMMA_MODEL)
6267
_GEMMA_AVAILABLE = True
68+
_GEMMA_BACKEND = "google"
6369
except Exception:
6470
pass
6571

72+
if not _GEMMA_AVAILABLE:
73+
try:
74+
from huggingface_hub import InferenceClient
75+
if _HF_TOKEN:
76+
_gemma_hf = InferenceClient(provider="featherless-ai", api_key=_HF_TOKEN)
77+
_GEMMA_AVAILABLE = True
78+
_GEMMA_BACKEND = "hf"
79+
except Exception:
80+
pass
81+
82+
_GEMMA_DISPLAY = (
83+
f"{_GEMMA_MODEL} via Google AI Studio" if _GEMMA_BACKEND == "google"
84+
else "google/gemma-3-12b-it via featherless-ai" if _GEMMA_BACKEND == "hf"
85+
else "unavailable"
86+
)
87+
6688
# ── Async helper (safe in Gradio worker threads) ─────────────────────────────
6789
def _run_async(coro):
6890
"""Run a coroutine in a dedicated thread — avoids event-loop conflicts."""
@@ -171,16 +193,23 @@ def _gemma_attack_prompt(src: str, dst: str) -> str:
171193
return base + poison
172194

173195
def _call_gemma(prompt: str, single: bool = False) -> str:
174-
"""Send prompt to Gemma 4; return raw text or empty string on failure."""
196+
"""Send prompt to Gemma; return raw text or empty string on failure."""
175197
if not _GEMMA_AVAILABLE:
176198
return ""
177199
try:
178-
result = _gemma_client.chat.completions.create(
179-
model=_GEMMA_MODEL,
180-
messages=[{"role": "user", "content": prompt}],
181-
max_tokens=160 if not single else 100,
182-
)
183-
return result.choices[0].message.content.strip()
200+
if _GEMMA_BACKEND == "google":
201+
resp = _gemma_google.generate_content(
202+
prompt,
203+
generation_config={"max_output_tokens": 300 if not single else 120},
204+
)
205+
return resp.text.strip()
206+
else:
207+
result = _gemma_hf.chat.completions.create(
208+
model="google/gemma-3-12b-it",
209+
messages=[{"role": "user", "content": prompt}],
210+
max_tokens=160 if not single else 100,
211+
)
212+
return result.choices[0].message.content.strip()
184213
except Exception as e:
185214
return f"[Gemma error: {e}]"
186215

@@ -624,7 +653,7 @@ def run_safe_routine(hdp_enabled: bool) -> Generator:
624653
"approved": True, "attack": False,
625654
"zone": src, "force_n": 0.0, "velocity_ms": 0.0,
626655
}])
627-
gemma_status = f"[AI] **Gemma** (`{_GEMMA_MODEL}`) planning route…"
656+
gemma_status = f"[AI] **Gemma 4** (`{_GEMMA_DISPLAY}`) planning route…"
628657
yield "", f"[AI] Gemma planning: {src}{dst}", reset_state, "", gemma_status, ""
629658
# Arm moves to source position while Gemma is computing
630659
yield "", f"[AI] Arm ready at {src} — waiting for Gemma plan…", reach_state, "", gemma_status, ""
@@ -793,11 +822,11 @@ def _edt_json() -> str:
793822
# ── UI ────────────────────────────────────────────────────────────────────────
794823
HEADER = f"""
795824
# HDP-P Physical Safety Demo
796-
**Gemma** (`{_GEMMA_MODEL}` via `{_GEMMA_PROVIDER}`) plans every robot action from natural language.
825+
**Gemma** (`{_GEMMA_DISPLAY}`) plans every robot action from natural language.
797826
**HDP-P** cryptographically verifies each Gemma action before the arm moves.
798827
799828
> Run the routine → inject an attack → toggle HDP-P to see the difference.
800-
{"[OK] **Gemma connected** — live inference active." if _GEMMA_AVAILABLE else "[WARN] **Gemma unavailable** — scripted fallback mode (set `HF_TOKEN` Space secret)."}
829+
{"[OK] **Gemma connected** — live inference active." if _GEMMA_AVAILABLE else "[WARN] **Gemma unavailable** — set `GOOGLE_API_KEY` Space secret (aistudio.google.com/apikey)."}
801830
"""
802831

803832
with gr.Blocks(
@@ -852,7 +881,7 @@ def _edt_json() -> str:
852881
User task description
853882
854883
855-
Gemma ({_GEMMA_MODEL})
884+
Gemma ({_GEMMA_DISPLAY})
856885
← adversary injects text here to poison Gemma's output
857886
│ generates: RobotAction(zone, force_n, velocity_ms)
858887
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
hdp-physical>=0.1.0
22
cryptography>=42.0.0
33
huggingface_hub>=0.20.0
4+
google-generativeai>=0.8.0

0 commit comments

Comments
 (0)