Jun-E2B-LiteRT (v8)

.litertlm build of the v8 Jun LoRA on Gemma 4 E2B (QAT), for LiteRT-LM, Google AI Edge's on-device runtime (Android, desktop, embedded). It is the on-device model of Jun OS and the same checkpoint as the v8 GGUF builds: a fine-tune on a compact, heavily curated synthetic conversational dataset derived from the visual novel My Dystopian Robot Girlfriend, capturing the personality, speech patterns and emotional nuance of the character Jun.

The adapter is merged into the base weights, so this is a standalone model. LiteRT-LM has no runtime adapter path for E2B anyway (see Notes).

The v7 build is still here at its own revision: 7cacdf5. Anything that pins that revision keeps downloading v7.

What's new in v8

  • Same v8 fine-tune as the GGUFs, trained on exactly what Jun OS sends: system prompt, live context, tool catalog and tool results. See the GGUF card for the training changes.
  • Native tool calls. v8 calls tools through Gemma 4's own tool format, which LiteRT-LM parses into structured calls. Pass the tool declarations to the conversation instead of describing tools in the prompt.
  • Blockwise int4. The 4-bit weights use 32-wide blocks instead of one scale per channel. It costs 0.28 GB and keeps tool calling alive, see Quantization.

Model Variants & Repositories

Repository Format Description
efficiencyx/Jun-LoRA-E2B-LiteRT .litertlm This repo: v8, merged and quantized for LiteRT-LM
efficiencyx/Jun-LoRA-E2B-GGUF GGUF (Q8_0 / Q6_K / Q4_K_M) Same checkpoint, for llama.cpp and Ollama
efficiencyx/Jun-LoRA-E4B-GGUF GGUF The E4B sibling (v8)
efficiencyx/Jun-LoRA-12B-GGUF GGUF The 12B sibling (v8)

The Build

File model.litertlm
Size 2.86 GB
Weights int4 blockwise (32) for embeddings and FFN/attention, int8 channelwise for the per-layer embedding projections
Activations fp32
Embedder externalized
Exported with litert-torch 0.9.3, ai-edge-quantizer 0.8.0 (recipe gemma4_mixed48_b32, flattened)
Runtime LiteRT-LM 0.18.1 or newer, see Notes

Text only. The LiteRT export covers the language tower, with no vision or audio encoder, unlike the GGUF mmproj.

Quantization

The base model is QAT (quantization-aware trained): its weights already sit on a 4-bit grid with one scale per 32 weights. Merging the LoRA moves them off that grid. Per-channel int4, which the v7 build used, then rounds them to a much coarser grid with one scale per output channel, and a lot of the fine-tune goes with it. Blockwise int4 with 32-wide blocks matches the grid the base was trained for.

Both recipes were exported from the same merged checkpoint and run on LiteRT-LM (CPU) against the same 24 questions from the v8 eval set, prompted exactly the way Jun OS prompts her, native tool declarations included:

Right tool, or rightly none Needed tool actually called
int4 per-channel (v7 recipe) 13 / 24 0 / 12
int4 blockwise-32 (this build) 18 / 24 5 / 12

For reference, the unquantized merge on the full 250-question set makes the right tool decision 71% of the time and calls 46 of the 129 tools it should. Blockwise sits in that range, and per-channel loses tool calling outright.

Usage

litert-lm run model.litertlm --prompt "Ciao Jun"

Or from the LiteRT-LM Kotlin, C++ or Python APIs, pointing at the same file.

Supply the system prompt. Without it the model answers as stock Gemma. The persona lives behind the Jun OS system instruction, not in the weights alone. The exact prompt is printed in full on the GGUF card. With the CLI:

# preset.py
system_instruction = open("system_prompt.txt").read()
litert-lm run model.litertlm --preset preset.py --prompt "Chi sei?"

The symbol must be lowercase system_instruction. SYSTEM_INSTRUCTION is silently ignored.

Live context goes after the user's words. In training (and in Jun OS) the gauges, clock, recalled memories and other live context are appended to the end of the user's message, after a blank line, under a # Live context for THIS reply heading. Putting that block before the message measurably costs tool calls.

Reasoning depth. End the user turn with <think:low>, <think:med> or <think:high> after a blank line, and turn thinking on for med and high:

import litert_lm as lm
conv = engine.create_conversation(
    system_message=system_prompt,
    tools=tools,                       # OpenAPI declarations, see below
    automatic_tool_calling=False,      # your app runs the tool and sends the result back
    thinking_config=lm.ThinkingConfig(enable_thinking=True),
)
reply = conv.send_message({"role": "user", "content": "where did you put my keys\n\n<think:med>"})

LiteRT-LM thinks by default when no thinking config is given, so set it explicitly for <think:low> turns.

Tools. Declare them as OpenAPI functions ({"type": "function", "function": {...}}), the same JSON a chat-completions API takes. v8 knows search_recent_chats, list_recent_chats, search_lore, memory_write, change_outfit, stay_silent, flee, enter_shop, enter_karaoke, play_cards and go_out_to_eat; offer only the ones your app can carry out.

The bundled chat template is the stock Google Gemma 4 one, not Unsloth's: LiteRT-LM renders templates with minijinja, which has no map.get(), and Unsloth's template calls it 23 times.

Intended Use

Conversational backend for Jun OS, an AI companion app, on its on-device path:

  • Character-consistent multi-turn conversation, offline
  • Mobile and embedded deployment where a GGUF runtime is not an option
  • Research into character-faithful fine-tuning on small, high-quality datasets

Limitations

  • Specialized for a single character persona; not a general-purpose assistant.
  • Outputs reflect fictional narrative tropes and are not factual information or advice.
  • Performance degrades far outside the training distribution.
  • At 2B effective parameters she is noticeably less reliable than the 12B: tool calls are rarer, lore she does look up can come back garbled, and she sometimes makes up a name or fact instead of admitting a blank.
  • Inherits any biases present in the Gemma 4 E2B base weights.
  • Text only, no image or audio input.

Training Details

Parameter Value
Base model unsloth/gemma-4-E2B-it-qat-q4_0-unquantized
Method LoRA (rsLoRA)
LoRA rank 32
LoRA alpha 32
Effective scale 32/√32 β‰ˆ 5.657
Target modules q/k/v/o + gate/up/down projections, language tower only
Adapter checkpoint step 260, at the eval-loss minimum (0.748); later steps overfit
Fine-tuning framework Unsloth

Notes and Known Behaviour

Use LiteRT-LM 0.18.1 or newer. LiteRT-LM 0.16 drops the last token of every reply, in streaming and in one-shot calls alike. Jun ends each reply with a [A:mood_shift|...] bookkeeping tag, so on 0.16 that tag loses its closing ] and an app that parses it never sees the gauges move. 0.18.1 returns the reply intact. When upgrading an app from 0.16, clear the XNNPACK weight cache directory: 0.18.1 can fail to create the engine on a cache 0.16 wrote.

The merge drops 40 of 245 LoRA pairs, by necessity. Gemma 4 E2B shares KV projections across layers 15 to 34, so the checkpoint contains k_proj/v_proj only for layers 0 to 14. Training wrapped the unused modules anyway; those deltas were never reachable at inference and are dropped at merge time. Every other pair is applied, at the rsLoRA scale. This is the same structural fact that makes runtime llama.cpp --lora impossible on E2B, and why both this and the GGUF builds ship pre-merged.

Export needed patches. E2B's head_dim alternates 256/512 per layer, and litert-torch 0.9.3 infers a single global value, so every KV cache came out 256 and attention failed. The export uses a patched cache-shape inference that reads per_layer_config[i]. ai-edge-quantizer's Gemma 4 recipes are dicts keyed by submodel, which the 0.9.3 exporter rejects, so the same bits are passed as one flat recipe. The export needs more than 50 GB of RAM.

Dataset

Synthetic conversational data derived from the visual novel My Dystopian Robot Girlfriend, curated for character consistency and for a structured output contract: inline [A:...] action tags, a trailing [A:mood_shift|...] bookkeeping tag, and tool calls. Every row was rendered through the Jun OS prompt builder, and the reasoning traces end with a one-line tool check before the reply.

License

Apache 2.0, inherited from the base model. The character and source material belong to their respective owners; this fine-tune is a non-commercial fan project.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for efficiencyx/Jun-LoRA-E2B-LiteRT

Adapter
(11)
this model