| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 |
- import { firstValueFrom, of } from 'rxjs';
- import { filter, timeout } from 'rxjs/operators';
- import { AiAnalysisService, AiStreamState } from './ai-analysis.service';
- import { CloudApiService } from '../../../app/core/services/cloud-api.service';
- describe('AiAnalysisService retry lifecycle', () => {
- it('keeps the outer stream alive while a transient failure is retried', async () => {
- const promptConfig = {
- ensureLoaded: () => of([]),
- getTextSnapshot: (_key: string, fallback: string) => fallback,
- getTextForDefaultTemplate: (template: string) => template,
- getPromptKeyForTemplateText: () => undefined,
- getDefaultAiModel: () => 'deepseek-v4-flash',
- getOutputStyleInstruction: () => '',
- };
- const cloud = jasmine.createSpyObj<CloudApiService>('CloudApiService', ['runPromise']);
- cloud.runPromise.and.returnValues(Promise.reject(new Error('temporary upstream failure')), Promise.resolve({ choices: [{ message: { content: '重试成功' }, finish_reason: 'stop' }] }));
- const service = new AiAnalysisService(promptConfig as any, cloud);
- const finalState = await firstValueFrom(service.streamAnalysis('分析真实评论').pipe(
- filter((state: AiStreamState) => state.complete && !state.error),
- timeout(2_000),
- ));
- expect(cloud.runPromise).toHaveBeenCalledTimes(2);
- expect(finalState.text).toBe('重试成功');
- });
- });
- describe('AiAnalysisService extractAiText reasoning isolation', () => {
- let service: AiAnalysisService;
- beforeEach(() => {
- const promptConfig = {
- ensureLoaded: () => of([]),
- getTextSnapshot: (_key: string, fallback: string) => fallback,
- getTextForDefaultTemplate: (template: string) => template,
- getPromptKeyForTemplateText: () => undefined,
- getDefaultAiModel: () => 'deepseek-v4-flash',
- getOutputStyleInstruction: () => '',
- };
- service = new AiAnalysisService(promptConfig as any, jasmine.createSpyObj<CloudApiService>('CloudApiService', ['runPromise']));
- });
- it('only extracts content when both content and reasoning_content are present', () => {
- const json = {
- choices: [{
- delta: {
- content: '正式内容',
- reasoning_content: '{"type":"metric-grid","metrics":[残缺草稿',
- },
- }],
- };
- expect((service as any).extractAiText(json)).toBe('正式内容');
- });
- it('returns an empty string when only reasoning_content exists (no content fallback)', () => {
- const json = {
- choices: [{ delta: { reasoning_content: '残缺JSON草稿,无正式内容' } }],
- };
- expect((service as any).extractAiText(json)).toBe('');
- });
- });
|