From f026bb81ae68f516910d0eb4f23c9344dd36b62b Mon Sep 17 00:00:00 2001 From: T Date: Mon, 25 May 2026 21:50:43 -0600 Subject: phase 2 done --- src/main.zig | 116 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 34 deletions(-) (limited to 'src/main.zig') diff --git a/src/main.zig b/src/main.zig index 832f037..688b61d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -85,39 +85,82 @@ const CLIReceiver = struct { } }; +fn loadConfig(environ_map: *const std.process.Environ.Map) !panto.config.Config { + const style_str = environ_map.get("PANTO_API_STYLE") orelse "openai_chat"; + const style = std.meta.stringToEnum(panto.config.APIStyle, style_str) orelse { + std.debug.print( + "error: PANTO_API_STYLE must be one of: openai_chat, anthropic_messages (got: {s})\n", + .{style_str}, + ); + return error.InvalidApiStyle; + }; + + switch (style) { + .openai_chat => { + const api_key = environ_map.get("OPENAI_API_KEY") orelse { + std.debug.print("error: OPENAI_API_KEY is required\n", .{}); + return error.MissingApiKey; + }; + const base_url = environ_map.get("OPENAI_BASE_URL") orelse "https://api.openai.com/v1"; + const model = environ_map.get("OPENAI_MODEL") orelse { + std.debug.print("error: OPENAI_MODEL is required\n", .{}); + return error.MissingModel; + }; + const reasoning: panto.config.ReasoningEffort = + if (environ_map.get("OPENAI_REASONING")) |val| + std.meta.stringToEnum(panto.config.ReasoningEffort, val) orelse { + std.debug.print( + "error: OPENAI_REASONING must be one of: default, off, minimal, low, medium, high (got: {s})\n", + .{val}, + ); + return error.InvalidReasoning; + } + else + .default; + return .{ .openai_chat = .{ + .api_key = api_key, + .base_url = base_url, + .model = model, + .reasoning = reasoning, + } }; + }, + .anthropic_messages => { + const api_key = environ_map.get("ANTHROPIC_API_KEY") orelse { + std.debug.print("error: ANTHROPIC_API_KEY is required\n", .{}); + return error.MissingApiKey; + }; + const base_url = environ_map.get("ANTHROPIC_BASE_URL") orelse "https://api.anthropic.com"; + const model = environ_map.get("ANTHROPIC_MODEL") orelse { + std.debug.print("error: ANTHROPIC_MODEL is required\n", .{}); + return error.MissingModel; + }; + const api_version = environ_map.get("ANTHROPIC_API_VERSION") orelse "2023-06-01"; + const max_tokens: u32 = if (environ_map.get("ANTHROPIC_MAX_TOKENS")) |val| + std.fmt.parseInt(u32, val, 10) catch { + std.debug.print( + "error: ANTHROPIC_MAX_TOKENS must be a positive integer (got: {s})\n", + .{val}, + ); + return error.InvalidMaxTokens; + } + else + 4096; + return .{ .anthropic_messages = .{ + .api_key = api_key, + .base_url = base_url, + .model = model, + .api_version = api_version, + .max_tokens = max_tokens, + } }; + }, + } +} + pub fn main(init: std.process.Init) !void { const alloc = init.gpa; const io = init.io; - const api_key = init.environ_map.get("PANTO_API_KEY") orelse { - std.debug.print("error: PANTO_API_KEY is required\n", .{}); - return error.MissingApiKey; - }; - const base_url = init.environ_map.get("PANTO_BASE_URL") orelse "https://api.openai.com/v1"; - const model = init.environ_map.get("PANTO_MODEL") orelse { - std.debug.print("error: PANTO_MODEL is required\n", .{}); - return error.MissingModel; - }; - - const reasoning: panto.config.ReasoningEffort = - if (init.environ_map.get("PANTO_REASONING")) |val| - std.meta.stringToEnum(panto.config.ReasoningEffort, val) orelse { - std.debug.print( - "error: PANTO_REASONING must be one of: default, off, minimal, low, medium, high (got: {s})\n", - .{val}, - ); - return error.InvalidReasoning; - } - else - .default; - - const config = panto.config.Config{ - .api_style = .openai_chat, - .api_key = api_key, - .base_url = base_url, - .model = model, - .reasoning = reasoning, - }; + const config = try loadConfig(init.environ_map); var stdout_buffer: [4096]u8 = undefined; var stdout_file = std.Io.File.stdout().writer(io, &stdout_buffer); @@ -131,12 +174,17 @@ pub fn main(init: std.process.Init) !void { defer conv.deinit(); try conv.addSystemMessage("You are a helpful assistant."); - var provider_impl = panto.provider_openai_chat.OpenAIChatProvider.init(alloc, io, config); - defer provider_impl.deinit(); + const prov = try panto.provider.Provider.init(alloc, io, config); + const agent = panto.agent.Agent.init(alloc, prov); + defer agent.deinit(); - const agent = panto.agent.Agent.init(alloc, provider_impl.provider()); - - try stdout.print("panto — model: {s} @ {s}\n", .{ config.model, config.base_url }); + const banner_model: []const u8 = switch (config) { + inline else => |c| c.model, + }; + const banner_base: []const u8 = switch (config) { + inline else => |c| c.base_url, + }; + try stdout.print("panto — {s}: {s} @ {s}\n", .{ @tagName(config), banner_model, banner_base }); try stdout.print("> ", .{}); try stdout_file.flush(); -- cgit v1.3