Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions crates/openplotva-llm/src/aifarm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5611,17 +5611,16 @@ const GENERATION_TOOL_NAMES: &[&str] = &["draw_image", "draw_api", "generate_son
/// Review a final reply against the bot's latest replies in `history` and the current
/// message: its reaction opener, read-back quotes and an unstarted generation promise are
/// cut; an opening still copied from a recent reply, or a reply that is only a promise,
/// is re-sampled once per turn. Custom personas keep their own form.
/// is re-sampled once per turn. A custom persona may keep a fixed opening of its own, so
/// it is never re-sampled for one.
fn review_reply_shape(
input: &DialogInput,
history: &[HistoryMessage],
transcript: &[SessionMessage],
text: String,
generation_offered: bool,
) -> Result<String, AifarmDialogError> {
if !input.persona.custom_persona.trim().is_empty() {
return Ok(text);
}
let custom_persona = !input.persona.custom_persona.trim().is_empty();
let recent_replies: Vec<&str> = history
.iter()
.rev()
Expand All @@ -5636,7 +5635,7 @@ fn review_reply_shape(
recent_replies: &recent_replies,
user_text: &input.message.text,
generation_pending: generation_offered && !generation_started(transcript),
resample_allowed: input.resample_verdict.trim().is_empty(),
resample_allowed: !custom_persona && input.resample_verdict.trim().is_empty(),
};
match reply_shape::review_reply(&text, &context) {
reply_shape::ReplyShape::Keep => Ok(text),
Expand Down Expand Up @@ -10940,6 +10939,47 @@ mod tests {
Ok(())
}

#[tokio::test]
async fn chat_step_trims_but_never_resamples_a_custom_persona_reply()
-> Result<(), CompletionError> {
for (reply, delivered) in [
(
"Ха-ха, ну ты и гурман! 😂 Смотрю: это сильный вкус.",
"Смотрю: это сильный вкус.",
),
(
"(я чувствую, как дождь стучит по крыше)",
"(я чувствую, как дождь стучит по крыше)",
),
] {
let (provider, _, _) = direct_dialog_provider(
json!({"choices": [{"message": {"role": "assistant", "content": reply}}]}),
AifarmDialogConfig::default(),
);
let mut input = base_input();
input.persona.custom_persona = "Ученица и помощник".to_owned();
input
.history
.push(bot_history_reply(10, "(я чувствую, как ветер шумит)"));
let output = crate::ChatStepProvider::run_chat_step(
&provider,
ChatStepRequest {
required_tool: None,
preferred_target: None,
input,
transcript: Vec::new(),
tools: ToolsMode::Native(
native_tool_values(&[STEP_WEB_SEARCH]).expect("tool defs"),
),
iteration: 1,
},
)
.await?;
assert_eq!(output.text, delivered);
}
Ok(())
}

#[tokio::test]
async fn chat_step_resamples_a_generation_announced_without_a_call() {
let reply = "Рисую твою радужную пещеру! 🎨";
Expand Down
Loading