Skip to content

pref: 事件流信息缺口与废弃函数清理 #134

Description

@phantom5099

背景

当前 NeoCode 采用事件流架构,Provider 通过 StreamEvent 向 Runtime 推送流式事件。这种设计允许 TUI 实时展示模型推理过程,同时为未来的完全流式化改造(去掉 ChatResponse 返回值)奠定基础。

在审查事件流的完整性时,发现两个问题:

  1. Provider 内部仍保留已废弃的辅助函数
  2. 事件流存在信息缺口,导致 Runtime 无法从事件重建完整消息

之前存在的问题

问题 1:mergeToolCallDeltas 函数已废弃但仍保留

该函数在之前的重构中已被 consumeStream 内联处理替代,不再被任何代码调用,但仍保留在代码库中:

  • 占用约 27 行代码
  • 相关测试 TestMergeToolCallDeltasTestMergeToolCallDeltasEdgeCases 占用约 150 行
  • 可能误导后续维护者

问题 2:事件流信息缺口,无法关联 tool_call_start 和 tool_call_delta

现象

// tool_call_start 事件(原先)
StreamEvent{
    Type:          StreamEventToolCallStart,
    ToolCallID:    "call_123",
    ToolName:      "edit_file",
    ToolCallIndex: -1,  // ❌ 强制为 -1,丢失了真实 index
}

// tool_call_delta 事件
StreamEvent{
    Type:               StreamEventToolCallDelta,
    ToolCallIndex:      0,
    ToolArgumentsDelta: "{\"path\":\"a.go\"}",
    // ❌ 没有 ToolCallID
}

问题

  • tool_call_start 有 ID 和 name,但缺少 index
  • tool_call_delta 有 index 和 arguments,但缺少 ID
  • 两个事件无法通过 index 关联

影响
如果未来要删除 ChatResponse 返回值,让 Runtime 完全从事件流重建 Message

// Runtime 需要自己累积事件
accumulator := newStreamAccumulator()
for event := range events {
    accumulator.consume(event)
}
message := accumulator.buildMessage()  // ❌ 无法正确重建

Runtime 无法知道某个 tool_call_delta 属于哪个 tool call,因为:

  • 创建 toolCalls map 条目时(tool_call_start)不知道 index
  • 追加 arguments 时(tool_call_delta)需要用 index 查找对应条目

问题 3:注释与实现不一致

StreamEvent.ToolCallIndex 的注释声明:

ToolCallIndex int `json:"tool_call_index"` // 非 tool_call_delta 时必须强制赋值为 -1

这个设计基于错误假设:"只有 tool_call_delta 需要 index"。实际上 tool_call_start 也需要 index 来定位 tool call 的位置。

预期解决方式

解决方案 1:删除废弃代码

  • 删除 mergeToolCallDeltas 函数
  • 删除 TestMergeToolCallDeltas 测试
  • 删除 TestMergeToolCallDeltasEdgeCases 测试
  • 更新相关注释

解决方案 2:修复事件流信息缺口

修改 emitToolCallStart 函数签名,添加 index 参数:

// 修改前
func emitToolCallStart(ctx context.Context, events chan<- StreamEvent, id, name string) error

// 修改后
func emitToolCallStart(ctx context.Context, events chan<- StreamEvent, index int, id, name string) error

修复后的事件流:

// tool_call_start 事件(修复后)
StreamEvent{
    Type:          StreamEventToolCallStart,
    ToolCallIndex: 0,              // ✅ 有真实 index
    ToolCallID:    "call_123",
    ToolName:      "edit_file",
}

// tool_call_delta 事件
StreamEvent{
    Type:               StreamEventToolCallDelta,
    ToolCallIndex:      0,         // ✅ 可以通过 index 关联
    ToolArgumentsDelta: "{\"path\":\"a.go\"}",
}

解决方案 3:优化事件结构

重新组织 StreamEvent 字段,按事件类型分组并更新注释:

type StreamEvent struct {
    Type StreamEventType

    // text_delta
    Text string `json:"text,omitempty"`

    // tool_call_start / tool_call_delta
    ToolCallIndex      int    `json:"tool_call_index,omitempty"`       // 工具调用索引
    ToolCallID         string `json:"tool_call_id,omitempty"`          // 工具调用 ID
    ToolName           string `json:"tool_name,omitempty"`             // 工具名称
    ToolArgumentsDelta string `json:"tool_arguments_delta,omitempty"`  // 参数增量

    // message_done
    FinishReason string `json:"finish_reason,omitempty"`
    Usage        *Usage `json:"usage,omitempty"`
}

同时清理不必要的字段设置:

  • emitTextDelta 不再设置 ToolCallIndex
  • emitMessageDone 不再设置 ToolCallIndex

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions