Documentation
¶
Overview ¶
Package delta 增量节流器(真流式观测面)。
节流窗口 / 总量帽 / seq 单调 / 权威收口语义的**基础机制在 oagentkit/runtime/pacer**; 本包提供其上的编排层:
- (kind, iteration) 键控:同一轮内的 reasoning / text 各自成流;
- 惰性建流(构造后可注入时钟,建流待首次使用);
- 收口扫序:轮升序 × reasoning→text,保证事件落序确定性。
语义约定(同轮权威快照优先):权威整段事件到达即 absorb 缓冲(回放/终稿以权威 为准);调用轮没有权威正文快照,残尾只能靠 FlushAll 冲刷落地。
单 goroutine 契约:调用方的 OnEvent 在 drain 循环内同步回调(不阻塞不并发), 与 pairer/obs 同一约束面,故不加锁——**换宿主时须确认该契约仍成立**。
生命周期是「单个环节」,不是「整个进程」 ¶
entries 按 (kind, iteration) 惰性建流且**从不回收**, FlushAll 的扫序上界又是 `max(iteration)`——这两处单独看都像缺陷(前者无界增长,后者按 max 空转)。 它们安全**只因为一个外部不变量**:宿主每个 LLM 环节新建一个 Pacer,在该环节的 defer 里 FlushAll 收口后整体丢弃;iteration 是该环节内的局部计数器,不是 模型可控值、也不跨环节累加(见 bianque `steps_llm.go` 的 `round`)。
所以本包**不能**被改成长生命周期对象(如「整个会话一个 Pacer」)而无人察觉: 那会同时引爆上面两条,且症状是「跑久了内存涨 / 收口卡住」,极难归因。 改宿主接线前先看这一段。
Index ¶
Constants ¶
const ( // FlushInterval 节流窗口:距上次发布 ≥ 该值或缓冲 ≥ FlushBytes 才发布。 FlushInterval = 400 * time.Millisecond // FlushBytes 缓冲字节下限(达到即发,不必等窗口)。 FlushBytes = 200 // CapBytes 单轮单类增量发布总量帽(深度思考可极长),超帽静默停发。 CapBytes = 64 << 10 )
const ( // ThinkOpenTag 推理段起始标签(缺省)。 ThinkOpenTag = "<think>" // ThinkCloseTag 推理段结束标签(缺省)。 ThinkCloseTag = "</think>" )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Pacer ¶
type Pacer struct {
// contains filtered or unexported fields
}
Pacer 按 (kind, iteration) 键控的增量节流集合。
type Pub ¶
Pub 一次待发布增量:seq 在 (kind, iteration) 内单调自 1 递增,text 为合并分片 (非累积快照——消费方按序追加,避免长文本 O(n²) 重发)。
type Splitter ¶
type Splitter struct {
// contains filtered or unexported fields
}
Splitter 跨分片标签分流器。零值不可用,请用 NewSplitter。
单 goroutine 契约:与 Pacer 同约束(调用方串行驱动),故不加锁。
func NewSplitterTags ¶
NewSplitterTags 构造分流器并指定标签对(宿主用别的推理标记时用)。 任一标签为空则回落缺省值——空标签会让「后缀是标签前缀」恒真, 结果是内容永远扣住不发,是最难查的一类挂起。
func (*Splitter) Feed ¶
Feed 吃进一个分片,返回本次**可安全发出**的推理段与正文段。
返回值可能为空——那不是丢内容,是扣在 pending 里等下一个分片。 扣住的上界是标签长度减一(标签本体一旦完整就会被消费,不会被扣)。
func (*Splitter) InReasoning ¶
InReasoning 当前是否处于推理段(供调用方决定归属,扣住的尾巴也在段内)。