v0.0.42 ac6d556

provider.go RawHistoryFolder

1// Package provider defines the LLM client abstraction used by zot.
2//
3// It supports exactly two providers: Anthropic (Messages API) and
4// OpenAI (Chat Completions API). Everything above this package operates
5// on the types declared here and does not know about HTTP or SSE.
6package provider
7
8import (
9	"context"
10	"encoding/json"
11	"time"
12)
13
14// Role is the speaker of a Message.
15type Role string
16
17const (
18	RoleUser      Role = "user"
19	RoleAssistant Role = "assistant"
20	RoleTool      Role = "tool"
21)
22
23// Content is a block inside a Message. One of TextBlock, ImageBlock,
24// ToolCallBlock, or ToolResultBlock.
25type Content interface {
26	isContent()
27}
28
29// TextBlock is plain text content.
30type TextBlock struct {
31	Text string `json:"text"`
32}
33
34func (TextBlock) isContent() {}
35
36// ImageBlock is an inline image (PNG/JPEG/GIF/WebP).
37type ImageBlock struct {
38	MimeType string `json:"mime_type"`
39	Data     []byte `json:"data"` // raw bytes; encoded to base64 on the wire
40}
41
42func (ImageBlock) isContent() {}
43
44// ToolCallBlock is an assistant-issued call to a tool.
45type ToolCallBlock struct {
46	ID        string          `json:"id"`
47	Name      string          `json:"name"`
48	Arguments json.RawMessage `json:"arguments"`
49}
50
51func (ToolCallBlock) isContent() {}
52
53// ToolResultBlock is the result of a tool execution, attached to a
54// Message with Role == RoleTool.
55type ToolResultBlock struct {
56	CallID  string    `json:"call_id"`
57	Content []Content `json:"content"`
58	IsError bool      `json:"is_error"`
59}
60
61func (ToolResultBlock) isContent() {}
62
63// Message is a single turn in the conversation.
64type Message struct {
65	Role    Role      `json:"role"`
66	Content []Content `json:"content"`
67	Time    time.Time `json:"time"`
68}
69
70// Tool is a tool definition advertised to the LLM.
71type Tool struct {
72	Name        string          `json:"name"`
73	Description string          `json:"description"`
74	Schema      json.RawMessage `json:"schema"` // JSON Schema for arguments
75}
76
77// Usage aggregates token counts and cost for a turn.
78type Usage struct {
79	InputTokens      int     `json:"input_tokens"`
80	OutputTokens     int     `json:"output_tokens"`
81	CacheReadTokens  int     `json:"cache_read_tokens"`
82	CacheWriteTokens int     `json:"cache_write_tokens"`
83	CostUSD          float64 `json:"cost_usd"`
84}
85
86// Add returns u plus v.
87func (u Usage) Add(v Usage) Usage {
88	return Usage{
89		InputTokens:      u.InputTokens + v.InputTokens,
90		OutputTokens:     u.OutputTokens + v.OutputTokens,
91		CacheReadTokens:  u.CacheReadTokens + v.CacheReadTokens,
92		CacheWriteTokens: u.CacheWriteTokens + v.CacheWriteTokens,
93		CostUSD:          u.CostUSD + v.CostUSD,
94	}
95}
96
97// StopReason describes why a turn ended.
98type StopReason string
99
100const (
101	StopEnd     StopReason = "end"
102	StopLength  StopReason = "length"
103	StopToolUse StopReason = "tool_use"
104	StopError   StopReason = "error"
105	StopAborted StopReason = "aborted"
106)
107
108// Event is one item from a provider stream.
109type Event interface {
110	isEvent()
111}
112
113type EventStart struct {
114	Model    string
115	Provider string
116}
117
118func (EventStart) isEvent() {}
119
120type EventTextDelta struct {
121	Delta string
122}
123
124func (EventTextDelta) isEvent() {}
125
126type EventToolStart struct {
127	ID   string
128	Name string
129}
130
131func (EventToolStart) isEvent() {}
132
133type EventToolArgs struct {
134	ID    string
135	Delta string // partial JSON
136}
137
138func (EventToolArgs) isEvent() {}
139
140type EventToolEnd struct {
141	ID string
142}
143
144func (EventToolEnd) isEvent() {}
145
146type EventUsage struct {
147	Usage Usage
148}
149
150func (EventUsage) isEvent() {}
151
152// EventDone is always the final event on a stream.
153type EventDone struct {
154	Stop    StopReason
155	Err     error
156	Message Message // fully assembled assistant message
157}
158
159func (EventDone) isEvent() {}
160
161// Request is a single LLM call.
162type Request struct {
163	Model       string
164	System      string
165	Messages    []Message
166	Tools       []Tool
167	MaxTokens   int
168	Temperature *float32
169	// Reasoning is "", "low", "medium", or "high".
170	// Anthropic maps it to extended-thinking budget tokens.
171	// OpenAI maps it to reasoning_effort.
172	Reasoning string
173}
174
175// Client is an LLM streaming client.
176type Client interface {
177	// Name returns "anthropic" or "openai".
178	Name() string
179	// Stream starts a request. The returned channel delivers events
180	// and is closed after EventDone. Errors during request setup are
181	// returned directly; runtime errors arrive as EventDone{Err: ...}.
182	Stream(ctx context.Context, req Request) (<-chan Event, error)
183}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close