(t *testing.T)
| 478 | } |
| 479 | |
| 480 | func TestDebugModel_Stream(t *testing.T) { |
| 481 | t.Parallel() |
| 482 | |
| 483 | ctrl := gomock.NewController(t) |
| 484 | db := dbmock.NewMockStore(ctrl) |
| 485 | chatID := uuid.New() |
| 486 | ownerID := uuid.New() |
| 487 | runID := uuid.New() |
| 488 | errPart := xerrors.New("chunk failed") |
| 489 | parts := []fantasy.StreamPart{ |
| 490 | {Type: fantasy.StreamPartTypeTextDelta, Delta: "hel"}, |
| 491 | {Type: fantasy.StreamPartTypeToolCall, ID: "tool-call-1", ToolCallName: "tool"}, |
| 492 | {Type: fantasy.StreamPartTypeSource, ID: "source-1", URL: "https://example.com", Title: "docs"}, |
| 493 | {Type: fantasy.StreamPartTypeWarnings, Warnings: []fantasy.CallWarning{{Message: "w1"}, {Message: "w2"}}}, |
| 494 | {Type: fantasy.StreamPartTypeError, Error: errPart}, |
| 495 | {Type: fantasy.StreamPartTypeFinish, FinishReason: fantasy.FinishReasonStop, Usage: fantasy.Usage{InputTokens: 8, OutputTokens: 3, TotalTokens: 11}}, |
| 496 | } |
| 497 | |
| 498 | expectDebugLoggingEnabled(t, db, ownerID) |
| 499 | stepID := expectCreateStep(t, db, runID, chatID, OperationStream) |
| 500 | expectUpdateStep(t, db, stepID, chatID, StatusError, func(params database.UpdateChatDebugStepParams) { |
| 501 | require.True(t, params.NormalizedResponse.Valid) |
| 502 | require.True(t, params.Usage.Valid) |
| 503 | require.True(t, params.Attempts.Valid) |
| 504 | require.True(t, params.Error.Valid) |
| 505 | require.True(t, params.Metadata.Valid) |
| 506 | |
| 507 | // Verify usage JSON content matches the finish part. |
| 508 | var usage normalizedUsage |
| 509 | require.NoError(t, json.Unmarshal(params.Usage.RawMessage, &usage)) |
| 510 | require.EqualValues(t, 8, usage.InputTokens) |
| 511 | require.EqualValues(t, 3, usage.OutputTokens) |
| 512 | require.EqualValues(t, 11, usage.TotalTokens) |
| 513 | |
| 514 | // Verify the response payload captures the streamed content. |
| 515 | var resp normalizedResponsePayload |
| 516 | require.NoError(t, json.Unmarshal(params.NormalizedResponse.RawMessage, &resp)) |
| 517 | require.Equal(t, "stop", resp.FinishReason) |
| 518 | require.NotEmpty(t, resp.Content, "stream response should capture content parts") |
| 519 | |
| 520 | // Verify error payload comes from the stream error part. |
| 521 | var errPayload normalizedErrorPayload |
| 522 | require.NoError(t, json.Unmarshal(params.Error.RawMessage, &errPayload)) |
| 523 | require.Equal(t, "chunk failed", errPayload.Message) |
| 524 | |
| 525 | // Verify metadata contains stream_summary. |
| 526 | var meta map[string]any |
| 527 | require.NoError(t, json.Unmarshal(params.Metadata.RawMessage, &meta)) |
| 528 | summary, ok := meta["stream_summary"].(map[string]any) |
| 529 | require.True(t, ok, "metadata must contain stream_summary") |
| 530 | require.EqualValues(t, 1, summary["text_delta_count"]) |
| 531 | require.EqualValues(t, 1, summary["tool_call_count"]) |
| 532 | require.EqualValues(t, 1, summary["source_count"]) |
| 533 | require.EqualValues(t, 1, summary["error_count"]) |
| 534 | }) |
| 535 | |
| 536 | svc := NewService(db, testutil.Logger(t), nil) |
| 537 | model := &debugModel{ |
nothing calls this directly
no test coverage detected