import queue
import threading
import time
import gradio as gr
import spaces
import torch
from transformers import PreTrainedTokenizerFast
import mfn_hf # shared look + usage analytics; import before @spaces.GPU
from flint import generate, load_flint
MODEL_ID = "RecursiveSI/flint"
TITLE = "Flint"
FP8_EXPERTS = False # bf16 fits on a full RTX Pro 6000; FP8 experts are the fallback
# Prompt + reply share this window. Measured on an RTX Pro 6000 with the bf16 weights:
# 32K tokens peak at ~79 GiB of the 95 GiB card.
MAX_CONTEXT = 32768
MAX_NEW_TOKENS = 8192
# Measured: prefill ~12K tok/s at 8K-32K, decode 100-113 tok/s (CUDA-graph decode). Conservative.
PREFILL_TOK_S = 6000.0
DECODE_TOK_S = 60.0
MAX_GPU_SECONDS = 300
tokenizer = PreTrainedTokenizerFast.from_pretrained(MODEL_ID)
model, _cfg = load_flint(MODEL_ID, device="cuda", dtype=torch.bfloat16, fp8_experts=FP8_EXPERTS, delete_shards=True)
EOS_IDS = {150001, 150003, 150005, 150018}
DEFAULT_SYSTEM = ""
def gpu_seconds(prompt_tokens, max_new_tokens):
est = 10 + prompt_tokens / PREFILL_TOK_S + int(max_new_tokens) / DECODE_TOK_S
return int(min(MAX_GPU_SECONDS, est))
def gpu_duration(message, history, system_prompt, max_new_tokens, *args):
try:
system, msgs = build_messages(message, history, system_prompt)
n = int(encode(system, msgs, max_new_tokens).shape[0])
except Exception:
n = MAX_CONTEXT - int(max_new_tokens)
return gpu_seconds(n, max_new_tokens)
def text_of(content):
if isinstance(content, str):
return content
if isinstance(content, dict):
return content.get("text") or ""
if isinstance(content, (list, tuple)):
return "".join(text_of(c) for c in content)
return ""
def build_messages(message, history, system_prompt):
"""Chat history -> template messages; collapsed thinking becomes reasoning_content."""
msgs = []
reasoning = None
for turn in history or []:
role = turn.get("role")
text = text_of(turn.get("content"))
if role == "assistant" and (turn.get("metadata") or {}).get("title"):
reasoning = text
continue
if role == "user":
msgs.append({"role": "user", "content": text})
reasoning = None
elif role == "assistant":
if msgs and msgs[-1]["role"] == "assistant":
msgs[-1]["content"] += "\n\n" + text
else:
m = {"role": "assistant", "content": text}
if reasoning:
m["reasoning_content"] = reasoning
msgs.append(m)
reasoning = None
msgs.append({"role": "user", "content": text_of(message)})
system = (system_prompt or "").strip()
return ([{"role": "system", "content": system}] if system else []), msgs
def encode(system, msgs, max_new_tokens):
limit = MAX_CONTEXT - int(max_new_tokens)
while True:
ids = tokenizer.apply_chat_template(
system + msgs, add_generation_prompt=True, return_tensors="pt", return_dict=True
)["input_ids"][0]
if ids.shape[0] <= limit or len(msgs) <= 1:
break
msgs = msgs[2:] if len(msgs) > 2 else msgs[1:] # drop the oldest exchange
if ids.shape[0] > limit:
raise gr.Error(
f"The message is too long for this demo: {MAX_CONTEXT:,} tokens are shared by the prompt and the reply."
)
return ids
def split_thinking(text):
"""Flint decides per turn whether to reason; reasoning comes first, inside ...."""
s = text.lstrip()
if not s.startswith(""):
if "".startswith(s): # still undecided
return "", "", False
return "", text.strip(), True
if "" in s:
thinking, answer = s[len(""):].split("", 1)
return thinking.strip(), answer.strip(), True
return s[len(""):].strip(), "", False
def render(thinking, answer, done_thinking, think_secs):
out = []
if thinking:
title = f"Thought for {think_secs:.1f} s" if done_thinking else "Thinking"
out.append(
gr.ChatMessage(
role="assistant",
content=thinking,
metadata={"title": title, "status": "done" if done_thinking else "pending"},
)
)
if answer or not out:
out.append(gr.ChatMessage(role="assistant", content=answer))
return out
@spaces.GPU(size="xlarge", duration=gpu_duration)
def chat(message, history, system_prompt, max_new_tokens, temperature, top_p, top_k):
if not text_of(message).strip():
raise gr.Error("Type a message first.")
t0 = time.time()
max_new_tokens = int(max_new_tokens)
system, msgs = build_messages(message, history, system_prompt)
input_ids = encode(system, msgs, max_new_tokens)
budget = gpu_seconds(int(input_ids.shape[0]), max_new_tokens) - 5
tokens = queue.SimpleQueue() # token ids from the generation thread; None marks the end
state = {"n": 0, "error": None, "timed_out": False}
def should_stop():
if time.time() - t0 > budget:
state["timed_out"] = True
return True
return False
def worker():
try:
state["n"] = generate(
model,
input_ids,
max_new_tokens=max_new_tokens,
eos_ids=EOS_IDS,
temperature=float(temperature),
top_p=float(top_p),
top_k=int(top_k),
on_token=tokens.put,
should_stop=should_stop,
)
except Exception as e: # surfaced in the UI below
state["error"] = e
finally:
tokens.put(None)
thread = threading.Thread(target=worker, daemon=True)
thread.start()
# Decode in this thread, and at most ~20 UI updates a second, so the GPU loop never waits.
ids = []
text = ""
think_start = None
think_secs = 0.0
finished = False
while not finished:
try:
item = tokens.get(timeout=0.05)
except queue.Empty:
continue
if item is None:
finished = True
else:
ids.append(item)
deadline = time.time() + 0.05
while not finished and time.time() < deadline:
try:
item = tokens.get(timeout=max(0.0, deadline - time.time()))
except queue.Empty:
break
if item is None:
finished = True
else:
ids.append(item)
if not ids:
continue
text = tokenizer.decode(ids, skip_special_tokens=False)
if text.endswith("\ufffd") and not finished: # half of a multi-byte character
continue
thinking, answer, done = split_thinking(text)
if thinking and not done:
think_start = think_start or time.time()
think_secs = time.time() - think_start
yield render(thinking, answer, done, think_secs), ""
thread.join()
if state["error"] is not None:
raise gr.Error("Generation failed. Please try again.")
dt = time.time() - t0
thinking, answer, _ = split_thinking(text)
n = state["n"]
took = f"Took {dt:.1f} s · {n} tokens · {n / dt:.1f} tok/s"
if state["timed_out"]:
took += " · stopped at the time limit"
elif n >= max_new_tokens:
took += " · hit the max new tokens limit"
yield render(thinking, answer, True, think_secs), took
EXAMPLES = [
"Write a Python function that merges overlapping intervals, with a few tests.",
"Explain what this does and find the bug:\n\n```python\ndef dedupe(items):\n seen = set()\n for i, x in enumerate(items):\n if x in seen:\n items.pop(i)\n seen.add(x)\n return items\n```",
"Implement an LRU cache in TypeScript with O(1) get and put.",
"Write a bash one-liner that finds the 10 largest files under the current directory.",
"Convert this SQL to a pandas expression: SELECT city, AVG(price) FROM sales WHERE year = 2024 GROUP BY city ORDER BY 2 DESC;",
]
with gr.Blocks(title=TITLE) as demo:
mfn_hf.header(
TITLE,
model=MODEL_ID,
description="Recursive's 40B mixture-of-experts coding model (about 2.5B active per token). It decides on each turn whether to reason first.",
flow_fn="/chat",
)
system_prompt = gr.Textbox(label="System prompt", lines=2, value=DEFAULT_SYSTEM, placeholder="Optional", render=False)
max_new_tokens = gr.Slider(256, MAX_NEW_TOKENS, value=4096, step=256, label="Max new tokens",
info="Reasoning counts toward this.", render=False)
temperature = gr.Slider(0.0, 1.5, value=1.0, step=0.05, label="Temperature", info="0 means greedy.", render=False)
top_p = gr.Slider(0.05, 1.0, value=0.95, step=0.05, label="Top-p", render=False)
top_k = gr.Slider(0, 200, value=20, step=1, label="Top-k", info="0 turns it off.", render=False)
took = gr.Markdown("", elem_classes="mfn-meta", render=False)
gr.ChatInterface(
chat,
additional_inputs=[system_prompt, max_new_tokens, temperature, top_p, top_k],
additional_inputs_accordion=gr.Accordion("Settings", open=False, render=False),
additional_outputs=[took],
examples=[[e] for e in EXAMPLES],
cache_examples=False,
chatbot=gr.Chatbot(height=560, label="Chat"),
textbox=gr.Textbox(placeholder="Ask a coding question", show_label=False),
api_name="chat",
fill_height=False,
)
took.render()
gr.Markdown("Model by [Recursive](https://www.recursive.com) · Apache-2.0", elem_classes="mfn-meta")
mfn_hf.footer()
if __name__ == "__main__":
demo.launch()