processStream 处理流式响应
(body io.ReadCloser, chunkCh chan<- StreamChunk)
| 360 | |
| 361 | // processStream 处理流式响应 |
| 362 | func (gp *GLMProvider) processStream(body io.ReadCloser, chunkCh chan<- StreamChunk) { |
| 363 | defer close(chunkCh) |
| 364 | defer func() { _ = body.Close() }() |
| 365 | |
| 366 | ctx := context.Background() // 用于日志记录 |
| 367 | |
| 368 | scanner := bufio.NewScanner(body) |
| 369 | eventCount := 0 |
| 370 | for scanner.Scan() { |
| 371 | line := scanner.Text() |
| 372 | |
| 373 | // SSE格式: "data: {...}" |
| 374 | if !strings.HasPrefix(line, "data: ") { |
| 375 | // 记录非数据行(用于调试) |
| 376 | if strings.TrimSpace(line) != "" && !strings.HasPrefix(line, ":") { |
| 377 | glmLog.Debug(ctx, "non-data line", map[string]any{"line": line}) |
| 378 | } |
| 379 | continue |
| 380 | } |
| 381 | |
| 382 | data := strings.TrimPrefix(line, "data: ") |
| 383 | |
| 384 | // 忽略特殊标记 |
| 385 | if data == "[DONE]" { |
| 386 | glmLog.Debug(ctx, "received [DONE] marker", nil) |
| 387 | break |
| 388 | } |
| 389 | |
| 390 | // 解析JSON |
| 391 | var event map[string]any |
| 392 | if err := json.Unmarshal([]byte(data), &event); err != nil { |
| 393 | glmLog.Debug(ctx, "failed to parse JSON", map[string]any{"error": err, "data": data}) |
| 394 | continue |
| 395 | } |
| 396 | |
| 397 | eventCount++ |
| 398 | glmLog.Debug(ctx, "stream event", map[string]any{"event_num": eventCount, "event": event}) |
| 399 | |
| 400 | chunk := gp.parseStreamEvent(event) |
| 401 | if chunk != nil { |
| 402 | glmLog.Debug(ctx, "parsed chunk", map[string]any{"type": chunk.Type, "index": chunk.Index}) |
| 403 | chunkCh <- *chunk |
| 404 | } else { |
| 405 | glmLog.Debug(ctx, "no chunk parsed from event", nil) |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | if err := scanner.Err(); err != nil { |
| 410 | glmLog.Error(ctx, "scanner error", map[string]any{"error": err}) |
| 411 | } |
| 412 | |
| 413 | glmLog.Debug(ctx, "processed events", map[string]any{"total": eventCount}) |
| 414 | } |
| 415 | |
| 416 | // parseStreamEvent 解析流式事件 |
| 417 | func (gp *GLMProvider) parseStreamEvent(event map[string]any) *StreamChunk { |
no test coverage detected