AI Basics: Roles, Turns & Completions
Understanding how Hashbrown (and most LLM APIs) model a conversation is the first step toward building anything useful. This page introduces the core vocabulary: messages, roles, the assistant turn, and a completion.
1. What is a message?
A message is a single unit of conversation exchanged between the user and the assistant. In TypeScript terms:
User Message
A user message is the simplest message in a conversation exchange. It has the role of user
and includes
either the string content or a JSON object, depending on how you format your messages.
interface UserMessage {
role: 'user';
content: string | JsonValue;
}
Error Message
If generating a completion fails, the error message is exposed using the role error
where the content
is the error message:
interface ErrorMessage {
role: 'error';
content: string;
}
Assistant Message
An assistant message is generated by the large-language model, and includes at least one of the following:
content
if the assistant has generated a responsetoolCalls
if the assistant wants to call a tool before generating a response
Occassionally, an assistant message may contain both content
and toolCalls
, though generally it will generate
toolCalls
in a loop until it is prepared to generate a message with content
.
Hashbrown strongly types tool calls for you. Assistant messages are modeled with the following types:
type ToolCall<ListOfTools extends AnyTool> =
| {
role: 'tool';
status: 'done';
name: Name;
args: Args;
result: PromiseSettledResult<Result>;
toolCallId: string;
}
| {
role: 'tool';
status: 'pending';
name: Name;
args: Args;
toolCallId: string;
progress?: number;
};
interface AssistantMessage<Output, ListOfTools> {
role: 'assistant';
content?: Output;
toolCalls: ToolCall<ListOfTools>[];
}
2. Message roles
Role | Who sends it | Typical purpose |
---|---|---|
user | Human (or your UI on their behalf) | Ask a question, issue a command |
assistant | The LLM | Answer, ask a clarifying question, or call a tool |
error | Your code or Hashbrown | Error generated as a result of some failure |
Examples
{ role: 'user', content: 'Turn on the living-room lights' }
// Assistant decides it needs additional data
{ role: 'assistant', toolCalls: [{ name: 'getLights', args: { room: 'living' }, status: 'pending' }] }
// Assistant can now finish its turn:
{ role: 'assistant', content: 'Lights on! Anything else I can help with?' }
3. The assistant turn
A single user message may trigger a chain of assistant actions until it produces a final answer. We call that chain a turn.
User ► Assistant(tool call) ► Tool ► Assistant(tool call) ► Tool ... ► Assistant(final)
The turn ends when the assistant sends a regular content message (without toolCalls
). Hashbrown takes care of wiring these messages together; you read them as a single, ordered array.
4. What is a completion?
A completion is the assistant’s entire response payload for a given prompt. In Hashbrown you encounter two flavours:
a) Single-turn completion
Use the
import { useCompletion } from '@hashbrownai/react';
const Weather = () => {
const { output, isReceiving } = useCompletion({
model: 'gpt-4.1',
input: 'Weather in Tokyo tomorrow?',
system: 'You are a terse weather bot.',
tools: [getWeatherTool],
});
return <p>{isReceiving ? '...' : output}</p>;
};
Hashbrown manages the request/stream for you and returns the assistant’s completed text (or structured JSON, when you use a schema).
b) Multi-turn chat completion
When you want stateful back-and-forth, reach for
import { useChat } from '@hashbrownai/react';
export function ChatExample() {
const { messages, sendMessage, isReceiving } = useChat({
model: 'gpt-4.1',
system: 'You are a helpful assistant.',
});
return (
<>
{messages.map((m, i) => (
<p key={i}>
{m.role}: {m.content ?? '[tool]'}{' '}
</p>
))}
<button onClick={() => sendMessage({ role: 'user', content: 'Hi!' })}>
Say hi
</button>
{isReceiving && <p>Assistant is typing...</p>}
</>
);
}
messages
already contains all roles, so you can render or inspect the conversation however you like.
5. Error messages
If generating an assistant's turn fails, Hashbrown converts the failure into a message:
{ role: 'error', content: '500: Internal Server Error' }
You can all the retry()
callback function returned from Hashbrown's hooks to reattempt
generating the completion.
6. Quick cheat-sheet
Message = { role, content | toolCalls }
Roles = user | assistant | error
Turn = everything the assistant does until it emits normal content
Completion (single) = output from useCompletion()
Completion (chat) = latest assistant message(s) inside useChat() state
Next steps
Write system instructions
Learn to set instructions for large-language models when generating completions