The Zep Go library provides convenient access to the Zep APIs from Go.
- Requirements
- Initialize Client
- Add Messages to A Thread
- Get Thread Context
- Optionals
- Request Options
- Automatic Retries
- Reference
- Usage
- Environments
- Pagination
- Errors
- Advanced
- Contributing
This module requires Go version >= 1.13.
Run the following command to use the Zep Go library in your module:
go get github.com/getzep/zep-go/v4import (
zep "github.com/getzep/zep-go/v4"
zepclient "github.com/getzep/zep-go/v4/client"
"github.com/getzep/zep-go/v4/option"
)
client := zepclient.NewClient(
// this api key is `api_secret` line from zep.yaml of your local server or your Zep cloud api-key
option.WithAPIKey("<YOUR_API_KEY>"),
)_, err = client.Thread.AddMessages(ctx, threadUUID, &zep.AddMessagesRequest{
Messages: []*zep.AddMessage{
{
Name: zep.String("customer"),
Content: zep.String("Hello, can I buy some shoes?"),
Role: zep.RoleTypeUser.Ptr(),
},
},
})threadContext, err := client.Thread.GetContext(
ctx,
threadUUID,
nil,
)This library models optional primitives and enum types as pointers. This is primarily meant to distinguish
default zero values from explicit values (e.g. false for bool and "" for string). A collection of
helper functions are provided to easily map a primitive or enum to its pointer-equivalent (e.g. zep.Int).
A variety of request options are included to adapt the behavior of the library, which includes configuring
authorization tokens, or providing your own instrumented *http.Client.
These request options can either be specified on the client so that they're applied on every request, or for an individual request, like so:
Providing your own
*http.Clientis recommended. Otherwise, thehttp.DefaultClientwill be used, and your client will wait indefinitely for a response (unless the per-request, context-based timeout is used).
// Specify default options applied on every request.
client := client.NewClient(
option.WithAPIKey("<YOUR_API_KEY>"),
option.WithHTTPClient(
&http.Client{
Timeout: 5 * time.Second,
},
),
)
// Specify options for an individual request.
response, err := client.Agent.Create(
...,
option.WithAPIKey("<YOUR_API_KEY>"),
)When credentials are not explicitly provided, the client reads them from the following environment variables:
ZEP_API_KEY
The Zep Go client is instrumented with automatic retries with exponential backoff. A request will be retried as long as the request is deemed retriable and the number of retry attempts has not grown larger than the configured retry limit (default: 2).
A request is deemed retriable when any of the following HTTP status codes is returned:
You can use the option.WithMaxAttempts option to configure the maximum retry limit to
your liking. For example, if you want to disable retries for the client entirely, you can
set this value to 1 like so:
client := zepclient.NewClient(
option.WithMaxAttempts(1),
)This can be done for an individual request, too:
_, _ = client.Thread.GetContext(ctx, "thread_uuid", nil, option.WithMaxAttempts(1))A full reference for this library is available here.
Instantiate and use the client with the following:
package example
import (
context "context"
zep "github.com/getzep/zep-go/v4"
client "github.com/getzep/zep-go/v4/client"
option "github.com/getzep/zep-go/v4/option"
)
func do() {
client := client.NewClient(
option.WithAPIKey(
"<value>",
),
)
request := &zep.CreateAgentRequest{
AgentID: "payments-agent",
Name: "Refund a duplicate charge",
SecurityDomain: "customer:support",
}
client.Agent.Create(
context.TODO(),
request,
)
}You can choose between different environments by using the option.WithBaseURL option. You can configure any arbitrary base
URL, which is particularly useful in test environments.
client := client.NewClient(
option.WithBaseURL(zep.Environments.Default),
)List endpoints are paginated. The SDK provides an iterator so that you can simply loop over the items.
You can also iterate page-by-page using the GetNextPage helper method.
The Page.Results attribute, which contains the relevant list of items returned by the call to the server,
is the only attribute you will need for most use cases. But if need be, several other attributes are available:
Page.Responsecontains the full spec-defined response as returned by the server.Page.StatusCodeandPage.Headerreturns HTTP metadata associated with the call to the server.Page.RawResponsereturns the pagination object if you need to access its fields (likeNext).
// Loop over the items using the provided iterator.
ctx := context.TODO()
page, err := client.Agent.List(
ctx,
...
)
if err != nil {
return err
}
iter := page.Iterator()
for iter.Next(ctx) {
item := iter.Current()
fmt.Printf("Got item: %v", *item)
}
if err := iter.Err(); err != nil {
return err
}
// Alternatively, iterate page-by-page.
for page != nil {
for _, item := range page.Results {
fmt.Printf("Got item: %v", *item)
}
page, err = page.GetNextPage(ctx)
if errors.Is(err, core.ErrNoPages) {
break
}
if err != nil {
return err
}
}
// Paginated endpoints return a Page with directly accessible headers, status code, and full response
page, err = client.Agent.List(
ctx,
...
)
if err != nil {
return err
}
// Access response metadata directly from the page
fmt.Printf("Got headers: %v", page.Header)
fmt.Printf("Got status code: %d", page.StatusCode)
// Access the full spec-defined response object
fullResponse := page.Response
// Access individual fields from the pagination object
nextCursor := page.RawResponse.NextStructured error types are returned from API calls that return non-success status codes. These errors are compatible
with the errors.Is and errors.As APIs, so you can access the error like so:
response, err := client.Agent.Create(...)
if err != nil {
var apiError *core.APIError
if errors.As(err, &apiError) {
// Do something with the API error ...
}
return err
}If you need to send a request body property that isn't part of the generated request type (e.g. an
undocumented or beta field), use the option.WithBodyProperties request option. Keys are sent exactly as
provided (use the API's wire-format names), and they override any generated field with the same name. If the
endpoint has no request body, a JSON body is created from the given properties. Body properties are applied to
JSON and form URL encoded request bodies; they are not applied to multipart file upload or raw byte requests.
response, err := client.Agent.Create(
...,
option.WithBodyProperties(map[string]interface{}{
"custom_field": "custom-value",
}),
)You can access the raw HTTP response data by using the WithRawResponse field on the client. This is useful
when you need to examine the response headers received from the API call. (When the endpoint is paginated,
the raw HTTP response data will be included automatically in the Page response object.)
response, err := client.Agent.WithRawResponse.Create(...)
if err != nil {
return err
}
fmt.Printf("Got response headers: %v", response.Header)
fmt.Printf("Got status code: %d", response.StatusCode)The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long as the request is deemed retryable and the number of retry attempts has not grown larger than the configured retry limit (default: 2).
Which status codes are retried depends on the retryStatusCodes generator configuration:
legacy (current default): retries on
recommended: retries on
- 408 (Timeout)
- 429 (Too Many Requests)
- 502 (Bad Gateway)
- 503 (Service Unavailable)
- 504 (Gateway Timeout)
If the Retry-After header is present in the response, the SDK will prioritize respecting its value exactly
over the default exponential backoff.
Use the option.WithMaxAttempts option to configure this behavior for the entire client or an individual request:
client := client.NewClient(
option.WithMaxAttempts(1),
)
response, err := client.Agent.Create(
...,
option.WithMaxAttempts(1),
)Setting a timeout for each individual request is as simple as using the standard context library. Setting a one second timeout for an individual API call looks like the following:
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
response, err := client.Agent.Create(ctx, ...)If you want to send the explicit null JSON value through an optional parameter, you can use the setters
that come with every object. Calling a setter method for a property will flip a bit in the explicitFields
bitfield for that setter's object; during serialization, any property with a flipped bit will have its
omittable status stripped, so zero or nil values will be sent explicitly rather than omitted altogether:
type ExampleRequest struct {
// An optional string parameter.
Name *string `json:"name,omitempty" url:"-"`
// Private bitmask of fields set to an explicit value and therefore not to be omitted
explicitFields *big.Int `json:"-" url:"-"`
}
request := &ExampleRequest{}
request.SetName(nil)
response, err := client.Agent.Create(ctx, request, ...)While we value open-source contributions to this SDK, this library is generated programmatically. Additions made directly to this library would have to be moved over to our generation code, otherwise they would be overwritten upon the next generated release. Feel free to open a PR as a proof of concept, but know that we will not be able to merge it as-is. We suggest opening an issue first to discuss with us!
On the other hand, contributions to the README are always very welcome!