Ideon commited on
Commit
faa8cd9
·
verified ·
1 Parent(s): c3dd3ff

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +91 -8
  2. app.py +124 -0
  3. model_adapters.py +132 -0
  4. requirements.txt +7 -0
README.md CHANGED
@@ -1,14 +1,97 @@
1
  ---
2
- title: Rfab Historic Gpt 1900
3
- emoji: 🔥
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 6.14.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
11
- license: mit
 
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: RFAB Historic Chat Space
3
+ emoji: 🕰️
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 5.49.1
 
8
  app_file: app.py
9
  pinned: false
10
+ models:
11
+ - Pclanglais/MonadGPT
12
  ---
13
 
14
+ # RFAB Historic Chat Gradio Space Template
15
+
16
+ This template exposes the same Gradio API shape used by Talkie-1930:
17
+
18
+ - `POST /gradio_api/call/v2/_bot_reply`
19
+ - `GET /gradio_api/call/_bot_reply/{event_id}`
20
+
21
+ The Reality Fabricator backend expects `_bot_reply` to accept named params:
22
+
23
+ - `history`
24
+ - `system_prompt`
25
+ - `temperature`
26
+ - `max_tokens`
27
+ - `top_p`
28
+ - `top_k`
29
+
30
+ ## Required Variables
31
+
32
+ Set these in the Space settings:
33
+
34
+ ```bash
35
+ RFAB_HISTORIC_MODEL_ID=Pclanglais/MonadGPT
36
+ RFAB_HISTORIC_ADAPTER=transformers
37
+ DRY_RUN=true
38
+ ```
39
+
40
+ For real model inference, set `DRY_RUN=false` and choose suitable hardware.
41
+
42
+ ## Model Presets
43
+
44
+ MonadGPT:
45
+
46
+ ```bash
47
+ RFAB_HISTORIC_MODEL_ID=Pclanglais/MonadGPT
48
+ RFAB_HISTORIC_ADAPTER=transformers
49
+ RFAB_HISTORIC_MODEL_KWARGS={"torch_dtype":"auto","device_map":"auto"}
50
+ ```
51
+
52
+ TimeCapsule:
53
+
54
+ ```bash
55
+ RFAB_HISTORIC_MODEL_ID=haykgrigorian/TimeCapsuleLLM-v2-llama-1.2B
56
+ RFAB_HISTORIC_ADAPTER=transformers
57
+ ```
58
+
59
+ GPT-1900:
60
+
61
+ ```bash
62
+ RFAB_HISTORIC_MODEL_ID=mhla/gpt1900-instruct-v3-sft
63
+ RFAB_HISTORIC_ADAPTER=transformers
64
+ ```
65
+
66
+ Mr. Chatterbox:
67
+
68
+ ```bash
69
+ RFAB_HISTORIC_MODEL_ID=tventurella/mr_chatterbox_model
70
+ RFAB_HISTORIC_ADAPTER=nanochat
71
+ RFAB_NANOCHAT_MODEL_TAG=d18
72
+ RFAB_NANOCHAT_SOURCE=sft
73
+ ```
74
+
75
+ The `nanochat` adapter is scaffolded for the Mr. Chatterbox code path. The Space must include the same nanochat package/checkpoint layout as `tventurella/mr_chatterbox` before `DRY_RUN=false`.
76
+
77
+ ## Hardware
78
+
79
+ - CPU Basic is free and good for build/API contract testing.
80
+ - ZeroGPU requires a personal PRO account or qualifying Team/organization setup.
81
+ - The same Space can be upgraded later in Settings or through `huggingface_hub`; hardware changes restart the Space.
82
+ - Keep `@spaces.GPU` in `app.py`; it is effect-free outside ZeroGPU and required once ZeroGPU is selected.
83
+
84
+ ## Backend Activation
85
+
86
+ After the Space is live:
87
+
88
+ ```bash
89
+ curl https://<space>.hf.space/gradio_api/info
90
+ npm run smoke:historic-hf -- --model <model>
91
+ ```
92
+
93
+ Copy the smoke JSON into `HISTORIC_HF_SMOKE_RESULTS_JSON` and set the matching backend URL env var, for example:
94
+
95
+ ```bash
96
+ HISTORIC_HF_SPACE_URL_MONAD_GPT=https://rfab-historic-monad-gpt.hf.space
97
+ ```
app.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import gradio as gr
4
+
5
+ try:
6
+ import spaces
7
+ except Exception:
8
+ spaces = None
9
+
10
+ from model_adapters import create_adapter
11
+
12
+
13
+ MODEL_LABEL = os.getenv("RFAB_HISTORIC_MODEL_LABEL", os.getenv("RFAB_HISTORIC_MODEL_ID", "RFAB Historic Model"))
14
+ GPU_DURATION_SECONDS = int(os.getenv("RFAB_GPU_DURATION_SECONDS", "60"))
15
+
16
+ adapter = create_adapter()
17
+
18
+
19
+ def gpu(fn):
20
+ if spaces is None:
21
+ return fn
22
+ return spaces.GPU(duration=GPU_DURATION_SECONDS)(fn)
23
+
24
+
25
+ def normalize_history(history):
26
+ if not isinstance(history, list):
27
+ return []
28
+
29
+ normalized = []
30
+ for message in history:
31
+ if not isinstance(message, dict):
32
+ continue
33
+ role = message.get("role")
34
+ if role not in {"user", "assistant"}:
35
+ continue
36
+ content = message.get("content") or []
37
+ if isinstance(content, str):
38
+ content = [{"type": "text", "text": content}]
39
+ normalized.append({
40
+ "role": role,
41
+ "metadata": message.get("metadata"),
42
+ "content": content,
43
+ "options": message.get("options")
44
+ })
45
+ return normalized
46
+
47
+
48
+ @gpu
49
+ def _bot_reply(history, system_prompt="", temperature=0.7, max_tokens=256, top_p=1.0, top_k=0):
50
+ history = normalize_history(history)
51
+ answer = adapter.generate(
52
+ history=history,
53
+ system_prompt=system_prompt or "",
54
+ temperature=float(temperature),
55
+ max_tokens=int(max_tokens),
56
+ top_p=float(top_p),
57
+ top_k=int(top_k),
58
+ )
59
+ history.append({
60
+ "role": "assistant",
61
+ "metadata": None,
62
+ "content": [{"type": "text", "text": answer}],
63
+ "options": None
64
+ })
65
+ return history
66
+
67
+
68
+ def preview_reply(message, system_prompt, temperature, max_tokens):
69
+ history = [{
70
+ "role": "user",
71
+ "metadata": None,
72
+ "content": [{"type": "text", "text": message}],
73
+ "options": None
74
+ }]
75
+ result = _bot_reply(history, system_prompt, temperature, max_tokens, 1.0, 0)
76
+ return result[-1]["content"][0]["text"] if result else ""
77
+
78
+
79
+ with gr.Blocks(title=MODEL_LABEL) as demo:
80
+ gr.Markdown(f"# {MODEL_LABEL}")
81
+ gr.Markdown("Reality Fabricator Historic Chat Space. The backend uses the `_bot_reply` API endpoint.")
82
+
83
+ with gr.Row():
84
+ user_message = gr.Textbox(label="Message", value="What is electricity?")
85
+ with gr.Row():
86
+ system_prompt_box = gr.Textbox(label="System prompt", value="")
87
+ with gr.Row():
88
+ temperature_slider = gr.Slider(0.0, 2.0, value=0.7, step=0.05, label="Temperature")
89
+ max_tokens_slider = gr.Slider(16, 1024, value=256, step=1, label="Max tokens")
90
+ preview_button = gr.Button("Generate")
91
+ preview_output = gr.Textbox(label="Response")
92
+
93
+ preview_button.click(
94
+ preview_reply,
95
+ inputs=[user_message, system_prompt_box, temperature_slider, max_tokens_slider],
96
+ outputs=preview_output,
97
+ api_name=False,
98
+ )
99
+
100
+ history_input = gr.JSON(visible=False)
101
+ system_prompt_input = gr.Textbox(visible=False)
102
+ temperature_input = gr.Number(visible=False)
103
+ max_tokens_input = gr.Number(visible=False)
104
+ top_p_input = gr.Number(visible=False)
105
+ top_k_input = gr.Number(visible=False)
106
+ history_output = gr.JSON(visible=False)
107
+
108
+ gr.Button("API", visible=False).click(
109
+ _bot_reply,
110
+ inputs=[
111
+ history_input,
112
+ system_prompt_input,
113
+ temperature_input,
114
+ max_tokens_input,
115
+ top_p_input,
116
+ top_k_input,
117
+ ],
118
+ outputs=history_output,
119
+ api_name="_bot_reply",
120
+ )
121
+
122
+
123
+ if __name__ == "__main__":
124
+ demo.queue().launch()
model_adapters.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from typing import Any, Dict, List
4
+
5
+
6
+ def env_bool(name: str, default: bool = False) -> bool:
7
+ value = os.getenv(name)
8
+ if value is None:
9
+ return default
10
+ return value.strip().lower() in {"1", "true", "yes", "on"}
11
+
12
+
13
+ def messages_to_prompt(history: List[Dict[str, Any]], system_prompt: str = "") -> str:
14
+ parts = []
15
+ if system_prompt:
16
+ parts.append(system_prompt.strip())
17
+
18
+ for message in history or []:
19
+ role = message.get("role", "user")
20
+ text = extract_text(message)
21
+ if text:
22
+ parts.append(f"{role}: {text}")
23
+
24
+ parts.append("assistant:")
25
+ return "\n".join(parts)
26
+
27
+
28
+ def extract_text(message: Dict[str, Any]) -> str:
29
+ content = message.get("content") or ""
30
+ if isinstance(content, str):
31
+ return content
32
+ if isinstance(content, list):
33
+ return "".join(
34
+ item.get("text", "")
35
+ for item in content
36
+ if isinstance(item, dict) and item.get("type") == "text"
37
+ )
38
+ return str(content)
39
+
40
+
41
+ class DryRunAdapter:
42
+ def __init__(self, model_id: str):
43
+ self.model_id = model_id
44
+
45
+ def generate(self, history, system_prompt, temperature, max_tokens, top_p, top_k):
46
+ last_user = ""
47
+ for message in reversed(history or []):
48
+ if message.get("role") == "user":
49
+ last_user = extract_text(message)
50
+ break
51
+ return (
52
+ f"[DRY_RUN:{self.model_id}] Historic Chat Space contract is working. "
53
+ f"Last user message: {last_user or 'none'}"
54
+ )
55
+
56
+
57
+ class TransformersAdapter:
58
+ def __init__(self, model_id: str):
59
+ import torch
60
+ from transformers import AutoModelForCausalLM, AutoTokenizer
61
+
62
+ kwargs = parse_json_env("RFAB_HISTORIC_MODEL_KWARGS", {})
63
+ if "torch_dtype" in kwargs and kwargs["torch_dtype"] == "auto":
64
+ kwargs["torch_dtype"] = "auto"
65
+ elif "torch_dtype" not in kwargs and torch.cuda.is_available():
66
+ kwargs["torch_dtype"] = torch.float16
67
+
68
+ if "device_map" not in kwargs:
69
+ kwargs["device_map"] = "auto" if torch.cuda.is_available() else None
70
+
71
+ kwargs = {key: value for key, value in kwargs.items() if value is not None}
72
+
73
+ self.model_id = model_id
74
+ self.tokenizer = AutoTokenizer.from_pretrained(model_id)
75
+ self.model = AutoModelForCausalLM.from_pretrained(model_id, **kwargs)
76
+ if not torch.cuda.is_available() and kwargs.get("device_map") is None:
77
+ self.model.to("cpu")
78
+
79
+ def generate(self, history, system_prompt, temperature, max_tokens, top_p, top_k):
80
+ import torch
81
+
82
+ prompt = messages_to_prompt(history, system_prompt)
83
+ inputs = self.tokenizer(prompt, return_tensors="pt", return_token_type_ids=False)
84
+ device = next(self.model.parameters()).device
85
+ inputs = {key: value.to(device) for key, value in inputs.items()}
86
+
87
+ with torch.no_grad():
88
+ generate_kwargs = {
89
+ **inputs,
90
+ "max_new_tokens": int(max_tokens),
91
+ "temperature": float(temperature),
92
+ "top_p": float(top_p),
93
+ "do_sample": float(temperature) > 0,
94
+ "pad_token_id": self.tokenizer.eos_token_id,
95
+ }
96
+ if int(top_k) > 0:
97
+ generate_kwargs["top_k"] = int(top_k)
98
+ output = self.model.generate(**generate_kwargs)
99
+
100
+ generated = self.tokenizer.decode(output[0], skip_special_tokens=True)
101
+ if generated.startswith(prompt):
102
+ generated = generated[len(prompt):]
103
+ return generated.strip()
104
+
105
+
106
+ class NanochatAdapter:
107
+ def __init__(self, model_id: str):
108
+ raise RuntimeError(
109
+ "RFAB_HISTORIC_ADAPTER=nanochat is a scaffold. Copy the nanochat runtime "
110
+ "from tventurella/mr_chatterbox into this Space and replace NanochatAdapter "
111
+ "with that loader before setting DRY_RUN=false."
112
+ )
113
+
114
+
115
+ def parse_json_env(name: str, default):
116
+ raw = os.getenv(name)
117
+ if not raw:
118
+ return default
119
+ return json.loads(raw)
120
+
121
+
122
+ def create_adapter():
123
+ model_id = os.getenv("RFAB_HISTORIC_MODEL_ID", "dry-run-model")
124
+ if env_bool("DRY_RUN", True):
125
+ return DryRunAdapter(model_id)
126
+
127
+ adapter = os.getenv("RFAB_HISTORIC_ADAPTER", "transformers").strip().lower()
128
+ if adapter == "transformers":
129
+ return TransformersAdapter(model_id)
130
+ if adapter == "nanochat":
131
+ return NanochatAdapter(model_id)
132
+ raise ValueError(f"Unsupported RFAB_HISTORIC_ADAPTER: {adapter}")
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=5.0,<6
2
+ transformers>=4.46
3
+ accelerate>=1.0
4
+ torch
5
+ sentencepiece
6
+ huggingface_hub
7
+ spaces