brute 0.1.6 → 0.1.7
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/lib/brute/middleware/tool_use_guard.rb +54 -0
- data/lib/brute/orchestrator.rb +3 -0
- data/lib/brute/version.rb +1 -1
- data/lib/brute.rb +1 -0
- metadata +12 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f0f32487b029541fdb462f5f4958a95e4727150911b51f6d8ab457b875187d8
|
|
4
|
+
data.tar.gz: f162d75e227b4270e4a56dba42fe3cdddd23f9492adeff5cd1f345cbbb811961
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9aa172f042960dc5c9ec3250cf27781614077be7db114edc65d4ce72e178dfcab9ed9a9918d6583ed190d36cc54e865d8118bea9ac9814064a12a3f2b7ac8627
|
|
7
|
+
data.tar.gz: c0cc5addf257b161cc06385417a24833bf9f99a73038040abc67c3caef75ab0a0b9e3d357364b0fa0d194ca3b259555b80f09bf805fc12d17be1e4386d8b061b
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Brute
|
|
4
|
+
module Middleware
|
|
5
|
+
# Guards against tool-only LLM responses where the assistant message
|
|
6
|
+
# is dropped from the context buffer.
|
|
7
|
+
#
|
|
8
|
+
# When the LLM responds with only tool_use blocks (no text), llm.rb's
|
|
9
|
+
# response adapter produces empty choices. Context#talk appends nil,
|
|
10
|
+
# BufferNilGuard strips it, and the assistant message carrying tool_use
|
|
11
|
+
# blocks is lost. This causes "unexpected tool_use_id" on the next call
|
|
12
|
+
# because tool_result references a tool_use that's missing from the buffer.
|
|
13
|
+
#
|
|
14
|
+
# This middleware runs post-call and injects a synthetic assistant message
|
|
15
|
+
# when tool calls exist but no assistant message was recorded.
|
|
16
|
+
class ToolUseGuard
|
|
17
|
+
def initialize(app)
|
|
18
|
+
@app = app
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call(env)
|
|
22
|
+
response = @app.call(env)
|
|
23
|
+
|
|
24
|
+
ctx = env[:context]
|
|
25
|
+
functions = ctx.functions
|
|
26
|
+
|
|
27
|
+
# If there are pending tool calls, ensure the buffer has an assistant
|
|
28
|
+
# message with tool_use blocks.
|
|
29
|
+
if functions && !functions.empty?
|
|
30
|
+
messages = ctx.messages.to_a
|
|
31
|
+
last_assistant = messages.reverse.find { |m| m.role.to_s == "assistant" }
|
|
32
|
+
|
|
33
|
+
unless last_assistant&.tool_call?
|
|
34
|
+
# Build a synthetic assistant message with the tool_use data
|
|
35
|
+
tool_calls = functions.map do |fn|
|
|
36
|
+
LLM::Object.from(id: fn.id, name: fn.name, arguments: fn.arguments)
|
|
37
|
+
end
|
|
38
|
+
original_tool_calls = functions.map do |fn|
|
|
39
|
+
{ "type" => "tool_use", "id" => fn.id, "name" => fn.name, "input" => fn.arguments || {} }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
synthetic = LLM::Message.new(:assistant, "", {
|
|
43
|
+
tool_calls: tool_calls,
|
|
44
|
+
original_tool_calls: original_tool_calls,
|
|
45
|
+
})
|
|
46
|
+
ctx.messages.concat([synthetic])
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
response
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/brute/orchestrator.rb
CHANGED
|
@@ -181,6 +181,9 @@ module Brute
|
|
|
181
181
|
# Handle reasoning params and model-switch normalization (pre-call)
|
|
182
182
|
use Middleware::ReasoningNormalizer, **reasoning unless reasoning.empty?
|
|
183
183
|
|
|
184
|
+
# Guard against tool-only responses dropping the assistant message
|
|
185
|
+
use Middleware::ToolUseGuard
|
|
186
|
+
|
|
184
187
|
# Innermost: the actual LLM call
|
|
185
188
|
run Middleware::LLMCall.new
|
|
186
189
|
end
|
data/lib/brute/version.rb
CHANGED
data/lib/brute.rb
CHANGED
|
@@ -47,6 +47,7 @@ require_relative 'brute/middleware/session_persistence'
|
|
|
47
47
|
require_relative 'brute/middleware/tracing'
|
|
48
48
|
require_relative 'brute/middleware/tool_error_tracking'
|
|
49
49
|
require_relative 'brute/middleware/reasoning_normalizer'
|
|
50
|
+
require_relative "brute/middleware/tool_use_guard"
|
|
50
51
|
|
|
51
52
|
# Tools
|
|
52
53
|
require_relative 'brute/tools/fs_read'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brute
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brute Contributors
|
|
@@ -27,16 +27,16 @@ dependencies:
|
|
|
27
27
|
name: diff-lcs
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
32
|
+
version: '1.5'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
39
|
+
version: '1.5'
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: llm.rb
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -52,33 +52,33 @@ dependencies:
|
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '4.11'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name:
|
|
55
|
+
name: rake
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '13.0'
|
|
61
61
|
type: :development
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
67
|
+
version: '13.0'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name:
|
|
69
|
+
name: rspec
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '13
|
|
74
|
+
version: '3.13'
|
|
75
75
|
type: :development
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '13
|
|
81
|
+
version: '3.13'
|
|
82
82
|
description: Production-grade coding agent with tool execution, middleware pipeline,
|
|
83
83
|
context compaction, session persistence, and multi-provider LLM support.
|
|
84
84
|
executables: []
|
|
@@ -101,6 +101,7 @@ files:
|
|
|
101
101
|
- lib/brute/middleware/session_persistence.rb
|
|
102
102
|
- lib/brute/middleware/token_tracking.rb
|
|
103
103
|
- lib/brute/middleware/tool_error_tracking.rb
|
|
104
|
+
- lib/brute/middleware/tool_use_guard.rb
|
|
104
105
|
- lib/brute/middleware/tracing.rb
|
|
105
106
|
- lib/brute/orchestrator.rb
|
|
106
107
|
- lib/brute/patches/anthropic_tool_role.rb
|