DeepThoughTree / app.py
FlameF0X's picture
Update app.py
49dd498 verified
Raw
History Blame Contribute Delete
7.24 kB
import gradio as gr
import spaces
import torch
import re
from transformers import pipeline
# Initialize the lightweight LiquidAI Thinking Model
MODEL_ID = "LiquidAI/LFM2.5-1.2B-Thinking"
generator = pipeline(
"text-generation",
model=MODEL_ID,
dtype=torch.bfloat16
)
def generate_with_chat_template(messages, max_new_tokens=1536, do_sample=True, temperature=0.7, num_return_sequences=1):
"""
Applies the model's chat template to structure the prompts properly,
preventing prompt injection or completion confusion.
"""
tokenizer = generator.tokenizer
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
outputs = generator(
prompt,
max_new_tokens=max_new_tokens,
max_length=None,
generation_config=None,
num_return_sequences=num_return_sequences,
do_sample=do_sample,
temperature=temperature if do_sample else None
)
results = []
for out in outputs:
gen_text = out['generated_text']
# Extract only the newly generated text following the prompt
if gen_text.startswith(prompt):
gen_text = gen_text[len(prompt):]
results.append(gen_text.strip())
return results
@spaces.GPU(duration=60) # 60s is plenty because LiquidAI is blindingly fast
def tot_search(problem: str, branches: int = 3, max_depth: int = 3) -> str:
"""
Executes a Tree of Thoughts (ToT) search using the LiquidAI 1.2B Thinking model
and returns ONLY the final clean answer to the client.
"""
# Track statistics for server-side logs
stats = {
"nodes_generated": 0,
"nodes_evaluated": 0,
"nodes_pruned": 0,
"actual_depth_reached": 0
}
current_paths = [{"history": [problem], "score": 1.0}]
tree_history_log = []
for depth in range(max_depth):
stats["actual_depth_reached"] = depth + 1
new_paths = []
depth_log = []
for p_idx, path in enumerate(current_paths):
latest_thought = path["history"][-1]
# 1. GENERATE BRANCHES
messages = [
{
"role": "system",
"content": "You are a helpful reasoning assistant. Provide a single, distinct, and logical next step to solve the user's problem. Be extremely concise, direct, and focused."
},
{
"role": "user",
"content": f"Problem: {problem}\nCurrent progress: {latest_thought}\nWhat is the single next logical step?"
}
]
outputs = generate_with_chat_template(
messages,
max_new_tokens=1536,
do_sample=True,
temperature=0.7,
num_return_sequences=branches
)
# 2. EVALUATE/SCORE BRANCHES
for out_text in outputs:
stats["nodes_generated"] += 1
# LiquidAI wraps internal thoughts in <think> tags. Extract clean step.
next_step = re.sub(r'<think>.*?</think>', '', out_text, flags=re.DOTALL).strip()
if not next_step:
next_step = out_text # Fallback
eval_messages = [
{
"role": "system",
"content": "You are an evaluator. Your task is to rate whether a proposed next step is helpful for solving the given problem. You must respond with exactly one of these words: 'Good', 'Maybe', or 'Bad'. Do not explain your choice."
},
{
"role": "user",
"content": f"Problem: {problem}\nProposed Next Step: {next_step}\nIs this step 'Good', 'Maybe', or 'Bad'?"
}
]
eval_outs = generate_with_chat_template(eval_messages, max_new_tokens=1536, do_sample=False)
eval_text = eval_outs[0].lower()
stats["nodes_evaluated"] += 1
# Strip thinking tags from evaluation
eval_text = re.sub(r'<think>.*?</think>', '', eval_text, flags=re.DOTALL).strip()
score = 0.0
if "good" in eval_text:
score = 1.0
elif "maybe" in eval_text:
score = 0.5
depth_log.append({
"parent_node": p_idx,
"thought": next_step[:120] + "...",
"evaluation": eval_text,
"score": score
})
if score > 0:
new_paths.append({
"history": path["history"] + [next_step],
"score": score
})
# 3. PRUNE (Keep top 2 paths)
original_count = len(new_paths)
current_paths = sorted(new_paths, key=lambda x: x["score"], reverse=True)[:2]
stats["nodes_pruned"] += (original_count - len(current_paths))
tree_history_log.append((depth + 1, depth_log))
if not current_paths:
break
# Print execution trace to the background Hugging Face Space console logs
print("\n--- Tree of Thoughts Execution Logs ---")
for d, logs in tree_history_log:
print(f"Depth {d}:")
for l in logs:
print(f" - [{l['evaluation'].upper()}] Thought: {l['thought']}")
print(f"Stats: Depth={stats['actual_depth_reached']}, Generated={stats['nodes_generated']}, Pruned={stats['nodes_pruned']}\n")
if not current_paths:
return "Error: All reasoning paths hit a dead end."
# 4. SYNTHESIZE THE WINNING PATH
best_chain = " -> ".join(current_paths[0]["history"])
final_messages = [
{
"role": "system",
"content": "You are a helpful assistant. Synthesize the final, concise answer to the problem based on the provided reasoning path. Do not include any meta-reasoning, instruction-following placeholders, or thinking tags. Provide only the clean, final direct answer."
},
{
"role": "user",
"content": f"Problem: {problem}\nReasoning path: {best_chain}\nWhat is the final concise answer?"
}
]
final_outs = generate_with_chat_template(final_messages, max_new_tokens=1536, do_sample=False)
final_response = final_outs[0]
# Strip everything down to get just the clean answer
clean_answer = re.sub(r'<think>.*?</think>', '', final_response, flags=re.DOTALL).strip()
return clean_answer
# Define the Gradio Interface
demo = gr.Interface(
fn=tot_search,
inputs=[
gr.Textbox(label="Problem"),
gr.Slider(2, 4, value=3, step=1, label="Branches"),
gr.Slider(2, 4, value=3, step=1, label="Max Depth")
],
outputs="text",
title="DeepThoughTree --- Tree of Thoughts (ToT) Orchestrator"
)
# Launch with MCP enabled
demo.launch(mcp_server=True)