Prompts and Chat

An LLMClient is prompted through its prompt API, the same API that Smarter Chat uses. The platform runs the whole prompt: it selects plugins, calls the LLM, runs the tools that the LLM calls, applies the Guardrails, and saves the chat session’s history. Your code receives the result.

The SDK always reads version 2 of the Smarter Chat API contract, whose structs are smarter.PromptChatAPIConfig, smarter.PromptChatAPIPromptResponse, and so on.

One prompt

A prompt is a chat thread, a []smarter.PromptChatAPIMessage. smarter.Text makes one of a single user message.

llmclient, err := client.LLMClients.Find(ctx, "calculator")
if err != nil {
    return err
}
response, err := client.LLMClients.Prompt(ctx, llmclient.Ref(), smarter.Text("What is 17 times 23?"), nil)
if err != nil {
    return err
}
fmt.Println(response.Text()) // the LLM's final reply
for _, message := range response.Data.Messages {
    fmt.Println(message.Role, message.Text())
}

response.Data.Messages is every message that the prompt added to the thread, in order: the assistant’s tool calls, the tools’ results, the reply, and Smarter’s own informational messages (role smarter), such as the prompt’s configuration and its token charges. response.Data.Completion is the LLM’s chat completion, with its token usage.

An LLMClient is identified by a smarter.ID: smarter.IntID(5), a hashed id such as smarter.ID("rMTAwMDAwNQx"), or a resource’s Ref(). Find looks one up by name.

A conversation

Chat starts a chat session and keeps it: each Send sends the whole thread, with the session’s key, and adds the prompt’s messages to the thread. The platform saves the session’s history, so a session can be resumed later, even from another process.

chat, err := client.LLMClients.Chat(ctx, llmclient.Ref(), nil)
if err != nil {
    return err
}
reply, err := chat.Send(ctx, "What is 2 to the 10th power?")
if err != nil {
    return err
}
fmt.Println(reply.Text())

key := chat.SessionKey() // save it to resume the session later
resumed, err := client.LLMClients.Chat(ctx, llmclient.Ref(), &smarter.PromptParams{SessionKey: key})
if err != nil {
    return err
}
fmt.Println(len(resumed.Messages))

A new session’s thread starts with the LLMClient’s system prompt and welcome message, exactly as Smarter Chat starts one. Only the roles that LLM providers accept (system, user, assistant and tool) are sent. Smarter’s own messages stay in the thread for you to read. A chat session is not safe for concurrent use.

Streaming a prompt’s progress

A prompt that calls tools can take a while. Stream reports its progress while it runs: each LLM request, tool call, plugin call and MCP call. Read it like a bufio.Scanner, and close it. When Next returns false, Response is the same response that Prompt returns.

stream := client.LLMClients.Stream(ctx, llmclient.Ref(), smarter.Text("What is sin(pi / 2)?"), nil)
defer stream.Close()
for stream.Next() {
    fmt.Println("...", stream.Current().Message) // e.g. "Calling tool calculator"
}
response, err := stream.Response() // the stream's error, if it failed
if err != nil {
    return err
}
fmt.Println(response.Text())

chat.Stream(ctx, "...") streams a conversation’s prompt, and updates the chat’s thread when the stream has been read. stream.UntilDone() reads a stream to its end and returns its response. The progress is streamed as Server-Sent Events, and the platform sends the reply once it is complete: the reply’s text itself is not streamed token by token.

Failed prompts

When the LLM provider rejects a prompt, the prompt API answers with the provider’s status, and the SDK returns the matching error, e.g. one that matches smarter.ErrBadRequest, whose Body is the error response. A failed stream’s Response returns a *smarter.PromptError. In both cases the session’s history keeps a smarter_error message, and a chat’s thread is left unchanged.

Prompts are billed by the LLM provider, to the account of the provider’s API key on your platform.