steerable-agent-runtime-native 0.6.32__tar.gz → 0.6.33__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/Cargo.lock +1 -1
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/Cargo.toml +1 -1
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/PKG-INFO +1 -1
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/pyproject.toml +1 -1
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/engine.rs +76 -52
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/hooks.rs +23 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/lib.rs +2 -1
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/python.rs +32 -1
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_loop.rs +87 -4
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/.gitignore +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/README.md +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/anthropic_http.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/anthropic_wire.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/budget.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/compat.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/errors.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/executor.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/gemini_http.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/gemini_wire.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/json_py.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/model_catalog.json +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/model_resolve.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/openai_http.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/openai_wire.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/presets.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/pricing.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/provider.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/ptc_js.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/responses_http.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/responses_wire.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/run_code.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/skills.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/sse_http.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/todo.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/tokens.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/types.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/web.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/web_html.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/web_search.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/wire.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/write_lease.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_llm_wire.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_ptc_js.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_run_code.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_todo.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_web.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_write_lease.rs +0 -0
- {steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/uv.lock +0 -0
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/engine.rs
RENAMED
|
@@ -387,60 +387,79 @@ impl<P: LLMProvider, E: ToolExecutor, H: LoopHooks> CoreLoop<P, E, H> {
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
let step_tool_choice
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
));
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
PreStepOutcome::Proceed {
|
|
407
|
-
appends,
|
|
408
|
-
rewrite,
|
|
409
|
-
reason,
|
|
410
|
-
action,
|
|
411
|
-
pre_tokens,
|
|
412
|
-
post_tokens,
|
|
413
|
-
tool_choice,
|
|
414
|
-
} => {
|
|
415
|
-
if let Some(replacement) = rewrite {
|
|
416
|
-
history = replacement;
|
|
417
|
-
emit(LoopEvent::new(
|
|
418
|
-
"hook_action",
|
|
419
|
-
json!({
|
|
420
|
-
"hook": "pre_step",
|
|
421
|
-
"action": action.clone(),
|
|
422
|
-
"reason": reason.clone(),
|
|
423
|
-
"round": round_index,
|
|
424
|
-
"pre_tokens": pre_tokens,
|
|
425
|
-
"post_tokens": post_tokens,
|
|
426
|
-
}),
|
|
390
|
+
let (step_tool_choice, step_tool_choice_honored) =
|
|
391
|
+
match self.hooks.pre_step(&history).await {
|
|
392
|
+
PreStepOutcome::Reject { reason } => {
|
|
393
|
+
emit(completion_event(
|
|
394
|
+
round_index,
|
|
395
|
+
"stop",
|
|
396
|
+
"",
|
|
397
|
+
&[],
|
|
398
|
+
ctx.consecutive_tool_errors,
|
|
399
|
+
"failed",
|
|
400
|
+
reason,
|
|
401
|
+
1.0,
|
|
402
|
+
&ctx,
|
|
427
403
|
));
|
|
404
|
+
break;
|
|
428
405
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
406
|
+
PreStepOutcome::Proceed {
|
|
407
|
+
appends,
|
|
408
|
+
rewrite,
|
|
409
|
+
reason,
|
|
410
|
+
action,
|
|
411
|
+
pre_tokens,
|
|
412
|
+
post_tokens,
|
|
413
|
+
tool_choice,
|
|
414
|
+
tool_choice_honored,
|
|
415
|
+
notes,
|
|
416
|
+
} => {
|
|
417
|
+
if let Some(replacement) = rewrite {
|
|
418
|
+
history = replacement;
|
|
419
|
+
emit(LoopEvent::new(
|
|
420
|
+
"hook_action",
|
|
421
|
+
json!({
|
|
422
|
+
"hook": "pre_step",
|
|
423
|
+
"action": action.clone(),
|
|
424
|
+
"reason": reason.clone(),
|
|
425
|
+
"round": round_index,
|
|
426
|
+
"pre_tokens": pre_tokens,
|
|
427
|
+
"post_tokens": post_tokens,
|
|
428
|
+
}),
|
|
429
|
+
));
|
|
430
|
+
}
|
|
431
|
+
for message in appends {
|
|
432
|
+
history.push(message);
|
|
433
|
+
emit(LoopEvent::new(
|
|
434
|
+
"hook_action",
|
|
435
|
+
json!({
|
|
436
|
+
"hook": "pre_step",
|
|
437
|
+
"action": action.clone(),
|
|
438
|
+
"reason": reason.clone(),
|
|
439
|
+
"round": round_index,
|
|
440
|
+
}),
|
|
441
|
+
));
|
|
442
|
+
}
|
|
443
|
+
// Verdicts that took no action. Without these a trace
|
|
444
|
+
// shows only the rounds a hook intervened in, which is a
|
|
445
|
+
// numerator with no denominator — "routing allowed a
|
|
446
|
+
// no-tool turn" and "routing never ran" look identical.
|
|
447
|
+
for note in notes {
|
|
448
|
+
emit(LoopEvent::new(
|
|
449
|
+
"hook_action",
|
|
450
|
+
json!({
|
|
451
|
+
"hook": "pre_step",
|
|
452
|
+
"action": note.action,
|
|
453
|
+
"reason": note.reason,
|
|
454
|
+
"value": note.value,
|
|
455
|
+
"probability": note.probability,
|
|
456
|
+
"round": round_index,
|
|
457
|
+
}),
|
|
458
|
+
));
|
|
459
|
+
}
|
|
460
|
+
(tool_choice, tool_choice_honored)
|
|
440
461
|
}
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
}
|
|
462
|
+
};
|
|
444
463
|
|
|
445
464
|
if let Some(tool_choice) = &step_tool_choice {
|
|
446
465
|
emit(LoopEvent::new(
|
|
@@ -448,7 +467,12 @@ impl<P: LLMProvider, E: ToolExecutor, H: LoopHooks> CoreLoop<P, E, H> {
|
|
|
448
467
|
json!({
|
|
449
468
|
"hook": "pre_step",
|
|
450
469
|
"action": "tool_choice",
|
|
451
|
-
"
|
|
470
|
+
"value": tool_choice,
|
|
471
|
+
// Several vendors downgrade `required` to `auto` on the
|
|
472
|
+
// wire. Logging the hook without this reads as a gate
|
|
473
|
+
// that held, so measurements over forced turns
|
|
474
|
+
// silently mix the models where the gate is a no-op.
|
|
475
|
+
"honored": step_tool_choice_honored.unwrap_or(true),
|
|
452
476
|
"round": round_index,
|
|
453
477
|
}),
|
|
454
478
|
));
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/hooks.rs
RENAMED
|
@@ -21,6 +21,22 @@ pub enum HookAction {
|
|
|
21
21
|
Narrate { message: String, reason: String },
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/// A hook verdict the engine records even though it changed nothing about the
|
|
25
|
+
/// step.
|
|
26
|
+
///
|
|
27
|
+
/// Verdicts that act are already visible as `hook_action` (a rewrite, an
|
|
28
|
+
/// append, a `tool_choice`). The verdict that declined to act leaves no trace,
|
|
29
|
+
/// so a trajectory cannot distinguish "the data-need router allowed a no-tool
|
|
30
|
+
/// turn" from "routing never ran" — the denominator a routing-variance
|
|
31
|
+
/// measurement needs.
|
|
32
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
33
|
+
pub struct DecisionNote {
|
|
34
|
+
pub action: String,
|
|
35
|
+
pub reason: String,
|
|
36
|
+
pub value: Option<String>,
|
|
37
|
+
pub probability: Option<f64>,
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
#[derive(Clone, Debug, PartialEq)]
|
|
25
41
|
pub enum PreStepOutcome {
|
|
26
42
|
Proceed {
|
|
@@ -31,6 +47,11 @@ pub enum PreStepOutcome {
|
|
|
31
47
|
pre_tokens: Option<i64>,
|
|
32
48
|
post_tokens: Option<i64>,
|
|
33
49
|
tool_choice: Option<String>,
|
|
50
|
+
/// Whether `tool_choice` survives to the wire, when the hook's host
|
|
51
|
+
/// could answer. `None` means unknown and is recorded as honored:
|
|
52
|
+
/// only the provider adapter knows about vendor downgrades.
|
|
53
|
+
tool_choice_honored: Option<bool>,
|
|
54
|
+
notes: Vec<DecisionNote>,
|
|
34
55
|
},
|
|
35
56
|
Reject {
|
|
36
57
|
reason: String,
|
|
@@ -59,6 +80,8 @@ impl Default for PreStepOutcome {
|
|
|
59
80
|
pre_tokens: None,
|
|
60
81
|
post_tokens: None,
|
|
61
82
|
tool_choice: None,
|
|
83
|
+
tool_choice_honored: None,
|
|
84
|
+
notes: Vec::new(),
|
|
62
85
|
}
|
|
63
86
|
}
|
|
64
87
|
}
|
|
@@ -52,7 +52,8 @@ pub use gemini_wire::{
|
|
|
52
52
|
encode_gemini_contents, parse_gemini_chunk, parse_gemini_usage, GoogleGenAIProvider,
|
|
53
53
|
};
|
|
54
54
|
pub use hooks::{
|
|
55
|
-
CompletionDraft, HookAction, LoopHooks, NoopHooks, PreStepOutcome,
|
|
55
|
+
CompletionDraft, DecisionNote, HookAction, LoopHooks, NoopHooks, PreStepOutcome,
|
|
56
|
+
RequestErrorAction,
|
|
56
57
|
};
|
|
57
58
|
pub use model_resolve::{
|
|
58
59
|
catalog_provider_for_base_url, describe_catalog_providers, resolve_in_catalog,
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/python.rs
RENAMED
|
@@ -10,7 +10,9 @@ use serde_json::Value;
|
|
|
10
10
|
use crate::engine::{CoreLoop, LoopConfig, RoundControl};
|
|
11
11
|
use crate::errors::{LLMError, LLMErrorKind};
|
|
12
12
|
use crate::executor::ToolExecutor;
|
|
13
|
-
use crate::hooks::{
|
|
13
|
+
use crate::hooks::{
|
|
14
|
+
CompletionDraft, DecisionNote, HookAction, LoopHooks, PreStepOutcome, RequestErrorAction,
|
|
15
|
+
};
|
|
14
16
|
use crate::provider::{LLMChunkStream, LLMProvider};
|
|
15
17
|
use crate::types::{LLMMessage, LoopContext, LoopEvent, ToolCall, ToolResult};
|
|
16
18
|
use crate::wire::{
|
|
@@ -25,6 +27,33 @@ struct CallbackProvider {
|
|
|
25
27
|
next_stream_chunk: Py<PyAny>,
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
/// Decode the pre_step payload's `notes`. A malformed entry is skipped rather
|
|
31
|
+
/// than failing the step: these describe a decision that already happened, so
|
|
32
|
+
/// losing one costs a trace line and nothing else.
|
|
33
|
+
fn decision_notes_from_value(raw: Option<&Value>) -> Vec<DecisionNote> {
|
|
34
|
+
let Some(items) = raw.and_then(Value::as_array) else {
|
|
35
|
+
return Vec::new();
|
|
36
|
+
};
|
|
37
|
+
items
|
|
38
|
+
.iter()
|
|
39
|
+
.filter_map(|item| {
|
|
40
|
+
Some(DecisionNote {
|
|
41
|
+
action: item.get("action").and_then(Value::as_str)?.to_string(),
|
|
42
|
+
reason: item
|
|
43
|
+
.get("reason")
|
|
44
|
+
.and_then(Value::as_str)
|
|
45
|
+
.unwrap_or_default()
|
|
46
|
+
.to_string(),
|
|
47
|
+
value: item
|
|
48
|
+
.get("value")
|
|
49
|
+
.and_then(Value::as_str)
|
|
50
|
+
.map(str::to_string),
|
|
51
|
+
probability: item.get("probability").and_then(Value::as_f64),
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
.collect()
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
fn callback_llm_error(value: &Value, default_message: &str) -> LLMError {
|
|
29
58
|
let kind = match value
|
|
30
59
|
.get("error_kind")
|
|
@@ -335,6 +364,8 @@ impl LoopHooks for CallbackHooks {
|
|
|
335
364
|
.get("tool_choice")
|
|
336
365
|
.and_then(Value::as_str)
|
|
337
366
|
.map(str::to_string),
|
|
367
|
+
tool_choice_honored: value.get("tool_choice_honored").and_then(Value::as_bool),
|
|
368
|
+
notes: decision_notes_from_value(value.get("notes")),
|
|
338
369
|
}
|
|
339
370
|
}
|
|
340
371
|
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_loop.rs
RENAMED
|
@@ -4,10 +4,11 @@ use async_trait::async_trait;
|
|
|
4
4
|
use pretty_assertions::assert_eq;
|
|
5
5
|
use serde_json::{json, Value};
|
|
6
6
|
use steerable_agent_runtime::{
|
|
7
|
-
chunk_stream, BudgetLimit, CompletionDraft, ContentPart, CoreLoop,
|
|
8
|
-
LLMError, LLMErrorKind, LLMMessage, LLMProvider, LLMStreamChunk, LLMUsage,
|
|
9
|
-
LoopContext, LoopEvent, LoopHooks,
|
|
10
|
-
ScriptedProvider, ScriptedTurn, ToolCall, ToolExecutor,
|
|
7
|
+
chunk_stream, BudgetLimit, CompletionDraft, ContentPart, CoreLoop, DecisionNote, HookAction,
|
|
8
|
+
LLMChunkStream, LLMError, LLMErrorKind, LLMMessage, LLMProvider, LLMStreamChunk, LLMUsage,
|
|
9
|
+
LoopConfig, LoopContext, LoopEvent, LoopHooks, PreStepOutcome, RequestErrorAction,
|
|
10
|
+
RoundControl, RouterToolExecutor, ScriptedProvider, ScriptedTurn, ToolCall, ToolExecutor,
|
|
11
|
+
ToolResult, ToolRouter,
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
fn provider(scripts: Vec<ScriptedTurn>) -> ScriptedProvider {
|
|
@@ -930,6 +931,88 @@ async fn test_dedup_ignores_argument_key_order_and_carries_guidance() {
|
|
|
930
931
|
.contains("do not repeat the same call"));
|
|
931
932
|
}
|
|
932
933
|
|
|
934
|
+
struct RouteVerdict {
|
|
935
|
+
verdict: &'static str,
|
|
936
|
+
honored: Option<bool>,
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
#[async_trait]
|
|
940
|
+
impl LoopHooks for RouteVerdict {
|
|
941
|
+
async fn pre_step(&self, _transcript: &[LLMMessage]) -> PreStepOutcome {
|
|
942
|
+
let forcing = self.verdict == "require_tool";
|
|
943
|
+
PreStepOutcome::Proceed {
|
|
944
|
+
appends: Vec::new(),
|
|
945
|
+
rewrite: None,
|
|
946
|
+
reason: None,
|
|
947
|
+
action: "append".into(),
|
|
948
|
+
pre_tokens: None,
|
|
949
|
+
post_tokens: None,
|
|
950
|
+
tool_choice: forcing.then(|| "required".to_string()),
|
|
951
|
+
tool_choice_honored: self.honored,
|
|
952
|
+
notes: vec![DecisionNote {
|
|
953
|
+
action: "data_need_route".into(),
|
|
954
|
+
reason: "anti-hallucination data-need routing".into(),
|
|
955
|
+
value: Some(self.verdict.into()),
|
|
956
|
+
probability: None,
|
|
957
|
+
}],
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
#[tokio::test]
|
|
963
|
+
async fn test_pre_step_notes_record_a_verdict_that_changed_nothing() {
|
|
964
|
+
let mut agent = CoreLoop::new(
|
|
965
|
+
provider(vec![text_turn("boiling point is 100C")]),
|
|
966
|
+
RouterToolExecutor::new(ToolRouter::new()),
|
|
967
|
+
)
|
|
968
|
+
.with_hooks(RouteVerdict {
|
|
969
|
+
verdict: "allow_no_tool",
|
|
970
|
+
honored: None,
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
let events = agent
|
|
974
|
+
.run(vec![LLMMessage::text("user", "boiling point?")])
|
|
975
|
+
.await;
|
|
976
|
+
|
|
977
|
+
let note = events
|
|
978
|
+
.iter()
|
|
979
|
+
.find(|event| event.kind == "hook_action" && event.data["action"] == "data_need_route")
|
|
980
|
+
.expect("a verdict that took no action must still be recorded");
|
|
981
|
+
assert_eq!(note.data["value"], json!("allow_no_tool"));
|
|
982
|
+
assert_eq!(note.data["hook"], json!("pre_step"));
|
|
983
|
+
// Nothing was forced, so there is no tool_choice event to confuse it with.
|
|
984
|
+
assert!(!events
|
|
985
|
+
.iter()
|
|
986
|
+
.any(|event| event.kind == "hook_action" && event.data["action"] == "tool_choice"));
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
#[tokio::test]
|
|
990
|
+
async fn test_tool_choice_event_reports_a_vendor_downgrade() {
|
|
991
|
+
let mut router = ToolRouter::new();
|
|
992
|
+
router.register("get_data", |_| async {
|
|
993
|
+
Ok(ToolResult::ok(json!({ "value": 42 })))
|
|
994
|
+
});
|
|
995
|
+
let mut agent = CoreLoop::new(
|
|
996
|
+
provider(vec![text_turn("42")]),
|
|
997
|
+
RouterToolExecutor::new(router),
|
|
998
|
+
)
|
|
999
|
+
.with_hooks(RouteVerdict {
|
|
1000
|
+
verdict: "require_tool",
|
|
1001
|
+
honored: Some(false),
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
let events = agent.run(vec![LLMMessage::text("user", "fetch it")]).await;
|
|
1005
|
+
|
|
1006
|
+
let forced = events
|
|
1007
|
+
.iter()
|
|
1008
|
+
.find(|event| event.kind == "hook_action" && event.data["action"] == "tool_choice")
|
|
1009
|
+
.expect("a forced step must record its gate");
|
|
1010
|
+
assert_eq!(forced.data["value"], json!("required"));
|
|
1011
|
+
// The gate never reached the wire; recording it as honored would make every
|
|
1012
|
+
// measurement over forced turns silently mix in models that ignore it.
|
|
1013
|
+
assert_eq!(forced.data["honored"], json!(false));
|
|
1014
|
+
}
|
|
1015
|
+
|
|
933
1016
|
struct NarrateOnFailure;
|
|
934
1017
|
|
|
935
1018
|
#[async_trait]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/budget.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/compat.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/errors.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/executor.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/gemini_http.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/gemini_wire.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/json_py.rs
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/model_resolve.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/openai_http.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/openai_wire.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/presets.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/pricing.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/provider.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/ptc_js.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/run_code.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/skills.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/sse_http.rs
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/tokens.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/types.rs
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/web_html.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/web_search.rs
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/src/write_lease.rs
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_ptc_js.rs
RENAMED
|
File without changes
|
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_todo.rs
RENAMED
|
File without changes
|
{steerable_agent_runtime_native-0.6.32 → steerable_agent_runtime_native-0.6.33}/tests/test_web.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|