sumitdotml commited on
Commit
c576ff8
·
verified ·
1 Parent(s): f0918bc

Initial Gradio demo for robuchan recipe adapter

Browse files
Files changed (4) hide show
  1. README.md +6 -6
  2. RUNBOOK.md +136 -0
  3. app.py +169 -0
  4. requirements.txt +7 -0
README.md CHANGED
@@ -1,12 +1,12 @@
1
  ---
2
- title: Robuchan Demo
3
- emoji: 📚
4
- colorFrom: purple
5
  colorTo: red
6
  sdk: gradio
7
- sdk_version: 6.8.0
8
  app_file: app.py
9
  pinned: false
 
 
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Robuchan - Recipe Adaptation
3
+ emoji: "\U0001F35C"
4
+ colorFrom: yellow
5
  colorTo: red
6
  sdk: gradio
7
+ sdk_version: "5.33.0"
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
+ suggested_hardware: t4-small
12
  ---
 
 
RUNBOOK.md ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Robuchan HF Space Deployment Runbook
2
+
3
+ Deploy the Gradio demo to `sumitdotml/robuchan-demo` on Hugging Face Spaces.
4
+
5
+ ## Prerequisites
6
+
7
+ - `hf` CLI installed ([docs](https://huggingface.co/docs/huggingface_hub/en/guides/cli))
8
+ - Authenticated with a write-access token
9
+ - Files ready in `demo/space/`: `app.py`, `requirements.txt`, `README.md`
10
+
11
+ ## Step 0: Install hf CLI (if needed)
12
+
13
+ ```bash
14
+ curl -LsSf https://hf.co/cli/install.sh | bash
15
+ ```
16
+
17
+ Or via uvx (no install needed):
18
+
19
+ ```bash
20
+ uvx hf --help
21
+ ```
22
+
23
+ ## Step 1: Authenticate
24
+
25
+ ```bash
26
+ hf auth login
27
+ # Paste your token when prompted (needs write access)
28
+ # Say yes to saving as git credential
29
+ ```
30
+
31
+ Verify:
32
+
33
+ ```bash
34
+ hf auth whoami
35
+ # Should print: sumitdotml
36
+ ```
37
+
38
+ ## Step 2: Create the Space
39
+
40
+ ```bash
41
+ hf repos create robuchan-demo --repo-type space --space-sdk gradio
42
+ ```
43
+
44
+ Expected output:
45
+
46
+ ```
47
+ Successfully created sumitdotml/robuchan-demo on the Hub.
48
+ Your repo is now available at https://huggingface.co/spaces/sumitdotml/robuchan-demo
49
+ ```
50
+
51
+ If the Space already exists, add `--exist-ok`:
52
+
53
+ ```bash
54
+ hf repos create robuchan-demo --repo-type space --space-sdk gradio --exist-ok
55
+ ```
56
+
57
+ ## Step 3: Upload files
58
+
59
+ From the repo root:
60
+
61
+ ```bash
62
+ hf upload sumitdotml/robuchan-demo demo/space . --repo-type space \
63
+ --commit-message "Initial Gradio demo for robuchan recipe adapter"
64
+ ```
65
+
66
+ This uploads the contents of `demo/space/` (app.py, requirements.txt, README.md) to the root of the Space repo.
67
+
68
+ Expected output:
69
+
70
+ ```
71
+ https://huggingface.co/spaces/sumitdotml/robuchan-demo/tree/main/
72
+ ```
73
+
74
+ ## Step 4: Set hardware to T4
75
+
76
+ The `README.md` frontmatter includes `suggested_hardware: t4-small`, but you may need to set it manually in Space settings:
77
+
78
+ 1. Go to https://huggingface.co/spaces/sumitdotml/robuchan-demo/settings
79
+ 2. Under **Space Hardware**, select **T4 small**
80
+ 3. Click **Save**
81
+
82
+ (Requires HF Pro or a hardware grant.)
83
+
84
+ ## Step 5: Wait for build
85
+
86
+ The Space will auto-build on push. Monitor the build log:
87
+
88
+ 1. Go to https://huggingface.co/spaces/sumitdotml/robuchan-demo
89
+ 2. Click the **Logs** tab (or the "Building" badge)
90
+ 3. Wait for "Running on local URL" in the logs
91
+
92
+ Build typically takes 3-5 minutes (dependency install + model download on first boot).
93
+
94
+ ## Step 6: Verify
95
+
96
+ ### Quick smoke test
97
+
98
+ 1. Open https://huggingface.co/spaces/sumitdotml/robuchan-demo
99
+ 2. Click the first example (tonkotsu ramen, vegan) and hit **Submit**
100
+ 3. Wait for generation (~30-60s on T4)
101
+ 4. Confirm output contains all 5 sections:
102
+ - Substitution Plan
103
+ - Adapted Ingredients
104
+ - Adapted Steps
105
+ - Flavor Preservation Notes
106
+ - Constraint Check
107
+
108
+ ### Full verification
109
+
110
+ Run both pre-loaded examples:
111
+
112
+ | Example | Constraint | Check |
113
+ |---------|-----------|-------|
114
+ | Tonkotsu ramen | vegan | No pork/eggs/animal products in adapted recipe |
115
+ | Japanese curry | gluten_free | No wheat flour/soy sauce in adapted recipe |
116
+
117
+ Also test a custom input: paste any recipe, select a constraint, verify structured output.
118
+
119
+ ## Updating the Space
120
+
121
+ After editing files in `demo/space/`, re-upload:
122
+
123
+ ```bash
124
+ hf upload sumitdotml/robuchan-demo demo/space . --repo-type space \
125
+ --commit-message "description of changes"
126
+ ```
127
+
128
+ ## Troubleshooting
129
+
130
+ | Symptom | Fix |
131
+ |---------|-----|
132
+ | Build fails on `bitsandbytes` | Needs CUDA runtime. Verify hardware is set to T4, not CPU. |
133
+ | OOM during model load | T4 has 16GB VRAM. 4-bit quantization should fit 8B model. If OOM, check no other process is using GPU. Restart Space. |
134
+ | "Model not found" error | Verify `sumitdotml/robuchan` adapter is public (or set `HF_TOKEN` as a Space secret). |
135
+ | Space stuck on "Building" | Check build logs for pip install errors. May need to pin a specific torch version in requirements.txt. |
136
+ | Slow first inference | Expected. First request triggers CUDA kernel compilation. Subsequent requests are faster. |
app.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Robuchan - Recipe Adaptation Demo (HF Space).
2
+
3
+ Loads the fine-tuned LoRA adapter with 4-bit quantization and serves
4
+ a Gradio interface for interactive recipe adaptation.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import gradio as gr
10
+ import torch
11
+ from peft import AutoPeftModelForCausalLM
12
+ from transformers import AutoTokenizer, BitsAndBytesConfig
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # Constants
16
+ # ---------------------------------------------------------------------------
17
+
18
+ ADAPTER_ID = "sumitdotml/robuchan"
19
+ BASE_MODEL = "mistralai/Ministral-8B-Instruct-2410"
20
+ MAX_NEW_TOKENS = 800
21
+
22
+ SYSTEM_PROMPT = (
23
+ "You are a culinary adaptation assistant. Priority: (1) strict dietary "
24
+ "compliance, (2) preserve dish identity and flavor profile, (3) keep "
25
+ "instructions practical and cookable. Never include forbidden ingredients "
26
+ "or their derivatives (stocks, sauces, pastes, broths). If no exact "
27
+ "compliant substitute exists, acknowledge the gap, choose the closest "
28
+ "viable option, and state the trade-off. Output sections exactly: "
29
+ "Substitution Plan, Adapted Ingredients, Adapted Steps, Flavor "
30
+ "Preservation Notes, Constraint Check."
31
+ )
32
+
33
+ CONSTRAINTS = [
34
+ "vegan",
35
+ "dairy_free",
36
+ "gluten_free",
37
+ "vegetarian",
38
+ "nut_free",
39
+ "egg_free",
40
+ "low_sodium",
41
+ ]
42
+
43
+ EXAMPLE_TONKOTSU = (
44
+ "Adapt this tonkotsu ramen recipe for a vegan diet:\n\n"
45
+ "Ingredients: pork bones, pork belly, eggs, wheat noodles, soy sauce, "
46
+ "mirin, garlic, ginger, green onions, nori, sesame oil.\n\n"
47
+ "Instructions: Boil pork bones for 12 hours for broth. Char pork belly. "
48
+ "Soft-boil eggs. Cook wheat noodles. Assemble with toppings."
49
+ )
50
+
51
+ EXAMPLE_CURRY = (
52
+ "Adapt this Japanese curry recipe for gluten-free:\n\n"
53
+ "Ingredients: curry roux (contains wheat flour), chicken thighs, potatoes, "
54
+ "carrots, onions, rice, soy sauce, mirin, dashi stock.\n\n"
55
+ "Instructions: Saut\u00e9 onions, brown chicken, add vegetables, add water and "
56
+ "curry roux, simmer 20 minutes. Serve over rice."
57
+ )
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # Model loading (once at startup)
61
+ # ---------------------------------------------------------------------------
62
+
63
+ print("Loading model with 4-bit quantization ...")
64
+
65
+ bnb_config = BitsAndBytesConfig(
66
+ load_in_4bit=True,
67
+ bnb_4bit_quant_type="nf4",
68
+ bnb_4bit_compute_dtype=torch.bfloat16,
69
+ bnb_4bit_use_double_quant=True,
70
+ )
71
+
72
+ model = AutoPeftModelForCausalLM.from_pretrained(
73
+ ADAPTER_ID,
74
+ quantization_config=bnb_config,
75
+ device_map="auto",
76
+ torch_dtype=torch.bfloat16,
77
+ )
78
+ model.eval()
79
+
80
+ tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
81
+ if tokenizer.pad_token is None:
82
+ tokenizer.pad_token = tokenizer.eos_token
83
+
84
+ print("Model loaded.")
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # Generation
88
+ # ---------------------------------------------------------------------------
89
+
90
+
91
+ def generate(recipe: str, constraint: str) -> str:
92
+ """Generate an adapted recipe from the fine-tuned model."""
93
+ if not recipe.strip():
94
+ return "Please paste a recipe above."
95
+
96
+ user_content = f"Constraint: {constraint}\n\n{recipe}"
97
+ messages = [
98
+ {"role": "system", "content": SYSTEM_PROMPT},
99
+ {"role": "user", "content": user_content},
100
+ ]
101
+
102
+ text = tokenizer.apply_chat_template(
103
+ messages, tokenize=False, add_generation_prompt=True,
104
+ )
105
+ inputs = tokenizer(text, return_tensors="pt")
106
+ inputs = {k: v.to(model.device) for k, v in inputs.items()}
107
+
108
+ gen_kwargs = {
109
+ "max_new_tokens": MAX_NEW_TOKENS,
110
+ "do_sample": False,
111
+ "pad_token_id": tokenizer.pad_token_id,
112
+ }
113
+ if tokenizer.eos_token_id is not None:
114
+ gen_kwargs["eos_token_id"] = tokenizer.eos_token_id
115
+
116
+ with torch.no_grad():
117
+ output_ids = model.generate(**inputs, **gen_kwargs)
118
+
119
+ prompt_len = inputs["input_ids"].shape[1]
120
+ completion_ids = output_ids[0][prompt_len:]
121
+ return tokenizer.decode(completion_ids, skip_special_tokens=True).strip()
122
+
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # Gradio UI
126
+ # ---------------------------------------------------------------------------
127
+
128
+ DESCRIPTION = """\
129
+ # Robuchan - Recipe Adaptation
130
+
131
+ Fine-tuned [Ministral 8B](https://huggingface.co/mistralai/Ministral-8B-Instruct-2410) \
132
+ adapter for dietary-compliant recipe transformation.
133
+
134
+ **How it works:** paste a recipe, pick a dietary constraint, and the model \
135
+ generates an adapted version with substitution rationale, modified ingredients, \
136
+ updated steps, and a compliance check.
137
+
138
+ Adapter: [`sumitdotml/robuchan`](https://huggingface.co/sumitdotml/robuchan)
139
+ """
140
+
141
+ examples = [
142
+ [EXAMPLE_TONKOTSU, "vegan"],
143
+ [EXAMPLE_CURRY, "gluten_free"],
144
+ ]
145
+
146
+ demo = gr.Interface(
147
+ fn=generate,
148
+ inputs=[
149
+ gr.Textbox(
150
+ label="Recipe",
151
+ placeholder="Paste ingredients + instructions here ...",
152
+ lines=10,
153
+ ),
154
+ gr.Dropdown(
155
+ choices=CONSTRAINTS,
156
+ value="vegan",
157
+ label="Dietary Constraint",
158
+ ),
159
+ ],
160
+ outputs=gr.Markdown(label="Adapted Recipe"),
161
+ title="Robuchan",
162
+ description=DESCRIPTION,
163
+ examples=examples,
164
+ cache_examples=False,
165
+ flagging_mode="never",
166
+ )
167
+
168
+ if __name__ == "__main__":
169
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ torch
2
+ transformers
3
+ peft
4
+ accelerate
5
+ bitsandbytes
6
+ gradio
7
+ huggingface-hub