Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

openai.zig

Zig Version

Generated Zig SDK for the OpenAI REST API.

  • Zig 0.16.0+
  • package/module name: openai
  • generated from the OpenAI OpenAPI spec with openapi2zig
  • generated root module: src/api.zig

Install

zig fetch --save=openai git+https://github.com/h0rv/openai.zig.git
const dep = b.dependency("openai", .{
    .target = target,
    .optimize = optimize,
});

exe.root_module.addImport("openai", dep.module("openai"));

Client

const std = @import("std");
const openai = @import("openai");

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;
    const api_key = init.environ_map.get("OPENAI_API_KEY") orelse return error.MissingApiKey;

    var client = openai.Client.init(gpa, init.io, api_key);
    defer client.deinit();
}

Returned Owned(T) values own their response body and parsed JSON tree. Call deinit().

Responses

var response = try openai.responses.create(&client, .{
    .model = "gpt-5.4-mini",
    .instructions = "You are a coding assistant that talks like a pirate.",
    .input = .{ .text = "How do I check if a Python object is an instance of a class?" },
});
defer response.deinit();

if (response.value().output_text) |text| {
    std.debug.print("{s}\n", .{text});
}

Chat completions

const messages = [_]openai.ChatCompletionRequestMessage{
    .{ .role = "developer", .content = .{ .string = "Talk like a pirate." } },
    .{ .role = "user", .content = .{ .string = "How do I check if a Python object is an instance of a class?" } },
};

var completion = try openai.chat.completions.create(&client, .{
    .model = "gpt-5.4-mini",
    .messages = &messages,
});
defer completion.deinit();

if (completion.value().choices[0].message.content) |text| {
    std.debug.print("{s}\n", .{text});
}

Model IDs are strings, not enums.

Streaming

Responses stream:

const Events = struct {
    pub fn event(_: *@This(), event: *openai.ResponseStreamEvent) !void {
        switch (event.*) {
            .response_output_text_delta => |e| std.debug.print("{s}", .{e.delta}),
            .response_completed => std.debug.print("\n", .{}),
            else => {},
        }
    }
};

var events = Events{};
try openai.responses.streamEvents(openai.ResponseStreamEvent, &client, .{
    .model = "gpt-5.4-mini",
    .input = .{ .text = "Write a one-sentence bedtime story about a unicorn." },
}, &events);

Chat completion chunks:

const Chunks = struct {
    pub fn event(_: *@This(), chunk: *openai.ChatCompletionChunk) !void {
        if (chunk.choices.len == 0) return;
        if (chunk.choices[0].delta.content) |text| {
            std.debug.print("{s}", .{text});
        }
    }
};

const messages = [_]openai.ChatCompletionRequestMessage{
    .{ .role = "user", .content = .{ .string = "Say hello in five words." } },
};

var chunks = Chunks{};
try openai.chat.completions.streamEvents(openai.ChatCompletionChunk, &client, .{
    .model = "gpt-5.4-mini",
    .messages = &messages,
}, &chunks);

Callback values are borrowed. Copy data if you need to keep it.

Vision

const content = [_]openai.InputContent{
    .{ .input_text = .{ .type = "input_text", .text = "What is in this image?" } },
    .{ .input_image = .{
        .type = "input_image",
        .image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/2023_06_08_Raccoon1.jpg/1599px-2023_06_08_Raccoon1.jpg",
        .detail = "auto",
    } },
};

const input = [_]openai.InputItem{
    .{ .easy_input_message = .{
        .role = "user",
        .content = .{ .parts = &content },
    } },
};

var response = try openai.responses.create(&client, .{
    .model = "gpt-5.4-mini",
    .input = .{ .input_item_list = &input },
});
defer response.deinit();

Base64 images use the same shape with an image_url value like data:image/png;base64,....

Error bodies

Use *Result methods to keep non-2xx response bodies.

const messages = [_]openai.ChatCompletionRequestMessage{
    .{ .role = "user", .content = .{ .string = "Say hi." } },
};

var result = try openai.chat.completions.createResult(&client, .{
    .model = "not-a-real-model",
    .messages = &messages,
});
defer result.deinit();

switch (result) {
    .ok => |*ok| std.debug.print("model={s}\n", .{ok.value().model}),
    .api_error => |err| std.debug.print("status={} body={s}\n", .{
        @intFromEnum(err.status),
        err.body,
    }),
    .parse_error => |err| std.debug.print("status={} parse={s} body={s}\n", .{
        @intFromEnum(err.raw.status),
        err.error_name,
        err.raw.body,
    }),
}

Plain methods such as create return error.ResponseError for non-2xx or parse-error responses.

OpenAI-compatible providers

var client = openai.Client.init(gpa, init.io, init.environ_map.get("OPENROUTER_API_KEY") orelse return error.MissingApiKey);
defer client.deinit();
client.withBaseUrl("https://openrouter.ai/api/v1");

const messages = [_]openai.ChatCompletionRequestMessage{
    .{ .role = "user", .content = .{ .string = "Say hi." } },
};

var completion = try openai.chat.completions.create(&client, .{
    .model = "openrouter/free",
    .messages = &messages,
});
defer completion.deinit();

Provider-specific fields can be sent with extra_body.

API shape

openai.responses.create(&client, request)
openai.responses.createResult(&client, request)
openai.responses.streamEvents(openai.ResponseStreamEvent, &client, request, &callback)

openai.chat.completions.create(&client, request)
openai.chat.completions.createResult(&client, request)
openai.chat.completions.streamEvents(openai.ChatCompletionChunk, &client, request, &callback)

Lower-level helpers are generated too:

openai.postJsonRaw(&client, "/chat/completions", payload)
openai.postJsonResult(openai.CreateChatCompletionResponse, &client, "/chat/completions", payload)

Regenerate

No OpenAPI spec is vendored.

zig build generate
zig build test

zig build generate fetches the spec, converts YAML to JSON, runs the pinned openapi2zig dependency, writes src/api.zig, then formats it.

Spec source:

https://app.stainless.com/api/spec/documented/openai/openapi.documented.yml

Requirements: curl and yq.

About

OpenAI API Client for Zig

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages