From 4a936e850d58a587402420259f2073b0de8faf8c Mon Sep 17 00:00:00 2001 From: Luxferre Date: Tue, 18 Aug 2026 09:28:24 +0300 Subject: [PATCH] some sloc optimizations --- main.go | 110 ++++++++++++++++++++++---------------------------------- mb | 105 +++++++++++++++++------------------------------------ 2 files changed, 76 insertions(+), 139 deletions(-) diff --git a/main.go b/main.go index 958e4fd..4f6aac3 100644 --- a/main.go +++ b/main.go @@ -703,12 +703,7 @@ type streamDelta struct { func cleanMessagesForLLM(msgs []Message) []Message { out := make([]Message, len(msgs)) for i, m := range msgs { - out[i] = Message{ - Role: m.Role, - Content: m.Content, - ToolCalls: m.ToolCalls, - ToolCallID: m.ToolCallID, - } + out[i] = Message{Role: m.Role, Content: m.Content, ToolCalls: m.ToolCalls, ToolCallID: m.ToolCallID} } return out } @@ -719,9 +714,7 @@ func filterText(s string) string { for _, r := range s { if r == ' ' || r == '\t' || r == '\n' { b.WriteRune(r) - } else if unicode.Is(unicode.Z, r) || unicode.IsControl(r) || unicode.Is(unicode.C, r) { - continue - } else if unicode.IsPrint(r) { + } else if !unicode.Is(unicode.Z, r) && !unicode.IsControl(r) && !unicode.Is(unicode.C, r) && unicode.IsPrint(r) { b.WriteRune(r) } } @@ -1430,6 +1423,42 @@ func readLine(prompt string) (string, bool) { } } +func runDirectShell(cmd string, timeout int) { + cmd = filterText(strings.TrimSpace(cmd)) + if cmd == "" { return } + astr, _ := json.Marshal(map[string]string{"command": cmd}) + fmt.Println(c(fmt.Sprintf("[tool call: shell_exec(%s)]", string(astr)), 33)) + sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + res := shell(sigCtx, cmd, timeout) + cancel() + fmt.Println(c("[tool result: shell_exec]", 32) + "\n" + c(res, 2)) +} + +func doCompact(cfg *Cfg, msgs []Message) []Message { + if len(msgs) <= 1 { + fmt.Println(c("Nothing to compact yet.", 33)) + return msgs + } + fmt.Println(c("[compacting conversation...]", 33)) + sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) + nm, sm, err := compact(sigCtx, cfg, msgs) + interrupted := sigCtx.Err() != nil + cancel() + if err != nil { + if interrupted || errors.Is(err, context.Canceled) { + fmt.Println(c("\n[interrupted]", 33)) + } else { + fmt.Println(c("[compact failed: "+err.Error()+"]", 31)) + } + return msgs + } + msgs = nm + autosave(msgs) + fmt.Println(c(fmt.Sprintf("[compacted to %d messages]", len(msgs)), 32)) + fmt.Println(c("--- summary ---", 33) + "\n" + c(sm, 2)) + return msgs +} + func main() { sp := prompt("system.txt") cfg := getCfg("model.cfg") @@ -1446,15 +1475,7 @@ func main() { } u := strings.TrimSpace(string(data)) if strings.HasPrefix(u, "!") { - cmd := filterText(strings.TrimSpace(strings.TrimPrefix(u, "!"))) - if cmd != "" { - astr, _ := json.Marshal(map[string]string{"command": cmd}) - fmt.Println(c(fmt.Sprintf("[tool call: shell_exec(%s)]", string(astr)), 33)) - sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) - res := shell(sigCtx, cmd, cfg.ShellTimeout) - cancel() - fmt.Println(c("[tool result: shell_exec]", 32) + "\n" + c(res, 2)) - } + runDirectShell(strings.TrimPrefix(u, "!"), cfg.ShellTimeout) return } msgs = append(msgs, Message{Role: "user", Content: strp(u)}) @@ -1527,27 +1548,7 @@ func main() { fmt.Println(c("[session loaded: "+parts[1]+"]", 32) + " " + c(summary(msgs), 2)) continue case u == "/compact": - if len(msgs) <= 1 { - fmt.Println(c("Nothing to compact yet.", 33)) - continue - } - fmt.Println(c("[compacting conversation...]", 33)) - sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) - nm, sm, err := compact(sigCtx, &cfg, msgs) - interrupted := sigCtx.Err() != nil - cancel() - if err != nil { - if interrupted || errors.Is(err, context.Canceled) { - fmt.Println(c("\n[interrupted]", 33)) - } else { - fmt.Println(c("[compact failed: "+err.Error()+"]", 31)) - } - continue - } - msgs = nm - autosave(msgs) - fmt.Println(c(fmt.Sprintf("[compacted to %d messages]", len(msgs)), 32)) - fmt.Println(c("--- summary ---", 33) + "\n" + c(sm, 2)) + msgs = doCompact(&cfg, msgs) continue case strings.HasPrefix(u, "/cfg"): parts := strings.SplitN(u, " ", 3) @@ -1575,15 +1576,7 @@ func main() { } continue case strings.HasPrefix(u, "!"): - cmd := filterText(strings.TrimSpace(strings.TrimPrefix(u, "!"))) - if cmd != "" { - astr, _ := json.Marshal(map[string]string{"command": cmd}) - fmt.Println(c(fmt.Sprintf("[tool call: shell_exec(%s)]", string(astr)), 33)) - sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) - res := shell(sigCtx, cmd, cfg.ShellTimeout) - cancel() - fmt.Println(c("[tool result: shell_exec]", 32) + "\n" + c(res, 2)) - } + runDirectShell(strings.TrimPrefix(u, "!"), cfg.ShellTimeout) continue case u == "/help": fmt.Println(c("Bantam commands:", 1, 36)) @@ -1612,27 +1605,10 @@ func main() { pct := float64(usg.PromptTokens) * 100.0 / float64(cfg.ContextWindow) if pct >= 60.0 && len(msgs) > 1 { fmt.Print(c(fmt.Sprintf("Context usage is at %.1f%% (%d / %d tokens). Compact conversation? [Y/n]: ", pct, usg.PromptTokens, cfg.ContextWindow), 33)) - ans, ok := readPlain("") - if ok { + if ans, ok := readPlain(""); ok { ans = strings.TrimSpace(strings.ToLower(ans)) if ans == "" || ans == "y" || ans == "yes" { - fmt.Println(c("[compacting conversation...]", 33)) - sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) - nm, sm, err := compact(sigCtx, &cfg, msgs) - interrupted := sigCtx.Err() != nil - cancel() - if err != nil { - if interrupted || errors.Is(err, context.Canceled) { - fmt.Println(c("\n[interrupted]", 33)) - } else { - fmt.Println(c("[compact failed: "+err.Error()+"]", 31)) - } - } else { - msgs = nm - autosave(msgs) - fmt.Println(c(fmt.Sprintf("[compacted to %d messages]", len(msgs)), 32)) - fmt.Println(c("--- summary ---", 33) + "\n" + c(sm, 2)) - } + msgs = doCompact(&cfg, msgs) } } } diff --git a/mb b/mb index 9777707..8186a7d 100755 --- a/mb +++ b/mb @@ -1,87 +1,58 @@ #!/usr/bin/env perl # MicroBantam (mb): the Bantam agent in <100 SLOC - readable, core modules only # Created by Luxferre in 2026, released into the public domain - use strict; use warnings; use HTTP::Tiny; use JSON::PP; use POSIX qw(strftime); use File::Path qw(make_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- run_subagent: delegate a sub-task to a child agent; returns its reply.\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. Delegate large or independent sub-tasks to run_subagent. 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 braces 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, GEMINI.md, CLAUDE.md contents in the project."; my $SDIR = ($ENV{HOME} || $ENV{USERPROFILE} || '.') . '/.bantam/sessions'; - sub cfg { my %d = (endpoint=>'https://opencode.ai/zen/v1', model=>'big-pickle', temperature=>0.7, api_key=>'-', timeout=>300, shell_timeout=>120, max_al_iterations=>1000); if (open my $f, '<:encoding(UTF-8)', 'model.cfg') { while (<$f>) { /^(\w+)\s*=\s*(.+)$/ and $d{$1} = $2; } } - $d{api_key} = $ENV{OPENAI_API_KEY} if $d{api_key} eq q{-} && $ENV{OPENAI_API_KEY}; - \%d; } - -sub sp { my $p = ''; - if (open my $f, '<:encoding(UTF-8)', 'system.txt') { local $/; $p = <$f>; } - $p =~ s/^\s+|\s+$//g; - length($p) ? $p : $DEF_SP; } - + $d{api_key} = $ENV{OPENAI_API_KEY} if $d{api_key} eq '-' && $ENV{OPENAI_API_KEY}; \%d } +sub sp { my $p = ''; if (open my $f, '<:encoding(UTF-8)', 'system.txt') { local $/; $p = <$f>; } $p =~ s/^\s+|\s+$//g; length($p) ? $p : $DEF_SP } sub filter_text { my $s = shift // ''; $s =~ s/[^\x20\t\n\p{L}\p{N}\p{P}\p{S}\p{M}]//g; $s } - sub T { my ($n, $d, $p) = @_; {type=>'function', function=>{name=>$n, description=>$d, parameters=>{type=>'object', properties=>$p, required=>[keys %$p]}}} } - sub sanitize_msgs { for my $m (@{$_[0]}) { if (ref $m eq 'HASH') { - if ($m->{tool_calls}) { for my $tc (@{$m->{tool_calls}}) { - $tc->{function}{arguments} = filter_text($tc->{function}{arguments}); + for my $tc (@{$m->{tool_calls} // []}) { $tc->{function}{arguments} = filter_text($tc->{function}{arguments}); my $a = eval { decode_json($tc->{function}{arguments} // '{}') }; - $tc->{function}{arguments} = encode_json({invalid_raw => $tc->{function}{arguments} // ''}) if ref $a ne 'HASH'; - } } - elsif ($m->{role} && $m->{role} eq 'tool' && defined $m->{content}) { $m->{content} = filter_text($m->{content}); } + $tc->{function}{arguments} = encode_json({invalid_raw => $tc->{function}{arguments} // ''}) if ref $a ne 'HASH'; } + $m->{content} = filter_text($m->{content}) if ($m->{role} // '') eq 'tool' && defined $m->{content}; } } } - -sub llm { my ($c, $msgs) = @_; # one non-streaming chat completion - sanitize_msgs($msgs); +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('run_subagent', 'Run a child agent with a prompt.', {prompt=>{type=>'string'}})], 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 $body = encode_json(\%p); 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=>$body}); - print "\r\e[K" if $tty; # clear the spinner line + 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)}); + print "\r\e[K" if $tty; my $d = $r->{success} ? eval { decode_json($r->{content}) } : undef; return $d->{choices}[0]{message} if $d && $d->{choices} && @{$d->{choices}}; - my $rb = $r->{content} // ''; - $rb = substr($rb, 0, 500) if length($rb) > 500; + my $rb = substr($r->{content} // '', 0, 500); die "API error: " . (length($rb) ? "$rb (HTTP $r->{status})" : ($r->{reason} || "HTTP $r->{status}")) . "\n"; } - -sub shell_exec { my ($cmd, $t) = @_; # run a command under a hard timeout - $cmd = filter_text($cmd); - my $out = ''; +sub shell_exec { my ($cmd, $t, $out) = (filter_text($_[0]), $_[1], ''); eval { local $SIG{ALRM} = sub { alarm 0; die "timeout\n" }; alarm $t; $out = `$cmd 2>&1`; alarm 0; }; - $out =~ s/\s+$//; - utf8::decode($out); - $out = filter_text($out); + $out =~ s/\s+$//; utf8::decode($out); $out = filter_text($out); $@ ? "$out\n[timeout after ${t}s]\nexit: -1" : "$out\nexit: " . ($? >> 8); } - -sub last_assistant { for my $m (reverse @{$_[0]}) { return $m->{content} if $m->{role} eq 'assistant' && defined $m->{content} && length $m->{content}; } '' } - -sub AL { my ($c, $msgs, $sp, $depth) = @_; # the agentic loop: LLM <-> tools until done - $depth ||= 0; +sub last_assistant { for my $m (reverse @{$_[0]}) { return $m->{content} if ($m->{role} // '') eq 'assistant' && defined $m->{content} && length $m->{content}; } '' } +sub AL { my ($c, $msgs, $sp, $depth) = ($_[0], $_[1], $_[2], $_[3] || 0); for (1 .. $c->{max_al_iterations}) { my $m = eval { llm($c, $msgs) }; - if ($@ && $@ =~ /Invalid assistant message|content or tool_calls must be set/ && grep({ $_->{role} eq 'assistant' } @$msgs)) { - for (my $j = @$msgs - 1; $j >= 0; $j--) { if ($msgs->[$j]{role} eq 'assistant') { splice @$msgs, $j, 1; last; } } + if ($@ && $@ =~ /Invalid assistant message|content or tool_calls must be set/ && grep({ ($_->{role} // '') eq 'assistant' } @$msgs)) { + for (my $j = @$msgs - 1; $j >= 0; $j--) { if (($msgs->[$j]{role} // '') eq 'assistant') { splice @$msgs, $j, 1; last; } } print "[stripped malformed assistant message]\n"; redo; } if ($@) { print $@; return $msgs; } push @$msgs, $m; print $m->{content}, "\n" if defined $m->{content} && length $m->{content}; - my $tcs = $m->{tool_calls}; - last unless $tcs && @$tcs; + my $tcs = $m->{tool_calls}; last unless $tcs && @$tcs; for my $tc (@$tcs) { - my $fn = $tc->{function}{name}; + my ($fn, $res) = ($tc->{function}{name}); $tc->{function}{arguments} = filter_text($tc->{function}{arguments}); my $a = eval { decode_json($tc->{function}{arguments} // '{}') }; - my $res; - 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(filter_text($a->{command} // ''), $c->{shell_timeout}); } + 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 'run_subagent') { $res = $depth >= 5 ? '[subagent depth limit (5) reached, child not spawned]' : filter_text(last_assistant(AL($c, [{role=>'system', content=>"$sp\n\nImportant: this is a child agent"}, {role=>'user', content=>filter_text($a->{prompt} // '')}], $sp, $depth + 1))); } else { $res = "unknown tool: $fn"; } $res = filter_text($res); @@ -90,42 +61,32 @@ sub AL { my ($c, $msgs, $sp, $depth) = @_; # the agentic loop: LLM <-> tools unt } } $msgs; } - -sub sessions { my @s; # all saved sessions, newest first - for my $f (glob "$SDIR/*.json") { open my $fh, '<', $f or next; local $/; my $d = eval { decode_json(<$fh>) }; push @s, $d if $d; } - sort { $b->{id} cmp $a->{id} } @s; } +sub sessions { my @s; for my $f (glob "$SDIR/*.json") { open my $fh, '<', $f or next; local $/; my $d = eval { decode_json(<$fh>) }; push @s, $d if $d; } sort { $b->{id} cmp $a->{id} } @s } sub sdir { make_path($SDIR) unless -d $SDIR; $SDIR } -sub save { sdir(); my $id = strftime('%Y%m%d-%H%M%S', localtime); my $i = 0; - $id .= '-' . ++$i while -f "$SDIR/$id.json"; - open my $f, '>', "$SDIR/$id.json" or die "cannot save: $!"; - print $f JSON::PP->new->utf8->pretty->encode({id=>$id, messages=>$_[0]}); close $f; $id; } -sub load { my ($want) = @_; my ($hit) = grep { $_->{id} eq $want } sessions(); die "no session: $want\n" unless $hit; $hit->{messages}; } +sub save { sdir(); my ($id, $i) = (strftime('%Y%m%d-%H%M%S', localtime), 0); $id .= '-' . ++$i while -f "$SDIR/$id.json"; + open my $f, '>', "$SDIR/$id.json" or die "cannot save: $!"; print $f JSON::PP->new->utf8->pretty->encode({id=>$id, messages=>$_[0]}); close $f; $id } +sub load { my ($hit) = grep { $_->{id} eq $_[0] } sessions(); die "no session: $_[0]\n" unless $hit; $hit->{messages} } sub autosave { sdir(); open my $f, '>', "$SDIR/autosave.json" or return; print $f JSON::PP->new->utf8->pretty->encode({id=>'autosave', messages=>$_[0]}); } sub list_sessions { map { [$_->{id}, scalar @{$_->{messages} // []}] } sessions() } -sub set_cfg { my ($k, $v) = @_; my (@ls, $f); - if (open my $fh, '<:encoding(UTF-8)', 'model.cfg') { while (<$fh>) { if (!/^#/ && /^(\w+)\s*=/ && $1 eq $k) { push @ls, "$k=$v\n"; $f = 1; } else { push @ls, $_; } } } +sub set_cfg { my ($k, $v, @ls, $f) = @_; + if (open my $fh, '<:encoding(UTF-8)', 'model.cfg') { while (<$fh>) { push @ls, (!/^#/ && /^(\w+)\s*=/ && $1 eq $k) ? ($f = 1, "$k=$v\n") : $_; } } push @ls, "$k=$v\n" unless $f; if (open my $fh, '>:encoding(UTF-8)', 'model.cfg') { print $fh @ls; close $fh; } } - sub main { - my ($c, $sp) = (cfg(), sp()); - my $msgs = [{role=>'system', content=>$sp}]; - if (@ARGV) { open my $f, '<:encoding(UTF-8)', $ARGV[0] or die "cannot open $ARGV[0]: $!"; # file mode - local $/; push @$msgs, {role=>'user', content=><$f>}; AL($c, $msgs, $sp); autosave($msgs); return; } + my ($c, $sp) = (cfg(), sp()); my $msgs = [{role=>'system', content=>$sp}]; + if (@ARGV) { open my $f, '<:encoding(UTF-8)', $ARGV[0] or die "cannot open $ARGV[0]: $!"; local $/; push @$msgs, {role=>'user', content=><$f>}; AL($c, $msgs, $sp); autosave($msgs); return; } print "MicroBantam ready ($c->{model}). Commands: /quit /clear /save /list /load /cfg [v] /help\n"; while (1) { - print "> "; my $u = ; last unless defined $u; - $u =~ s/^\s+|\s+$//g; next unless length $u; - if ($u eq '/quit') { last; } + print "> "; my $u = ; last unless defined $u; $u =~ s/^\s+|\s+$//g; next unless length $u; + if ($u eq '/quit') { last; } elsif ($u eq '/clear') { $msgs = [{role=>'system', content=>$sp}]; autosave($msgs); } - elsif ($u eq '/save') { print "session saved: ", save($msgs), "\n"; } - elsif ($u eq '/list') { print "$_->[0] [$_->[1] msgs]\n" for list_sessions(); } + elsif ($u eq '/save') { print "session saved: ", save($msgs), "\n"; } + elsif ($u eq '/list') { print "$_->[0] [$_->[1] msgs]\n" for list_sessions(); } elsif ($u =~ /^\/load(?:\s+(\S+))?$/) { if (defined $1) { $msgs = eval { load($1) }; $@ ? print($@) : (autosave($msgs), print "loaded: $1\n"); } else { print "usage: /load \n"; } } elsif ($u =~ /^\/cfg(?:\s+(\S+)(?:\s+(.+))?)?$/) { if (defined $2) { set_cfg($1, $2); $c = cfg(); print "config: $1=$2\n"; } elsif (defined $1) { print exists $c->{$1} ? "$1=$c->{$1}\n" : "$1 not set\n"; } else { print "usage: /cfg [val]\n"; } } - elsif ($u eq '/help') { print "Commands: /quit /clear /save /list /load /cfg [v] /help\n"; } + elsif ($u eq '/help') { print "Commands: /quit /clear /save /list /load /cfg [v] /help\n"; } else { push @$msgs, {role=>'user', content=>$u}; AL($c, $msgs, $sp); autosave($msgs); } } autosave($msgs); } - main() unless caller();