many improvements

This commit is contained in:
Luxferre
2026-08-15 08:27:24 +03:00
parent 398754dabb
commit e6acb066ca
9 changed files with 283 additions and 22 deletions
+5 -3
View File
@@ -19,7 +19,7 @@ proc http_request {m url hdrs body {t 300}} { set proto http; set host ""; set p
proc clean_msg_for_api {m} { if {![is_dict $m]} { return "" }; set r [safe_get $m role]; if {$r eq ""} { return "" }; set c [safe_get $m content]; set res [dict create role $r]; if {$r eq "system" || $r eq "user"} { dict set res content $c } elseif {$r eq "assistant"} { dict set res content $c; set vtcs [list]; set raw_tcs [safe_get $m tool_calls]; if {[is_tool_call $raw_tcs]} { set raw_tcs [list $raw_tcs] }; foreach tc $raw_tcs { if {[is_tool_call $tc]} { set fn [dict get [dict get $tc function] name]; set a "\{\}"; if {[dict exists $tc function arguments]} { set a [dict get [dict get $tc function] arguments] }; set tcid [safe_get $tc id]; if {$tcid eq ""} { set tcid "call_0" }; set tp "function"; if {[dict exists $tc type]} { set tp [dict get $tc type] }; lappend vtcs [dict create id $tcid type $tp function [dict create name $fn arguments $a]] } }; if {[llength $vtcs]} { dict set res tool_calls $vtcs } } elseif {$r eq "tool"} { set tcid [safe_get $m tool_call_id]; if {$tcid eq ""} { set tcid "call_0" }; dict set res tool_call_id $tcid; dict set res content $c }; return $res }
proc encode_json_msg {m} { if {![is_dict $m]} { return "\{\}" }; set parts [list]; dict for {k v} $m { if {$k eq "tool_calls"} { set raw_tcs $v; if {[is_tool_call $raw_tcs]} { set raw_tcs [list $raw_tcs] }; set tcs [list]; foreach tc $raw_tcs { if {[is_tool_call $tc]} { set tcp [list]; dict for {tck tcv} $tc { if {$tck eq "function" && [is_dict $tcv]} { set fnp [list]; dict for {fk fv} $tcv { lappend fnp "[jesc $fk]:[jesc $fv]" }; lappend tcp "[jesc $tck]:\{ [join $fnp ", "] \}" } else { lappend tcp "[jesc $tck]:[jesc $tcv]" } }; lappend tcs "\{ [join $tcp ", "] \}" } }; lappend parts "[jesc $k]:\[ [join $tcs ", "] \]" } else { lappend parts [expr {$v eq "null" ? "[jesc $k]:null" : "[jesc $k]:[jesc $v]"}] } }; return "\{ [join $parts ", "] \}" }
proc sanitize_msgs {msgs_var} { upvar 1 $msgs_var msgs; set new [list]; foreach m $msgs { if {[is_dict $m] && [dict exists $m tool_calls] && [set tcs [dict get $m tool_calls]] ne "null" && $tcs ne ""} { if {[is_tool_call $tcs]} { set tcs [list $tcs] }; set ntcs [list]; foreach tc $tcs { if {[is_tool_call $tc]} { set raw [dict get [dict get $tc function] arguments]; if {[catch {json::decode $raw} p] || ![is_dict $p]} { dict set tc function arguments [encode_json_msg [dict create invalid_raw $raw]] } }; lappend ntcs $tc }; dict set m tool_calls $ntcs }; lappend new $m }; set msgs $new }
proc encode_payload {c msgs} { set mjs [list]; foreach m $msgs { set cm [clean_msg_for_api $m]; if {$cm ne ""} { lappend mjs [encode_json_msg $cm] } }; set tools {[{"type":"function","function":{"name":"shell_exec","description":"Run a shell command, return output and exit code.","parameters":{"type":"object","properties":{"command":{"type":"string"}},"required":["command"]}}},{"type":"function","function":{"name":"run_subagent","description":"Run a child agent with a prompt.","parameters":{"type":"object","properties":{"prompt":{"type":"string"}},"required":["prompt"]}}}]}; return "\{ \"model\": [jesc [dict get $c model]], \"temperature\": [expr {[dict get $c temperature] + 0}], \"messages\": \[ [join $mjs ", "] \], \"tools\": $tools \}" }
proc encode_payload {c msgs} { set mjs [list]; foreach m $msgs { set cm [clean_msg_for_api $m]; if {$cm ne ""} { lappend mjs [encode_json_msg $cm] } }; set tools {[{"type":"function","function":{"name":"shell_exec","description":"Run a shell command, return output and exit code.","parameters":{"type":"object","properties":{"command":{"type":"string"}},"required":["command"]}}},{"type":"function","function":{"name":"run_subagent","description":"Run a child agent with a prompt.","parameters":{"type":"object","properties":{"prompt":{"type":"string"}},"required":["prompt"]}}}]}; set extra [list]; dict for {k v} $c { if {$k in {endpoint api_key timeout shell_timeout max_al_iterations stream color}} continue; if {![catch {json::decode $v} jv]} { lappend extra "[jesc $k]:$v" } else { lappend extra "[jesc $k]:[jesc $v]" } }; set ex_str [expr {[llength $extra] ? ", [join $extra ", "]" : ""}]; return "\{ \"model\": [jesc [dict get $c model]], \"temperature\": [expr {[dict get $c temperature] + 0}], \"messages\": \[ [join $mjs ", "] \], \"tools\": $tools$ex_str \}" }
proc llm {c msgs_var} {
upvar 1 $msgs_var msgs; sanitize_msgs msgs
@@ -83,6 +83,7 @@ proc save {msgs} { set sd [sdir]; set base [clock format [clock seconds] -format
proc load_session {want} { foreach s [sessions] { if {[safe_get $s id] eq $want} { return [dict get $s messages] } }; error "no session: $want" }
proc autosave {msgs} { set sd [sdir]; set mjs [list]; foreach m $msgs { lappend mjs [encode_json_msg $m] }; if {![catch {open "$sd/autosave.json" w} f]} { puts $f "\{\n \"id\": \"autosave\",\n \"messages\": \[\n [join $mjs ",\n "]\n \]\n\}"; close $f } }
proc list_sessions {} { set res [list]; foreach s [sessions] { lappend res [list [safe_get $s id] [llength [expr {[dict exists $s messages] ? [dict get $s messages] : {}}]]] }; return $res }
proc set_cfg {k v} { set ls [list]; set f 0; if {[file exists model.cfg] && ![catch {open model.cfg r} fh]} { while {[gets $fh l] >= 0} { if {![regexp {^\s*#} $l] && [regexp {^(\w+)\s*=} $l -> pk] && $pk eq $k} { lappend ls "$k=$v"; set f 1 } else { lappend ls $l } }; close $fh }; if {!$f} { lappend ls "$k=$v" }; if {![catch {open model.cfg w} fh]} { puts $fh [join $ls "\n"]; close $fh } }
proc main {argv} {
set c [cfg]; set sp_text [sp]; set msgs [list [dict create role system content $sp_text]]
@@ -90,7 +91,7 @@ proc main {argv} {
if {[catch {open [lindex $argv 0] r} f]} { puts stderr "cannot open [lindex $argv 0]: $f"; exit 1 }
set content [read $f]; close $f; lappend msgs [dict create role user content $content]; AL $c msgs $sp_text 0; autosave $msgs; return
}
puts "MicroBantam ready ([dict get $c model]). Commands: /quit /clear /save /list /load <id> /help"
puts "MicroBantam ready ([dict get $c model]). Commands: /quit /clear /save /list /load <id> /cfg <k> \[v\] /help"
while {1} {
puts -nonewline "> "; flush stdout; if {[gets stdin u] < 0} break
if {[set u [string trim $u]] eq ""} continue
@@ -99,7 +100,8 @@ proc main {argv} {
elseif {$u eq "/save"} { puts "session saved: [save $msgs]" } \
elseif {$u eq "/list"} { foreach item [list_sessions] { puts "[lindex $item 0] \[[lindex $item 1] msgs\]" } } \
elseif {[regexp {^\/load(?:\s+(\S+))?$} $u -> want_id]} { if {$want_id eq ""} { puts "usage: /load <session id>" } elseif {[catch {load_session $want_id} loaded]} { puts $loaded } else { set msgs $loaded; autosave $msgs; puts "loaded: $want_id" } } \
elseif {$u eq "/help"} { puts "Commands: /quit /clear /save /list /load <id> /help" } \
elseif {[regexp {^\/cfg(?:\s+(\S+)(?:\s+(.+))?)?$} $u -> ck cv]} { if {$cv ne ""} { set_cfg $ck [string trim $cv]; set c [cfg]; puts "config: $ck=[string trim $cv]" } elseif {$ck ne ""} { puts [expr {[dict exists $c $ck] ? "$ck=[dict get $c $ck]" : "$ck not set"}] } else { puts "usage: /cfg <param> \[val\]" } } \
elseif {$u eq "/help"} { puts "Commands: /quit /clear /save /list /load <id> /cfg <k> \[v\] /help" } \
else { lappend msgs [dict create role user content $u]; AL $c msgs $sp_text 0; autosave $msgs }
}
autosave $msgs