Skip to content
Merged
Show file tree
Hide file tree
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
6 changes: 2 additions & 4 deletions crates/goose/src/conversation/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,8 @@ impl MessageContent {
.cloned()
.collect();

if filtered_content.is_empty() {
return None;
}

// Preserve ToolResponse even when content is empty - some providers
// (like Google) need to handle empty tool responses specially
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without the deleted code, this comment seems a little out of place, you could just drop it

Some(MessageContent::ToolResponse(ToolResponse {
id: res.id.clone(),
tool_result: Ok(CallToolResult {
Expand Down
5 changes: 3 additions & 2 deletions crates/goose/src/providers/chatgpt_codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ fn build_input_items(messages: &[Message]) -> Result<Vec<Value>> {
let mut items = Vec::new();

for message in messages.iter().filter(|m| m.is_agent_visible()) {
let role = match message.role {
let filtered = message.agent_visible_content();
let role = match filtered.role {
Role::User => Some("user"),
Role::Assistant => Some("assistant"),
};
Expand All @@ -95,7 +96,7 @@ fn build_input_items(messages: &[Message]) -> Result<Vec<Value>> {
}
};

for content in &message.content {
for content in &filtered.content {
match content {
MessageContent::Text(text) => {
if !text.text.is_empty() {
Expand Down
5 changes: 3 additions & 2 deletions crates/goose/src/providers/cursor_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ impl CursorAgentProvider {

// Add conversation history
for message in messages.iter().filter(|m| m.is_agent_visible()) {
let role_prefix = match message.role {
let filtered = message.agent_visible_content();
let role_prefix = match filtered.role {
Role::User => "Human: ",
Role::Assistant => "Assistant: ",
};
full_prompt.push_str(role_prefix);

for content in &message.content {
for content in &filtered.content {
match content {
MessageContent::Text(text_content) => {
full_prompt.push_str(&text_content.text);
Expand Down
8 changes: 7 additions & 1 deletion crates/goose/src/providers/formats/anthropic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ const DATA_FIELD: &str = "data";
pub fn format_messages(messages: &[Message]) -> Vec<Value> {
let mut anthropic_messages = Vec::new();

for message in messages.iter().filter(|m| m.is_agent_visible()) {
let filtered_messages: Vec<Message> = messages
.iter()
.filter(|m| m.is_agent_visible())
.map(|m| m.agent_visible_content())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe agent_visible_content should return an Option<Message> (None if is_agent_visible would return false) and you could .filter_map these

.collect();

for message in &filtered_messages {
let role = match message.role {
Role::User => USER_ROLE,
Role::Assistant => ASSISTANT_ROLE,
Expand Down
11 changes: 4 additions & 7 deletions crates/goose/src/providers/formats/bedrock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ use super::super::base::Usage;
use crate::conversation::message::{Message, MessageContent};

pub fn to_bedrock_message(message: &Message) -> Result<bedrock::Message> {
let filtered = message.agent_visible_content();

bedrock::Message::builder()
.role(to_bedrock_role(&message.role))
.role(to_bedrock_role(&filtered.role))
.set_content(Some(
message
filtered
.content
.iter()
.map(to_bedrock_message_content)
Expand Down Expand Up @@ -90,11 +92,6 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result<bedrock::C
result
.content
.iter()
// Filter out content items that have User in their audience
.filter(|c| {
c.audience()
.is_none_or(|audience| !audience.contains(&Role::User))
})
.map(|c| to_bedrock_tool_result_content_block(&tool_res.id, c.clone()))
.collect::<Result<_>>()?,
),
Expand Down
12 changes: 4 additions & 8 deletions crates/goose/src/providers/formats/databricks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,7 @@ fn format_tool_response(

match &response.tool_result {
Ok(call_result) => {
let abridged: Vec<_> = call_result
.content
.iter()
.filter(|c| c.audience().is_none_or(|a| a.contains(&Role::Assistant)))
.map(|c| c.raw.clone())
.collect();
let abridged: Vec<_> = call_result.content.iter().map(|c| c.raw.clone()).collect();

let mut tool_content = Vec::new();
let mut image_messages = Vec::new();
Expand Down Expand Up @@ -113,9 +108,10 @@ fn format_tool_response(
fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<DatabricksMessage> {
let mut result = Vec::new();
for message in messages.iter().filter(|m| m.is_agent_visible()) {
let filtered = message.agent_visible_content();
let mut converted = DatabricksMessage {
content: Value::Null,
role: match message.role {
role: match filtered.role {
Role::User => "user".to_string(),
Role::Assistant => "assistant".to_string(),
},
Expand All @@ -127,7 +123,7 @@ fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<Data
let mut has_tool_calls = false;
let mut has_multiple_content = false;

for content in &message.content {
for content in &filtered.content {
match content {
MessageContent::Text(text) => {
if !text.text.is_empty() {
Expand Down
17 changes: 4 additions & 13 deletions crates/goose/src/providers/formats/openai.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,16 @@ struct StreamingChunk {
pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<Value> {
let mut messages_spec = Vec::new();
for message in messages.iter().filter(|m| m.is_agent_visible()) {
let filtered = message.agent_visible_content();
let mut converted = json!({
"role": message.role
"role": filtered.role
});

let mut output = Vec::new();
let mut content_array = Vec::new();
let mut text_array = Vec::new();

for content in &message.content {
for content in &filtered.content {
match content {
MessageContent::Text(text) => {
if !text.text.is_empty() {
Expand Down Expand Up @@ -131,17 +132,7 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
MessageContent::ToolResponse(response) => {
match &response.tool_result {
Ok(result) => {
// Send only contents with no audience or with Assistant in the audience
let abridged: Vec<_> = result
.content
.iter()
.filter(|content| {
content
.audience()
.is_none_or(|audience| audience.contains(&Role::Assistant))
})
.cloned()
.collect();
let abridged: Vec<_> = result.content.to_vec();

// Process all content, replacing images with placeholder text
let mut tool_content = Vec::new();
Expand Down
3 changes: 2 additions & 1 deletion crates/goose/src/providers/formats/openai_responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ fn add_function_calls(input_items: &mut Vec<Value>, messages: &[Message]) {

fn add_function_call_outputs(input_items: &mut Vec<Value>, messages: &[Message]) {
for message in messages.iter().filter(|m| m.is_agent_visible()) {
for content in &message.content {
let filtered = message.agent_visible_content();
for content in &filtered.content {
if let MessageContent::ToolResponse(response) = content {
match &response.tool_result {
Ok(contents) => {
Expand Down
9 changes: 7 additions & 2 deletions crates/goose/src/providers/formats/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ use std::collections::HashSet;
pub fn format_messages(messages: &[Message]) -> Vec<Value> {
let mut snowflake_messages = Vec::new();

// Convert messages to Snowflake format
for message in messages.iter().filter(|m| m.is_agent_visible()) {
let filtered_messages: Vec<Message> = messages
.iter()
.filter(|m| m.is_agent_visible())
.map(|m| m.agent_visible_content())
.collect();

for message in &filtered_messages {
let role = match message.role {
Role::User => "user",
Role::Assistant => "assistant",
Expand Down
7 changes: 4 additions & 3 deletions crates/goose/src/providers/toolshim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,10 +321,11 @@ pub fn convert_tool_messages_to_text(messages: &[Message]) -> Conversation {
let converted_messages: Vec<Message> = messages
.iter()
.map(|message| {
let filtered = message.agent_visible_content();
let mut new_content = Vec::new();
let mut has_tool_content = false;

for content in &message.content {
for content in &filtered.content {
match content {
MessageContent::ToolRequest(req) => {
has_tool_content = true;
Expand Down Expand Up @@ -369,9 +370,9 @@ pub fn convert_tool_messages_to_text(messages: &[Message]) -> Conversation {
}

if has_tool_content {
Message::new(message.role.clone(), message.created, new_content)
Message::new(filtered.role.clone(), filtered.created, new_content)
} else {
message.clone()
filtered
}
})
.collect();
Expand Down
Loading