Why: a crash after a tool was about to run left the call unpaired, and workspace writes could not be rewound. Evidence: GOWORK=off go test ./runtime/ ./runtime/llm/ ./runtime/checkpoint/ ./runtime/telemetry/ ./runtime/tools/ ./runtime/session/ ./adapters/native/ Impact: pending calls rerun only when both the stored and current replay declarations are safe; otherwise the result is an error. Empty fallback lists and a nil checkpoint log keep the previous behavior. Refs: feature/durable-rewind Co-authored-by: Cursor <cursoragent@cursor.com>
127 lines
4.1 KiB
Go
127 lines
4.1 KiB
Go
package llm
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// Role is a transcript role carried into the provider request.
|
|
type Role string
|
|
|
|
const (
|
|
RoleSystem Role = "system"
|
|
RoleUser Role = "user"
|
|
RoleAssistant Role = "assistant"
|
|
RoleTool Role = "tool"
|
|
)
|
|
|
|
// StopReason is the terminal condition of one assistant turn.
|
|
type StopReason string
|
|
|
|
const (
|
|
StopEnd StopReason = "end"
|
|
StopToolCalls StopReason = "toolCalls"
|
|
StopError StopReason = "error"
|
|
StopAborted StopReason = "aborted"
|
|
)
|
|
|
|
// EventKind identifies one item on the assistant stream.
|
|
type EventKind string
|
|
|
|
const (
|
|
KindTextDelta EventKind = "text_delta"
|
|
KindReasoningDelta EventKind = "reasoning_delta"
|
|
KindToolCallDelta EventKind = "tool_call_delta"
|
|
KindMessageEnd EventKind = "message_end"
|
|
KindError EventKind = "error"
|
|
)
|
|
|
|
// ToolCall is one function call requested by the assistant.
|
|
type ToolCall struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Arguments string `json:"arguments"`
|
|
}
|
|
|
|
// Message is one transcript entry. System, user, assistant, and tool results share this shape.
|
|
type Message struct {
|
|
Role Role `json:"role"`
|
|
Content string `json:"content,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Images []Image `json:"images,omitempty"`
|
|
ToolCallID string `json:"tool_call_id,omitempty"`
|
|
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
|
StopReason StopReason `json:"stop_reason,omitempty"`
|
|
IsError bool `json:"is_error,omitempty"`
|
|
}
|
|
|
|
// ToolDecl is the JSON-schema declaration sent to the provider.
|
|
type ToolDecl struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Parameters json.RawMessage `json:"parameters"`
|
|
}
|
|
|
|
// Image is one picture attached to a user message.
|
|
type Image struct {
|
|
MIME string `json:"mime,omitempty"`
|
|
URL string `json:"url,omitempty"`
|
|
Data string `json:"data,omitempty"`
|
|
}
|
|
|
|
// Usage counts tokens. On one provider response, PromptTokens, CompletionTokens
|
|
// and TotalTokens are that request. On a loop Result they are the billing sum
|
|
// across requests; Last* is only the latest request, for context observation.
|
|
// Zero means the provider omitted usage.
|
|
type Usage struct {
|
|
PromptTokens int `json:"prompt_tokens"`
|
|
CompletionTokens int `json:"completion_tokens"`
|
|
TotalTokens int `json:"total_tokens"`
|
|
CacheReadTokens int `json:"cache_read_tokens,omitempty"`
|
|
CacheWriteTokens int `json:"cache_write_tokens,omitempty"`
|
|
LastPromptTokens int `json:"last_prompt_tokens,omitempty"`
|
|
LastCompletionTokens int `json:"last_completion_tokens,omitempty"`
|
|
LastTotalTokens int `json:"last_total_tokens,omitempty"`
|
|
}
|
|
|
|
// Event is one streamed update. Failures are KindError plus a final assistant
|
|
// message whose StopReason is error or aborted. Stream implementations do not panic.
|
|
type Event struct {
|
|
Kind EventKind
|
|
Text string
|
|
ReasoningDelta string
|
|
ToolIndex int
|
|
ToolID string
|
|
ToolName string
|
|
ArgumentsDelta string
|
|
Message Message
|
|
Usage Usage
|
|
// FinishReason 是供应商原始结束原因(stop、length、tool_calls 等)。
|
|
// 空表示这条事件不携带结束原因。StopReason 的映射不受此字段影响。
|
|
FinishReason string
|
|
Err error
|
|
Retryable bool
|
|
ErrorMessage string
|
|
// HTTPStatus 是提供方的 HTTP 状态。0 表示这条错误不是 HTTP 响应。
|
|
HTTPStatus int
|
|
// RetryAfter is the provider's requested wait. HasRetryAfter distinguishes
|
|
// a real zero from a missing or unparseable header.
|
|
RetryAfter time.Duration
|
|
HasRetryAfter bool
|
|
}
|
|
|
|
// StreamOptions tunes one provider request.
|
|
type StreamOptions struct {
|
|
MaxTokens int
|
|
APIBase string
|
|
APIKey string
|
|
// ThinkingLevel is off, minimal, low, medium, high, xhigh, or max.
|
|
// Empty leaves the provider default unchanged.
|
|
ThinkingLevel string
|
|
}
|
|
|
|
// StreamFunc is the provider boundary. It must return a channel that is closed
|
|
// after a terminal message_end or error event. It must not panic.
|
|
type StreamFunc func(ctx context.Context, model string, messages []Message, tools []ToolDecl, opts StreamOptions) <-chan Event
|