mirror of
https://github.com/RightNow-AI/openfang.git
synced 2026-07-30 06:32:17 +00:00
bug fixes
This commit is contained in:
@@ -1085,7 +1085,11 @@ impl OpenFangKernel {
|
||||
|| disk_manifest.model.model
|
||||
!= entry.manifest.model.model
|
||||
|| disk_manifest.capabilities.tools
|
||||
!= entry.manifest.capabilities.tools;
|
||||
!= entry.manifest.capabilities.tools
|
||||
|| disk_manifest.tool_allowlist
|
||||
!= entry.manifest.tool_allowlist
|
||||
|| disk_manifest.tool_blocklist
|
||||
!= entry.manifest.tool_blocklist;
|
||||
if changed {
|
||||
info!(
|
||||
agent = %name,
|
||||
@@ -4738,6 +4742,17 @@ impl OpenFangKernel {
|
||||
McpTransportEntry::Sse { url } => McpTransport::Sse { url: url.clone() },
|
||||
};
|
||||
|
||||
// Resolve env vars from vault/dotenv before passing to MCP subprocess.
|
||||
// The MCP spawn calls env_clear() then re-adds only whitelisted vars
|
||||
// from std::env — so we must ensure they're in std::env first.
|
||||
for var_name in &server_config.env {
|
||||
if std::env::var(var_name).is_err() {
|
||||
if let Some(val) = self.resolve_credential(var_name) {
|
||||
std::env::set_var(var_name, &val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mcp_config = McpServerConfig {
|
||||
name: server_config.name.clone(),
|
||||
transport,
|
||||
|
||||
@@ -244,7 +244,9 @@ pub async fn run_agent_loop(
|
||||
}
|
||||
|
||||
// Build the messages for the LLM, filtering system messages
|
||||
// System prompt goes into the separate `system` field
|
||||
// System prompt goes into the separate `system` field.
|
||||
// NOTE: We build llm_messages BEFORE stripping images so the LLM
|
||||
// sees the full image data for the current turn.
|
||||
let llm_messages: Vec<Message> = session
|
||||
.messages
|
||||
.iter()
|
||||
@@ -252,6 +254,23 @@ pub async fn run_agent_loop(
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// Strip Image blocks from session to prevent base64 bloat.
|
||||
// The LLM already received them via llm_messages above.
|
||||
for msg in session.messages.iter_mut() {
|
||||
if let MessageContent::Blocks(blocks) = &mut msg.content {
|
||||
let had_images = blocks.iter().any(|b| matches!(b, ContentBlock::Image { .. }));
|
||||
if had_images {
|
||||
blocks.retain(|b| !matches!(b, ContentBlock::Image { .. }));
|
||||
if blocks.is_empty() {
|
||||
blocks.push(ContentBlock::Text {
|
||||
text: "[Image processed]".to_string(),
|
||||
provider_metadata: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and repair session history (drop orphans, merge consecutive)
|
||||
let mut messages = crate::session_repair::validate_and_repair(&llm_messages);
|
||||
|
||||
@@ -421,7 +440,7 @@ pub async fn run_agent_loop(
|
||||
// One-shot retry: if the LLM returns empty text with no tool use,
|
||||
// try once more before accepting the empty result.
|
||||
// Triggers on first call OR when input_tokens=0 (silently failed request).
|
||||
if text.trim().is_empty() && response.tool_calls.is_empty() {
|
||||
if text.trim().is_empty() && response.tool_calls.is_empty() && !response.has_any_content() {
|
||||
let is_silent_failure =
|
||||
response.usage.input_tokens == 0 && response.usage.output_tokens == 0;
|
||||
if iteration == 0 || is_silent_failure {
|
||||
@@ -1221,6 +1240,23 @@ pub async fn run_agent_loop_streaming(
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// Strip Image blocks from session to prevent base64 bloat.
|
||||
// The LLM already received them via llm_messages above.
|
||||
for msg in session.messages.iter_mut() {
|
||||
if let MessageContent::Blocks(blocks) = &mut msg.content {
|
||||
let had_images = blocks.iter().any(|b| matches!(b, ContentBlock::Image { .. }));
|
||||
if had_images {
|
||||
blocks.retain(|b| !matches!(b, ContentBlock::Image { .. }));
|
||||
if blocks.is_empty() {
|
||||
blocks.push(ContentBlock::Text {
|
||||
text: "[Image processed]".to_string(),
|
||||
provider_metadata: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate and repair session history (drop orphans, merge consecutive)
|
||||
let mut messages = crate::session_repair::validate_and_repair(&llm_messages);
|
||||
|
||||
@@ -1408,7 +1444,7 @@ pub async fn run_agent_loop_streaming(
|
||||
// One-shot retry: if the LLM returns empty text with no tool use,
|
||||
// try once more before accepting the empty result.
|
||||
// Triggers on first call OR when input_tokens=0 (silently failed request).
|
||||
if text.trim().is_empty() && response.tool_calls.is_empty() {
|
||||
if text.trim().is_empty() && response.tool_calls.is_empty() && !response.has_any_content() {
|
||||
let is_silent_failure =
|
||||
response.usage.input_tokens == 0 && response.usage.output_tokens == 0;
|
||||
if iteration == 0 || is_silent_failure {
|
||||
|
||||
@@ -93,6 +93,17 @@ impl CompletionResponse {
|
||||
.collect::<Vec<_>>()
|
||||
.join("")
|
||||
}
|
||||
|
||||
/// Check if the response has any meaningful content (including Thinking blocks).
|
||||
/// Used to distinguish true empty responses from thinking-only responses.
|
||||
pub fn has_any_content(&self) -> bool {
|
||||
self.content.iter().any(|block| match block {
|
||||
ContentBlock::Text { text, .. } => !text.is_empty(),
|
||||
ContentBlock::Thinking { thinking, .. } => !thinking.is_empty(),
|
||||
ContentBlock::ToolUse { .. } | ContentBlock::Image { .. } => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Events emitted during streaming LLM completion.
|
||||
|
||||
Reference in New Issue
Block a user