Skip to content

Commit 0b59205

Browse files
authored
Merge pull request #881 from pbranchu/fix/token-estimation-tooluse
Fix token estimation: include ToolUse arguments in text_length
2 parents 3f8ceab + 06d0479 commit 0b59205

File tree

9 files changed

+53
-35
lines changed

9 files changed

+53
-35
lines changed

crates/openfang-api/src/channel_bridge.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ use openfang_channels::discourse::DiscourseAdapter;
4949
use openfang_channels::gitter::GitterAdapter;
5050
use openfang_channels::gotify::GotifyAdapter;
5151
use openfang_channels::linkedin::LinkedInAdapter;
52-
use openfang_channels::mumble::MumbleAdapter;
5352
use openfang_channels::mqtt::MqttAdapter;
53+
use openfang_channels::mumble::MumbleAdapter;
5454
use openfang_channels::ntfy::NtfyAdapter;
5555
use openfang_channels::webhook::WebhookAdapter;
5656
use openfang_channels::wecom::WeComAdapter;

crates/openfang-api/src/routes.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,8 @@ pub async fn get_agent_session(
579579
msg.get_mut("tools").and_then(|v| v.as_array_mut())
580580
{
581581
if let Some(tool_obj) = tools_arr.get_mut(tool_idx) {
582-
tool_obj["result"] = serde_json::Value::String(result.clone());
582+
tool_obj["result"] =
583+
serde_json::Value::String(result.clone());
583584
tool_obj["is_error"] =
584585
serde_json::Value::Bool(*is_error);
585586
}

crates/openfang-channels/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ pub mod discourse;
4848
pub mod gitter;
4949
pub mod gotify;
5050
pub mod linkedin;
51-
pub mod mumble;
5251
pub mod mqtt;
52+
pub mod mumble;
5353
pub mod ntfy;
5454
pub mod webhook;
5555
pub mod wecom;

crates/openfang-channels/src/line.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl LineAdapter {
108108
diff |= a ^ b;
109109
}
110110
if diff != 0 {
111-
let computed = base64::engine::general_purpose::STANDARD.encode(&result);
111+
let computed = base64::engine::general_purpose::STANDARD.encode(result);
112112
// Log first/last 4 chars of each signature for debugging without leaking full HMAC
113113
let comp_redacted = format!(
114114
"{}...{}",
@@ -381,8 +381,7 @@ impl ChannelAdapter for LineAdapter {
381381
axum::routing::post({
382382
let secret = Arc::clone(&channel_secret);
383383
let tx = Arc::clone(&tx);
384-
move |headers: axum::http::HeaderMap,
385-
body: axum::body::Bytes| {
384+
move |headers: axum::http::HeaderMap, body: axum::body::Bytes| {
386385
let secret = Arc::clone(&secret);
387386
let tx = Arc::clone(&tx);
388387
async move {
@@ -404,8 +403,7 @@ impl ChannelAdapter for LineAdapter {
404403
shutdown_rx: watch::channel(false).1,
405404
};
406405

407-
if !signature.is_empty()
408-
&& !adapter.verify_signature(&body, signature)
406+
if !signature.is_empty() && !adapter.verify_signature(&body, signature)
409407
{
410408
warn!("LINE: invalid webhook signature");
411409
return axum::http::StatusCode::UNAUTHORIZED;

crates/openfang-channels/src/mqtt.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ impl MqttAdapter {
152152
}
153153

154154
/// Parse host:port string.
155-
fn parse_host_port(s: &str, default_port: u16) -> Result<(String, u16), Box<dyn std::error::Error>> {
155+
fn parse_host_port(
156+
s: &str,
157+
default_port: u16,
158+
) -> Result<(String, u16), Box<dyn std::error::Error>> {
156159
let s = s.trim();
157160
if let Some(colon_pos) = s.rfind(':') {
158161
let host = s[..colon_pos].to_string();
@@ -239,7 +242,8 @@ impl ChannelAdapter for MqttAdapter {
239242

240243
async fn start(
241244
&self,
242-
) -> Result<Pin<Box<dyn Stream<Item = ChannelMessage> + Send>>, Box<dyn std::error::Error>> {
245+
) -> Result<Pin<Box<dyn Stream<Item = ChannelMessage> + Send>>, Box<dyn std::error::Error>>
246+
{
243247
let options = self.build_mqtt_options()?;
244248
let (client, mut eventloop) = AsyncClient::new(options, 10);
245249

crates/openfang-kernel/src/kernel.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,20 +2930,16 @@ impl OpenFangKernel {
29302930
model: &str,
29312931
explicit_provider: Option<&str>,
29322932
) -> KernelResult<()> {
2933-
let catalog_entry = self
2934-
.model_catalog
2935-
.read()
2936-
.ok()
2937-
.and_then(|catalog| {
2938-
// When the caller specifies a provider, use provider-aware lookup
2939-
// so we resolve the model on the correct provider — not a builtin
2940-
// from a different provider that happens to share the same name (#833).
2941-
if let Some(ep) = explicit_provider {
2942-
catalog.find_model_for_provider(model, ep).cloned()
2943-
} else {
2944-
catalog.find_model(model).cloned()
2945-
}
2946-
});
2933+
let catalog_entry = self.model_catalog.read().ok().and_then(|catalog| {
2934+
// When the caller specifies a provider, use provider-aware lookup
2935+
// so we resolve the model on the correct provider — not a builtin
2936+
// from a different provider that happens to share the same name (#833).
2937+
if let Some(ep) = explicit_provider {
2938+
catalog.find_model_for_provider(model, ep).cloned()
2939+
} else {
2940+
catalog.find_model(model).cloned()
2941+
}
2942+
});
29472943
let provider = if let Some(ep) = explicit_provider {
29482944
// User explicitly set the provider — use it as-is
29492945
Some(ep.to_string())

crates/openfang-runtime/src/compactor.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,7 +1478,10 @@ mod tests {
14781478
Message::assistant("Done reading."),
14791479
];
14801480
let adjusted = adjust_split_for_tool_pairs(&messages, 2);
1481-
assert_eq!(adjusted, 1, "Should pull back split to keep ToolUse + ToolResult together");
1481+
assert_eq!(
1482+
adjusted, 1,
1483+
"Should pull back split to keep ToolUse + ToolResult together"
1484+
);
14821485
}
14831486

14841487
#[test]
@@ -1489,7 +1492,10 @@ mod tests {
14891492
Message::user("c"),
14901493
];
14911494
let adjusted = adjust_split_for_tool_pairs(&messages, 1);
1492-
assert_eq!(adjusted, 1, "Should not change split for plain text messages");
1495+
assert_eq!(
1496+
adjusted, 1,
1497+
"Should not change split for plain text messages"
1498+
);
14931499
}
14941500

14951501
#[test]

crates/openfang-runtime/src/context_overflow.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ fn safe_drain_boundary(messages: &[Message], mut boundary: usize) -> usize {
3232
// is in the last drained message (boundary - 1). Pull boundary back by 1.
3333
if messages[boundary].role == Role::User {
3434
if let MessageContent::Blocks(blocks) = &messages[boundary].content {
35-
let has_tool_result = blocks.iter().any(|b| matches!(b, ContentBlock::ToolResult { .. }));
35+
let has_tool_result = blocks
36+
.iter()
37+
.any(|b| matches!(b, ContentBlock::ToolResult { .. }));
3638
if has_tool_result && boundary > 0 && messages[boundary - 1].role == Role::Assistant {
3739
if let MessageContent::Blocks(asst_blocks) = &messages[boundary - 1].content {
38-
let has_tool_use = asst_blocks.iter().any(|b| matches!(b, ContentBlock::ToolUse { .. }));
40+
let has_tool_use = asst_blocks
41+
.iter()
42+
.any(|b| matches!(b, ContentBlock::ToolUse { .. }));
3943
if has_tool_use {
4044
boundary -= 1;
4145
debug!(
@@ -135,7 +139,8 @@ pub fn recover_from_overflow(
135139
debug!(
136140
estimated_tokens = estimated,
137141
removing = remove,
138-
"Stage 1: moderate trim to last {} messages", messages.len() - remove
142+
"Stage 1: moderate trim to last {} messages",
143+
messages.len() - remove
139144
);
140145
messages.drain(..remove);
141146
// Re-check after trim
@@ -156,7 +161,8 @@ pub fn recover_from_overflow(
156161
warn!(
157162
estimated_tokens = estimate_tokens(messages, system_prompt, tools),
158163
removing = remove,
159-
"Stage 2: aggressive overflow compaction to last {} messages", messages.len() - remove
164+
"Stage 2: aggressive overflow compaction to last {} messages",
165+
messages.len() - remove
160166
);
161167
let summary = Message::user(format!(
162168
"[System: {} earlier messages were removed due to context overflow. \
@@ -373,7 +379,10 @@ mod tests {
373379
];
374380
// Boundary 2 would cut between the assistant(ToolUse) at [1] and user(ToolResult) at [2].
375381
let adjusted = safe_drain_boundary(&msgs, 2);
376-
assert_eq!(adjusted, 1, "Should pull boundary back to keep the ToolUse/ToolResult pair together");
382+
assert_eq!(
383+
adjusted, 1,
384+
"Should pull boundary back to keep the ToolUse/ToolResult pair together"
385+
);
377386
}
378387

379388
#[test]
@@ -385,7 +394,10 @@ mod tests {
385394
Message::assistant("d"),
386395
];
387396
let adjusted = safe_drain_boundary(&msgs, 2);
388-
assert_eq!(adjusted, 2, "Should not change boundary for plain text messages");
397+
assert_eq!(
398+
adjusted, 2,
399+
"Should not change boundary for plain text messages"
400+
);
389401
}
390402

391403
#[test]

crates/openfang-types/src/message.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,10 @@ impl MessageContent {
142142
ContentBlock::Text { text, .. } => text.len(),
143143
ContentBlock::ToolResult { content, .. } => content.len(),
144144
ContentBlock::Thinking { thinking } => thinking.len(),
145-
ContentBlock::ToolUse { .. }
146-
| ContentBlock::Image { .. }
147-
| ContentBlock::Unknown => 0,
145+
ContentBlock::ToolUse { name, input, .. } => {
146+
name.len() + input.to_string().len()
147+
}
148+
ContentBlock::Image { .. } | ContentBlock::Unknown => 0,
148149
})
149150
.sum(),
150151
}

0 commit comments

Comments
 (0)