├── .gitignore ├── thread ├── message.go ├── message │ ├── client.go │ └── raw_client.go └── client │ └── client.go ├── .fernignore ├── go.mod ├── internal ├── stringer.go ├── http.go ├── error_decoder.go ├── error_decoder_test.go ├── time.go ├── retrier.go ├── extra_properties.go ├── retrier_test.go ├── query_test.go ├── caller.go ├── extra_properties_test.go ├── query.go └── caller_test.go ├── core ├── http.go ├── api_error.go └── request_option.go ├── environments.go ├── graph ├── episode.go ├── client │ ├── base_edge.go │ ├── base_entity.go │ ├── schema.go │ ├── ontology.go │ └── client.go ├── edge │ ├── client.go │ └── raw_client.go ├── node │ ├── client.go │ └── raw_client.go └── episode │ ├── client.go │ └── raw_client.go ├── .github └── workflows │ └── ci.yml ├── client ├── client_test.go └── client.go ├── go.sum ├── file_param.go ├── project ├── client.go └── raw_client.go ├── task ├── client.go └── raw_client.go ├── errors.go ├── option └── request_option.go ├── entity_types.go ├── pointer.go ├── context ├── client.go └── raw_client.go ├── project.go ├── context.go ├── context_string_test.go ├── context_string.go ├── user └── client.go ├── task.go ├── README.md └── thread.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /thread/message.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package thread 4 | 5 | type ThreadMessageUpdate struct { 6 | Metadata map[string]interface{} `json:"metadata,omitempty" url:"-"` 7 | } 8 | -------------------------------------------------------------------------------- /.fernignore: -------------------------------------------------------------------------------- 1 | # Specify files that shouldn't be modified by Fern 2 | 3 | README.md 4 | 5 | .gitignore 6 | examples/ 7 | graph/client/base_entity.go 8 | graph/client/base_edge.go 9 | graph/client/schema.go 10 | graph/client/ontology.go 11 | entity_types.go 12 | context_string.go 13 | context_string_test.go 14 | LICENSE -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/getzep/zep-go/v3 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/google/uuid v1.4.0 7 | github.com/stretchr/testify v1.7.0 8 | ) 9 | 10 | require ( 11 | github.com/davecgh/go-spew v1.1.0 // indirect 12 | github.com/pmezard/go-difflib v1.0.0 // indirect 13 | gopkg.in/yaml.v3 v3.0.1 // indirect 14 | ) 15 | -------------------------------------------------------------------------------- /internal/stringer.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "encoding/json" 4 | 5 | // StringifyJSON returns a pretty JSON string representation of 6 | // the given value. 7 | func StringifyJSON(value interface{}) (string, error) { 8 | bytes, err := json.MarshalIndent(value, "", " ") 9 | if err != nil { 10 | return "", err 11 | } 12 | return string(bytes), nil 13 | } 14 | -------------------------------------------------------------------------------- /core/http.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import "net/http" 4 | 5 | // HTTPClient is an interface for a subset of the *http.Client. 6 | type HTTPClient interface { 7 | Do(*http.Request) (*http.Response, error) 8 | } 9 | 10 | // Response is an HTTP response from an HTTP client. 11 | type Response[T any] struct { 12 | StatusCode int 13 | Header http.Header 14 | Body T 15 | } 16 | -------------------------------------------------------------------------------- /environments.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package zep 4 | 5 | // Environments defines all of the API environments. 6 | // These values can be used with the WithBaseURL 7 | // RequestOption to override the client's default environment, 8 | // if any. 9 | var Environments = struct { 10 | Default string 11 | }{ 12 | Default: "https://api.getzep.com/api/v2", 13 | } 14 | -------------------------------------------------------------------------------- /graph/episode.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package graph 4 | 5 | type EpisodeGetByGraphIDRequest struct { 6 | // The number of most recent episodes to retrieve. 7 | Lastn *int `json:"-" url:"lastn,omitempty"` 8 | } 9 | 10 | type EpisodeGetByUserIDRequest struct { 11 | // The number of most recent episodes entries to retrieve. 12 | Lastn *int `json:"-" url:"lastn,omitempty"` 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push] 4 | 5 | jobs: 6 | compile: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout repo 10 | uses: actions/checkout@v4 11 | 12 | - name: Set up go 13 | uses: actions/setup-go@v4 14 | 15 | - name: Compile 16 | run: go build ./... 17 | test: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout repo 21 | uses: actions/checkout@v4 22 | 23 | - name: Set up go 24 | uses: actions/setup-go@v4 25 | 26 | - name: Test 27 | run: go test ./... 28 | -------------------------------------------------------------------------------- /client/client_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package client 4 | 5 | import ( 6 | option "github.com/getzep/zep-go/v3/option" 7 | assert "github.com/stretchr/testify/assert" 8 | http "net/http" 9 | testing "testing" 10 | time "time" 11 | ) 12 | 13 | func TestNewClient(t *testing.T) { 14 | t.Run("default", func(t *testing.T) { 15 | c := NewClient() 16 | assert.Empty(t, c.baseURL) 17 | }) 18 | 19 | t.Run("base url", func(t *testing.T) { 20 | c := NewClient( 21 | option.WithBaseURL("test.co"), 22 | ) 23 | assert.Equal(t, "test.co", c.baseURL) 24 | }) 25 | 26 | t.Run("http client", func(t *testing.T) { 27 | httpClient := &http.Client{ 28 | Timeout: 5 * time.Second, 29 | } 30 | c := NewClient( 31 | option.WithHTTPClient(httpClient), 32 | ) 33 | assert.Empty(t, c.baseURL) 34 | }) 35 | 36 | t.Run("http header", func(t *testing.T) { 37 | header := make(http.Header) 38 | header.Set("X-API-Tenancy", "test") 39 | c := NewClient( 40 | option.WithHTTPHeader(header), 41 | ) 42 | assert.Empty(t, c.baseURL) 43 | assert.Equal(t, "test", c.header.Get("X-API-Tenancy")) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /core/api_error.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | // APIError is a lightweight wrapper around the standard error 9 | // interface that preserves the status code from the RPC, if any. 10 | type APIError struct { 11 | err error 12 | 13 | StatusCode int `json:"-"` 14 | Header http.Header `json:"-"` 15 | } 16 | 17 | // NewAPIError constructs a new API error. 18 | func NewAPIError(statusCode int, header http.Header, err error) *APIError { 19 | return &APIError{ 20 | err: err, 21 | Header: header, 22 | StatusCode: statusCode, 23 | } 24 | } 25 | 26 | // Unwrap returns the underlying error. This also makes the error compatible 27 | // with errors.As and errors.Is. 28 | func (a *APIError) Unwrap() error { 29 | if a == nil { 30 | return nil 31 | } 32 | return a.err 33 | } 34 | 35 | // Error returns the API error's message. 36 | func (a *APIError) Error() string { 37 | if a == nil || (a.err == nil && a.StatusCode == 0) { 38 | return "" 39 | } 40 | if a.err == nil { 41 | return fmt.Sprintf("%d", a.StatusCode) 42 | } 43 | if a.StatusCode == 0 { 44 | return a.err.Error() 45 | } 46 | return fmt.Sprintf("%d: %s", a.StatusCode, a.err.Error()) 47 | } 48 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= 4 | github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 8 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 9 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 11 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 12 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 13 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 14 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 15 | -------------------------------------------------------------------------------- /file_param.go: -------------------------------------------------------------------------------- 1 | package zep 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // FileParam is a file type suitable for multipart/form-data uploads. 8 | type FileParam struct { 9 | io.Reader 10 | filename string 11 | contentType string 12 | } 13 | 14 | // FileParamOption adapts the behavior of the FileParam. No options are 15 | // implemented yet, but this interface allows for future extensibility. 16 | type FileParamOption interface { 17 | apply() 18 | } 19 | 20 | // NewFileParam returns a *FileParam type suitable for multipart/form-data uploads. All file 21 | // upload endpoints accept a simple io.Reader, which is usually created by opening a file 22 | // via os.Open. 23 | // 24 | // However, some endpoints require additional metadata about the file such as a specific 25 | // Content-Type or custom filename. FileParam makes it easier to create the correct type 26 | // signature for these endpoints. 27 | func NewFileParam( 28 | reader io.Reader, 29 | filename string, 30 | contentType string, 31 | opts ...FileParamOption, 32 | ) *FileParam { 33 | return &FileParam{ 34 | Reader: reader, 35 | filename: filename, 36 | contentType: contentType, 37 | } 38 | } 39 | 40 | func (f *FileParam) Name() string { return f.filename } 41 | func (f *FileParam) ContentType() string { return f.contentType } 42 | -------------------------------------------------------------------------------- /internal/http.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | ) 8 | 9 | // HTTPClient is an interface for a subset of the *http.Client. 10 | type HTTPClient interface { 11 | Do(*http.Request) (*http.Response, error) 12 | } 13 | 14 | // ResolveBaseURL resolves the base URL from the given arguments, 15 | // preferring the first non-empty value. 16 | func ResolveBaseURL(values ...string) string { 17 | for _, value := range values { 18 | if value != "" { 19 | return value 20 | } 21 | } 22 | return "" 23 | } 24 | 25 | // EncodeURL encodes the given arguments into the URL, escaping 26 | // values as needed. 27 | func EncodeURL(urlFormat string, args ...interface{}) string { 28 | escapedArgs := make([]interface{}, 0, len(args)) 29 | for _, arg := range args { 30 | escapedArgs = append(escapedArgs, url.PathEscape(fmt.Sprintf("%v", arg))) 31 | } 32 | return fmt.Sprintf(urlFormat, escapedArgs...) 33 | } 34 | 35 | // MergeHeaders merges the given headers together, where the right 36 | // takes precedence over the left. 37 | func MergeHeaders(left, right http.Header) http.Header { 38 | for key, values := range right { 39 | if len(values) > 1 { 40 | left[key] = values 41 | continue 42 | } 43 | if value := right.Get(key); value != "" { 44 | left.Set(key, value) 45 | } 46 | } 47 | return left 48 | } 49 | -------------------------------------------------------------------------------- /project/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package project 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | os "os" 13 | ) 14 | 15 | type Client struct { 16 | WithRawResponse *RawClient 17 | 18 | baseURL string 19 | caller *internal.Caller 20 | header http.Header 21 | } 22 | 23 | func NewClient(opts ...option.RequestOption) *Client { 24 | options := core.NewRequestOptions(opts...) 25 | if options.APIKey == "" { 26 | options.APIKey = os.Getenv("ZEP_API_KEY") 27 | } 28 | return &Client{ 29 | WithRawResponse: NewRawClient(options), 30 | baseURL: options.BaseURL, 31 | caller: internal.NewCaller( 32 | &internal.CallerParams{ 33 | Client: options.HTTPClient, 34 | MaxAttempts: options.MaxAttempts, 35 | }, 36 | ), 37 | header: options.ToHeader(), 38 | } 39 | } 40 | 41 | // Retrieve project info based on the provided api key. 42 | func (c *Client) Get( 43 | ctx context.Context, 44 | opts ...option.RequestOption, 45 | ) (*v3.ProjectInfoResponse, error) { 46 | response, err := c.WithRawResponse.Get( 47 | ctx, 48 | opts..., 49 | ) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return response.Body, nil 54 | } 55 | -------------------------------------------------------------------------------- /task/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package task 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | os "os" 13 | ) 14 | 15 | type Client struct { 16 | WithRawResponse *RawClient 17 | 18 | baseURL string 19 | caller *internal.Caller 20 | header http.Header 21 | } 22 | 23 | func NewClient(opts ...option.RequestOption) *Client { 24 | options := core.NewRequestOptions(opts...) 25 | if options.APIKey == "" { 26 | options.APIKey = os.Getenv("ZEP_API_KEY") 27 | } 28 | return &Client{ 29 | WithRawResponse: NewRawClient(options), 30 | baseURL: options.BaseURL, 31 | caller: internal.NewCaller( 32 | &internal.CallerParams{ 33 | Client: options.HTTPClient, 34 | MaxAttempts: options.MaxAttempts, 35 | }, 36 | ), 37 | header: options.ToHeader(), 38 | } 39 | } 40 | 41 | // Gets a task by its ID 42 | func (c *Client) Get( 43 | ctx context.Context, 44 | // Task ID 45 | taskID string, 46 | opts ...option.RequestOption, 47 | ) (*v3.GetTaskResponse, error) { 48 | response, err := c.WithRawResponse.Get( 49 | ctx, 50 | taskID, 51 | opts..., 52 | ) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return response.Body, nil 57 | } 58 | -------------------------------------------------------------------------------- /internal/error_decoder.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "net/http" 10 | 11 | "github.com/getzep/zep-go/v3/core" 12 | ) 13 | 14 | // ErrorDecoder decodes *http.Response errors and returns a 15 | // typed API error (e.g. *core.APIError). 16 | type ErrorDecoder func(statusCode int, header http.Header, body io.Reader) error 17 | 18 | // ErrorCodes maps HTTP status codes to error constructors. 19 | type ErrorCodes map[int]func(*core.APIError) error 20 | 21 | // NewErrorDecoder returns a new ErrorDecoder backed by the given error codes. 22 | func NewErrorDecoder(errorCodes ErrorCodes) ErrorDecoder { 23 | return func(statusCode int, header http.Header, body io.Reader) error { 24 | raw, err := io.ReadAll(body) 25 | if err != nil { 26 | return fmt.Errorf("failed to read error from response body: %w", err) 27 | } 28 | apiError := core.NewAPIError( 29 | statusCode, 30 | header, 31 | errors.New(string(raw)), 32 | ) 33 | newErrorFunc, ok := errorCodes[statusCode] 34 | if !ok { 35 | // This status code isn't recognized, so we return 36 | // the API error as-is. 37 | return apiError 38 | } 39 | customError := newErrorFunc(apiError) 40 | if err := json.NewDecoder(bytes.NewReader(raw)).Decode(customError); err != nil { 41 | // If we fail to decode the error, we return the 42 | // API error as-is. 43 | return apiError 44 | } 45 | return customError 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /thread/message/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package message 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | thread "github.com/getzep/zep-go/v3/thread" 12 | http "net/http" 13 | os "os" 14 | ) 15 | 16 | type Client struct { 17 | WithRawResponse *RawClient 18 | 19 | baseURL string 20 | caller *internal.Caller 21 | header http.Header 22 | } 23 | 24 | func NewClient(opts ...option.RequestOption) *Client { 25 | options := core.NewRequestOptions(opts...) 26 | if options.APIKey == "" { 27 | options.APIKey = os.Getenv("ZEP_API_KEY") 28 | } 29 | return &Client{ 30 | WithRawResponse: NewRawClient(options), 31 | baseURL: options.BaseURL, 32 | caller: internal.NewCaller( 33 | &internal.CallerParams{ 34 | Client: options.HTTPClient, 35 | MaxAttempts: options.MaxAttempts, 36 | }, 37 | ), 38 | header: options.ToHeader(), 39 | } 40 | } 41 | 42 | // Updates a message. 43 | func (c *Client) Update( 44 | ctx context.Context, 45 | // The UUID of the message. 46 | messageUUID string, 47 | request *thread.ThreadMessageUpdate, 48 | opts ...option.RequestOption, 49 | ) (*v3.Message, error) { 50 | response, err := c.WithRawResponse.Update( 51 | ctx, 52 | messageUUID, 53 | request, 54 | opts..., 55 | ) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return response.Body, nil 60 | } 61 | -------------------------------------------------------------------------------- /graph/client/base_edge.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/getzep/zep-go/v3" 7 | ) 8 | 9 | // BaseEdgeMetadata contains information extracted from BaseEdge struct tags 10 | type BaseEdgeMetadata struct { 11 | Description string 12 | Name string 13 | } 14 | 15 | // ExtractBaseEdgeMetadata tries to find an embedded BaseEdge by reflection 16 | // and extract metadata from direct struct tags 17 | func ExtractBaseEdgeMetadata(entity zep.EdgeDefinition) (BaseEdgeMetadata, bool) { 18 | v := reflect.ValueOf(entity) 19 | 20 | if v.Kind() == reflect.Ptr { 21 | v = v.Elem() 22 | } 23 | 24 | if v.Kind() != reflect.Struct { 25 | return BaseEdgeMetadata{}, false 26 | } 27 | 28 | t := v.Type() 29 | 30 | // Look for BaseEdge field and its struct tags 31 | for i := 0; i < t.NumField(); i++ { 32 | field := t.Field(i) 33 | 34 | // Check if this is the BaseEdge field 35 | if field.Type.Name() == "BaseEdge" && field.Anonymous { 36 | metadata := BaseEdgeMetadata{} 37 | foundName := false 38 | 39 | // Extract name tag directly 40 | nameTag := field.Tag.Get("name") 41 | if nameTag != "" { 42 | metadata.Name = nameTag 43 | foundName = true 44 | } 45 | 46 | // Extract description tag directly 47 | descTag := field.Tag.Get("description") 48 | if descTag != "" { 49 | metadata.Description = descTag 50 | } 51 | 52 | // Name is required, description is optional 53 | if foundName { 54 | return metadata, true 55 | } 56 | } 57 | } 58 | 59 | return BaseEdgeMetadata{}, false 60 | } 61 | -------------------------------------------------------------------------------- /graph/client/base_entity.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/getzep/zep-go/v3" 7 | ) 8 | 9 | // BaseEntityMetadata contains information extracted from BaseEntity struct tags 10 | type BaseEntityMetadata struct { 11 | Description string 12 | Name string 13 | } 14 | 15 | // ExtractBaseEntityMetadata tries to find an embedded BaseEntity by reflection 16 | // and extract metadata from direct struct tags 17 | func ExtractBaseEntityMetadata(entity zep.EntityDefinition) (BaseEntityMetadata, bool) { 18 | v := reflect.ValueOf(entity) 19 | 20 | if v.Kind() == reflect.Ptr { 21 | v = v.Elem() 22 | } 23 | 24 | if v.Kind() != reflect.Struct { 25 | return BaseEntityMetadata{}, false 26 | } 27 | 28 | t := v.Type() 29 | 30 | // Look for BaseEntity field and its struct tags 31 | for i := 0; i < t.NumField(); i++ { 32 | field := t.Field(i) 33 | 34 | // Check if this is the BaseEntity field 35 | if field.Type.Name() == "BaseEntity" && field.Anonymous { 36 | metadata := BaseEntityMetadata{} 37 | foundName := false 38 | 39 | // Extract name tag directly 40 | nameTag := field.Tag.Get("name") 41 | if nameTag != "" { 42 | metadata.Name = nameTag 43 | foundName = true 44 | } 45 | 46 | // Extract description tag directly 47 | descTag := field.Tag.Get("description") 48 | if descTag != "" { 49 | metadata.Description = descTag 50 | } 51 | 52 | // Name is required, description is optional 53 | if foundName { 54 | return metadata, true 55 | } 56 | } 57 | } 58 | 59 | return BaseEntityMetadata{}, false 60 | } 61 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package client 4 | 5 | import ( 6 | context "github.com/getzep/zep-go/v3/context" 7 | core "github.com/getzep/zep-go/v3/core" 8 | client "github.com/getzep/zep-go/v3/graph/client" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | project "github.com/getzep/zep-go/v3/project" 12 | task "github.com/getzep/zep-go/v3/task" 13 | threadclient "github.com/getzep/zep-go/v3/thread/client" 14 | user "github.com/getzep/zep-go/v3/user" 15 | http "net/http" 16 | os "os" 17 | ) 18 | 19 | type Client struct { 20 | Context *context.Client 21 | Graph *client.Client 22 | Project *project.Client 23 | Task *task.Client 24 | Thread *threadclient.Client 25 | User *user.Client 26 | 27 | baseURL string 28 | caller *internal.Caller 29 | header http.Header 30 | } 31 | 32 | func NewClient(opts ...option.RequestOption) *Client { 33 | options := core.NewRequestOptions(opts...) 34 | if options.APIKey == "" { 35 | options.APIKey = os.Getenv("ZEP_API_KEY") 36 | } 37 | return &Client{ 38 | Context: context.NewClient(opts...), 39 | Graph: client.NewClient(opts...), 40 | Project: project.NewClient(opts...), 41 | Task: task.NewClient(opts...), 42 | Thread: threadclient.NewClient(opts...), 43 | User: user.NewClient(opts...), 44 | baseURL: options.BaseURL, 45 | caller: internal.NewCaller( 46 | &internal.CallerParams{ 47 | Client: options.HTTPClient, 48 | MaxAttempts: options.MaxAttempts, 49 | }, 50 | ), 51 | header: options.ToHeader(), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package zep 4 | 5 | import ( 6 | json "encoding/json" 7 | core "github.com/getzep/zep-go/v3/core" 8 | ) 9 | 10 | // Bad Request 11 | type BadRequestError struct { 12 | *core.APIError 13 | Body *APIError 14 | } 15 | 16 | func (b *BadRequestError) UnmarshalJSON(data []byte) error { 17 | var body *APIError 18 | if err := json.Unmarshal(data, &body); err != nil { 19 | return err 20 | } 21 | b.StatusCode = 400 22 | b.Body = body 23 | return nil 24 | } 25 | 26 | func (b *BadRequestError) MarshalJSON() ([]byte, error) { 27 | return json.Marshal(b.Body) 28 | } 29 | 30 | func (b *BadRequestError) Unwrap() error { 31 | return b.APIError 32 | } 33 | 34 | // Internal Server Error 35 | type InternalServerError struct { 36 | *core.APIError 37 | Body *APIError 38 | } 39 | 40 | func (i *InternalServerError) UnmarshalJSON(data []byte) error { 41 | var body *APIError 42 | if err := json.Unmarshal(data, &body); err != nil { 43 | return err 44 | } 45 | i.StatusCode = 500 46 | i.Body = body 47 | return nil 48 | } 49 | 50 | func (i *InternalServerError) MarshalJSON() ([]byte, error) { 51 | return json.Marshal(i.Body) 52 | } 53 | 54 | func (i *InternalServerError) Unwrap() error { 55 | return i.APIError 56 | } 57 | 58 | // Not Found 59 | type NotFoundError struct { 60 | *core.APIError 61 | Body *APIError 62 | } 63 | 64 | func (n *NotFoundError) UnmarshalJSON(data []byte) error { 65 | var body *APIError 66 | if err := json.Unmarshal(data, &body); err != nil { 67 | return err 68 | } 69 | n.StatusCode = 404 70 | n.Body = body 71 | return nil 72 | } 73 | 74 | func (n *NotFoundError) MarshalJSON() ([]byte, error) { 75 | return json.Marshal(n.Body) 76 | } 77 | 78 | func (n *NotFoundError) Unwrap() error { 79 | return n.APIError 80 | } 81 | -------------------------------------------------------------------------------- /internal/error_decoder_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "net/http" 7 | "testing" 8 | 9 | "github.com/getzep/zep-go/v3/core" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestErrorDecoder(t *testing.T) { 14 | decoder := NewErrorDecoder( 15 | ErrorCodes{ 16 | http.StatusNotFound: func(apiError *core.APIError) error { 17 | return &NotFoundError{APIError: apiError} 18 | }, 19 | }) 20 | 21 | tests := []struct { 22 | description string 23 | giveStatusCode int 24 | giveHeader http.Header 25 | giveBody string 26 | wantError error 27 | }{ 28 | { 29 | description: "unrecognized status code", 30 | giveStatusCode: http.StatusInternalServerError, 31 | giveHeader: http.Header{}, 32 | giveBody: "Internal Server Error", 33 | wantError: core.NewAPIError(http.StatusInternalServerError, http.Header{}, errors.New("Internal Server Error")), 34 | }, 35 | { 36 | description: "not found with valid JSON", 37 | giveStatusCode: http.StatusNotFound, 38 | giveHeader: http.Header{}, 39 | giveBody: `{"message": "Resource not found"}`, 40 | wantError: &NotFoundError{ 41 | APIError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New(`{"message": "Resource not found"}`)), 42 | Message: "Resource not found", 43 | }, 44 | }, 45 | { 46 | description: "not found with invalid JSON", 47 | giveStatusCode: http.StatusNotFound, 48 | giveHeader: http.Header{}, 49 | giveBody: `Resource not found`, 50 | wantError: core.NewAPIError(http.StatusNotFound, http.Header{}, errors.New("Resource not found")), 51 | }, 52 | } 53 | 54 | for _, tt := range tests { 55 | t.Run(tt.description, func(t *testing.T) { 56 | assert.Equal(t, tt.wantError, decoder(tt.giveStatusCode, tt.giveHeader, bytes.NewReader([]byte(tt.giveBody)))) 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /option/request_option.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package option 4 | 5 | import ( 6 | core "github.com/getzep/zep-go/v3/core" 7 | http "net/http" 8 | url "net/url" 9 | ) 10 | 11 | // RequestOption adapts the behavior of an individual request. 12 | type RequestOption = core.RequestOption 13 | 14 | // WithBaseURL sets the base URL, overriding the default 15 | // environment, if any. 16 | func WithBaseURL(baseURL string) *core.BaseURLOption { 17 | return &core.BaseURLOption{ 18 | BaseURL: baseURL, 19 | } 20 | } 21 | 22 | // WithHTTPClient uses the given HTTPClient to issue the request. 23 | func WithHTTPClient(httpClient core.HTTPClient) *core.HTTPClientOption { 24 | return &core.HTTPClientOption{ 25 | HTTPClient: httpClient, 26 | } 27 | } 28 | 29 | // WithHTTPHeader adds the given http.Header to the request. 30 | func WithHTTPHeader(httpHeader http.Header) *core.HTTPHeaderOption { 31 | return &core.HTTPHeaderOption{ 32 | // Clone the headers so they can't be modified after the option call. 33 | HTTPHeader: httpHeader.Clone(), 34 | } 35 | } 36 | 37 | // WithBodyProperties adds the given body properties to the request. 38 | func WithBodyProperties(bodyProperties map[string]interface{}) *core.BodyPropertiesOption { 39 | copiedBodyProperties := make(map[string]interface{}, len(bodyProperties)) 40 | for key, value := range bodyProperties { 41 | copiedBodyProperties[key] = value 42 | } 43 | return &core.BodyPropertiesOption{ 44 | BodyProperties: copiedBodyProperties, 45 | } 46 | } 47 | 48 | // WithQueryParameters adds the given query parameters to the request. 49 | func WithQueryParameters(queryParameters url.Values) *core.QueryParametersOption { 50 | copiedQueryParameters := make(url.Values, len(queryParameters)) 51 | for key, values := range queryParameters { 52 | copiedQueryParameters[key] = values 53 | } 54 | return &core.QueryParametersOption{ 55 | QueryParameters: copiedQueryParameters, 56 | } 57 | } 58 | 59 | // WithMaxAttempts configures the maximum number of retry attempts. 60 | func WithMaxAttempts(attempts uint) *core.MaxAttemptsOption { 61 | return &core.MaxAttemptsOption{ 62 | MaxAttempts: attempts, 63 | } 64 | } 65 | 66 | // WithAPIKey sets the apiKey auth request header. 67 | func WithAPIKey(apiKey string) *core.APIKeyOption { 68 | return &core.APIKeyOption{ 69 | APIKey: apiKey, 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /task/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package task 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | ) 13 | 14 | type RawClient struct { 15 | baseURL string 16 | caller *internal.Caller 17 | header http.Header 18 | } 19 | 20 | func NewRawClient(options *core.RequestOptions) *RawClient { 21 | return &RawClient{ 22 | baseURL: options.BaseURL, 23 | caller: internal.NewCaller( 24 | &internal.CallerParams{ 25 | Client: options.HTTPClient, 26 | MaxAttempts: options.MaxAttempts, 27 | }, 28 | ), 29 | header: options.ToHeader(), 30 | } 31 | } 32 | 33 | func (r *RawClient) Get( 34 | ctx context.Context, 35 | // Task ID 36 | taskID string, 37 | opts ...option.RequestOption, 38 | ) (*core.Response[*v3.GetTaskResponse], error) { 39 | options := core.NewRequestOptions(opts...) 40 | baseURL := internal.ResolveBaseURL( 41 | options.BaseURL, 42 | r.baseURL, 43 | "https://api.getzep.com/api/v2", 44 | ) 45 | endpointURL := internal.EncodeURL( 46 | baseURL+"/tasks/%v", 47 | taskID, 48 | ) 49 | headers := internal.MergeHeaders( 50 | r.header.Clone(), 51 | options.ToHeader(), 52 | ) 53 | errorCodes := internal.ErrorCodes{ 54 | 404: func(apiError *core.APIError) error { 55 | return &v3.NotFoundError{ 56 | APIError: apiError, 57 | } 58 | }, 59 | 500: func(apiError *core.APIError) error { 60 | return &v3.InternalServerError{ 61 | APIError: apiError, 62 | } 63 | }, 64 | } 65 | var response *v3.GetTaskResponse 66 | raw, err := r.caller.Call( 67 | ctx, 68 | &internal.CallParams{ 69 | URL: endpointURL, 70 | Method: http.MethodGet, 71 | Headers: headers, 72 | MaxAttempts: options.MaxAttempts, 73 | BodyProperties: options.BodyProperties, 74 | QueryParameters: options.QueryParameters, 75 | Client: options.HTTPClient, 76 | Response: &response, 77 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 78 | }, 79 | ) 80 | if err != nil { 81 | return nil, err 82 | } 83 | return &core.Response[*v3.GetTaskResponse]{ 84 | StatusCode: raw.StatusCode, 85 | Header: raw.Header, 86 | Body: response, 87 | }, nil 88 | } 89 | -------------------------------------------------------------------------------- /project/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package project 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | ) 13 | 14 | type RawClient struct { 15 | baseURL string 16 | caller *internal.Caller 17 | header http.Header 18 | } 19 | 20 | func NewRawClient(options *core.RequestOptions) *RawClient { 21 | return &RawClient{ 22 | baseURL: options.BaseURL, 23 | caller: internal.NewCaller( 24 | &internal.CallerParams{ 25 | Client: options.HTTPClient, 26 | MaxAttempts: options.MaxAttempts, 27 | }, 28 | ), 29 | header: options.ToHeader(), 30 | } 31 | } 32 | 33 | func (r *RawClient) Get( 34 | ctx context.Context, 35 | opts ...option.RequestOption, 36 | ) (*core.Response[*v3.ProjectInfoResponse], error) { 37 | options := core.NewRequestOptions(opts...) 38 | baseURL := internal.ResolveBaseURL( 39 | options.BaseURL, 40 | r.baseURL, 41 | "https://api.getzep.com/api/v2", 42 | ) 43 | endpointURL := baseURL + "/projects/info" 44 | headers := internal.MergeHeaders( 45 | r.header.Clone(), 46 | options.ToHeader(), 47 | ) 48 | errorCodes := internal.ErrorCodes{ 49 | 400: func(apiError *core.APIError) error { 50 | return &v3.BadRequestError{ 51 | APIError: apiError, 52 | } 53 | }, 54 | 404: func(apiError *core.APIError) error { 55 | return &v3.NotFoundError{ 56 | APIError: apiError, 57 | } 58 | }, 59 | 500: func(apiError *core.APIError) error { 60 | return &v3.InternalServerError{ 61 | APIError: apiError, 62 | } 63 | }, 64 | } 65 | var response *v3.ProjectInfoResponse 66 | raw, err := r.caller.Call( 67 | ctx, 68 | &internal.CallParams{ 69 | URL: endpointURL, 70 | Method: http.MethodGet, 71 | Headers: headers, 72 | MaxAttempts: options.MaxAttempts, 73 | BodyProperties: options.BodyProperties, 74 | QueryParameters: options.QueryParameters, 75 | Client: options.HTTPClient, 76 | Response: &response, 77 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 78 | }, 79 | ) 80 | if err != nil { 81 | return nil, err 82 | } 83 | return &core.Response[*v3.ProjectInfoResponse]{ 84 | StatusCode: raw.StatusCode, 85 | Header: raw.Header, 86 | Body: response, 87 | }, nil 88 | } 89 | -------------------------------------------------------------------------------- /thread/message/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package message 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | thread "github.com/getzep/zep-go/v3/thread" 12 | http "net/http" 13 | ) 14 | 15 | type RawClient struct { 16 | baseURL string 17 | caller *internal.Caller 18 | header http.Header 19 | } 20 | 21 | func NewRawClient(options *core.RequestOptions) *RawClient { 22 | return &RawClient{ 23 | baseURL: options.BaseURL, 24 | caller: internal.NewCaller( 25 | &internal.CallerParams{ 26 | Client: options.HTTPClient, 27 | MaxAttempts: options.MaxAttempts, 28 | }, 29 | ), 30 | header: options.ToHeader(), 31 | } 32 | } 33 | 34 | func (r *RawClient) Update( 35 | ctx context.Context, 36 | // The UUID of the message. 37 | messageUUID string, 38 | request *thread.ThreadMessageUpdate, 39 | opts ...option.RequestOption, 40 | ) (*core.Response[*v3.Message], error) { 41 | options := core.NewRequestOptions(opts...) 42 | baseURL := internal.ResolveBaseURL( 43 | options.BaseURL, 44 | r.baseURL, 45 | "https://api.getzep.com/api/v2", 46 | ) 47 | endpointURL := internal.EncodeURL( 48 | baseURL+"/messages/%v", 49 | messageUUID, 50 | ) 51 | headers := internal.MergeHeaders( 52 | r.header.Clone(), 53 | options.ToHeader(), 54 | ) 55 | headers.Add("Content-Type", "application/json") 56 | errorCodes := internal.ErrorCodes{ 57 | 404: func(apiError *core.APIError) error { 58 | return &v3.NotFoundError{ 59 | APIError: apiError, 60 | } 61 | }, 62 | 500: func(apiError *core.APIError) error { 63 | return &v3.InternalServerError{ 64 | APIError: apiError, 65 | } 66 | }, 67 | } 68 | var response *v3.Message 69 | raw, err := r.caller.Call( 70 | ctx, 71 | &internal.CallParams{ 72 | URL: endpointURL, 73 | Method: http.MethodPatch, 74 | Headers: headers, 75 | MaxAttempts: options.MaxAttempts, 76 | BodyProperties: options.BodyProperties, 77 | QueryParameters: options.QueryParameters, 78 | Client: options.HTTPClient, 79 | Request: request, 80 | Response: &response, 81 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 82 | }, 83 | ) 84 | if err != nil { 85 | return nil, err 86 | } 87 | return &core.Response[*v3.Message]{ 88 | StatusCode: raw.StatusCode, 89 | Header: raw.Header, 90 | Body: response, 91 | }, nil 92 | } 93 | -------------------------------------------------------------------------------- /entity_types.go: -------------------------------------------------------------------------------- 1 | package zep 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // GraphOntologyOptions contains options for graph ontology operations 9 | type GraphOntologyOptions struct { 10 | UserIDs []string 11 | GraphIDs []string 12 | } 13 | 14 | // GraphOntologyOption is a functional option for configuring graph ontology operations 15 | type GraphOntologyOption func(*GraphOntologyOptions) 16 | 17 | // ForUsers allows to specify target user identifiers when setting entity/edge types. 18 | func ForUsers(userIDs []string) GraphOntologyOption { 19 | return func(opts *GraphOntologyOptions) { 20 | opts.UserIDs = userIDs 21 | } 22 | } 23 | 24 | // ForGraphs allows to specify target graph identifiers when setting entity/edge types. 25 | func ForGraphs(graphIDs []string) GraphOntologyOption { 26 | return func(opts *GraphOntologyOptions) { 27 | opts.GraphIDs = graphIDs 28 | } 29 | } 30 | 31 | type BaseEntity struct{} 32 | 33 | type EntityDefinition interface { 34 | // This is a marker interface - any struct embedding BaseEntity will satisfy it 35 | isEntityDefinition() 36 | } 37 | 38 | // isEntityDefinition is a marker method of EntityDefinition interface 39 | func (BaseEntity) isEntityDefinition() {} 40 | 41 | // UnmarshalNodeAttributes unmarshals a map[string]interface{} into a struct 42 | // that embeds BaseEntity 43 | func UnmarshalNodeAttributes(attributes map[string]interface{}, dest EntityDefinition) error { 44 | jsonData, err := json.Marshal(attributes) 45 | if err != nil { 46 | return fmt.Errorf("error marshaling node attributes: %w", err) 47 | } 48 | 49 | err = json.Unmarshal(jsonData, dest) 50 | if err != nil { 51 | return fmt.Errorf("error unmarshaling to struct: %w", err) 52 | } 53 | 54 | return nil 55 | } 56 | 57 | type BaseEdge struct{} 58 | 59 | type EdgeDefinitionWithSourceTargets struct { 60 | EdgeModel EdgeDefinition `json:"edge_model"` 61 | SourceTargets []EntityEdgeSourceTarget `json:"source_targets,omitempty"` 62 | } 63 | 64 | type EdgeDefinition interface { 65 | // This is a marker interface - any struct embedding BaseEdge will satisfy it 66 | isEdgeDefinition() 67 | } 68 | 69 | // isEdgeDefinition is a marker method of EntityDefinition interface 70 | func (BaseEdge) isEdgeDefinition() {} 71 | 72 | // UnmarshalEdgeAttributes unmarshals a map[string]interface{} into a struct 73 | // that embeds BaseEdge 74 | func UnmarshalEdgeAttributes(attributes map[string]interface{}, dest EdgeDefinition) error { 75 | jsonData, err := json.Marshal(attributes) 76 | if err != nil { 77 | return fmt.Errorf("error marshaling node attributes: %w", err) 78 | } 79 | 80 | err = json.Unmarshal(jsonData, dest) 81 | if err != nil { 82 | return fmt.Errorf("error unmarshaling to struct: %w", err) 83 | } 84 | 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /graph/edge/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package edge 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | os "os" 13 | ) 14 | 15 | type Client struct { 16 | WithRawResponse *RawClient 17 | 18 | baseURL string 19 | caller *internal.Caller 20 | header http.Header 21 | } 22 | 23 | func NewClient(opts ...option.RequestOption) *Client { 24 | options := core.NewRequestOptions(opts...) 25 | if options.APIKey == "" { 26 | options.APIKey = os.Getenv("ZEP_API_KEY") 27 | } 28 | return &Client{ 29 | WithRawResponse: NewRawClient(options), 30 | baseURL: options.BaseURL, 31 | caller: internal.NewCaller( 32 | &internal.CallerParams{ 33 | Client: options.HTTPClient, 34 | MaxAttempts: options.MaxAttempts, 35 | }, 36 | ), 37 | header: options.ToHeader(), 38 | } 39 | } 40 | 41 | // Returns all edges for a graph. 42 | func (c *Client) GetByGraphID( 43 | ctx context.Context, 44 | // Graph ID 45 | graphID string, 46 | request *v3.GraphEdgesRequest, 47 | opts ...option.RequestOption, 48 | ) ([]*v3.EntityEdge, error) { 49 | response, err := c.WithRawResponse.GetByGraphID( 50 | ctx, 51 | graphID, 52 | request, 53 | opts..., 54 | ) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return response.Body, nil 59 | } 60 | 61 | // Returns all edges for a user. 62 | func (c *Client) GetByUserID( 63 | ctx context.Context, 64 | // User ID 65 | userID string, 66 | request *v3.GraphEdgesRequest, 67 | opts ...option.RequestOption, 68 | ) ([]*v3.EntityEdge, error) { 69 | response, err := c.WithRawResponse.GetByUserID( 70 | ctx, 71 | userID, 72 | request, 73 | opts..., 74 | ) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return response.Body, nil 79 | } 80 | 81 | // Returns a specific edge by its UUID. 82 | func (c *Client) Get( 83 | ctx context.Context, 84 | // Edge UUID 85 | uuid string, 86 | opts ...option.RequestOption, 87 | ) (*v3.EntityEdge, error) { 88 | response, err := c.WithRawResponse.Get( 89 | ctx, 90 | uuid, 91 | opts..., 92 | ) 93 | if err != nil { 94 | return nil, err 95 | } 96 | return response.Body, nil 97 | } 98 | 99 | // Deletes an edge by UUID. 100 | func (c *Client) Delete( 101 | ctx context.Context, 102 | // Edge UUID 103 | uuid string, 104 | opts ...option.RequestOption, 105 | ) (*v3.SuccessResponse, error) { 106 | response, err := c.WithRawResponse.Delete( 107 | ctx, 108 | uuid, 109 | opts..., 110 | ) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return response.Body, nil 115 | } 116 | -------------------------------------------------------------------------------- /graph/node/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package node 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | os "os" 13 | ) 14 | 15 | type Client struct { 16 | WithRawResponse *RawClient 17 | 18 | baseURL string 19 | caller *internal.Caller 20 | header http.Header 21 | } 22 | 23 | func NewClient(opts ...option.RequestOption) *Client { 24 | options := core.NewRequestOptions(opts...) 25 | if options.APIKey == "" { 26 | options.APIKey = os.Getenv("ZEP_API_KEY") 27 | } 28 | return &Client{ 29 | WithRawResponse: NewRawClient(options), 30 | baseURL: options.BaseURL, 31 | caller: internal.NewCaller( 32 | &internal.CallerParams{ 33 | Client: options.HTTPClient, 34 | MaxAttempts: options.MaxAttempts, 35 | }, 36 | ), 37 | header: options.ToHeader(), 38 | } 39 | } 40 | 41 | // Returns all nodes for a graph. 42 | func (c *Client) GetByGraphID( 43 | ctx context.Context, 44 | // Graph ID 45 | graphID string, 46 | request *v3.GraphNodesRequest, 47 | opts ...option.RequestOption, 48 | ) ([]*v3.EntityNode, error) { 49 | response, err := c.WithRawResponse.GetByGraphID( 50 | ctx, 51 | graphID, 52 | request, 53 | opts..., 54 | ) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return response.Body, nil 59 | } 60 | 61 | // Returns all nodes for a user 62 | func (c *Client) GetByUserID( 63 | ctx context.Context, 64 | // User ID 65 | userID string, 66 | request *v3.GraphNodesRequest, 67 | opts ...option.RequestOption, 68 | ) ([]*v3.EntityNode, error) { 69 | response, err := c.WithRawResponse.GetByUserID( 70 | ctx, 71 | userID, 72 | request, 73 | opts..., 74 | ) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return response.Body, nil 79 | } 80 | 81 | // Returns all edges for a node 82 | func (c *Client) GetEdges( 83 | ctx context.Context, 84 | // Node UUID 85 | nodeUUID string, 86 | opts ...option.RequestOption, 87 | ) ([]*v3.EntityEdge, error) { 88 | response, err := c.WithRawResponse.GetEdges( 89 | ctx, 90 | nodeUUID, 91 | opts..., 92 | ) 93 | if err != nil { 94 | return nil, err 95 | } 96 | return response.Body, nil 97 | } 98 | 99 | // Returns all episodes that mentioned a given node 100 | func (c *Client) GetEpisodes( 101 | ctx context.Context, 102 | // Node UUID 103 | nodeUUID string, 104 | opts ...option.RequestOption, 105 | ) (*v3.EpisodeResponse, error) { 106 | response, err := c.WithRawResponse.GetEpisodes( 107 | ctx, 108 | nodeUUID, 109 | opts..., 110 | ) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return response.Body, nil 115 | } 116 | 117 | // Returns a specific node by its UUID. 118 | func (c *Client) Get( 119 | ctx context.Context, 120 | // Node UUID 121 | uuid string, 122 | opts ...option.RequestOption, 123 | ) (*v3.EntityNode, error) { 124 | response, err := c.WithRawResponse.Get( 125 | ctx, 126 | uuid, 127 | opts..., 128 | ) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return response.Body, nil 133 | } 134 | -------------------------------------------------------------------------------- /pointer.go: -------------------------------------------------------------------------------- 1 | package zep 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/google/uuid" 7 | ) 8 | 9 | // Bool returns a pointer to the given bool value. 10 | func Bool(b bool) *bool { 11 | return &b 12 | } 13 | 14 | // Byte returns a pointer to the given byte value. 15 | func Byte(b byte) *byte { 16 | return &b 17 | } 18 | 19 | // Complex64 returns a pointer to the given complex64 value. 20 | func Complex64(c complex64) *complex64 { 21 | return &c 22 | } 23 | 24 | // Complex128 returns a pointer to the given complex128 value. 25 | func Complex128(c complex128) *complex128 { 26 | return &c 27 | } 28 | 29 | // Float32 returns a pointer to the given float32 value. 30 | func Float32(f float32) *float32 { 31 | return &f 32 | } 33 | 34 | // Float64 returns a pointer to the given float64 value. 35 | func Float64(f float64) *float64 { 36 | return &f 37 | } 38 | 39 | // Int returns a pointer to the given int value. 40 | func Int(i int) *int { 41 | return &i 42 | } 43 | 44 | // Int8 returns a pointer to the given int8 value. 45 | func Int8(i int8) *int8 { 46 | return &i 47 | } 48 | 49 | // Int16 returns a pointer to the given int16 value. 50 | func Int16(i int16) *int16 { 51 | return &i 52 | } 53 | 54 | // Int32 returns a pointer to the given int32 value. 55 | func Int32(i int32) *int32 { 56 | return &i 57 | } 58 | 59 | // Int64 returns a pointer to the given int64 value. 60 | func Int64(i int64) *int64 { 61 | return &i 62 | } 63 | 64 | // Rune returns a pointer to the given rune value. 65 | func Rune(r rune) *rune { 66 | return &r 67 | } 68 | 69 | // String returns a pointer to the given string value. 70 | func String(s string) *string { 71 | return &s 72 | } 73 | 74 | // Uint returns a pointer to the given uint value. 75 | func Uint(u uint) *uint { 76 | return &u 77 | } 78 | 79 | // Uint8 returns a pointer to the given uint8 value. 80 | func Uint8(u uint8) *uint8 { 81 | return &u 82 | } 83 | 84 | // Uint16 returns a pointer to the given uint16 value. 85 | func Uint16(u uint16) *uint16 { 86 | return &u 87 | } 88 | 89 | // Uint32 returns a pointer to the given uint32 value. 90 | func Uint32(u uint32) *uint32 { 91 | return &u 92 | } 93 | 94 | // Uint64 returns a pointer to the given uint64 value. 95 | func Uint64(u uint64) *uint64 { 96 | return &u 97 | } 98 | 99 | // Uintptr returns a pointer to the given uintptr value. 100 | func Uintptr(u uintptr) *uintptr { 101 | return &u 102 | } 103 | 104 | // UUID returns a pointer to the given uuid.UUID value. 105 | func UUID(u uuid.UUID) *uuid.UUID { 106 | return &u 107 | } 108 | 109 | // Time returns a pointer to the given time.Time value. 110 | func Time(t time.Time) *time.Time { 111 | return &t 112 | } 113 | 114 | // MustParseDate attempts to parse the given string as a 115 | // date time.Time, and panics upon failure. 116 | func MustParseDate(date string) time.Time { 117 | t, err := time.Parse("2006-01-02", date) 118 | if err != nil { 119 | panic(err) 120 | } 121 | return t 122 | } 123 | 124 | // MustParseDateTime attempts to parse the given string as a 125 | // datetime time.Time, and panics upon failure. 126 | func MustParseDateTime(datetime string) time.Time { 127 | t, err := time.Parse(time.RFC3339, datetime) 128 | if err != nil { 129 | panic(err) 130 | } 131 | return t 132 | } 133 | -------------------------------------------------------------------------------- /graph/episode/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package episode 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | graph "github.com/getzep/zep-go/v3/graph" 10 | internal "github.com/getzep/zep-go/v3/internal" 11 | option "github.com/getzep/zep-go/v3/option" 12 | http "net/http" 13 | os "os" 14 | ) 15 | 16 | type Client struct { 17 | WithRawResponse *RawClient 18 | 19 | baseURL string 20 | caller *internal.Caller 21 | header http.Header 22 | } 23 | 24 | func NewClient(opts ...option.RequestOption) *Client { 25 | options := core.NewRequestOptions(opts...) 26 | if options.APIKey == "" { 27 | options.APIKey = os.Getenv("ZEP_API_KEY") 28 | } 29 | return &Client{ 30 | WithRawResponse: NewRawClient(options), 31 | baseURL: options.BaseURL, 32 | caller: internal.NewCaller( 33 | &internal.CallerParams{ 34 | Client: options.HTTPClient, 35 | MaxAttempts: options.MaxAttempts, 36 | }, 37 | ), 38 | header: options.ToHeader(), 39 | } 40 | } 41 | 42 | // Returns episodes by graph id. 43 | func (c *Client) GetByGraphID( 44 | ctx context.Context, 45 | // Graph ID 46 | graphID string, 47 | request *graph.EpisodeGetByGraphIDRequest, 48 | opts ...option.RequestOption, 49 | ) (*v3.EpisodeResponse, error) { 50 | response, err := c.WithRawResponse.GetByGraphID( 51 | ctx, 52 | graphID, 53 | request, 54 | opts..., 55 | ) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return response.Body, nil 60 | } 61 | 62 | // Returns episodes by user id. 63 | func (c *Client) GetByUserID( 64 | ctx context.Context, 65 | // User ID 66 | userID string, 67 | request *graph.EpisodeGetByUserIDRequest, 68 | opts ...option.RequestOption, 69 | ) (*v3.EpisodeResponse, error) { 70 | response, err := c.WithRawResponse.GetByUserID( 71 | ctx, 72 | userID, 73 | request, 74 | opts..., 75 | ) 76 | if err != nil { 77 | return nil, err 78 | } 79 | return response.Body, nil 80 | } 81 | 82 | // Returns episodes by UUID 83 | func (c *Client) Get( 84 | ctx context.Context, 85 | // Episode UUID 86 | uuid string, 87 | opts ...option.RequestOption, 88 | ) (*v3.Episode, error) { 89 | response, err := c.WithRawResponse.Get( 90 | ctx, 91 | uuid, 92 | opts..., 93 | ) 94 | if err != nil { 95 | return nil, err 96 | } 97 | return response.Body, nil 98 | } 99 | 100 | // Deletes an episode by its UUID. 101 | func (c *Client) Delete( 102 | ctx context.Context, 103 | // Episode UUID 104 | uuid string, 105 | opts ...option.RequestOption, 106 | ) (*v3.SuccessResponse, error) { 107 | response, err := c.WithRawResponse.Delete( 108 | ctx, 109 | uuid, 110 | opts..., 111 | ) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return response.Body, nil 116 | } 117 | 118 | // Returns nodes and edges mentioned in an episode 119 | func (c *Client) GetNodesAndEdges( 120 | ctx context.Context, 121 | // Episode uuid 122 | uuid string, 123 | opts ...option.RequestOption, 124 | ) (*v3.EpisodeMentions, error) { 125 | response, err := c.WithRawResponse.GetNodesAndEdges( 126 | ctx, 127 | uuid, 128 | opts..., 129 | ) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return response.Body, nil 134 | } 135 | -------------------------------------------------------------------------------- /context/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package context 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | os "os" 13 | ) 14 | 15 | type Client struct { 16 | WithRawResponse *RawClient 17 | 18 | baseURL string 19 | caller *internal.Caller 20 | header http.Header 21 | } 22 | 23 | func NewClient(opts ...option.RequestOption) *Client { 24 | options := core.NewRequestOptions(opts...) 25 | if options.APIKey == "" { 26 | options.APIKey = os.Getenv("ZEP_API_KEY") 27 | } 28 | return &Client{ 29 | WithRawResponse: NewRawClient(options), 30 | baseURL: options.BaseURL, 31 | caller: internal.NewCaller( 32 | &internal.CallerParams{ 33 | Client: options.HTTPClient, 34 | MaxAttempts: options.MaxAttempts, 35 | }, 36 | ), 37 | header: options.ToHeader(), 38 | } 39 | } 40 | 41 | // Lists all context templates. 42 | func (c *Client) ListContextTemplates( 43 | ctx context.Context, 44 | opts ...option.RequestOption, 45 | ) (*v3.ListContextTemplatesResponse, error) { 46 | response, err := c.WithRawResponse.ListContextTemplates( 47 | ctx, 48 | opts..., 49 | ) 50 | if err != nil { 51 | return nil, err 52 | } 53 | return response.Body, nil 54 | } 55 | 56 | // Creates a new context template. 57 | func (c *Client) CreateContextTemplate( 58 | ctx context.Context, 59 | request *v3.CreateContextTemplateRequest, 60 | opts ...option.RequestOption, 61 | ) (*v3.ContextTemplateResponse, error) { 62 | response, err := c.WithRawResponse.CreateContextTemplate( 63 | ctx, 64 | request, 65 | opts..., 66 | ) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return response.Body, nil 71 | } 72 | 73 | // Retrieves a context template by template_id. 74 | func (c *Client) GetContextTemplate( 75 | ctx context.Context, 76 | // Template ID 77 | templateID string, 78 | opts ...option.RequestOption, 79 | ) (*v3.ContextTemplateResponse, error) { 80 | response, err := c.WithRawResponse.GetContextTemplate( 81 | ctx, 82 | templateID, 83 | opts..., 84 | ) 85 | if err != nil { 86 | return nil, err 87 | } 88 | return response.Body, nil 89 | } 90 | 91 | // Updates an existing context template by template_id. 92 | func (c *Client) UpdateContextTemplate( 93 | ctx context.Context, 94 | // Template ID 95 | templateID string, 96 | request *v3.UpdateContextTemplateRequest, 97 | opts ...option.RequestOption, 98 | ) (*v3.ContextTemplateResponse, error) { 99 | response, err := c.WithRawResponse.UpdateContextTemplate( 100 | ctx, 101 | templateID, 102 | request, 103 | opts..., 104 | ) 105 | if err != nil { 106 | return nil, err 107 | } 108 | return response.Body, nil 109 | } 110 | 111 | // Deletes a context template by template_id. 112 | func (c *Client) DeleteContextTemplate( 113 | ctx context.Context, 114 | // Template ID 115 | templateID string, 116 | opts ...option.RequestOption, 117 | ) (*v3.SuccessResponse, error) { 118 | response, err := c.WithRawResponse.DeleteContextTemplate( 119 | ctx, 120 | templateID, 121 | opts..., 122 | ) 123 | if err != nil { 124 | return nil, err 125 | } 126 | return response.Body, nil 127 | } 128 | -------------------------------------------------------------------------------- /project.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package zep 4 | 5 | import ( 6 | json "encoding/json" 7 | fmt "fmt" 8 | internal "github.com/getzep/zep-go/v3/internal" 9 | ) 10 | 11 | type ProjectInfo struct { 12 | CreatedAt *string `json:"created_at,omitempty" url:"created_at,omitempty"` 13 | Description *string `json:"description,omitempty" url:"description,omitempty"` 14 | Name *string `json:"name,omitempty" url:"name,omitempty"` 15 | UUID *string `json:"uuid,omitempty" url:"uuid,omitempty"` 16 | 17 | extraProperties map[string]interface{} 18 | rawJSON json.RawMessage 19 | } 20 | 21 | func (p *ProjectInfo) GetCreatedAt() *string { 22 | if p == nil { 23 | return nil 24 | } 25 | return p.CreatedAt 26 | } 27 | 28 | func (p *ProjectInfo) GetDescription() *string { 29 | if p == nil { 30 | return nil 31 | } 32 | return p.Description 33 | } 34 | 35 | func (p *ProjectInfo) GetName() *string { 36 | if p == nil { 37 | return nil 38 | } 39 | return p.Name 40 | } 41 | 42 | func (p *ProjectInfo) GetUUID() *string { 43 | if p == nil { 44 | return nil 45 | } 46 | return p.UUID 47 | } 48 | 49 | func (p *ProjectInfo) GetExtraProperties() map[string]interface{} { 50 | return p.extraProperties 51 | } 52 | 53 | func (p *ProjectInfo) UnmarshalJSON(data []byte) error { 54 | type unmarshaler ProjectInfo 55 | var value unmarshaler 56 | if err := json.Unmarshal(data, &value); err != nil { 57 | return err 58 | } 59 | *p = ProjectInfo(value) 60 | extraProperties, err := internal.ExtractExtraProperties(data, *p) 61 | if err != nil { 62 | return err 63 | } 64 | p.extraProperties = extraProperties 65 | p.rawJSON = json.RawMessage(data) 66 | return nil 67 | } 68 | 69 | func (p *ProjectInfo) String() string { 70 | if len(p.rawJSON) > 0 { 71 | if value, err := internal.StringifyJSON(p.rawJSON); err == nil { 72 | return value 73 | } 74 | } 75 | if value, err := internal.StringifyJSON(p); err == nil { 76 | return value 77 | } 78 | return fmt.Sprintf("%#v", p) 79 | } 80 | 81 | type ProjectInfoResponse struct { 82 | Project *ProjectInfo `json:"project,omitempty" url:"project,omitempty"` 83 | 84 | extraProperties map[string]interface{} 85 | rawJSON json.RawMessage 86 | } 87 | 88 | func (p *ProjectInfoResponse) GetProject() *ProjectInfo { 89 | if p == nil { 90 | return nil 91 | } 92 | return p.Project 93 | } 94 | 95 | func (p *ProjectInfoResponse) GetExtraProperties() map[string]interface{} { 96 | return p.extraProperties 97 | } 98 | 99 | func (p *ProjectInfoResponse) UnmarshalJSON(data []byte) error { 100 | type unmarshaler ProjectInfoResponse 101 | var value unmarshaler 102 | if err := json.Unmarshal(data, &value); err != nil { 103 | return err 104 | } 105 | *p = ProjectInfoResponse(value) 106 | extraProperties, err := internal.ExtractExtraProperties(data, *p) 107 | if err != nil { 108 | return err 109 | } 110 | p.extraProperties = extraProperties 111 | p.rawJSON = json.RawMessage(data) 112 | return nil 113 | } 114 | 115 | func (p *ProjectInfoResponse) String() string { 116 | if len(p.rawJSON) > 0 { 117 | if value, err := internal.StringifyJSON(p.rawJSON); err == nil { 118 | return value 119 | } 120 | } 121 | if value, err := internal.StringifyJSON(p); err == nil { 122 | return value 123 | } 124 | return fmt.Sprintf("%#v", p) 125 | } 126 | -------------------------------------------------------------------------------- /internal/time.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | const dateFormat = "2006-01-02" 9 | 10 | // DateTime wraps time.Time and adapts its JSON representation 11 | // to conform to a RFC3339 date (e.g. 2006-01-02). 12 | // 13 | // Ref: https://ijmacd.github.io/rfc3339-iso8601 14 | type Date struct { 15 | t *time.Time 16 | } 17 | 18 | // NewDate returns a new *Date. If the given time.Time 19 | // is nil, nil will be returned. 20 | func NewDate(t time.Time) *Date { 21 | return &Date{t: &t} 22 | } 23 | 24 | // NewOptionalDate returns a new *Date. If the given time.Time 25 | // is nil, nil will be returned. 26 | func NewOptionalDate(t *time.Time) *Date { 27 | if t == nil { 28 | return nil 29 | } 30 | return &Date{t: t} 31 | } 32 | 33 | // Time returns the Date's underlying time, if any. If the 34 | // date is nil, the zero value is returned. 35 | func (d *Date) Time() time.Time { 36 | if d == nil || d.t == nil { 37 | return time.Time{} 38 | } 39 | return *d.t 40 | } 41 | 42 | // TimePtr returns a pointer to the Date's underlying time.Time, if any. 43 | func (d *Date) TimePtr() *time.Time { 44 | if d == nil || d.t == nil { 45 | return nil 46 | } 47 | if d.t.IsZero() { 48 | return nil 49 | } 50 | return d.t 51 | } 52 | 53 | func (d *Date) MarshalJSON() ([]byte, error) { 54 | if d == nil || d.t == nil { 55 | return nil, nil 56 | } 57 | return json.Marshal(d.t.Format(dateFormat)) 58 | } 59 | 60 | func (d *Date) UnmarshalJSON(data []byte) error { 61 | var raw string 62 | if err := json.Unmarshal(data, &raw); err != nil { 63 | return err 64 | } 65 | 66 | parsedTime, err := time.Parse(dateFormat, raw) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | *d = Date{t: &parsedTime} 72 | return nil 73 | } 74 | 75 | // DateTime wraps time.Time and adapts its JSON representation 76 | // to conform to a RFC3339 date-time (e.g. 2017-07-21T17:32:28Z). 77 | // 78 | // Ref: https://ijmacd.github.io/rfc3339-iso8601 79 | type DateTime struct { 80 | t *time.Time 81 | } 82 | 83 | // NewDateTime returns a new *DateTime. 84 | func NewDateTime(t time.Time) *DateTime { 85 | return &DateTime{t: &t} 86 | } 87 | 88 | // NewOptionalDateTime returns a new *DateTime. If the given time.Time 89 | // is nil, nil will be returned. 90 | func NewOptionalDateTime(t *time.Time) *DateTime { 91 | if t == nil { 92 | return nil 93 | } 94 | return &DateTime{t: t} 95 | } 96 | 97 | // Time returns the DateTime's underlying time, if any. If the 98 | // date-time is nil, the zero value is returned. 99 | func (d *DateTime) Time() time.Time { 100 | if d == nil || d.t == nil { 101 | return time.Time{} 102 | } 103 | return *d.t 104 | } 105 | 106 | // TimePtr returns a pointer to the DateTime's underlying time.Time, if any. 107 | func (d *DateTime) TimePtr() *time.Time { 108 | if d == nil || d.t == nil { 109 | return nil 110 | } 111 | if d.t.IsZero() { 112 | return nil 113 | } 114 | return d.t 115 | } 116 | 117 | func (d *DateTime) MarshalJSON() ([]byte, error) { 118 | if d == nil || d.t == nil { 119 | return nil, nil 120 | } 121 | return json.Marshal(d.t.Format(time.RFC3339)) 122 | } 123 | 124 | func (d *DateTime) UnmarshalJSON(data []byte) error { 125 | var raw string 126 | if err := json.Unmarshal(data, &raw); err != nil { 127 | return err 128 | } 129 | 130 | parsedTime, err := time.Parse(time.RFC3339, raw) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | *d = DateTime{t: &parsedTime} 136 | return nil 137 | } 138 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package zep 4 | 5 | import ( 6 | json "encoding/json" 7 | fmt "fmt" 8 | internal "github.com/getzep/zep-go/v3/internal" 9 | ) 10 | 11 | type CreateContextTemplateRequest struct { 12 | // The template content (max 1200 characters). 13 | Template string `json:"template" url:"-"` 14 | // Unique identifier for the template (max 100 characters). 15 | TemplateID string `json:"template_id" url:"-"` 16 | } 17 | 18 | type ContextTemplateResponse struct { 19 | // The template content. 20 | Template *string `json:"template,omitempty" url:"template,omitempty"` 21 | // Unique identifier for the template (max 100 characters). 22 | TemplateID *string `json:"template_id,omitempty" url:"template_id,omitempty"` 23 | // Unique identifier for the template. 24 | UUID *string `json:"uuid,omitempty" url:"uuid,omitempty"` 25 | 26 | extraProperties map[string]interface{} 27 | rawJSON json.RawMessage 28 | } 29 | 30 | func (c *ContextTemplateResponse) GetTemplate() *string { 31 | if c == nil { 32 | return nil 33 | } 34 | return c.Template 35 | } 36 | 37 | func (c *ContextTemplateResponse) GetTemplateID() *string { 38 | if c == nil { 39 | return nil 40 | } 41 | return c.TemplateID 42 | } 43 | 44 | func (c *ContextTemplateResponse) GetUUID() *string { 45 | if c == nil { 46 | return nil 47 | } 48 | return c.UUID 49 | } 50 | 51 | func (c *ContextTemplateResponse) GetExtraProperties() map[string]interface{} { 52 | return c.extraProperties 53 | } 54 | 55 | func (c *ContextTemplateResponse) UnmarshalJSON(data []byte) error { 56 | type unmarshaler ContextTemplateResponse 57 | var value unmarshaler 58 | if err := json.Unmarshal(data, &value); err != nil { 59 | return err 60 | } 61 | *c = ContextTemplateResponse(value) 62 | extraProperties, err := internal.ExtractExtraProperties(data, *c) 63 | if err != nil { 64 | return err 65 | } 66 | c.extraProperties = extraProperties 67 | c.rawJSON = json.RawMessage(data) 68 | return nil 69 | } 70 | 71 | func (c *ContextTemplateResponse) String() string { 72 | if len(c.rawJSON) > 0 { 73 | if value, err := internal.StringifyJSON(c.rawJSON); err == nil { 74 | return value 75 | } 76 | } 77 | if value, err := internal.StringifyJSON(c); err == nil { 78 | return value 79 | } 80 | return fmt.Sprintf("%#v", c) 81 | } 82 | 83 | type ListContextTemplatesResponse struct { 84 | Templates []*ContextTemplateResponse `json:"templates,omitempty" url:"templates,omitempty"` 85 | 86 | extraProperties map[string]interface{} 87 | rawJSON json.RawMessage 88 | } 89 | 90 | func (l *ListContextTemplatesResponse) GetTemplates() []*ContextTemplateResponse { 91 | if l == nil { 92 | return nil 93 | } 94 | return l.Templates 95 | } 96 | 97 | func (l *ListContextTemplatesResponse) GetExtraProperties() map[string]interface{} { 98 | return l.extraProperties 99 | } 100 | 101 | func (l *ListContextTemplatesResponse) UnmarshalJSON(data []byte) error { 102 | type unmarshaler ListContextTemplatesResponse 103 | var value unmarshaler 104 | if err := json.Unmarshal(data, &value); err != nil { 105 | return err 106 | } 107 | *l = ListContextTemplatesResponse(value) 108 | extraProperties, err := internal.ExtractExtraProperties(data, *l) 109 | if err != nil { 110 | return err 111 | } 112 | l.extraProperties = extraProperties 113 | l.rawJSON = json.RawMessage(data) 114 | return nil 115 | } 116 | 117 | func (l *ListContextTemplatesResponse) String() string { 118 | if len(l.rawJSON) > 0 { 119 | if value, err := internal.StringifyJSON(l.rawJSON); err == nil { 120 | return value 121 | } 122 | } 123 | if value, err := internal.StringifyJSON(l); err == nil { 124 | return value 125 | } 126 | return fmt.Sprintf("%#v", l) 127 | } 128 | 129 | type UpdateContextTemplateRequest struct { 130 | // The template content (max 1200 characters). 131 | Template string `json:"template" url:"-"` 132 | } 133 | -------------------------------------------------------------------------------- /core/request_option.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package core 4 | 5 | import ( 6 | fmt "fmt" 7 | http "net/http" 8 | url "net/url" 9 | ) 10 | 11 | // RequestOption adapts the behavior of the client or an individual request. 12 | type RequestOption interface { 13 | applyRequestOptions(*RequestOptions) 14 | } 15 | 16 | // RequestOptions defines all of the possible request options. 17 | // 18 | // This type is primarily used by the generated code and is not meant 19 | // to be used directly; use the option package instead. 20 | type RequestOptions struct { 21 | BaseURL string 22 | HTTPClient HTTPClient 23 | HTTPHeader http.Header 24 | BodyProperties map[string]interface{} 25 | QueryParameters url.Values 26 | MaxAttempts uint 27 | APIKey string 28 | } 29 | 30 | // NewRequestOptions returns a new *RequestOptions value. 31 | // 32 | // This function is primarily used by the generated code and is not meant 33 | // to be used directly; use RequestOption instead. 34 | func NewRequestOptions(opts ...RequestOption) *RequestOptions { 35 | options := &RequestOptions{ 36 | HTTPHeader: make(http.Header), 37 | BodyProperties: make(map[string]interface{}), 38 | QueryParameters: make(url.Values), 39 | } 40 | for _, opt := range opts { 41 | opt.applyRequestOptions(options) 42 | } 43 | return options 44 | } 45 | 46 | // ToHeader maps the configured request options into a http.Header used 47 | // for the request(s). 48 | func (r *RequestOptions) ToHeader() http.Header { 49 | header := r.cloneHeader() 50 | if r.APIKey != "" { 51 | header.Set("Authorization", fmt.Sprintf("Api-Key %v", r.APIKey)) 52 | } 53 | return header 54 | } 55 | 56 | func (r *RequestOptions) cloneHeader() http.Header { 57 | headers := r.HTTPHeader.Clone() 58 | headers.Set("X-Fern-Language", "Go") 59 | headers.Set("X-Fern-SDK-Name", "github.com/getzep/zep-go/v3") 60 | headers.Set("X-Fern-SDK-Version", "v3.13.0") 61 | headers.Set("User-Agent", "github.com/getzep/zep-go/3.13.0") 62 | return headers 63 | } 64 | 65 | // BaseURLOption implements the RequestOption interface. 66 | type BaseURLOption struct { 67 | BaseURL string 68 | } 69 | 70 | func (b *BaseURLOption) applyRequestOptions(opts *RequestOptions) { 71 | opts.BaseURL = b.BaseURL 72 | } 73 | 74 | // HTTPClientOption implements the RequestOption interface. 75 | type HTTPClientOption struct { 76 | HTTPClient HTTPClient 77 | } 78 | 79 | func (h *HTTPClientOption) applyRequestOptions(opts *RequestOptions) { 80 | opts.HTTPClient = h.HTTPClient 81 | } 82 | 83 | // HTTPHeaderOption implements the RequestOption interface. 84 | type HTTPHeaderOption struct { 85 | HTTPHeader http.Header 86 | } 87 | 88 | func (h *HTTPHeaderOption) applyRequestOptions(opts *RequestOptions) { 89 | opts.HTTPHeader = h.HTTPHeader 90 | } 91 | 92 | // BodyPropertiesOption implements the RequestOption interface. 93 | type BodyPropertiesOption struct { 94 | BodyProperties map[string]interface{} 95 | } 96 | 97 | func (b *BodyPropertiesOption) applyRequestOptions(opts *RequestOptions) { 98 | opts.BodyProperties = b.BodyProperties 99 | } 100 | 101 | // QueryParametersOption implements the RequestOption interface. 102 | type QueryParametersOption struct { 103 | QueryParameters url.Values 104 | } 105 | 106 | func (q *QueryParametersOption) applyRequestOptions(opts *RequestOptions) { 107 | opts.QueryParameters = q.QueryParameters 108 | } 109 | 110 | // MaxAttemptsOption implements the RequestOption interface. 111 | type MaxAttemptsOption struct { 112 | MaxAttempts uint 113 | } 114 | 115 | func (m *MaxAttemptsOption) applyRequestOptions(opts *RequestOptions) { 116 | opts.MaxAttempts = m.MaxAttempts 117 | } 118 | 119 | // APIKeyOption implements the RequestOption interface. 120 | type APIKeyOption struct { 121 | APIKey string 122 | } 123 | 124 | func (a *APIKeyOption) applyRequestOptions(opts *RequestOptions) { 125 | opts.APIKey = a.APIKey 126 | } 127 | -------------------------------------------------------------------------------- /internal/retrier.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "crypto/rand" 5 | "math/big" 6 | "net/http" 7 | "time" 8 | ) 9 | 10 | const ( 11 | defaultRetryAttempts = 2 12 | minRetryDelay = 500 * time.Millisecond 13 | maxRetryDelay = 5000 * time.Millisecond 14 | ) 15 | 16 | // RetryOption adapts the behavior the *Retrier. 17 | type RetryOption func(*retryOptions) 18 | 19 | // RetryFunc is a retryable HTTP function call (i.e. *http.Client.Do). 20 | type RetryFunc func(*http.Request) (*http.Response, error) 21 | 22 | // WithMaxAttempts configures the maximum number of attempts 23 | // of the *Retrier. 24 | func WithMaxAttempts(attempts uint) RetryOption { 25 | return func(opts *retryOptions) { 26 | opts.attempts = attempts 27 | } 28 | } 29 | 30 | // Retrier retries failed requests a configurable number of times with an 31 | // exponential back-off between each retry. 32 | type Retrier struct { 33 | attempts uint 34 | } 35 | 36 | // NewRetrier constructs a new *Retrier with the given options, if any. 37 | func NewRetrier(opts ...RetryOption) *Retrier { 38 | options := new(retryOptions) 39 | for _, opt := range opts { 40 | opt(options) 41 | } 42 | attempts := uint(defaultRetryAttempts) 43 | if options.attempts > 0 { 44 | attempts = options.attempts 45 | } 46 | return &Retrier{ 47 | attempts: attempts, 48 | } 49 | } 50 | 51 | // Run issues the request and, upon failure, retries the request if possible. 52 | // 53 | // The request will be retried as long as the request is deemed retryable and the 54 | // number of retry attempts has not grown larger than the configured retry limit. 55 | func (r *Retrier) Run( 56 | fn RetryFunc, 57 | request *http.Request, 58 | errorDecoder ErrorDecoder, 59 | opts ...RetryOption, 60 | ) (*http.Response, error) { 61 | options := new(retryOptions) 62 | for _, opt := range opts { 63 | opt(options) 64 | } 65 | maxRetryAttempts := r.attempts 66 | if options.attempts > 0 { 67 | maxRetryAttempts = options.attempts 68 | } 69 | var ( 70 | retryAttempt uint 71 | previousError error 72 | ) 73 | return r.run( 74 | fn, 75 | request, 76 | errorDecoder, 77 | maxRetryAttempts, 78 | retryAttempt, 79 | previousError, 80 | ) 81 | } 82 | 83 | func (r *Retrier) run( 84 | fn RetryFunc, 85 | request *http.Request, 86 | errorDecoder ErrorDecoder, 87 | maxRetryAttempts uint, 88 | retryAttempt uint, 89 | previousError error, 90 | ) (*http.Response, error) { 91 | if retryAttempt >= maxRetryAttempts { 92 | return nil, previousError 93 | } 94 | 95 | // If the call has been cancelled, don't issue the request. 96 | if err := request.Context().Err(); err != nil { 97 | return nil, err 98 | } 99 | 100 | response, err := fn(request) 101 | if err != nil { 102 | return nil, err 103 | } 104 | 105 | if r.shouldRetry(response) { 106 | defer response.Body.Close() 107 | 108 | delay, err := r.retryDelay(retryAttempt) 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | time.Sleep(delay) 114 | 115 | return r.run( 116 | fn, 117 | request, 118 | errorDecoder, 119 | maxRetryAttempts, 120 | retryAttempt+1, 121 | decodeError(response, errorDecoder), 122 | ) 123 | } 124 | 125 | return response, nil 126 | } 127 | 128 | // shouldRetry returns true if the request should be retried based on the given 129 | // response status code. 130 | func (r *Retrier) shouldRetry(response *http.Response) bool { 131 | return response.StatusCode == http.StatusTooManyRequests || 132 | response.StatusCode == http.StatusRequestTimeout || 133 | response.StatusCode >= http.StatusInternalServerError 134 | } 135 | 136 | // retryDelay calculates the delay time in milliseconds based on the retry attempt. 137 | func (r *Retrier) retryDelay(retryAttempt uint) (time.Duration, error) { 138 | // Apply exponential backoff. 139 | delay := minRetryDelay + minRetryDelay*time.Duration(retryAttempt*retryAttempt) 140 | 141 | // Do not allow the number to exceed maxRetryDelay. 142 | if delay > maxRetryDelay { 143 | delay = maxRetryDelay 144 | } 145 | 146 | // Apply some jitter by randomizing the value in the range of 75%-100%. 147 | max := big.NewInt(int64(delay / 4)) 148 | jitter, err := rand.Int(rand.Reader, max) 149 | if err != nil { 150 | return 0, err 151 | } 152 | 153 | delay -= time.Duration(jitter.Int64()) 154 | 155 | // Never sleep less than the base sleep seconds. 156 | if delay < minRetryDelay { 157 | delay = minRetryDelay 158 | } 159 | 160 | return delay, nil 161 | } 162 | 163 | type retryOptions struct { 164 | attempts uint 165 | } 166 | -------------------------------------------------------------------------------- /graph/client/schema.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // PropertyType defines the supported property types 10 | type PropertyType string 11 | 12 | const ( 13 | Text PropertyType = "Text" 14 | Int PropertyType = "Int" 15 | Float PropertyType = "Float" 16 | Boolean PropertyType = "Boolean" 17 | ) 18 | 19 | // IsValid checks if the property type is valid 20 | func (pt PropertyType) IsValid() bool { 21 | switch pt { 22 | case Text, Int, Float, Boolean: 23 | return true 24 | default: 25 | return false 26 | } 27 | } 28 | 29 | // Property represents a property definition for a schema 30 | type Property struct { 31 | Type PropertyType `json:"type"` 32 | Description string `json:"description"` 33 | } 34 | 35 | // Schema represents a schema for an entity or edge type 36 | type Schema struct { 37 | Name string `json:"name"` 38 | Properties map[string]Property `json:"properties"` 39 | } 40 | 41 | // Validate checks if the schema is valid 42 | func (s Schema) Validate() error { 43 | if s.Name == "" { 44 | return fmt.Errorf("schema name is required") 45 | } 46 | 47 | if len(s.Properties) == 0 { 48 | return fmt.Errorf("schema must have at least one property") 49 | } 50 | 51 | if len(s.Properties) > 10 { 52 | return fmt.Errorf("schema cannot have more than 10 properties") 53 | } 54 | 55 | for name, prop := range s.Properties { 56 | if name == "" { 57 | return fmt.Errorf("property name cannot be empty") 58 | } 59 | 60 | if !prop.Type.IsValid() { 61 | return fmt.Errorf("invalid property type for %s: %s", name, prop.Type) 62 | } 63 | 64 | if prop.Description == "" { 65 | return fmt.Errorf("property description is required for %s", name) 66 | } 67 | } 68 | 69 | return nil 70 | } 71 | 72 | // ExtractSchema extracts a schema from a struct using reflection 73 | func ExtractSchema(obj interface{}, name string) (Schema, error) { 74 | schema := Schema{ 75 | Name: name, 76 | Properties: make(map[string]Property), 77 | } 78 | 79 | t := reflect.TypeOf(obj) 80 | if t.Kind() == reflect.Ptr { 81 | t = t.Elem() 82 | } 83 | 84 | if t.Kind() != reflect.Struct { 85 | return schema, fmt.Errorf("object must be a struct") 86 | } 87 | 88 | for i := 0; i < t.NumField(); i++ { 89 | field := t.Field(i) 90 | 91 | // Skip unexported fields 92 | if !field.IsExported() { 93 | continue 94 | } 95 | 96 | // Get the description tag 97 | tag := field.Tag.Get("description") 98 | if tag == "" || tag == "-" { 99 | continue 100 | } 101 | 102 | // Get the json tag for the field name 103 | jsonTag := field.Tag.Get("json") 104 | fieldName := field.Name 105 | if jsonTag != "" { 106 | parts := strings.Split(jsonTag, ",") 107 | if parts[0] != "" { 108 | fieldName = parts[0] 109 | } 110 | } 111 | 112 | // Parse the tag 113 | var propType PropertyType 114 | var description string = tag 115 | 116 | switch field.Type.Kind() { 117 | case reflect.String: 118 | propType = Text 119 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 120 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 121 | propType = Int 122 | case reflect.Float32, reflect.Float64: 123 | propType = Float 124 | case reflect.Bool: 125 | propType = Boolean 126 | default: 127 | // Skip unsupported types 128 | continue 129 | } 130 | 131 | // Add the property to the schema 132 | schema.Properties[fieldName] = Property{ 133 | Type: propType, 134 | Description: description, 135 | } 136 | } 137 | 138 | // Validate the schema 139 | if err := schema.Validate(); err != nil { 140 | return schema, err 141 | } 142 | 143 | return schema, nil 144 | } 145 | 146 | // ExtractEntitySchema extracts an entity schema from a struct using reflection 147 | // This is a wrapper around ExtractSchema for backward compatibility 148 | func ExtractEntitySchema(entity interface{}, name string) (Schema, error) { 149 | return ExtractSchema(entity, name) 150 | } 151 | 152 | // ExtractEdgeSchema extracts an edge schema from a struct using reflection 153 | // This is a wrapper around ExtractSchema for backward compatibility 154 | func ExtractEdgeSchema(edge interface{}, name string) (Schema, error) { 155 | return ExtractSchema(edge, name) 156 | } 157 | 158 | // EntitySchema is an alias for Schema for backward compatibility 159 | type EntitySchema = Schema 160 | 161 | // EdgeSchema is an alias for Schema for backward compatibility 162 | type EdgeSchema = Schema 163 | -------------------------------------------------------------------------------- /context_string_test.go: -------------------------------------------------------------------------------- 1 | package zep 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestFormatEdgeDateRange(t *testing.T) { 10 | t.Run("both dates present", func(t *testing.T) { 11 | validAt := "2023-01-01T10:00:00Z" 12 | invalidAt := "2023-01-02T15:00:00Z" 13 | edge := &EntityEdge{ 14 | ValidAt: &validAt, 15 | InvalidAt: &invalidAt, 16 | } 17 | result := formatEdgeDateRange(edge) 18 | assert.Equal(t, "2023-01-01 10:00:00 - 2023-01-02 15:00:00", result) 19 | }) 20 | 21 | t.Run("only valid_at present", func(t *testing.T) { 22 | validAt := "2023-01-01T10:00:00Z" 23 | edge := &EntityEdge{ 24 | ValidAt: &validAt, 25 | } 26 | result := formatEdgeDateRange(edge) 27 | assert.Equal(t, "2023-01-01 10:00:00 - present", result) 28 | }) 29 | 30 | t.Run("no dates present", func(t *testing.T) { 31 | edge := &EntityEdge{} 32 | result := formatEdgeDateRange(edge) 33 | assert.Equal(t, "date unknown - present", result) 34 | }) 35 | } 36 | 37 | func TestComposeContextString(t *testing.T) { 38 | t.Run("with facts, entities, and episodes", func(t *testing.T) { 39 | validAt := "2023-01-01T10:00:00Z" 40 | edges := []*EntityEdge{ 41 | { 42 | Fact: "John likes coffee", 43 | ValidAt: &validAt, 44 | }, 45 | } 46 | 47 | labels := []string{"Person", "Entity"} 48 | attributes := map[string]interface{}{ 49 | "age": 30, 50 | "labels": []string{"Person"}, 51 | } 52 | nodes := []*EntityNode{ 53 | { 54 | Name: "John", 55 | Labels: labels, 56 | Attributes: attributes, 57 | Summary: "A coffee enthusiast", 58 | }, 59 | } 60 | 61 | role := "user" 62 | roleType := RoleTypeUserRole 63 | episodes := []*Episode{ 64 | { 65 | Role: &role, 66 | RoleType: &roleType, 67 | Content: "I love coffee", 68 | CreatedAt: "2023-01-01T12:00:00Z", 69 | }, 70 | } 71 | 72 | result := ComposeContextString(edges, nodes, episodes) 73 | 74 | assert.Contains(t, result, "FACTS and ENTITIES, and EPISODES represent") 75 | assert.Contains(t, result, "John likes coffee (2023-01-01 10:00:00 - present)") 76 | assert.Contains(t, result, "Name: John") 77 | assert.Contains(t, result, "Label: Person") 78 | assert.Contains(t, result, "Attributes:") 79 | assert.Contains(t, result, " age: 30") 80 | assert.Contains(t, result, "Summary: A coffee enthusiast") 81 | assert.Contains(t, result, "user (user): I love coffee (2023-01-01 12:00:00)") 82 | assert.Contains(t, result, "") 83 | }) 84 | 85 | t.Run("without episodes", func(t *testing.T) { 86 | validAt := "2023-01-01T10:00:00Z" 87 | edges := []*EntityEdge{ 88 | { 89 | Fact: "John likes coffee", 90 | ValidAt: &validAt, 91 | }, 92 | } 93 | 94 | nodes := []*EntityNode{ 95 | { 96 | Name: "John", 97 | Summary: "A person", 98 | }, 99 | } 100 | 101 | result := ComposeContextString(edges, nodes, nil) 102 | 103 | assert.Contains(t, result, "FACTS and ENTITIES represent") 104 | assert.NotContains(t, result, ", and EPISODES") 105 | assert.NotContains(t, result, "") 106 | assert.Contains(t, result, "John likes coffee") 107 | assert.Contains(t, result, "Name: John") 108 | assert.Contains(t, result, "Summary: A person") 109 | }) 110 | 111 | t.Run("entity with only Entity label filtered out", func(t *testing.T) { 112 | labels := []string{"Entity"} 113 | nodes := []*EntityNode{ 114 | { 115 | Name: "Test", 116 | Labels: labels, 117 | Summary: "Test entity", 118 | }, 119 | } 120 | 121 | result := ComposeContextString(nil, nodes, nil) 122 | 123 | assert.Contains(t, result, "Name: Test") 124 | assert.NotContains(t, result, "Label: Entity") 125 | assert.Contains(t, result, "Summary: Test entity") 126 | }) 127 | 128 | t.Run("episode with only role_type", func(t *testing.T) { 129 | roleType := RoleTypeAssistantRole 130 | episodes := []*Episode{ 131 | { 132 | RoleType: &roleType, 133 | Content: "Hello there", 134 | CreatedAt: "2023-01-01T12:00:00Z", 135 | }, 136 | } 137 | 138 | result := ComposeContextString(nil, nil, episodes) 139 | 140 | assert.Contains(t, result, "(assistant): Hello there") 141 | }) 142 | 143 | t.Run("empty inputs", func(t *testing.T) { 144 | result := ComposeContextString(nil, nil, nil) 145 | 146 | assert.Contains(t, result, "FACTS and ENTITIES represent") 147 | assert.NotContains(t, result, "EPISODES") 148 | assert.Contains(t, result, "\n\n") 149 | assert.Contains(t, result, "\n\n") 150 | }) 151 | } 152 | -------------------------------------------------------------------------------- /thread/client/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package client 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | message "github.com/getzep/zep-go/v3/thread/message" 12 | http "net/http" 13 | os "os" 14 | ) 15 | 16 | type Client struct { 17 | WithRawResponse *RawClient 18 | Message *message.Client 19 | 20 | baseURL string 21 | caller *internal.Caller 22 | header http.Header 23 | } 24 | 25 | func NewClient(opts ...option.RequestOption) *Client { 26 | options := core.NewRequestOptions(opts...) 27 | if options.APIKey == "" { 28 | options.APIKey = os.Getenv("ZEP_API_KEY") 29 | } 30 | return &Client{ 31 | Message: message.NewClient(opts...), 32 | WithRawResponse: NewRawClient(options), 33 | baseURL: options.BaseURL, 34 | caller: internal.NewCaller( 35 | &internal.CallerParams{ 36 | Client: options.HTTPClient, 37 | MaxAttempts: options.MaxAttempts, 38 | }, 39 | ), 40 | header: options.ToHeader(), 41 | } 42 | } 43 | 44 | // Returns all threads. 45 | func (c *Client) ListAll( 46 | ctx context.Context, 47 | request *v3.ThreadListAllRequest, 48 | opts ...option.RequestOption, 49 | ) (*v3.ThreadListResponse, error) { 50 | response, err := c.WithRawResponse.ListAll( 51 | ctx, 52 | request, 53 | opts..., 54 | ) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return response.Body, nil 59 | } 60 | 61 | // Start a new thread. 62 | func (c *Client) Create( 63 | ctx context.Context, 64 | request *v3.CreateThreadRequest, 65 | opts ...option.RequestOption, 66 | ) (*v3.Thread, error) { 67 | response, err := c.WithRawResponse.Create( 68 | ctx, 69 | request, 70 | opts..., 71 | ) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return response.Body, nil 76 | } 77 | 78 | // Deletes a thread. 79 | func (c *Client) Delete( 80 | ctx context.Context, 81 | // The ID of the thread for which memory should be deleted. 82 | threadID string, 83 | opts ...option.RequestOption, 84 | ) (*v3.SuccessResponse, error) { 85 | response, err := c.WithRawResponse.Delete( 86 | ctx, 87 | threadID, 88 | opts..., 89 | ) 90 | if err != nil { 91 | return nil, err 92 | } 93 | return response.Body, nil 94 | } 95 | 96 | // Returns most relevant context from the user graph (including memory from any/all past threads) based on the content of the past few messages of the given thread. 97 | func (c *Client) GetUserContext( 98 | ctx context.Context, 99 | // The ID of the current thread (for which context is being retrieved). 100 | threadID string, 101 | request *v3.ThreadGetUserContextRequest, 102 | opts ...option.RequestOption, 103 | ) (*v3.ThreadContextResponse, error) { 104 | response, err := c.WithRawResponse.GetUserContext( 105 | ctx, 106 | threadID, 107 | request, 108 | opts..., 109 | ) 110 | if err != nil { 111 | return nil, err 112 | } 113 | return response.Body, nil 114 | } 115 | 116 | // Returns messages for a thread. 117 | func (c *Client) Get( 118 | ctx context.Context, 119 | // Thread ID 120 | threadID string, 121 | request *v3.ThreadGetRequest, 122 | opts ...option.RequestOption, 123 | ) (*v3.MessageListResponse, error) { 124 | response, err := c.WithRawResponse.Get( 125 | ctx, 126 | threadID, 127 | request, 128 | opts..., 129 | ) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return response.Body, nil 134 | } 135 | 136 | // Add messages to a thread. 137 | func (c *Client) AddMessages( 138 | ctx context.Context, 139 | // The ID of the thread to which messages should be added. 140 | threadID string, 141 | request *v3.AddThreadMessagesRequest, 142 | opts ...option.RequestOption, 143 | ) (*v3.AddThreadMessagesResponse, error) { 144 | response, err := c.WithRawResponse.AddMessages( 145 | ctx, 146 | threadID, 147 | request, 148 | opts..., 149 | ) 150 | if err != nil { 151 | return nil, err 152 | } 153 | return response.Body, nil 154 | } 155 | 156 | // Add messages to a thread in batch mode. This will process messages concurrently, which is useful for data migrations. 157 | func (c *Client) AddMessagesBatch( 158 | ctx context.Context, 159 | // The ID of the thread to which messages should be added. 160 | threadID string, 161 | request *v3.AddThreadMessagesRequest, 162 | opts ...option.RequestOption, 163 | ) (*v3.AddThreadMessagesResponse, error) { 164 | response, err := c.WithRawResponse.AddMessagesBatch( 165 | ctx, 166 | threadID, 167 | request, 168 | opts..., 169 | ) 170 | if err != nil { 171 | return nil, err 172 | } 173 | return response.Body, nil 174 | } 175 | -------------------------------------------------------------------------------- /internal/extra_properties.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "strings" 9 | ) 10 | 11 | // MarshalJSONWithExtraProperty marshals the given value to JSON, including the extra property. 12 | func MarshalJSONWithExtraProperty(marshaler interface{}, key string, value interface{}) ([]byte, error) { 13 | return MarshalJSONWithExtraProperties(marshaler, map[string]interface{}{key: value}) 14 | } 15 | 16 | // MarshalJSONWithExtraProperties marshals the given value to JSON, including any extra properties. 17 | func MarshalJSONWithExtraProperties(marshaler interface{}, extraProperties map[string]interface{}) ([]byte, error) { 18 | bytes, err := json.Marshal(marshaler) 19 | if err != nil { 20 | return nil, err 21 | } 22 | if len(extraProperties) == 0 { 23 | return bytes, nil 24 | } 25 | keys, err := getKeys(marshaler) 26 | if err != nil { 27 | return nil, err 28 | } 29 | for _, key := range keys { 30 | if _, ok := extraProperties[key]; ok { 31 | return nil, fmt.Errorf("cannot add extra property %q because it is already defined on the type", key) 32 | } 33 | } 34 | extraBytes, err := json.Marshal(extraProperties) 35 | if err != nil { 36 | return nil, err 37 | } 38 | if isEmptyJSON(bytes) { 39 | if isEmptyJSON(extraBytes) { 40 | return bytes, nil 41 | } 42 | return extraBytes, nil 43 | } 44 | result := bytes[:len(bytes)-1] 45 | result = append(result, ',') 46 | result = append(result, extraBytes[1:len(extraBytes)-1]...) 47 | result = append(result, '}') 48 | return result, nil 49 | } 50 | 51 | // ExtractExtraProperties extracts any extra properties from the given value. 52 | func ExtractExtraProperties(bytes []byte, value interface{}, exclude ...string) (map[string]interface{}, error) { 53 | val := reflect.ValueOf(value) 54 | for val.Kind() == reflect.Ptr { 55 | if val.IsNil() { 56 | return nil, fmt.Errorf("value must be non-nil to extract extra properties") 57 | } 58 | val = val.Elem() 59 | } 60 | if err := json.Unmarshal(bytes, &value); err != nil { 61 | return nil, err 62 | } 63 | var extraProperties map[string]interface{} 64 | if err := json.Unmarshal(bytes, &extraProperties); err != nil { 65 | return nil, err 66 | } 67 | for i := 0; i < val.Type().NumField(); i++ { 68 | key := jsonKey(val.Type().Field(i)) 69 | if key == "" || key == "-" { 70 | continue 71 | } 72 | delete(extraProperties, key) 73 | } 74 | for _, key := range exclude { 75 | delete(extraProperties, key) 76 | } 77 | if len(extraProperties) == 0 { 78 | return nil, nil 79 | } 80 | return extraProperties, nil 81 | } 82 | 83 | // getKeys returns the keys associated with the given value. The value must be a 84 | // a struct or a map with string keys. 85 | func getKeys(value interface{}) ([]string, error) { 86 | val := reflect.ValueOf(value) 87 | if val.Kind() == reflect.Ptr { 88 | val = val.Elem() 89 | } 90 | if !val.IsValid() { 91 | return nil, nil 92 | } 93 | switch val.Kind() { 94 | case reflect.Struct: 95 | return getKeysForStructType(val.Type()), nil 96 | case reflect.Map: 97 | var keys []string 98 | if val.Type().Key().Kind() != reflect.String { 99 | return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) 100 | } 101 | for _, key := range val.MapKeys() { 102 | keys = append(keys, key.String()) 103 | } 104 | return keys, nil 105 | default: 106 | return nil, fmt.Errorf("cannot extract keys from %T; only structs and maps with string keys are supported", value) 107 | } 108 | } 109 | 110 | // getKeysForStructType returns all the keys associated with the given struct type, 111 | // visiting embedded fields recursively. 112 | func getKeysForStructType(structType reflect.Type) []string { 113 | if structType.Kind() == reflect.Pointer { 114 | structType = structType.Elem() 115 | } 116 | if structType.Kind() != reflect.Struct { 117 | return nil 118 | } 119 | var keys []string 120 | for i := 0; i < structType.NumField(); i++ { 121 | field := structType.Field(i) 122 | if field.Anonymous { 123 | keys = append(keys, getKeysForStructType(field.Type)...) 124 | continue 125 | } 126 | keys = append(keys, jsonKey(field)) 127 | } 128 | return keys 129 | } 130 | 131 | // jsonKey returns the JSON key from the struct tag of the given field, 132 | // excluding the omitempty flag (if any). 133 | func jsonKey(field reflect.StructField) string { 134 | return strings.TrimSuffix(field.Tag.Get("json"), ",omitempty") 135 | } 136 | 137 | // isEmptyJSON returns true if the given data is empty, the empty JSON object, or 138 | // an explicit null. 139 | func isEmptyJSON(data []byte) bool { 140 | return len(data) <= 2 || bytes.Equal(data, []byte("null")) 141 | } 142 | -------------------------------------------------------------------------------- /context_string.go: -------------------------------------------------------------------------------- 1 | package zep 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | // dateFormat defines the format for date strings 10 | const dateFormat = "2006-01-02 15:04:05" 11 | 12 | // templateString defines the template for context information 13 | const templateString = ` 14 | FACTS and ENTITIES%s represent relevant context to the current conversation. 15 | 16 | # These are the most relevant facts and their valid date ranges 17 | # format: FACT (Date range: from - to) 18 | 19 | %s 20 | 21 | 22 | # These are the most relevant entities 23 | # Name: ENTITY_NAME 24 | # Label: entity_label (if present) 25 | # Attributes: (if present) 26 | # attr_name: attr_value 27 | # Summary: entity summary 28 | 29 | %s 30 | 31 | %s 32 | ` 33 | 34 | // formatEdgeDateRange formats the date range of an entity edge. 35 | func formatEdgeDateRange(edge *EntityEdge) string { 36 | validAt := "date unknown" 37 | invalidAt := "present" 38 | 39 | if edge.ValidAt != nil && *edge.ValidAt != "" { 40 | if t, err := time.Parse(time.RFC3339, *edge.ValidAt); err == nil { 41 | validAt = t.Format(dateFormat) 42 | } 43 | } 44 | 45 | if edge.InvalidAt != nil && *edge.InvalidAt != "" { 46 | if t, err := time.Parse(time.RFC3339, *edge.InvalidAt); err == nil { 47 | invalidAt = t.Format(dateFormat) 48 | } 49 | } 50 | 51 | return fmt.Sprintf("%s - %s", validAt, invalidAt) 52 | } 53 | 54 | // ComposeContextString composes a search context from entity edges, nodes, and episodes. 55 | func ComposeContextString(edges []*EntityEdge, nodes []*EntityNode, episodes []*Episode) string { 56 | var facts []string 57 | for _, edge := range edges { 58 | fact := fmt.Sprintf(" - %s (%s)", edge.Fact, formatEdgeDateRange(edge)) 59 | facts = append(facts, fact) 60 | } 61 | 62 | var entities []string 63 | for _, node := range nodes { 64 | var entityParts []string 65 | entityParts = append(entityParts, fmt.Sprintf("Name: %s", node.Name)) 66 | 67 | // Add label if present (excluding 'Entity' from labels) 68 | if node.Labels != nil && len(node.Labels) > 0 { 69 | labels := make([]string, 0, len(node.Labels)) 70 | for _, label := range node.Labels { 71 | if label != "Entity" { 72 | labels = append(labels, label) 73 | } 74 | } 75 | if len(labels) > 0 { 76 | entityParts = append(entityParts, fmt.Sprintf("Label: %s", labels[0])) 77 | } 78 | } 79 | 80 | // Add attributes if present (excluding 'labels' attribute) 81 | if node.Attributes != nil && len(node.Attributes) > 0 { 82 | hasNonLabelAttributes := false 83 | for key := range node.Attributes { 84 | if key != "labels" { 85 | hasNonLabelAttributes = true 86 | break 87 | } 88 | } 89 | if hasNonLabelAttributes { 90 | entityParts = append(entityParts, "Attributes:") 91 | for key, value := range node.Attributes { 92 | if key != "labels" { 93 | entityParts = append(entityParts, fmt.Sprintf(" %s: %v", key, value)) 94 | } 95 | } 96 | } 97 | } 98 | 99 | // Add summary if present 100 | if node.Summary != "" { 101 | entityParts = append(entityParts, fmt.Sprintf("Summary: %s", node.Summary)) 102 | } 103 | 104 | entity := strings.Join(entityParts, "\n") 105 | entities = append(entities, entity) 106 | } 107 | 108 | // Format episodes 109 | var episodesList []string 110 | if episodes != nil { 111 | for _, episode := range episodes { 112 | var rolePrefix string 113 | if episode.Role != nil && *episode.Role != "" { 114 | if episode.RoleType != nil && *episode.RoleType != "" { 115 | rolePrefix = fmt.Sprintf("%s (%s): ", *episode.Role, *episode.RoleType) 116 | } else { 117 | rolePrefix = fmt.Sprintf("%s: ", *episode.Role) 118 | } 119 | } else if episode.RoleType != nil && *episode.RoleType != "" { 120 | rolePrefix = fmt.Sprintf("(%s): ", *episode.RoleType) 121 | } 122 | 123 | timestamp := "date unknown" 124 | if episode.CreatedAt != "" { 125 | if t, err := time.Parse(time.RFC3339, episode.CreatedAt); err == nil { 126 | timestamp = t.Format(dateFormat) 127 | } 128 | } 129 | 130 | episodeStr := fmt.Sprintf(" - %s%s (%s)", rolePrefix, episode.Content, timestamp) 131 | episodesList = append(episodesList, episodeStr) 132 | } 133 | } 134 | 135 | factsStr := strings.Join(facts, "\n") 136 | entitiesStr := strings.Join(entities, "\n") 137 | episodesStr := strings.Join(episodesList, "\n") 138 | 139 | // Determine if episodes section should be included 140 | episodesHeader := "" 141 | episodesSection := "" 142 | if len(episodesList) > 0 { 143 | episodesHeader = ", and EPISODES" 144 | episodesSection = fmt.Sprintf("\n# These are the most relevant episodes\n\n%s\n", episodesStr) 145 | } 146 | 147 | return fmt.Sprintf(templateString, episodesHeader, factsStr, entitiesStr, episodesSection) 148 | } 149 | -------------------------------------------------------------------------------- /graph/client/ontology.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/getzep/zep-go/v3" 8 | ) 9 | 10 | // SetOntology sets entity end edge types for the target, replacing any existing entity/edge types set for the target. If no user/graph target is set, it will default to the project target. 11 | // It takes a slice of EntityDefinition, which is satisfied by any struct that embeds BaseEntity, and a slice of EdgeDefinition, which is satisfied by any struct that embeds BaseEdge 12 | func (c *Client) SetOntology( 13 | ctx context.Context, 14 | entities []zep.EntityDefinition, 15 | edges []zep.EdgeDefinitionWithSourceTargets, 16 | options ...zep.GraphOntologyOption, 17 | ) (*zep.SuccessResponse, error) { 18 | return c.SetEntityTypes(ctx, entities, edges, options...) 19 | } 20 | 21 | // SetEntityTypes sets entity end edge types for the target, replacing any existing entity/edge types set for the target. If no user/graph target is set, it will default to the project target. 22 | // It takes a slice of EntityDefinition, which is satisfied by any struct that embeds BaseEntity, and a slice of EdgeDefinition, which is satisfied by any struct that embeds BaseEdge 23 | func (c *Client) SetEntityTypes( 24 | ctx context.Context, 25 | entities []zep.EntityDefinition, 26 | edges []zep.EdgeDefinitionWithSourceTargets, 27 | options ...zep.GraphOntologyOption, 28 | ) (*zep.SuccessResponse, error) { 29 | opts := &zep.GraphOntologyOptions{} 30 | for _, option := range options { 31 | option(opts) 32 | } 33 | 34 | var entitySchemas []*zep.EntityType 35 | var edgeSchemas []*zep.EdgeType 36 | 37 | for i, entityStruct := range entities { 38 | // Try to extract metadata from embedded BaseEntity struct tags 39 | metadata, found := ExtractBaseEntityMetadata(entityStruct) 40 | if !found { 41 | return nil, fmt.Errorf("entity at index %d does not have a BaseEntity with required name tag", i) 42 | } 43 | 44 | // Name is always from the struct tag 45 | entityName := metadata.Name 46 | 47 | // Extract entity schema as usual 48 | entitySchema, err := ExtractEntitySchema(entityStruct, entityName) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | entityProperties := make([]*zep.EntityProperty, 0, len(entitySchema.Properties)) 54 | for name, property := range entitySchema.Properties { 55 | entityProperties = append(entityProperties, &zep.EntityProperty{ 56 | Name: name, 57 | Type: zep.EntityPropertyType(property.Type), 58 | Description: property.Description, 59 | }) 60 | } 61 | 62 | // If description is not provided in struct tag, use a default or empty string 63 | description := metadata.Description 64 | if description == "" { 65 | description = fmt.Sprintf("Entity type for %s", entityName) 66 | } 67 | 68 | entityType := &zep.EntityType{ 69 | Name: entityName, 70 | Description: description, 71 | Properties: entityProperties, 72 | } 73 | 74 | entitySchemas = append(entitySchemas, entityType) 75 | } 76 | 77 | for i, edgeWithSourceTargets := range edges { 78 | edgeStruct := edgeWithSourceTargets.EdgeModel 79 | // Try to extract metadata from embedded BaseEntity struct tags 80 | metadata, found := ExtractBaseEdgeMetadata(edgeStruct) 81 | if !found { 82 | return nil, fmt.Errorf("entity at index %d does not have a BaseEdge with required name tag", i) 83 | } 84 | 85 | // Name is always from the struct tag 86 | edgeName := metadata.Name 87 | 88 | // Extract entity schema as usual 89 | edgeSchema, err := ExtractEdgeSchema(edgeStruct, edgeName) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | entityProperties := make([]*zep.EntityProperty, 0, len(edgeSchema.Properties)) 95 | for name, property := range edgeSchema.Properties { 96 | entityProperties = append(entityProperties, &zep.EntityProperty{ 97 | Name: name, 98 | Type: zep.EntityPropertyType(property.Type), 99 | Description: property.Description, 100 | }) 101 | } 102 | 103 | // If description is not provided in struct tag, use a default or empty string 104 | description := metadata.Description 105 | if description == "" { 106 | description = fmt.Sprintf("Entity type for %s", edgeName) 107 | } 108 | var sourceTargets []*zep.EntityEdgeSourceTarget 109 | if edgeWithSourceTargets.SourceTargets != nil { 110 | for _, sourceTarget := range edgeWithSourceTargets.SourceTargets { 111 | sourceTargets = append(sourceTargets, &zep.EntityEdgeSourceTarget{ 112 | Source: sourceTarget.Source, 113 | Target: sourceTarget.Target, 114 | }) 115 | } 116 | } 117 | edgeType := &zep.EdgeType{ 118 | Name: edgeName, 119 | Description: description, 120 | Properties: entityProperties, 121 | SourceTargets: sourceTargets, 122 | } 123 | 124 | edgeSchemas = append(edgeSchemas, edgeType) 125 | } 126 | 127 | request := &zep.EntityTypeRequest{ 128 | EntityTypes: entitySchemas, 129 | EdgeTypes: edgeSchemas, 130 | GraphIDs: opts.GraphIDs, 131 | UserIDs: opts.UserIDs, 132 | } 133 | return c.SetEntityTypesInternal(ctx, request) 134 | } 135 | -------------------------------------------------------------------------------- /user/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package user 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | os "os" 13 | ) 14 | 15 | type Client struct { 16 | WithRawResponse *RawClient 17 | 18 | baseURL string 19 | caller *internal.Caller 20 | header http.Header 21 | } 22 | 23 | func NewClient(opts ...option.RequestOption) *Client { 24 | options := core.NewRequestOptions(opts...) 25 | if options.APIKey == "" { 26 | options.APIKey = os.Getenv("ZEP_API_KEY") 27 | } 28 | return &Client{ 29 | WithRawResponse: NewRawClient(options), 30 | baseURL: options.BaseURL, 31 | caller: internal.NewCaller( 32 | &internal.CallerParams{ 33 | Client: options.HTTPClient, 34 | MaxAttempts: options.MaxAttempts, 35 | }, 36 | ), 37 | header: options.ToHeader(), 38 | } 39 | } 40 | 41 | // Lists all user summary instructions for a project, user. 42 | func (c *Client) ListUserSummaryInstructions( 43 | ctx context.Context, 44 | request *v3.UserListUserSummaryInstructionsRequest, 45 | opts ...option.RequestOption, 46 | ) (*v3.ListUserInstructionsResponse, error) { 47 | response, err := c.WithRawResponse.ListUserSummaryInstructions( 48 | ctx, 49 | request, 50 | opts..., 51 | ) 52 | if err != nil { 53 | return nil, err 54 | } 55 | return response.Body, nil 56 | } 57 | 58 | // Adds new summary instructions for users graphs without removing existing ones. If user_ids is empty, adds to project-wide default instructions. 59 | func (c *Client) AddUserSummaryInstructions( 60 | ctx context.Context, 61 | request *v3.AddUserInstructionsRequest, 62 | opts ...option.RequestOption, 63 | ) (*v3.SuccessResponse, error) { 64 | response, err := c.WithRawResponse.AddUserSummaryInstructions( 65 | ctx, 66 | request, 67 | opts..., 68 | ) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return response.Body, nil 73 | } 74 | 75 | // Deletes user summary/instructions for users or project wide defaults. 76 | func (c *Client) DeleteUserSummaryInstructions( 77 | ctx context.Context, 78 | request *v3.DeleteUserInstructionsRequest, 79 | opts ...option.RequestOption, 80 | ) (*v3.SuccessResponse, error) { 81 | response, err := c.WithRawResponse.DeleteUserSummaryInstructions( 82 | ctx, 83 | request, 84 | opts..., 85 | ) 86 | if err != nil { 87 | return nil, err 88 | } 89 | return response.Body, nil 90 | } 91 | 92 | // Adds a user. 93 | func (c *Client) Add( 94 | ctx context.Context, 95 | request *v3.CreateUserRequest, 96 | opts ...option.RequestOption, 97 | ) (*v3.User, error) { 98 | response, err := c.WithRawResponse.Add( 99 | ctx, 100 | request, 101 | opts..., 102 | ) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return response.Body, nil 107 | } 108 | 109 | // Returns all users. 110 | func (c *Client) ListOrdered( 111 | ctx context.Context, 112 | request *v3.UserListOrderedRequest, 113 | opts ...option.RequestOption, 114 | ) (*v3.UserListResponse, error) { 115 | response, err := c.WithRawResponse.ListOrdered( 116 | ctx, 117 | request, 118 | opts..., 119 | ) 120 | if err != nil { 121 | return nil, err 122 | } 123 | return response.Body, nil 124 | } 125 | 126 | // Returns a user. 127 | func (c *Client) Get( 128 | ctx context.Context, 129 | // The user_id of the user to get. 130 | userID string, 131 | opts ...option.RequestOption, 132 | ) (*v3.User, error) { 133 | response, err := c.WithRawResponse.Get( 134 | ctx, 135 | userID, 136 | opts..., 137 | ) 138 | if err != nil { 139 | return nil, err 140 | } 141 | return response.Body, nil 142 | } 143 | 144 | // Deletes a user. 145 | func (c *Client) Delete( 146 | ctx context.Context, 147 | // User ID 148 | userID string, 149 | opts ...option.RequestOption, 150 | ) (*v3.SuccessResponse, error) { 151 | response, err := c.WithRawResponse.Delete( 152 | ctx, 153 | userID, 154 | opts..., 155 | ) 156 | if err != nil { 157 | return nil, err 158 | } 159 | return response.Body, nil 160 | } 161 | 162 | // Updates a user. 163 | func (c *Client) Update( 164 | ctx context.Context, 165 | // User ID 166 | userID string, 167 | request *v3.UpdateUserRequest, 168 | opts ...option.RequestOption, 169 | ) (*v3.User, error) { 170 | response, err := c.WithRawResponse.Update( 171 | ctx, 172 | userID, 173 | request, 174 | opts..., 175 | ) 176 | if err != nil { 177 | return nil, err 178 | } 179 | return response.Body, nil 180 | } 181 | 182 | // Returns a user's node. 183 | func (c *Client) GetNode( 184 | ctx context.Context, 185 | // The user_id of the user to get the node for. 186 | userID string, 187 | opts ...option.RequestOption, 188 | ) (*v3.UserNodeResponse, error) { 189 | response, err := c.WithRawResponse.GetNode( 190 | ctx, 191 | userID, 192 | opts..., 193 | ) 194 | if err != nil { 195 | return nil, err 196 | } 197 | return response.Body, nil 198 | } 199 | 200 | // Returns all threads for a user. 201 | func (c *Client) GetThreads( 202 | ctx context.Context, 203 | // User ID 204 | userID string, 205 | opts ...option.RequestOption, 206 | ) ([]*v3.Thread, error) { 207 | response, err := c.WithRawResponse.GetThreads( 208 | ctx, 209 | userID, 210 | opts..., 211 | ) 212 | if err != nil { 213 | return nil, err 214 | } 215 | return response.Body, nil 216 | } 217 | 218 | // Hints Zep to warm a user's graph for low-latency search 219 | func (c *Client) Warm( 220 | ctx context.Context, 221 | // User ID 222 | userID string, 223 | opts ...option.RequestOption, 224 | ) (*v3.SuccessResponse, error) { 225 | response, err := c.WithRawResponse.Warm( 226 | ctx, 227 | userID, 228 | opts..., 229 | ) 230 | if err != nil { 231 | return nil, err 232 | } 233 | return response.Body, nil 234 | } 235 | -------------------------------------------------------------------------------- /task.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package zep 4 | 5 | import ( 6 | json "encoding/json" 7 | fmt "fmt" 8 | internal "github.com/getzep/zep-go/v3/internal" 9 | ) 10 | 11 | type GetTaskResponse struct { 12 | CompletedAt *string `json:"completed_at,omitempty" url:"completed_at,omitempty"` 13 | CreatedAt *string `json:"created_at,omitempty" url:"created_at,omitempty"` 14 | Error *TaskErrorResponse `json:"error,omitempty" url:"error,omitempty"` 15 | Progress *TaskProgress `json:"progress,omitempty" url:"progress,omitempty"` 16 | StartedAt *string `json:"started_at,omitempty" url:"started_at,omitempty"` 17 | Status *string `json:"status,omitempty" url:"status,omitempty"` 18 | TaskID *string `json:"task_id,omitempty" url:"task_id,omitempty"` 19 | Type *string `json:"type,omitempty" url:"type,omitempty"` 20 | UpdatedAt *string `json:"updated_at,omitempty" url:"updated_at,omitempty"` 21 | 22 | extraProperties map[string]interface{} 23 | rawJSON json.RawMessage 24 | } 25 | 26 | func (g *GetTaskResponse) GetCompletedAt() *string { 27 | if g == nil { 28 | return nil 29 | } 30 | return g.CompletedAt 31 | } 32 | 33 | func (g *GetTaskResponse) GetCreatedAt() *string { 34 | if g == nil { 35 | return nil 36 | } 37 | return g.CreatedAt 38 | } 39 | 40 | func (g *GetTaskResponse) GetError() *TaskErrorResponse { 41 | if g == nil { 42 | return nil 43 | } 44 | return g.Error 45 | } 46 | 47 | func (g *GetTaskResponse) GetProgress() *TaskProgress { 48 | if g == nil { 49 | return nil 50 | } 51 | return g.Progress 52 | } 53 | 54 | func (g *GetTaskResponse) GetStartedAt() *string { 55 | if g == nil { 56 | return nil 57 | } 58 | return g.StartedAt 59 | } 60 | 61 | func (g *GetTaskResponse) GetStatus() *string { 62 | if g == nil { 63 | return nil 64 | } 65 | return g.Status 66 | } 67 | 68 | func (g *GetTaskResponse) GetTaskID() *string { 69 | if g == nil { 70 | return nil 71 | } 72 | return g.TaskID 73 | } 74 | 75 | func (g *GetTaskResponse) GetType() *string { 76 | if g == nil { 77 | return nil 78 | } 79 | return g.Type 80 | } 81 | 82 | func (g *GetTaskResponse) GetUpdatedAt() *string { 83 | if g == nil { 84 | return nil 85 | } 86 | return g.UpdatedAt 87 | } 88 | 89 | func (g *GetTaskResponse) GetExtraProperties() map[string]interface{} { 90 | return g.extraProperties 91 | } 92 | 93 | func (g *GetTaskResponse) UnmarshalJSON(data []byte) error { 94 | type unmarshaler GetTaskResponse 95 | var value unmarshaler 96 | if err := json.Unmarshal(data, &value); err != nil { 97 | return err 98 | } 99 | *g = GetTaskResponse(value) 100 | extraProperties, err := internal.ExtractExtraProperties(data, *g) 101 | if err != nil { 102 | return err 103 | } 104 | g.extraProperties = extraProperties 105 | g.rawJSON = json.RawMessage(data) 106 | return nil 107 | } 108 | 109 | func (g *GetTaskResponse) String() string { 110 | if len(g.rawJSON) > 0 { 111 | if value, err := internal.StringifyJSON(g.rawJSON); err == nil { 112 | return value 113 | } 114 | } 115 | if value, err := internal.StringifyJSON(g); err == nil { 116 | return value 117 | } 118 | return fmt.Sprintf("%#v", g) 119 | } 120 | 121 | type TaskErrorResponse struct { 122 | Code *string `json:"code,omitempty" url:"code,omitempty"` 123 | Details map[string]interface{} `json:"details,omitempty" url:"details,omitempty"` 124 | Message *string `json:"message,omitempty" url:"message,omitempty"` 125 | 126 | extraProperties map[string]interface{} 127 | rawJSON json.RawMessage 128 | } 129 | 130 | func (t *TaskErrorResponse) GetCode() *string { 131 | if t == nil { 132 | return nil 133 | } 134 | return t.Code 135 | } 136 | 137 | func (t *TaskErrorResponse) GetDetails() map[string]interface{} { 138 | if t == nil { 139 | return nil 140 | } 141 | return t.Details 142 | } 143 | 144 | func (t *TaskErrorResponse) GetMessage() *string { 145 | if t == nil { 146 | return nil 147 | } 148 | return t.Message 149 | } 150 | 151 | func (t *TaskErrorResponse) GetExtraProperties() map[string]interface{} { 152 | return t.extraProperties 153 | } 154 | 155 | func (t *TaskErrorResponse) UnmarshalJSON(data []byte) error { 156 | type unmarshaler TaskErrorResponse 157 | var value unmarshaler 158 | if err := json.Unmarshal(data, &value); err != nil { 159 | return err 160 | } 161 | *t = TaskErrorResponse(value) 162 | extraProperties, err := internal.ExtractExtraProperties(data, *t) 163 | if err != nil { 164 | return err 165 | } 166 | t.extraProperties = extraProperties 167 | t.rawJSON = json.RawMessage(data) 168 | return nil 169 | } 170 | 171 | func (t *TaskErrorResponse) String() string { 172 | if len(t.rawJSON) > 0 { 173 | if value, err := internal.StringifyJSON(t.rawJSON); err == nil { 174 | return value 175 | } 176 | } 177 | if value, err := internal.StringifyJSON(t); err == nil { 178 | return value 179 | } 180 | return fmt.Sprintf("%#v", t) 181 | } 182 | 183 | type TaskProgress struct { 184 | Message *string `json:"message,omitempty" url:"message,omitempty"` 185 | Stage *string `json:"stage,omitempty" url:"stage,omitempty"` 186 | 187 | extraProperties map[string]interface{} 188 | rawJSON json.RawMessage 189 | } 190 | 191 | func (t *TaskProgress) GetMessage() *string { 192 | if t == nil { 193 | return nil 194 | } 195 | return t.Message 196 | } 197 | 198 | func (t *TaskProgress) GetStage() *string { 199 | if t == nil { 200 | return nil 201 | } 202 | return t.Stage 203 | } 204 | 205 | func (t *TaskProgress) GetExtraProperties() map[string]interface{} { 206 | return t.extraProperties 207 | } 208 | 209 | func (t *TaskProgress) UnmarshalJSON(data []byte) error { 210 | type unmarshaler TaskProgress 211 | var value unmarshaler 212 | if err := json.Unmarshal(data, &value); err != nil { 213 | return err 214 | } 215 | *t = TaskProgress(value) 216 | extraProperties, err := internal.ExtractExtraProperties(data, *t) 217 | if err != nil { 218 | return err 219 | } 220 | t.extraProperties = extraProperties 221 | t.rawJSON = json.RawMessage(data) 222 | return nil 223 | } 224 | 225 | func (t *TaskProgress) String() string { 226 | if len(t.rawJSON) > 0 { 227 | if value, err := internal.StringifyJSON(t.rawJSON); err == nil { 228 | return value 229 | } 230 | } 231 | if value, err := internal.StringifyJSON(t); err == nil { 232 | return value 233 | } 234 | return fmt.Sprintf("%#v", t) 235 | } 236 | -------------------------------------------------------------------------------- /internal/retrier_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | "time" 11 | 12 | "github.com/getzep/zep-go/v3/core" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | type RetryTestCase struct { 18 | description string 19 | 20 | giveAttempts uint 21 | giveStatusCodes []int 22 | giveResponse *Response 23 | 24 | wantResponse *Response 25 | wantError *core.APIError 26 | } 27 | 28 | func TestRetrier(t *testing.T) { 29 | tests := []*RetryTestCase{ 30 | { 31 | description: "retry request succeeds after multiple failures", 32 | giveAttempts: 3, 33 | giveStatusCodes: []int{ 34 | http.StatusServiceUnavailable, 35 | http.StatusServiceUnavailable, 36 | http.StatusOK, 37 | }, 38 | giveResponse: &Response{ 39 | Id: "1", 40 | }, 41 | wantResponse: &Response{ 42 | Id: "1", 43 | }, 44 | }, 45 | { 46 | description: "retry request fails if MaxAttempts is exceeded", 47 | giveAttempts: 3, 48 | giveStatusCodes: []int{ 49 | http.StatusRequestTimeout, 50 | http.StatusRequestTimeout, 51 | http.StatusRequestTimeout, 52 | http.StatusOK, 53 | }, 54 | wantError: &core.APIError{ 55 | StatusCode: http.StatusRequestTimeout, 56 | }, 57 | }, 58 | { 59 | description: "retry durations increase exponentially and stay within the min and max delay values", 60 | giveAttempts: 4, 61 | giveStatusCodes: []int{ 62 | http.StatusServiceUnavailable, 63 | http.StatusServiceUnavailable, 64 | http.StatusServiceUnavailable, 65 | http.StatusOK, 66 | }, 67 | }, 68 | { 69 | description: "retry does not occur on status code 404", 70 | giveAttempts: 2, 71 | giveStatusCodes: []int{http.StatusNotFound, http.StatusOK}, 72 | wantError: &core.APIError{ 73 | StatusCode: http.StatusNotFound, 74 | }, 75 | }, 76 | { 77 | description: "retries occur on status code 429", 78 | giveAttempts: 2, 79 | giveStatusCodes: []int{http.StatusTooManyRequests, http.StatusOK}, 80 | }, 81 | { 82 | description: "retries occur on status code 408", 83 | giveAttempts: 2, 84 | giveStatusCodes: []int{http.StatusRequestTimeout, http.StatusOK}, 85 | }, 86 | { 87 | description: "retries occur on status code 500", 88 | giveAttempts: 2, 89 | giveStatusCodes: []int{http.StatusInternalServerError, http.StatusOK}, 90 | }, 91 | } 92 | 93 | for _, tc := range tests { 94 | t.Run(tc.description, func(t *testing.T) { 95 | var ( 96 | test = tc 97 | server = newTestRetryServer(t, test) 98 | client = server.Client() 99 | ) 100 | 101 | t.Parallel() 102 | 103 | caller := NewCaller( 104 | &CallerParams{ 105 | Client: client, 106 | }, 107 | ) 108 | 109 | var response *Response 110 | _, err := caller.Call( 111 | context.Background(), 112 | &CallParams{ 113 | URL: server.URL, 114 | Method: http.MethodGet, 115 | Request: &Request{}, 116 | Response: &response, 117 | MaxAttempts: test.giveAttempts, 118 | ResponseIsOptional: true, 119 | }, 120 | ) 121 | 122 | if test.wantError != nil { 123 | require.IsType(t, err, &core.APIError{}) 124 | expectedErrorCode := test.wantError.StatusCode 125 | actualErrorCode := err.(*core.APIError).StatusCode 126 | assert.Equal(t, expectedErrorCode, actualErrorCode) 127 | return 128 | } 129 | 130 | require.NoError(t, err) 131 | assert.Equal(t, test.wantResponse, response) 132 | }) 133 | } 134 | } 135 | 136 | // newTestRetryServer returns a new *httptest.Server configured with the 137 | // given test parameters, suitable for testing retries. 138 | func newTestRetryServer(t *testing.T, tc *RetryTestCase) *httptest.Server { 139 | var index int 140 | timestamps := make([]time.Time, 0, len(tc.giveStatusCodes)) 141 | 142 | return httptest.NewServer( 143 | http.HandlerFunc( 144 | func(w http.ResponseWriter, r *http.Request) { 145 | timestamps = append(timestamps, time.Now()) 146 | if index > 0 && index < len(expectedRetryDurations) { 147 | // Ensure that the duration between retries increases exponentially, 148 | // and that it is within the minimum and maximum retry delay values. 149 | actualDuration := timestamps[index].Sub(timestamps[index-1]) 150 | expectedDurationMin := expectedRetryDurations[index-1] * 75 / 100 151 | expectedDurationMax := expectedRetryDurations[index-1] * 125 / 100 152 | assert.True( 153 | t, 154 | actualDuration >= expectedDurationMin && actualDuration <= expectedDurationMax, 155 | "expected duration to be in range [%v, %v], got %v", 156 | expectedDurationMin, 157 | expectedDurationMax, 158 | actualDuration, 159 | ) 160 | assert.LessOrEqual( 161 | t, 162 | actualDuration, 163 | maxRetryDelay, 164 | "expected duration to be less than the maxRetryDelay (%v), got %v", 165 | maxRetryDelay, 166 | actualDuration, 167 | ) 168 | assert.GreaterOrEqual( 169 | t, 170 | actualDuration, 171 | minRetryDelay, 172 | "expected duration to be greater than the minRetryDelay (%v), got %v", 173 | minRetryDelay, 174 | actualDuration, 175 | ) 176 | } 177 | 178 | request := new(Request) 179 | bytes, err := io.ReadAll(r.Body) 180 | require.NoError(t, err) 181 | require.NoError(t, json.Unmarshal(bytes, request)) 182 | require.LessOrEqual(t, index, len(tc.giveStatusCodes)) 183 | 184 | statusCode := tc.giveStatusCodes[index] 185 | w.WriteHeader(statusCode) 186 | 187 | if tc.giveResponse != nil && statusCode == http.StatusOK { 188 | bytes, err = json.Marshal(tc.giveResponse) 189 | require.NoError(t, err) 190 | _, err = w.Write(bytes) 191 | require.NoError(t, err) 192 | } 193 | 194 | index++ 195 | }, 196 | ), 197 | ) 198 | } 199 | 200 | // expectedRetryDurations holds an array of calculated retry durations, 201 | // where the index of the array should correspond to the retry attempt. 202 | // 203 | // Values are calculated based off of `minRetryDelay + minRetryDelay*i*i`, with 204 | // a max and min value of 5000ms and 500ms respectively. 205 | var expectedRetryDurations = []time.Duration{ 206 | 500 * time.Millisecond, 207 | 1000 * time.Millisecond, 208 | 2500 * time.Millisecond, 209 | 5000 * time.Millisecond, 210 | 5000 * time.Millisecond, 211 | } 212 | -------------------------------------------------------------------------------- /graph/client/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package client 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | edge "github.com/getzep/zep-go/v3/graph/edge" 10 | episode "github.com/getzep/zep-go/v3/graph/episode" 11 | node "github.com/getzep/zep-go/v3/graph/node" 12 | internal "github.com/getzep/zep-go/v3/internal" 13 | option "github.com/getzep/zep-go/v3/option" 14 | http "net/http" 15 | os "os" 16 | ) 17 | 18 | type Client struct { 19 | WithRawResponse *RawClient 20 | Edge *edge.Client 21 | Episode *episode.Client 22 | Node *node.Client 23 | 24 | baseURL string 25 | caller *internal.Caller 26 | header http.Header 27 | } 28 | 29 | func NewClient(opts ...option.RequestOption) *Client { 30 | options := core.NewRequestOptions(opts...) 31 | if options.APIKey == "" { 32 | options.APIKey = os.Getenv("ZEP_API_KEY") 33 | } 34 | return &Client{ 35 | Edge: edge.NewClient(opts...), 36 | Episode: episode.NewClient(opts...), 37 | Node: node.NewClient(opts...), 38 | WithRawResponse: NewRawClient(options), 39 | baseURL: options.BaseURL, 40 | caller: internal.NewCaller( 41 | &internal.CallerParams{ 42 | Client: options.HTTPClient, 43 | MaxAttempts: options.MaxAttempts, 44 | }, 45 | ), 46 | header: options.ToHeader(), 47 | } 48 | } 49 | 50 | // Returns all entity types for a project, user, or graph. 51 | func (c *Client) ListEntityTypes( 52 | ctx context.Context, 53 | request *v3.GraphListEntityTypesRequest, 54 | opts ...option.RequestOption, 55 | ) (*v3.EntityTypeResponse, error) { 56 | response, err := c.WithRawResponse.ListEntityTypes( 57 | ctx, 58 | request, 59 | opts..., 60 | ) 61 | if err != nil { 62 | return nil, err 63 | } 64 | return response.Body, nil 65 | } 66 | 67 | // Sets the entity types for multiple users and graphs, replacing any existing ones. 68 | func (c *Client) SetEntityTypesInternal( 69 | ctx context.Context, 70 | request *v3.EntityTypeRequest, 71 | opts ...option.RequestOption, 72 | ) (*v3.SuccessResponse, error) { 73 | response, err := c.WithRawResponse.SetEntityTypesInternal( 74 | ctx, 75 | request, 76 | opts..., 77 | ) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return response.Body, nil 82 | } 83 | 84 | // Add data to the graph. 85 | func (c *Client) Add( 86 | ctx context.Context, 87 | request *v3.AddDataRequest, 88 | opts ...option.RequestOption, 89 | ) (*v3.Episode, error) { 90 | response, err := c.WithRawResponse.Add( 91 | ctx, 92 | request, 93 | opts..., 94 | ) 95 | if err != nil { 96 | return nil, err 97 | } 98 | return response.Body, nil 99 | } 100 | 101 | // Add data to the graph in batch mode, processing episodes concurrently. Use only for data that is insensitive to processing order. 102 | func (c *Client) AddBatch( 103 | ctx context.Context, 104 | request *v3.AddDataBatchRequest, 105 | opts ...option.RequestOption, 106 | ) ([]*v3.Episode, error) { 107 | response, err := c.WithRawResponse.AddBatch( 108 | ctx, 109 | request, 110 | opts..., 111 | ) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return response.Body, nil 116 | } 117 | 118 | // Add a fact triple for a user or group 119 | func (c *Client) AddFactTriple( 120 | ctx context.Context, 121 | request *v3.AddTripleRequest, 122 | opts ...option.RequestOption, 123 | ) (*v3.AddTripleResponse, error) { 124 | response, err := c.WithRawResponse.AddFactTriple( 125 | ctx, 126 | request, 127 | opts..., 128 | ) 129 | if err != nil { 130 | return nil, err 131 | } 132 | return response.Body, nil 133 | } 134 | 135 | // Clone a user or group graph. 136 | func (c *Client) Clone( 137 | ctx context.Context, 138 | request *v3.CloneGraphRequest, 139 | opts ...option.RequestOption, 140 | ) (*v3.CloneGraphResponse, error) { 141 | response, err := c.WithRawResponse.Clone( 142 | ctx, 143 | request, 144 | opts..., 145 | ) 146 | if err != nil { 147 | return nil, err 148 | } 149 | return response.Body, nil 150 | } 151 | 152 | // Creates a new graph. 153 | func (c *Client) Create( 154 | ctx context.Context, 155 | request *v3.CreateGraphRequest, 156 | opts ...option.RequestOption, 157 | ) (*v3.Graph, error) { 158 | response, err := c.WithRawResponse.Create( 159 | ctx, 160 | request, 161 | opts..., 162 | ) 163 | if err != nil { 164 | return nil, err 165 | } 166 | return response.Body, nil 167 | } 168 | 169 | // Returns all graphs. In order to list users, use user.list_ordered instead 170 | func (c *Client) ListAll( 171 | ctx context.Context, 172 | request *v3.GraphListAllRequest, 173 | opts ...option.RequestOption, 174 | ) (*v3.GraphListResponse, error) { 175 | response, err := c.WithRawResponse.ListAll( 176 | ctx, 177 | request, 178 | opts..., 179 | ) 180 | if err != nil { 181 | return nil, err 182 | } 183 | return response.Body, nil 184 | } 185 | 186 | // Perform a graph search query. 187 | func (c *Client) Search( 188 | ctx context.Context, 189 | request *v3.GraphSearchQuery, 190 | opts ...option.RequestOption, 191 | ) (*v3.GraphSearchResults, error) { 192 | response, err := c.WithRawResponse.Search( 193 | ctx, 194 | request, 195 | opts..., 196 | ) 197 | if err != nil { 198 | return nil, err 199 | } 200 | return response.Body, nil 201 | } 202 | 203 | // Returns a graph. 204 | func (c *Client) Get( 205 | ctx context.Context, 206 | // The graph_id of the graph to get. 207 | graphID string, 208 | opts ...option.RequestOption, 209 | ) (*v3.Graph, error) { 210 | response, err := c.WithRawResponse.Get( 211 | ctx, 212 | graphID, 213 | opts..., 214 | ) 215 | if err != nil { 216 | return nil, err 217 | } 218 | return response.Body, nil 219 | } 220 | 221 | // Deletes a graph. If you would like to delete a user graph, make sure to use user.delete instead. 222 | func (c *Client) Delete( 223 | ctx context.Context, 224 | // Graph ID 225 | graphID string, 226 | opts ...option.RequestOption, 227 | ) (*v3.SuccessResponse, error) { 228 | response, err := c.WithRawResponse.Delete( 229 | ctx, 230 | graphID, 231 | opts..., 232 | ) 233 | if err != nil { 234 | return nil, err 235 | } 236 | return response.Body, nil 237 | } 238 | 239 | // Updates information about a graph. 240 | func (c *Client) Update( 241 | ctx context.Context, 242 | // Graph ID 243 | graphID string, 244 | request *v3.UpdateGraphRequest, 245 | opts ...option.RequestOption, 246 | ) (*v3.Graph, error) { 247 | response, err := c.WithRawResponse.Update( 248 | ctx, 249 | graphID, 250 | request, 251 | opts..., 252 | ) 253 | if err != nil { 254 | return nil, err 255 | } 256 | return response.Body, nil 257 | } 258 | -------------------------------------------------------------------------------- /internal/query_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestQueryValues(t *testing.T) { 12 | t.Run("empty optional", func(t *testing.T) { 13 | type nested struct { 14 | Value *string `json:"value,omitempty" url:"value,omitempty"` 15 | } 16 | type example struct { 17 | Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` 18 | } 19 | 20 | values, err := QueryValues(&example{}) 21 | require.NoError(t, err) 22 | assert.Empty(t, values) 23 | }) 24 | 25 | t.Run("empty required", func(t *testing.T) { 26 | type nested struct { 27 | Value *string `json:"value,omitempty" url:"value,omitempty"` 28 | } 29 | type example struct { 30 | Required string `json:"required" url:"required"` 31 | Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` 32 | } 33 | 34 | values, err := QueryValues(&example{}) 35 | require.NoError(t, err) 36 | assert.Equal(t, "required=", values.Encode()) 37 | }) 38 | 39 | t.Run("allow multiple", func(t *testing.T) { 40 | type example struct { 41 | Values []string `json:"values" url:"values"` 42 | } 43 | 44 | values, err := QueryValues( 45 | &example{ 46 | Values: []string{"foo", "bar", "baz"}, 47 | }, 48 | ) 49 | require.NoError(t, err) 50 | assert.Equal(t, "values=foo&values=bar&values=baz", values.Encode()) 51 | }) 52 | 53 | t.Run("nested object", func(t *testing.T) { 54 | type nested struct { 55 | Value *string `json:"value,omitempty" url:"value,omitempty"` 56 | } 57 | type example struct { 58 | Required string `json:"required" url:"required"` 59 | Nested *nested `json:"nested,omitempty" url:"nested,omitempty"` 60 | } 61 | 62 | nestedValue := "nestedValue" 63 | values, err := QueryValues( 64 | &example{ 65 | Required: "requiredValue", 66 | Nested: &nested{ 67 | Value: &nestedValue, 68 | }, 69 | }, 70 | ) 71 | require.NoError(t, err) 72 | assert.Equal(t, "nested%5Bvalue%5D=nestedValue&required=requiredValue", values.Encode()) 73 | }) 74 | 75 | t.Run("url unspecified", func(t *testing.T) { 76 | type example struct { 77 | Required string `json:"required" url:"required"` 78 | NotFound string `json:"notFound"` 79 | } 80 | 81 | values, err := QueryValues( 82 | &example{ 83 | Required: "requiredValue", 84 | NotFound: "notFound", 85 | }, 86 | ) 87 | require.NoError(t, err) 88 | assert.Equal(t, "required=requiredValue", values.Encode()) 89 | }) 90 | 91 | t.Run("url ignored", func(t *testing.T) { 92 | type example struct { 93 | Required string `json:"required" url:"required"` 94 | NotFound string `json:"notFound" url:"-"` 95 | } 96 | 97 | values, err := QueryValues( 98 | &example{ 99 | Required: "requiredValue", 100 | NotFound: "notFound", 101 | }, 102 | ) 103 | require.NoError(t, err) 104 | assert.Equal(t, "required=requiredValue", values.Encode()) 105 | }) 106 | 107 | t.Run("datetime", func(t *testing.T) { 108 | type example struct { 109 | DateTime time.Time `json:"dateTime" url:"dateTime"` 110 | } 111 | 112 | values, err := QueryValues( 113 | &example{ 114 | DateTime: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), 115 | }, 116 | ) 117 | require.NoError(t, err) 118 | assert.Equal(t, "dateTime=1994-03-16T12%3A34%3A56Z", values.Encode()) 119 | }) 120 | 121 | t.Run("date", func(t *testing.T) { 122 | type example struct { 123 | Date time.Time `json:"date" url:"date" format:"date"` 124 | } 125 | 126 | values, err := QueryValues( 127 | &example{ 128 | Date: time.Date(1994, 3, 16, 12, 34, 56, 0, time.UTC), 129 | }, 130 | ) 131 | require.NoError(t, err) 132 | assert.Equal(t, "date=1994-03-16", values.Encode()) 133 | }) 134 | 135 | t.Run("optional time", func(t *testing.T) { 136 | type example struct { 137 | Date *time.Time `json:"date,omitempty" url:"date,omitempty" format:"date"` 138 | } 139 | 140 | values, err := QueryValues( 141 | &example{}, 142 | ) 143 | require.NoError(t, err) 144 | assert.Empty(t, values.Encode()) 145 | }) 146 | 147 | t.Run("omitempty with non-pointer zero value", func(t *testing.T) { 148 | type enum string 149 | 150 | type example struct { 151 | Enum enum `json:"enum,omitempty" url:"enum,omitempty"` 152 | } 153 | 154 | values, err := QueryValues( 155 | &example{}, 156 | ) 157 | require.NoError(t, err) 158 | assert.Empty(t, values.Encode()) 159 | }) 160 | 161 | t.Run("object array", func(t *testing.T) { 162 | type object struct { 163 | Key string `json:"key" url:"key"` 164 | Value string `json:"value" url:"value"` 165 | } 166 | type example struct { 167 | Objects []*object `json:"objects,omitempty" url:"objects,omitempty"` 168 | } 169 | 170 | values, err := QueryValues( 171 | &example{ 172 | Objects: []*object{ 173 | { 174 | Key: "hello", 175 | Value: "world", 176 | }, 177 | { 178 | Key: "foo", 179 | Value: "bar", 180 | }, 181 | }, 182 | }, 183 | ) 184 | require.NoError(t, err) 185 | assert.Equal(t, "objects%5Bkey%5D=hello&objects%5Bkey%5D=foo&objects%5Bvalue%5D=world&objects%5Bvalue%5D=bar", values.Encode()) 186 | }) 187 | 188 | t.Run("map", func(t *testing.T) { 189 | type request struct { 190 | Metadata map[string]interface{} `json:"metadata" url:"metadata"` 191 | } 192 | values, err := QueryValues( 193 | &request{ 194 | Metadata: map[string]interface{}{ 195 | "foo": "bar", 196 | "baz": "qux", 197 | }, 198 | }, 199 | ) 200 | require.NoError(t, err) 201 | assert.Equal(t, "metadata%5Bbaz%5D=qux&metadata%5Bfoo%5D=bar", values.Encode()) 202 | }) 203 | 204 | t.Run("nested map", func(t *testing.T) { 205 | type request struct { 206 | Metadata map[string]interface{} `json:"metadata" url:"metadata"` 207 | } 208 | values, err := QueryValues( 209 | &request{ 210 | Metadata: map[string]interface{}{ 211 | "inner": map[string]interface{}{ 212 | "foo": "bar", 213 | }, 214 | }, 215 | }, 216 | ) 217 | require.NoError(t, err) 218 | assert.Equal(t, "metadata%5Binner%5D%5Bfoo%5D=bar", values.Encode()) 219 | }) 220 | 221 | t.Run("nested map array", func(t *testing.T) { 222 | type request struct { 223 | Metadata map[string]interface{} `json:"metadata" url:"metadata"` 224 | } 225 | values, err := QueryValues( 226 | &request{ 227 | Metadata: map[string]interface{}{ 228 | "inner": []string{ 229 | "one", 230 | "two", 231 | "three", 232 | }, 233 | }, 234 | }, 235 | ) 236 | require.NoError(t, err) 237 | assert.Equal(t, "metadata%5Binner%5D=one&metadata%5Binner%5D=two&metadata%5Binner%5D=three", values.Encode()) 238 | }) 239 | } 240 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zep Go Library 2 | 3 |

4 | 5 | Zep Logo 6 | 7 |

8 | 9 |

10 | Zep: Context Engineering for AI Agents. 11 |

12 |

Assemble the right context from chat history, business data, and user behavior. Build agents that work.

13 |
14 |

15 | Chat on Discord 19 | Twitter Follow 20 | Go Reference 21 | Go Report Card 22 | CI 26 | 27 | CI 31 | 32 |

33 |

34 | Documentation | 35 | LangChain | 36 | Discord
37 | www.getzep.com 38 |

39 | 40 | 41 | 42 | The Zep Go library provides convenient access to the Zep Cloud API from Go. 43 | 44 | ## Requirements 45 | 46 | This module requires Go version >= 1.13. 47 | 48 | # Installation 49 | 50 | Run the following command to use the Zep Go library in your module: 51 | 52 | ```sh 53 | go get github.com/getzep/zep-go/v3 54 | ``` 55 | 56 | ## Initialize Client 57 | 58 | ```go 59 | import ( 60 | "github.com/getzep/zep-go/v3" 61 | zepclient "github.com/getzep/zep-go/v3/client" 62 | "github.com/getzep/zep-go/v3/option" 63 | ) 64 | 65 | client := zepclient.NewClient( 66 | // this api key is `api_secret` line from zep.yaml of your local server or your Zep cloud api-key 67 | option.WithAPIKey(""), 68 | ) 69 | ``` 70 | 71 | ## Add Messages to thread 72 | 73 | ```go 74 | _, err = client.Thread.AddMessages(ctx, threadID, &zep.AddThreadMessagesRequest{ 75 | Messages: []*zep.Message{ 76 | { 77 | Name: zep.String("customer"), 78 | Content: "Hello, can I buy some shoes?", 79 | Role: "user", 80 | }, 81 | }, 82 | }) 83 | ``` 84 | 85 | ## Get User context 86 | 87 | ```go 88 | threadUserContext, err := client.Thread.GetUserContext( 89 | ctx, 90 | threadID, 91 | nil, 92 | ) 93 | ``` 94 | 95 | ## Optionals 96 | 97 | This library models optional primitives and enum types as pointers. This is primarily meant to distinguish 98 | default zero values from explicit values (e.g. `false` for `bool` and `""` for `string`). A collection of 99 | helper functions are provided to easily map a primitive or enum to its pointer-equivalent (e.g. `zep.Int`). 100 | 101 | ## Request Options 102 | 103 | A variety of request options are included to adapt the behavior of the library, which includes 104 | configuring authorization tokens, or providing your own instrumented `*http.Client`. Both of 105 | these options are shown below: 106 | 107 | ```go 108 | client := zepclient.NewClient( 109 | option.WithAPIKey(""), 110 | option.WithHTTPClient( 111 | &http.Client{ 112 | Timeout: 5 * time.Second, 113 | }, 114 | ), 115 | ) 116 | ``` 117 | 118 | These request options can either be specified on the client so that they're applied on _every_ 119 | request (shown above), or for an individual request like so: 120 | 121 | ```go 122 | _, _ = client.Thread.GetUserContext(ctx, "thread_id", nil, option.WithAPIKey("")) 123 | ``` 124 | 125 | > Providing your own `*http.Client` is recommended. Otherwise, the `http.DefaultClient` will be used, 126 | > and your client will wait indefinitely for a response (unless the per-request, context-based timeout 127 | > is used). 128 | 129 | ## Automatic Retries 130 | 131 | The Zep Go client is instrumented with automatic retries with exponential backoff. A request will be 132 | retried as long as the request is deemed retriable and the number of retry attempts has not grown larger 133 | than the configured retry limit (default: 2). 134 | 135 | A request is deemed retriable when any of the following HTTP status codes is returned: 136 | 137 | - [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) 138 | - [409](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409) (Conflict) 139 | - [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) 140 | - [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) 141 | 142 | You can use the `option.WithMaxAttempts` option to configure the maximum retry limit to 143 | your liking. For example, if you want to disable retries for the client entirely, you can 144 | set this value to 1 like so: 145 | 146 | ```go 147 | client := zepclient.NewClient( 148 | option.WithMaxAttempts(1), 149 | ) 150 | ``` 151 | 152 | This can be done for an individual request, too: 153 | 154 | ```go 155 | _, _ = client.Thread.GetUserContext(ctx, "thread_id", nil, option.WithMaxAttempts(1)) 156 | ``` 157 | 158 | ## Errors 159 | 160 | Structured error types are returned from API calls that return non-success status codes. For example, 161 | you can check if the error was due to a bad request (i.e. status code 400) with the following: 162 | 163 | ```go 164 | _, err := client.Thread.GetUserContext(ctx, "thread_id", nil) 165 | if err != nil { 166 | if badRequestErr, ok := err.(*zep.BadRequestError); 167 | // Do something with the bad request ... 168 | } 169 | return err 170 | } 171 | ``` 172 | 173 | These errors are also compatible with the `errors.Is` and `errors.As` APIs, so you can access the error 174 | like so: 175 | 176 | ```go 177 | _, err := client.Thread.GetUserContext(ctx, "thread_id", nil) 178 | if err != nil { 179 | var badRequestErr *zep.BadRequestError 180 | if errors.As(err, badRequestErr) { 181 | // Do something with the bad request ... 182 | } 183 | return err 184 | } 185 | ``` 186 | 187 | If you'd like to wrap the errors with additional information and still retain the ability 188 | to access the type with `errors.Is` and `errors.As`, you can use the `%w` directive: 189 | 190 | ```go 191 | _, err := client.Thread.GetUserContext(ctx, "thread_id", nil) 192 | if err != nil { 193 | return fmt.Errorf("failed to get context: %w", err) 194 | } 195 | ``` 196 | 197 | ## Contributing 198 | 199 | While we value open-source contributions to this SDK, this library is generated programmatically. 200 | Additions made directly to this library would have to be moved over to our generation code, 201 | otherwise they would be overwritten upon the next generated release. Feel free to open a PR as 202 | a proof of concept, but know that we will not be able to merge it as-is. We suggest opening 203 | an issue first to discuss with us! 204 | 205 | On the other hand, contributions to the `README.md` are always very welcome! 206 | -------------------------------------------------------------------------------- /graph/edge/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package edge 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | ) 13 | 14 | type RawClient struct { 15 | baseURL string 16 | caller *internal.Caller 17 | header http.Header 18 | } 19 | 20 | func NewRawClient(options *core.RequestOptions) *RawClient { 21 | return &RawClient{ 22 | baseURL: options.BaseURL, 23 | caller: internal.NewCaller( 24 | &internal.CallerParams{ 25 | Client: options.HTTPClient, 26 | MaxAttempts: options.MaxAttempts, 27 | }, 28 | ), 29 | header: options.ToHeader(), 30 | } 31 | } 32 | 33 | func (r *RawClient) GetByGraphID( 34 | ctx context.Context, 35 | // Graph ID 36 | graphID string, 37 | request *v3.GraphEdgesRequest, 38 | opts ...option.RequestOption, 39 | ) (*core.Response[[]*v3.EntityEdge], error) { 40 | options := core.NewRequestOptions(opts...) 41 | baseURL := internal.ResolveBaseURL( 42 | options.BaseURL, 43 | r.baseURL, 44 | "https://api.getzep.com/api/v2", 45 | ) 46 | endpointURL := internal.EncodeURL( 47 | baseURL+"/graph/edge/graph/%v", 48 | graphID, 49 | ) 50 | headers := internal.MergeHeaders( 51 | r.header.Clone(), 52 | options.ToHeader(), 53 | ) 54 | errorCodes := internal.ErrorCodes{ 55 | 400: func(apiError *core.APIError) error { 56 | return &v3.BadRequestError{ 57 | APIError: apiError, 58 | } 59 | }, 60 | 500: func(apiError *core.APIError) error { 61 | return &v3.InternalServerError{ 62 | APIError: apiError, 63 | } 64 | }, 65 | } 66 | var response []*v3.EntityEdge 67 | raw, err := r.caller.Call( 68 | ctx, 69 | &internal.CallParams{ 70 | URL: endpointURL, 71 | Method: http.MethodPost, 72 | Headers: headers, 73 | MaxAttempts: options.MaxAttempts, 74 | BodyProperties: options.BodyProperties, 75 | QueryParameters: options.QueryParameters, 76 | Client: options.HTTPClient, 77 | Request: request, 78 | Response: &response, 79 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 80 | }, 81 | ) 82 | if err != nil { 83 | return nil, err 84 | } 85 | return &core.Response[[]*v3.EntityEdge]{ 86 | StatusCode: raw.StatusCode, 87 | Header: raw.Header, 88 | Body: response, 89 | }, nil 90 | } 91 | 92 | func (r *RawClient) GetByUserID( 93 | ctx context.Context, 94 | // User ID 95 | userID string, 96 | request *v3.GraphEdgesRequest, 97 | opts ...option.RequestOption, 98 | ) (*core.Response[[]*v3.EntityEdge], error) { 99 | options := core.NewRequestOptions(opts...) 100 | baseURL := internal.ResolveBaseURL( 101 | options.BaseURL, 102 | r.baseURL, 103 | "https://api.getzep.com/api/v2", 104 | ) 105 | endpointURL := internal.EncodeURL( 106 | baseURL+"/graph/edge/user/%v", 107 | userID, 108 | ) 109 | headers := internal.MergeHeaders( 110 | r.header.Clone(), 111 | options.ToHeader(), 112 | ) 113 | errorCodes := internal.ErrorCodes{ 114 | 400: func(apiError *core.APIError) error { 115 | return &v3.BadRequestError{ 116 | APIError: apiError, 117 | } 118 | }, 119 | 500: func(apiError *core.APIError) error { 120 | return &v3.InternalServerError{ 121 | APIError: apiError, 122 | } 123 | }, 124 | } 125 | var response []*v3.EntityEdge 126 | raw, err := r.caller.Call( 127 | ctx, 128 | &internal.CallParams{ 129 | URL: endpointURL, 130 | Method: http.MethodPost, 131 | Headers: headers, 132 | MaxAttempts: options.MaxAttempts, 133 | BodyProperties: options.BodyProperties, 134 | QueryParameters: options.QueryParameters, 135 | Client: options.HTTPClient, 136 | Request: request, 137 | Response: &response, 138 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 139 | }, 140 | ) 141 | if err != nil { 142 | return nil, err 143 | } 144 | return &core.Response[[]*v3.EntityEdge]{ 145 | StatusCode: raw.StatusCode, 146 | Header: raw.Header, 147 | Body: response, 148 | }, nil 149 | } 150 | 151 | func (r *RawClient) Get( 152 | ctx context.Context, 153 | // Edge UUID 154 | uuid string, 155 | opts ...option.RequestOption, 156 | ) (*core.Response[*v3.EntityEdge], error) { 157 | options := core.NewRequestOptions(opts...) 158 | baseURL := internal.ResolveBaseURL( 159 | options.BaseURL, 160 | r.baseURL, 161 | "https://api.getzep.com/api/v2", 162 | ) 163 | endpointURL := internal.EncodeURL( 164 | baseURL+"/graph/edge/%v", 165 | uuid, 166 | ) 167 | headers := internal.MergeHeaders( 168 | r.header.Clone(), 169 | options.ToHeader(), 170 | ) 171 | errorCodes := internal.ErrorCodes{ 172 | 400: func(apiError *core.APIError) error { 173 | return &v3.BadRequestError{ 174 | APIError: apiError, 175 | } 176 | }, 177 | 404: func(apiError *core.APIError) error { 178 | return &v3.NotFoundError{ 179 | APIError: apiError, 180 | } 181 | }, 182 | 500: func(apiError *core.APIError) error { 183 | return &v3.InternalServerError{ 184 | APIError: apiError, 185 | } 186 | }, 187 | } 188 | var response *v3.EntityEdge 189 | raw, err := r.caller.Call( 190 | ctx, 191 | &internal.CallParams{ 192 | URL: endpointURL, 193 | Method: http.MethodGet, 194 | Headers: headers, 195 | MaxAttempts: options.MaxAttempts, 196 | BodyProperties: options.BodyProperties, 197 | QueryParameters: options.QueryParameters, 198 | Client: options.HTTPClient, 199 | Response: &response, 200 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 201 | }, 202 | ) 203 | if err != nil { 204 | return nil, err 205 | } 206 | return &core.Response[*v3.EntityEdge]{ 207 | StatusCode: raw.StatusCode, 208 | Header: raw.Header, 209 | Body: response, 210 | }, nil 211 | } 212 | 213 | func (r *RawClient) Delete( 214 | ctx context.Context, 215 | // Edge UUID 216 | uuid string, 217 | opts ...option.RequestOption, 218 | ) (*core.Response[*v3.SuccessResponse], error) { 219 | options := core.NewRequestOptions(opts...) 220 | baseURL := internal.ResolveBaseURL( 221 | options.BaseURL, 222 | r.baseURL, 223 | "https://api.getzep.com/api/v2", 224 | ) 225 | endpointURL := internal.EncodeURL( 226 | baseURL+"/graph/edge/%v", 227 | uuid, 228 | ) 229 | headers := internal.MergeHeaders( 230 | r.header.Clone(), 231 | options.ToHeader(), 232 | ) 233 | errorCodes := internal.ErrorCodes{ 234 | 400: func(apiError *core.APIError) error { 235 | return &v3.BadRequestError{ 236 | APIError: apiError, 237 | } 238 | }, 239 | 500: func(apiError *core.APIError) error { 240 | return &v3.InternalServerError{ 241 | APIError: apiError, 242 | } 243 | }, 244 | } 245 | var response *v3.SuccessResponse 246 | raw, err := r.caller.Call( 247 | ctx, 248 | &internal.CallParams{ 249 | URL: endpointURL, 250 | Method: http.MethodDelete, 251 | Headers: headers, 252 | MaxAttempts: options.MaxAttempts, 253 | BodyProperties: options.BodyProperties, 254 | QueryParameters: options.QueryParameters, 255 | Client: options.HTTPClient, 256 | Response: &response, 257 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 258 | }, 259 | ) 260 | if err != nil { 261 | return nil, err 262 | } 263 | return &core.Response[*v3.SuccessResponse]{ 264 | StatusCode: raw.StatusCode, 265 | Header: raw.Header, 266 | Body: response, 267 | }, nil 268 | } 269 | -------------------------------------------------------------------------------- /internal/caller.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "net/http" 11 | "net/url" 12 | "reflect" 13 | "strings" 14 | 15 | "github.com/getzep/zep-go/v3/core" 16 | ) 17 | 18 | const ( 19 | // contentType specifies the JSON Content-Type header value. 20 | contentType = "application/json" 21 | contentTypeHeader = "Content-Type" 22 | ) 23 | 24 | // Caller calls APIs and deserializes their response, if any. 25 | type Caller struct { 26 | client core.HTTPClient 27 | retrier *Retrier 28 | } 29 | 30 | // CallerParams represents the parameters used to constrcut a new *Caller. 31 | type CallerParams struct { 32 | Client core.HTTPClient 33 | MaxAttempts uint 34 | } 35 | 36 | // NewCaller returns a new *Caller backed by the given parameters. 37 | func NewCaller(params *CallerParams) *Caller { 38 | var httpClient core.HTTPClient = http.DefaultClient 39 | if params.Client != nil { 40 | httpClient = params.Client 41 | } 42 | var retryOptions []RetryOption 43 | if params.MaxAttempts > 0 { 44 | retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) 45 | } 46 | return &Caller{ 47 | client: httpClient, 48 | retrier: NewRetrier(retryOptions...), 49 | } 50 | } 51 | 52 | // CallParams represents the parameters used to issue an API call. 53 | type CallParams struct { 54 | URL string 55 | Method string 56 | MaxAttempts uint 57 | Headers http.Header 58 | BodyProperties map[string]interface{} 59 | QueryParameters url.Values 60 | Client core.HTTPClient 61 | Request interface{} 62 | Response interface{} 63 | ResponseIsOptional bool 64 | ErrorDecoder ErrorDecoder 65 | } 66 | 67 | // CallResponse is a parsed HTTP response from an API call. 68 | type CallResponse struct { 69 | StatusCode int 70 | Header http.Header 71 | } 72 | 73 | // Call issues an API call according to the given call parameters. 74 | func (c *Caller) Call(ctx context.Context, params *CallParams) (*CallResponse, error) { 75 | url := buildURL(params.URL, params.QueryParameters) 76 | req, err := newRequest( 77 | ctx, 78 | url, 79 | params.Method, 80 | params.Headers, 81 | params.Request, 82 | params.BodyProperties, 83 | ) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | // If the call has been cancelled, don't issue the request. 89 | if err := ctx.Err(); err != nil { 90 | return nil, err 91 | } 92 | 93 | client := c.client 94 | if params.Client != nil { 95 | // Use the HTTP client scoped to the request. 96 | client = params.Client 97 | } 98 | 99 | var retryOptions []RetryOption 100 | if params.MaxAttempts > 0 { 101 | retryOptions = append(retryOptions, WithMaxAttempts(params.MaxAttempts)) 102 | } 103 | 104 | resp, err := c.retrier.Run( 105 | client.Do, 106 | req, 107 | params.ErrorDecoder, 108 | retryOptions..., 109 | ) 110 | if err != nil { 111 | return nil, err 112 | } 113 | 114 | // Close the response body after we're done. 115 | defer resp.Body.Close() 116 | 117 | // Check if the call was cancelled before we return the error 118 | // associated with the call and/or unmarshal the response data. 119 | if err := ctx.Err(); err != nil { 120 | return nil, err 121 | } 122 | 123 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { 124 | return nil, decodeError(resp, params.ErrorDecoder) 125 | } 126 | 127 | // Mutate the response parameter in-place. 128 | if params.Response != nil { 129 | if writer, ok := params.Response.(io.Writer); ok { 130 | _, err = io.Copy(writer, resp.Body) 131 | } else { 132 | err = json.NewDecoder(resp.Body).Decode(params.Response) 133 | } 134 | if err != nil { 135 | if err == io.EOF { 136 | if params.ResponseIsOptional { 137 | // The response is optional, so we should ignore the 138 | // io.EOF error 139 | return &CallResponse{ 140 | StatusCode: resp.StatusCode, 141 | Header: resp.Header, 142 | }, nil 143 | } 144 | return nil, fmt.Errorf("expected a %T response, but the server responded with nothing", params.Response) 145 | } 146 | return nil, err 147 | } 148 | } 149 | 150 | return &CallResponse{ 151 | StatusCode: resp.StatusCode, 152 | Header: resp.Header, 153 | }, nil 154 | } 155 | 156 | // buildURL constructs the final URL by appending the given query parameters (if any). 157 | func buildURL( 158 | url string, 159 | queryParameters url.Values, 160 | ) string { 161 | if len(queryParameters) == 0 { 162 | return url 163 | } 164 | if strings.ContainsRune(url, '?') { 165 | url += "&" 166 | } else { 167 | url += "?" 168 | } 169 | url += queryParameters.Encode() 170 | return url 171 | } 172 | 173 | // newRequest returns a new *http.Request with all of the fields 174 | // required to issue the call. 175 | func newRequest( 176 | ctx context.Context, 177 | url string, 178 | method string, 179 | endpointHeaders http.Header, 180 | request interface{}, 181 | bodyProperties map[string]interface{}, 182 | ) (*http.Request, error) { 183 | requestBody, err := newRequestBody(request, bodyProperties) 184 | if err != nil { 185 | return nil, err 186 | } 187 | req, err := http.NewRequestWithContext(ctx, method, url, requestBody) 188 | if err != nil { 189 | return nil, err 190 | } 191 | req = req.WithContext(ctx) 192 | req.Header.Set(contentTypeHeader, contentType) 193 | for name, values := range endpointHeaders { 194 | req.Header[name] = values 195 | } 196 | return req, nil 197 | } 198 | 199 | // newRequestBody returns a new io.Reader that represents the HTTP request body. 200 | func newRequestBody(request interface{}, bodyProperties map[string]interface{}) (io.Reader, error) { 201 | if isNil(request) { 202 | if len(bodyProperties) == 0 { 203 | return nil, nil 204 | } 205 | requestBytes, err := json.Marshal(bodyProperties) 206 | if err != nil { 207 | return nil, err 208 | } 209 | return bytes.NewReader(requestBytes), nil 210 | } 211 | if body, ok := request.(io.Reader); ok { 212 | return body, nil 213 | } 214 | requestBytes, err := MarshalJSONWithExtraProperties(request, bodyProperties) 215 | if err != nil { 216 | return nil, err 217 | } 218 | return bytes.NewReader(requestBytes), nil 219 | } 220 | 221 | // decodeError decodes the error from the given HTTP response. Note that 222 | // it's the caller's responsibility to close the response body. 223 | func decodeError(response *http.Response, errorDecoder ErrorDecoder) error { 224 | if errorDecoder != nil { 225 | // This endpoint has custom errors, so we'll 226 | // attempt to unmarshal the error into a structured 227 | // type based on the status code. 228 | return errorDecoder(response.StatusCode, response.Header, response.Body) 229 | } 230 | // This endpoint doesn't have any custom error 231 | // types, so we just read the body as-is, and 232 | // put it into a normal error. 233 | bytes, err := io.ReadAll(response.Body) 234 | if err != nil && err != io.EOF { 235 | return err 236 | } 237 | if err == io.EOF { 238 | // The error didn't have a response body, 239 | // so all we can do is return an error 240 | // with the status code. 241 | return core.NewAPIError(response.StatusCode, response.Header, nil) 242 | } 243 | return core.NewAPIError(response.StatusCode, response.Header, errors.New(string(bytes))) 244 | } 245 | 246 | // isNil is used to determine if the request value is equal to nil (i.e. an interface 247 | // value that holds a nil concrete value is itself non-nil). 248 | func isNil(value interface{}) bool { 249 | return value == nil || reflect.ValueOf(value).IsNil() 250 | } 251 | -------------------------------------------------------------------------------- /internal/extra_properties_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | type testMarshaler struct { 13 | Name string `json:"name"` 14 | BirthDate time.Time `json:"birthDate"` 15 | CreatedAt time.Time `json:"created_at"` 16 | } 17 | 18 | func (t *testMarshaler) MarshalJSON() ([]byte, error) { 19 | type embed testMarshaler 20 | var marshaler = struct { 21 | embed 22 | BirthDate string `json:"birthDate"` 23 | CreatedAt string `json:"created_at"` 24 | }{ 25 | embed: embed(*t), 26 | BirthDate: t.BirthDate.Format("2006-01-02"), 27 | CreatedAt: t.CreatedAt.Format(time.RFC3339), 28 | } 29 | return MarshalJSONWithExtraProperty(marshaler, "type", "test") 30 | } 31 | 32 | func TestMarshalJSONWithExtraProperties(t *testing.T) { 33 | tests := []struct { 34 | desc string 35 | giveMarshaler interface{} 36 | giveExtraProperties map[string]interface{} 37 | wantBytes []byte 38 | wantError string 39 | }{ 40 | { 41 | desc: "invalid type", 42 | giveMarshaler: []string{"invalid"}, 43 | giveExtraProperties: map[string]interface{}{"key": "overwrite"}, 44 | wantError: `cannot extract keys from []string; only structs and maps with string keys are supported`, 45 | }, 46 | { 47 | desc: "invalid key type", 48 | giveMarshaler: map[int]interface{}{42: "value"}, 49 | giveExtraProperties: map[string]interface{}{"key": "overwrite"}, 50 | wantError: `cannot extract keys from map[int]interface {}; only structs and maps with string keys are supported`, 51 | }, 52 | { 53 | desc: "invalid map overwrite", 54 | giveMarshaler: map[string]interface{}{"key": "value"}, 55 | giveExtraProperties: map[string]interface{}{"key": "overwrite"}, 56 | wantError: `cannot add extra property "key" because it is already defined on the type`, 57 | }, 58 | { 59 | desc: "invalid struct overwrite", 60 | giveMarshaler: new(testMarshaler), 61 | giveExtraProperties: map[string]interface{}{"birthDate": "2000-01-01"}, 62 | wantError: `cannot add extra property "birthDate" because it is already defined on the type`, 63 | }, 64 | { 65 | desc: "invalid struct overwrite embedded type", 66 | giveMarshaler: new(testMarshaler), 67 | giveExtraProperties: map[string]interface{}{"name": "bob"}, 68 | wantError: `cannot add extra property "name" because it is already defined on the type`, 69 | }, 70 | { 71 | desc: "nil", 72 | giveMarshaler: nil, 73 | giveExtraProperties: nil, 74 | wantBytes: []byte(`null`), 75 | }, 76 | { 77 | desc: "empty", 78 | giveMarshaler: map[string]interface{}{}, 79 | giveExtraProperties: map[string]interface{}{}, 80 | wantBytes: []byte(`{}`), 81 | }, 82 | { 83 | desc: "no extra properties", 84 | giveMarshaler: map[string]interface{}{"key": "value"}, 85 | giveExtraProperties: map[string]interface{}{}, 86 | wantBytes: []byte(`{"key":"value"}`), 87 | }, 88 | { 89 | desc: "only extra properties", 90 | giveMarshaler: map[string]interface{}{}, 91 | giveExtraProperties: map[string]interface{}{"key": "value"}, 92 | wantBytes: []byte(`{"key":"value"}`), 93 | }, 94 | { 95 | desc: "single extra property", 96 | giveMarshaler: map[string]interface{}{"key": "value"}, 97 | giveExtraProperties: map[string]interface{}{"extra": "property"}, 98 | wantBytes: []byte(`{"key":"value","extra":"property"}`), 99 | }, 100 | { 101 | desc: "multiple extra properties", 102 | giveMarshaler: map[string]interface{}{"key": "value"}, 103 | giveExtraProperties: map[string]interface{}{"one": 1, "two": 2}, 104 | wantBytes: []byte(`{"key":"value","one":1,"two":2}`), 105 | }, 106 | { 107 | desc: "nested properties", 108 | giveMarshaler: map[string]interface{}{"key": "value"}, 109 | giveExtraProperties: map[string]interface{}{ 110 | "user": map[string]interface{}{ 111 | "age": 42, 112 | "name": "alice", 113 | }, 114 | }, 115 | wantBytes: []byte(`{"key":"value","user":{"age":42,"name":"alice"}}`), 116 | }, 117 | { 118 | desc: "multiple nested properties", 119 | giveMarshaler: map[string]interface{}{"key": "value"}, 120 | giveExtraProperties: map[string]interface{}{ 121 | "metadata": map[string]interface{}{ 122 | "ip": "127.0.0.1", 123 | }, 124 | "user": map[string]interface{}{ 125 | "age": 42, 126 | "name": "alice", 127 | }, 128 | }, 129 | wantBytes: []byte(`{"key":"value","metadata":{"ip":"127.0.0.1"},"user":{"age":42,"name":"alice"}}`), 130 | }, 131 | { 132 | desc: "custom marshaler", 133 | giveMarshaler: &testMarshaler{ 134 | Name: "alice", 135 | BirthDate: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), 136 | CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), 137 | }, 138 | giveExtraProperties: map[string]interface{}{ 139 | "extra": "property", 140 | }, 141 | wantBytes: []byte(`{"name":"alice","birthDate":"2000-01-01","created_at":"2024-01-01T00:00:00Z","type":"test","extra":"property"}`), 142 | }, 143 | } 144 | for _, tt := range tests { 145 | t.Run(tt.desc, func(t *testing.T) { 146 | bytes, err := MarshalJSONWithExtraProperties(tt.giveMarshaler, tt.giveExtraProperties) 147 | if tt.wantError != "" { 148 | require.EqualError(t, err, tt.wantError) 149 | assert.Nil(t, tt.wantBytes) 150 | return 151 | } 152 | require.NoError(t, err) 153 | assert.Equal(t, tt.wantBytes, bytes) 154 | 155 | value := make(map[string]interface{}) 156 | require.NoError(t, json.Unmarshal(bytes, &value)) 157 | }) 158 | } 159 | } 160 | 161 | func TestExtractExtraProperties(t *testing.T) { 162 | t.Run("none", func(t *testing.T) { 163 | type user struct { 164 | Name string `json:"name"` 165 | } 166 | value := &user{ 167 | Name: "alice", 168 | } 169 | extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice"}`), value) 170 | require.NoError(t, err) 171 | assert.Nil(t, extraProperties) 172 | }) 173 | 174 | t.Run("non-nil pointer", func(t *testing.T) { 175 | type user struct { 176 | Name string `json:"name"` 177 | } 178 | value := &user{ 179 | Name: "alice", 180 | } 181 | extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) 182 | require.NoError(t, err) 183 | assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) 184 | }) 185 | 186 | t.Run("nil pointer", func(t *testing.T) { 187 | type user struct { 188 | Name string `json:"name"` 189 | } 190 | var value *user 191 | _, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) 192 | assert.EqualError(t, err, "value must be non-nil to extract extra properties") 193 | }) 194 | 195 | t.Run("non-zero value", func(t *testing.T) { 196 | type user struct { 197 | Name string `json:"name"` 198 | } 199 | value := user{ 200 | Name: "alice", 201 | } 202 | extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) 203 | require.NoError(t, err) 204 | assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) 205 | }) 206 | 207 | t.Run("zero value", func(t *testing.T) { 208 | type user struct { 209 | Name string `json:"name"` 210 | } 211 | var value user 212 | extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value) 213 | require.NoError(t, err) 214 | assert.Equal(t, map[string]interface{}{"age": float64(42)}, extraProperties) 215 | }) 216 | 217 | t.Run("exclude", func(t *testing.T) { 218 | type user struct { 219 | Name string `json:"name"` 220 | } 221 | value := &user{ 222 | Name: "alice", 223 | } 224 | extraProperties, err := ExtractExtraProperties([]byte(`{"name": "alice", "age": 42}`), value, "age") 225 | require.NoError(t, err) 226 | assert.Nil(t, extraProperties) 227 | }) 228 | } 229 | -------------------------------------------------------------------------------- /internal/query.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "net/url" 7 | "reflect" 8 | "strings" 9 | "time" 10 | 11 | "github.com/google/uuid" 12 | ) 13 | 14 | var ( 15 | bytesType = reflect.TypeOf([]byte{}) 16 | queryEncoderType = reflect.TypeOf(new(QueryEncoder)).Elem() 17 | timeType = reflect.TypeOf(time.Time{}) 18 | uuidType = reflect.TypeOf(uuid.UUID{}) 19 | ) 20 | 21 | // QueryEncoder is an interface implemented by any type that wishes to encode 22 | // itself into URL values in a non-standard way. 23 | type QueryEncoder interface { 24 | EncodeQueryValues(key string, v *url.Values) error 25 | } 26 | 27 | // QueryValues encodes url.Values from request objects. 28 | // 29 | // Note: This type is inspired by Google's query encoding library, but 30 | // supports far less customization and is tailored to fit this SDK's use case. 31 | // 32 | // Ref: https://github.com/google/go-querystring 33 | func QueryValues(v interface{}) (url.Values, error) { 34 | values := make(url.Values) 35 | val := reflect.ValueOf(v) 36 | for val.Kind() == reflect.Ptr { 37 | if val.IsNil() { 38 | return values, nil 39 | } 40 | val = val.Elem() 41 | } 42 | 43 | if v == nil { 44 | return values, nil 45 | } 46 | 47 | if val.Kind() != reflect.Struct { 48 | return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) 49 | } 50 | 51 | err := reflectValue(values, val, "") 52 | return values, err 53 | } 54 | 55 | // reflectValue populates the values parameter from the struct fields in val. 56 | // Embedded structs are followed recursively (using the rules defined in the 57 | // Values function documentation) breadth-first. 58 | func reflectValue(values url.Values, val reflect.Value, scope string) error { 59 | typ := val.Type() 60 | for i := 0; i < typ.NumField(); i++ { 61 | sf := typ.Field(i) 62 | if sf.PkgPath != "" && !sf.Anonymous { 63 | // Skip unexported fields. 64 | continue 65 | } 66 | 67 | sv := val.Field(i) 68 | tag := sf.Tag.Get("url") 69 | if tag == "" || tag == "-" { 70 | continue 71 | } 72 | 73 | name, opts := parseTag(tag) 74 | if name == "" { 75 | name = sf.Name 76 | } 77 | 78 | if scope != "" { 79 | name = scope + "[" + name + "]" 80 | } 81 | 82 | if opts.Contains("omitempty") && isEmptyValue(sv) { 83 | continue 84 | } 85 | 86 | if sv.Type().Implements(queryEncoderType) { 87 | // If sv is a nil pointer and the custom encoder is defined on a non-pointer 88 | // method receiver, set sv to the zero value of the underlying type 89 | if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(queryEncoderType) { 90 | sv = reflect.New(sv.Type().Elem()) 91 | } 92 | 93 | m := sv.Interface().(QueryEncoder) 94 | if err := m.EncodeQueryValues(name, &values); err != nil { 95 | return err 96 | } 97 | continue 98 | } 99 | 100 | // Recursively dereference pointers, but stop at nil pointers. 101 | for sv.Kind() == reflect.Ptr { 102 | if sv.IsNil() { 103 | break 104 | } 105 | sv = sv.Elem() 106 | } 107 | 108 | if sv.Type() == uuidType || sv.Type() == bytesType || sv.Type() == timeType { 109 | values.Add(name, valueString(sv, opts, sf)) 110 | continue 111 | } 112 | 113 | if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { 114 | if sv.Len() == 0 { 115 | // Skip if slice or array is empty. 116 | continue 117 | } 118 | for i := 0; i < sv.Len(); i++ { 119 | value := sv.Index(i) 120 | if isStructPointer(value) && !value.IsNil() { 121 | if err := reflectValue(values, value.Elem(), name); err != nil { 122 | return err 123 | } 124 | } else { 125 | values.Add(name, valueString(value, opts, sf)) 126 | } 127 | } 128 | continue 129 | } 130 | 131 | if sv.Kind() == reflect.Map { 132 | if err := reflectMap(values, sv, name); err != nil { 133 | return err 134 | } 135 | continue 136 | } 137 | 138 | if sv.Kind() == reflect.Struct { 139 | if err := reflectValue(values, sv, name); err != nil { 140 | return err 141 | } 142 | continue 143 | } 144 | 145 | values.Add(name, valueString(sv, opts, sf)) 146 | } 147 | 148 | return nil 149 | } 150 | 151 | // reflectMap handles map types specifically, generating query parameters in the format key[mapkey]=value 152 | func reflectMap(values url.Values, val reflect.Value, scope string) error { 153 | if val.IsNil() { 154 | return nil 155 | } 156 | 157 | iter := val.MapRange() 158 | for iter.Next() { 159 | k := iter.Key() 160 | v := iter.Value() 161 | 162 | key := fmt.Sprint(k.Interface()) 163 | paramName := scope + "[" + key + "]" 164 | 165 | for v.Kind() == reflect.Ptr { 166 | if v.IsNil() { 167 | break 168 | } 169 | v = v.Elem() 170 | } 171 | 172 | for v.Kind() == reflect.Interface { 173 | v = v.Elem() 174 | } 175 | 176 | if v.Kind() == reflect.Map { 177 | if err := reflectMap(values, v, paramName); err != nil { 178 | return err 179 | } 180 | continue 181 | } 182 | 183 | if v.Kind() == reflect.Struct { 184 | if err := reflectValue(values, v, paramName); err != nil { 185 | return err 186 | } 187 | continue 188 | } 189 | 190 | if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { 191 | if v.Len() == 0 { 192 | continue 193 | } 194 | for i := 0; i < v.Len(); i++ { 195 | value := v.Index(i) 196 | if isStructPointer(value) && !value.IsNil() { 197 | if err := reflectValue(values, value.Elem(), paramName); err != nil { 198 | return err 199 | } 200 | } else { 201 | values.Add(paramName, valueString(value, tagOptions{}, reflect.StructField{})) 202 | } 203 | } 204 | continue 205 | } 206 | 207 | values.Add(paramName, valueString(v, tagOptions{}, reflect.StructField{})) 208 | } 209 | 210 | return nil 211 | } 212 | 213 | // valueString returns the string representation of a value. 214 | func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { 215 | for v.Kind() == reflect.Ptr { 216 | if v.IsNil() { 217 | return "" 218 | } 219 | v = v.Elem() 220 | } 221 | 222 | if v.Type() == timeType { 223 | t := v.Interface().(time.Time) 224 | if format := sf.Tag.Get("format"); format == "date" { 225 | return t.Format("2006-01-02") 226 | } 227 | return t.Format(time.RFC3339) 228 | } 229 | 230 | if v.Type() == uuidType { 231 | u := v.Interface().(uuid.UUID) 232 | return u.String() 233 | } 234 | 235 | if v.Type() == bytesType { 236 | b := v.Interface().([]byte) 237 | return base64.StdEncoding.EncodeToString(b) 238 | } 239 | 240 | return fmt.Sprint(v.Interface()) 241 | } 242 | 243 | // isEmptyValue checks if a value should be considered empty for the purposes 244 | // of omitting fields with the "omitempty" option. 245 | func isEmptyValue(v reflect.Value) bool { 246 | type zeroable interface { 247 | IsZero() bool 248 | } 249 | 250 | if !v.IsZero() { 251 | if z, ok := v.Interface().(zeroable); ok { 252 | return z.IsZero() 253 | } 254 | } 255 | 256 | switch v.Kind() { 257 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 258 | return v.Len() == 0 259 | case reflect.Bool: 260 | return !v.Bool() 261 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 262 | return v.Int() == 0 263 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 264 | return v.Uint() == 0 265 | case reflect.Float32, reflect.Float64: 266 | return v.Float() == 0 267 | case reflect.Interface, reflect.Ptr: 268 | return v.IsNil() 269 | case reflect.Invalid, reflect.Complex64, reflect.Complex128, reflect.Chan, reflect.Func, reflect.Struct, reflect.UnsafePointer: 270 | return false 271 | } 272 | 273 | return false 274 | } 275 | 276 | // isStructPointer returns true if the given reflect.Value is a pointer to a struct. 277 | func isStructPointer(v reflect.Value) bool { 278 | return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct 279 | } 280 | 281 | // tagOptions is the string following a comma in a struct field's "url" tag, or 282 | // the empty string. It does not include the leading comma. 283 | type tagOptions []string 284 | 285 | // parseTag splits a struct field's url tag into its name and comma-separated 286 | // options. 287 | func parseTag(tag string) (string, tagOptions) { 288 | s := strings.Split(tag, ",") 289 | return s[0], s[1:] 290 | } 291 | 292 | // Contains checks whether the tagOptions contains the specified option. 293 | func (o tagOptions) Contains(option string) bool { 294 | for _, s := range o { 295 | if s == option { 296 | return true 297 | } 298 | } 299 | return false 300 | } 301 | -------------------------------------------------------------------------------- /graph/node/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package node 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | ) 13 | 14 | type RawClient struct { 15 | baseURL string 16 | caller *internal.Caller 17 | header http.Header 18 | } 19 | 20 | func NewRawClient(options *core.RequestOptions) *RawClient { 21 | return &RawClient{ 22 | baseURL: options.BaseURL, 23 | caller: internal.NewCaller( 24 | &internal.CallerParams{ 25 | Client: options.HTTPClient, 26 | MaxAttempts: options.MaxAttempts, 27 | }, 28 | ), 29 | header: options.ToHeader(), 30 | } 31 | } 32 | 33 | func (r *RawClient) GetByGraphID( 34 | ctx context.Context, 35 | // Graph ID 36 | graphID string, 37 | request *v3.GraphNodesRequest, 38 | opts ...option.RequestOption, 39 | ) (*core.Response[[]*v3.EntityNode], error) { 40 | options := core.NewRequestOptions(opts...) 41 | baseURL := internal.ResolveBaseURL( 42 | options.BaseURL, 43 | r.baseURL, 44 | "https://api.getzep.com/api/v2", 45 | ) 46 | endpointURL := internal.EncodeURL( 47 | baseURL+"/graph/node/graph/%v", 48 | graphID, 49 | ) 50 | headers := internal.MergeHeaders( 51 | r.header.Clone(), 52 | options.ToHeader(), 53 | ) 54 | errorCodes := internal.ErrorCodes{ 55 | 400: func(apiError *core.APIError) error { 56 | return &v3.BadRequestError{ 57 | APIError: apiError, 58 | } 59 | }, 60 | 500: func(apiError *core.APIError) error { 61 | return &v3.InternalServerError{ 62 | APIError: apiError, 63 | } 64 | }, 65 | } 66 | var response []*v3.EntityNode 67 | raw, err := r.caller.Call( 68 | ctx, 69 | &internal.CallParams{ 70 | URL: endpointURL, 71 | Method: http.MethodPost, 72 | Headers: headers, 73 | MaxAttempts: options.MaxAttempts, 74 | BodyProperties: options.BodyProperties, 75 | QueryParameters: options.QueryParameters, 76 | Client: options.HTTPClient, 77 | Request: request, 78 | Response: &response, 79 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 80 | }, 81 | ) 82 | if err != nil { 83 | return nil, err 84 | } 85 | return &core.Response[[]*v3.EntityNode]{ 86 | StatusCode: raw.StatusCode, 87 | Header: raw.Header, 88 | Body: response, 89 | }, nil 90 | } 91 | 92 | func (r *RawClient) GetByUserID( 93 | ctx context.Context, 94 | // User ID 95 | userID string, 96 | request *v3.GraphNodesRequest, 97 | opts ...option.RequestOption, 98 | ) (*core.Response[[]*v3.EntityNode], error) { 99 | options := core.NewRequestOptions(opts...) 100 | baseURL := internal.ResolveBaseURL( 101 | options.BaseURL, 102 | r.baseURL, 103 | "https://api.getzep.com/api/v2", 104 | ) 105 | endpointURL := internal.EncodeURL( 106 | baseURL+"/graph/node/user/%v", 107 | userID, 108 | ) 109 | headers := internal.MergeHeaders( 110 | r.header.Clone(), 111 | options.ToHeader(), 112 | ) 113 | errorCodes := internal.ErrorCodes{ 114 | 400: func(apiError *core.APIError) error { 115 | return &v3.BadRequestError{ 116 | APIError: apiError, 117 | } 118 | }, 119 | 500: func(apiError *core.APIError) error { 120 | return &v3.InternalServerError{ 121 | APIError: apiError, 122 | } 123 | }, 124 | } 125 | var response []*v3.EntityNode 126 | raw, err := r.caller.Call( 127 | ctx, 128 | &internal.CallParams{ 129 | URL: endpointURL, 130 | Method: http.MethodPost, 131 | Headers: headers, 132 | MaxAttempts: options.MaxAttempts, 133 | BodyProperties: options.BodyProperties, 134 | QueryParameters: options.QueryParameters, 135 | Client: options.HTTPClient, 136 | Request: request, 137 | Response: &response, 138 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 139 | }, 140 | ) 141 | if err != nil { 142 | return nil, err 143 | } 144 | return &core.Response[[]*v3.EntityNode]{ 145 | StatusCode: raw.StatusCode, 146 | Header: raw.Header, 147 | Body: response, 148 | }, nil 149 | } 150 | 151 | func (r *RawClient) GetEdges( 152 | ctx context.Context, 153 | // Node UUID 154 | nodeUUID string, 155 | opts ...option.RequestOption, 156 | ) (*core.Response[[]*v3.EntityEdge], error) { 157 | options := core.NewRequestOptions(opts...) 158 | baseURL := internal.ResolveBaseURL( 159 | options.BaseURL, 160 | r.baseURL, 161 | "https://api.getzep.com/api/v2", 162 | ) 163 | endpointURL := internal.EncodeURL( 164 | baseURL+"/graph/node/%v/entity-edges", 165 | nodeUUID, 166 | ) 167 | headers := internal.MergeHeaders( 168 | r.header.Clone(), 169 | options.ToHeader(), 170 | ) 171 | errorCodes := internal.ErrorCodes{ 172 | 400: func(apiError *core.APIError) error { 173 | return &v3.BadRequestError{ 174 | APIError: apiError, 175 | } 176 | }, 177 | 500: func(apiError *core.APIError) error { 178 | return &v3.InternalServerError{ 179 | APIError: apiError, 180 | } 181 | }, 182 | } 183 | var response []*v3.EntityEdge 184 | raw, err := r.caller.Call( 185 | ctx, 186 | &internal.CallParams{ 187 | URL: endpointURL, 188 | Method: http.MethodGet, 189 | Headers: headers, 190 | MaxAttempts: options.MaxAttempts, 191 | BodyProperties: options.BodyProperties, 192 | QueryParameters: options.QueryParameters, 193 | Client: options.HTTPClient, 194 | Response: &response, 195 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 196 | }, 197 | ) 198 | if err != nil { 199 | return nil, err 200 | } 201 | return &core.Response[[]*v3.EntityEdge]{ 202 | StatusCode: raw.StatusCode, 203 | Header: raw.Header, 204 | Body: response, 205 | }, nil 206 | } 207 | 208 | func (r *RawClient) GetEpisodes( 209 | ctx context.Context, 210 | // Node UUID 211 | nodeUUID string, 212 | opts ...option.RequestOption, 213 | ) (*core.Response[*v3.EpisodeResponse], error) { 214 | options := core.NewRequestOptions(opts...) 215 | baseURL := internal.ResolveBaseURL( 216 | options.BaseURL, 217 | r.baseURL, 218 | "https://api.getzep.com/api/v2", 219 | ) 220 | endpointURL := internal.EncodeURL( 221 | baseURL+"/graph/node/%v/episodes", 222 | nodeUUID, 223 | ) 224 | headers := internal.MergeHeaders( 225 | r.header.Clone(), 226 | options.ToHeader(), 227 | ) 228 | errorCodes := internal.ErrorCodes{ 229 | 400: func(apiError *core.APIError) error { 230 | return &v3.BadRequestError{ 231 | APIError: apiError, 232 | } 233 | }, 234 | 500: func(apiError *core.APIError) error { 235 | return &v3.InternalServerError{ 236 | APIError: apiError, 237 | } 238 | }, 239 | } 240 | var response *v3.EpisodeResponse 241 | raw, err := r.caller.Call( 242 | ctx, 243 | &internal.CallParams{ 244 | URL: endpointURL, 245 | Method: http.MethodGet, 246 | Headers: headers, 247 | MaxAttempts: options.MaxAttempts, 248 | BodyProperties: options.BodyProperties, 249 | QueryParameters: options.QueryParameters, 250 | Client: options.HTTPClient, 251 | Response: &response, 252 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 253 | }, 254 | ) 255 | if err != nil { 256 | return nil, err 257 | } 258 | return &core.Response[*v3.EpisodeResponse]{ 259 | StatusCode: raw.StatusCode, 260 | Header: raw.Header, 261 | Body: response, 262 | }, nil 263 | } 264 | 265 | func (r *RawClient) Get( 266 | ctx context.Context, 267 | // Node UUID 268 | uuid string, 269 | opts ...option.RequestOption, 270 | ) (*core.Response[*v3.EntityNode], error) { 271 | options := core.NewRequestOptions(opts...) 272 | baseURL := internal.ResolveBaseURL( 273 | options.BaseURL, 274 | r.baseURL, 275 | "https://api.getzep.com/api/v2", 276 | ) 277 | endpointURL := internal.EncodeURL( 278 | baseURL+"/graph/node/%v", 279 | uuid, 280 | ) 281 | headers := internal.MergeHeaders( 282 | r.header.Clone(), 283 | options.ToHeader(), 284 | ) 285 | errorCodes := internal.ErrorCodes{ 286 | 400: func(apiError *core.APIError) error { 287 | return &v3.BadRequestError{ 288 | APIError: apiError, 289 | } 290 | }, 291 | 404: func(apiError *core.APIError) error { 292 | return &v3.NotFoundError{ 293 | APIError: apiError, 294 | } 295 | }, 296 | 500: func(apiError *core.APIError) error { 297 | return &v3.InternalServerError{ 298 | APIError: apiError, 299 | } 300 | }, 301 | } 302 | var response *v3.EntityNode 303 | raw, err := r.caller.Call( 304 | ctx, 305 | &internal.CallParams{ 306 | URL: endpointURL, 307 | Method: http.MethodGet, 308 | Headers: headers, 309 | MaxAttempts: options.MaxAttempts, 310 | BodyProperties: options.BodyProperties, 311 | QueryParameters: options.QueryParameters, 312 | Client: options.HTTPClient, 313 | Response: &response, 314 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 315 | }, 316 | ) 317 | if err != nil { 318 | return nil, err 319 | } 320 | return &core.Response[*v3.EntityNode]{ 321 | StatusCode: raw.StatusCode, 322 | Header: raw.Header, 323 | Body: response, 324 | }, nil 325 | } 326 | -------------------------------------------------------------------------------- /graph/episode/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package episode 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | graph "github.com/getzep/zep-go/v3/graph" 10 | internal "github.com/getzep/zep-go/v3/internal" 11 | option "github.com/getzep/zep-go/v3/option" 12 | http "net/http" 13 | ) 14 | 15 | type RawClient struct { 16 | baseURL string 17 | caller *internal.Caller 18 | header http.Header 19 | } 20 | 21 | func NewRawClient(options *core.RequestOptions) *RawClient { 22 | return &RawClient{ 23 | baseURL: options.BaseURL, 24 | caller: internal.NewCaller( 25 | &internal.CallerParams{ 26 | Client: options.HTTPClient, 27 | MaxAttempts: options.MaxAttempts, 28 | }, 29 | ), 30 | header: options.ToHeader(), 31 | } 32 | } 33 | 34 | func (r *RawClient) GetByGraphID( 35 | ctx context.Context, 36 | // Graph ID 37 | graphID string, 38 | request *graph.EpisodeGetByGraphIDRequest, 39 | opts ...option.RequestOption, 40 | ) (*core.Response[*v3.EpisodeResponse], error) { 41 | options := core.NewRequestOptions(opts...) 42 | baseURL := internal.ResolveBaseURL( 43 | options.BaseURL, 44 | r.baseURL, 45 | "https://api.getzep.com/api/v2", 46 | ) 47 | endpointURL := internal.EncodeURL( 48 | baseURL+"/graph/episodes/graph/%v", 49 | graphID, 50 | ) 51 | queryParams, err := internal.QueryValues(request) 52 | if err != nil { 53 | return nil, err 54 | } 55 | if len(queryParams) > 0 { 56 | endpointURL += "?" + queryParams.Encode() 57 | } 58 | headers := internal.MergeHeaders( 59 | r.header.Clone(), 60 | options.ToHeader(), 61 | ) 62 | errorCodes := internal.ErrorCodes{ 63 | 400: func(apiError *core.APIError) error { 64 | return &v3.BadRequestError{ 65 | APIError: apiError, 66 | } 67 | }, 68 | 500: func(apiError *core.APIError) error { 69 | return &v3.InternalServerError{ 70 | APIError: apiError, 71 | } 72 | }, 73 | } 74 | var response *v3.EpisodeResponse 75 | raw, err := r.caller.Call( 76 | ctx, 77 | &internal.CallParams{ 78 | URL: endpointURL, 79 | Method: http.MethodGet, 80 | Headers: headers, 81 | MaxAttempts: options.MaxAttempts, 82 | BodyProperties: options.BodyProperties, 83 | QueryParameters: options.QueryParameters, 84 | Client: options.HTTPClient, 85 | Response: &response, 86 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 87 | }, 88 | ) 89 | if err != nil { 90 | return nil, err 91 | } 92 | return &core.Response[*v3.EpisodeResponse]{ 93 | StatusCode: raw.StatusCode, 94 | Header: raw.Header, 95 | Body: response, 96 | }, nil 97 | } 98 | 99 | func (r *RawClient) GetByUserID( 100 | ctx context.Context, 101 | // User ID 102 | userID string, 103 | request *graph.EpisodeGetByUserIDRequest, 104 | opts ...option.RequestOption, 105 | ) (*core.Response[*v3.EpisodeResponse], error) { 106 | options := core.NewRequestOptions(opts...) 107 | baseURL := internal.ResolveBaseURL( 108 | options.BaseURL, 109 | r.baseURL, 110 | "https://api.getzep.com/api/v2", 111 | ) 112 | endpointURL := internal.EncodeURL( 113 | baseURL+"/graph/episodes/user/%v", 114 | userID, 115 | ) 116 | queryParams, err := internal.QueryValues(request) 117 | if err != nil { 118 | return nil, err 119 | } 120 | if len(queryParams) > 0 { 121 | endpointURL += "?" + queryParams.Encode() 122 | } 123 | headers := internal.MergeHeaders( 124 | r.header.Clone(), 125 | options.ToHeader(), 126 | ) 127 | errorCodes := internal.ErrorCodes{ 128 | 400: func(apiError *core.APIError) error { 129 | return &v3.BadRequestError{ 130 | APIError: apiError, 131 | } 132 | }, 133 | 500: func(apiError *core.APIError) error { 134 | return &v3.InternalServerError{ 135 | APIError: apiError, 136 | } 137 | }, 138 | } 139 | var response *v3.EpisodeResponse 140 | raw, err := r.caller.Call( 141 | ctx, 142 | &internal.CallParams{ 143 | URL: endpointURL, 144 | Method: http.MethodGet, 145 | Headers: headers, 146 | MaxAttempts: options.MaxAttempts, 147 | BodyProperties: options.BodyProperties, 148 | QueryParameters: options.QueryParameters, 149 | Client: options.HTTPClient, 150 | Response: &response, 151 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 152 | }, 153 | ) 154 | if err != nil { 155 | return nil, err 156 | } 157 | return &core.Response[*v3.EpisodeResponse]{ 158 | StatusCode: raw.StatusCode, 159 | Header: raw.Header, 160 | Body: response, 161 | }, nil 162 | } 163 | 164 | func (r *RawClient) Get( 165 | ctx context.Context, 166 | // Episode UUID 167 | uuid string, 168 | opts ...option.RequestOption, 169 | ) (*core.Response[*v3.Episode], error) { 170 | options := core.NewRequestOptions(opts...) 171 | baseURL := internal.ResolveBaseURL( 172 | options.BaseURL, 173 | r.baseURL, 174 | "https://api.getzep.com/api/v2", 175 | ) 176 | endpointURL := internal.EncodeURL( 177 | baseURL+"/graph/episodes/%v", 178 | uuid, 179 | ) 180 | headers := internal.MergeHeaders( 181 | r.header.Clone(), 182 | options.ToHeader(), 183 | ) 184 | errorCodes := internal.ErrorCodes{ 185 | 400: func(apiError *core.APIError) error { 186 | return &v3.BadRequestError{ 187 | APIError: apiError, 188 | } 189 | }, 190 | 500: func(apiError *core.APIError) error { 191 | return &v3.InternalServerError{ 192 | APIError: apiError, 193 | } 194 | }, 195 | } 196 | var response *v3.Episode 197 | raw, err := r.caller.Call( 198 | ctx, 199 | &internal.CallParams{ 200 | URL: endpointURL, 201 | Method: http.MethodGet, 202 | Headers: headers, 203 | MaxAttempts: options.MaxAttempts, 204 | BodyProperties: options.BodyProperties, 205 | QueryParameters: options.QueryParameters, 206 | Client: options.HTTPClient, 207 | Response: &response, 208 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 209 | }, 210 | ) 211 | if err != nil { 212 | return nil, err 213 | } 214 | return &core.Response[*v3.Episode]{ 215 | StatusCode: raw.StatusCode, 216 | Header: raw.Header, 217 | Body: response, 218 | }, nil 219 | } 220 | 221 | func (r *RawClient) Delete( 222 | ctx context.Context, 223 | // Episode UUID 224 | uuid string, 225 | opts ...option.RequestOption, 226 | ) (*core.Response[*v3.SuccessResponse], error) { 227 | options := core.NewRequestOptions(opts...) 228 | baseURL := internal.ResolveBaseURL( 229 | options.BaseURL, 230 | r.baseURL, 231 | "https://api.getzep.com/api/v2", 232 | ) 233 | endpointURL := internal.EncodeURL( 234 | baseURL+"/graph/episodes/%v", 235 | uuid, 236 | ) 237 | headers := internal.MergeHeaders( 238 | r.header.Clone(), 239 | options.ToHeader(), 240 | ) 241 | errorCodes := internal.ErrorCodes{ 242 | 400: func(apiError *core.APIError) error { 243 | return &v3.BadRequestError{ 244 | APIError: apiError, 245 | } 246 | }, 247 | 404: func(apiError *core.APIError) error { 248 | return &v3.NotFoundError{ 249 | APIError: apiError, 250 | } 251 | }, 252 | 500: func(apiError *core.APIError) error { 253 | return &v3.InternalServerError{ 254 | APIError: apiError, 255 | } 256 | }, 257 | } 258 | var response *v3.SuccessResponse 259 | raw, err := r.caller.Call( 260 | ctx, 261 | &internal.CallParams{ 262 | URL: endpointURL, 263 | Method: http.MethodDelete, 264 | Headers: headers, 265 | MaxAttempts: options.MaxAttempts, 266 | BodyProperties: options.BodyProperties, 267 | QueryParameters: options.QueryParameters, 268 | Client: options.HTTPClient, 269 | Response: &response, 270 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 271 | }, 272 | ) 273 | if err != nil { 274 | return nil, err 275 | } 276 | return &core.Response[*v3.SuccessResponse]{ 277 | StatusCode: raw.StatusCode, 278 | Header: raw.Header, 279 | Body: response, 280 | }, nil 281 | } 282 | 283 | func (r *RawClient) GetNodesAndEdges( 284 | ctx context.Context, 285 | // Episode uuid 286 | uuid string, 287 | opts ...option.RequestOption, 288 | ) (*core.Response[*v3.EpisodeMentions], error) { 289 | options := core.NewRequestOptions(opts...) 290 | baseURL := internal.ResolveBaseURL( 291 | options.BaseURL, 292 | r.baseURL, 293 | "https://api.getzep.com/api/v2", 294 | ) 295 | endpointURL := internal.EncodeURL( 296 | baseURL+"/graph/episodes/%v/mentions", 297 | uuid, 298 | ) 299 | headers := internal.MergeHeaders( 300 | r.header.Clone(), 301 | options.ToHeader(), 302 | ) 303 | errorCodes := internal.ErrorCodes{ 304 | 400: func(apiError *core.APIError) error { 305 | return &v3.BadRequestError{ 306 | APIError: apiError, 307 | } 308 | }, 309 | 500: func(apiError *core.APIError) error { 310 | return &v3.InternalServerError{ 311 | APIError: apiError, 312 | } 313 | }, 314 | } 315 | var response *v3.EpisodeMentions 316 | raw, err := r.caller.Call( 317 | ctx, 318 | &internal.CallParams{ 319 | URL: endpointURL, 320 | Method: http.MethodGet, 321 | Headers: headers, 322 | MaxAttempts: options.MaxAttempts, 323 | BodyProperties: options.BodyProperties, 324 | QueryParameters: options.QueryParameters, 325 | Client: options.HTTPClient, 326 | Response: &response, 327 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 328 | }, 329 | ) 330 | if err != nil { 331 | return nil, err 332 | } 333 | return &core.Response[*v3.EpisodeMentions]{ 334 | StatusCode: raw.StatusCode, 335 | Header: raw.Header, 336 | Body: response, 337 | }, nil 338 | } 339 | -------------------------------------------------------------------------------- /context/raw_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package context 4 | 5 | import ( 6 | context "context" 7 | v3 "github.com/getzep/zep-go/v3" 8 | core "github.com/getzep/zep-go/v3/core" 9 | internal "github.com/getzep/zep-go/v3/internal" 10 | option "github.com/getzep/zep-go/v3/option" 11 | http "net/http" 12 | ) 13 | 14 | type RawClient struct { 15 | baseURL string 16 | caller *internal.Caller 17 | header http.Header 18 | } 19 | 20 | func NewRawClient(options *core.RequestOptions) *RawClient { 21 | return &RawClient{ 22 | baseURL: options.BaseURL, 23 | caller: internal.NewCaller( 24 | &internal.CallerParams{ 25 | Client: options.HTTPClient, 26 | MaxAttempts: options.MaxAttempts, 27 | }, 28 | ), 29 | header: options.ToHeader(), 30 | } 31 | } 32 | 33 | func (r *RawClient) ListContextTemplates( 34 | ctx context.Context, 35 | opts ...option.RequestOption, 36 | ) (*core.Response[*v3.ListContextTemplatesResponse], error) { 37 | options := core.NewRequestOptions(opts...) 38 | baseURL := internal.ResolveBaseURL( 39 | options.BaseURL, 40 | r.baseURL, 41 | "https://api.getzep.com/api/v2", 42 | ) 43 | endpointURL := baseURL + "/context-templates" 44 | headers := internal.MergeHeaders( 45 | r.header.Clone(), 46 | options.ToHeader(), 47 | ) 48 | errorCodes := internal.ErrorCodes{ 49 | 400: func(apiError *core.APIError) error { 50 | return &v3.BadRequestError{ 51 | APIError: apiError, 52 | } 53 | }, 54 | 500: func(apiError *core.APIError) error { 55 | return &v3.InternalServerError{ 56 | APIError: apiError, 57 | } 58 | }, 59 | } 60 | var response *v3.ListContextTemplatesResponse 61 | raw, err := r.caller.Call( 62 | ctx, 63 | &internal.CallParams{ 64 | URL: endpointURL, 65 | Method: http.MethodGet, 66 | Headers: headers, 67 | MaxAttempts: options.MaxAttempts, 68 | BodyProperties: options.BodyProperties, 69 | QueryParameters: options.QueryParameters, 70 | Client: options.HTTPClient, 71 | Response: &response, 72 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 73 | }, 74 | ) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return &core.Response[*v3.ListContextTemplatesResponse]{ 79 | StatusCode: raw.StatusCode, 80 | Header: raw.Header, 81 | Body: response, 82 | }, nil 83 | } 84 | 85 | func (r *RawClient) CreateContextTemplate( 86 | ctx context.Context, 87 | request *v3.CreateContextTemplateRequest, 88 | opts ...option.RequestOption, 89 | ) (*core.Response[*v3.ContextTemplateResponse], error) { 90 | options := core.NewRequestOptions(opts...) 91 | baseURL := internal.ResolveBaseURL( 92 | options.BaseURL, 93 | r.baseURL, 94 | "https://api.getzep.com/api/v2", 95 | ) 96 | endpointURL := baseURL + "/context-templates" 97 | headers := internal.MergeHeaders( 98 | r.header.Clone(), 99 | options.ToHeader(), 100 | ) 101 | headers.Add("Content-Type", "application/json") 102 | errorCodes := internal.ErrorCodes{ 103 | 400: func(apiError *core.APIError) error { 104 | return &v3.BadRequestError{ 105 | APIError: apiError, 106 | } 107 | }, 108 | 500: func(apiError *core.APIError) error { 109 | return &v3.InternalServerError{ 110 | APIError: apiError, 111 | } 112 | }, 113 | } 114 | var response *v3.ContextTemplateResponse 115 | raw, err := r.caller.Call( 116 | ctx, 117 | &internal.CallParams{ 118 | URL: endpointURL, 119 | Method: http.MethodPost, 120 | Headers: headers, 121 | MaxAttempts: options.MaxAttempts, 122 | BodyProperties: options.BodyProperties, 123 | QueryParameters: options.QueryParameters, 124 | Client: options.HTTPClient, 125 | Request: request, 126 | Response: &response, 127 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 128 | }, 129 | ) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return &core.Response[*v3.ContextTemplateResponse]{ 134 | StatusCode: raw.StatusCode, 135 | Header: raw.Header, 136 | Body: response, 137 | }, nil 138 | } 139 | 140 | func (r *RawClient) GetContextTemplate( 141 | ctx context.Context, 142 | // Template ID 143 | templateID string, 144 | opts ...option.RequestOption, 145 | ) (*core.Response[*v3.ContextTemplateResponse], error) { 146 | options := core.NewRequestOptions(opts...) 147 | baseURL := internal.ResolveBaseURL( 148 | options.BaseURL, 149 | r.baseURL, 150 | "https://api.getzep.com/api/v2", 151 | ) 152 | endpointURL := internal.EncodeURL( 153 | baseURL+"/context-templates/%v", 154 | templateID, 155 | ) 156 | headers := internal.MergeHeaders( 157 | r.header.Clone(), 158 | options.ToHeader(), 159 | ) 160 | errorCodes := internal.ErrorCodes{ 161 | 400: func(apiError *core.APIError) error { 162 | return &v3.BadRequestError{ 163 | APIError: apiError, 164 | } 165 | }, 166 | 404: func(apiError *core.APIError) error { 167 | return &v3.NotFoundError{ 168 | APIError: apiError, 169 | } 170 | }, 171 | 500: func(apiError *core.APIError) error { 172 | return &v3.InternalServerError{ 173 | APIError: apiError, 174 | } 175 | }, 176 | } 177 | var response *v3.ContextTemplateResponse 178 | raw, err := r.caller.Call( 179 | ctx, 180 | &internal.CallParams{ 181 | URL: endpointURL, 182 | Method: http.MethodGet, 183 | Headers: headers, 184 | MaxAttempts: options.MaxAttempts, 185 | BodyProperties: options.BodyProperties, 186 | QueryParameters: options.QueryParameters, 187 | Client: options.HTTPClient, 188 | Response: &response, 189 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 190 | }, 191 | ) 192 | if err != nil { 193 | return nil, err 194 | } 195 | return &core.Response[*v3.ContextTemplateResponse]{ 196 | StatusCode: raw.StatusCode, 197 | Header: raw.Header, 198 | Body: response, 199 | }, nil 200 | } 201 | 202 | func (r *RawClient) UpdateContextTemplate( 203 | ctx context.Context, 204 | // Template ID 205 | templateID string, 206 | request *v3.UpdateContextTemplateRequest, 207 | opts ...option.RequestOption, 208 | ) (*core.Response[*v3.ContextTemplateResponse], error) { 209 | options := core.NewRequestOptions(opts...) 210 | baseURL := internal.ResolveBaseURL( 211 | options.BaseURL, 212 | r.baseURL, 213 | "https://api.getzep.com/api/v2", 214 | ) 215 | endpointURL := internal.EncodeURL( 216 | baseURL+"/context-templates/%v", 217 | templateID, 218 | ) 219 | headers := internal.MergeHeaders( 220 | r.header.Clone(), 221 | options.ToHeader(), 222 | ) 223 | headers.Add("Content-Type", "application/json") 224 | errorCodes := internal.ErrorCodes{ 225 | 400: func(apiError *core.APIError) error { 226 | return &v3.BadRequestError{ 227 | APIError: apiError, 228 | } 229 | }, 230 | 404: func(apiError *core.APIError) error { 231 | return &v3.NotFoundError{ 232 | APIError: apiError, 233 | } 234 | }, 235 | 500: func(apiError *core.APIError) error { 236 | return &v3.InternalServerError{ 237 | APIError: apiError, 238 | } 239 | }, 240 | } 241 | var response *v3.ContextTemplateResponse 242 | raw, err := r.caller.Call( 243 | ctx, 244 | &internal.CallParams{ 245 | URL: endpointURL, 246 | Method: http.MethodPut, 247 | Headers: headers, 248 | MaxAttempts: options.MaxAttempts, 249 | BodyProperties: options.BodyProperties, 250 | QueryParameters: options.QueryParameters, 251 | Client: options.HTTPClient, 252 | Request: request, 253 | Response: &response, 254 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 255 | }, 256 | ) 257 | if err != nil { 258 | return nil, err 259 | } 260 | return &core.Response[*v3.ContextTemplateResponse]{ 261 | StatusCode: raw.StatusCode, 262 | Header: raw.Header, 263 | Body: response, 264 | }, nil 265 | } 266 | 267 | func (r *RawClient) DeleteContextTemplate( 268 | ctx context.Context, 269 | // Template ID 270 | templateID string, 271 | opts ...option.RequestOption, 272 | ) (*core.Response[*v3.SuccessResponse], error) { 273 | options := core.NewRequestOptions(opts...) 274 | baseURL := internal.ResolveBaseURL( 275 | options.BaseURL, 276 | r.baseURL, 277 | "https://api.getzep.com/api/v2", 278 | ) 279 | endpointURL := internal.EncodeURL( 280 | baseURL+"/context-templates/%v", 281 | templateID, 282 | ) 283 | headers := internal.MergeHeaders( 284 | r.header.Clone(), 285 | options.ToHeader(), 286 | ) 287 | errorCodes := internal.ErrorCodes{ 288 | 400: func(apiError *core.APIError) error { 289 | return &v3.BadRequestError{ 290 | APIError: apiError, 291 | } 292 | }, 293 | 404: func(apiError *core.APIError) error { 294 | return &v3.NotFoundError{ 295 | APIError: apiError, 296 | } 297 | }, 298 | 500: func(apiError *core.APIError) error { 299 | return &v3.InternalServerError{ 300 | APIError: apiError, 301 | } 302 | }, 303 | } 304 | var response *v3.SuccessResponse 305 | raw, err := r.caller.Call( 306 | ctx, 307 | &internal.CallParams{ 308 | URL: endpointURL, 309 | Method: http.MethodDelete, 310 | Headers: headers, 311 | MaxAttempts: options.MaxAttempts, 312 | BodyProperties: options.BodyProperties, 313 | QueryParameters: options.QueryParameters, 314 | Client: options.HTTPClient, 315 | Response: &response, 316 | ErrorDecoder: internal.NewErrorDecoder(errorCodes), 317 | }, 318 | ) 319 | if err != nil { 320 | return nil, err 321 | } 322 | return &core.Response[*v3.SuccessResponse]{ 323 | StatusCode: raw.StatusCode, 324 | Header: raw.Header, 325 | Body: response, 326 | }, nil 327 | } 328 | -------------------------------------------------------------------------------- /internal/caller_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "net/http" 11 | "net/http/httptest" 12 | "net/url" 13 | "strconv" 14 | "testing" 15 | 16 | "github.com/getzep/zep-go/v3/core" 17 | "github.com/stretchr/testify/assert" 18 | "github.com/stretchr/testify/require" 19 | ) 20 | 21 | // TestCase represents a single test case. 22 | type TestCase struct { 23 | description string 24 | 25 | // Server-side assertions. 26 | givePathSuffix string 27 | giveMethod string 28 | giveResponseIsOptional bool 29 | giveHeader http.Header 30 | giveErrorDecoder ErrorDecoder 31 | giveRequest *Request 32 | giveQueryParams url.Values 33 | giveBodyProperties map[string]interface{} 34 | 35 | // Client-side assertions. 36 | wantResponse *Response 37 | wantHeaders http.Header 38 | wantError error 39 | } 40 | 41 | // Request a simple request body. 42 | type Request struct { 43 | Id string `json:"id"` 44 | } 45 | 46 | // Response a simple response body. 47 | type Response struct { 48 | Id string `json:"id"` 49 | ExtraBodyProperties map[string]interface{} `json:"extraBodyProperties,omitempty"` 50 | QueryParameters url.Values `json:"queryParameters,omitempty"` 51 | } 52 | 53 | // NotFoundError represents a 404. 54 | type NotFoundError struct { 55 | *core.APIError 56 | 57 | Message string `json:"message"` 58 | } 59 | 60 | func TestCall(t *testing.T) { 61 | tests := []*TestCase{ 62 | { 63 | description: "GET success", 64 | giveMethod: http.MethodGet, 65 | giveHeader: http.Header{ 66 | "X-API-Status": []string{"success"}, 67 | }, 68 | giveRequest: &Request{ 69 | Id: "123", 70 | }, 71 | wantResponse: &Response{ 72 | Id: "123", 73 | }, 74 | }, 75 | { 76 | description: "GET success with query", 77 | givePathSuffix: "?limit=1", 78 | giveMethod: http.MethodGet, 79 | giveHeader: http.Header{ 80 | "X-API-Status": []string{"success"}, 81 | }, 82 | giveRequest: &Request{ 83 | Id: "123", 84 | }, 85 | wantResponse: &Response{ 86 | Id: "123", 87 | QueryParameters: url.Values{ 88 | "limit": []string{"1"}, 89 | }, 90 | }, 91 | }, 92 | { 93 | description: "GET not found", 94 | giveMethod: http.MethodGet, 95 | giveHeader: http.Header{ 96 | "X-API-Status": []string{"fail"}, 97 | }, 98 | giveRequest: &Request{ 99 | Id: strconv.Itoa(http.StatusNotFound), 100 | }, 101 | giveErrorDecoder: newTestErrorDecoder(t), 102 | wantError: &NotFoundError{ 103 | APIError: core.NewAPIError( 104 | http.StatusNotFound, 105 | http.Header{}, 106 | errors.New(`{"message":"ID \"404\" not found"}`), 107 | ), 108 | }, 109 | }, 110 | { 111 | description: "POST empty body", 112 | giveMethod: http.MethodPost, 113 | giveHeader: http.Header{ 114 | "X-API-Status": []string{"fail"}, 115 | }, 116 | giveRequest: nil, 117 | wantError: core.NewAPIError( 118 | http.StatusBadRequest, 119 | http.Header{}, 120 | errors.New("invalid request"), 121 | ), 122 | }, 123 | { 124 | description: "POST optional response", 125 | giveMethod: http.MethodPost, 126 | giveHeader: http.Header{ 127 | "X-API-Status": []string{"success"}, 128 | }, 129 | giveRequest: &Request{ 130 | Id: "123", 131 | }, 132 | giveResponseIsOptional: true, 133 | }, 134 | { 135 | description: "POST API error", 136 | giveMethod: http.MethodPost, 137 | giveHeader: http.Header{ 138 | "X-API-Status": []string{"fail"}, 139 | }, 140 | giveRequest: &Request{ 141 | Id: strconv.Itoa(http.StatusInternalServerError), 142 | }, 143 | wantError: core.NewAPIError( 144 | http.StatusInternalServerError, 145 | http.Header{}, 146 | errors.New("failed to process request"), 147 | ), 148 | }, 149 | { 150 | description: "POST extra properties", 151 | giveMethod: http.MethodPost, 152 | giveHeader: http.Header{ 153 | "X-API-Status": []string{"success"}, 154 | }, 155 | giveRequest: new(Request), 156 | giveBodyProperties: map[string]interface{}{ 157 | "key": "value", 158 | }, 159 | wantResponse: &Response{ 160 | ExtraBodyProperties: map[string]interface{}{ 161 | "key": "value", 162 | }, 163 | }, 164 | }, 165 | { 166 | description: "GET extra query parameters", 167 | giveMethod: http.MethodGet, 168 | giveHeader: http.Header{ 169 | "X-API-Status": []string{"success"}, 170 | }, 171 | giveQueryParams: url.Values{ 172 | "extra": []string{"true"}, 173 | }, 174 | giveRequest: &Request{ 175 | Id: "123", 176 | }, 177 | wantResponse: &Response{ 178 | Id: "123", 179 | QueryParameters: url.Values{ 180 | "extra": []string{"true"}, 181 | }, 182 | }, 183 | }, 184 | { 185 | description: "GET merge extra query parameters", 186 | givePathSuffix: "?limit=1", 187 | giveMethod: http.MethodGet, 188 | giveHeader: http.Header{ 189 | "X-API-Status": []string{"success"}, 190 | }, 191 | giveRequest: &Request{ 192 | Id: "123", 193 | }, 194 | giveQueryParams: url.Values{ 195 | "extra": []string{"true"}, 196 | }, 197 | wantResponse: &Response{ 198 | Id: "123", 199 | QueryParameters: url.Values{ 200 | "limit": []string{"1"}, 201 | "extra": []string{"true"}, 202 | }, 203 | }, 204 | }, 205 | } 206 | for _, test := range tests { 207 | t.Run(test.description, func(t *testing.T) { 208 | var ( 209 | server = newTestServer(t, test) 210 | client = server.Client() 211 | ) 212 | caller := NewCaller( 213 | &CallerParams{ 214 | Client: client, 215 | }, 216 | ) 217 | var response *Response 218 | _, err := caller.Call( 219 | context.Background(), 220 | &CallParams{ 221 | URL: server.URL + test.givePathSuffix, 222 | Method: test.giveMethod, 223 | Headers: test.giveHeader, 224 | BodyProperties: test.giveBodyProperties, 225 | QueryParameters: test.giveQueryParams, 226 | Request: test.giveRequest, 227 | Response: &response, 228 | ResponseIsOptional: test.giveResponseIsOptional, 229 | ErrorDecoder: test.giveErrorDecoder, 230 | }, 231 | ) 232 | if test.wantError != nil { 233 | assert.EqualError(t, err, test.wantError.Error()) 234 | return 235 | } 236 | require.NoError(t, err) 237 | assert.Equal(t, test.wantResponse, response) 238 | }) 239 | } 240 | } 241 | 242 | func TestMergeHeaders(t *testing.T) { 243 | t.Run("both empty", func(t *testing.T) { 244 | merged := MergeHeaders(make(http.Header), make(http.Header)) 245 | assert.Empty(t, merged) 246 | }) 247 | 248 | t.Run("empty left", func(t *testing.T) { 249 | left := make(http.Header) 250 | 251 | right := make(http.Header) 252 | right.Set("X-API-Version", "0.0.1") 253 | 254 | merged := MergeHeaders(left, right) 255 | assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) 256 | }) 257 | 258 | t.Run("empty right", func(t *testing.T) { 259 | left := make(http.Header) 260 | left.Set("X-API-Version", "0.0.1") 261 | 262 | right := make(http.Header) 263 | 264 | merged := MergeHeaders(left, right) 265 | assert.Equal(t, "0.0.1", merged.Get("X-API-Version")) 266 | }) 267 | 268 | t.Run("single value override", func(t *testing.T) { 269 | left := make(http.Header) 270 | left.Set("X-API-Version", "0.0.0") 271 | 272 | right := make(http.Header) 273 | right.Set("X-API-Version", "0.0.1") 274 | 275 | merged := MergeHeaders(left, right) 276 | assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) 277 | }) 278 | 279 | t.Run("multiple value override", func(t *testing.T) { 280 | left := make(http.Header) 281 | left.Set("X-API-Versions", "0.0.0") 282 | 283 | right := make(http.Header) 284 | right.Add("X-API-Versions", "0.0.1") 285 | right.Add("X-API-Versions", "0.0.2") 286 | 287 | merged := MergeHeaders(left, right) 288 | assert.Equal(t, []string{"0.0.1", "0.0.2"}, merged.Values("X-API-Versions")) 289 | }) 290 | 291 | t.Run("disjoint merge", func(t *testing.T) { 292 | left := make(http.Header) 293 | left.Set("X-API-Tenancy", "test") 294 | 295 | right := make(http.Header) 296 | right.Set("X-API-Version", "0.0.1") 297 | 298 | merged := MergeHeaders(left, right) 299 | assert.Equal(t, []string{"test"}, merged.Values("X-API-Tenancy")) 300 | assert.Equal(t, []string{"0.0.1"}, merged.Values("X-API-Version")) 301 | }) 302 | } 303 | 304 | // newTestServer returns a new *httptest.Server configured with the 305 | // given test parameters. 306 | func newTestServer(t *testing.T, tc *TestCase) *httptest.Server { 307 | return httptest.NewServer( 308 | http.HandlerFunc( 309 | func(w http.ResponseWriter, r *http.Request) { 310 | assert.Equal(t, tc.giveMethod, r.Method) 311 | assert.Equal(t, contentType, r.Header.Get(contentTypeHeader)) 312 | for header, value := range tc.giveHeader { 313 | assert.Equal(t, value, r.Header.Values(header)) 314 | } 315 | 316 | request := new(Request) 317 | 318 | bytes, err := io.ReadAll(r.Body) 319 | if tc.giveRequest == nil { 320 | require.Empty(t, bytes) 321 | w.WriteHeader(http.StatusBadRequest) 322 | _, err = w.Write([]byte("invalid request")) 323 | require.NoError(t, err) 324 | return 325 | } 326 | require.NoError(t, err) 327 | require.NoError(t, json.Unmarshal(bytes, request)) 328 | 329 | switch request.Id { 330 | case strconv.Itoa(http.StatusNotFound): 331 | notFoundError := &NotFoundError{ 332 | APIError: &core.APIError{ 333 | StatusCode: http.StatusNotFound, 334 | }, 335 | Message: fmt.Sprintf("ID %q not found", request.Id), 336 | } 337 | bytes, err = json.Marshal(notFoundError) 338 | require.NoError(t, err) 339 | 340 | w.WriteHeader(http.StatusNotFound) 341 | _, err = w.Write(bytes) 342 | require.NoError(t, err) 343 | return 344 | 345 | case strconv.Itoa(http.StatusInternalServerError): 346 | w.WriteHeader(http.StatusInternalServerError) 347 | _, err = w.Write([]byte("failed to process request")) 348 | require.NoError(t, err) 349 | return 350 | } 351 | 352 | if tc.giveResponseIsOptional { 353 | w.WriteHeader(http.StatusOK) 354 | return 355 | } 356 | 357 | extraBodyProperties := make(map[string]interface{}) 358 | require.NoError(t, json.Unmarshal(bytes, &extraBodyProperties)) 359 | delete(extraBodyProperties, "id") 360 | 361 | response := &Response{ 362 | Id: request.Id, 363 | ExtraBodyProperties: extraBodyProperties, 364 | QueryParameters: r.URL.Query(), 365 | } 366 | bytes, err = json.Marshal(response) 367 | require.NoError(t, err) 368 | 369 | _, err = w.Write(bytes) 370 | require.NoError(t, err) 371 | }, 372 | ), 373 | ) 374 | } 375 | 376 | // newTestErrorDecoder returns an error decoder suitable for tests. 377 | func newTestErrorDecoder(t *testing.T) func(int, http.Header, io.Reader) error { 378 | return func(statusCode int, header http.Header, body io.Reader) error { 379 | raw, err := io.ReadAll(body) 380 | require.NoError(t, err) 381 | 382 | var ( 383 | apiError = core.NewAPIError(statusCode, header, errors.New(string(raw))) 384 | decoder = json.NewDecoder(bytes.NewReader(raw)) 385 | ) 386 | if statusCode == http.StatusNotFound { 387 | value := new(NotFoundError) 388 | value.APIError = apiError 389 | require.NoError(t, decoder.Decode(value)) 390 | 391 | return value 392 | } 393 | return apiError 394 | } 395 | } 396 | -------------------------------------------------------------------------------- /thread.go: -------------------------------------------------------------------------------- 1 | // Code generated by Fern. DO NOT EDIT. 2 | 3 | package zep 4 | 5 | import ( 6 | json "encoding/json" 7 | fmt "fmt" 8 | internal "github.com/getzep/zep-go/v3/internal" 9 | ) 10 | 11 | type CreateThreadRequest struct { 12 | // The unique identifier of the thread. 13 | ThreadID string `json:"thread_id" url:"-"` 14 | // The unique identifier of the user associated with the thread 15 | UserID string `json:"user_id" url:"-"` 16 | } 17 | 18 | type ThreadGetRequest struct { 19 | // Limit the number of results returned 20 | Limit *int `json:"-" url:"limit,omitempty"` 21 | // Cursor for pagination 22 | Cursor *int `json:"-" url:"cursor,omitempty"` 23 | // Number of most recent messages to return (overrides limit and cursor) 24 | Lastn *int `json:"-" url:"lastn,omitempty"` 25 | } 26 | 27 | type ThreadGetUserContextRequest struct { 28 | // The minimum rating by which to filter relevant facts. 29 | MinRating *float64 `json:"-" url:"minRating,omitempty"` 30 | // Optional template ID to use for custom context rendering. 31 | TemplateID *string `json:"-" url:"template_id,omitempty"` 32 | // Deprecated, this field will be removed in a future release. Defaults to summary mode. Use basic for lower latency 33 | Mode *ThreadGetUserContextRequestMode `json:"-" url:"mode,omitempty"` 34 | } 35 | 36 | type ThreadListAllRequest struct { 37 | // Page number for pagination, starting from 1 38 | PageNumber *int `json:"-" url:"page_number,omitempty"` 39 | // Number of threads to retrieve per page. 40 | PageSize *int `json:"-" url:"page_size,omitempty"` 41 | // Field to order the results by: created_at, updated_at, user_id, thread_id. 42 | OrderBy *string `json:"-" url:"order_by,omitempty"` 43 | // Order direction: true for ascending, false for descending. 44 | Asc *bool `json:"-" url:"asc,omitempty"` 45 | } 46 | 47 | type AddThreadMessagesRequest struct { 48 | // Optional list of role types to ignore when adding messages to graph memory. 49 | // The message itself will still be added, retained and used as context for messages 50 | // that are added to a user's graph. 51 | IgnoreRoles []RoleType `json:"ignore_roles,omitempty" url:"ignore_roles,omitempty"` 52 | // A list of message objects, where each message contains a role and content. 53 | Messages []*Message `json:"messages" url:"messages"` 54 | // Optionally return context block relevant to the most recent messages. 55 | ReturnContext *bool `json:"return_context,omitempty" url:"return_context,omitempty"` 56 | 57 | extraProperties map[string]interface{} 58 | rawJSON json.RawMessage 59 | } 60 | 61 | func (a *AddThreadMessagesRequest) GetIgnoreRoles() []RoleType { 62 | if a == nil { 63 | return nil 64 | } 65 | return a.IgnoreRoles 66 | } 67 | 68 | func (a *AddThreadMessagesRequest) GetMessages() []*Message { 69 | if a == nil { 70 | return nil 71 | } 72 | return a.Messages 73 | } 74 | 75 | func (a *AddThreadMessagesRequest) GetReturnContext() *bool { 76 | if a == nil { 77 | return nil 78 | } 79 | return a.ReturnContext 80 | } 81 | 82 | func (a *AddThreadMessagesRequest) GetExtraProperties() map[string]interface{} { 83 | return a.extraProperties 84 | } 85 | 86 | func (a *AddThreadMessagesRequest) UnmarshalJSON(data []byte) error { 87 | type unmarshaler AddThreadMessagesRequest 88 | var value unmarshaler 89 | if err := json.Unmarshal(data, &value); err != nil { 90 | return err 91 | } 92 | *a = AddThreadMessagesRequest(value) 93 | extraProperties, err := internal.ExtractExtraProperties(data, *a) 94 | if err != nil { 95 | return err 96 | } 97 | a.extraProperties = extraProperties 98 | a.rawJSON = json.RawMessage(data) 99 | return nil 100 | } 101 | 102 | func (a *AddThreadMessagesRequest) String() string { 103 | if len(a.rawJSON) > 0 { 104 | if value, err := internal.StringifyJSON(a.rawJSON); err == nil { 105 | return value 106 | } 107 | } 108 | if value, err := internal.StringifyJSON(a); err == nil { 109 | return value 110 | } 111 | return fmt.Sprintf("%#v", a) 112 | } 113 | 114 | type AddThreadMessagesResponse struct { 115 | Context *string `json:"context,omitempty" url:"context,omitempty"` 116 | MessageUUIDs []string `json:"message_uuids,omitempty" url:"message_uuids,omitempty"` 117 | TaskID *string `json:"task_id,omitempty" url:"task_id,omitempty"` 118 | 119 | extraProperties map[string]interface{} 120 | rawJSON json.RawMessage 121 | } 122 | 123 | func (a *AddThreadMessagesResponse) GetContext() *string { 124 | if a == nil { 125 | return nil 126 | } 127 | return a.Context 128 | } 129 | 130 | func (a *AddThreadMessagesResponse) GetMessageUUIDs() []string { 131 | if a == nil { 132 | return nil 133 | } 134 | return a.MessageUUIDs 135 | } 136 | 137 | func (a *AddThreadMessagesResponse) GetTaskID() *string { 138 | if a == nil { 139 | return nil 140 | } 141 | return a.TaskID 142 | } 143 | 144 | func (a *AddThreadMessagesResponse) GetExtraProperties() map[string]interface{} { 145 | return a.extraProperties 146 | } 147 | 148 | func (a *AddThreadMessagesResponse) UnmarshalJSON(data []byte) error { 149 | type unmarshaler AddThreadMessagesResponse 150 | var value unmarshaler 151 | if err := json.Unmarshal(data, &value); err != nil { 152 | return err 153 | } 154 | *a = AddThreadMessagesResponse(value) 155 | extraProperties, err := internal.ExtractExtraProperties(data, *a) 156 | if err != nil { 157 | return err 158 | } 159 | a.extraProperties = extraProperties 160 | a.rawJSON = json.RawMessage(data) 161 | return nil 162 | } 163 | 164 | func (a *AddThreadMessagesResponse) String() string { 165 | if len(a.rawJSON) > 0 { 166 | if value, err := internal.StringifyJSON(a.rawJSON); err == nil { 167 | return value 168 | } 169 | } 170 | if value, err := internal.StringifyJSON(a); err == nil { 171 | return value 172 | } 173 | return fmt.Sprintf("%#v", a) 174 | } 175 | 176 | type MessageListResponse struct { 177 | // A list of message objects. 178 | Messages []*Message `json:"messages,omitempty" url:"messages,omitempty"` 179 | // The number of messages returned. 180 | RowCount *int `json:"row_count,omitempty" url:"row_count,omitempty"` 181 | // The total number of messages. 182 | TotalCount *int `json:"total_count,omitempty" url:"total_count,omitempty"` 183 | 184 | extraProperties map[string]interface{} 185 | rawJSON json.RawMessage 186 | } 187 | 188 | func (m *MessageListResponse) GetMessages() []*Message { 189 | if m == nil { 190 | return nil 191 | } 192 | return m.Messages 193 | } 194 | 195 | func (m *MessageListResponse) GetRowCount() *int { 196 | if m == nil { 197 | return nil 198 | } 199 | return m.RowCount 200 | } 201 | 202 | func (m *MessageListResponse) GetTotalCount() *int { 203 | if m == nil { 204 | return nil 205 | } 206 | return m.TotalCount 207 | } 208 | 209 | func (m *MessageListResponse) GetExtraProperties() map[string]interface{} { 210 | return m.extraProperties 211 | } 212 | 213 | func (m *MessageListResponse) UnmarshalJSON(data []byte) error { 214 | type unmarshaler MessageListResponse 215 | var value unmarshaler 216 | if err := json.Unmarshal(data, &value); err != nil { 217 | return err 218 | } 219 | *m = MessageListResponse(value) 220 | extraProperties, err := internal.ExtractExtraProperties(data, *m) 221 | if err != nil { 222 | return err 223 | } 224 | m.extraProperties = extraProperties 225 | m.rawJSON = json.RawMessage(data) 226 | return nil 227 | } 228 | 229 | func (m *MessageListResponse) String() string { 230 | if len(m.rawJSON) > 0 { 231 | if value, err := internal.StringifyJSON(m.rawJSON); err == nil { 232 | return value 233 | } 234 | } 235 | if value, err := internal.StringifyJSON(m); err == nil { 236 | return value 237 | } 238 | return fmt.Sprintf("%#v", m) 239 | } 240 | 241 | type ThreadContextResponse struct { 242 | // Context block containing relevant facts, entities, and messages/episodes from the user graph. Meant to be replaced in the system prompt on every chat turn. 243 | Context *string `json:"context,omitempty" url:"context,omitempty"` 244 | 245 | extraProperties map[string]interface{} 246 | rawJSON json.RawMessage 247 | } 248 | 249 | func (t *ThreadContextResponse) GetContext() *string { 250 | if t == nil { 251 | return nil 252 | } 253 | return t.Context 254 | } 255 | 256 | func (t *ThreadContextResponse) GetExtraProperties() map[string]interface{} { 257 | return t.extraProperties 258 | } 259 | 260 | func (t *ThreadContextResponse) UnmarshalJSON(data []byte) error { 261 | type unmarshaler ThreadContextResponse 262 | var value unmarshaler 263 | if err := json.Unmarshal(data, &value); err != nil { 264 | return err 265 | } 266 | *t = ThreadContextResponse(value) 267 | extraProperties, err := internal.ExtractExtraProperties(data, *t) 268 | if err != nil { 269 | return err 270 | } 271 | t.extraProperties = extraProperties 272 | t.rawJSON = json.RawMessage(data) 273 | return nil 274 | } 275 | 276 | func (t *ThreadContextResponse) String() string { 277 | if len(t.rawJSON) > 0 { 278 | if value, err := internal.StringifyJSON(t.rawJSON); err == nil { 279 | return value 280 | } 281 | } 282 | if value, err := internal.StringifyJSON(t); err == nil { 283 | return value 284 | } 285 | return fmt.Sprintf("%#v", t) 286 | } 287 | 288 | type ThreadListResponse struct { 289 | ResponseCount *int `json:"response_count,omitempty" url:"response_count,omitempty"` 290 | Threads []*Thread `json:"threads,omitempty" url:"threads,omitempty"` 291 | TotalCount *int `json:"total_count,omitempty" url:"total_count,omitempty"` 292 | 293 | extraProperties map[string]interface{} 294 | rawJSON json.RawMessage 295 | } 296 | 297 | func (t *ThreadListResponse) GetResponseCount() *int { 298 | if t == nil { 299 | return nil 300 | } 301 | return t.ResponseCount 302 | } 303 | 304 | func (t *ThreadListResponse) GetThreads() []*Thread { 305 | if t == nil { 306 | return nil 307 | } 308 | return t.Threads 309 | } 310 | 311 | func (t *ThreadListResponse) GetTotalCount() *int { 312 | if t == nil { 313 | return nil 314 | } 315 | return t.TotalCount 316 | } 317 | 318 | func (t *ThreadListResponse) GetExtraProperties() map[string]interface{} { 319 | return t.extraProperties 320 | } 321 | 322 | func (t *ThreadListResponse) UnmarshalJSON(data []byte) error { 323 | type unmarshaler ThreadListResponse 324 | var value unmarshaler 325 | if err := json.Unmarshal(data, &value); err != nil { 326 | return err 327 | } 328 | *t = ThreadListResponse(value) 329 | extraProperties, err := internal.ExtractExtraProperties(data, *t) 330 | if err != nil { 331 | return err 332 | } 333 | t.extraProperties = extraProperties 334 | t.rawJSON = json.RawMessage(data) 335 | return nil 336 | } 337 | 338 | func (t *ThreadListResponse) String() string { 339 | if len(t.rawJSON) > 0 { 340 | if value, err := internal.StringifyJSON(t.rawJSON); err == nil { 341 | return value 342 | } 343 | } 344 | if value, err := internal.StringifyJSON(t); err == nil { 345 | return value 346 | } 347 | return fmt.Sprintf("%#v", t) 348 | } 349 | 350 | type ThreadGetUserContextRequestMode string 351 | 352 | const ( 353 | ThreadGetUserContextRequestModeBasic ThreadGetUserContextRequestMode = "basic" 354 | ThreadGetUserContextRequestModeSummary ThreadGetUserContextRequestMode = "summary" 355 | ) 356 | 357 | func NewThreadGetUserContextRequestModeFromString(s string) (ThreadGetUserContextRequestMode, error) { 358 | switch s { 359 | case "basic": 360 | return ThreadGetUserContextRequestModeBasic, nil 361 | case "summary": 362 | return ThreadGetUserContextRequestModeSummary, nil 363 | } 364 | var t ThreadGetUserContextRequestMode 365 | return "", fmt.Errorf("%s is not a valid %T", s, t) 366 | } 367 | 368 | func (t ThreadGetUserContextRequestMode) Ptr() *ThreadGetUserContextRequestMode { 369 | return &t 370 | } 371 | --------------------------------------------------------------------------------