Spaces:
Sleeping
Sleeping
| """Northwind Ops POC chat — Hugging Face Space (app.py).""" | |
| from __future__ import annotations | |
| import os | |
| from functools import lru_cache | |
| from typing import Any | |
| import gradio as gr | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| try: | |
| import spaces | |
| except ImportError: # CPU Space / local without ZeroGPU | |
| class _SpacesShim: | |
| def GPU(fn=None, **_kwargs): | |
| if fn is None: | |
| return lambda f: f | |
| return fn | |
| spaces = _SpacesShim() # type: ignore | |
| MODEL_ID = os.environ.get("NORTHWIND_MODEL_ID", "UnaverageTech411/northwind-ops") | |
| SYSTEM = os.environ.get( | |
| "NORTHWIND_SYSTEM", | |
| ( | |
| "You are Northwind Ops Assistant for Northwind Traders — the showcase company model " | |
| "from the Arriella custom-model factory. For Northwind IT, HR, and finance procedures, " | |
| "answer from documented company knowledge and name the official tool (ServiceNow, Concur, Workday). " | |
| "Give crisp step paths (for example ServiceNow → IT → VPN Token Reset with Northwind SSO). " | |
| "For ordinary world knowledge, math, spelling, and general chat, answer normally and concisely. " | |
| "Only say a fact is not documented when it is a Northwind-internal detail missing from training " | |
| "(salary bands, PTO balances, unpublished policies). Never say not documented for VPN, Concur, " | |
| "or Workday procedures that are in your training. Never refuse common general-knowledge questions." | |
| ), | |
| ) | |
| # (button label, prompt) — training-data / gate probes | |
| EXAMPLE_PROMPTS: list[tuple[str, str]] = [ | |
| ("VPN reset", "How do I reset my VPN token at Northwind in ServiceNow?"), | |
| ("VPN by email?", "Can I reset my Northwind VPN token by emailing IT?"), | |
| ("Expense / Concur", "How do I submit an expense report in Concur?"), | |
| ("PTO / Workday", "Where do I check PTO in Workday?"), | |
| ("Salary band", "What is my exact salary band code?"), | |
| ("SaaS access", "Where do I request SaaS access?"), | |
| ("Who is CTO?", "Who is the Northwind CTO?"), | |
| ("Capital of France", "What is the capital of France?"), | |
| ] | |
| CSS = """ | |
| .gradio-container { | |
| max-width: 920px !important; | |
| margin: auto; | |
| font-family: "IBM Plex Sans", "Segoe UI", sans-serif !important; | |
| } | |
| footer { display: none !important; } | |
| #poc-title h1 { | |
| font-family: "Syne", "Arial Narrow", sans-serif !important; | |
| letter-spacing: -0.02em; | |
| margin-bottom: 0.25rem !important; | |
| } | |
| #poc-lede { | |
| color: #9aabbd !important; | |
| font-size: 0.98rem !important; | |
| line-height: 1.5 !important; | |
| margin-top: 0 !important; | |
| } | |
| #chatbot { border: 2px solid rgba(244,247,251,0.22) !important; } | |
| #probe-row button, #probe-row-2 button { | |
| font-size: 0.82rem !important; | |
| } | |
| """ | |
| def _load() -> tuple[Any, Any]: | |
| tok = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True) | |
| dtype = torch.float16 if torch.cuda.is_available() else torch.float32 | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_ID, | |
| torch_dtype=dtype, | |
| device_map="auto" if torch.cuda.is_available() else None, | |
| trust_remote_code=True, | |
| ) | |
| if not torch.cuda.is_available(): | |
| model = model.to("cpu") | |
| model.eval() | |
| return tok, model | |
| def _history_to_messages(history: list[dict[str, str]]) -> list[dict[str, str]]: | |
| messages = [{"role": "system", "content": SYSTEM}] | |
| for turn in history or []: | |
| role = turn.get("role") | |
| content = (turn.get("content") or "").strip() | |
| if role in {"user", "assistant"} and content: | |
| messages.append({"role": role, "content": content}) | |
| return messages | |
| def _generate(messages: list[dict[str, str]], max_new_tokens: int = 220) -> str: | |
| tok, model = _load() | |
| prompt = tok.apply_chat_template( | |
| messages, tokenize=False, add_generation_prompt=True | |
| ) | |
| inputs = tok(prompt, return_tensors="pt") | |
| device = next(model.parameters()).device | |
| inputs = {k: v.to(device) for k, v in inputs.items()} | |
| with torch.inference_mode(): | |
| out = model.generate( | |
| **inputs, | |
| max_new_tokens=max_new_tokens, | |
| do_sample=False, | |
| pad_token_id=tok.eos_token_id, | |
| ) | |
| gen = out[0, inputs["input_ids"].shape[-1] :] | |
| return tok.decode(gen, skip_special_tokens=True).strip() | |
| def respond(message: str, history: list[dict[str, str]]): | |
| text = (message or "").strip() | |
| if not text: | |
| return history, "" | |
| history = list(history or []) | |
| history.append({"role": "user", "content": text}) | |
| try: | |
| reply = _generate(_history_to_messages(history)) | |
| except Exception as exc: # noqa: BLE001 | |
| reply = f"(Demo error: {exc})" | |
| history.append({"role": "assistant", "content": reply or "(empty reply)"}) | |
| return history, "" | |
| def use_example(prompt: str, history: list[dict[str, str]]): | |
| return respond(prompt, history) | |
| def clear_chat(): | |
| return [], "" | |
| try: | |
| _load() | |
| except Exception: | |
| pass | |
| theme = gr.themes.Soft( | |
| primary_hue=gr.themes.colors.amber, | |
| secondary_hue=gr.themes.colors.emerald, | |
| neutral_hue=gr.themes.colors.slate, | |
| ).set( | |
| body_background_fill="#06090d", | |
| body_background_fill_dark="#06090d", | |
| block_background_fill="#111922", | |
| block_background_fill_dark="#111922", | |
| body_text_color="#f4f7fb", | |
| body_text_color_dark="#f4f7fb", | |
| border_color_primary="rgba(244,247,251,0.22)", | |
| border_color_primary_dark="rgba(244,247,251,0.22)", | |
| ) | |
| with gr.Blocks(title="Northwind Ops Assistant", theme=theme, css=CSS) as demo: | |
| with gr.Column(elem_id="poc-title"): | |
| gr.Markdown("# Northwind Ops — try the POC") | |
| gr.Markdown( | |
| "1B company model: ServiceNow / Concur / Workday procedures, " | |
| "Pile-hardened, Heretic ×2, openness-gated. " | |
| "Tap a probe from the training set, or type your own. Clear anytime.", | |
| elem_id="poc-lede", | |
| ) | |
| chatbot = gr.Chatbot( | |
| label="Northwind Ops", | |
| height=440, | |
| type="messages", | |
| elem_id="chatbot", | |
| value=[ | |
| { | |
| "role": "assistant", | |
| "content": ( | |
| "Ask a company procedure and a general fact — that contrast is the demo. " | |
| "Try **VPN reset** or **Capital of France** below." | |
| ), | |
| } | |
| ], | |
| ) | |
| with gr.Row(): | |
| msg = gr.Textbox( | |
| label="Message", | |
| show_label=False, | |
| placeholder="How do I reset my VPN token at Northwind in ServiceNow?", | |
| scale=5, | |
| autofocus=True, | |
| container=False, | |
| ) | |
| send = gr.Button("Send", variant="primary", scale=1) | |
| clear_btn = gr.Button("Clear chat", variant="secondary", scale=1) | |
| gr.Markdown("**Training-data probes**") | |
| with gr.Row(elem_id="probe-row"): | |
| btns_a = [gr.Button(label, size="sm") for label, _ in EXAMPLE_PROMPTS[:4]] | |
| with gr.Row(elem_id="probe-row-2"): | |
| btns_b = [gr.Button(label, size="sm") for label, _ in EXAMPLE_PROMPTS[4:]] | |
| example_btns = btns_a + btns_b | |
| gr.Markdown( | |
| "Weights: [`UnaverageTech411/northwind-ops`](https://huggingface.co/UnaverageTech411/northwind-ops) · " | |
| "Local: `ollama run northwind-ops` · " | |
| "Arriella custom-model factory POC" | |
| ) | |
| send.click(respond, inputs=[msg, chatbot], outputs=[chatbot, msg]) | |
| msg.submit(respond, inputs=[msg, chatbot], outputs=[chatbot, msg]) | |
| clear_btn.click(clear_chat, outputs=[chatbot, msg]) | |
| for btn, (_label, prompt) in zip(example_btns, EXAMPLE_PROMPTS): | |
| btn.click( | |
| lambda hist, p=prompt: use_example(p, hist), | |
| inputs=[chatbot], | |
| outputs=[chatbot, msg], | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue(default_concurrency_limit=1).launch() | |