Skip to main content
The Conversation type manages message history, validation, and transformations for LLM interactions.

Overview

Conversation provides:
  • Ordered message storage
  • Automatic message validation and fixing
  • Message visibility filtering (user vs agent)
  • Tool call/response pairing
  • Message deduplication and merging

Conversation Struct

pub struct Conversation(Vec<Message>);
A validated sequence of messages. Source: crates/goose/src/conversation/mod.rs:12

Constructor Methods

new()

Create a validated conversation.
pub fn new<I>(messages: I) -> Result<Self, InvalidConversation>
where
    I: IntoIterator<Item = Message>,
messages
I: IntoIterator<Item = Message>
required
Iterator of messages
Returns: Validated Conversation or error Errors: Returns InvalidConversation if messages violate conversation rules Source: crates/goose/src/conversation/mod.rs:22-27 Example:
let messages = vec![
    Message::user().with_text("Hello"),
    Message::assistant().with_text("Hi there!"),
];
let conversation = Conversation::new(messages)?;

new_unvalidated()

Create an unvalidated conversation.
pub fn new_unvalidated<I>(messages: I) -> Self
where
    I: IntoIterator<Item = Message>,
messages
I: IntoIterator<Item = Message>
required
Iterator of messages
Returns: Unvalidated Conversation Note: Used internally before applying fixes Source: crates/goose/src/conversation/mod.rs:29-34

empty()

Create an empty conversation.
pub fn empty() -> Self
Source: crates/goose/src/conversation/mod.rs:36-38

Message Access

messages()

Get the message list.
pub fn messages(&self) -> &Vec<Message>
Returns: Reference to message vector Source: crates/goose/src/conversation/mod.rs:40-42 Example:
for message in conversation.messages() {
    println!("Role: {:?}, Text: {}", message.role, message.as_concat_text());
}

last()

Get the last message.
pub fn last(&self) -> Option<&Message>
Source: crates/goose/src/conversation/mod.rs:65-67

first()

Get the first message.
pub fn first(&self) -> Option<&Message>
Source: crates/goose/src/conversation/mod.rs:69-71

len()

Get message count.
pub fn len(&self) -> usize
Source: crates/goose/src/conversation/mod.rs:73-75

is_empty()

Check if conversation is empty.
pub fn is_empty(&self) -> bool
Source: crates/goose/src/conversation/mod.rs:77-79

Message Manipulation

push()

Add a message, merging with the last if IDs match.
pub fn push(&mut self, message: Message)
message
Message
required
Message to append
Behavior:
  • If the last message has the same ID, content is merged
  • Otherwise, message is appended
Source: crates/goose/src/conversation/mod.rs:44-63 Example:
let mut conversation = Conversation::empty();
conversation.push(Message::user().with_text("Hello"));
conversation.push(Message::assistant().with_text("Hi!"));

extend()

Add multiple messages.
pub fn extend<I>(&mut self, iter: I)
where
    I: IntoIterator<Item = Message>,
iter
I: IntoIterator<Item = Message>
required
Messages to add
Source: crates/goose/src/conversation/mod.rs:81-88

pop()

Remove and return the last message.
pub fn pop(&mut self) -> Option<Message>
Source: crates/goose/src/conversation/mod.rs:94-96

truncate()

Keep only the first N messages.
pub fn truncate(&mut self, len: usize)
len
usize
required
Number of messages to keep
Source: crates/goose/src/conversation/mod.rs:98-100

clear()

Remove all messages.
pub fn clear(&mut self)
Source: crates/goose/src/conversation/mod.rs:102-104

Filtering

filtered_messages()

Filter messages by metadata.
pub fn filtered_messages<F>(&self, filter: F) -> Vec<Message>
where
    F: Fn(&MessageMetadata) -> bool,
filter
F: Fn(&MessageMetadata) -> bool
required
Predicate function
Returns: Filtered message vector Source: crates/goose/src/conversation/mod.rs:106-115

agent_visible_messages()

Get messages visible to the agent.
pub fn agent_visible_messages(&self) -> Vec<Message>
Returns: Messages with metadata.agent_visible = true Source: crates/goose/src/conversation/mod.rs:117-119 Example:
let agent_messages = conversation.agent_visible_messages();
for msg in agent_messages {
    // These messages will be sent to the LLM
}

user_visible_messages()

Get messages visible to the user.
pub fn user_visible_messages(&self) -> Vec<Message>
Returns: Messages with metadata.user_visible = true Source: crates/goose/src/conversation/mod.rs:121-123

Validation and Fixing

fix_conversation()

Automatically fix conversation issues.
pub fn fix_conversation(conversation: Conversation) -> (Conversation, Vec<String>)
conversation
Conversation
required
Conversation to fix
Returns: Tuple of (fixed_conversation, issues_found) Fixes Applied:
  1. Merge consecutive text content in assistant messages
  2. Trim trailing whitespace from assistant messages
  3. Remove empty messages
  4. Fix orphaned tool calls/responses
  5. Merge consecutive messages with same role
  6. Ensure conversation starts with user and ends with user
  7. Add placeholder “Hello” if empty
Source: crates/goose/src/conversation/mod.rs:164-200 Example:
let (fixed, issues) = fix_conversation(conversation);
if !issues.is_empty() {
    println!("Fixed issues: {:?}", issues);
}

Message Type

Message Struct

pub struct Message {
    pub id: Option<String>,
    pub role: Role,
    pub created: i64,
    pub content: Vec<MessageContent>,
    pub metadata: MessageMetadata,
}
Source: crates/goose/src/conversation/message.rs:663-670
id
Option<String>
Unique message identifier (auto-generated if not set)
role
Role
required
Role::User or Role::Assistant
created
i64
required
Unix timestamp (seconds)
content
Vec<MessageContent>
required
Message content items (text, images, tool calls, etc.)
metadata
MessageMetadata
required
Visibility settings

Message Constructors

user()

Create a user message.
pub fn user() -> Self
Source: crates/goose/src/conversation/message.rs:700-708

assistant()

Create an assistant message.
pub fn assistant() -> Self
Source: crates/goose/src/conversation/message.rs:710-718 Example:
let user_msg = Message::user()
    .with_text("Run the tests")
    .with_generated_id();

let assistant_msg = Message::assistant()
    .with_text("I'll run the tests for you.")
    .with_tool_request("req_1", Ok(tool_params));

Message Builders

with_text()

Add text content.
pub fn with_text<S: Into<String>>(self, text: S) -> Self
Source: crates/goose/src/conversation/message.rs:737-748

with_image()

Add image content.
pub fn with_image<S: Into<String>, T: Into<String>>(self, data: S, mime_type: T) -> Self
Source: crates/goose/src/conversation/message.rs:751-753

with_tool_request()

Add a tool call.
pub fn with_tool_request<S: Into<String>>(
    self,
    id: S,
    tool_call: ToolResult<CallToolRequestParams>,
) -> Self
Source: crates/goose/src/conversation/message.rs:756-762

with_tool_response()

Add a tool result.
pub fn with_tool_response<S: Into<String>>(
    self,
    id: S,
    result: ToolResult<CallToolResult>,
) -> Self
Source: crates/goose/src/conversation/message.rs:780-786

with_visibility()

Set visibility flags.
pub fn with_visibility(mut self, user_visible: bool, agent_visible: bool) -> Self
Source: crates/goose/src/conversation/message.rs:927-931 Example:
// Message visible only to agent
let internal_msg = Message::assistant()
    .with_text("Internal processing note")
    .with_visibility(false, true);

Message Utilities

as_concat_text()

Get all text content concatenated.
pub fn as_concat_text(&self) -> String
Returns: Newline-joined text from all text content items Source: crates/goose/src/conversation/message.rs:835-841

is_tool_call()

Check if message contains tool requests.
pub fn is_tool_call(&self) -> bool
Source: crates/goose/src/conversation/message.rs:844-848

is_tool_response()

Check if message contains tool responses.
pub fn is_tool_response(&self) -> bool
Source: crates/goose/src/conversation/message.rs:851-855

MessageContent Variants

pub enum MessageContent {
    Text(TextContent),
    Image(ImageContent),
    ToolRequest(ToolRequest),
    ToolResponse(ToolResponse),
    ToolConfirmationRequest(ToolConfirmationRequest),
    ActionRequired(ActionRequired),
    FrontendToolRequest(FrontendToolRequest),
    Thinking(ThinkingContent),
    RedactedThinking(RedactedThinkingContent),
    SystemNotification(SystemNotificationContent),
    Reasoning(ReasoningContent),
}
Source: crates/goose/src/conversation/message.rs:185-199

MessageMetadata

Controls message visibility.
pub struct MessageMetadata {
    pub user_visible: bool,
    pub agent_visible: bool,
}
Source: crates/goose/src/conversation/message.rs:586-591

Constructors

impl MessageMetadata {
    pub fn agent_only() -> Self;   // user_visible: false, agent_visible: true
    pub fn user_only() -> Self;    // user_visible: true, agent_visible: false
    pub fn invisible() -> Self;    // user_visible: false, agent_visible: false
}
Source: crates/goose/src/conversation/message.rs:603-625 Example:
let internal_msg = Message::assistant()
    .with_text("System processing")
    .with_metadata(MessageMetadata::agent_only());
  • Agent - Uses Conversation for context
  • Session - Persists Conversation
  • Config - Configuration system

Build docs developers (and LLMs) love