Skip to content

Commit 2a47389

Browse files
franchbclaude
andauthored
feat: add Go client SDK with ogen code generation (#375)
Add a Go client for the Hindsight API using ogen for strongly-typed code generation from the OpenAPI 3.1 spec. The client provides a high-level wrapper with functional options around the generated code, covering all core operations (retain, recall, reflect, bank management). Includes: - ogen-based code generation with OpenAPI 3.1 spec preprocessing - High-level Client wrapper with idiomatic Go API - Functional options for all operations (WithBudget, WithTags, etc.) - OgenClient() escape hatch for advanced operations - Integration tests and godoc examples - Go SDK reference docs and cookbook entries (quickstart, concurrent pipeline, memory-augmented API service) - Updated generate-clients.sh with Go generation step Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b4b5c44 commit 2a47389

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+31851
-5
lines changed

hindsight-clients/go/README.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Hindsight Go Client
2+
3+
Go client for the [Hindsight](https://github.com/vectorize-io/hindsight) agent memory API.
4+
5+
## Installation
6+
7+
```bash
8+
go get github.com/vectorize-io/hindsight-client-go
9+
```
10+
11+
Requires Go 1.25+.
12+
13+
## Quick Start
14+
15+
```go
16+
package main
17+
18+
import (
19+
"context"
20+
"fmt"
21+
"log"
22+
23+
hindsight "github.com/vectorize-io/hindsight-client-go"
24+
)
25+
26+
func main() {
27+
client, err := hindsight.New("http://localhost:8888")
28+
if err != nil {
29+
log.Fatal(err)
30+
}
31+
32+
ctx := context.Background()
33+
34+
// Store a memory
35+
_, err = client.Retain(ctx, "my-bank", "The user prefers dark mode")
36+
if err != nil {
37+
log.Fatal(err)
38+
}
39+
40+
// Recall memories
41+
resp, err := client.Recall(ctx, "my-bank", "What are the user's preferences?")
42+
if err != nil {
43+
log.Fatal(err)
44+
}
45+
for _, r := range resp.Results {
46+
fmt.Println(r.Text)
47+
}
48+
49+
// Reflect with reasoning
50+
ref, err := client.Reflect(ctx, "my-bank", "Summarize what you know about the user")
51+
if err != nil {
52+
log.Fatal(err)
53+
}
54+
fmt.Println(ref.Text)
55+
}
56+
```
57+
58+
## Authentication
59+
60+
```go
61+
client, err := hindsight.New("http://localhost:8888", hindsight.WithAPIKey("your-key"))
62+
```
63+
64+
## Core Operations
65+
66+
### Retain (Store Memories)
67+
68+
```go
69+
// Single memory
70+
_, err := client.Retain(ctx, "bank-id", "Alice loves Python",
71+
hindsight.WithContext("programming discussion"),
72+
hindsight.WithTags([]string{"tech"}),
73+
hindsight.WithDocumentID("conv-123"),
74+
)
75+
76+
// Batch
77+
items := []hindsight.MemoryItem{
78+
{Content: "First memory"},
79+
{Content: "Second memory"},
80+
}
81+
_, err := client.RetainBatch(ctx, "bank-id", items,
82+
hindsight.WithDocumentTags([]string{"import"}),
83+
hindsight.WithAsync(true),
84+
)
85+
```
86+
87+
### Recall (Retrieve Memories)
88+
89+
```go
90+
resp, err := client.Recall(ctx, "bank-id", "What does Alice like?",
91+
hindsight.WithBudget(hindsight.BudgetHigh),
92+
hindsight.WithMaxTokens(4096),
93+
hindsight.WithTypes([]string{"world", "experience"}),
94+
hindsight.WithTrace(true),
95+
hindsight.WithRecallTags([]string{"tech"}),
96+
)
97+
98+
for _, r := range resp.Results {
99+
fmt.Printf("[%s] %s\n", r.Type.Or("unknown"), r.Text)
100+
}
101+
```
102+
103+
### Reflect (Reason with Memories)
104+
105+
```go
106+
resp, err := client.Reflect(ctx, "bank-id", "What are the user's interests?",
107+
hindsight.WithReflectBudget(hindsight.BudgetMid),
108+
hindsight.WithReflectMaxTokens(2048),
109+
hindsight.WithResponseSchema(map[string]any{
110+
"type": "object",
111+
"properties": map[string]any{
112+
"interests": map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
113+
},
114+
}),
115+
)
116+
117+
fmt.Println(resp.Text)
118+
```
119+
120+
### Bank Management
121+
122+
```go
123+
// Create bank with personality
124+
_, err := client.CreateBank(ctx, "my-bank",
125+
hindsight.WithBankName("My Agent"),
126+
hindsight.WithMission("Help users with coding tasks"),
127+
hindsight.WithDisposition(hindsight.DispositionTraits{
128+
Skepticism: 3,
129+
Literalism: 2,
130+
Empathy: 4,
131+
}),
132+
)
133+
134+
// Update mission
135+
_, err = client.SetMission(ctx, "my-bank", "New mission statement")
136+
137+
// List all banks
138+
banks, err := client.ListBanks(ctx)
139+
140+
// Delete bank
141+
err = client.DeleteBank(ctx, "my-bank")
142+
```
143+
144+
## Advanced Usage
145+
146+
For operations not covered by the high-level wrapper (documents, entities, operations, mental models, directives), access the ogen-generated client directly:
147+
148+
```go
149+
ogen := client.OgenClient()
150+
151+
// List entities
152+
resp, err := ogen.ListEntities(ctx, ogenapi.ListEntitiesParams{
153+
BankID: "my-bank",
154+
})
155+
156+
// Create mental model
157+
resp, err := ogen.CreateMentalModel(ctx, &ogenapi.CreateMentalModelRequest{
158+
Name: "user-preferences",
159+
SourceQuery: "What are the user's preferences?",
160+
}, ogenapi.CreateMentalModelParams{BankID: "my-bank"})
161+
```
162+
163+
## Code Generation
164+
165+
The client is built on [ogen](https://github.com/ogen-go/ogen), generating strongly-typed Go code from the OpenAPI 3.1 spec. To regenerate after API changes:
166+
167+
```bash
168+
cd hindsight-clients/go
169+
go generate ./...
170+
```
171+
172+
## Running Tests
173+
174+
Integration tests require a running Hindsight API server:
175+
176+
```bash
177+
HINDSIGHT_API_URL=http://localhost:8888 go test -v -tags=integration ./...
178+
```

hindsight-clients/go/banks.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package hindsight
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/vectorize-io/hindsight-client-go/internal/ogenapi"
8+
)
9+
10+
// CreateBank creates a new memory bank or updates an existing one.
11+
func (c *Client) CreateBank(ctx context.Context, bankID string, opts ...CreateBankOption) (*BankProfileResponse, error) {
12+
var cfg createBankConfig
13+
for _, o := range opts {
14+
o(&cfg)
15+
}
16+
17+
req := &ogenapi.CreateBankRequest{}
18+
if cfg.name != nil {
19+
req.Name = ogenapi.NewOptString(*cfg.name)
20+
}
21+
if cfg.mission != nil {
22+
req.Mission = ogenapi.NewOptString(*cfg.mission)
23+
}
24+
if cfg.disposition != nil {
25+
req.Disposition = ogenapi.NewOptDispositionTraits(*cfg.disposition)
26+
}
27+
28+
res, err := c.api.CreateOrUpdateBank(ctx, req, ogenapi.CreateOrUpdateBankParams{
29+
BankID: bankID,
30+
})
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
resp, ok := res.(*ogenapi.BankProfileResponse)
36+
if !ok {
37+
return nil, fmt.Errorf("hindsight: unexpected response type %T", res)
38+
}
39+
return resp, nil
40+
}
41+
42+
// GetBankProfile retrieves the profile for a memory bank.
43+
func (c *Client) GetBankProfile(ctx context.Context, bankID string) (*BankProfileResponse, error) {
44+
res, err := c.api.GetBankProfile(ctx, ogenapi.GetBankProfileParams{
45+
BankID: bankID,
46+
})
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
resp, ok := res.(*ogenapi.BankProfileResponse)
52+
if !ok {
53+
return nil, fmt.Errorf("hindsight: unexpected response type %T", res)
54+
}
55+
return resp, nil
56+
}
57+
58+
// ListBanks returns all memory banks.
59+
func (c *Client) ListBanks(ctx context.Context) (*BankListResponse, error) {
60+
res, err := c.api.ListBanks(ctx)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
resp, ok := res.(*ogenapi.BankListResponse)
66+
if !ok {
67+
return nil, fmt.Errorf("hindsight: unexpected response type %T", res)
68+
}
69+
return resp, nil
70+
}
71+
72+
// DeleteBank permanently deletes a memory bank and all its data.
73+
func (c *Client) DeleteBank(ctx context.Context, bankID string) error {
74+
_, err := c.api.DeleteBank(ctx, ogenapi.DeleteBankParams{
75+
BankID: bankID,
76+
})
77+
return err
78+
}
79+
80+
// SetMission updates the mission for a memory bank.
81+
func (c *Client) SetMission(ctx context.Context, bankID, mission string) (*BankProfileResponse, error) {
82+
req := &ogenapi.CreateBankRequest{
83+
Mission: ogenapi.NewOptString(mission),
84+
}
85+
86+
res, err := c.api.CreateOrUpdateBank(ctx, req, ogenapi.CreateOrUpdateBankParams{
87+
BankID: bankID,
88+
})
89+
if err != nil {
90+
return nil, err
91+
}
92+
93+
resp, ok := res.(*ogenapi.BankProfileResponse)
94+
if !ok {
95+
return nil, fmt.Errorf("hindsight: unexpected response type %T", res)
96+
}
97+
return resp, nil
98+
}
99+
100+
// UpdateDisposition updates the personality traits for a memory bank.
101+
func (c *Client) UpdateDisposition(ctx context.Context, bankID string, traits DispositionTraits) (*BankProfileResponse, error) {
102+
req := &ogenapi.UpdateDispositionRequest{
103+
Disposition: traits,
104+
}
105+
106+
res, err := c.api.UpdateBankDisposition(ctx, req, ogenapi.UpdateBankDispositionParams{
107+
BankID: bankID,
108+
})
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
resp, ok := res.(*ogenapi.BankProfileResponse)
114+
if !ok {
115+
return nil, fmt.Errorf("hindsight: unexpected response type %T", res)
116+
}
117+
return resp, nil
118+
}

hindsight-clients/go/doc.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Package hindsight provides a Go client for the Hindsight agent memory API.
2+
//
3+
// Hindsight is a long-term memory system for AI agents. This client wraps the
4+
// auto-generated ogen API client with a simpler, Go-idiomatic interface for the
5+
// core operations: retain (store), recall (retrieve), and reflect (reason).
6+
//
7+
// # Quick Start
8+
//
9+
// client, err := hindsight.New("http://localhost:8888")
10+
// if err != nil {
11+
// log.Fatal(err)
12+
// }
13+
//
14+
// // Store a memory
15+
// _, err = client.Retain(ctx, "my-bank", "The user prefers dark mode")
16+
//
17+
// // Recall memories
18+
// resp, err := client.Recall(ctx, "my-bank", "What are the user's preferences?")
19+
// for _, r := range resp.Results {
20+
// fmt.Println(r.Text)
21+
// }
22+
//
23+
// // Reflect with reasoning
24+
// ref, err := client.Reflect(ctx, "my-bank", "Summarize what you know about the user")
25+
// fmt.Println(ref.Text)
26+
//
27+
// # Authentication
28+
//
29+
// For authenticated deployments, pass an API key:
30+
//
31+
// client, err := hindsight.New("http://localhost:8888", hindsight.WithAPIKey("your-key"))
32+
//
33+
// # Advanced Usage
34+
//
35+
// For operations not covered by the high-level wrapper (documents, entities,
36+
// operations, mental models, directives), access the ogen-generated client:
37+
//
38+
// ogen := client.OgenClient()
39+
// resp, err := ogen.ListEntities(ctx, ogenapi.ListEntitiesParams{BankID: "my-bank"})
40+
package hindsight

0 commit comments

Comments
 (0)