Async::OllamaSourceAsyncOllamaConversation

class Conversation

Represents a conversation with the Ollama API, managing messages, tool calls, and summarization.

Nested

Definitions

def initialize(client, model: MODEL, messages: [], **options)

Initializes a new conversation.

Signature

parameter client Client

The Ollama client instance.

parameter model String

The model to use for the conversation.

parameter messages Array(Hash)

The initial messages for the conversation.

parameter options Hash

Additional options for the conversation.

Implementation

def initialize(client, model: MODEL, messages: [], **options)
	@client = client
	@model = model
	@messages = messages
	@options = options
	
	@toolbox = Toolbox.new
	
	@last_response = nil
end

attr :toolbox

Signature

attribute Toolbox

The toolbox for this conversation.

attr :messages

Signature

attribute Array(Hash)

The messages in the conversation.

def size

Signature

returns Integer

The number of messages in the conversation.

Implementation

def size
	@messages.size
end

def token_count

Signature

returns Integer

The token count of the last response, or 0 if none.

Implementation

def token_count
	@last_response&.token_count || 0
end

def call(prompt, &block)

Sends a prompt to the conversation and processes the response, including tool calls.

Signature

parameter prompt String | Hash

The prompt to send (as a string or message hash).

returns Chat

The final chat response.

Implementation

def call(prompt, &block)
	if prompt.is_a?(String)
		@messages << {
			role: "user",
			content: prompt
		}
	else
		@messages << prompt
	end
	
	while true
		@last_response = @client.chat(@messages, model: @model, tools: @toolbox.explain, options: @options, &block)
		
		if error = @last_response.error
			raise ChatError, error
		end
		
		@messages << @last_response.message
		
		tool_calls = @last_response.tool_calls
		
		if tool_calls.nil? || tool_calls.empty?
			return @last_response
		end
		
		tool_calls.each do |tool_call|
			@messages << @toolbox.call(tool_call)
		end
	end
end

def summarize!(retain = -1, role: "user")

Summarizes the conversation and truncates messages to reduce context usage.

Signature

parameter retain Integer

The number of messages to retain after summarization.

parameter role String

The role to use for the summarization message.

returns void

Implementation

def summarize!(retain = -1, role: "user")
	current_size = @messages.size
	
	# In principle, this could generate several messages:
	message = {
		role: role,
		content: SUMMARIZE_MESSAGE,
	}
	
	self.call(message)
	
	# The number of messages generated by the summarization:
	delta = @messages.size - current_size
	
	# After much experimentation, I found that leaving the SUMMARIZE_MESSAGE in the message stream caused extreme confusion, so we set retain to -1 to remove it by default.
	retain += delta
	if retain < @messages.size
		truncated = @messages.size - retain
		
		# We need to truncate the messages:
		@messages = @messages.last(retain)
		
		@messages.unshift({
			role: "system",
			content: "#{truncated} previous messages that have been removed and summarized to reduce context usage. Continue the conversation naturally as if the previous messages were still present.",
		})
	end
end