The Go SDK (sdk/go/ledgersdk) covers the manual Record API. OTel-instrumented Go apps should point their existing exporter at the ledger's OTLP receiver (/v1/otlp/v1/traces) instead — there's no per-language span processor for Go.
go get github.com/bf95/AI_Comp_Ledger/sdk/go/ledgersdk
package main
import (
"time"
"github.com/bf95/AI_Comp_Ledger/sdk/go/ledgersdk"
)
func main() {
l := ledgersdk.New("http://127.0.0.1:4010", ingestKey, "urn:agent:research-assistant")
l.SetTrigger(ledgersdk.Trigger{Type: "human", Subject: "person@example.com"})
provider, model := "anthropic", "claude-fable-5"
tokensIn, tokensOut := 420, 180
response := "The policy provides that…"
span := l.Record(ledgersdk.RecordInput{
ActionType: "llm_call",
Provider: &provider,
Model: &model,
StartedAt: time.Now().Add(-time.Second),
EndedAt: time.Now(),
Status: "success",
Usage: &ledgersdk.Usage{TokensIn: &tokensIn, TokensOut: &tokensOut},
Payloads: []ledgersdk.Payload{{Kind: "response", Content: &response}},
})
l.Flush()
_ = span // the span id — pass as another event's ParentRef for decision lineage
}
Record never panics into the host application — failures go to Ledger.OnError instead. Most fields are pointers because a nil value means "not reported," which is distinct from a zero value (e.g. Usage.TokensIn == nil means unknown, not zero tokens).
Set ParentRef (and HasParentRef: true) on a RecordInput to cite the span that caused this event:
parent := searchSpan
l.Record(ledgersdk.RecordInput{
ActionType: "llm_call",
ParentRef: &parent,
HasParentRef: true,
// ...
})
Flush() delivers buffered events and is safe to call on a timer or before process exit. Delivery is at-least-once — the server dedups by span id, so a retried batch after a failed flush never double-records.