diff options
Diffstat (limited to 'libpanto/src/provider.zig')
| -rw-r--r-- | libpanto/src/provider.zig | 107 |
1 files changed, 53 insertions, 54 deletions
diff --git a/libpanto/src/provider.zig b/libpanto/src/provider.zig index ab60678..1a8ee4e 100644 --- a/libpanto/src/provider.zig +++ b/libpanto/src/provider.zig @@ -88,59 +88,58 @@ pub const Receiver = struct { } }; -pub const ProviderVTable = struct { - streamStep: *const fn ( - *anyopaque, - *conversation.Conversation, - *const ToolRegistry, - *Receiver, - ) anyerror!void, - deinit: *const fn (*anyopaque) void, -}; - -pub const Provider = struct { - ptr: *anyopaque, - vtable: *const ProviderVTable, - - /// Construct the concrete provider implementation that matches `cfg`'s - /// API style, heap-allocate it, and return a `Provider` interface bound - /// to it. `deinit` tears down the impl and frees the allocation. - /// - /// The concrete provider types (`OpenAIChatProvider`, etc.) are - /// implementation details; callers interact only with this interface. - pub fn init( - allocator: std.mem.Allocator, - io: std.Io, - cfg: config_mod.Config, - ) !Provider { - // Imported lazily to break the circular module graph: - // provider.zig <- provider_openai_chat.zig <- provider.zig. - const provider_openai_chat = @import("provider_openai_chat.zig"); - const provider_anthropic_messages = @import("provider_anthropic_messages.zig"); - switch (cfg) { - .openai_chat => |c| { - const impl = try allocator.create(provider_openai_chat.OpenAIChatProvider); - impl.* = provider_openai_chat.OpenAIChatProvider.init(allocator, io, c); - return impl.provider(); - }, - .anthropic_messages => |c| { - const impl = try allocator.create(provider_anthropic_messages.AnthropicMessagesProvider); - impl.* = provider_anthropic_messages.AnthropicMessagesProvider.init(allocator, io, c); - return impl.provider(); - }, - } - } - - pub fn streamStep( - self: Provider, - conv: *conversation.Conversation, - tools: *const ToolRegistry, - receiver: *Receiver, - ) anyerror!void { - return self.vtable.streamStep(self.ptr, conv, tools, receiver); +/// Drive one streaming provider turn against the active config snapshot. +/// +/// This is the single dispatch point: it switches on `cfg.provider`'s +/// `APIStyle` tag, builds a transient per-request object bound to the +/// process-global HTTP client, and runs it. There is no persistent +/// provider object — every turn re-reads `cfg`, so swapping the agent's +/// `*const Config` between turns changes provider, model, base_url, and +/// the visible tool set with no transport teardown. +/// +/// The tool registry is taken from `cfg.registry`; the serializers receive +/// it directly. +pub fn streamStep( + allocator: std.mem.Allocator, + io: std.Io, + cfg: *const config_mod.Config, + conv: *conversation.Conversation, + receiver: *Receiver, +) anyerror!void { + // Imported lazily to break the circular module graph: + // provider.zig <- provider_openai_chat.zig <- provider.zig. + const provider_openai_chat = @import("provider_openai_chat.zig"); + const provider_anthropic_messages = @import("provider_anthropic_messages.zig"); + const client = config_mod.httpClient(); + switch (cfg.provider) { + .openai_chat => |*c| { + var req: provider_openai_chat.OpenAIChatRequest = .{ + .allocator = allocator, + .io = io, + .config = c, + .http_client = client, + }; + return req.streamStep(conv, cfg.registry, receiver); + }, + .anthropic_messages => |*c| { + var req: provider_anthropic_messages.AnthropicMessagesRequest = .{ + .allocator = allocator, + .io = io, + .config = c, + .http_client = client, + }; + return req.streamStep(conv, cfg.registry, receiver); + }, } +} - pub fn deinit(self: Provider) void { - self.vtable.deinit(self.ptr); - } -}; +/// The shape of `streamStep`, exposed as a function-pointer type so the +/// agent can carry an injectable seam (real dispatch in production, a stub +/// in tests) without resurrecting a per-provider vtable. +pub const StreamFn = *const fn ( + allocator: std.mem.Allocator, + io: std.Io, + cfg: *const config_mod.Config, + conv: *conversation.Conversation, + receiver: *Receiver, +) anyerror!void; |
