When Single Answer Is Not Enough: Rethinking Single-Step Retrosynthesis Benchmarks for LLMs
Paper • 2602.03554 • Published
How to use insilicomedicine/C3LM with Transformers:
# Use a pipeline as a high-level helper
from transformers import pipeline
pipe = pipeline("text-generation", model="insilicomedicine/C3LM")
messages = [
{"role": "user", "content": "Who are you?"},
]
pipe(messages) # Load model directly
from transformers import AutoTokenizer, AutoModelForMultimodalLM
tokenizer = AutoTokenizer.from_pretrained("insilicomedicine/C3LM")
model = AutoModelForMultimodalLM.from_pretrained("insilicomedicine/C3LM")
messages = [
{"role": "user", "content": "Who are you?"},
]
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=True,
return_dict=True,
return_tensors="pt",
).to(model.device)
outputs = model.generate(**inputs, max_new_tokens=40)
print(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:]))How to use insilicomedicine/C3LM with vLLM:
# Install vLLM from pip:
pip install vllm
# Start the vLLM server:
vllm serve "insilicomedicine/C3LM"
# Call the server using curl (OpenAI-compatible API):
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
--data '{
"model": "insilicomedicine/C3LM",
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
}
]
}'docker model run hf.co/insilicomedicine/C3LM
How to use insilicomedicine/C3LM with SGLang:
# Install SGLang from pip:
pip install sglang
# Start the SGLang server:
python3 -m sglang.launch_server \
--model-path "insilicomedicine/C3LM" \
--host 0.0.0.0 \
--port 30000
# Call the server using curl (OpenAI-compatible API):
curl -X POST "http://localhost:30000/v1/chat/completions" \
-H "Content-Type: application/json" \
--data '{
"model": "insilicomedicine/C3LM",
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
}
]
}'docker run --gpus all \
--shm-size 32g \
-p 30000:30000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env "HF_TOKEN=<secret>" \
--ipc=host \
lmsysorg/sglang:latest \
python3 -m sglang.launch_server \
--model-path "insilicomedicine/C3LM" \
--host 0.0.0.0 \
--port 30000
# Call the server using curl (OpenAI-compatible API):
curl -X POST "http://localhost:30000/v1/chat/completions" \
-H "Content-Type: application/json" \
--data '{
"model": "insilicomedicine/C3LM",
"messages": [
{
"role": "user",
"content": "What is the capital of France?"
}
]
}'How to use insilicomedicine/C3LM with Docker Model Runner:
docker model run hf.co/insilicomedicine/C3LM
C3LM is a chemistry-focused language model for Single-Step Retrosynthesis task. This checkpoint was initialized from LFM2-2.6B and trained with supervised fine-tuning (SFT) on a combined CREED + USPTO-full dataset.
from transformers import AutoModelForCausalLM, AutoTokenizer
# Load model and tokenizer
model_id = "insilicomedicine/C3LM"
model = AutoModelForCausalLM.from_pretrained(
model_id,
device_map="auto",
torch_dtype="bfloat16",
# attn_implementation="flash_attention_2" <- uncomment on compatible GPU
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
import re
ATOM_TOKENS = ["Ag","Al","As","Au","Bi","Br","Ca","Cl","Cr","Cu","Fe","Ga","Gd","Ge","Hg","Li","Mg","Mo","Na","Pt","Ru","Sb","Se","Si","Sn","Sc","B","C","F","H","I","K","M","N","O","P","S","V","W","Z","c","e","n","o","p","s"]
SMI_REGEX_PATTERN = (
r"(\[|\]|\(|\)|\.|=|#|-|\+|\\|\/|:|~|@|\?|>>?|\*|\$|\%[0-9]{2}|[0-9]|"
+ "|".join(ATOM_TOKENS)
+ ")"
)
smi_regex = re.compile(SMI_REGEX_PATTERN)
def isolate_sequence(s):
return "".join(["<sm_" + tok + ">" for tok in smi_regex.findall(s)])
def deisolate_sequence(s):
s = re.sub("[<\s]?sm_[^>]+>", lambda matchobj: matchobj.group(0)[4:-1], s).strip()
return s
# Prepare prompts
product_smiles = "N1C(=O)Cc2ccc(Nc3ccc(N[C@@H]4CN(C(c5cc(NCCO)ccc5)=O)C[C@@H]4c4scnc4)cc3)cc21"
prompt = f"What are the possible reactants that could have formed the following product <smiles>{isolate_sequence(product_smiles)}</smiles>? Wrap an answer in <smiles> tags/think"
input_ids = tokenizer.apply_chat_template(
[
{"role": "system", "content": "You are a helpful assistant in chemistry and biology."},
{"role": "user", "content": prompt}
],
add_generation_prompt=True,
return_tensors="pt",
tokenize=True,
)['input_ids'].to(model.device)
# Generate answer
output = model.generate(
input_ids,
do_sample=True,
temperature=0.3,
min_p=0.15,
repetition_penalty=1.05,
max_new_tokens=512,
)
for out in output:
out = tokenizer.decode(out, skip_special_tokens=False)
answer_part = out.split('</think>')[1].split('<smiles>')[1].split('</smiles>')[0]
answer_part = deisolate_sequence(answer_part)
print('Reactants: ', answer_part)
@misc{zagribelnyy2026singleanswerenoughrethinking,
title={When Single Answer Is Not Enough: Rethinking Single-Step Retrosynthesis Benchmarks for LLMs},
author={Bogdan Zagribelnyy and Ivan Ilin and Maksim Kuznetsov and Nikita Bondarev and Roman Schutski
and Thomas MacDougall and Rim Shayakhmetov and Zulfat Miftakhutdinov
and Mikolaj Mizera and Vladimir Aladinskiy and Alex Aliper and Alex Zhavoronkov},
year={2026},
eprint={2602.03554},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2602.03554},
}
Base model
LiquidAI/LFM2-2.6B