collavre_openclaw 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/collavre_openclaw/health_controller.rb +1 -1
- data/app/services/collavre_openclaw/ai_client_extension.rb +6 -3
- data/app/services/collavre_openclaw/connection_manager.rb +1 -1
- data/app/services/collavre_openclaw/openclaw_adapter.rb +14 -59
- data/app/services/collavre_openclaw/websocket_client.rb +2 -2
- data/lib/collavre_openclaw/configuration.rb +5 -1
- data/lib/collavre_openclaw/engine.rb +1 -1
- data/lib/collavre_openclaw/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09c2c291bce3fdf441d7c93dacf44883ca47804db6abb38d78b3ea70a412deaf'
|
|
4
|
+
data.tar.gz: 8bd10b6c57adedb5e655b23688088a1be0d8c2e97dca6300ef7470b1a28c8c33
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba347bb7844c93e96b5601f728267df346ba28b78458d77e0ea8a8f83ceb77fc83127ddad0dd164704f8bad95701df1838ac636e8c59aaa356bab07c63e5d58d
|
|
7
|
+
data.tar.gz: 90b4555d3b69f2f0489dff54e6f326b4ee57404989e1ef7068a2b91d8761541ddf42be7154ed6e0a7b8e5f536d6408441b3412997731c3868c603fb3e25a7122
|
|
@@ -12,8 +12,8 @@ module CollavreOpenclaw
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# @param messages_input [Hash, Array] Hash { messages:, first_message:, context_changed: }
|
|
16
|
-
# from
|
|
15
|
+
# @param messages_input [Hash, Array] Hash { messages:, first_message:, context_changed:, system_prompt: }
|
|
16
|
+
# from SessionContextResolver, or a plain Array from standalone callers (e.g., CompressJob).
|
|
17
17
|
def chat(messages_input, tools: [], &block)
|
|
18
18
|
normalized_vendor = vendor.to_s.downcase
|
|
19
19
|
messages_data = normalize_messages_input(messages_input)
|
|
@@ -23,10 +23,13 @@ module CollavreOpenclaw
|
|
|
23
23
|
|
|
24
24
|
if adapter_class
|
|
25
25
|
# Use the custom adapter (tools not supported for OpenClaw)
|
|
26
|
+
# Prefer resolved system_prompt from SessionContextResolver over instance default.
|
|
27
|
+
# key?(:system_prompt) distinguishes "not provided" (Array input) from "explicitly nil" (incremental session).
|
|
28
|
+
resolved_system_prompt = messages_data.key?(:system_prompt) ? messages_data[:system_prompt] : system_prompt
|
|
26
29
|
user = context&.dig(:user)
|
|
27
30
|
adapter = adapter_class.new(
|
|
28
31
|
user: user,
|
|
29
|
-
system_prompt:
|
|
32
|
+
system_prompt: resolved_system_prompt,
|
|
30
33
|
context: context
|
|
31
34
|
)
|
|
32
35
|
|
|
@@ -205,7 +205,7 @@ module CollavreOpenclaw
|
|
|
205
205
|
next unless @idle_check_counter >= 60 # Run check every ~60 seconds
|
|
206
206
|
@idle_check_counter = 0
|
|
207
207
|
check_idle_connections!
|
|
208
|
-
rescue => e
|
|
208
|
+
rescue StandardError => e
|
|
209
209
|
Rails.logger.error("[CollavreOpenclaw::ConnectionManager] Idle checker error: #{e.message}")
|
|
210
210
|
end
|
|
211
211
|
end
|
|
@@ -3,9 +3,11 @@ require "json"
|
|
|
3
3
|
|
|
4
4
|
module CollavreOpenclaw
|
|
5
5
|
class OpenclawAdapter
|
|
6
|
-
#
|
|
6
|
+
# Pure transport adapter for OpenClaw AI Gateway.
|
|
7
|
+
# Session context filtering (full vs incremental) is handled upstream
|
|
8
|
+
# by SessionContextResolver — this adapter sends exactly what it receives.
|
|
7
9
|
#
|
|
8
|
-
#
|
|
10
|
+
# Transport modes:
|
|
9
11
|
# 1. WebSocket (primary) - via faye-websocket + EventMachine
|
|
10
12
|
# 2. HTTP (fallback) - via Faraday POST /v1/chat/completions
|
|
11
13
|
#
|
|
@@ -72,27 +74,12 @@ module CollavreOpenclaw
|
|
|
72
74
|
|
|
73
75
|
private
|
|
74
76
|
|
|
75
|
-
CONTEXT_KINDS = %i[creative_context context_creative referenced_creative].freeze
|
|
76
|
-
|
|
77
77
|
def parse_messages_data!(data)
|
|
78
78
|
@all_messages = data[:messages] || []
|
|
79
79
|
@first_message = data[:first_message]
|
|
80
80
|
@context_changed = data[:context_changed]
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def context_messages
|
|
84
|
-
@all_messages.select { |m| CONTEXT_KINDS.include?(m[:kind]) }
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def trigger_message
|
|
88
|
-
@all_messages.find { |m| m[:kind] == :trigger }
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Send system prompt and context on first message or when they changed.
|
|
92
|
-
def include_full_context?
|
|
93
|
-
@first_message || @context_changed
|
|
94
|
-
end
|
|
95
|
-
|
|
96
83
|
# ─────────────────────────────────────────────
|
|
97
84
|
# WebSocket transport
|
|
98
85
|
# ─────────────────────────────────────────────
|
|
@@ -159,42 +146,24 @@ module CollavreOpenclaw
|
|
|
159
146
|
end
|
|
160
147
|
end
|
|
161
148
|
|
|
162
|
-
# Build WebSocket chat.send payload.
|
|
163
|
-
#
|
|
164
|
-
# Token optimization: only includes system prompt and creative context on
|
|
165
|
-
# the first message or when context has changed. The Gateway maintains its
|
|
166
|
-
# own session history, so chat history is never sent — only the trigger.
|
|
167
149
|
def build_ws_chat_payload
|
|
168
|
-
trigger = trigger_message
|
|
169
150
|
{
|
|
170
151
|
message: format_message_for_ws,
|
|
171
|
-
attachments:
|
|
152
|
+
attachments: extract_ws_attachments(@all_messages).presence
|
|
172
153
|
}
|
|
173
154
|
end
|
|
174
155
|
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
# On first message (or context change):
|
|
178
|
-
# [system prompt] + [creative context] + [trigger]
|
|
179
|
-
# On subsequent messages:
|
|
180
|
-
# [trigger only]
|
|
181
|
-
#
|
|
182
|
-
# Chat history is NOT included — the Gateway's SessionManager tracks
|
|
183
|
-
# conversation turns automatically.
|
|
156
|
+
# SessionContextResolver already decided what to include.
|
|
157
|
+
# We just format and send everything we received.
|
|
184
158
|
def format_message_for_ws
|
|
185
159
|
parts = []
|
|
160
|
+
parts << @system_prompt if @system_prompt.present?
|
|
186
161
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
text = extract_message_text(m)
|
|
191
|
-
parts << text if text.present?
|
|
192
|
-
end
|
|
162
|
+
@all_messages.each do |m|
|
|
163
|
+
text = extract_message_text(m)
|
|
164
|
+
parts << text if text.present?
|
|
193
165
|
end
|
|
194
166
|
|
|
195
|
-
trigger = trigger_message
|
|
196
|
-
parts << extract_message_text(trigger) if trigger
|
|
197
|
-
|
|
198
167
|
parts.join("\n\n")
|
|
199
168
|
end
|
|
200
169
|
|
|
@@ -347,28 +316,14 @@ module CollavreOpenclaw
|
|
|
347
316
|
# HTTP payload building
|
|
348
317
|
# ─────────────────────────────────────────────
|
|
349
318
|
|
|
350
|
-
#
|
|
351
|
-
#
|
|
352
|
-
# On first message (or context change):
|
|
353
|
-
# system prompt + creative context + trigger
|
|
354
|
-
# On subsequent messages:
|
|
355
|
-
# trigger only
|
|
356
|
-
#
|
|
357
|
-
# Chat history is NOT included — the Gateway's SessionManager tracks
|
|
358
|
-
# conversation turns via the stable session key.
|
|
319
|
+
# SessionContextResolver already decided what to include.
|
|
359
320
|
def build_payload
|
|
360
321
|
agent_id = extract_agent_id_from_email
|
|
361
322
|
model_value = agent_id.present? ? "openclaw:#{agent_id}" : "openclaw"
|
|
362
323
|
|
|
363
324
|
formatted = []
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
formatted << { role: "system", content: @system_prompt } if @system_prompt.present?
|
|
367
|
-
context_messages.each { |m| formatted << format_single_message(m) }
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
trigger = trigger_message
|
|
371
|
-
formatted << format_single_message(trigger) if trigger
|
|
325
|
+
formatted << { role: "system", content: @system_prompt } if @system_prompt.present?
|
|
326
|
+
@all_messages.each { |m| formatted << format_single_message(m) }
|
|
372
327
|
|
|
373
328
|
{
|
|
374
329
|
model: model_value,
|
|
@@ -94,7 +94,7 @@ module CollavreOpenclaw
|
|
|
94
94
|
EmReactor.next_tick do
|
|
95
95
|
begin
|
|
96
96
|
do_connect!(queue)
|
|
97
|
-
rescue => e
|
|
97
|
+
rescue StandardError => e
|
|
98
98
|
queue.push({ error: e.message })
|
|
99
99
|
end
|
|
100
100
|
end
|
|
@@ -659,7 +659,7 @@ module CollavreOpenclaw
|
|
|
659
659
|
schedule_reconnect!
|
|
660
660
|
end
|
|
661
661
|
end
|
|
662
|
-
rescue => e
|
|
662
|
+
rescue StandardError => e
|
|
663
663
|
Rails.logger.error("[CollavreOpenclaw::WS] RECONNECT gateway=#{url} state=fail reason=#{e.message}")
|
|
664
664
|
schedule_reconnect!
|
|
665
665
|
end
|
|
@@ -27,7 +27,11 @@ module CollavreOpenclaw
|
|
|
27
27
|
|
|
28
28
|
def initialize
|
|
29
29
|
@open_timeout = ENV.fetch("OPENCLAW_OPEN_TIMEOUT", 10).to_i
|
|
30
|
-
@read_timeout =
|
|
30
|
+
@read_timeout = begin
|
|
31
|
+
Collavre::SystemSetting.llm_request_timeout_seconds
|
|
32
|
+
rescue StandardError
|
|
33
|
+
1800
|
|
34
|
+
end
|
|
31
35
|
@max_retries = ENV.fetch("OPENCLAW_MAX_RETRIES", 2).to_i
|
|
32
36
|
@ws_idle_timeout = ENV.fetch("OPENCLAW_WS_IDLE_TIMEOUT", 1800).to_i # 30 minutes
|
|
33
37
|
@ws_reconnect_max = ENV.fetch("OPENCLAW_WS_RECONNECT_MAX", 10).to_i
|