obol

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

obol-go

Go Reference

Go binding for obol — a library that reads agent transcripts and estimates their USD cost. Dialects: Claude Code, Codex, Pi, Gemini, opencode, Copilot, Kimi, and obol's own session logs.

import "github.com/prime-radiant-inc/obol-go"
go get github.com/prime-radiant-inc/obol-go

The binding has no cgo and needs no C toolchain (CGO_ENABLED=0 works). The platform's native library is embedded in the module, so a plain go get is all a consumer needs on macOS (arm64/x64) and Linux (x64/arm64).

Usage

import "github.com/prime-radiant-inc/obol-go"

obol.Version() // e.g. "0.4.1" — the Rust core version

// Dialect is required; pass one of the supported identifiers.
est, err := obol.EstimatePath("transcript.jsonl", "claude")
if err != nil {
	// *obol.ObolError carries .Code, .Kind, and .Message from the FFI error envelope.
	log.Fatal(err)
}
fmt.Println(est.TotalUSD, est.PricingAsOf)
for _, m := range est.PerModel {
	fmt.Println(m.Model, m.Provider, m.SubtotalUSD)
}

// Refresh the on-disk pricing snapshot (network call).
report, err := obol.Refresh("2026-06-12")

Dialect identifiers: claude, codex, pi, gemini, opencode, copilot, kimi, obol. An empty, unknown, or invalid dialect returns an *ObolError (Kind == "InvalidArgument" or "UnknownDialect") — there is no auto-detection.

Pricing tables must exist

EstimatePath reads a pricing snapshot from disk. Either run obol refresh (the CLI) / call obol.Refresh(...), or point OBOL_PRICING_DIR at a directory containing current.json. With no snapshot the call returns an *ObolError with Kind == "PricingTablesMissing" (code 1).

On Linux with CGO_ENABLED=0, a runtime os.Setenv("OBOL_PRICING_DIR", …) does not reach the dlopen'd native library — Go makes raw syscalls and never links libc, so the library's getenv won't see it. Set the variable before the process starts. Inherited environment is fine everywhere.

Pointing at a specific library

Set OBOL_LIB to an explicit path to override the embedded library. On macOS under a hardened runtime with library validation, an unsigned extracted dylib may be rejected — point OBOL_LIB at a signed copy in that case.

How it works

The binding loads obol's native library at runtime via purego (dlopen) and re-types the JSON the Rust core returns into idiomatic Go structs. The Rust core stays the single source of truth for all accounting; this package only marshals C strings and unmarshals JSON. In the published module the platform library is embedded and extracted to a content-hashed dir under os.UserCacheDir() (or the temp dir) on first use, then dlopen'd.

This is a generated repository

The source of truth lives in the main repo under bindings/go/. This repository is populated automatically by obol's release workflow: each vX.Y.Z release assembles the Go source together with the prebuilt native libraries and tags a matching release here, so go get resolves a self-contained module.

License

Apache-2.0. See LICENSE and NOTICE.

Documentation

Overview

Package obol is a thin purego binding over obol-core's C ABI. The Rust core owns all accounting; this package only marshals C strings and unmarshals JSON. No cgo: the native library is loaded at runtime via github.com/ebitengine/purego (CGO_ENABLED=0 works).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Version

func Version() string

Version returns the obol core library version (static C string; not freed).

Types

type Approximation

type Approximation struct {
	Kind   string `json:"kind"`
	Detail string `json:"detail,omitempty"`
}

type CostEstimate

type CostEstimate struct {
	TotalUSD       float64         `json:"total_usd"`
	PerModel       []ModelCost     `json:"per_model"`
	Tokens         TokenBuckets    `json:"tokens"`
	UnpricedModels []string        `json:"unpriced_models"`
	Approximations []Approximation `json:"approximations"`
	PricingAsOf    string          `json:"pricing_as_of"`
}

func EstimatePath

func EstimatePath(path, dialect string) (*CostEstimate, error)

EstimatePath estimates a transcript file's cost. dialect is required.

type ModelCost

type ModelCost struct {
	Model       string       `json:"model"`
	Provider    string       `json:"provider"`
	Tokens      TokenBuckets `json:"tokens"`
	SubtotalUSD float64      `json:"subtotal_usd"`
}

type ObolError

type ObolError struct {
	Code    int    `json:"code"`
	Kind    string `json:"kind"`
	Message string `json:"message"`
}

ObolError carries the FFI error envelope.

func (*ObolError) Error

func (e *ObolError) Error() string

type RefreshReport

type RefreshReport struct {
	Models    uint64 `json:"models"`
	AsOf      string `json:"as_of"`
	WrittenTo string `json:"written_to"`
}

func Refresh

func Refresh(asOf string) (*RefreshReport, error)

Refresh pulls fresh pricing tables. asOf is the caller's date string.

type TokenBuckets

type TokenBuckets struct {
	Input      uint64 `json:"input"`
	Output     uint64 `json:"output"`
	CacheRead  uint64 `json:"cache_read"`
	CacheWrite uint64 `json:"cache_write"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL