feat(graph): treat llm max_iterations: 0 as no turn cap
This commit is contained in:
1 file changed
+32
-14
+32
-14
@@ -257,6 +257,11 @@ async fn run_with_retries(
|
|||||||
Err(last_err.unwrap_or_else(|| anyhow!("llm node exhausted retries")))
|
Err(last_err.unwrap_or_else(|| anyhow!("llm node exhausted retries")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `turn` (0-based) is the final turn the node's cap allows. A cap of 0 means no cap.
|
||||||
|
pub(crate) fn is_last_turn(turn: u32, max_iterations: u32) -> bool {
|
||||||
|
max_iterations > 0 && turn + 1 == max_iterations
|
||||||
|
}
|
||||||
|
|
||||||
async fn run_chat_loop(node: &LlmNode, prompt: &str, ctx: &mut RequestContext) -> Result<String> {
|
async fn run_chat_loop(node: &LlmNode, prompt: &str, ctx: &mut RequestContext) -> Result<String> {
|
||||||
let abort = create_abort_signal();
|
let abort = create_abort_signal();
|
||||||
let app_cfg = Arc::clone(&ctx.app.config);
|
let app_cfg = Arc::clone(&ctx.app.config);
|
||||||
@@ -264,7 +269,8 @@ async fn run_chat_loop(node: &LlmNode, prompt: &str, ctx: &mut RequestContext) -
|
|||||||
let mut input = Input::from_str(ctx, prompt, role_for_input)?;
|
let mut input = Input::from_str(ctx, prompt, role_for_input)?;
|
||||||
let mut accumulated = String::new();
|
let mut accumulated = String::new();
|
||||||
|
|
||||||
for turn in 0..node.max_iterations {
|
let mut turn: u32 = 0;
|
||||||
|
loop {
|
||||||
let client = input.create_client()?;
|
let client = input.create_client()?;
|
||||||
ctx.before_chat_completion(&input)?;
|
ctx.before_chat_completion(&input)?;
|
||||||
let (output, tool_results) =
|
let (output, tool_results) =
|
||||||
@@ -291,7 +297,7 @@ async fn run_chat_loop(node: &LlmNode, prompt: &str, ctx: &mut RequestContext) -
|
|||||||
return Ok(accumulated);
|
return Ok(accumulated);
|
||||||
}
|
}
|
||||||
GuardrailAction::Inject(prompt) => {
|
GuardrailAction::Inject(prompt) => {
|
||||||
if turn + 1 == node.max_iterations {
|
if is_last_turn(turn, node.max_iterations) {
|
||||||
bail!(
|
bail!(
|
||||||
"llm node hit max_iterations ({}) before LLM concluded",
|
"llm node hit max_iterations ({}) before LLM concluded",
|
||||||
node.max_iterations
|
node.max_iterations
|
||||||
@@ -299,22 +305,19 @@ async fn run_chat_loop(node: &LlmNode, prompt: &str, ctx: &mut RequestContext) -
|
|||||||
}
|
}
|
||||||
let role = ctx.role.clone();
|
let role = ctx.role.clone();
|
||||||
input = Input::from_str(ctx, &prompt, role)?;
|
input = Input::from_str(ctx, &prompt, role)?;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if is_last_turn(turn, node.max_iterations) {
|
||||||
|
bail!(
|
||||||
|
"llm node hit max_iterations ({}) before LLM concluded",
|
||||||
|
node.max_iterations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
input = input.merge_tool_results(output, tool_results);
|
||||||
}
|
}
|
||||||
|
turn = turn.saturating_add(1);
|
||||||
if turn + 1 == node.max_iterations {
|
|
||||||
bail!(
|
|
||||||
"llm node hit max_iterations ({}) before LLM concluded",
|
|
||||||
node.max_iterations
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
input = input.merge_tool_results(output, tool_results);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bail!("llm node ended without producing output")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_inline_role(
|
fn build_inline_role(
|
||||||
@@ -736,4 +739,19 @@ mod tests {
|
|||||||
Some(Duration::from_secs(5))
|
Some(Duration::from_secs(5))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_last_turn_bounded() {
|
||||||
|
assert!(is_last_turn(0, 1));
|
||||||
|
assert!(!is_last_turn(0, 10));
|
||||||
|
assert!(is_last_turn(9, 10));
|
||||||
|
assert!(!is_last_turn(10, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_last_turn_zero_cap_never_fires() {
|
||||||
|
assert!(!is_last_turn(0, 0));
|
||||||
|
assert!(!is_last_turn(9, 0));
|
||||||
|
assert!(!is_last_turn(u32::MAX, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user