basant307/AI_Governance_Project
048
1/**2 * @license3 * Copyright 2026 Qwen Team4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, expect, it } from 'vitest';8import { GenerateContentResponse } from '@google/genai';9import { hasUserVisibleContent } from './streamContentDetection.js';10 11function chunkWithParts(parts: unknown[]): GenerateContentResponse {12 const r = new GenerateContentResponse();13 r.candidates = [14 {15 content: { role: 'model', parts: parts as never },16 },17 ];18 return r;19}20 21describe('hasUserVisibleContent', () => {22 it('returns true for non-empty text part', () => {23 expect(hasUserVisibleContent(chunkWithParts([{ text: 'hi' }]))).toBe(true);24 });25 26 it('returns false for empty text part', () => {27 expect(hasUserVisibleContent(chunkWithParts([{ text: '' }]))).toBe(false);28 });29 30 it('returns true for functionCall part', () => {31 expect(32 hasUserVisibleContent(33 chunkWithParts([{ functionCall: { name: 'read', args: {} } }]),34 ),35 ).toBe(true);36 });37 38 it('returns true for inlineData part', () => {39 expect(40 hasUserVisibleContent(41 chunkWithParts([42 { inlineData: { mimeType: 'image/png', data: 'abc' } },43 ]),44 ),45 ).toBe(true);46 });47 48 it('returns true for executableCode part', () => {49 expect(50 hasUserVisibleContent(51 chunkWithParts([52 { executableCode: { language: 'PYTHON', code: 'print(1)' } },53 ]),54 ),55 ).toBe(true);56 });57 58 it('returns true for thought / reasoning part with thought: true', () => {59 expect(hasUserVisibleContent(chunkWithParts([{ thought: true }]))).toBe(60 true,61 );62 });63 64 it('returns false for thought: false (explicit non-thought part)', () => {65 // Codebase convention: `thought` is a boolean flag where false means66 // "explicitly not a thought." A part with only `thought: false` and no67 // other content must not trigger TTFT.68 expect(hasUserVisibleContent(chunkWithParts([{ thought: false }]))).toBe(69 false,70 );71 });72 73 it('returns false for thought: undefined / missing (default non-thought)', () => {74 // A bare object without the `thought` key is the common case for non-thinking75 // chunks; must not match the thought branch.76 expect(hasUserVisibleContent(chunkWithParts([{}]))).toBe(false);77 });78 79 it('returns true when thought: true coexists with empty text', () => {80 // First Anthropic <thinking> chunk often arrives as { text: '', thought: true }.81 // Per design doc D1, "thought / reasoning content" is user-visible — TTFT fires.82 expect(83 hasUserVisibleContent(chunkWithParts([{ text: '', thought: true }])),84 ).toBe(true);85 });86 87 it('returns true when any part is user-visible (mixed)', () => {88 expect(89 hasUserVisibleContent(chunkWithParts([{ text: '' }, { text: 'hi' }])),90 ).toBe(true);91 });92 93 it('returns false for empty parts array', () => {94 expect(hasUserVisibleContent(chunkWithParts([]))).toBe(false);95 });96 97 it('returns false when candidates is missing', () => {98 const r = new GenerateContentResponse();99 expect(hasUserVisibleContent(r)).toBe(false);100 });101 102 it('returns false when content is missing', () => {103 const r = new GenerateContentResponse();104 r.candidates = [{}];105 expect(hasUserVisibleContent(r)).toBe(false);106 });107 108 it('returns false when parts is undefined', () => {109 const r = new GenerateContentResponse();110 r.candidates = [{ content: { role: 'model' } }];111 expect(hasUserVisibleContent(r)).toBe(false);112 });113 114 it('returns false for usage-only / role-only chunks', () => {115 const r = new GenerateContentResponse();116 r.candidates = [{ content: { role: 'model', parts: [] } }];117 r.usageMetadata = { totalTokenCount: 42 };118 expect(hasUserVisibleContent(r)).toBe(false);119 });120 121 it('handles parts that are non-objects defensively', () => {122 expect(123 hasUserVisibleContent(124 chunkWithParts([null, undefined, 'string', 42, { text: 'real' }]),125 ),126 ).toBe(true);127 expect(hasUserVisibleContent(chunkWithParts([null, undefined, 'x']))).toBe(128 false,129 );130 });131});132 