zep

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 17 Imported by: 0

README

zep-go

A complete, production-grade Go client for the Zep AI memory API — Threads, Users, Context Templates, the temporal Knowledge Graph, Batch ingestion, Projects, Tasks, and Webhook signature verification.

Zero external runtime dependencies — built entirely on the standard library (net/http, encoding/json, crypto/hmac).

Package layout (domain-driven design)

The root package (zep.go — the only file in the repo's root directory) wires together a *Client and its shared HTTP transport. Every domain owns its own package and its own types:

Package Covers
thread Thread, Message, rolling Summary, assembled user Context
user User identity, per-user summary instructions
graph the temporal Knowledge Graph — Edge, Node, Episode, Scope, ontology, pattern detection
contexttemplate custom context-rendering templates
project settings for the current API key/project
task polling for asynchronous operations
batch bulk ingestion jobs
webhook verifying incoming webhook deliveries — no Client required
apierror the *Error type returned for non-2xx responses
pagination the generic Iterator[T] used by every list endpoint
param shared *string/*int/*bool builders and query/body setters
polling the shared Await/backoff loop used by task and batch
transport the low-level HTTP execution every domain service uses

Each domain is reachable off the root Client (client.Thread, client.User, client.Graph, ...), but its request/response and params types live in that domain's own package, imported alongside zep itself.

Installation

go get github.com/iamkanishka/zep-go

Quick start

package main

import (
	"context"
	"log"

	zep "github.com/iamkanishka/zep-go"
	"github.com/iamkanishka/zep-go/thread"
	"github.com/iamkanishka/zep-go/user"
)

func main() {
	client, err := zep.NewClient("z_...") // or leave empty and set ZEP_API_KEY
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	_, err = client.User.Add(ctx, "user-1", &user.AddParams{
		FirstName: zep.String("Ada"),
		LastName:  zep.String("Lovelace"),
	})
	if err != nil {
		log.Fatal(err)
	}

	_, err = client.Thread.Create(ctx, "thread-1", "user-1", nil)
	if err != nil {
		log.Fatal(err)
	}

	_, err = client.Thread.AddMessages(ctx, "thread-1", []thread.Message{
		{Role: "user", Content: "Hi, I'm Ada. I love analytical engines."},
	}, nil)
	if err != nil {
		log.Fatal(err)
	}

	threadCtx, err := client.Thread.GetUserContext(ctx, "thread-1", nil)
	if err != nil {
		log.Fatal(err)
	}
	log.Println(threadCtx.Context)
}

Configuration

A *Client is built once via NewClient and is safe for concurrent use by multiple goroutines — construct one per API key/project and share it, the same way you'd share an http.Client.

client, err := zep.NewClient(
	"z_...",
	zep.WithBaseURL("https://api.getzep.com"), // default
	zep.WithMaxRetries(2),                     // default; 0 disables retries
	zep.WithReceiveTimeout(30 * time.Second),  // default
	zep.WithHTTPClient(customHTTPClient),      // full control over transport
)

If the API key argument is empty, NewClient falls back to the ZEP_API_KEY environment variable and returns an error if neither is set. MustNewClient panics instead, for program-initialization contexts.

Resources

Field Type Covers
client.Thread *thread.Service list, create, delete, get/add messages, user context, rolling summary, update message
client.User *user.Service add, get, update, delete, ListOrdered, GetThreads, summary instructions
client.Context *contexttemplate.Service context-rendering templates (list/get/create/update/delete)
client.Graph *graph.Service create/get/update/delete/ListAllGraphs, Add, AddBatch, AddFactTriple, Clone, Search, ontology, pattern detection, cache warming
client.Graph.Edge *graph.EdgeService individual fact/edge CRUD
client.Graph.Episode *graph.EpisodeService individual episode CRUD + streaming
client.Graph.Node *graph.NodeService individual node CRUD + connected edges
client.Graph.CustomInstructions *graph.CustomInstructionsService per-graph extraction instructions
client.Graph.Observations *graph.ObservationsService standalone timestamped graph notes
client.Graph.ThreadSummaries *graph.ThreadSummariesService thread summaries folded into a graph
client.Project *project.Service current project settings
client.Task *task.Service async task status + polling (Await)
client.Batch *batch.Service bulk ingestion jobs: create → add → process → poll/list
webhook.Verify verify incoming Zep webhook deliveries (HMAC-SHA256 / Svix) — no Client needed

Pagination

Paginated list endpoints have a corresponding Iterator-returning method (e.g. thread.Service.List alongside thread.Service.ListAll) built on the generic pagination.Iterator[T] type, which walks every page lazily:

it := client.Thread.ListAll(ctx, &thread.ListParams{PageSize: zep.Int(100)})
for it.Next(ctx) {
	t := it.Item()
	// ...
}
if err := it.Err(); err != nil {
	log.Fatal(err)
}

Error handling

Every method returns a plain Go error. API errors (non-2xx responses) are always of concrete type *zep.Error (an alias for *apierror.Error) and can be inspected with errors.As or the IsNotFound/IsForbidden/etc. helpers, available on both the root zep package and apierror directly:

_, err := client.Thread.GetSummary(ctx, "thread-1")
switch {
case zep.IsNotFound(err):
	// no summary yet
case zep.IsForbidden(err):
	// plan upgrade required
case err != nil:
	log.Fatal(err)
}

429 and 5xx responses are retried automatically with exponential backoff (WithMaxRetries, default 2); 4xx errors are not retried. Transport failures (DNS, connection refused, timeouts) are returned as their underlying error — use errors.Is/context.DeadlineExceeded as usual.

Batch ingestion

The Batch API is a three-step lifecycle — create an empty batch, add up to 500 items per call (up to 50,000 per batch), then start processing:

job, err := client.Batch.Create(ctx, &batch.CreateParams{
	Metadata: map[string]any{"description": "Support backfill"},
})

err = client.Batch.Add(ctx, job.BatchID, []batch.ItemInput{
	{Type: "graph_episode", UserID: "alice", Data: "Alice upgraded to Pro.", DataType: "text"},
	{Type: "thread_message", ThreadID: "alice-support-42", Content: "Dashboard won't load.", Role: "user", Name: "Alice"},
})

err = client.Batch.Process(ctx, job.BatchID)
final, err := client.Batch.Await(ctx, job.BatchID, nil)

graph.Service.AddBatch remains fine for small (≤20 episodes), same-graph, order-independent batches. For everything larger, or batches mixing thread messages and graph episodes across multiple targets, use client.Batch. For batches with thousands of items, prefer subscribing to the ingest.batch.completed webhook over polling Await. thread.Service.AddMessagesBatch is deprecated in favor of client.Batch.

Webhooks

Zep signs webhook deliveries via Svix (HMAC-SHA256). Endpoint management (creating/rotating endpoints) happens in the Zep dashboard; webhook.Verify verifies deliveries your server receives — it needs no *zep.Client:

import "github.com/iamkanishka/zep-go/webhook"

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "read error", http.StatusBadRequest)
		return
	}

	err = webhook.Verify(body, webhook.Headers{
		SvixID:        r.Header.Get("svix-id"),
		SvixTimestamp: r.Header.Get("svix-timestamp"),
		SvixSignature: r.Header.Get("svix-signature"),
	}, signingSecret)
	if err != nil {
		http.Error(w, "invalid signature: "+err.Error(), http.StatusBadRequest)
		return
	}

	// handle the event...
}

Always verify against the raw request body — many web frameworks parse JSON before your handler runs, which breaks verification.

Graph scoping

Every graph operation is scoped to exactly one of a user's graph or a standalone graph, via graph.Scope:

client.Graph.Add(ctx, graph.DataTypeText, "Ada loves math.", graph.AddParams{
	Scope: graph.UserScope("user-1"),
})

client.Graph.Add(ctx, graph.DataTypeText, "Company policy update.", graph.AddParams{
	Scope: graph.GraphIDScope("company-docs"),
})

Passing neither or both returns graph.ErrInvalidScope before any HTTP request is made.

Development

go build ./...
go vet ./...
go test ./... -race -count=1 -cover
gofmt -l .

Documentation

Overview

Package zep provides a complete Go client for the Zep AI memory API (https://www.getzep.com) - Threads, Users, Context Templates, the temporal Knowledge Graph, Batch ingestion, Projects, Tasks, and Webhook signature verification.

Domain-driven layout

This is the only file in the root package - it wires together the Client and its transport. Every domain lives in its own package and owns its own types:

thread            - Thread, Message, and rolling Summary
user              - User identity and per-user instructions
graph             - the temporal Knowledge Graph (Edge, Node, Episode, ...)
contexttemplate   - custom context-rendering templates
project           - settings for the current API key/project
task              - polling for asynchronous operations
batch             - bulk ingestion jobs
webhook           - verifying incoming webhook deliveries (no Client needed)
apierror          - the *Error type returned for non-2xx responses
pagination        - the generic Iterator used by every list endpoint
param             - shared *string/*int/*bool builders and setters
polling           - the shared Await/backoff loop used by task and batch
transport         - the low-level HTTP execution every domain service uses

Quick start

client, err := zep.NewClient("z_...")
if err != nil {
	log.Fatal(err)
}

u, err := client.User.Add(ctx, "user-1", &user.AddParams{
	FirstName: zep.String("Ada"),
	LastName:  zep.String("Lovelace"),
})

t, err := client.Thread.Create(ctx, "thread-1", "user-1", nil)

_, err = client.Thread.AddMessages(ctx, "thread-1", []thread.Message{
	{Role: "user", Content: "Hi, I'm Ada."},
}, nil)

ctxBlock, err := client.Thread.GetUserContext(ctx, "thread-1", nil)

Configuration

A Client is built once via NewClient and is safe for concurrent use by multiple goroutines - construct one per API key/project and share it, the same way you would share an http.Client.

Errors

Every method returns a plain Go error. API errors (non-2xx responses) are always of concrete type *Error (an alias for *apierror.Error) and can be inspected with errors.As or the IsNotFound, IsForbidden, etc. helpers. Transport failures (DNS, connection refused, timeouts) are returned as their underlying error, optionally wrapped - use errors.Is / context.DeadlineExceeded as usual.

Pagination

Paginated list endpoints have a corresponding streaming iterator (for example thread.Service.ListAll alongside thread.Service.List) built on the generic [pagination.Iterator] type, which walks every page lazily as you call Next.

Example (Pagination)
package main

import (
	"context"
	"fmt"
	"log"

	zep "github.com/iamkanishka/zep-go"
	"github.com/iamkanishka/zep-go/thread"
)

func main() {
	client, err := zep.NewClient("z_...")
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	it := client.Thread.ListAll(ctx, &thread.ListParams{PageSize: zep.Int(100)})
	for it.Next(ctx) {
		t := it.Item()
		fmt.Println(t.ThreadID)
	}
	if err := it.Err(); err != nil {
		log.Fatal(err)
	}
}

Index

Examples

Constants

View Source
const (
	ErrorReasonBadRequest          = apierror.ReasonBadRequest
	ErrorReasonUnauthorized        = apierror.ReasonUnauthorized
	ErrorReasonForbidden           = apierror.ReasonForbidden
	ErrorReasonNotFound            = apierror.ReasonNotFound
	ErrorReasonConflict            = apierror.ReasonConflict
	ErrorReasonUnprocessableEntity = apierror.ReasonUnprocessableEntity
	ErrorReasonRateLimited         = apierror.ReasonRateLimited
	ErrorReasonInternalServerError = apierror.ReasonInternalServerError
	ErrorReasonServiceUnavailable  = apierror.ReasonServiceUnavailable
	ErrorReasonUnknown             = apierror.ReasonUnknown
)

Known error reasons, derived from the response's HTTP status code.

View Source
const DefaultBaseURL = "https://api.getzep.com"

DefaultBaseURL is used when no WithBaseURL option is given.

View Source
const Version = "1.0.0"

Version is the current package version, sent as part of the User-Agent header on every request.

Variables

View Source
var ErrTimeout = polling.ErrTimeout

ErrTimeout is returned by Await-style methods when the polling deadline elapses before the task/batch reaches a terminal status.

Functions

func Bool

func Bool(b bool) *bool

Bool returns a pointer to the given bool.

func Float64

func Float64(f float64) *float64

Float64 returns a pointer to the given float64.

func Int

func Int(i int) *int

Int returns a pointer to the given int.

func IsConflict

func IsConflict(err error) bool

IsConflict reports whether err is a *Error with ErrorReasonConflict.

func IsForbidden

func IsForbidden(err error) bool

IsForbidden reports whether err is a *Error with ErrorReasonForbidden.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err is a *Error with ErrorReasonNotFound.

Example
package main

import (
	"context"
	"fmt"
	"log"

	zep "github.com/iamkanishka/zep-go"
)

func main() {
	client, err := zep.NewClient("z_...")
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	_, err = client.Thread.GetSummary(ctx, "thread-1")
	switch {
	case zep.IsNotFound(err):
		fmt.Println("no summary yet")
	case err != nil:
		log.Fatal(err)
	}
}

func IsRateLimited

func IsRateLimited(err error) bool

IsRateLimited reports whether err is a *Error with ErrorReasonRateLimited.

func IsUnauthorized

func IsUnauthorized(err error) bool

IsUnauthorized reports whether err is a *Error with ErrorReasonUnauthorized.

func String

func String(s string) *string

String returns a pointer to the given string. Convenience helper for populating optional *string fields in params structs without a temporary variable, mirroring the pattern used throughout the Go standard library's own ecosystem (e.g. aws-sdk-go's aws.String).

Types

type AwaitParams

type AwaitParams = polling.AwaitParams

AwaitParams configures the Await methods on the task and batch packages' Service types.

type Client

type Client struct {

	// Resource services, each backed by this Client's shared transport.
	Thread  *thread.Service
	User    *user.Service
	Context *contexttemplate.Service
	Graph   *graph.Service
	Project *project.Service
	Task    *task.Service
	Batch   *batch.Service
	// contains filtered or unexported fields
}

Client is a configured handle for talking to the Zep API. Build one with NewClient and reuse it - it is safe for concurrent use. Each domain is exposed as a field backed by that domain's own package.

func MustNewClient

func MustNewClient(apiKey string, opts ...ClientOption) *Client

MustNewClient is like NewClient but panics on error. Intended for program initialization (e.g. package-level vars in a main package), where a misconfigured client should fail fast.

func NewClient

func NewClient(apiKey string, opts ...ClientOption) (*Client, error)

NewClient builds a new Client. apiKey is your Zep project API key; if empty, NewClient looks it up from the ZEP_API_KEY environment variable and returns an error if neither is set.

Example
package main

import (
	"log"

	zep "github.com/iamkanishka/zep-go"
)

func main() {
	client, err := zep.NewClient("z_...")
	if err != nil {
		log.Fatal(err)
	}
	_ = client
}

type ClientOption

type ClientOption func(*Client)

ClientOption configures a Client constructed via NewClient.

func WithBaseURL

func WithBaseURL(baseURL string) ClientOption

WithBaseURL overrides the API base URL (default DefaultBaseURL). Useful for testing against a local server or a regional endpoint.

func WithHTTPClient

func WithHTTPClient(httpClient *http.Client) ClientOption

WithHTTPClient overrides the underlying http.Client. Use this to customize transport-level behavior (proxies, TLS config, connection pooling) or to inject a test double.

func WithMaxRetries

func WithMaxRetries(n int) ClientOption

WithMaxRetries sets how many times a retriable error (HTTP 429 or 5xx) is retried with exponential backoff before giving up. Default is 2. Pass 0 to disable retries entirely.

func WithReceiveTimeout

func WithReceiveTimeout(d time.Duration) ClientOption

WithReceiveTimeout sets the per-request timeout applied via the underlying http.Client's Timeout field, unless a custom WithHTTPClient is also supplied (in which case that client's own timeout configuration wins).

func WithUserAgent

func WithUserAgent(userAgent string) ClientOption

WithUserAgent overrides the User-Agent header sent with every request.

type Error

type Error = apierror.Error

Error represents a non-2xx response from the Zep API. It is a type alias for apierror.Error - see that package for details.

type ErrorReason

type ErrorReason = apierror.Reason

ErrorReason classifies an API error by HTTP status code.

Directories

Path Synopsis
Package apierror defines the error type returned for non-2xx responses from the Zep API, and helpers for classifying/inspecting it.
Package apierror defines the error type returned for non-2xx responses from the Zep API, and helpers for classifying/inspecting it.
Package batch implements the Batch domain - the recommended way to load large historical datasets (backfills, document collections, archived conversations, migrations) into Context Graphs.
Package batch implements the Batch domain - the recommended way to load large historical datasets (backfills, document collections, archived conversations, migrations) into Context Graphs.
Package contexttemplate implements the Context Template domain: custom context-rendering templates referenced by TemplateID from the thread package's GetUserContext.
Package contexttemplate implements the Context Template domain: custom context-rendering templates referenced by TemplateID from the thread package's GetUserContext.
Package graph implements the Graph domain: the temporal knowledge graph - standalone/group/user graphs, episodes, edges, nodes, custom instructions, observations, and folded-in thread summaries.
Package graph implements the Graph domain: the temporal knowledge graph - standalone/group/user graphs, episodes, edges, nodes, custom instructions, observations, and folded-in thread summaries.
Package pagination provides a lazy, page-by-page iterator used by every paginated list endpoint across the Zep SDK's domain packages.
Package pagination provides a lazy, page-by-page iterator used by every paginated list endpoint across the Zep SDK's domain packages.
Package param provides small shared helpers for building optional request bodies/query strings from pointer fields, and for constructing those pointers in the first place.
Package param provides small shared helpers for building optional request bodies/query strings from pointer fields, and for constructing those pointers in the first place.
Package polling provides the shared "poll until terminal status, or time out" loop used by both the task and batch domain packages' Await methods.
Package polling provides the shared "poll until terminal status, or time out" loop used by both the task and batch domain packages' Await methods.
Package project implements the Project domain: settings for the API key currently in use.
Package project implements the Project domain: settings for the API key currently in use.
Package task implements the Task domain: status polling for asynchronous, long-running Zep operations.
Package task implements the Task domain: status polling for asynchronous, long-running Zep operations.
Package testutil provides small shared helpers for testing domain packages against a real httptest.Server, without each domain package needing to depend on the others' tests.
Package testutil provides small shared helpers for testing domain packages against a real httptest.Server, without each domain package needing to depend on the others' tests.
Package thread implements the Thread domain: conversation threads, the messages on them, rolling summaries, and the assembled user-context block.
Package thread implements the Thread domain: conversation threads, the messages on them, rolling summaries, and the assembled user-context block.
Package transport implements the shared, low-level HTTP execution used by every domain package: request construction, retries with backoff, and JSON encoding/decoding.
Package transport implements the shared, low-level HTTP execution used by every domain package: request construction, retries with backoff, and JSON encoding/decoding.
Package user implements the User domain: the identity that threads and graph memory attach to.
Package user implements the User domain: the identity that threads and graph memory attach to.
Package webhook verifies incoming Zep webhook deliveries.
Package webhook verifies incoming Zep webhook deliveries.

Jump to

Keyboard shortcuts

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