Documentation
¶
Overview ¶
Package llm LLM 客户端栈(eino BaseChatModel 之上),本包唯一 package doc。 文件族分工:
- client/errors/config:客户端薄封装——生成失败指数退避+jitter 重试(429 退避 下限提高)、JSON 输出解析失败带错误回喂重试、token 记账(优先响应 usage, 缺省 CJK 感知估算(EstTokensCJK))、fitInput 输入自守恒、可选 token bucket 限速
- provider/openai:Provider 抽象与多后端 + FallbackChain 降级链
- resilient/failover:弹性客户端(降级矩阵/熔断/预算短路)+ 裸模型路径的 FailoverModel 装饰器
- budget/cost/usage:预算、成本、完整用量采集三套记账
- stage_router:按业务阶段路由模型
FailoverModel 模型链 failover 装饰器(eino BaseChatModel/ToolCallingChatModel 双形态)。
Resilient/FallbackChain 只覆盖 agentkit 自己的 Generator 客户端路径;ReAct 主路径 (ADK ChatModelAgent 直调模型)拿到的就是裸 BaseChatModel,没有降级链。本装饰器 填补这一层:按序尝试模型,前一个失败且调用方 ctx 仍存活时切下一个重放同一次请求。
刻意保持薄:不做熔断/限速/预算/同模型重试(那是 Resilient 的职责,二者可叠加—— Resilient.RawModelWithFailover() 即本装饰器包住整条 Provider 链)。切换决策对任何 错误恒真:429/5xx/网络类错误切换必然正确;401/403/上下文超限类确定性错误在模型 同端点时切了也白切,但异端点/异凭证时能救——误切代价仅一次模型调用。 调用方语义的失败(ctx 取消/超时)不切换,原样上抛。
从 bianque 生产装配提炼(v0.9.4);v0.10.9 由主备二元泛化为 N 模型链。
本文件族是 LLM Provider 抽象与多后端支持(Provider 接口 + FallbackChain 模型降级链)。包级总览见 client.go 的唯一 package doc。
流恢复原语(对标 ZCode streaming-recovery.ts + model-errors.ts):turn 级 流式请求的瞬态错误分类、空终态检测、上下文溢出识别与 busy 准入退避。 单请求重试骨架见 errors.go(ClassifyLLMError)与 retryafter.go;本文件补 「流断在哪、什么算瞬态、什么时候放弃」的 turn 级语义。
StageRouter 分阶段模型路由:不同业务阶段(stage)用不同的 Generator—— 大窗口模型吃长输入(R1)、快模型跑判定(R3)、强模型保质量(R4)。
匹配语义:精确命中优先,其次最长前缀(注册 "R1" 可命中 "R1"/"R1a"); 未命中走缺省 Generator。装配示例:
sr := llm.NewStageRouter(defaultGen) // 缺省 = 主链(含预算注入)
sr.Use("R1", r1Gen) // 前缀路由,命中 "R1"/"R1a"
sr.Use("qa", qaGen) // 精确路由
// 之后 sr.Generate(ctx, "R1a", ...) 走 r1Gen
预算:路由不改变预算语义——各 Generator 应由调用方完成预算注入 (BudgetInjector.WithBudget)后再注册;UsedTokens 聚合全部已注册链。
usage.go — LLM 用量采集(eino callbacks)——**全仓唯一的 callbacks 侧记账 出口**:拿得到 Cached/Reasoning tokens、FinishReason、Duration、Iteration。 归因经 Labels(map)透传,领域键由调用方决定。 (v0.10.12 前 obsx.Options.OnUsage 是并行的五数字出口,防重护栏跨包放在 obsx——现护栏归位于本包,obsx 回归纯 trace,ReAct/RawModel 旁路记账统一 走 NewUsageHandler 注入 callbacks。)
Index ¶
- Constants
- Variables
- func BusyAdmissionDelay(retry int) time.Duration
- func IsContextExceededError(err error) bool
- func IsContextExceededFinish(finishReason, rawFinishReason string) bool
- func IsProviderBusyCode(code string) bool
- func IsRateLimitError(err error) bool
- func IsTransientStreamError(err error) bool
- func IsTruncatedError(err error) bool
- func LookupPrice(model string) (Price, PriceLookup)
- func NewUsageHandler(onRecord Sink) callbacks.Handler
- func PriceOf(pricing map[string]Price, model string, prompt, completion int) float64deprecated
- func RetryAfterFrom(ctx context.Context) time.Duration
- func RetryableLLMError(err error) bool
- func SelectRetryDelay(local, hint time.Duration) time.Duration
- func SuspiciousEmptyResult(finishReason string, responseLen, toolCallCount, usageTotal int) bool
- func WithCallCounter(ctx context.Context) context.Context
- func WithRetryAfterSink(ctx context.Context) context.Context
- func WithUsageLabels(ctx context.Context, labels map[string]string) context.Context
- type Attempt
- type AttemptError
- type Budget
- type BudgetConfig
- type BudgetHolder
- type BudgetInjector
- type ChainLink
- type Client
- func (c *Client) BoundBudget() TokenAccountant
- func (c *Client) Generate(ctx context.Context, stage string, msgs []*schema.Message) (*schema.Message, error)
- func (c *Client) GenerateJSON(ctx context.Context, stage string, msgs []*schema.Message, out any) error
- func (c *Client) RawModel() model.BaseChatModel
- func (c *Client) UsedTokens() int
- func (c *Client) WithBudget(b TokenAccountant) Generator
- type CostRecord
- type CostTracker
- type FailoverModel
- func (m *FailoverModel) Generate(ctx context.Context, input []*schema.Message, opts ...einomodel.Option) (*schema.Message, error)
- func (m *FailoverModel) Stream(ctx context.Context, input []*schema.Message, opts ...einomodel.Option) (*schema.StreamReader[*schema.Message], error)
- func (m *FailoverModel) WithTools(tools []*schema.ToolInfo) (einomodel.ToolCallingChatModel, error)
- type FallbackChain
- type Generator
- type MatchKind
- type ModelSummary
- type OpenAIProvider
- func (p *OpenAIProvider) AttemptTimeout() time.Duration
- func (p *OpenAIProvider) ContextTokens() int
- func (p *OpenAIProvider) CostPer1KTokens() (float64, float64)
- func (p *OpenAIProvider) MaxOutputTokens() int
- func (p *OpenAIProvider) Model() model.BaseChatModel
- func (p *OpenAIProvider) ModelName() string
- func (p *OpenAIProvider) Name() string
- type OpenAIProviderConfig
- type Price
- type PriceLookup
- type PriceStatus
- type PriceTable
- type PricedProvider
- type Provider
- type Resilient
- func (r *Resilient) BoundBudget() TokenAccountant
- func (r *Resilient) Generate(ctx context.Context, stage string, msgs []*schema.Message) (*schema.Message, error)
- func (r *Resilient) GenerateJSON(ctx context.Context, stage string, msgs []*schema.Message, out any) error
- func (r *Resilient) LastModel() string
- func (r *Resilient) PrimaryModel() model.BaseChatModel
- func (r *Resilient) RawModel() model.BaseChatModel
- func (r *Resilient) RawModelWithFailover() model.BaseChatModel
- func (r *Resilient) String() string
- func (r *Resilient) UsedTokens() int
- func (r *Resilient) WithBudget(b TokenAccountant) Generator
- type ResilientConfig
- type RetryHint
- type Sink
- type StageRouter
- func (r *StageRouter) Generate(ctx context.Context, stage string, msgs []*schema.Message) (*schema.Message, error)
- func (r *StageRouter) GenerateJSON(ctx context.Context, stage string, msgs []*schema.Message, out any) error
- func (r *StageRouter) RawModel() model.BaseChatModel
- func (r *StageRouter) Use(stage string, gen Generator)
- func (r *StageRouter) UsedTokens() int
- type TokenAccountant
- type UsageCallCounter
- type UsageRecord
Constants ¶
const ( // PricePriced 已按单价算出成本。 PricePriced = port.PricePriced // PriceUnpriced 价格未知(**不等于成本为 0**)。 PriceUnpriced = port.PriceUnpriced // MatchNone 未命中(价格未知)。 MatchNone = port.MatchNone // MatchExact 原名精确命中。 MatchExact = port.MatchExact // MatchNormalized 归一化后命中(去部署前缀 / 去日期后缀)。 MatchNormalized = port.MatchNormalized )
const MaxServerRetryAfter = httpjson.MaxServerRetryAfter
MaxServerRetryAfter 服务端退避建议的可信上限:超过 5 分钟的建议不直接 采信(长时间限流应交由 failover/降级链处置,而非原地等)。
const MaxStreamRecoveryRetries = 10
MaxStreamRecoveryRetries 流断点恢复预算(对标 STREAM_RECOVERY_MAX_RETRIES: 跨过单请求重试边界后只恢复 1 次与用户预期差距过大,10 次是生产调过的上限)。
Variables ¶
BusyAdmissionDelays busy 准入重试的两档退避(对标 START_PLAN_BUSY_MAIN_TURN_ADMISSION_RETRY_DELAYS_MS:1s/2s)。
var ErrPriceUnknown = port.ErrPriceUnknown
ErrPriceUnknown 价表里查不到该模型。
Functions ¶
func BusyAdmissionDelay ¶
BusyAdmissionDelay 按 retry 序号(0 起)取准入退避;越界取最后一档。
func IsContextExceededError ¶
IsContextExceededError 错误文案层识别上下文溢出(provider 报文形态)。
func IsContextExceededFinish ¶
IsContextExceededFinish finishReason 层识别上下文溢出。
func IsProviderBusyCode ¶
IsProviderBusyCode provider 业务码是否「繁忙」类(准入级退避重试的判定; 耗尽后调用方应合成不可重试错误,别无限排队)。
func IsTransientStreamError ¶
IsTransientStreamError 流式 turn 中的错误是否瞬态(值得从安全锚点重开流)。 与 ClassifyLLMError 的分工:后者面向单请求 HTTP 重试;本判定面向 turn 级 恢复——额外覆盖流空闲超时与原因码形态(错误链上任意一层的文案都参与匹配)。 调用方主动取消不算瞬态(与降级矩阵「ctx 取消 → 全链中止」一致):若判为 瞬态,取消后的恢复循环会空转 MaxStreamRecoveryRetries 轮、每轮立即再失败。
func LookupPrice ¶ added in v0.13.0
func LookupPrice(model string) (Price, PriceLookup)
LookupPrice 用内建价表查价。
func NewUsageHandler ¶
NewUsageHandler 构建 eino callbacks.Handler(非流式 OnEnd + 流式 OnEndWithStreamOutput——流式分支在后台 goroutine 排空副本流后回调, ⚠ onRecord 可能被多 goroutine 并发调用,须并发安全(第十轮审计钉约)。 OnEndWithStreamOutput——第八轮审计补齐:eino-ext 流式只触发后者,仅注册 OnEnd 的 handler 在开流(agentrun v0.10.32 起默认)下一条记录都不产生, 静默失明。流式分支消费框架给的副本流取末个非 nil usage,末帧即汇总帧)。 无 TokenUsage、无 RunInfo 的调用静默跳过;Labels 缺省不阻断采集。 防重:Client 自身配置了 OnUsage/Budget 时(Client 记账标记已在 ctx), 本 handler 对同一次物理调用自动跳过——两侧不会双倍记账,可同时启用。 ⚠ agentrun 场景:Config.OnUsage 与本 handler 记同一笔——**择一装配** (两者没有共享防重标记,同时挂会双计)。
func PriceOf
deprecated
PriceOf 按模型名匹配定价表估算单次调用成本(USD;未配价返回 0,不阻塞记账)。
Deprecated: 未配价时返回 0,与「免费模型」不可区分——这正是本仓要根除的静默失真。 改用 PriceTable.Lookup / LookupPrice:查不到时是「价格未知」而非 0。
另注意其「最长前缀回落」会把同主干异价 SKU 静默定价错(gpt-4o-mini 命中 gpt-4o),新查价 API 不再提供该行为。
func RetryAfterFrom ¶
RetryAfterFrom 读取最近一次 429 的服务端建议(0 = 无)。
func SelectRetryDelay ¶
SelectRetryDelay 退避取舍(钳制:建议 ≤5min 或 < 本地曲线才采信)。
func SuspiciousEmptyResult ¶
SuspiciousEmptyResult 空终态检测:零文本 + 零工具调用 + 非 stop 终止 + 零用量 = 可疑空响应(各家 provider 都会偶发;按可重试错误处理而非当正常 完结吞掉)。usageTotal 为 provider 报告的总 token(≤0 视为零用量/未知)。
func WithCallCounter ¶
WithCallCounter ctx 挂轮次计数器(每个 agent 运行作用域一个)。
func WithRetryAfterSink ¶
WithRetryAfterSink 在 ctx 注入 sink(重试环上游调用一次;传输层据此回写)。
Types ¶
type Attempt ¶
type Attempt struct {
Provider string
Model string
// Err error 类型(v0.11 起不再折损为 string):errors.Is/As 经
// AttemptError.Unwrap 可直达 RequestError/context.Canceled 等——下游无须
// 对聚合文案做文本分类(多模型聚合文本里 429 与 401 共存时文本分类结果
// 取决于 marker 优先级,不可靠)。
Err error
Duration time.Duration
}
Attempt 单模型尝试记录(AttemptError 的组成部分)。
type AttemptError ¶
type AttemptError struct {
Stage string
Attempts []Attempt
// BreakerSkipped 被熔断跳过(未真正尝试)的模型数——仅入 Error 文案,
// 区分"链上真失败 N 个"与"另 M 个熔断未试"。
BreakerSkipped int
}
AttemptError 全部模型失败的聚合错误(含每个模型的失败原因链)。
func (*AttemptError) Error ¶
func (e *AttemptError) Error() string
func (*AttemptError) Unwrap ¶
func (e *AttemptError) Unwrap() []error
Unwrap 暴露各模型的原始错误链(Go 多错误 Unwrap 约定):errors.Is/As 穿透 聚合错误直达底层错误类型。
type BudgetConfig ¶
type BudgetConfig struct {
ContextTokens int // 模型上下文窗口(token 数,输入+输出合计)
}
BudgetConfig 模型窗口派生配置("单一基准"哲学:窗口是唯一基准,全部上限按比例派生)。
func (BudgetConfig) MaxDiffChars ¶
func (c BudgetConfig) MaxDiffChars() int
MaxDiffChars diff 总量上限(字符):窗口 35% 折算字符,min 10k。 chars/token 按 4 估算(中文按 2 保守,见 fitInput)。
func (BudgetConfig) MaxOutputTokens ¶
func (c BudgetConfig) MaxOutputTokens() int
MaxOutputTokens 单次生成上限:max(4096, 窗口 5%)。
func (BudgetConfig) ReqBodyMaxChars ¶
func (c BudgetConfig) ReqBodyMaxChars() int
ReqBodyMaxChars 需求文本上限:窗口 6%。
func (BudgetConfig) ReqIssueMaxChars ¶
func (c BudgetConfig) ReqIssueMaxChars() int
ReqIssueMaxChars issue 文本上限:窗口 1.5%。
func (BudgetConfig) TaskTokenBudget ¶
func (c BudgetConfig) TaskTokenBudget() int
TaskTokenBudget 任务 token 预算:窗口 60%。
type BudgetHolder ¶
type BudgetHolder interface {
BoundBudget() TokenAccountant
}
BudgetHolder 可选接口:暴露 Generator 绑定的预算累计器。 聚合方(StageRouter.UsedTokens)按其指针身份去重——各链共享同一 Budget 时 逐链求和会按链数倍增。
type BudgetInjector ¶
type BudgetInjector interface {
WithBudget(b TokenAccountant) Generator
}
BudgetInjector 可选接口:支持运行时注入任务级预算(Dispatcher fan-out 场景)。 调用方通过类型断言检测;不实现则预算保持构造时的值。
type ChainLink ¶
type ChainLink struct {
Model einomodel.BaseChatModel
Name string
}
ChainLink failover 链单链节:模型 + 观测名(结构化链节消平行切片错位—— names[i] 对不上 models[i] 是装配期静默事故)。
type Client ¶
type Client struct {
Model model.BaseChatModel
ModelName string
// 模型窗口(token,输入+输出合计)与单次输出预留。
ContextTokens int
MaxOutputTokens int
// OnUsage 每次调用后的 token 记账回调(stage, promptTokens, completionTokens)。
OnUsage func(stage string, prompt, completion int)
// OnUsageDetailed 每次调用后的完整用量回调(含前缀缓存命中量等)。
//
// 与 OnUsage 并行触发、两者可同置:OnUsage 保持旧签名不破坏;需要 cached 的
// 消费方(成本缓存折扣、命中率观测)用本回调。UsageRecord.CachedTokens 是
// 命中前缀缓存的 prompt token(DeepSeek/MiMo 等端点的 PromptTokenDetails)。
OnUsageDetailed func(stage string, rec UsageRecord)
// Budget 任务 token 预算累计器(可 nil)。
Budget TokenAccountant
// Limiter 全局 LLM 请求限速器(token bucket);nil = 不限速。
Limiter *rate.Limiter
MaxRetries int // 最大尝试次数(含首次),默认 3
BaseDelay time.Duration // 指数退避基准,默认 2s
MaxDelay time.Duration // 退避上限,默认 30s
}
Client LLM 客户端(带重试、限速、预算、fitInput)。
func NewClient ¶
func NewClient(m model.BaseChatModel, modelName string, budget TokenAccountant) *Client
NewClient 创建 LLM 客户端。
func (*Client) BoundBudget ¶
func (c *Client) BoundBudget() TokenAccountant
BoundBudget 返回绑定的预算累计器(可 nil)。BudgetHolder 接口实现。
func (*Client) Generate ¶
func (c *Client) Generate(ctx context.Context, stage string, msgs []*schema.Message) (*schema.Message, error)
Generate 带重试的普通生成(指数退避 + jitter)。 确定性失败(鉴权/权限/上下文超限)不重试;429 退避下限提高后值得等 (单客户端无备选可切)。stage 同时注入 ctx(obsx.WithStage)——eino callbacks handler 可读到业务阶段。
func (*Client) GenerateJSON ¶
func (c *Client) GenerateJSON(ctx context.Context, stage string, msgs []*schema.Message, out any) error
GenerateJSON 生成并解析 JSON;解析失败把原始输出与错误回喂重试 1 次。 out 必须是 *T。截断在重试核心内已转为错误返回,走到这里的解析失败只会是 json.Unmarshal 错误——回喂提示只有"输出合法 JSON"一种。 刻意走 ExtractJSON+严格 Unmarshal 而非 jsonrepair.Unmarshal 宽容链:宽容修复 会改变输出内容,而本方法的失败路径要回喂重试,lastErr 须是纯解析错误。 宽容回收半损坏输出归 jsonrepair.Unmarshal(无重试回喂的场景用)。
func (*Client) RawModel ¶
func (c *Client) RawModel() model.BaseChatModel
RawModel 返回底层 eino 模型(Generator 接口实现)。
func (*Client) UsedTokens ¶
UsedTokens 返回累计消耗 token 数(无预算绑定时返回 0)。
func (*Client) WithBudget ¶
func (c *Client) WithBudget(b TokenAccountant) Generator
WithBudget Client 的预算注入:浅拷贝(Client 只含无锁值字段,Budget 指针共享是有意为之)。
type CostTracker ¶
type CostTracker = port.CostTracker
CostTracker token 成本追踪器。
func NewCostTrackerWithTable ¶ added in v0.13.0
func NewCostTrackerWithTable(table *port.PriceTable) *CostTracker
NewCostTrackerWithTable 创建成本追踪器(指定价表)。
type FailoverModel ¶
type FailoverModel struct {
// OnFailover 切换观测回调(from/to 模型名 + reason=失败模型错误;nil=静默)。
OnFailover func(from, to, reason string)
// contains filtered or unexported fields
}
FailoverModel 模型链装饰器(构建后只读;OnFailover 构建期设置)。
func NewFailoverModel ¶
func NewFailoverModel(links ...ChainLink) *FailoverModel
NewFailoverModel 模型链按序 failover 装饰(至少一节;主备二元传两节即可)。 nil 模型会在被尝试到时报"failover 链含 nil 模型"错误并继续下一个。
func (*FailoverModel) Generate ¶
func (m *FailoverModel) Generate(ctx context.Context, input []*schema.Message, opts ...einomodel.Option) (*schema.Message, error)
Generate 按链序尝试;失败且 ctx 存活(非调用方取消/超时)时切下一个。 全部失败上抛末个模型的错误——此前各模型的失败经 OnFailover 回调可见。
func (*FailoverModel) Stream ¶
func (m *FailoverModel) Stream(ctx context.Context, input []*schema.Message, opts ...einomodel.Option) (*schema.StreamReader[*schema.Message], error)
Stream 与 Generate 同一降级语义(首块前失败才可切换;已在流中失败无法换模型重放)。
func (*FailoverModel) WithTools ¶
func (m *FailoverModel) WithTools(tools []*schema.ToolInfo) (einomodel.ToolCallingChatModel, error)
WithTools 工具绑定转发:链上各模型分别派生带工具的实例后重新包装(不可变派生, 并发安全语义与 eino ToolCallingChatModel 契约一致)。
type FallbackChain ¶
type FallbackChain struct {
Providers []Provider
}
FallbackChain 模型降级链:按优先级尝试多个 Provider。
func NewFallbackChain ¶
func NewFallbackChain(providers ...Provider) *FallbackChain
NewFallbackChain 创建降级链。
func (*FallbackChain) Fallback ¶
func (fc *FallbackChain) Fallback() []Provider
Fallback 返回备选 Provider 列表(不含主 Provider)。
func (*FallbackChain) Primary ¶
func (fc *FallbackChain) Primary() Provider
Primary 返回主 Provider(优先级最高)。
type Generator ¶
type Generator interface {
Generate(ctx context.Context, stage string, msgs []*schema.Message) (*schema.Message, error)
GenerateJSON(ctx context.Context, stage string, msgs []*schema.Message, out any) error
// UsedTokens 返回该客户端累计消耗的 token 数(无预算绑定时返回 0)。
UsedTokens() int
// RawModel 返回底层 eino 模型(供 ReAct agent 等需要裸模型的场景;
// Resilient 返回主模型——降级仅覆盖 Generate/GenerateJSON 路径)。
RawModel() model.BaseChatModel
}
Generator LLM 生成接口:*Client 与 *Resilient 都满足,调用方无感切换。
type OpenAIProvider ¶
type OpenAIProvider struct {
// contains filtered or unexported fields
}
OpenAIProvider OpenAI 兼容 LLM 提供者。 支持 OpenAI / 百炼 / DeepSeek / 任何 OpenAI 兼容端点。
func NewOpenAIProvider ¶
func NewOpenAIProvider(ctx context.Context, cfg OpenAIProviderConfig) (*OpenAIProvider, error)
NewOpenAIProvider 创建 OpenAI 兼容提供者。
func (*OpenAIProvider) AttemptTimeout ¶
func (p *OpenAIProvider) AttemptTimeout() time.Duration
AttemptTimeout 单次尝试超时(Resilient 降级链按 Provider 独立限时)。
func (*OpenAIProvider) ContextTokens ¶
func (p *OpenAIProvider) ContextTokens() int
func (*OpenAIProvider) CostPer1KTokens ¶
func (p *OpenAIProvider) CostPer1KTokens() (float64, float64)
func (*OpenAIProvider) MaxOutputTokens ¶
func (p *OpenAIProvider) MaxOutputTokens() int
func (*OpenAIProvider) Model ¶
func (p *OpenAIProvider) Model() model.BaseChatModel
func (*OpenAIProvider) ModelName ¶
func (p *OpenAIProvider) ModelName() string
func (*OpenAIProvider) Name ¶
func (p *OpenAIProvider) Name() string
type OpenAIProviderConfig ¶
type OpenAIProviderConfig struct {
BaseURL string
APIKey string
Model string
ContextTokens int // 模型上下文窗口(默认 128k)
MaxOutputTokens int // 单次输出上限(默认从窗口派生)
Timeout time.Duration // 请求超时(默认 60s)
// CaptureRetryAfter 捕获 429 响应的 Retry-After 头(批次四十五,对标 ZCode
// runner-retry 的服务端退避建议):经 ctx sink 透出给重试环的退避取舍。
// 需要 http.Client 注入(Timeout 语义由注入的 Client 自带保留)。
CaptureRetryAfter bool
// HTTPClient 注入自定义 HTTP 客户端(**可选**,nil = 走缺省)。
//
// 为什么需要:出站守卫要真正闭合「写面校验 → 拨号」的 DNS rebinding 窗口,
// 守卫必须挂在 Transport.DialContext 上——写面校验(按 URL 判)拦不住
// 「解析与拨号之间权威 DNS 改口」。而 LLM 的 base_url 是**用户可配**的
// (平台代发 LLM 请求的目标),是这条链上暴露最大的一个面。
//
// 语义:**显式注入优先于内部构造**。注入后由注入的 Client 完全接管超时
// (Timeout 语义由注入的 Client 自带保留;eino 侧同一口径——设了 HTTPClient
// 就不再用 config.Timeout),本仓不再叠加自己的 Timeout。
// 注入的 Client 的 Transport 为 nil 时由 net/http 回落 http.DefaultTransport。
//
// 与 CaptureRetryAfter 可叠加:注入的同时要捕获 Retry-After 时,本仓在
// **注入的 Transport 外面**套一层 RetryAfterTransport——守卫与退避建议
// 捕获不再互相排斥(此前二者是排斥关系:自建 client 走 http.DefaultTransport,
// 宿主的守卫挂不上去)。
//
// 宿主侧参考(agent-harness 的 netguard):
//
// hc, err := netguard.NewClient(nil, netguard.Allow{Private: true, Loopback: true})
// cfg.HTTPClient = hc // hc.Transport 挂了 netguard.DialFunc
HTTPClient *http.Client
CostPer1KPrompt float64 // 每 1000 prompt token 成本(美元)
CostPer1KCompletion float64 // 每 1000 completion token 成本(美元)
}
OpenAIProviderConfig OpenAI 提供者配置。
type Price ¶
Price 单价(美元 / 每 100 万 token)。
func LookupPriceStrict ¶ added in v0.13.0
LookupPriceStrict 查价,查不到返回 ErrPriceUnknown。
func PriceOfMatched
deprecated
PriceOfMatched PriceOf 的可观测形态:返回命中的定价键与是否经前缀回落。
前缀回落对「同主干异价 SKU」(gpt-4o 前缀命中 gpt-4o-mini)会静默误定价 ——宿主可在装配期对回落命中做校验/上报(第十轮审计)。
Deprecated: 同 PriceOf——未配价返回 0 不可区分「免费」,前缀回落会误定价。 改用 PriceTable.Lookup(返回 PriceLookup,命中方式可见且不做前缀回落)。
type PriceLookup ¶ added in v0.13.0
type PriceLookup = port.PriceLookup
PriceLookup 查价结果:命中的键 + 命中方式。
type PriceTable ¶ added in v0.13.0
type PriceTable = port.PriceTable
PriceTable 价表:内建价 + 调用方覆盖。
func DefaultPriceTable ¶ added in v0.13.0
func DefaultPriceTable() *PriceTable
DefaultPriceTable 返回内建价表(无覆盖)。
func NewPriceTable ¶ added in v0.13.0
func NewPriceTable(overrides map[string]Price) *PriceTable
NewPriceTable 造一份价表:内建价 + overrides 覆盖。
覆盖键与内建表走**同一套归一化规则**(折算成基座形态再入库),所以覆盖 `mimo-v2.5-pro` 与覆盖 `mimo-v2.5-pro-20250101` 是等价的——同一基座上的 多个覆盖键按固定的优先级规则只留一个生效(详见 port.NewPriceTable 的文档)。
type PricedProvider ¶ added in v0.13.0
type PricedProvider interface {
Provider
// ModelPricing 返回该模型的单价与是否已定价。
// 返回 (Price{}, false) 表示「未定价」,llm 会继续回落到价表。
ModelPricing() (Price, bool)
}
PricedProvider 可选扩展接口:Provider **显式**给出单价。
这是「模型档案带 pricing」的口子:实现了它的 Provider,其单价**优先于** llm 内建价表;没实现时 llm 才会按模型名去价表里查。
单价单位是**美元 / 每 100 万 token**(Price),与 CostPer1KTokens 的 每 1K 不同——两套单位并存容易错账一个数量级,所以新实现方用这个接口。
type Provider ¶
type Provider interface {
// Name 提供者名称(如 "openai", "anthropic", "ollama")。
Name() string
// Model 返回 eino ChatModel 实例。
Model() model.BaseChatModel
// ModelName 返回模型标识(如 "gpt-4o", "deepseek-v4")。
ModelName() string
// ContextTokens 返回模型上下文窗口大小(token 数)。
ContextTokens() int
// MaxOutputTokens 返回单次生成输出上限。
MaxOutputTokens() int
// CostPer1KTokens 返回每 1000 token 的成本(美元)。
// 用于成本追踪,返回 0 表示不计费。
//
// ⚠️ 返回 0 有歧义:「这个模型免费」与「没配价」分不开。llm 层的定价
// 会把 0 视为「未指定」,回落到内建价表(PriceTable);价表也查不到时
// 记为**价格未知**(CostRecord.PriceStatus = PriceUnpriced),不是 0 成本。
// 想显式声明单价(折扣价/加价率/人民币折算价)见 PricedProvider。
CostPer1KTokens() (prompt, completion float64)
}
Provider LLM 提供者接口:抽象模型创建与配置。
type Resilient ¶
type Resilient struct {
// Budget 任务 token 预算(nil = 不限)。耗尽时全链短路。
Budget TokenAccountant
// Limiter 全局限速器(所有模型共享;nil = 不限速)。
Limiter *rate.Limiter
// Tracker 成本追踪器(nil = 不追踪)。记账跟随实际执行的模型。
Tracker *CostTracker
// PriceTable 价表(nil = llm 内建价表)。
//
// 单价解析优先级:Provider 显式给价(PricedProvider / CostPer1KTokens)
// > 按模型名查本表。**都查不到时记为「价格未知」**(CostRecord.PriceStatus
// = PriceUnpriced),不是 0 成本——否则成本看板恒为 0 而无人察觉。
PriceTable *PriceTable
// OnUsage token 使用回调(model = 实际执行的模型)。
OnUsage func(model, stage string, prompt, completion int)
// OnFallback 降级事件回调(from = 失败模型,to = 切换到的模型)。
OnFallback func(from, to, stage, reason string)
// contains filtered or unexported fields
}
Resilient 弹性 LLM 客户端。
与 *Client 方法签名一致(满足 Generator),可作 drop-in 替换。 额外能力:多模型降级、按模型熔断、预算短路、每模型窗口自适应 fitInput。
func NewResilient ¶
func NewResilient(chain *FallbackChain, cfg ResilientConfig) (*Resilient, error)
NewResilient 创建弹性客户端。空 Provider 列表报错。
func (*Resilient) BoundBudget ¶
func (r *Resilient) BoundBudget() TokenAccountant
BoundBudget 返回绑定的预算累计器(可 nil)。BudgetHolder 接口实现。
func (*Resilient) Generate ¶
func (r *Resilient) Generate(ctx context.Context, stage string, msgs []*schema.Message) (*schema.Message, error)
Generate 弹性生成:按链序尝试各 Provider,全部失败返回 *AttemptError。
func (*Resilient) GenerateJSON ¶
func (r *Resilient) GenerateJSON(ctx context.Context, stage string, msgs []*schema.Message, out any) error
GenerateJSON 弹性 JSON 生成:每模型内部含解析失败回喂重试,仍败切换下一模型。
func (*Resilient) PrimaryModel ¶
func (r *Resilient) PrimaryModel() model.BaseChatModel
PrimaryModel 返回主模型的 BaseChatModel(供 eino agent 工具表使用)。
func (*Resilient) RawModel ¶
func (r *Resilient) RawModel() model.BaseChatModel
RawModel Generator 接口实现:等价 RawModelWithFailover()——裸模型路径同样获得 链式降级(v0.10.9 起不再返回未装饰的主模型;要主模型裸实例用 PrimaryModel)。 ⚠ 此路径不受 Budget/Tracker/熔断约束(failover 薄切换刻意不做记账——ReAct 主路径依赖此形态时预算可被烧穿、Tracker 对该路径消耗记零;需要硬上限时勿把 RawModel 交给不受限消费方,或消费方自行在回调侧记账)。
func (*Resilient) RawModelWithFailover ¶
func (r *Resilient) RawModelWithFailover() model.BaseChatModel
RawModelWithFailover 把整条 Provider 链包装成一个带 failover 的 BaseChatModel: ReAct 等裸模型路径按链序降级,不再静默丢弃降级链(此前 RawModel 返回未装饰的 主模型——链/熔断只覆盖 Generate/GenerateJSON,两条路径两套命运)。语义与 FailoverModel 一致:薄切换(无熔断/限速/预算/同模型重试),全败上抛末个模型错误, 切换经 OnFallback 观测。
func (*Resilient) UsedTokens ¶
UsedTokens Generator 接口实现:返回预算累计消耗。
func (*Resilient) WithBudget ¶
func (r *Resilient) WithBudget(b TokenAccountant) Generator
WithBudget Resilient 的预算注入:显式构造新实例(Resilient 含互斥字段不可浅拷贝)。 chain/breakers 共享原实例(熔断状态跨任务全局),Budget/Tracker 指向任务级。
type ResilientConfig ¶
type ResilientConfig struct {
// RetriesPerModel 每模型重试次数(不含首次),默认 2。
RetriesPerModel int
// BreakerTrip 模型熔断阈值(连续失败次数),默认 3。
BreakerTrip int
// BreakerCooldown 模型熔断冷却期,默认 5 分钟。
BreakerCooldown time.Duration // BaseDelay 重试退避基准,默认 2s(测试可调小)。
BaseDelay time.Duration
// MaxDelay 重试退避上限,默认 30s。
MaxDelay time.Duration
// BreakerProbeTimeout 熔断半开探测的单次时限。缺省按「单模型一次完整尝试」
// 推导:(RetriesPerModel+1) 次尝试 × (MaxDelay + 90s 预算)。该值必须覆盖
// tryOneProvider 的最长在途时间——否则 Breakers 会把超时探测判为失联并
// 放行并发新探测,半开「单探测隔离」失效(v0.10.28 审计修复)。
BreakerProbeTimeout time.Duration
}
ResilientConfig 弹性客户端配置。
type RetryHint ¶
type RetryHint struct {
Retryable bool // 是否值得重试
IsRateLimit bool // 是否 429/rate limit
IsTruncated bool // 是否输出截断
}
RetryHint 错误分类结果。
func ClassifyLLMError ¶
ClassifyLLMError 判定是否值得重试及错误类型。 分类优先级:结构化 HTTP 状态码(go-openai RequestError)→ 文本 marker。 确定性失败(鉴权/参数错/模型不存在/上下文超限等 4xx)不重试——重试纯浪费 钱和延迟;429 可重试 + 退避下限提高;5xx/网络错误默认可重试。
type StageRouter ¶
type StageRouter struct {
// contains filtered or unexported fields
}
StageRouter 按 stage 分发的 Generator(Generator 接口实现,可无感嵌入现有装配)。
func NewStageRouter ¶
func NewStageRouter(def Generator) *StageRouter
NewStageRouter 创建阶段路由器(def = 未命中阶段的缺省 Generator)。 def 为 nil panic——构造期配置错误尽早暴露(与 toolprior.Table.Add 同纪律), 运行期才炸的 nil 难排查。
func (*StageRouter) GenerateJSON ¶
func (*StageRouter) RawModel ¶
func (r *StageRouter) RawModel() model.BaseChatModel
RawModel 缺省链的底层模型(StageRouter 本身不做 ReAct——ReAct 需要哪个模型 由调用方显式指定)。
func (*StageRouter) Use ¶
func (r *StageRouter) Use(stage string, gen Generator)
Use 注册阶段路由。stage 为精确名("qa")或前缀("R1" 命中 "R1a"); 重名注册覆盖旧路由。
func (*StageRouter) UsedTokens ¶
func (r *StageRouter) UsedTokens() int
UsedTokens 全部链(缺省 + 已注册)的预算累计和——任务预算口径下各链共享 同一 Budget 时即任务总消耗。暴露 BudgetHolder 的链按预算指针身份去重, 避免共享预算被按链数倍增;未暴露的链退回逐链求和。
type UsageCallCounter ¶
type UsageCallCounter struct {
// contains filtered or unexported fields
}
UsageCallCounter 单作用域 LLM 调用轮次计数(原子递增)。
type UsageRecord ¶
type UsageRecord struct {
Model string
PromptTokens int
CachedTokens int
CompletionTokens int
ReasoningTokens int
TotalTokens int
DurationMS int64
FinishReason string
Iteration int64 // 本作用域第几次 LLM 调用(1 起;无计数器为 0)
Labels map[string]string // 归因标签(session/case/step/agent 等,调用方自定)
}
UsageRecord 单次 LLM 调用的用量记录。
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package decision 是 **System One 协议**(非自回归判定模型)的客户端。
|
Package decision 是 **System One 协议**(非自回归判定模型)的客户端。 |
|
Package llm 是历史 import 路径 github.com/oagentkit/llm/llm 的**兼容层**。
|
Package llm 是历史 import 路径 github.com/oagentkit/llm/llm 的**兼容层**。 |
|
Package llmtest eino ChatModel/Provider 的脚本化测试桩——llm/reflection/router 等包的测试共用(此前四份手写桩已漂移出两种耗尽语义与多种记录口径)。
|
Package llmtest eino ChatModel/Provider 的脚本化测试桩——llm/reflection/router 等包的测试共用(此前四份手写桩已漂移出两种耗尽语义与多种记录口径)。 |
|
Package obsx 可观测性扩展:对齐 eino callbacks 体系的 LLM 调用追踪。
|
Package obsx 可观测性扩展:对齐 eino callbacks 体系的 LLM 调用追踪。 |
|
Package port 是 oagentkit/llm 的**窄接口层**——本仓不依赖 eino 的那一半。
|
Package port 是 oagentkit/llm 的**窄接口层**——本仓不依赖 eino 的那一半。 |
|
输出截断续写(对标 ZCode turn-output-token-continuation):finish reason 为 length/max_tokens 且消息无工具调用时,保留部分输出并以「直接续写」指令再调, 内容顺序拼接,最多 maxContinuations 轮。
|
输出截断续写(对标 ZCode turn-output-token-continuation):finish reason 为 length/max_tokens 且消息无工具调用时,保留部分输出并以「直接续写」指令再调, 内容顺序拼接,最多 maxContinuations 轮。 |