summaryrefslogtreecommitdiff
path: root/libpanto/src/config.zig
blob: e395230bd0e4f65108bce04767b4a4577f44ca7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! Per-provider configuration.
//!
//! `Config` is a tagged union keyed by `APIStyle`. Each variant carries the
//! settings specific to one wire dialect. New providers add a new tag and a
//! new payload struct here; nothing else in libpanto needs a central enum
//! refresh.

/// The wire dialect a provider speaks.
pub const APIStyle = enum {
    openai_chat,
    anthropic_messages,
};

/// Reasoning intensity hint sent to providers that support it.
///
/// `.default` omits the field entirely so the provider's own default applies.
/// `.off` sends `"none"` (supported by OpenRouter/NanoGPT; ignored or rejected
/// elsewhere). The remaining values mirror the `reasoning_effort` parameter
/// accepted by OpenAI reasoning models and OpenAI-compatible proxies.
pub const ReasoningEffort = enum {
    default,
    off,
    minimal,
    low,
    medium,
    high,
};

pub const OpenAIChatConfig = struct {
    api_key: []const u8,
    base_url: []const u8,
    model: []const u8,
    reasoning: ReasoningEffort = .default,
};

pub const AnthropicMessagesConfig = struct {
    api_key: []const u8,
    base_url: []const u8,
    model: []const u8,
    /// Value sent in the `anthropic-version` header.
    api_version: []const u8 = "2023-06-01",
    /// Required by Anthropic's Messages API.
    max_tokens: u32 = 4096,
};

pub const Config = union(APIStyle) {
    openai_chat: OpenAIChatConfig,
    anthropic_messages: AnthropicMessagesConfig,

    pub fn style(self: Config) APIStyle {
        return @as(APIStyle, self);
    }
};

const t = @import("std").testing;

test "Config - openai_chat variant" {
    const cfg: Config = .{ .openai_chat = .{
        .api_key = "sk-test",
        .base_url = "https://api.openai.com/v1",
        .model = "gpt-4o",
        .reasoning = .high,
    } };
    try t.expectEqual(APIStyle.openai_chat, cfg.style());
    try t.expectEqualStrings("sk-test", cfg.openai_chat.api_key);
    try t.expectEqual(ReasoningEffort.high, cfg.openai_chat.reasoning);
}

test "Config - anthropic_messages variant" {
    const cfg: Config = .{ .anthropic_messages = .{
        .api_key = "sk-ant-test",
        .base_url = "https://api.anthropic.com",
        .model = "claude-sonnet-4-20250514",
    } };
    try t.expectEqual(APIStyle.anthropic_messages, cfg.style());
    try t.expectEqualStrings("2023-06-01", cfg.anthropic_messages.api_version);
    try t.expectEqual(@as(u32, 4096), cfg.anthropic_messages.max_tokens);
}

test "Config - openai_chat reasoning defaults to .default" {
    const cfg: Config = .{ .openai_chat = .{
        .api_key = "k",
        .base_url = "u",
        .model = "m",
    } };
    try t.expectEqual(ReasoningEffort.default, cfg.openai_chat.reasoning);
}