smart_prompt 0.4.4 → 0.5.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/CHANGELOG.md +16 -0
- data/README.cn.md +305 -11
- data/README.md +309 -11
- data/Rakefile +10 -1
- data/config/anthropic_config.yml +151 -0
- data/config/image_generation_config.yml +22 -0
- data/config/multimodal_config.yml +85 -0
- data/config/sensenova_config.yml +63 -0
- data/config/zhipu_config.yml +73 -0
- data/docs/ANTHROPIC_EXAMPLES.md +559 -0
- data/docs/CONVERSATION_INTEGRATION_SUMMARY.md +155 -0
- data/docs/HISTORY_EXAMPLES_README.md +533 -0
- data/docs/HISTORY_MANAGEMENT_GUIDE.md +797 -0
- data/docs/MONITORING_GUIDE.md +278 -0
- data/docs/MULTIMODAL_README.md +265 -0
- data/docs/RELEVANCE_BASED_STRATEGY_IMPLEMENTATION.md +124 -0
- data/docs/STT_README.md +302 -0
- data/docs/TTS_README.md +303 -0
- data/docs/VIDEO_GENERATION_README.md +246 -0
- data/docs/delete_files_list.md +124 -0
- data/examples/anthropic_basic_chat.rb +143 -0
- data/examples/anthropic_example.rb +232 -0
- data/examples/anthropic_multimodal.rb +212 -0
- data/examples/anthropic_streaming.rb +312 -0
- data/examples/anthropic_tool_calling.rb +393 -0
- data/examples/automatic_cleanup_example.rb +109 -0
- data/examples/history_management_examples.rb +522 -0
- data/examples/image_generation_example.rb +130 -0
- data/examples/monitoring_example.rb +121 -0
- data/examples/multimodal_example.rb +63 -0
- data/examples/relevance_based_strategy_example.rb +87 -0
- data/examples/sensenova_example.rb +129 -0
- data/examples/stt_example.rb +287 -0
- data/examples/tts_example.rb +244 -0
- data/examples/video_generation_example.rb +189 -0
- data/examples/zhipu_example.rb +151 -0
- data/lib/smart_prompt/anthropic_adapter.rb +407 -298
- data/lib/smart_prompt/compression_engine.rb +201 -0
- data/lib/smart_prompt/context_strategy.rb +22 -0
- data/lib/smart_prompt/conversation.rb +47 -4
- data/lib/smart_prompt/engine.rb +29 -2
- data/lib/smart_prompt/history_manager.rb +596 -0
- data/lib/smart_prompt/hybrid_strategy.rb +222 -0
- data/lib/smart_prompt/image_generation_adapter.rb +297 -0
- data/lib/smart_prompt/lru_cache.rb +133 -0
- data/lib/smart_prompt/message.rb +57 -0
- data/lib/smart_prompt/multimodal_adapter.rb +277 -0
- data/lib/smart_prompt/persistence_layer.rb +197 -0
- data/lib/smart_prompt/relevance_based_strategy.rb +221 -0
- data/lib/smart_prompt/sensenova_adapter.rb +410 -0
- data/lib/smart_prompt/session.rb +140 -0
- data/lib/smart_prompt/sliding_window_strategy.rb +100 -0
- data/lib/smart_prompt/stt_adapter.rb +381 -0
- data/lib/smart_prompt/summary_based_strategy.rb +152 -0
- data/lib/smart_prompt/token_counter.rb +74 -0
- data/lib/smart_prompt/tts_adapter.rb +403 -0
- data/lib/smart_prompt/version.rb +1 -1
- data/lib/smart_prompt/video_generation_adapter.rb +330 -0
- data/lib/smart_prompt/worker.rb +28 -3
- data/lib/smart_prompt/zhipu_adapter.rb +616 -0
- data/lib/smart_prompt.rb +21 -0
- data/workers/history_management_examples.rb +407 -0
- data/workers/image_generation_workers.rb +119 -0
- data/workers/multimodal_workers.rb +110 -0
- data/workers/sensenova_workers.rb +62 -0
- data/workers/stt_workers.rb +195 -0
- data/workers/tts_workers.rb +388 -0
- data/workers/video_generation_workers.rb +264 -0
- data/workers/zhipu_workers.rb +113 -0
- metadata +88 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
module SmartPrompt
|
|
2
|
+
# CompressionEngine handles automatic compression of conversation history
|
|
3
|
+
# through summarization using an LLM adapter
|
|
4
|
+
#
|
|
5
|
+
# This engine:
|
|
6
|
+
# - Generates summaries of older messages to reduce token usage
|
|
7
|
+
# - Preserves key facts, decisions, and context in summaries
|
|
8
|
+
# - Falls back to truncation strategies when summarization fails
|
|
9
|
+
# - Tracks compression metrics for monitoring
|
|
10
|
+
class CompressionEngine
|
|
11
|
+
attr_reader :config
|
|
12
|
+
|
|
13
|
+
# Initialize the compression engine
|
|
14
|
+
# @param config [Hash] Configuration options
|
|
15
|
+
# @option config [LLMAdapter] :llm_adapter LLM adapter for generating summaries
|
|
16
|
+
# @option config [String] :prompt Custom summarization prompt template
|
|
17
|
+
# @option config [Float] :compression_ratio (0.5) Target compression ratio
|
|
18
|
+
# @option config [Integer] :min_messages_to_compress (5) Minimum messages needed for compression
|
|
19
|
+
def initialize(config = {})
|
|
20
|
+
@config = config
|
|
21
|
+
@llm_adapter = config[:llm_adapter]
|
|
22
|
+
@summarization_prompt = config[:prompt] || default_prompt
|
|
23
|
+
@compression_ratio = config[:compression_ratio] || 0.5
|
|
24
|
+
@min_messages_to_compress = config[:min_messages_to_compress] || 5
|
|
25
|
+
@token_counter = TokenCounter.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Summarize a collection of messages into a single summary message
|
|
29
|
+
# @param messages [Array<Message>] Messages to summarize
|
|
30
|
+
# @return [Message, nil] Summary message or nil if summarization fails
|
|
31
|
+
def summarize(messages)
|
|
32
|
+
return nil if messages.nil? || messages.empty?
|
|
33
|
+
return nil if messages.length < @min_messages_to_compress
|
|
34
|
+
|
|
35
|
+
# Build the content to summarize
|
|
36
|
+
content = messages.map { |msg| "#{msg.role}: #{msg.content}" }.join("\n")
|
|
37
|
+
|
|
38
|
+
# Create the summarization prompt
|
|
39
|
+
prompt = @summarization_prompt.gsub("{content}", content)
|
|
40
|
+
|
|
41
|
+
begin
|
|
42
|
+
# Call LLM to generate summary
|
|
43
|
+
summary_text = if @llm_adapter
|
|
44
|
+
@llm_adapter.send_request([
|
|
45
|
+
{ role: "user", content: prompt }
|
|
46
|
+
])
|
|
47
|
+
else
|
|
48
|
+
# If no LLM adapter, create a simple summary
|
|
49
|
+
create_fallback_summary(messages)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Calculate original token count
|
|
53
|
+
original_tokens = messages.sum { |msg| msg.token_count || @token_counter.count(msg.content) }
|
|
54
|
+
|
|
55
|
+
# Create summary message
|
|
56
|
+
summary_message = Message.new(
|
|
57
|
+
role: "system",
|
|
58
|
+
content: "[Summary of previous conversation]\n#{summary_text}",
|
|
59
|
+
is_summary: true,
|
|
60
|
+
metadata: {
|
|
61
|
+
original_count: messages.count,
|
|
62
|
+
original_tokens: original_tokens,
|
|
63
|
+
compressed_at: Time.now.iso8601
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Calculate tokens for the summary
|
|
68
|
+
summary_message.calculate_tokens(@token_counter)
|
|
69
|
+
|
|
70
|
+
SmartPrompt.logger.info "Compressed #{messages.count} messages (#{original_tokens} tokens) " \
|
|
71
|
+
"into summary (#{summary_message.token_count} tokens)"
|
|
72
|
+
|
|
73
|
+
summary_message
|
|
74
|
+
rescue => e
|
|
75
|
+
SmartPrompt.logger.error "Summarization failed: #{e.message}\n#{e.backtrace.join("\n")}"
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Compress a session by identifying and summarizing compressible segments
|
|
81
|
+
# @param session [Session] The session to compress
|
|
82
|
+
# @return [Boolean] true if compression was successful
|
|
83
|
+
def compress(session)
|
|
84
|
+
return false if session.nil? || session.messages.empty?
|
|
85
|
+
|
|
86
|
+
begin
|
|
87
|
+
# Identify compressible message segments
|
|
88
|
+
compressible_segments = identify_compressible_segments(session.messages)
|
|
89
|
+
|
|
90
|
+
return false if compressible_segments.empty?
|
|
91
|
+
|
|
92
|
+
# Generate summaries for each segment
|
|
93
|
+
summaries = compressible_segments.map { |segment| summarize(segment) }.compact
|
|
94
|
+
|
|
95
|
+
return false if summaries.empty?
|
|
96
|
+
|
|
97
|
+
# Replace original messages with summaries
|
|
98
|
+
replace_with_summaries(session, compressible_segments, summaries)
|
|
99
|
+
|
|
100
|
+
SmartPrompt.logger.info "Session #{session.id} compressed: #{compressible_segments.flatten.count} " \
|
|
101
|
+
"messages replaced with #{summaries.count} summaries"
|
|
102
|
+
true
|
|
103
|
+
rescue => e
|
|
104
|
+
SmartPrompt.logger.error "Compression failed for session #{session.id}: #{e.message}"
|
|
105
|
+
|
|
106
|
+
# Fall back to truncation strategy
|
|
107
|
+
fallback_truncate(session)
|
|
108
|
+
false
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Check if a session should be compressed based on configuration
|
|
113
|
+
# @param session [Session] The session to evaluate
|
|
114
|
+
# @return [Boolean] true if compression is recommended
|
|
115
|
+
def should_compress?(session)
|
|
116
|
+
return false if session.nil?
|
|
117
|
+
|
|
118
|
+
# Check if session has enough messages to warrant compression
|
|
119
|
+
session.message_count > (@min_messages_to_compress * 2)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
# Default summarization prompt template
|
|
125
|
+
def default_prompt
|
|
126
|
+
"Please provide a concise summary of the following conversation, " \
|
|
127
|
+
"preserving key facts, decisions, and context. Focus on the most important " \
|
|
128
|
+
"information that would be needed to continue the conversation:\n\n{content}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Create a simple fallback summary when LLM is not available
|
|
132
|
+
# @param messages [Array<Message>] Messages to summarize
|
|
133
|
+
# @return [String] Simple summary text
|
|
134
|
+
def create_fallback_summary(messages)
|
|
135
|
+
"Previous conversation contained #{messages.count} messages covering various topics."
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Identify segments of messages that can be compressed
|
|
139
|
+
# Strategy: Keep recent messages, compress older ones
|
|
140
|
+
# @param messages [Array<Message>] All messages in the session
|
|
141
|
+
# @return [Array<Array<Message>>] Array of message segments to compress
|
|
142
|
+
def identify_compressible_segments(messages)
|
|
143
|
+
return [] if messages.length <= @min_messages_to_compress
|
|
144
|
+
|
|
145
|
+
# Keep the most recent 5 messages uncompressed
|
|
146
|
+
keep_recent = 5
|
|
147
|
+
|
|
148
|
+
# Separate system messages (never compress) from others
|
|
149
|
+
system_messages = messages.select(&:system_message?)
|
|
150
|
+
non_system_messages = messages.reject(&:system_message?)
|
|
151
|
+
|
|
152
|
+
# If we don't have enough non-system messages, don't compress
|
|
153
|
+
return [] if non_system_messages.length <= keep_recent
|
|
154
|
+
|
|
155
|
+
# Identify the older messages that can be compressed
|
|
156
|
+
compressible = non_system_messages[0...-keep_recent]
|
|
157
|
+
|
|
158
|
+
# Group into segments (for now, treat all compressible messages as one segment)
|
|
159
|
+
compressible.empty? ? [] : [compressible]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Replace original messages with summary messages in the session
|
|
163
|
+
# @param session [Session] The session to modify
|
|
164
|
+
# @param segments [Array<Array<Message>>] Original message segments
|
|
165
|
+
# @param summaries [Array<Message>] Summary messages
|
|
166
|
+
def replace_with_summaries(session, segments, summaries)
|
|
167
|
+
# Get all messages to compress (flatten segments)
|
|
168
|
+
messages_to_remove = segments.flatten
|
|
169
|
+
|
|
170
|
+
# Remove the original messages
|
|
171
|
+
session.messages.reject! { |msg| messages_to_remove.include?(msg) }
|
|
172
|
+
|
|
173
|
+
# Insert summaries at the beginning (after system messages)
|
|
174
|
+
system_messages = session.messages.select(&:system_message?)
|
|
175
|
+
other_messages = session.messages.reject(&:system_message?)
|
|
176
|
+
|
|
177
|
+
# Rebuild messages array: system messages + summaries + remaining messages
|
|
178
|
+
session.instance_variable_set(:@messages, system_messages + summaries + other_messages)
|
|
179
|
+
session.instance_variable_set(:@updated_at, Time.now)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Fallback truncation strategy when summarization fails
|
|
183
|
+
# Simply removes oldest non-system messages to reduce size
|
|
184
|
+
# @param session [Session] The session to truncate
|
|
185
|
+
def fallback_truncate(session)
|
|
186
|
+
SmartPrompt.logger.warn "Falling back to truncation for session #{session.id}"
|
|
187
|
+
|
|
188
|
+
# Keep system messages and recent messages
|
|
189
|
+
system_messages = session.messages.select(&:system_message?)
|
|
190
|
+
non_system_messages = session.messages.reject(&:system_message?)
|
|
191
|
+
|
|
192
|
+
# Keep only the most recent half of non-system messages
|
|
193
|
+
keep_count = (non_system_messages.length * 0.5).ceil
|
|
194
|
+
kept_messages = non_system_messages.last(keep_count)
|
|
195
|
+
|
|
196
|
+
# Update session messages
|
|
197
|
+
session.instance_variable_set(:@messages, system_messages + kept_messages)
|
|
198
|
+
session.instance_variable_set(:@updated_at, Time.now)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module SmartPrompt
|
|
2
|
+
# ContextStrategy defines the interface for context selection strategies
|
|
3
|
+
# Different strategies implement different algorithms for selecting which
|
|
4
|
+
# messages to include in the context window based on various criteria
|
|
5
|
+
module ContextStrategy
|
|
6
|
+
# Select messages from the session to include in context
|
|
7
|
+
# @param messages [Array<Message>] All messages in the session
|
|
8
|
+
# @param max_tokens [Integer, nil] Maximum token limit for selected messages
|
|
9
|
+
# @param current_message [Message, nil] The current message being processed (for relevance)
|
|
10
|
+
# @return [Array<Message>] Selected messages that fit within constraints
|
|
11
|
+
def select_messages(messages, max_tokens, current_message = nil)
|
|
12
|
+
raise NotImplementedError, "#{self.class} must implement #select_messages"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Determine if the session should be compressed
|
|
16
|
+
# @param session [Session] The session to evaluate
|
|
17
|
+
# @return [Boolean] true if compression is recommended
|
|
18
|
+
def should_compress?(session)
|
|
19
|
+
raise NotImplementedError, "#{self.class} must implement #should_compress?"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -20,8 +20,9 @@ module SmartPrompt
|
|
|
20
20
|
|
|
21
21
|
attr_reader :messages, :last_response, :config_file
|
|
22
22
|
attr_reader :last_call_id
|
|
23
|
+
attr_reader :session_id
|
|
23
24
|
|
|
24
|
-
def initialize(engine, tools = nil)
|
|
25
|
+
def initialize(engine, tools = nil, session_id = nil)
|
|
25
26
|
SmartPrompt.logger.info "Create Conversation"
|
|
26
27
|
@messages = []
|
|
27
28
|
@engine = engine
|
|
@@ -37,6 +38,8 @@ module SmartPrompt
|
|
|
37
38
|
@request_options = {}
|
|
38
39
|
@pending_content_parts = []
|
|
39
40
|
@thinking_enabled = nil
|
|
41
|
+
@session_id = session_id
|
|
42
|
+
@use_history_manager = false
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
def use(llm_name)
|
|
@@ -86,12 +89,29 @@ module SmartPrompt
|
|
|
86
89
|
end
|
|
87
90
|
|
|
88
91
|
def history_messages
|
|
89
|
-
|
|
92
|
+
# If using HistoryManager, get messages from session
|
|
93
|
+
if @use_history_manager && @engine.history_manager
|
|
94
|
+
session_messages = @engine.history_manager.get_context(@session_id)
|
|
95
|
+
# Convert Message objects to hash format for backward compatibility
|
|
96
|
+
session_messages.map(&:to_h)
|
|
97
|
+
else
|
|
98
|
+
# Fall back to old implementation
|
|
99
|
+
@engine.history_messages
|
|
100
|
+
end
|
|
90
101
|
end
|
|
91
102
|
|
|
92
103
|
def add_message(msg, with_history = false)
|
|
93
104
|
if with_history
|
|
94
|
-
|
|
105
|
+
# If HistoryManager is available, use it
|
|
106
|
+
if @engine.history_manager
|
|
107
|
+
@use_history_manager = true
|
|
108
|
+
# Ensure we have a session ID
|
|
109
|
+
@session_id ||= generate_default_session_id
|
|
110
|
+
@engine.history_manager.add_message(@session_id, msg)
|
|
111
|
+
else
|
|
112
|
+
# Fall back to old implementation
|
|
113
|
+
@engine.history_messages << msg
|
|
114
|
+
end
|
|
95
115
|
end
|
|
96
116
|
@messages << msg
|
|
97
117
|
end
|
|
@@ -110,7 +130,7 @@ module SmartPrompt
|
|
|
110
130
|
end
|
|
111
131
|
end
|
|
112
132
|
|
|
113
|
-
def sys_msg(message, params)
|
|
133
|
+
def sys_msg(message, params = {})
|
|
114
134
|
@sys_msg = thinking_system_message(message)
|
|
115
135
|
add_message({ role: "system", content: @sys_msg }, params[:with_history])
|
|
116
136
|
self
|
|
@@ -144,6 +164,15 @@ module SmartPrompt
|
|
|
144
164
|
@last_response
|
|
145
165
|
end
|
|
146
166
|
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
def generate_default_session_id
|
|
170
|
+
# Generate a default session ID based on worker name or timestamp
|
|
171
|
+
"default_#{Time.now.to_i}_#{rand(1000)}"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
public
|
|
175
|
+
|
|
147
176
|
def send_msg(params = {})
|
|
148
177
|
Retriable.retriable(RETRY_OPTIONS) do
|
|
149
178
|
raise ConfigurationError, "No LLM selected" if @current_llm.nil?
|
|
@@ -318,5 +347,19 @@ module SmartPrompt
|
|
|
318
347
|
system_message = @messages.find { |item| (item[:role] || item["role"]) == "system" }
|
|
319
348
|
system_message[:content] = message if system_message
|
|
320
349
|
end
|
|
350
|
+
|
|
351
|
+
public
|
|
352
|
+
|
|
353
|
+
def generate_image(prompt, params = {})
|
|
354
|
+
@current_llm.generate_image(prompt, params)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def edit_image(prompt, params = {})
|
|
358
|
+
@current_llm.edit_image(prompt, params)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def save_image(image_data, output_dir = "./output", filename_prefix = "generated_image")
|
|
362
|
+
@current_llm.save_image(image_data, output_dir, filename_prefix)
|
|
363
|
+
end
|
|
321
364
|
end
|
|
322
365
|
end
|
data/lib/smart_prompt/engine.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module SmartPrompt
|
|
2
2
|
class Engine
|
|
3
3
|
attr_reader :config_file, :config, :adapters, :current_adapter, :llms, :models, :templates
|
|
4
|
-
attr_reader :stream_response
|
|
4
|
+
attr_reader :stream_response, :history_manager
|
|
5
5
|
|
|
6
6
|
def initialize(config_file)
|
|
7
7
|
@config_file = config_file
|
|
@@ -11,6 +11,7 @@ module SmartPrompt
|
|
|
11
11
|
@templates = {}
|
|
12
12
|
@current_workers = {}
|
|
13
13
|
@history_messages = []
|
|
14
|
+
@history_manager = nil
|
|
14
15
|
load_config(config_file)
|
|
15
16
|
SmartPrompt.logger.info "Started create the SmartPrompt engine."
|
|
16
17
|
@stream_proc = Proc.new do |chunk, _bytesize|
|
|
@@ -79,6 +80,14 @@ module SmartPrompt
|
|
|
79
80
|
template_name = file.gsub(@config["template_path"] + "/", "").gsub("\.erb", "")
|
|
80
81
|
@templates[template_name] = PromptTemplate.new(file)
|
|
81
82
|
end
|
|
83
|
+
|
|
84
|
+
# Initialize HistoryManager if configured
|
|
85
|
+
if @config["history"]
|
|
86
|
+
history_config = symbolize_keys(@config["history"])
|
|
87
|
+
@history_manager = HistoryManager.new(history_config)
|
|
88
|
+
SmartPrompt.logger.info "HistoryManager initialized with configuration"
|
|
89
|
+
end
|
|
90
|
+
|
|
82
91
|
load_workers
|
|
83
92
|
rescue Psych::SyntaxError => ex
|
|
84
93
|
SmartPrompt.logger.error "YAML syntax error in config file: #{ex.message}"
|
|
@@ -146,7 +155,8 @@ module SmartPrompt
|
|
|
146
155
|
begin
|
|
147
156
|
@origin_proc = proc
|
|
148
157
|
@stream_response = {}
|
|
149
|
-
worker.execute_by_stream(params, &@stream_proc)
|
|
158
|
+
ret = worker.execute_by_stream(params, &@stream_proc)
|
|
159
|
+
@stream_response = ret if @stream_response.empty?
|
|
150
160
|
SmartPrompt.logger.info "Worker #{worker_name} executed(stream) successfully"
|
|
151
161
|
SmartPrompt.logger.info "Worker #{worker_name} stream response is: #{@stream_response}"
|
|
152
162
|
rescue => e
|
|
@@ -166,10 +176,16 @@ module SmartPrompt
|
|
|
166
176
|
end
|
|
167
177
|
|
|
168
178
|
def history_messages
|
|
179
|
+
if @history_manager
|
|
180
|
+
SmartPrompt.logger.warn "[DEPRECATED] Engine#history_messages is deprecated. Use history_manager.get_context(session_id) instead."
|
|
181
|
+
end
|
|
169
182
|
@history_messages
|
|
170
183
|
end
|
|
171
184
|
|
|
172
185
|
def clear_history_messages
|
|
186
|
+
if @history_manager
|
|
187
|
+
SmartPrompt.logger.warn "[DEPRECATED] Engine#clear_history_messages is deprecated. Use history_manager.clear_session(session_id) instead."
|
|
188
|
+
end
|
|
173
189
|
@history_messages = []
|
|
174
190
|
end
|
|
175
191
|
|
|
@@ -189,5 +205,16 @@ module SmartPrompt
|
|
|
189
205
|
def sanitize_history_content(content)
|
|
190
206
|
content.to_s.gsub(/<\|channel\>thought\n.*?<channel\|>/m, "")
|
|
191
207
|
end
|
|
208
|
+
|
|
209
|
+
# Recursively convert hash keys from strings to symbols
|
|
210
|
+
def symbolize_keys(hash)
|
|
211
|
+
return hash unless hash.is_a?(Hash)
|
|
212
|
+
|
|
213
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
214
|
+
new_key = key.is_a?(String) ? key.to_sym : key
|
|
215
|
+
new_value = value.is_a?(Hash) ? symbolize_keys(value) : value
|
|
216
|
+
result[new_key] = new_value
|
|
217
|
+
end
|
|
218
|
+
end
|
|
192
219
|
end
|
|
193
220
|
end
|