Class: Ask::Agent::Chat

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/agent/chat.rb

Overview

Thin wrapper around Provider + an internal message array that presents a Chat-like API for ask-agent internal use.

Manages conversation history, resolves the correct provider/model, handles streaming chunk accumulation, and normalises tool call formats between Ask::Provider (Array of Hashes) and ask-agent internal usage (Hash of { id => ToolCallInfo }).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model:, tools: [], temperature: nil, schema: nil) ⇒ Chat

Returns a new instance of Chat.

Parameters:

  • model (String, #ask)

    model ID or chat-like object

  • tools (Array<Ask::Tool>) (defaults to: [])

    tool instances available to the chat

  • temperature (Float, nil) (defaults to: nil)

    sampling temperature

  • schema (Ask::Schema, Hash, nil) (defaults to: nil)

    structured output schema



38
39
40
41
42
43
44
45
46
# File 'lib/ask/agent/chat.rb', line 38

def initialize(model:, tools: [], temperature: nil, schema: nil, **)
  @model_id = model.respond_to?(:id) ? model.id : model.to_s
  @model_info = resolve_model(@model_id)
  @tools = tools
  @temperature = temperature
  @schema = schema
  @messages = []
  @provider = nil
end

Instance Attribute Details

#messagesArray<Ask::Message> (readonly)

Returns all messages in the conversation.

Returns:

  • (Array<Ask::Message>)

    all messages in the conversation



32
33
34
# File 'lib/ask/agent/chat.rb', line 32

def messages
  @messages
end

#modelString (readonly)

Returns model ID (e.g. “gpt-4o”).

Returns:

  • (String)

    model ID (e.g. “gpt-4o”)



29
30
31
# File 'lib/ask/agent/chat.rb', line 29

def model
  @model
end

Instance Method Details

#add_message(role:, content: nil, tool_call_id: nil, tool_calls: nil) ⇒ Object

Add a message to the conversation history.

Parameters:

  • role (Symbol)

    :system, :user, :assistant, :tool

  • content (String, nil) (defaults to: nil)

    message content

  • tool_call_id (String, nil) (defaults to: nil)

    tool call ID (for tool results)

  • tool_calls (Array<Hash>, nil) (defaults to: nil)

    tool call invocations



107
108
109
110
111
112
113
114
# File 'lib/ask/agent/chat.rb', line 107

def add_message(role:, content: nil, tool_call_id: nil, tool_calls: nil)
  @messages << Ask::Message.new(
    role: role,
    content: content,
    tool_call_id: tool_call_id,
    tool_calls: tool_calls
  )
end

#ask(message = nil) {|ChatChunk| ... } ⇒ ResponseMessage

Send a user message and get a completion response.

Parameters:

  • message (String, nil) (defaults to: nil)

    user message text

Yields:

  • (ChatChunk)

    streaming chunks (only when a block is given)

Returns:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ask/agent/chat.rb', line 53

def ask(message = nil, &block)
  @messages << Ask::Message.new(role: :user, content: message.to_s) if message

  stream = block_given?
  tool_defs = @tools.map { |t| Ask::ToolDef.from_tool(t) }

  # Accumulator for tool calls during streaming (keyed by index)
  calls_acc = {}

  result = provider.chat(
    @messages.map(&:to_h),
    model: @model_id,
    tools: tool_defs,
    temperature: @temperature,
    stream: stream,
    schema: @schema&.respond_to?(:to_json_schema) ? @schema.to_json_schema : @schema
  ) do |raw_chunk|
    next unless block_given?

    # Accumulate tool calls by index during streaming
    accumulate_tool_calls(raw_chunk, calls_acc)

    # Yield adapted chunk with current tool call state
    yield ChatChunk.new(
      content: raw_chunk.content,
      tool_calls: build_current_tool_calls(calls_acc),
      thinking: raw_chunk.respond_to?(:thinking) ? raw_chunk.thinking : nil
    )
  end

  response_msg = if stream
    build_stream_response(result, calls_acc)
  else
    build_response(result)
  end

  # Store assistant response in conversation history
  @messages << Ask::Message.new(
    role: :assistant,
    content: response_msg.content,
    tool_calls: response_msg.tool_calls&.values&.map { |tc|
      { id: tc.id, type: "function", name: tc.name, arguments: tc.arguments }
    }
  )

  response_msg
end

#reset_messages!Object

Clear all messages from the conversation.



127
128
129
# File 'lib/ask/agent/chat.rb', line 127

def reset_messages!
  @messages.clear
end

#with_instructions(prompt) ⇒ self

Set or replace the system prompt.

Parameters:

  • prompt (String)

    system instructions

Returns:

  • (self)


120
121
122
123
124
# File 'lib/ask/agent/chat.rb', line 120

def with_instructions(prompt)
  @messages.reject! { |m| m.role == :system }
  @messages.unshift(Ask::Message.new(role: :system, content: prompt))
  self
end