pwn 0.5.755 → 0.5.756
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/pwn/ai/anthropic.rb +23 -10
- data/lib/pwn/version.rb +1 -1
- data/spec/lib/pwn/ai/anthropic_spec.rb +27 -0
- data/spec/lib/pwn/plugins/binary_parser_spec.rb +11 -2
- 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: 7fac5f5a9f5b01e9c4ce830db72a7e454f1a2dce37b44e57f98e290c154cfbbf
|
|
4
|
+
data.tar.gz: 1c508a671f0eb88f704bd38e58b7f3ad7154eca3e0e8d6b4d62852c7bf217a1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 311ad0755488c66ad2c92c10751c52c5b86b4b725aae976fa697b0f206fb3c88afc954371d8ec8628fcc9431449fe58b71948f4ec2e3ad73b189e88165bcf3b4
|
|
7
|
+
data.tar.gz: e0055f29efafc74e550e84fd504debb71400acbfaf768eb1be82caa1d57cc1d79d49addfb78bb31608fdd0fd2716a4f68277b0d88272960e6b4dffb990b829f9
|
data/lib/pwn/ai/anthropic.rb
CHANGED
|
@@ -578,7 +578,7 @@ module PWN
|
|
|
578
578
|
input_schema: anthropic_input_schema(schema: fn[:parameters] || fn['parameters'])
|
|
579
579
|
}
|
|
580
580
|
end
|
|
581
|
-
http_body[:tool_choice] = anth_tool_choice(choice: opts[:tool_choice]) if opts[:tool_choice]
|
|
581
|
+
http_body[:tool_choice] = anth_tool_choice(choice: opts[:tool_choice], model: model) if opts[:tool_choice]
|
|
582
582
|
end
|
|
583
583
|
|
|
584
584
|
response = anthropic_rest_call(
|
|
@@ -794,15 +794,28 @@ module PWN
|
|
|
794
794
|
|
|
795
795
|
private_class_method def self.anth_tool_choice(opts = {})
|
|
796
796
|
choice = opts[:choice]
|
|
797
|
-
case choice
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
797
|
+
mapped = case choice
|
|
798
|
+
when 'none', :none then { type: 'none' }
|
|
799
|
+
when 'required', :required, 'any', :any then { type: 'any' }
|
|
800
|
+
when Hash
|
|
801
|
+
fn = choice[:function] || choice['function'] || choice
|
|
802
|
+
{ type: 'tool', name: fn[:name] || fn['name'] }
|
|
803
|
+
else
|
|
804
|
+
{ type: 'auto' }
|
|
805
|
+
end
|
|
806
|
+
return { type: 'auto' } if !forced_tool_choice?(model: opts[:model]) && %w[any tool].include?(mapped[:type].to_s)
|
|
807
|
+
|
|
808
|
+
mapped
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
# Claude Mythos 5.1, Fable 5.1, Opus 5.5, and Sonnet 5.5 reject forced tool use.
|
|
812
|
+
# Mythos 5 still accepts type any/tool, so match the documented ids, not every Mythos model.
|
|
813
|
+
private_class_method def self.forced_tool_choice?(opts = {})
|
|
814
|
+
model = opts[:model].to_s
|
|
815
|
+
return false if model.match?(/claude-(?:mythos|fable)-5-1(?:-|\z)/i)
|
|
816
|
+
return false if model.match?(/claude-(?:opus|sonnet)-5-5(?:-|\z)/i)
|
|
817
|
+
|
|
818
|
+
true
|
|
806
819
|
end
|
|
807
820
|
|
|
808
821
|
# Supported Method Parameters::
|
data/lib/pwn/version.rb
CHANGED
|
@@ -106,4 +106,31 @@ describe PWN::AI::Anthropic do
|
|
|
106
106
|
expect(flat[:properties].keys.map(&:to_s)).to include('handle', 'path')
|
|
107
107
|
expect(flat[:description]).to include('handle', 'path')
|
|
108
108
|
end
|
|
109
|
+
|
|
110
|
+
it 'does not force tool use on Anthropic models that reject tool_choice any and tool' do
|
|
111
|
+
allow(PWN::Env).to receive(:[]).with(:ai).and_return(
|
|
112
|
+
anthropic: { key: 'test-key', model: 'claude-mythos-5', temp: 1, max_tokens: 100 }
|
|
113
|
+
)
|
|
114
|
+
allow(PWN::AI::Agent::PromptCache).to receive(:enabled?).and_return(false) if defined?(PWN::AI::Agent::PromptCache)
|
|
115
|
+
captured = nil
|
|
116
|
+
allow(described_class).to receive(:anthropic_rest_call) do |opts|
|
|
117
|
+
captured = opts[:http_body]
|
|
118
|
+
'{"id":"msg_choice","type":"message","role":"assistant","content":[{"type":"text","text":"ok"}],"stop_reason":"end_turn"}'
|
|
119
|
+
end
|
|
120
|
+
tools = [{ type: 'function', function: { name: 'memory_recall', description: 'recall', parameters: { type: 'object', properties: {} } } }]
|
|
121
|
+
messages = [{ role: 'user', content: 'hello' }]
|
|
122
|
+
|
|
123
|
+
described_class.chat_with_tools(messages: messages, tools: tools, model: 'claude-mythos-5', tool_choice: 'required')
|
|
124
|
+
expect(captured[:tool_choice]).to eq(type: 'any')
|
|
125
|
+
|
|
126
|
+
%w[claude-mythos-5-1 claude-fable-5-1 claude-opus-5-5 claude-sonnet-5-5].each do |model|
|
|
127
|
+
described_class.chat_with_tools(messages: messages, tools: tools, model: model, tool_choice: 'required')
|
|
128
|
+
expect(captured[:tool_choice]).to eq(type: 'auto'), model
|
|
129
|
+
described_class.chat_with_tools(messages: messages, tools: tools, model: model, tool_choice: { type: 'function', function: { name: 'memory_recall' } })
|
|
130
|
+
expect(captured[:tool_choice]).to eq(type: 'auto'), model
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
described_class.chat_with_tools(messages: messages, tools: tools, model: 'claude-mythos-5-1', tool_choice: 'none')
|
|
134
|
+
expect(captured[:tool_choice]).to eq(type: 'none')
|
|
135
|
+
end
|
|
109
136
|
end
|
|
@@ -91,13 +91,22 @@ describe PWN::Plugins::BinaryParser do
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
it 'elf_resolve points system at the .plt.sec stub, not the next slot' do
|
|
94
|
+
x86_64 = RbConfig::CONFIG['host_cpu'].to_s.match?(/amd64|x86_64/)
|
|
94
95
|
Dir.mktmpdir('pwn-elf-ibt-') do |dir|
|
|
95
96
|
src = File.join(dir, 't.c')
|
|
96
97
|
path = File.join(dir, 't')
|
|
97
98
|
File.write(src, "#include <stdlib.h>\nint main(void) { return system(\"x\"); }\n")
|
|
98
|
-
|
|
99
|
+
flags = ['-O0', '-fno-pie', '-no-pie']
|
|
100
|
+
flags << '-Wl,-z,ibt' if x86_64
|
|
101
|
+
out, status = Open3.capture2e('cc', *flags, '-o', path, src)
|
|
99
102
|
expect(status.success?).to eq(true), out
|
|
100
|
-
|
|
103
|
+
binary = File.binread(path)
|
|
104
|
+
unless x86_64
|
|
105
|
+
expect(binary).not_to include('.plt.sec')
|
|
106
|
+
next
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
expect(binary).to include('.plt.sec')
|
|
101
110
|
row = described_class.elf_resolve(path: path)
|
|
102
111
|
dump, dump_status = Open3.capture2('objdump', '-d', path)
|
|
103
112
|
expect(dump_status.success?).to eq(true), dump
|