Fifteen components that dress an AI agent's work as an RPG party: AgentCard, ToolCall, PlanQuest, ThinkingOrb, TokenMana, ApprovalPrompt, AgentLog, SubAgentParty, MemoryCrystals, AgentStatus, DiffScroll, ModelSelect, PromptBox, CostCoins and Checkpoints. The context window is HP and the token budget MP; tool calls are spells that CAST, glow, then land OK or FAILED; the transcript reads as a battle log whose newest line types itself out; models line up like fighters. Busy signs keep moving under Reduce Motion, decoration stops. Commands and prompts never reach the log, and a veiled PromptBox casts nothing. icon.Crystal is the new 12x12 save and memory crystal.
73 lines
1.7 KiB
Go
73 lines
1.7 KiB
Go
// Package agent dresses AI agent work as an RPG party.
|
|
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
|
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
|
)
|
|
|
|
// Status is where an agent stands.
|
|
type Status int
|
|
|
|
const (
|
|
Idle Status = iota
|
|
Working
|
|
Blocked
|
|
Done
|
|
)
|
|
|
|
// look is a status's word and slot; unknown ones panic.
|
|
func (s Status) look(k pixel.Kit) (string, pixel.Slot) {
|
|
if s < Idle || s > Done {
|
|
panic(fmt.Sprintf("agent: unknown Status %d", s))
|
|
}
|
|
return k.Msg([...]string{"idle", "working", "blocked", "done"}[s]), [...]pixel.Slot{pixel.Faint, pixel.Info, pixel.Warn, pixel.Good}[s]
|
|
}
|
|
|
|
// tokens prints n short: 950, 1.5K, 128K, 2M.
|
|
func tokens(n int) string {
|
|
if n < 1000 {
|
|
return strconv.Itoa(n)
|
|
}
|
|
div, unit := 1000, 0
|
|
for n/div >= 1000 {
|
|
div, unit = div*1000, unit+1
|
|
}
|
|
s := strconv.Itoa(n / div)
|
|
if t := n % div / (div / 10); n/div < 10 && t > 0 {
|
|
s += "." + strconv.Itoa(t)
|
|
}
|
|
return s + "KMBTPE"[unit:unit+1]
|
|
}
|
|
|
|
// gauge frames a bar eight dots high and fills part.
|
|
func gauge(g pixel.Canvas, x, y, w int, part float64, ink pixel.Slot) {
|
|
g.Frame(x, y, w, 8, pixel.Sunken, pixel.Well)
|
|
g.Rect(x+2, y+2, int(part*float64(w-4)), 4, ink)
|
|
}
|
|
|
|
// health: Good past half, Warn past a quarter, else Bad.
|
|
func health(left, full int) pixel.Slot {
|
|
switch {
|
|
case left <= full/4:
|
|
return pixel.Bad
|
|
case left <= full/2:
|
|
return pixel.Warn
|
|
}
|
|
return pixel.Good
|
|
}
|
|
|
|
// portrait is p or the robot; over 12 dots panics.
|
|
func portrait(name string, p *pixel.Sprite) *pixel.Sprite {
|
|
if p == nil {
|
|
return icon.Robot
|
|
}
|
|
if p.W > 12 || p.H > 12 {
|
|
panic(fmt.Sprintf("agent: %s portrait is %dx%d, over 12", name, p.W, p.H))
|
|
}
|
|
return p
|
|
}
|