diff options
| -rw-r--r-- | main.rb | 138 |
1 files changed, 138 insertions, 0 deletions
@@ -0,0 +1,138 @@ +#!/usr/bin/env ruby +# GPT-5 Action-Oriented Agent - pure stdlib + +require "net/http" +require "json" +require "uri" + +API_KEY = ENV["OPENAI_API_KEY"] or abort("Set OPENAI_API_KEY") +MODEL = "gpt-5-mini" + +# Tools GPT-5 can use +TOOLS = [ + { + type: "function", + function: { + name: "read_file", + description: "Read contents of a file", + parameters: { + type: "object", + properties: { path: { type: "string" } }, + required: ["path"] + } + } + }, + { + type: "function", + function: { + name: "write_file", + description: "Write contents to a file", + parameters: { + type: "object", + properties: { path: { type: "string" }, content: { type: "string" } }, + required: ["path","content"] + } + } + }, + { + type: "function", + function: { + name: "run_command", + description: "Run a shell command and return output", + parameters: { + type: "object", + properties: { command: { type: "string" } }, + required: ["command"] + } + } + } +] + +# Execute tools +def run_tool(name, args) + case name + when "read_file" + begin + { content: File.read(args["path"]) } + rescue => e + { error: e.message } + end + when "write_file" + begin + File.write(args["path"], args["content"]) + { status: "ok" } + rescue => e + { error: e.message } + end + when "run_command" + begin + { output: `#{args["command"]}`.strip } + rescue => e + { error: e.message } + end + else + { error: "unknown tool #{name}" } + end +end + +# Call OpenAI +def openai_chat(messages, tools) + uri = URI("https://api.openai.com/v1/chat/completions") + req = Net::HTTP::Post.new(uri) + req["Authorization"] = "Bearer #{API_KEY}" + req["Content-Type"] = "application/json" + + req.body = { + model: MODEL, + messages: messages, + tools: tools, + tool_choice: "auto" + }.to_json + + res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) } + raise "HTTP #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess) + JSON.parse(res.body) +end + +# Conversation with bias-for-action +messages = [ + { role: "system", content: <<~MSG + You are a reasoning coding and system agent. Prioritize taking practical actions whenever possible. + Use read_file, write_file, and run_command proactively to achieve user goals. + Explain your reasoning briefly in content. Avoid unnecessary delays; act whenever sufficient info is available. + MSG + } +] + +puts ">> Type instructions (or 'exit')" + +loop do + print "\nUser> " + input = STDIN.gets&.strip + break if input.nil? || input.downcase == "exit" + messages << { role: "user", content: input } + + loop do + resp = openai_chat(messages, TOOLS) + msg = resp.dig("choices", 0, "message") + messages << msg + + # Print reasoning content immediately + if msg["content"] && !msg["content"].empty? + puts "\nAssistant> #{msg['content']}" + end + + # Execute any tool calls + if msg["tool_calls"] + msg["tool_calls"].each do |call| + name = call.dig("function","name") + args = JSON.parse(call.dig("function","arguments")) + result = run_tool(name, args) + messages << { role: "tool", tool_call_id: call["id"], content: JSON.dump(result) } + end + next # continue reasoning on tool results + end + + break # no tool calls left, turn is done + end +end |
