Runtime and integration

Validate a LiteRT-LM Chat Template

Bind prompt formatting to the exact model artifact and test it with fixed conversation fixtures.

Review status: Fact and editorial QA complete; owner review pending · 2026-10-06 · Fact IDs: LITERTLM-01, COVE-MODEL-02, LITERTLM-KOTLIN-CONVERSATION-SNAPSHOT-2026-10

Direct answer

A chat-template preflight checks the serialization boundary between application messages and the exact model artifact before a release or artifact upgrade. It answers whether representative Message objects render with the intended role, content, control tokens, prefix, and end-of-turn convention. It does not measure translation or reasoning quality, and a rendered string that looks plausible does not prove that the resolved Android dependency accepts the same API or that a device task succeeds. Start by recording the artifact revision, tokenizer/template identity, resolved runtime component, app build, and the source format expected by that model family. Gemma 4 defines literal role names and turn markers that are specific to its tokenizer. Derive the preflight from the authoritative formatting guide for the selected generation and artifact, not from an abstract application role name or a different Gemma generation. When using the documented Gemma 4 format, assert the literal system, user, and model roles, the opening marker <|turn>, and the ending marker <turn|>. An open generation prefix is the documented <|turn>model form; a completed prior model turn ends with the documented turn marker. Include a system turn only if the product path uses one, and keep tool or multimodal markers out of a text-only fixture unless that path is being validated. The pinned LiteRT-LM Kotlin Conversation source provides a renderMessageIntoString helper marked experimental. It is useful as a diagnostic or test observation for a Message and optional extraContext only after a native Conversation has been created from an initialized engine and is still alive. The helper checks conversation state and can throw if it is closed or native rendering fails. It is not a guarantee that every released artifact or current project dependency exposes this method. The upstream commit cited here is a code snapshot; it does not prove which version Gradle resolved in an application build. Normal send APIs accept a Message, Contents, or text plus extraContext and pass that message representation to the native runtime, which performs its own prompt rendering. Do not take a rendered diagnostic string and submit it as ordinary message text unless the API contract explicitly says the input is already serialized. Doing so can wrap role markers a second time or make visible control-token spellings ordinary content. Conversely, do not hand-format messages and assume the native runtime will preserve the exact bytes. Test the supported typed path separately from any diagnostic rendering helper. Make fixtures small, synthetic, and structural. A useful set covers one user turn, a prior completed model turn followed by a new user turn, the open <|turn>model generation prefix, optional extra context, and a completed turn ending with <turn|>. Assert the literal role order, whether the model prefix is open or closed, and the marker expected by the selected official format. Tie expected fixtures to artifact and template revision. A hand-constructed golden string is only a fixture; it does not prove what the native renderer produced. Compare it with the live helper output in a test Conversation before treating rendering as observed. The preflight should not persist or log real prompts. Rendered text may include private user content, system instructions, or task-specific context, so use synthetic messages and restrict diagnostic output to a test report. Store artifact and template identifiers, fixture ID, expected structural assertions, observed rendering, resolved dependency coordinates, and result. If the helper is missing or experimental opt-in is not allowed, test through the documented typed send path with a controlled native fixture or keep the limitation explicit; do not fabricate a test result from application-side string construction alone. Interpret failures by layer. A wrong role or marker may indicate an application serializer or format expectation issue. A correct rendered fixture with a failed native send points to a different boundary such as dependency/API availability, model load, or runtime behavior. An output that is fluent or malformed is not itself proof of a template mismatch without comparing the exact request and supported model format. Do not tune sampling, alter the tokenizer, or switch artifacts in the same experiment. The outcome is a reproducible formatting check for one named configuration, not a model-capability statement or a device benchmark.

A runtime upgrade is proposed for a pinned model

An Android team updates the resolved LiteRT-LM dependency while keeping the same model family label. The app sends typed messages with extraContext and maintains a multi-turn conversation. A recent successful run used an earlier resolved artifact, and a developer considers manually rendering the prompt before sending it to make the new output easier to inspect.

Decision

First verify which API methods exist in the resolved dependency. Then use a small synthetic conversation to inspect rendering with the experimental helper only as a test aid, and exercise the ordinary typed send path without wrapping a pre-rendered string a second time.

Expected outcome

The release record names the dependency, artifact and template revision, structural fixture, and observed stage. It does not claim the upstream source snapshot matches the app dependency or that a template check proves device support or task quality.

Evidence to collect

The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:

  • Artifact and template revision
  • Rendered prompt fixture
  • Known conversation outcomes

A runtime comparison is publishable only when artifact identity, build configuration, selected backend, task fixture, and failures are recorded.

Implementation workflow

  1. Pin the exact inputs and resolved API surface. Record model artifact and tokenizer revision, prompt/template revision, app build, and the actual resolved Android runtime coordinate. Inspect the resolved class methods before referencing renderMessageIntoString or cancel APIs; an upstream commit URL alone does not establish that the app includes them.
  2. Translate the official format into assertions. For the exact model generation, transcribe literal roles and control markers from its official formatting guide. For Gemma 4, check system/user/model role names, <|turn>, <turn|>, and the open <|turn>model prefix; do not substitute an application-level assistant role without source support.
  3. Create minimal synthetic message fixtures. Use one-turn and two-turn examples, a completed prior model response, the documented open model-generation prefix where applicable, and a separate extra-context case. A hand-built expected string is not observed runtime output; compare against a live native Conversation in a controlled test.
  4. Inspect diagnostic rendering only as a test path. If the resolved class provides the experimental renderMessageIntoString helper, opt into it in a controlled test and verify the conversation is alive. Record its output against structural assertions; do not treat helper availability or a plausible string as proof that the app dependency and artifact combination is supported.
  5. Exercise the actual typed send path once. Submit the Message through the documented send or sendAsync overload with its intended extraContext. Do not feed the rendered fixture back as user text or add role markers manually. Record whether the native call starts and reaches a terminal callback separately from the rendering assertion.
  6. Review differences without broadening the claim. When a fixture differs, change one versioned input at a time and preserve the previous result. Attribute the finding to serialization, dependency API availability, model load, or task output only when the evidence distinguishes those layers.

Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.

Acceptance criteria

  • The record pins the resolved Android runtime and exact artifact/template identities; upstream source code is not treated as evidence of the resolved dependency surface.
  • Synthetic fixtures assert role order, prefix and turn-ending behavior against the official format for the selected generation, and diagnostics contain no private prompt content.
  • The ordinary typed send path is tested separately from experimental rendering, with no double-formatting of an already rendered message.

Decision table

DimensionChoice or evidenceHow to interpret it
Application-side string constructionManually concatenate visible role markers and message textCan test a locally built string but may not match native template rendering, tokenization, or runtime behavior.
Experimental render helperRender a typed Message in a controlled diagnostic fixtureCan expose the pinned runtime renderer output when that exact resolved API exists; it is experimental and not the normal send operation.
Typed send pathPass Message or Contents to the documented send APIExercises the integration path where native rendering occurs; its success still does not prove model quality or broad device compatibility.

Failure patterns to prevent

  • Copying a template from another model family
  • Changing template and decoding together

Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.

Minimum reproducibility record

LayerRecord
DeviceManufacturer, model, chipset, RAM class, operating-system build
SoftwareApplication version and git commit
ModelFamily, variant, revision, format, file length, hash, quantization
RuntimeName, revision, requested backend, observed backend evidence
WorkloadFixture revision, input hash, prompt hash, output policy
OutcomeCompleted, failed, cancelled, fallback, and privacy-safe diagnostics

Release checklist

  • ☐ The primary query is answered without an unsupported number.
  • ☐ Every artifact and runtime is pinned.
  • ☐ The representative task and failure policy are explicit.
  • ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
  • ☐ User-content and network boundaries are documented.
  • ☐ Result wording applies only to the recorded configuration.
  • ☐ The page links to raw method or evidence when results are added.

Sources and related evidence

Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.

Frequently asked questions

Should renderMessageIntoString be used to format every production request?

No. In the cited Kotlin source it is an experimental diagnostic helper. The send APIs accept typed messages and perform rendering internally, so manually rendering and then sending the string can apply formatting twice.

Does the upstream Conversation.kt method prove my Gradle dependency has it?

No. The immutable source URL identifies one repository snapshot. Inspect the resolved Android artifact and available class methods before compiling against that helper or describing it as present in the application.

If the rendered fixture matches Gemma formatting, is the model supported?

No. The check validates one serialization path and template expectation. Artifact compatibility, runtime initialization, task behavior, and any device-specific result require separate evidence.