1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
#!/usr/bin/env lua
-- A tiny standalone libpanto-lua app: one prompt in, streamed text out.
--
-- Run with Lua 5.4 after building/installing libpanto-lua, for example:
-- LUA_CPATH='./libpanto-lua/zig-out/lib/?.so;;' lua examples/simple-agent.lua "list the files"
--
-- Required environment:
-- PANTO_API_STYLE=openai_chat|anthropic_messages
-- PANTO_API_KEY=...
-- PANTO_BASE_URL=...
-- PANTO_MODEL=...
local panto = require("panto")
local function getenv(name, default)
local value = os.getenv(name)
if value == nil or value == "" then return default end
return value
end
local function require_env(name)
local value = getenv(name)
if value == nil then error("missing required env var: " .. name, 0) end
return value
end
local function shell_quote(s)
return "'" .. tostring(s):gsub("'", "'\\''") .. "'"
end
local function read_all(pipe)
local chunks = {}
while true do
local chunk = pipe:read(8192)
if chunk == nil then break end
chunks[#chunks + 1] = chunk
end
return table.concat(chunks)
end
local function run_bash(command)
local wrapped = "bash -lc " .. shell_quote(command) .. " 2>&1; printf '\\n__panto_exit_code:%s\\n' $?"
local pipe = assert(io.popen(wrapped, "r"))
local output = read_all(pipe)
pipe:close()
local body, code = output:match("^(.*)\n__panto_exit_code:(%-?%d+)\n$")
return string.format("exit_code=%s\n%s", code or "unknown", body or output)
end
local prompt = table.concat(arg, " ")
if prompt == "" then
io.stderr:write("usage: lua examples/simple-agent.lua <prompt>\n")
os.exit(2)
end
local agent = panto.agent {
api_style = require_env("PANTO_API_STYLE"),
api_key = require_env("PANTO_API_KEY"),
base_url = require_env("PANTO_BASE_URL"),
model = require_env("PANTO_MODEL"),
max_tokens = tonumber(getenv("PANTO_MAX_TOKENS", "4096")),
}
agent:set_system_prompt(table.concat({
"You are a very small coding agent.",
"Use the bash tool when you need to inspect or change the local workspace.",
"Keep final answers concise.",
}, "\n"))
agent:register_tool {
name = "bash",
description = "Run a shell command with bash -lc and return combined stdout/stderr plus the exit code.",
schema = {
type = "object",
properties = {
command = {
type = "string",
description = "The command to run.",
},
},
required = { "command" },
},
handler = function(input)
if type(input.command) ~= "string" or input.command == "" then
error("bash.command must be a non-empty string")
end
return run_bash(input.command)
end,
}
for ev in agent:run(prompt):events() do
if ev.type == "content_delta" then
io.write(ev.delta)
io.flush()
elseif ev.type == "tool_dispatch_start" then
io.stderr:write(string.format("\n[bash: running %d tool call(s)]\n", ev.count))
elseif ev.type == "tool_dispatch_complete" then
io.stderr:write("[bash: done]\n")
end
end
io.write("\n")
|