diff options
| author | t <t@tjp.lol> | 2026-07-07 11:26:32 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-07-07 11:26:45 -0600 |
| commit | f83578fdc9264019a1a1cef8c5484a161167d3dd (patch) | |
| tree | 888f11767f944d61e5ca8eb92fa1b2dba295a4b8 /examples/simple-agent.lua | |
initial commit, moved libpanto over from the pantograph repo
Diffstat (limited to 'examples/simple-agent.lua')
| -rw-r--r-- | examples/simple-agent.lua | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/examples/simple-agent.lua b/examples/simple-agent.lua new file mode 100644 index 0000000..baf00b5 --- /dev/null +++ b/examples/simple-agent.lua @@ -0,0 +1,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") |
