summaryrefslogtreecommitdiff
path: root/libpanto/src/provider.zig
diff options
context:
space:
mode:
Diffstat (limited to 'libpanto/src/provider.zig')
-rw-r--r--libpanto/src/provider.zig107
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;