Conversation
Go · package github.com/muthuishere/toolnexus/golang · SPEC §8 · golang/client.go
func (c *Client) Conversation(tk *Toolkit) *Conversation
type Conversation struct { Messages []any // the full running transcript}
func (conv *Conversation) Send(ctx context.Context, prompt string) (RunResult, error)func (conv *Conversation) Reset()Conversation is a stateful handle bound to one Client and Toolkit: each Send continues the
same transcript automatically — the system prompt isn’t re-added, and every prior user/assistant/tool
message is replayed as history — so the model remembers what it already said and did.
When to use it
Section titled “When to use it”Reach for Conversation for in-process, multi-turn interactions — a CLI chat loop, a test harness
driving several turns, an in-memory agent session — where you hold the handle for the process’s
lifetime and don’t need the transcript to survive a restart.
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”Each example points CreateClient at a local httptest.Server — no real network call, no real API
key.
1. The smallest useful call — two turns, transcript grows
Section titled “1. The smallest useful call — two turns, transcript grows”package main
import ( "context" "fmt" "log" "net/http" "net/http/httptest"
toolnexus "github.com/muthuishere/toolnexus/golang")
func main() { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"choices":[{"message":{"role":"assistant","content":"ack"}}]}`)) })) defer srv.Close()
tk, err := toolnexus.CreateToolkit(context.Background(), toolnexus.Options{}) if err != nil { log.Fatal(err) } defer tk.Close()
client := toolnexus.CreateClient(toolnexus.ClientOptions{BaseURL: srv.URL, Style: toolnexus.StyleOpenAI, Model: "m", APIKey: "test-key"}) conv := client.Conversation(tk)
if _, err := conv.Send(context.Background(), "one"); err != nil { log.Fatal(err) } afterFirst := len(conv.Messages)
if _, err := conv.Send(context.Background(), "two"); err != nil { log.Fatal(err) } if len(conv.Messages) <= afterFirst { log.Fatalf("transcript did not grow: %d then %d", afterFirst, len(conv.Messages)) }
fmt.Println("ok: transcript grew from", afterFirst, "to", len(conv.Messages))}2. The realistic case — the system prompt is sent once, not per turn
Section titled “2. The realistic case — the system prompt is sent once, not per turn”package main
import ( "context" "encoding/json" "fmt" "log" "net/http" "net/http/httptest"
toolnexus "github.com/muthuishere/toolnexus/golang")
func main() { var lastRoles []string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body struct { Messages []map[string]any `json:"messages"` } _ = json.NewDecoder(r.Body).Decode(&body) lastRoles = nil for _, m := range body.Messages { lastRoles = append(lastRoles, fmt.Sprint(m["role"])) } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"choices":[{"message":{"role":"assistant","content":"ok"}}]}`)) })) defer srv.Close()
tk, err := toolnexus.CreateToolkit(context.Background(), toolnexus.Options{Builtins: false}) if err != nil { log.Fatal(err) } defer tk.Close()
client := toolnexus.CreateClient(toolnexus.ClientOptions{ BaseURL: srv.URL, Style: toolnexus.StyleOpenAI, Model: "m", APIKey: "test-key", SystemPrompt: "You are terse.", }) conv := client.Conversation(tk)
if _, err := conv.Send(context.Background(), "first"); err != nil { log.Fatal(err) } // turn 1: [system, user] if len(lastRoles) != 2 || lastRoles[0] != "system" { log.Fatalf("turn1 roles = %v, want [system user]", lastRoles) }
if _, err := conv.Send(context.Background(), "second"); err != nil { log.Fatal(err) } // turn 2: [system, user, assistant, user] — the system message is NOT repeated. systemCount := 0 for _, r := range lastRoles { if r == "system" { systemCount++ } } if systemCount != 1 || len(lastRoles) != 4 { log.Fatalf("turn2 roles = %v, want exactly one system message and 4 total", lastRoles) }
fmt.Println("ok: system sent once, transcript on turn 2 =", lastRoles)}3. The full surface — Reset clears memory, then continues fresh
Section titled “3. The full surface — Reset clears memory, then continues fresh”package main
import ( "context" "fmt" "log" "net/http" "net/http/httptest"
toolnexus "github.com/muthuishere/toolnexus/golang")
func main() { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"choices":[{"message":{"role":"assistant","content":"ack"}}]}`)) })) defer srv.Close()
tk, err := toolnexus.CreateToolkit(context.Background(), toolnexus.Options{}) if err != nil { log.Fatal(err) } defer tk.Close()
client := toolnexus.CreateClient(toolnexus.ClientOptions{BaseURL: srv.URL, Style: toolnexus.StyleOpenAI, Model: "m", APIKey: "test-key"}) conv := client.Conversation(tk)
if _, err := conv.Send(context.Background(), "hello"); err != nil { log.Fatal(err) } if len(conv.Messages) == 0 { log.Fatal("expected a non-empty transcript after Send") }
conv.Reset() if len(conv.Messages) != 0 { log.Fatalf("after Reset, Messages = %d, want 0", len(conv.Messages)) }
// A fresh Send after Reset starts a new transcript from scratch. res, err := conv.Send(context.Background(), "hello again") if err != nil { log.Fatal(err) } if res.Turns != 1 || len(conv.Messages) != 2 { // [user, assistant] log.Fatalf("post-reset turn unexpected: turns=%d messages=%d", res.Turns, len(conv.Messages)) }
fmt.Println("ok: reset then", len(conv.Messages), "messages after one fresh turn")}Conversation surface
Section titled “Conversation surface”| Member | What it does |
|---|---|
Client.Conversation(tk *Toolkit) *Conversation |
Builds a Conversation bound to this client and toolkit. |
Messages []any |
The full running transcript — read it directly to inspect or persist it yourself. |
Send(ctx, prompt) (RunResult, error) |
Sends the next user turn with prior history retained; updates Messages to the returned RunResult.Messages. |
Reset() |
Clears Messages — the next Send starts a brand-new transcript. |
See also
Section titled “See also”CreateClient— The unified client: system prompt, skills injection, parallel and chained tool calls, retries, memory.Client.Run— Send a prompt, let the loop call tools until the model stops, get a RunResult.Client.Stream— The streaming loop: text deltas, tool-call events, and suspension events as they happen.NewInMemoryConversationStore— TheClient.Ask-based alternative when memory must survive a restart or be keyed by a request id.