pub async fn generate(
stream_fn: &StreamFn,
opts: &LlmOpts,
ctx: &[AgentMessage],
) -> Result<String, String>Expand description
Ask the model for a session title and return the sanitized result.
The single model call is shaped exactly like
crate::compaction::summarize (crates/wcode-harness/src/compaction.rs:628):
one stream_fn invocation with an EMPTY tool list, the [TITLE_PROMPT] as
system, and one AgentMessage::user_text carrying both halves; then a fold
of LlmStreamEvent::TextDelta → String until Done (or Error).
§Contract
- Input. The first user text and the first assistant text in
ctx, each clamped to [TITLE_INPUT_CHARS], rendered asUSER:\n<…>\n\nASSISTANT:\n<…>in ONE user message — the two halves the test namegenerate_collects_text_and_sends_both_halvesasserts on.ctx[0]is SKIPPED when it is a compaction summary (it starts withCOMPACTION_SUMMARY_PREFIX); seeSession::messages(crates/wcode-harness/src/session.rs:186-188) which prepends one on a resume. Half-missing is not an error: a request with only the user half (an assistant turn that produced no text) still gets asked. - Output.
Ok(title)wheretitleis the output ofsanitize— never empty, never longer thanTITLE_CLAMPchars. - Empty answer (no
TextDeltaat all, onlyDone, or deltas thatsanitizerejects):Err("titler produced no text"). Mirrorssummarize’sErr("summarizer produced no text")(compaction.rs:660-662). - Stream error (
LlmStreamEvent::Error { message, .. }):Err(message)verbatim, first error wins, the fold stops. Mirrorscompaction.rs:654. - No input at all (no user text and no assistant text):
Err("no input to title from")WITHOUT a model call — a request that cannot help is not sent. - Does NOT promise: a retry of its own (it inherits the adapter’s
retry/idle policy exactly as
summarizedoes), a usage report (the caller does not bill it), a cancel path (v1: the call is bounded by the adapter’s idle backstop, and the auto-trigger waits for it — D1’s accepted cost), or that the model follows [TITLE_PROMPT] — that issanitize’s job. - Never panics.