parse-stack-next 5.6.0 → 5.7.0
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 +409 -0
- data/README.md +378 -6
- data/docs/caching.md +748 -0
- data/examples/basic_client.rb +3 -3
- data/examples/basic_server.rb +3 -3
- data/examples/live_query_listener.rb +12 -12
- data/examples/rag_chatbot.rb +14 -14
- data/examples/transaction_example.rb +44 -45
- data/examples/webhook_server.rb +3 -3
- data/lib/parse/access.rb +493 -0
- data/lib/parse/acl_scope.rb +125 -52
- data/lib/parse/agent/approval_gate.rb +0 -0
- data/lib/parse/agent/cancellation_token.rb +3 -3
- data/lib/parse/agent/constraint_translator.rb +4 -4
- data/lib/parse/agent/describe.rb +34 -34
- data/lib/parse/agent/errors.rb +9 -9
- data/lib/parse/agent/mcp_client.rb +61 -59
- data/lib/parse/agent/mcp_dispatcher.rb +89 -101
- data/lib/parse/agent/mcp_rack_app.rb +92 -93
- data/lib/parse/agent/mcp_server.rb +1 -1
- data/lib/parse/agent/mcp_subscriptions.rb +22 -22
- data/lib/parse/agent/metadata_audit.rb +1 -2
- data/lib/parse/agent/metadata_dsl.rb +7 -7
- data/lib/parse/agent/metadata_registry.rb +12 -12
- data/lib/parse/agent/prompt_hardening.rb +4 -4
- data/lib/parse/agent/prompts.rb +10 -10
- data/lib/parse/agent/result_formatter.rb +4 -4
- data/lib/parse/agent/tools.rb +493 -400
- data/lib/parse/agent.rb +157 -116
- data/lib/parse/api/hooks.rb +1 -1
- data/lib/parse/api/server.rb +2 -2
- data/lib/parse/api/users.rb +2 -2
- data/lib/parse/atlas_search/index_manager.rb +1 -1
- data/lib/parse/atlas_search/session.rb +40 -218
- data/lib/parse/atlas_search.rb +181 -60
- data/lib/parse/authorization.rb +466 -0
- data/lib/parse/cache/invalidation.rb +167 -0
- data/lib/parse/cache/keyspace.rb +306 -0
- data/lib/parse/cache/moneta_surface.rb +126 -0
- data/lib/parse/cache/pool.rb +48 -5
- data/lib/parse/cache/redis.rb +425 -10
- data/lib/parse/cache/scoped_view.rb +443 -0
- data/lib/parse/cache/sub_cache.rb +264 -0
- data/lib/parse/cache/upstream_roles.rb +230 -0
- data/lib/parse/client/authentication.rb +1 -1
- data/lib/parse/client/body_builder.rb +1 -2
- data/lib/parse/client/caching.rb +84 -5
- data/lib/parse/client.rb +327 -189
- data/lib/parse/clp_scope.rb +225 -28
- data/lib/parse/console.rb +3 -3
- data/lib/parse/embeddings/batch_embedder.rb +3 -3
- data/lib/parse/embeddings/cache.rb +17 -17
- data/lib/parse/embeddings/cohere.rb +21 -22
- data/lib/parse/embeddings/image_fetch.rb +22 -21
- data/lib/parse/embeddings/jina.rb +21 -21
- data/lib/parse/embeddings/local_http.rb +6 -7
- data/lib/parse/embeddings/media_file.rb +5 -4
- data/lib/parse/embeddings/openai.rb +5 -5
- data/lib/parse/embeddings/provider.rb +1 -1
- data/lib/parse/embeddings/qwen.rb +10 -10
- data/lib/parse/embeddings/spend_cap.rb +2 -3
- data/lib/parse/embeddings/streaming_body.rb +11 -11
- data/lib/parse/embeddings/video_source.rb +7 -6
- data/lib/parse/embeddings/voyage.rb +59 -61
- data/lib/parse/embeddings.rb +28 -27
- data/lib/parse/graphql/type_generator.rb +1 -1
- data/lib/parse/graphql.rb +5 -5
- data/lib/parse/live_query/client.rb +1 -1
- data/lib/parse/live_query.rb +1 -1
- data/lib/parse/lock.rb +15 -18
- data/lib/parse/lock_backend.rb +1 -1
- data/lib/parse/lookup_rewriter.rb +6 -6
- data/lib/parse/model/classes/role.rb +227 -28
- data/lib/parse/model/classes/user.rb +60 -1
- data/lib/parse/model/clp.rb +4 -4
- data/lib/parse/model/core/actions.rb +97 -15
- data/lib/parse/model/core/create_lock.rb +0 -2
- data/lib/parse/model/core/describe.rb +57 -57
- data/lib/parse/model/core/embed_managed.rb +21 -23
- data/lib/parse/model/core/fetching.rb +2 -0
- data/lib/parse/model/core/indexing.rb +14 -14
- data/lib/parse/model/core/parse_reference.rb +1 -1
- data/lib/parse/model/core/properties.rb +2 -2
- data/lib/parse/model/core/querying.rb +1 -1
- data/lib/parse/model/core/schema.rb +2 -2
- data/lib/parse/model/core/search_indexing.rb +2 -2
- data/lib/parse/model/core/vector_searchable.rb +4 -5
- data/lib/parse/model/file.rb +21 -15
- data/lib/parse/model/geojson.rb +2 -2
- data/lib/parse/model/geopoint.rb +1 -0
- data/lib/parse/model/object.rb +132 -66
- data/lib/parse/model/pointer.rb +2 -0
- data/lib/parse/model/polygon.rb +3 -6
- data/lib/parse/model/push.rb +2 -2
- data/lib/parse/model/vector.rb +3 -1
- data/lib/parse/mongodb.rb +512 -282
- data/lib/parse/pipeline_security.rb +3 -0
- data/lib/parse/query/constraints.rb +46 -46
- data/lib/parse/query.rb +146 -59
- data/lib/parse/retrieval/agent_tool.rb +34 -20
- data/lib/parse/retrieval/chunk.rb +1 -0
- data/lib/parse/retrieval/reranker/cohere.rb +11 -11
- data/lib/parse/retrieval/reranker.rb +3 -4
- data/lib/parse/retrieval/retriever.rb +4 -6
- data/lib/parse/schema/index_migrator.rb +26 -26
- data/lib/parse/schema/search_index_migrator.rb +19 -19
- data/lib/parse/stack/tasks.rb +6 -6
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/stack.rb +10 -10
- data/lib/parse/vector_search/hybrid.rb +66 -41
- data/lib/parse/vector_search.rb +14 -11
- data/lib/parse/webhooks/payload.rb +1 -0
- data/lib/parse/webhooks/registration.rb +4 -4
- data/lib/parse/webhooks/trigger_audit.rb +50 -49
- data/lib/parse/webhooks.rb +22 -4
- data/parse-stack-next.gemspec +6 -6
- metadata +10 -1
|
@@ -79,19 +79,20 @@ module Parse
|
|
|
79
79
|
parts << "─── usage ───" << " #{usage}" if usage && usage.total_tokens.positive?
|
|
80
80
|
parts.join("\n")
|
|
81
81
|
end
|
|
82
|
+
|
|
82
83
|
alias_method :inspect, :to_s
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
DEFAULT_MODELS = {
|
|
86
|
-
openai:
|
|
87
|
+
openai: "gpt-4o-mini",
|
|
87
88
|
anthropic: "claude-haiku-4-5",
|
|
88
|
-
lmstudio:
|
|
89
|
+
lmstudio: "qwen2.5-7b-instruct",
|
|
89
90
|
}.freeze
|
|
90
91
|
|
|
91
92
|
DEFAULT_BASE_URLS = {
|
|
92
|
-
openai:
|
|
93
|
+
openai: "https://api.openai.com/v1",
|
|
93
94
|
anthropic: "https://api.anthropic.com/v1",
|
|
94
|
-
lmstudio:
|
|
95
|
+
lmstudio: "http://localhost:1234/v1",
|
|
95
96
|
}.freeze
|
|
96
97
|
|
|
97
98
|
# Per-1M-tokens list-price pricing (USD). Override via constructor's
|
|
@@ -99,13 +100,13 @@ module Parse
|
|
|
99
100
|
# Local-model providers (LM Studio) default to zero. Update these
|
|
100
101
|
# numbers as providers shift their pricing.
|
|
101
102
|
DEFAULT_PRICING = {
|
|
102
|
-
"gpt-4o-mini"
|
|
103
|
-
"gpt-4o"
|
|
104
|
-
"gpt-4.1-mini"
|
|
105
|
-
"gpt-4.1"
|
|
106
|
-
"claude-haiku-4-5"
|
|
107
|
-
"claude-sonnet-4-5"
|
|
108
|
-
"claude-opus-4-5"
|
|
103
|
+
"gpt-4o-mini" => { input: 0.15, output: 0.60 },
|
|
104
|
+
"gpt-4o" => { input: 2.50, output: 10.00 },
|
|
105
|
+
"gpt-4.1-mini" => { input: 0.40, output: 1.60 },
|
|
106
|
+
"gpt-4.1" => { input: 2.00, output: 8.00 },
|
|
107
|
+
"claude-haiku-4-5" => { input: 1.00, output: 5.00 },
|
|
108
|
+
"claude-sonnet-4-5" => { input: 3.00, output: 15.00 },
|
|
109
|
+
"claude-opus-4-5" => { input: 15.00, output: 75.00 },
|
|
109
110
|
}.freeze
|
|
110
111
|
|
|
111
112
|
# Token + cost roll-up. `cost_usd` is computed from the model's pricing
|
|
@@ -114,10 +115,10 @@ module Parse
|
|
|
114
115
|
Usage = Struct.new(:prompt_tokens, :completion_tokens, :total_tokens, :cost_usd, keyword_init: true) do
|
|
115
116
|
def +(other)
|
|
116
117
|
Usage.new(
|
|
117
|
-
prompt_tokens:
|
|
118
|
+
prompt_tokens: prompt_tokens + other.prompt_tokens,
|
|
118
119
|
completion_tokens: completion_tokens + other.completion_tokens,
|
|
119
|
-
total_tokens:
|
|
120
|
-
cost_usd:
|
|
120
|
+
total_tokens: total_tokens + other.total_tokens,
|
|
121
|
+
cost_usd: cost_usd + other.cost_usd,
|
|
121
122
|
)
|
|
122
123
|
end
|
|
123
124
|
|
|
@@ -125,6 +126,7 @@ module Parse
|
|
|
125
126
|
format("%d in + %d out = %d tokens $%.6f",
|
|
126
127
|
prompt_tokens, completion_tokens, total_tokens, cost_usd)
|
|
127
128
|
end
|
|
129
|
+
|
|
128
130
|
alias_method :inspect, :to_s
|
|
129
131
|
end
|
|
130
132
|
|
|
@@ -150,8 +152,8 @@ module Parse
|
|
|
150
152
|
def initialize(agent:, provider: nil, api_key: nil, model: nil, base_url: nil,
|
|
151
153
|
max_iterations: 8, timeout: 90, system_prompt: nil,
|
|
152
154
|
pricing: nil, auto_compact_at: nil)
|
|
153
|
-
@agent
|
|
154
|
-
@provider
|
|
155
|
+
@agent = agent
|
|
156
|
+
@provider = (provider || ENV["LLM_PROVIDER"])&.to_sym
|
|
155
157
|
raise ArgumentError, "provider required: pass provider: or set LLM_PROVIDER (one of: #{DEFAULT_MODELS.keys.join(", ")})" unless @provider
|
|
156
158
|
unless DEFAULT_MODELS.key?(@provider)
|
|
157
159
|
raise ArgumentError, "unknown provider #{@provider.inspect}; expected one of #{DEFAULT_MODELS.keys.inspect}"
|
|
@@ -163,19 +165,19 @@ module Parse
|
|
|
163
165
|
raise ArgumentError, "api_key required for #{@provider}: pass api_key: or set LLM_API_KEY"
|
|
164
166
|
end
|
|
165
167
|
|
|
166
|
-
@model
|
|
167
|
-
@base_url
|
|
168
|
+
@model = model || ENV["LLM_MODEL"] || DEFAULT_MODELS[@provider]
|
|
169
|
+
@base_url = base_url || ENV["LLM_BASE_URL"] || DEFAULT_BASE_URLS[@provider]
|
|
168
170
|
Parse::Agent.assert_llm_endpoint_allowed!(@base_url) if Parse::Agent.respond_to?(:assert_llm_endpoint_allowed!)
|
|
169
|
-
@max_iterations
|
|
170
|
-
@timeout
|
|
171
|
-
@system_prompt
|
|
172
|
-
@pricing
|
|
171
|
+
@max_iterations = max_iterations
|
|
172
|
+
@timeout = timeout
|
|
173
|
+
@system_prompt = system_prompt
|
|
174
|
+
@pricing = pricing || DEFAULT_PRICING
|
|
173
175
|
# When set, the round-trip will trigger compact! after a successful
|
|
174
176
|
# call if `usage.total_tokens` exceeds this threshold. Useful for
|
|
175
177
|
# long-running chat sessions to avoid blowing past context limits.
|
|
176
178
|
@auto_compact_at = auto_compact_at
|
|
177
|
-
@history
|
|
178
|
-
@usage
|
|
179
|
+
@history = []
|
|
180
|
+
@usage = ZERO_USAGE.dup
|
|
179
181
|
@last_call_usage = nil
|
|
180
182
|
end
|
|
181
183
|
|
|
@@ -225,12 +227,12 @@ module Parse
|
|
|
225
227
|
# can re-price after the fact with a different rate table.
|
|
226
228
|
def price(prompt_tokens, completion_tokens)
|
|
227
229
|
rates = @pricing[@model] || @pricing[@model.to_s] || { input: 0.0, output: 0.0 }
|
|
228
|
-
cost
|
|
230
|
+
cost = (prompt_tokens * rates[:input] + completion_tokens * rates[:output]) / 1_000_000.0
|
|
229
231
|
Usage.new(
|
|
230
|
-
prompt_tokens:
|
|
232
|
+
prompt_tokens: prompt_tokens,
|
|
231
233
|
completion_tokens: completion_tokens,
|
|
232
|
-
total_tokens:
|
|
233
|
-
cost_usd:
|
|
234
|
+
total_tokens: prompt_tokens + completion_tokens,
|
|
235
|
+
cost_usd: cost,
|
|
234
236
|
)
|
|
235
237
|
end
|
|
236
238
|
|
|
@@ -283,7 +285,7 @@ module Parse
|
|
|
283
285
|
unless entry.is_a?(Hash)
|
|
284
286
|
raise ArgumentError, "restore_history!: entry #{i} is not a Hash (got #{entry.class})"
|
|
285
287
|
end
|
|
286
|
-
role
|
|
288
|
+
role = entry[:role] || entry["role"]
|
|
287
289
|
content = entry[:content] || entry["content"]
|
|
288
290
|
if role.to_s.empty?
|
|
289
291
|
raise ArgumentError, "restore_history!: entry #{i} is missing :role"
|
|
@@ -314,7 +316,7 @@ module Parse
|
|
|
314
316
|
# if tool lists grow large, but they're usually small).
|
|
315
317
|
def tool_definitions
|
|
316
318
|
envelope = Parse::Agent::MCPDispatcher.call(
|
|
317
|
-
body:
|
|
319
|
+
body: { "jsonrpc" => "2.0", "id" => SecureRandom.hex(4), "method" => "tools/list", "params" => {} },
|
|
318
320
|
agent: @agent,
|
|
319
321
|
)
|
|
320
322
|
tools = envelope.dig(:body, "result", "tools") || []
|
|
@@ -323,9 +325,9 @@ module Parse
|
|
|
323
325
|
{
|
|
324
326
|
type: "function",
|
|
325
327
|
function: {
|
|
326
|
-
name:
|
|
328
|
+
name: h["name"],
|
|
327
329
|
description: h["description"].to_s[0, 1024],
|
|
328
|
-
parameters:
|
|
330
|
+
parameters: h["inputSchema"] || { "type" => "object", "properties" => {} },
|
|
329
331
|
},
|
|
330
332
|
}
|
|
331
333
|
end
|
|
@@ -336,10 +338,10 @@ module Parse
|
|
|
336
338
|
# a Result with the final-turn text, the ordered tool-call trace, and
|
|
337
339
|
# the full transcript for debugging.
|
|
338
340
|
def round_trip
|
|
339
|
-
tools
|
|
340
|
-
messages
|
|
341
|
+
tools = tool_definitions
|
|
342
|
+
messages = build_messages_for_provider
|
|
341
343
|
transcript = []
|
|
342
|
-
all_calls
|
|
344
|
+
all_calls = []
|
|
343
345
|
call_usage = ZERO_USAGE.dup
|
|
344
346
|
|
|
345
347
|
@max_iterations.times do
|
|
@@ -354,18 +356,18 @@ module Parse
|
|
|
354
356
|
dispatch_envelope = Parse::Agent::MCPDispatcher.call(
|
|
355
357
|
body: {
|
|
356
358
|
"jsonrpc" => "2.0",
|
|
357
|
-
"id"
|
|
358
|
-
"method"
|
|
359
|
-
"params"
|
|
359
|
+
"id" => SecureRandom.hex(4),
|
|
360
|
+
"method" => "tools/call",
|
|
361
|
+
"params" => { "name" => tc[:name], "arguments" => tc[:arguments] },
|
|
360
362
|
},
|
|
361
363
|
agent: @agent,
|
|
362
364
|
)
|
|
363
365
|
body = dispatch_envelope[:body] || {}
|
|
364
366
|
tool_text = if body["result"]
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
367
|
+
(body.dig("result", "content", 0, "text") || body["result"].to_json)
|
|
368
|
+
else
|
|
369
|
+
body.dig("error", "message").to_s
|
|
370
|
+
end
|
|
369
371
|
all_calls << { name: tc[:name], arguments: tc[:arguments], result: tool_text }
|
|
370
372
|
messages << { role: "tool", tool_call_id: tc[:id], content: tool_text }
|
|
371
373
|
transcript << { role: "tool", content: tool_text }
|
|
@@ -375,7 +377,7 @@ module Parse
|
|
|
375
377
|
# The assistant's last content message is the answer. Walk the
|
|
376
378
|
# transcript backwards to find it.
|
|
377
379
|
final = transcript.reverse.find { |m| m[:role] == "assistant" && !m[:content].to_s.empty? }
|
|
378
|
-
text
|
|
380
|
+
text = final ? final[:content].to_s : ""
|
|
379
381
|
|
|
380
382
|
# Append the assistant's final message to history so a follow-up
|
|
381
383
|
# `ask(..., reset: false)` sees the prior context.
|
|
@@ -384,7 +386,7 @@ module Parse
|
|
|
384
386
|
end
|
|
385
387
|
|
|
386
388
|
@last_call_usage = call_usage
|
|
387
|
-
@usage
|
|
389
|
+
@usage = @usage + call_usage
|
|
388
390
|
|
|
389
391
|
# Auto-compact when configured and we've crossed the threshold. The
|
|
390
392
|
# compact call itself adds usage; that's reflected in @usage too.
|
|
@@ -408,7 +410,7 @@ module Parse
|
|
|
408
410
|
def call_llm(messages:, tools:)
|
|
409
411
|
case @provider
|
|
410
412
|
when :anthropic then anthropic_chat(messages: messages, tools: tools)
|
|
411
|
-
else
|
|
413
|
+
else openai_chat(messages: messages, tools: tools)
|
|
412
414
|
end
|
|
413
415
|
end
|
|
414
416
|
|
|
@@ -438,20 +440,20 @@ module Parse
|
|
|
438
440
|
body = JSON.generate({ model: @model, messages: openai_messages, tools: tools, tool_choice: "auto" })
|
|
439
441
|
|
|
440
442
|
req = Net::HTTP::Post.new(uri)
|
|
441
|
-
req["Content-Type"]
|
|
443
|
+
req["Content-Type"] = "application/json"
|
|
442
444
|
req["Authorization"] = "Bearer #{@api_key}"
|
|
443
445
|
req.body = body
|
|
444
446
|
|
|
445
447
|
res = Net::HTTP.start(uri.hostname, uri.port,
|
|
446
|
-
use_ssl:
|
|
448
|
+
use_ssl: uri.scheme == "https",
|
|
447
449
|
read_timeout: @timeout) { |h| h.request(req) }
|
|
448
450
|
unless res.code.to_i.between?(200, 299)
|
|
449
451
|
raise "LLM call failed: HTTP #{res.code} #{res.body}"
|
|
450
452
|
end
|
|
451
453
|
|
|
452
454
|
parsed = JSON.parse(res.body)
|
|
453
|
-
msg
|
|
454
|
-
calls
|
|
455
|
+
msg = parsed.dig("choices", 0, "message") || {}
|
|
456
|
+
calls = Array(msg["tool_calls"]).map do |tc|
|
|
455
457
|
args = tc.dig("function", "arguments")
|
|
456
458
|
# Defensively normalize to a Hash. OpenAI returns a JSON-encoded
|
|
457
459
|
# String here; some models occasionally emit an empty string when
|
|
@@ -466,15 +468,15 @@ module Parse
|
|
|
466
468
|
{ id: tc["id"] || SecureRandom.hex(4), name: tc.dig("function", "name"), arguments: args }
|
|
467
469
|
end
|
|
468
470
|
usage_h = parsed["usage"] || {}
|
|
469
|
-
usage
|
|
471
|
+
usage = price(usage_h["prompt_tokens"].to_i, usage_h["completion_tokens"].to_i)
|
|
470
472
|
{ role: "assistant", content: msg["content"], tool_calls: calls, usage: usage }
|
|
471
473
|
end
|
|
472
474
|
|
|
473
475
|
def anthropic_chat(messages:, tools:)
|
|
474
476
|
anth_tools = tools.map do |t|
|
|
475
477
|
{
|
|
476
|
-
name:
|
|
477
|
-
description:
|
|
478
|
+
name: t[:function][:name],
|
|
479
|
+
description: t[:function][:description],
|
|
478
480
|
input_schema: t[:function][:parameters],
|
|
479
481
|
}
|
|
480
482
|
end
|
|
@@ -487,13 +489,13 @@ module Parse
|
|
|
487
489
|
body = JSON.generate(request_body)
|
|
488
490
|
|
|
489
491
|
req = Net::HTTP::Post.new(uri)
|
|
490
|
-
req["Content-Type"]
|
|
491
|
-
req["x-api-key"]
|
|
492
|
+
req["Content-Type"] = "application/json"
|
|
493
|
+
req["x-api-key"] = @api_key
|
|
492
494
|
req["anthropic-version"] = "2023-06-01"
|
|
493
495
|
req.body = body
|
|
494
496
|
|
|
495
497
|
res = Net::HTTP.start(uri.hostname, uri.port,
|
|
496
|
-
use_ssl:
|
|
498
|
+
use_ssl: uri.scheme == "https",
|
|
497
499
|
read_timeout: @timeout) { |h| h.request(req) }
|
|
498
500
|
unless res.code.to_i.between?(200, 299)
|
|
499
501
|
raise "Anthropic call failed: HTTP #{res.code} #{res.body}"
|
|
@@ -501,13 +503,13 @@ module Parse
|
|
|
501
503
|
|
|
502
504
|
parsed = JSON.parse(res.body)
|
|
503
505
|
blocks = Array(parsed["content"])
|
|
504
|
-
text
|
|
505
|
-
calls
|
|
506
|
+
text = blocks.select { |b| b["type"] == "text" }.map { |b| b["text"] }.join("\n")
|
|
507
|
+
calls = blocks.select { |b| b["type"] == "tool_use" }.map do |b|
|
|
506
508
|
{ id: b["id"], name: b["name"], arguments: b["input"] || {} }
|
|
507
509
|
end
|
|
508
510
|
usage_h = parsed["usage"] || {}
|
|
509
511
|
# Anthropic returns input_tokens / output_tokens (not prompt/completion).
|
|
510
|
-
usage
|
|
512
|
+
usage = price(usage_h["input_tokens"].to_i, usage_h["output_tokens"].to_i)
|
|
511
513
|
{ role: "assistant", content: text, tool_calls: calls, usage: usage }
|
|
512
514
|
end
|
|
513
515
|
|
|
@@ -555,7 +557,7 @@ module Parse
|
|
|
555
557
|
messages.map do |m|
|
|
556
558
|
case m[:role]
|
|
557
559
|
when "user", "assistant" then { role: m[:role], content: m[:content].to_s }
|
|
558
|
-
when "system"
|
|
560
|
+
when "system" then { role: "user", content: "[Context] #{m[:content]}" }
|
|
559
561
|
when "tool"
|
|
560
562
|
{ role: "user", content: [{ type: "tool_result", tool_use_id: m[:tool_call_id], content: wrap_tool_content_for_llm(m[:content]) }] }
|
|
561
563
|
end
|