baldator's picture
remove copy button
c3855b0
Raw
History Blame Contribute Delete
7.59 kB
import gradio as gr
import os
import logging
# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger("SpoutSpoon")
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
HF_MODEL = "Qwen/Qwen2.5-Coder-3B-Instruct:nscale"
HF_API_TOKEN = os.environ.get("HF_API_TOKEN", "")
# ---------------------------------------------------------------------------
# System Prompt
# ---------------------------------------------------------------------------
SYSTEM_PROMPT = """You are Sprout & Spoon, a concise and helpful assistant for cooking and gardening advice.
Rules you MUST follow:
- Do NOT include any conversational filler. No greetings, no 'Hello', no 'Hope this helps', no 'Let me know if...'.
- Use strict Markdown formatting with **bold headers** and bullet points where appropriate.
- Keep answers short, direct, and easy to read.
- Use large, easy-to-read text structure (short paragraphs, clear separation)."""
# ---------------------------------------------------------------------------
# Real LLM call via Hugging Face InferenceClient
# ---------------------------------------------------------------------------
def call_local_model(prompt: str) -> str:
prompt_preview = prompt.strip()[:60].replace("\n", " ")
logger.info("Received question: \"%s\"", prompt_preview)
if not HF_API_TOKEN:
logger.warning("HF_API_TOKEN not set - using fallback responses")
return _fallback_response(prompt)
logger.info(
"Sending request to Hugging Face Inference API (model=%s)", HF_MODEL
)
try:
from huggingface_hub import InferenceClient
client = InferenceClient(token=HF_API_TOKEN)
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt},
]
stream = client.chat.completions.create(
model=HF_MODEL,
messages=messages,
max_tokens=512,
temperature=0.3,
top_p=0.9,
stream=False,
)
answer = stream.choices[0].message.content.strip()
logger.info(
"API call succeeded (response_length=%s chars)", len(answer)
)
return answer
except ImportError:
logger.warning(
"huggingface_hub not installed - falling back to keyword response"
)
return _fallback_response(prompt)
except Exception as exc:
logger.error(
"API request failed: %s - falling back to keyword response", exc
)
return _fallback_response(prompt)
# ---------------------------------------------------------------------------
# Fallback responses
# ---------------------------------------------------------------------------
def _fallback_response(prompt: str) -> str:
lower = prompt.lower()
if "tomato" in lower or "tomatoes" in lower:
return (
"**Watering**\n"
"- Water deeply 2-3 times per week, early in the morning.\n"
"- Avoid wetting the leaves to prevent blight.\n\n"
"**Feeding**\n"
"- Apply a balanced 10-10-10 fertiliser every 2 weeks.\n\n"
"**Support**\n"
"- Use stakes or cages once the plant is 12 inches tall.\n"
"- Tie main stem loosely with soft garden twine."
)
if "chicken" in lower or "leftover" in lower:
return (
"**Quick Chicken Salad**\n"
"- Shred leftover chicken and mix with Greek yoghurt, diced celery, "
"grapes, and a pinch of salt.\n\n"
"**Chicken and Veggie Stir-Fry**\n"
"- Slice chicken, stir-fry with broccoli, bell peppers, and soy "
"sauce for 5 minutes.\n\n"
"**Warming Soup**\n"
"- Simmer chicken with broth, carrots, onions, and egg noodles "
"for 20 minutes."
)
if "rose" in lower or "prune" in lower:
return (
"**When to Prune**\n"
"- Late winter or early spring, just before new growth begins.\n\n"
"**How to Prune**\n"
"- Remove dead, damaged, or crossing branches first.\n"
"- Cut at a 45 degree angle 1/4 inch above an outward-facing bud.\n"
"- Open the centre of the plant for airflow.\n\n"
"**Aftercare**\n"
"- Apply a layer of mulch and water thoroughly."
)
return (
"**Quick Tips**\n"
"- Keep your workspace clean and organised.\n"
"- Prep all ingredients before you start cooking.\n"
"- In the garden, water deeply and less often for stronger roots."
)
# ---------------------------------------------------------------------------
# Gradio application
# ---------------------------------------------------------------------------
CUSTOM_CSS = """
.gradio-container { max-width: 800px; margin: auto; }
label { font-size: 1.2rem !important; }
button { font-size: 1.1rem !important; }
.md_output p, .md_output li { font-size: 1.4rem !important; line-height: 1.6; }
"""
with gr.Blocks(title="Sprout & Spoon") as demo:
gr.Markdown("# \U0001f373 Sprout & Spoon\nAsk a cooking or gardening question below.")
user_input = gr.Textbox(
label="Your question",
placeholder="e.g. How do I store fresh basil?",
lines=3,
)
with gr.Row():
submit_btn = gr.Button("Submit", variant="primary")
clear_btn = gr.Button("Clear")
# Native Markdown output — no manual HTML conversion
output = gr.Markdown(
value="_No answer yet._",
label="Answer",
elem_classes="md_output",
)
# Hidden textarea holding the raw markdown for the copy button
raw_holder = gr.Textbox(
value="",
label="",
visible=False,
elem_id="raw-text-holder",
)
gr.Markdown("### Try an example")
with gr.Row():
example_tomato = gr.Button("\U0001f345 Help with my Tomatoes")
example_chicken = gr.Button("\U0001f357 Leftover Chicken Recipe")
example_rose = gr.Button("\U0001f339 How to prune Roses")
def respond(message: str):
if not message or not message.strip():
empty = "_Please enter a question._"
return empty, empty
answer = call_local_model(message)
return answer, answer
submit_btn.click(
fn=respond, inputs=user_input, outputs=[output, raw_holder]
)
user_input.submit(
fn=respond, inputs=user_input, outputs=[output, raw_holder]
)
def clear_all():
return "", "_No answer yet._", ""
clear_btn.click(
fn=clear_all,
inputs=[],
outputs=[user_input, output, raw_holder],
)
for btn, text in [
(example_tomato, "Help with my Tomatoes"),
(example_chicken, "Leftover Chicken Recipe"),
(example_rose, "How to prune Roses"),
]:
btn.click(
fn=lambda q=text: q,
inputs=[],
outputs=user_input,
).then(
fn=respond,
inputs=user_input,
outputs=[output, raw_holder],
)
if __name__ == "__main__":
demo.launch(share=True, theme=gr.themes.Soft(), css=CUSTOM_CSS)