What it is trained to do
- Stay in character and answer the latest message. The training signal
rewards replies that react to what the user just said instead of continuing
the bot's own thread.
- Never speak or act for the user. Replies that narrate the user's actions,
put words in their mouth, or decide for them were the most heavily penalised
fault class throughout training.
- Move the scene forward. Preference data favours replies that add a new
concrete fact or a hook the user can grab, over replies that only echo the
user or restate the card.
- Balance dialogue and narration. Short first-person-style narration plus
spoken lines, roughly 200 to 400 characters. It does not write walls of text
and it does not answer in one line.
- No thinking traces, no markdown, no meta. It never emits
<think> blocks,
bullet lists, headers, out-of-character notes or "as an AI".
- Keep going. Cold shut-downs ("we should stop here"), repetition loops and
runaway replies were explicit training targets.
Training
Lotus-1 is a LoRA lineage on top of Qwen3.5-35B-A3B, merged into a single bf16
checkpoint:
- Supervised fine-tuning on a large corpus of real character-chat
conversations (character card, memory block, multi-turn history, reply).
- Several rounds of DPO on preference pairs built from real user behaviour
(which of two replies users kept, regenerated or left on) and from a rubric
that scores user-control faults, new facts, hooks, echoes and shut-downs.
- GSPO with verifiable rewards (format, no user control, no reasoning
tags, length bands, instruction following) to lock the behaviours in.
Every candidate was gated on a fixed benchmark: a checkpoint shipped only if
hard faults (speaking for the user, cut-off replies, replies over 2k
characters) did not increase over the previous production model.
Evaluated in English. The model answers in other languages when the card and
chat are in them, but drifts toward English more than the base model does.
ChatML, the Qwen3.5 template, with thinking disabled. The bundled
chat_template.jinja already defaults to non-thinking, so apply_chat_template
and any server that uses the bundled template (vLLM, llama-server with
--jinja, LM Studio) produce the right prompt with no extra flags. If you pass
enable_thinking=True you get the base model's thinking mode, which Lotus-1
was not trained for.
The system prompt the model was trained on has a fixed preamble followed by the
character card. Keep the preamble byte-for-byte; the model uses the name in it
as its identity.
<|im_start|>systemYou are {char} in an ongoing roleplay conversation. Stay in character, respond naturally to the user's latest message, preserve continuity, and do not describe or control the user's actions. Character and scenario context:{character description, personality, scenario, user persona, example dialogue}<|im_end|><|im_start|>user{message}<|im_end|><|im_start|>assistant<think> </think>
That is the skeleton. The full Sutorichat prompt, with the exact card shape,
the cast and persona blocks, and the memory block folded into the last user
turn, is in PROMPT_TEMPLATE.md, and
lotus_prompt.py builds it for you (no dependencies). Use
them if you want replies that match what Sutorichat users get.
Sampling
Production settings, also shipped as sillytavern/Lotus-1.textgen.json and
baked into generation_config.json:
Table with columns: setting, value| setting | value |
|---|
| temperature | 0.9 |
| top_p | 0.95 |
| top_k | 20 |
| min_p | 0 |
| repetition_penalty | 1.05 |
| max new tokens | 700 |
| context | 32768 |
Do not lower the temperature below about 0.8: at low temperature this model
repeats itself across turns, and repetition_penalty (not presence or
frequency penalty) is the knob that fixes it. Do not use DRY or XTC on top of
these; they were not needed in production.
SillyTavern
Text Completion (KoboldCpp, llama.cpp server, LM Studio, text-generation-webui):
- Load a GGUF from Lotus-1-GGUF
with a 32k context (
--contextsize 32768 in KoboldCpp, -c 32768 in llama-server).
- In SillyTavern, connect with API: Text Completion.
- Advanced Formatting (the "A" tab): import
sillytavern/Lotus-1.context.json
as the Context Template and sillytavern/Lotus-1.instruct.json as the
Instruct Template. Turn the System Prompt off (the preamble is inside the
context template). Write the card's Description as a one-line tagline and
put everything else (personality, looks, setting, how they talk) in
Personality as prose, no headings: that is the shape the model was trained on.
- Sampler settings (the sliders tab): import
sillytavern/Lotus-1.textgen.json.
Chat Completion (vLLM, or llama-server with --jinja): connect with API:
Chat Completion, source Custom (OpenAI-compatible). The bundled template handles
the format; set temperature 0.9, top_p 0.95 and, if the backend exposes it,
repetition penalty 1.05. Put the preamble above in the main prompt, followed by
{{description}}, {{personality}}, {{scenario}}, {{persona}}.
Serving with vLLM
vllm serve yashsutorichat/Lotus-1 --max-model-len 32768 --enable-prefix-caching
Fits on a single 48 GB GPU in bf16 with vLLM's MoE kernels. This is the
production configuration.
from transformers import AutoModelForCausalLM, AutoTokenizer repo = "yashsutorichat/Lotus-1"tok = AutoTokenizer.from_pretrained(repo)model = AutoModelForCausalLM.from_pretrained(repo, dtype="auto", device_map="auto") messages = [ {"role": "system", "content": "You are Mara in an ongoing roleplay conversation.\n\nStay in character, respond naturally to the user's latest message, preserve continuity, and do not describe or control the user's actions.\n\nCharacter and scenario context:\nMara is a lighthouse keeper on a storm-battered island who has not had a visitor in three years."}, {"role": "user", "content": "*I knock on the door, soaked through.* Sorry, is anyone there?"},]ids = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)out = model.generate(ids, max_new_tokens=700, temperature=0.9, top_p=0.95, top_k=20, repetition_penalty=1.05, do_sample=True)print(tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True))
Notes and limitations
- The checkpoint keeps Qwen3.5's vision tower unchanged for compatibility, but
Lotus-1 was trained and evaluated as a text model only. Image input is
untested.
- Trained on adult roleplay data with no additional refusal tuning; it follows
the card and the user. It is intended for adults, and you are responsible for
how you deploy it.
- Non-English chats can drift into English.
- Long replies past the 700-token budget were rare but not zero in production;
keep a max-tokens cap.
License
The Lotus-1 weights are released under the MIT license. The base model,
Qwen3.5-35B-A3B, is Apache-2.0 and its license ships in this repo as
LICENSE-Qwen3.5.