runner

package
v1.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2026 License: GPL-3.0 Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// FallbackMaxOutputTokens 未知模型的默认最大输出 token
	FallbackMaxOutputTokens = 16384
	// FallbackContextWindow 未知模型的默认上下文窗口
	FallbackContextWindow = 128000
)

Variables

This section is empty.

Functions

func BuildProvider

func BuildProvider(p *ai_provider_entity.AIProvider, apiKey string) (cagoProvider.Provider, error)

BuildProvider 根据 AIProvider entity + 已解密 API Key 构造 cago provider.Provider。 后续对话全部走 cago provider;model / max_tokens / reasoning 在 request 阶段注入, 这里只负责"传输层"。

func BuildSystem

func BuildSystem(ctx context.Context, cfg SystemConfig) (*coding.System, error)

BuildSystem 拼装 coding.System:

  • 关掉 ~/.claude 自动加载 / skills / slash commands;
  • 用 OpsKat 自定义 system 模板;
  • 注册 auditMiddleware(around 模式:跑前挂 *aictx.CheckResult slot,跑后落审计);
  • 非空 SystemPrompt 走 coding.AppendSystem 注入;
  • 非空 Model 走 coding.WithModel;
  • ProviderEntity 开启 reasoning 时走 coding.WithThinking;
  • 非空 Tools 走 coding.WithExtraTools;
  • aitool.LocalToolGate 非 nil 时为 local_bash/local_write/local_edit 加审批 middleware (local_read/local_grep/local_find/local_ls 只读,不审批)。注册顺序: audit 在前(外层),gate 在后(内层)—— 这样 gate AbortWithDeny 后 audit 的 c.Next() 返回时 c.Output 已是 deny block,照样落审计。
  • cago 的 subagent dispatch 工具不会把父 middleware 透传给 child agent。 Explore/Plan 工具集只读,无 local_bash/local_write/local_edit 路径; GeneralPurpose 含全套 coding 工具,因此显式替换默认 GP,把 audit + aitool.LocalToolGate 中间件挂上。

func ToAgentMessages

func ToAgentMessages(messages []Message) []agent.Message

ToAgentMessages 把 OpsKat 的 []Message 转换为 cago agent.Message 切片。

转换规则:

  • user → agent.Message{Role: RoleUser, Content: [TextBlock]}
  • assistant → Content 依次包含 ThinkingBlock?(若 Thinking 非空)+ TextBlock?(若 Content 非空)
  • 每个 ToolCall 转 ToolUseBlock(State=ToolUseReady,Input 来自 Arguments JSON)
  • tool → Content = [ToolResultBlock{ToolUseID, Content:[TextBlock]}]

块必须使用值类型(agent.TextBlock{}),cago 的 BuildRequest 与 Conversation 内部 一律用值类型 type switch(case TextBlock:);指针块满足 ContentBlock 接口但无法 匹配,会被 BuildRequest 静默丢弃,导致历史在 LLM 端完全消失。

ReasoningContent(OpenAI/DeepSeek 风格)目前与 Thinking 合并处理——cago 的 ThinkingBlock 兼容两者。 ThinkingBlock.Signature 仅在 Anthropic 流式 thinking 模式必填,DB schema 当前未持久化此字段; 重放时缺 Signature 可能导致 Anthropic 拒绝继续多轮 thinking——这是已知 follow-up(见 plan 风险章节)。

Types

type AIContext

type AIContext struct {
	OpenTabs []TabInfo `json:"openTabs"`
}

AIContext 前端传入的上下文信息

type EventTranslator

type EventTranslator struct {
	// contains filtered or unexported fields
}

EventTranslator 把 cago agent.Event 翻译成 OpsKat 现有 StreamEvent。

前端契约(type 字段名 + 内部字段)保持不变;这里只是从 cago 的 event 流里 反推出旧 provider 的事件形态,保证前端 ai:event:{convID} 监听者无感知。

翻译是有状态的:thinking → content / tool_start 之间的转移需要插入一条 "thinking_done",保持前端既有 stream contract。EventTranslator 实例每个对话每轮 1 个。

审批事件(approval_request / approval_result)由 app_ai.go 的 confirmFunc 直接 emit,不走 cago event 流,因此不在本适配器范围。

func NewStreamTranslator

func NewStreamTranslator() *EventTranslator

NewStreamTranslator 创建一个新的事件翻译器。

func (*EventTranslator) Translate

func (t *EventTranslator) Translate(ev agent.Event, emit func(StreamEvent))

Translate 处理一条 cago agent.Event,调用 emit 0..N 次发出对应 StreamEvent。

多发场景:thinking → content 转移会先发 "thinking_done" 再发 "content"。 不映射的 EventKind(如 EventMessageEnd / EventToolDelta)直接静默—— 前者由 EventTextDelta / EventTurnEnd 隐含,后者是 cago 流式工具参数增量, 旧 OpsKat StreamEvent 没有对应字段,可丢弃。

type Message

type Message struct {
	Role             Role       `json:"role"`
	Content          string     `json:"content"`
	Thinking         string     `json:"thinking,omitempty"`          // Anthropic 格式
	ReasoningContent string     `json:"reasoning_content,omitempty"` // DeepSeek/OpenAI 格式
	ToolCalls        []ToolCall `json:"tool_calls,omitempty"`
	ToolCallID       string     `json:"tool_call_id,omitempty"` // role=tool 时标识调用
}

Message 对话消息

func SplitForReplay

func SplitForReplay(messages []Message) (history []Message, lastUserText string)

SplitForReplay 把 OpsKat 的消息列表切分为「需要灌进 cago Conversation 的历史」+「本轮要发给 LLM 的最后一条用户文本」。

调用约定:messages 来自 SendAIMessage 入参,包含全部历史 + 末尾的新 user message。 如果末尾不是 user message(边界场景,比如纯历史回放),则全部归入 history、lastUserText = ""。

type ModelDefaults

type ModelDefaults struct {
	MaxOutputTokens int // 最大输出 token 数
	ContextWindow   int // 上下文窗口大小
}

ModelDefaults 已知模型的默认参数

func GetModelDefaults

func GetModelDefaults(model string) *ModelDefaults

GetModelDefaults 获取模型默认参数:前缀匹配(最长前缀优先),未知模型返回 nil

type ModelInfo

type ModelInfo struct {
	ID string `json:"id"`
}

ModelInfo 从 API 返回的模型信息

func FetchModels

func FetchModels(providerType, apiBase, apiKey string) ([]ModelInfo, error)

FetchModels 从 API 获取可用模型列表

type PermissionResponse

type PermissionResponse struct {
	Behavior string `json:"behavior"` // "allow" | "deny"
	Message  string `json:"message"`  // deny 原因
}

PermissionResponse 权限响应

type PromptBuilder

type PromptBuilder struct {
	// contains filtered or unexported fields
}

PromptBuilder 动态构建 System Prompt

func NewPromptBuilder

func NewPromptBuilder(language string, context AIContext) *PromptBuilder

NewPromptBuilder 创建 PromptBuilder

func (*PromptBuilder) Build

func (b *PromptBuilder) Build() string

Build 构建运行时上下文 prompt,注入到 cago 模板的 {{.AppendSystem}} 位。 角色身份已经搬到 internal/ai/system_template.go 的模板 intro,此处只输出 每次 Send 都可能变化的动态段(语言 / Tab / 知识 / 错误恢复 / extension SKILL.md)。

func (*PromptBuilder) SetExtensionSkillMDs

func (b *PromptBuilder) SetExtensionSkillMDs(mds map[string]string)

SetExtensionSkillMDs sets all extension SKILL.md contents to inject. Keys are extension names, values are the raw markdown.

type Role

type Role string

Role 消息角色

const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
)

type StreamEvent

type StreamEvent struct {
	Type string `json:"type"` // "content" | "tool_start" | "tool_result" | "approval_request" | "approval_result" | "agent_start" | "agent_end" | "queue_consumed" | "done" | "error" | "thinking" | "thinking_done" | "stopped" | "retry" | "usage" | "compacted"
	// type=retry 时 Content 携带 attempt 序号(字符串形式,从 1 开始),RetryDelayMs 是下一次重试前的等待毫秒(用于前端倒计时同步)。
	Content    string `json:"content,omitempty"`      // type=content/tool_result/approval_result/agent_end 时的文本
	QueueID    string `json:"queue_id,omitempty"`     // type=queue_consumed 时的前端队列项 ID
	ToolName   string `json:"tool_name,omitempty"`    // type=tool_start/tool_result 时的工具名
	ToolInput  string `json:"tool_input,omitempty"`   // type=tool_start 时的输入摘要
	ToolCallID string `json:"tool_call_id,omitempty"` // type=tool_start/tool_result 时的工具调用 ID,前端用于跨 turn 还原 tool_calls 历史
	ConfirmID  string `json:"confirm_id,omitempty"`   // type=approval_request/approval_result 时的确认请求 ID
	Error      string `json:"error,omitempty"`        // type=error 时的错误信息
	AgentRole  string `json:"agent_role,omitempty"`   // type=agent_start/approval_request 时的角色描述
	AgentTask  string `json:"agent_task,omitempty"`   // type=agent_start 时的任务描述
	// approval_request 专用字段
	Kind        string                    `json:"kind,omitempty"`        // "single" | "batch" | "grant" | "local_tool"
	Items       []permission.ApprovalItem `json:"items,omitempty"`       // 审批项列表
	Description string                    `json:"description,omitempty"` // grant 描述
	SessionID   string                    `json:"session_id,omitempty"`  // grant session ID
	Patterns    []string                  `json:"patterns,omitempty"`    // local_tool 默认 pattern 列表(与 sub-commands 对齐),前端预填可编辑
	// type=usage 时的 token 统计(前端累加到当前 assistant 消息)
	Usage *Usage `json:"usage,omitempty"`
	// type=retry 时的等待毫秒;前端据此显示倒计时。0 表示无显式退避(立即重试)。
	RetryDelayMs int `json:"retryDelayMs,omitempty"`
}

StreamEvent 流式响应事件

type SystemConfig

type SystemConfig struct {
	Provider       cagoProvider.Provider
	ProviderEntity *ai_provider_entity.AIProvider
	APIKey         string
	Cwd            string
	SystemPrompt   string
	Model          string
	Tools          []tool.Tool
	LocalToolGate  *aitool.LocalToolGate
}

SystemConfig 组装一个 coding.System 需要的所有依赖。 调用方持有 *coding.System + *agent.Runner,自己处理 Send / Cancel / Steer / Close。

  • Provider:可选;测试场景用 providertest.Mock 注入。非 nil 时优先使用。
  • ProviderEntity / APIKey:生产路径——传给 BuildProvider 构造 cago provider。
  • Cwd:coding.New 必填参数(read/write/bash 等工具的工作目录),通常为 ~/.opskat。
  • SystemPrompt:动态系统提示词,附加到 coding 默认 system 之后。
  • Model:覆盖 provider 默认模型。
  • Tools:额外注册到父 agent 的工具集,通常是 aitool.Tools()。
  • aitool.LocalToolGate:可选,非 nil 时为 bash/write/edit 挂 PreToolUseHook 走用户审批。

type TabInfo

type TabInfo struct {
	Type      string `json:"type"` // "ssh" | "database" | "redis" | "sftp"
	AssetID   int64  `json:"assetId"`
	AssetName string `json:"assetName"`
}

TabInfo 当前打开的 Tab 信息

type ToolCall

type ToolCall struct {
	ID       string       `json:"id"`
	Type     string       `json:"type"` // "function"
	Function ToolFunction `json:"function"`
}

ToolCall AI 发起的工具调用

type ToolFunction added in v1.11.0

type ToolFunction struct {
	Name      string `json:"name"`
	Arguments string `json:"arguments"` // JSON string
}

ToolFunction describes the function selected by an AI tool call. Keep this named so Wails can generate a typed frontend binding for it.

type Usage

type Usage struct {
	InputTokens         int `json:"input_tokens,omitempty"`
	OutputTokens        int `json:"output_tokens,omitempty"`
	CacheCreationTokens int `json:"cache_creation_tokens,omitempty"` // Anthropic cache write
	CacheReadTokens     int `json:"cache_read_tokens,omitempty"`     // Anthropic cache read / OpenAI cached prompt tokens
}

Usage 本轮 LLM 调用的 token 使用情况 语义统一:InputTokens 仅包含本次真正新增的输入;CacheReadTokens / CacheCreationTokens 分开统计缓存命中与缓存写入,便于前端展示和成本核算。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL