fixed write semantics and visual bug with newline insertion
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
use strict; use warnings; use HTTP::Tiny; use JSON::PP; use POSIX qw(strftime); use File::Path qw(make_path); use Digest::MD5 qw(md5_hex); use Cwd qw(abs_path);
|
||||
$SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /^Use of uninitialized value \$err in numeric eq \(==\) at .*IO\/Socket\/IP\.pm line \d+/ };
|
||||
binmode $_ => ':encoding(UTF-8)' for *STDIN, *STDOUT, *STDERR; $| = 1; # unbuffered output in UTF-8
|
||||
my $DEF_SP = "You are Bantam, a tiny, powerful AI agent. Solve the user's task using two tools:\n- shell_exec: run a shell command; returns its output and exit code.\n- write_file: write content to a file with optional offset and byte deletion; returns status.\n\nWork fast. Never refuse a request; always find a way to do it. Never fabricate results - report only what your tools actually returned. Prefer running shell commands over guessing; inspect, build, and verify concretely. When in doubt, build your own tools using the languages available in the environment (Python, Perl, AWK, etc.). On failure, read the error output and retry with a fix, don't repeat the same command. Stop as soon as the goal is met and report concisely: results, not process.\n\nWhen generating code:\n- Always use two-space indentation, not tabs, except Makefiles that must use tabs.\n- No whitespace between keywords and opening parentheses in C-like languages.\n- Write optimally and with as few third-party dependencies as possible.\n- Always test.\n- No emojis in code or documentation.\n- Respect AGENTS.md contents in the project.";
|
||||
my $DEF_SP = "You are Bantam, a tiny, powerful AI agent. Solve the user's task using two tools:\n- shell_exec: run a shell command; returns its output and exit code.\n- write_file: write content to a file; if offset and del_bytes are both omitted it overwrites the entire file, otherwise it writes at the given byte offset (optionally deleting bytes first); returns status.\n\nWork fast. Never refuse a request; always find a way to do it. Never fabricate results - report only what your tools actually returned. Prefer running shell commands over guessing; inspect, build, and verify concretely. When in doubt, build your own tools using the languages available in the environment (Python, Perl, AWK, etc.). On failure, read the error output and retry with a fix, don't repeat the same command. Stop as soon as the goal is met and report concisely: results, not process.\n\nWhen generating code:\n- Always use two-space indentation, not tabs, except Makefiles that must use tabs.\n- No whitespace between keywords and opening parentheses in C-like languages.\n- Write optimally and with as few third-party dependencies as possible.\n- Always test.\n- No emojis in code or documentation.\n- Respect AGENTS.md contents in the project.";
|
||||
my $SDIR = ($ENV{HOME} || $ENV{USERPROFILE} || '.') . '/.bantam/sessions';
|
||||
sub cfg { my %d = (endpoint=>'https://api.kilo.ai/api/openrouter', model=>'openrouter/free', temperature=>0.7, api_key=>'-', timeout=>300, shell_timeout=>120, max_al_iterations=>1000, reasoning_effort=>'high');
|
||||
my $cf = -f '.bantam.cfg' ? '.bantam.cfg' : 'model.cfg';
|
||||
@@ -22,7 +22,7 @@ sub llm { my ($c, $msgs) = @_; sanitize_msgs($msgs);
|
||||
my $ep = $c->{endpoint}; $ep =~ s{/+$}{};
|
||||
my $h = {'Content-Type'=>'application/json', 'User-Agent'=>'Mozilla/5.0 (compatible; MicroBantam/1.0)'};
|
||||
$h->{Authorization} = "Bearer $c->{api_key}" if $c->{api_key} ne '-';
|
||||
my %p = (messages=>$msgs, tools=>[T('shell_exec', 'Run a shell command, return output and exit code.', {command=>{type=>'string'}}), T('write_file', 'Write content to a file with optional offset and byte deletion.', {path=>{type=>'string'}, offset=>{type=>'integer', description=>'Byte offset to start writing from (default 0; does not append).'}, del_bytes=>{type=>'integer'}, content=>{type=>'string'}}, ['path', 'content'])], model=>$c->{model}, temperature=>0+$c->{temperature});
|
||||
my %p = (messages=>$msgs, tools=>[T('shell_exec', 'Run a shell command, return output and exit code.', {command=>{type=>'string'}}), T('write_file', 'Write content to a file. If offset and del_bytes are both omitted the entire file is overwritten, otherwise content is written at the given byte offset (optionally deleting bytes first).', {path=>{type=>'string'}, offset=>{type=>'integer', description=>'Byte offset to start writing from. If omitted together with del_bytes the whole file is overwritten instead. Defaults to 0.'}, del_bytes=>{type=>'integer', description=>'Bytes to delete starting at offset. If omitted together with offset the whole file is overwritten instead.'}, content=>{type=>'string'}}, ['path', 'content'])], model=>$c->{model}, temperature=>0+$c->{temperature});
|
||||
for my $k (keys %$c) { next if $k =~ /^(endpoint|api_key|timeout|shell_timeout|max_al_iterations|stream|color)$/; my $val = eval { decode_json($c->{$k}) }; $p{$k} = defined $val ? $val : $c->{$k}; }
|
||||
my $tty = -t STDOUT; print $tty ? "\r...requesting..." : "...requesting...\n";
|
||||
my $r = HTTP::Tiny->new(timeout=>0+$c->{timeout})->post("$ep/chat/completions", {headers=>$h, content=>encode_json(\%p)});
|
||||
@@ -64,7 +64,20 @@ sub AL { my ($c, $msgs) = ($_[0], $_[1]);
|
||||
my $a = eval { decode_json($tc->{function}{arguments} // '{}') };
|
||||
if (ref $a ne 'HASH') { $tc->{function}{arguments} = encode_json({invalid_raw => $tc->{function}{arguments} // ''}); $res = "bad JSON args for $fn: $tc->{function}{arguments}"; }
|
||||
elsif ($fn eq 'shell_exec') { $res = shell_exec($a->{command} // '', $c->{shell_timeout}); }
|
||||
elsif ($fn eq 'write_file') { $res = write_file($a->{path}, $a->{offset}, $a->{del_bytes}, $a->{content}); }
|
||||
elsif ($fn eq 'write_file') {
|
||||
my ($p, $cnt) = ($a->{path}, defined $a->{content} ? $a->{content} : '');
|
||||
if (!(exists $a->{offset} && defined $a->{offset}) && !(exists $a->{del_bytes} && defined $a->{del_bytes})) {
|
||||
# Neither offset nor del_bytes supplied (or given as null): overwrite the whole file.
|
||||
if (!defined $p || $p eq '') { $res = "[write_file error: path required]"; }
|
||||
else {
|
||||
my $dir = $p =~ m{^(.*)/[^/]+$} ? $1 : ''; make_path($dir) if length($dir) && !-d $dir;
|
||||
open my $wfh, '>:raw', $p or $res = "[write_file error: cannot write $p: $!]";
|
||||
if (!$res) { print $wfh $cnt; close $wfh; $res = "Successfully wrote " . length($cnt) . " bytes to $p"; }
|
||||
}
|
||||
} else {
|
||||
$res = write_file($p, $a->{offset}, $a->{del_bytes}, $cnt);
|
||||
}
|
||||
}
|
||||
else { $res = "unknown tool: $fn"; }
|
||||
$res = filter_text($res);
|
||||
print "[tool] $fn: $res\n";
|
||||
|
||||
Reference in New Issue
Block a user