Class: Kward::Conversation

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/conversation.rb

Constant Summary collapse

DEFAULT_SYSTEM_MESSAGE =
Object.new.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(system_message: DEFAULT_SYSTEM_MESSAGE, messages: [], read_paths: [], on_append: nil, on_compact: nil, on_tool_execution: nil, workspace_root: Dir.pwd, compaction_system_message: DEFAULT_SYSTEM_MESSAGE, model: nil, reasoning_effort: nil, memory_context: nil, session_memories: [], last_memory_retrieval: nil, plugin_registry: nil) ⇒ Conversation

Returns a new instance of Conversation.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/kward/conversation.rb', line 14

def initialize(system_message: DEFAULT_SYSTEM_MESSAGE, messages: [], read_paths: [], on_append: nil, on_compact: nil, on_tool_execution: nil, workspace_root: Dir.pwd, compaction_system_message: DEFAULT_SYSTEM_MESSAGE, model: nil, reasoning_effort: nil, memory_context: nil, session_memories: [], last_memory_retrieval: nil, plugin_registry: nil)
  @workspace_root = ConfigFiles.canonical_workspace_root(workspace_root)
  @model = model
  @reasoning_effort = reasoning_effort
  @plugin_registry = plugin_registry
  @messages = []
  if system_message.equal?(DEFAULT_SYSTEM_MESSAGE)
    system_message = messages.any? { |message| MessageAccess.role(message) == "system" } ? nil : Prompts.system_message(workspace_root: @workspace_root, model: @model, reasoning_effort: @reasoning_effort, memory_context: memory_context, plugin_context: plugin_prompt_context)
  end
  @system_message_enabled = !!(system_message || messages.find { |message| MessageAccess.role(message) == "system" })
  if compaction_system_message.equal?(DEFAULT_SYSTEM_MESSAGE)
    compaction_system_message = @system_message_enabled ? Prompts.system_message(workspace_root: @workspace_root, include_workspace_personality: false, model: @model, reasoning_effort: @reasoning_effort) : nil
  end
  @compaction_system_message = compaction_system_message
  @workspace_agents_mtime = workspace_agents_mtime
  @last_entry_compaction = false
  @memory_context = memory_context
  @session_memories = Array(session_memories)
  @last_memory_retrieval = last_memory_retrieval
  @messages << system_message unless system_message.nil?
  @messages.concat(messages)
  @read_paths = Set.new(read_paths)
  @on_append = on_append
  @on_compact = on_compact
  @on_tool_execution = on_tool_execution
end

Instance Attribute Details

#compaction_system_messageObject (readonly)

Returns the value of attribute compaction_system_message.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def compaction_system_message
  @compaction_system_message
end

#last_memory_retrievalObject

Returns the value of attribute last_memory_retrieval.



12
13
14
# File 'lib/kward/conversation.rb', line 12

def last_memory_retrieval
  @last_memory_retrieval
end

#memory_contextObject

Returns the value of attribute memory_context.



12
13
14
# File 'lib/kward/conversation.rb', line 12

def memory_context
  @memory_context
end

#messagesObject (readonly)

Returns the value of attribute messages.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def messages
  @messages
end

#modelObject (readonly)

Returns the value of attribute model.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def model
  @model
end

#on_appendObject

Returns the value of attribute on_append.



12
13
14
# File 'lib/kward/conversation.rb', line 12

def on_append
  @on_append
end

#on_compactObject

Returns the value of attribute on_compact.



12
13
14
# File 'lib/kward/conversation.rb', line 12

def on_compact
  @on_compact
end

#on_tool_executionObject

Returns the value of attribute on_tool_execution.



12
13
14
# File 'lib/kward/conversation.rb', line 12

def on_tool_execution
  @on_tool_execution
end

#plugin_registryObject

Returns the value of attribute plugin_registry.



12
13
14
# File 'lib/kward/conversation.rb', line 12

def plugin_registry
  @plugin_registry
end

#read_pathsObject (readonly)

Returns the value of attribute read_paths.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def read_paths
  @read_paths
end

#reasoning_effortObject (readonly)

Returns the value of attribute reasoning_effort.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def reasoning_effort
  @reasoning_effort
end

#session_memoriesObject (readonly)

Returns the value of attribute session_memories.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def session_memories
  @session_memories
end

#workspace_rootObject (readonly)

Returns the value of attribute workspace_root.



11
12
13
# File 'lib/kward/conversation.rb', line 11

def workspace_root
  @workspace_root
end

Instance Method Details

#append_assistant(message) ⇒ Object



48
49
50
51
# File 'lib/kward/conversation.rb', line 48

def append_assistant(message)
  message = { role: "assistant", content: message } if message.is_a?(String)
  append_message(message)
end

#append_tool(tool_call_id:, name:, content:) ⇒ Object



53
54
55
56
57
58
59
60
# File 'lib/kward/conversation.rb', line 53

def append_tool(tool_call_id:, name:, content:)
  append_message({
    role: "tool",
    tool_call_id: tool_call_id,
    name: name,
    content: content
  })
end

#append_tool_execution(tool_call:, content:) ⇒ Object



62
63
64
# File 'lib/kward/conversation.rb', line 62

def append_tool_execution(tool_call:, content:)
  @on_tool_execution&.call(tool_call, content)
end

#append_user(content, display_content: nil) ⇒ Object



41
42
43
44
45
46
# File 'lib/kward/conversation.rb', line 41

def append_user(content, display_content: nil)
  content = ImageAttachments.content_from_text(content) unless content.is_a?(Array)
  message = { role: "user", content: content }
  message[:display_content] = display_content.to_s unless display_content.nil?
  append_message(message)
end

#compact!(summary, compaction_summary: false, first_kept_entry_id: nil, tokens_before: nil, from_hook: false, details: {}, keep_messages: []) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/kward/conversation.rb', line 98

def compact!(summary, compaction_summary: false, first_kept_entry_id: nil, tokens_before: nil, from_hook: false, details: {}, keep_messages: [])
  message = if compaction_summary
              { role: "compactionSummary", summary: summary.to_s }
            else
              { role: "assistant", content: summary.to_s }
            end
  if compaction_summary
    message[:first_kept_entry_id] = first_kept_entry_id if first_kept_entry_id
    message[:tokens_before] = tokens_before if tokens_before
    message[:from_hook] = from_hook
    message[:details] = details || {}
  end
  @messages = @messages.select { |item| MessageAccess.role(item) == "system" }
  @messages << message
  @messages.concat(Array(keep_messages))
  @read_paths.clear
  @last_entry_compaction = true
  @on_compact&.call(message)
  message
end

#last_entry_compaction?Boolean

Returns:

  • (Boolean)


119
120
121
# File 'lib/kward/conversation.rb', line 119

def last_entry_compaction?
  @last_entry_compaction
end

#last_file_change_resultObject



127
128
129
130
131
# File 'lib/kward/conversation.rb', line 127

def last_file_change_result
  @messages.select do |message|
    MessageAccess.role(message) == "tool" && ["write_file", "edit_file"].include?(MessageAccess.name(message))
  end.last
end

#mark_last_entry_compaction!Object



123
124
125
# File 'lib/kward/conversation.rb', line 123

def mark_last_entry_compaction!
  @last_entry_compaction = true
end

#mark_read(path) ⇒ Object



87
88
89
# File 'lib/kward/conversation.rb', line 87

def mark_read(path)
  @read_paths << path
end

#plugin_prompt_contextObject



91
92
93
94
95
96
# File 'lib/kward/conversation.rb', line 91

def plugin_prompt_context
  return nil unless plugin_registry

  context = PluginRegistry::Context.new(conversation: self, workspace_root: @workspace_root)
  plugin_registry.prompt_context(context)
end

#refresh_system_message!Object



66
67
68
69
70
71
72
73
74
75
# File 'lib/kward/conversation.rb', line 66

def refresh_system_message!
  return nil unless @system_message_enabled

  replacement = Prompts.system_message(workspace_root: @workspace_root, model: @model, reasoning_effort: @reasoning_effort, memory_context: @memory_context, plugin_context: plugin_prompt_context)
  index = @messages.index { |message| MessageAccess.role(message) == "system" }
  index ? @messages[index] = replacement : @messages.unshift(replacement)
  @compaction_system_message = Prompts.system_message(workspace_root: @workspace_root, include_workspace_personality: false, model: @model, reasoning_effort: @reasoning_effort)
  @workspace_agents_mtime = workspace_agents_mtime
  replacement
end

#refresh_system_message_if_workspace_agents_changed!Object



83
84
85
# File 'lib/kward/conversation.rb', line 83

def refresh_system_message_if_workspace_agents_changed!
  refresh_system_message! if @system_message_enabled && workspace_agents_mtime != @workspace_agents_mtime
end

#update_runtime_context!(model:, reasoning_effort:) ⇒ Object



77
78
79
80
81
# File 'lib/kward/conversation.rb', line 77

def update_runtime_context!(model:, reasoning_effort:)
  @model = model
  @reasoning_effort = reasoning_effort
  refresh_system_message!
end