https://github.com/theseyan/openai-zig
OpenAI API client SDK for Zig
https://github.com/theseyan/openai-zig
api openai zig
Last synced: 23 days ago
JSON representation
OpenAI API client SDK for Zig
- Host: GitHub
- URL: https://github.com/theseyan/openai-zig
- Owner: theseyan
- License: mit
- Created: 2026-05-30T07:59:59.000Z (24 days ago)
- Default Branch: main
- Last Pushed: 2026-05-30T15:18:59.000Z (24 days ago)
- Last Synced: 2026-05-30T17:10:32.851Z (24 days ago)
- Topics: api, openai, zig
- Language: Zig
- Homepage: https://theseyan.github.io/openai-zig/
- Size: 59.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
openai-zig
A Zig client for the OpenAI API.
## Features
- An easy-to-use interface, similar to `openai-python`
- Built-in retry logic
- Environment variable config support for API keys, organization IDs, project IDs, and base URLs
- Chat completions, including streaming responses, image/audio/file inputs, and tool calling
- Embeddings
- Models
- Files upload with `multipart/form-data`
- Generic `request`, `requestStream`, and multipart request methods for missing endpoints
## Installation
To install the latest version of `openai-zig`, run
```bash
zig fetch --save "git+https://github.com/theseyan/openai-zig"
```
To install a specific version, run
```bash
zig fetch --save "https://github.com/theseyan/openai-zig/archive/refs/tags/.tar.gz"
```
This branch targets Zig `0.16.0`.
And add the following to your `build.zig`
```zig
const openai = b.dependency("openai_zig", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("openai", openai.module("openai"));
```
## Usage
|✨ Documentation ✨||
|--|--|
|📙 openai-zig Docs |Generated with `zig build docs` |
|📗 OpenAI API Docs||
### Client Configuration
```zig
const openai = @import("openai");
const OpenAI = openai.OpenAI;
```
```zig
pub fn main(init: std.process.Init) !void {
// Uses OPENAI_API_KEY from init.environ_map, or pass .api_key explicitly.
var client = try OpenAI.init(init.gpa, init.io, .{
.environ_map = init.environ_map,
});
defer client.deinit();
}
```
For applications that manage their own allocator and IO implementation:
```zig
var io: std.Io.Threaded = .init(allocator, .{});
defer io.deinit();
var client = try OpenAI.init(allocator, io.io(), .{
.api_key = "sk-...",
// Optional; defaults to "openai-zig/0.1.0".
.user_agent = "my-app/1.0",
});
defer client.deinit();
```
### Chat Completions
#### Regular
```zig
const ChatMessage = openai.ChatMessage;
var response = try client.chat.completions.create(.{
.model = "gpt-4o",
.messages = &[_]ChatMessage{
.{
.role = "user",
.content = .{ .text = "Hello, world!" },
},
},
});
// This will free all the memory allocated for the response
defer response.deinit();
if (response.choices[0].message.content) |content| {
std.log.debug("{s}", .{content});
}
```
#### Streamed Response
```zig
var stream = try client.chat.completions.createStream(.{
.model = "gpt-4o-mini",
.messages = &[_]ChatMessage{
.{
.role = "user",
.content = .{ .text = "Write me a poem about lizards. Make it a paragraph or two." },
},
},
});
defer stream.deinit();
std.debug.print("\n", .{});
while (try stream.next()) |val| {
if (val.choices[0].delta.content) |content| {
std.debug.print("{s}", .{content});
}
}
std.debug.print("\n", .{});
```
#### Image Understanding
```zig
const ChatContentPart = openai.ChatContentPart;
const parts = [_]ChatContentPart{
.{ .text = "What is in this image?" },
.{ .image_url = .{
.url = "https://example.com/image.png",
.detail = .high,
} },
};
var response = try client.chat.completions.create(.{
.model = "gpt-4o-mini",
.messages = &[_]ChatMessage{
.{
.role = "user",
.content = .{ .parts = &parts },
},
},
});
defer response.deinit();
```
For local image bytes, pass a data URL in `ImageUrl.url`, such as `data:image/png;base64,...`.
Use `openai.ImageUrl.dataUrl(allocator, "image/png", bytes)` to build one.
The same content-part API supports modern multimodal inputs:
```zig
const parts = [_]ChatContentPart{
.{ .text = "Summarize these inputs." },
.{ .input_audio = .{ .data = base64_wav, .format = "wav" } },
.{ .file = .{ .file_id = "file_abc123" } },
};
```
#### Structured Outputs
```zig
const CalendarEvent = struct {
name: []const u8,
participants: []const []const u8,
location: ?[]const u8 = null,
};
var output = try openai.StructuredOutput(CalendarEvent).init(allocator, .{
.name = "calendar_event",
.description = "Extract a calendar event.",
});
defer output.deinit();
var response = try client.chat.completions.create(.{
.model = "gpt-4o-mini",
.messages = &[_]ChatMessage{
.{
.role = "user",
.content = .{ .text = "Ada and Grace are reviewing the board plan." },
},
},
.response_format = output.responseFormat(),
});
defer response.deinit();
var event = try output.parse(allocator, response.choices[0].message.content.?);
defer event.deinit();
std.log.debug("event: {s}", .{event.value.name});
```
#### Tool Calling
```zig
const ChatTool = openai.ChatTool;
const ToolCall = openai.ToolCall;
const tools = [_]ChatTool{
.{
.function = .{
.name = "get_weather",
.description = "Get weather for a location",
.parameters = schema_json_value,
},
},
};
var response = try client.chat.completions.create(.{
.model = "gpt-4o-mini",
.messages = &[_]ChatMessage{
.{
.role = "user",
.content = .{ .text = "Weather in Paris?" },
},
},
.tools = &tools,
.tool_choice = .auto,
});
defer response.deinit();
if (response.choices[0].message.tool_calls) |tool_calls| {
const call = tool_calls[0];
if (call.function) |function| {
std.log.debug("Call {s} with {s}", .{ function.name, function.arguments });
}
}
```
Send tool results back with a tool message:
```zig
const tool_calls = [_]ToolCall{
.{
.id = "call_123",
.function = .{
.name = "get_weather",
.arguments = "{\"location\":\"Paris\"}",
},
},
};
var follow_up = try client.chat.completions.create(.{
.model = "gpt-4o-mini",
.messages = &[_]ChatMessage{
.{
.role = "assistant",
.tool_calls = &tool_calls,
},
.{
.role = "tool",
.content = .{ .text = "{\"temperature\":\"18C\"}" },
.tool_call_id = "call_123",
},
},
});
defer follow_up.deinit();
```
For idiomatic Zig tool functions, use the compile-time `Tools` helper. It generates function tool schemas from Zig argument structs and dispatches returned tool calls back to the registered functions.
```zig
const WeatherArgs = struct {
location: []const u8,
unit: enum { c, f } = .c,
};
const WeatherResult = struct {
temperature: []const u8,
};
fn getWeather(args: WeatherArgs) !WeatherResult {
_ = args;
return .{ .temperature = "18C" };
}
const ToolSet = openai.Tools(.{
.{
.name = "get_weather",
.description = "Get weather for a location",
.function = getWeather,
},
});
var tool_set = try ToolSet.init(allocator);
defer tool_set.deinit();
var response = try client.chat.completions.create(.{
.model = "gpt-4o-mini",
.messages = &messages,
.tools = tool_set.definitions,
});
defer response.deinit();
if (response.choices[0].message.tool_calls) |tool_calls| {
const tool_messages = try tool_set.runAll(allocator, tool_calls);
defer tool_messages.deinit();
// Send `tool_messages.messages` in the next chat completion request.
}
```
### Embeddings
```zig
const inputs = [_][]const u8{ "Hello", "Foo", "Bar" };
const response = try client.embeddings.create(.{
.model = "text-embedding-3-small",
.input = .{ .texts = &inputs },
.encoding_format = "float",
});
// Don't forget to free resources!
defer response.deinit();
const vector = response.data[0].embedding.float;
std.log.debug("Model: {s}\nNumber of Embeddings: {d}\nDimensions of Embeddings: {d}", .{
response.model, response.data.len, vector.len,
});
```
### Files
```zig
var response = try client.files.create(.{
.file = .{
.filename = "training.jsonl",
.content = file_bytes,
.content_type = "application/jsonl",
},
.purpose = .@"fine-tune",
});
defer response.deinit();
std.log.debug("Uploaded file: {s}", .{response.id});
```
`files.create` sends `multipart/form-data`. Optional expiration metadata is supported with `.expires_after`.
```zig
var files = try client.files.list(.{
.purpose = "fine-tune",
.limit = 20,
.order = .desc,
});
defer files.deinit();
var file = try client.files.retrieve(files.data[0].id);
defer file.deinit();
const contents = try client.files.content(file.id, 10 * 1024 * 1024);
defer client.allocator.free(contents);
var deleted = try client.files.delete(file.id);
defer deleted.deinit();
```
### Models
#### Get model details
```zig
var response = try client.models.retrieve("gpt-4o");
defer response.deinit();
std.log.debug("Model is owned by '{s}'", .{response.owned_by});
```
#### List all models
```zig
var response = try client.models.list();
defer response.deinit();
std.log.debug("The first model you have available is '{s}'", .{response.data[0].id});
```
#### Delete a fine-tuned model
```zig
var deleted = try client.models.delete("ft:gpt-4o-mini:org:custom:abc");
defer deleted.deinit();
std.log.debug("Deleted: {}", .{deleted.deleted});
```
## Configuring Logging
By default all logs are enabled for your entire application.
To configure your application, and set the log level for `openai-zig`, include the following in your `main.zig`.
```zig
pub const std_options = std.Options{
.log_level = .debug, // this sets your app level log config
.log_scope_levels = &[_]std.log.ScopeLevel{
.{
.scope = .openai,
.level = .info, // set to .debug, .warn, .info, or .err
},
},
};
```
All logs in `openai-zig` use the scope `.openai`, so if you don't want to see debug/info logs of the requests being sent, set `.level = .err`. This will only display when an error occurs that the client can't recover from.
## Contributions
Contributions are welcome and encouraged! Submit an issue for any bugs/feature requests and open a PR if you tackled one of them!
## Building Docs
```bash
zig build
zig build test
zig build docs
```