Skip to content

Commit f069e2f

Browse files
authored
Merge branch 'main' into main
2 parents c511d57 + 077a86a commit f069e2f

File tree

71 files changed

+1314
-594
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1314
-594
lines changed

src/app/app.component.spec.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
import {Location} from '@angular/common';
1819
import {TestBed} from '@angular/core/testing';
1920
import {MatDialog} from '@angular/material/dialog';
2021
import {DomSanitizer} from '@angular/platform-browser';
@@ -23,20 +24,21 @@ import {ActivatedRoute} from '@angular/router';
2324
import {of} from 'rxjs';
2425

2526
import {AppComponent} from './app.component';
26-
import {AGENT_SERVICE} from './core/services/agent.service';
27-
import {ARTIFACT_SERVICE} from './core/services/artifact.service';
28-
import {AUDIO_PLAYING_SERVICE} from './core/services/audio-playing.service';
29-
import {AUDIO_RECORDING_SERVICE} from './core/services/audio-recording.service';
30-
import {DOWNLOAD_SERVICE} from './core/services/download.service';
31-
import {EVAL_SERVICE} from './core/services/eval.service';
32-
import {EVENT_SERVICE} from './core/services/event.service';
33-
import {FEATURE_FLAG_SERVICE} from './core/services/feature-flag.service';
34-
import {GRAPH_SERVICE} from './core/services/graph.service';
27+
import {AGENT_SERVICE} from './core/services/interfaces/agent';
28+
import {ARTIFACT_SERVICE} from './core/services/interfaces/artifact';
29+
import {AUDIO_PLAYING_SERVICE} from './core/services/interfaces/audio-playing';
30+
import {AUDIO_RECORDING_SERVICE} from './core/services/interfaces/audio-recording';
31+
import {DOWNLOAD_SERVICE} from './core/services/interfaces/download';
32+
import {EVAL_SERVICE} from './core/services/interfaces/eval';
33+
import {EVENT_SERVICE} from './core/services/interfaces/event';
34+
import {FEATURE_FLAG_SERVICE} from './core/services/interfaces/feature-flag';
35+
import {GRAPH_SERVICE} from './core/services/interfaces/graph';
3536
import {LOCAL_FILE_SERVICE} from './core/services/interfaces/localfile';
3637
import {SAFE_VALUES_SERVICE} from './core/services/interfaces/safevalues';
3738
import {STRING_TO_COLOR_SERVICE} from './core/services/interfaces/string-to-color';
38-
import {SESSION_SERVICE} from './core/services/session.service';
39-
import {STREAM_CHAT_SERVICE} from './core/services/stream-chat.service';
39+
import {LOCATION_SERVICE} from './core/services/location.service';
40+
import {SESSION_SERVICE} from './core/services/interfaces/session';
41+
import {STREAM_CHAT_SERVICE} from './core/services/interfaces/stream-chat';
4042
import {MockAgentService} from './core/services/testing/mock-agent.service';
4143
import {MockArtifactService} from './core/services/testing/mock-artifact.service';
4244
import {MockAudioPlayingService} from './core/services/testing/mock-audio-playing.service';
@@ -54,9 +56,9 @@ import {MockStringToColorService} from './core/services/testing/mock-string-to-c
5456
import {MockTraceService} from './core/services/testing/mock-trace.service';
5557
import {MockVideoService} from './core/services/testing/mock-video.service';
5658
import {MockWebSocketService} from './core/services/testing/mock-websocket.service';
57-
import {TRACE_SERVICE} from './core/services/trace.service';
58-
import {VIDEO_SERVICE} from './core/services/video.service';
59-
import {WEBSOCKET_SERVICE} from './core/services/websocket.service';
59+
import {TRACE_SERVICE} from './core/services/interfaces/trace';
60+
import {VIDEO_SERVICE} from './core/services/interfaces/video';
61+
import {WEBSOCKET_SERVICE} from './core/services/interfaces/websocket';
6062

6163
describe('AppComponent', () => {
6264
beforeEach(async () => {
@@ -78,7 +80,7 @@ describe('AppComponent', () => {
7880
const localFileService = new MockLocalFileService();
7981

8082
traceService.selectedTraceRow$.next(undefined);
81-
traceService.hoveredMessageIndicies$.next([]);
83+
traceService.hoveredMessageIndices$.next([]);
8284

8385
const graphService = new MockGraphService();
8486
graphService.render.and.returnValue(Promise.resolve('svg'));
@@ -169,6 +171,10 @@ describe('AppComponent', () => {
169171
provide: LOCAL_FILE_SERVICE,
170172
useValue: localFileService,
171173
},
174+
{
175+
provide: LOCATION_SERVICE,
176+
useClass: Location,
177+
}
172178
],
173179
})
174180
.compileComponents();

src/app/components/artifact-tab/artifact-tab.component.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
import { ComponentFixture, TestBed } from '@angular/core/testing';
1919
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
2020
import { ArtifactTabComponent } from './artifact-tab.component';
21-
import { DOWNLOAD_SERVICE, DownloadService } from '../../core/services/download.service';
21+
import { DownloadService } from '../../core/services/download.service';
2222
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
2323
import { SAFE_VALUES_SERVICE } from '../../core/services/interfaces/safevalues';
24+
import {DOWNLOAD_SERVICE} from '../../core/services/interfaces/download';
2425
import { MockSafeValuesService } from '../../core/services/testing/mock-safevalues.service';
2526

2627
describe('ArtifactTabComponent', () => {

src/app/components/artifact-tab/artifact-tab.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {MatDialog, MatDialogModule} from '@angular/material/dialog';
2323
import {MatIcon} from '@angular/material/icon';
2424
import {MatSelect} from '@angular/material/select';
2525

26-
import {DOWNLOAD_SERVICE, DownloadService} from '../../core/services/download.service';
26+
import {DOWNLOAD_SERVICE, DownloadService} from '../../core/services/interfaces/download';
2727
import {SAFE_VALUES_SERVICE} from '../../core/services/interfaces/safevalues';
2828
import {AudioPlayerComponent} from '../audio-player/audio-player.component';
2929
import {ViewImageDialogComponent} from '../view-image-dialog/view-image-dialog.component';

src/app/components/chat-panel/chat-panel.component.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
2424
import {MarkdownModule} from 'ngx-markdown';
2525
import {of} from 'rxjs';
2626

27-
import {FEATURE_FLAG_SERVICE} from '../../core/services/feature-flag.service';
27+
import {FEATURE_FLAG_SERVICE} from '../../core/services/interfaces/feature-flag';
2828
import {STRING_TO_COLOR_SERVICE} from '../../core/services/interfaces/string-to-color';
2929
import {StringToColorServiceImpl} from '../../core/services/string-to-color.service';
3030
import {MockFeatureFlagService} from '../../core/services/testing/mock-feature-flag.service';

src/app/components/chat-panel/chat-panel.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
3131
import {NgxJsonViewerModule} from 'ngx-json-viewer';
3232

3333
import type {EvalCase} from '../../core/models/Eval';
34-
import {FEATURE_FLAG_SERVICE} from '../../core/services/feature-flag.service';
34+
import {FEATURE_FLAG_SERVICE} from '../../core/services/interfaces/feature-flag';
3535
import {STRING_TO_COLOR_SERVICE} from '../../core/services/interfaces/string-to-color';
3636
import {MediaType,} from '../artifact-tab/artifact-tab.component';
3737
import {AudioPlayerComponent} from '../audio-player/audio-player.component';

src/app/components/chat/chat.component.spec.ts

Lines changed: 119 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,19 @@ import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
2828
import {BehaviorSubject, NEVER, of, Subject, throwError} from 'rxjs';
2929

3030
import {EvalCase} from '../../core/models/Eval';
31-
import {AGENT_SERVICE, AgentService} from '../../core/services/agent.service';
32-
import {ARTIFACT_SERVICE, ArtifactService,} from '../../core/services/artifact.service';
33-
import {DOWNLOAD_SERVICE, DownloadService,} from '../../core/services/download.service';
34-
import {EVAL_SERVICE, EvalService} from '../../core/services/eval.service';
35-
import {EVENT_SERVICE, EventService} from '../../core/services/event.service';
36-
import {FEATURE_FLAG_SERVICE, FeatureFlagService,} from '../../core/services/feature-flag.service';
37-
import {GRAPH_SERVICE, GraphService} from '../../core/services/graph.service';
31+
import {AGENT_SERVICE, AgentService} from '../../core/services/interfaces/agent';
32+
import {ARTIFACT_SERVICE, ArtifactService,} from '../../core/services/interfaces/artifact';
33+
import {DOWNLOAD_SERVICE, DownloadService,} from '../../core/services/interfaces/download';
34+
import {EVAL_SERVICE, EvalService} from '../../core/services/interfaces/eval';
35+
import {EVENT_SERVICE, EventService} from '../../core/services/interfaces/event';
36+
import {FEATURE_FLAG_SERVICE, FeatureFlagService,} from '../../core/services/interfaces/feature-flag';
37+
import {GRAPH_SERVICE, GraphService} from '../../core/services/interfaces/graph';
3838
import {LOCAL_FILE_SERVICE} from '../../core/services/interfaces/localfile';
3939
import {SAFE_VALUES_SERVICE} from '../../core/services/interfaces/safevalues';
4040
import {STRING_TO_COLOR_SERVICE} from '../../core/services/interfaces/string-to-color';
41-
import {SESSION_SERVICE, SessionService,} from '../../core/services/session.service';
42-
import {STREAM_CHAT_SERVICE} from '../../core/services/stream-chat.service';
41+
import {LOCATION_SERVICE} from '../../core/services/location.service';
42+
import {SESSION_SERVICE, SessionService,} from '../../core/services/interfaces/session';
43+
import {STREAM_CHAT_SERVICE} from '../../core/services/interfaces/stream-chat';
4344
import {MockAgentService} from '../../core/services/testing/mock-agent.service';
4445
import {MockArtifactService} from '../../core/services/testing/mock-artifact.service';
4546
import {MockDownloadService} from '../../core/services/testing/mock-download.service';
@@ -55,9 +56,9 @@ import {MockStringToColorService} from '../../core/services/testing/mock-string-
5556
import {MockTraceService} from '../../core/services/testing/mock-trace.service';
5657
import {MockVideoService} from '../../core/services/testing/mock-video.service';
5758
import {MockWebSocketService} from '../../core/services/testing/mock-websocket.service';
58-
import {TRACE_SERVICE, TraceService} from '../../core/services/trace.service';
59-
import {VIDEO_SERVICE, VideoService} from '../../core/services/video.service';
60-
import {WEBSOCKET_SERVICE, WebSocketService,} from '../../core/services/websocket.service';
59+
import {TRACE_SERVICE, TraceService} from '../../core/services/interfaces/trace';
60+
import {VIDEO_SERVICE, VideoService} from '../../core/services/interfaces/video';
61+
import {WEBSOCKET_SERVICE, WebSocketService,} from '../../core/services/interfaces/websocket';
6162
import {fakeAsync,
6263
tick} from '../../testing/utils';
6364
import {ChatPanelComponent} from '../chat-panel/chat-panel.component';
@@ -145,7 +146,7 @@ describe('ChatComponent', () => {
145146
mockSessionService.createSessionResponse.next(
146147
{id: SESSION_1_ID, state: {}});
147148
mockTraceService.selectedTraceRow$.next(undefined);
148-
mockTraceService.hoveredMessageIndicies$.next([]);
149+
mockTraceService.hoveredMessageIndices$.next([]);
149150
mockFeatureFlagService.isImportSessionEnabledResponse.next(true);
150151
mockFeatureFlagService.isEditFunctionArgsEnabledResponse.next(true);
151152
mockFeatureFlagService.isSessionUrlEnabledResponse.next(true);
@@ -215,7 +216,7 @@ describe('ChatComponent', () => {
215216
{provide: MatSnackBar, useValue: mockSnackBar},
216217
{provide: Router, useValue: mockRouter},
217218
{provide: ActivatedRoute, useValue: mockActivatedRoute},
218-
{provide: Location, useValue: mockLocation},
219+
{provide: LOCATION_SERVICE, useValue: mockLocation},
219220
{provide: MARKDOWN_COMPONENT, useValue: MockMarkdownComponent},
220221
],
221222
})
@@ -291,29 +292,35 @@ describe('ChatComponent', () => {
291292
});
292293

293294
describe('when session ID is provided in URL', () => {
294-
beforeEach(async () => {
295+
beforeEach(() => {
296+
mockAgentService.listAppsResponse.next([TEST_APP_1_NAME]);
295297
mockFeatureFlagService.isSessionUrlEnabledResponse.next(true);
296298
mockActivatedRoute.snapshot!.queryParams = {
297299
[APP_QUERY_PARAM]: TEST_APP_1_NAME,
298300
[SESSION_QUERY_PARAM]: SESSION_2_ID,
299301
};
300302
mockSessionService.getSessionResponse.next(
301303
{id: SESSION_2_ID, state: {}, events: []});
302-
fixture = TestBed.createComponent(ChatComponent);
303-
component = fixture.componentInstance;
304-
component.ngOnInit();
305-
fixture.detectChanges();
306-
component.selectApp(TEST_APP_2_NAME);
307-
await fixture.whenStable();
308304
});
309-
it('should load session from URL', () => {
310-
expect(mockSessionService.getSession)
311-
.toHaveBeenCalledWith(
312-
USER_ID,
313-
TEST_APP_2_NAME,
314-
SESSION_2_ID,
315-
);
316-
expect(component.sessionId).toBe(SESSION_2_ID);
305+
306+
describe('on app change', () => {
307+
beforeEach(async () => {
308+
fixture = TestBed.createComponent(ChatComponent);
309+
component = fixture.componentInstance;
310+
component.ngOnInit();
311+
fixture.detectChanges();
312+
component.selectApp(TEST_APP_2_NAME);
313+
await fixture.whenStable();
314+
});
315+
it('should load session from URL', () => {
316+
expect(mockSessionService.getSession)
317+
.toHaveBeenCalledWith(
318+
USER_ID,
319+
TEST_APP_2_NAME,
320+
SESSION_2_ID,
321+
);
322+
expect(component.sessionId).toBe(SESSION_2_ID);
323+
});
317324
});
318325
});
319326

@@ -389,7 +396,8 @@ describe('ChatComponent', () => {
389396
'sessionTab', ['refreshSession', 'getSession']);
390397
sessionTabSpy.refreshSession.and.returnValue(
391398
{id: SESSION_2_ID} as any);
392-
spyOn(component, 'sessionTab').and.returnValue(sessionTabSpy);
399+
spyOnProperty(component, 'sessionTab', 'get')
400+
.and.returnValue(sessionTabSpy);
393401
component.deleteSession(SESSION_1_ID);
394402
});
395403
it('should delete session', () => {
@@ -509,11 +517,12 @@ describe('ChatComponent', () => {
509517
component.sessionId = SESSION_1_ID;
510518
component.messages.set(
511519
[{role: 'bot', text: 'response', eventId: EVENT_1_ID}]);
512-
component.eventData = new Map([[EVENT_1_ID, {id: EVENT_1_ID}]]);
513520
spyOn(component.sideDrawer()!, 'open');
514-
component.clickEvent(0);
515521
});
522+
516523
it('should open side panel with event details', () => {
524+
component.eventData = new Map([[EVENT_1_ID, {id: EVENT_1_ID}]]);
525+
component.clickEvent(0);
517526
expect(component.sideDrawer()!.open).toHaveBeenCalled();
518527
expect(component.selectedEvent.id).toBe(EVENT_1_ID);
519528
expect(mockEventService.getEventTrace).toHaveBeenCalledWith(EVENT_1_ID);
@@ -525,6 +534,31 @@ describe('ChatComponent', () => {
525534
EVENT_1_ID,
526535
);
527536
});
537+
538+
it('should call getEventTrace with filter and parse llm request/response',
539+
() => {
540+
const invocationId = 'inv-1';
541+
const timestamp = 123456789;
542+
component.eventData = new Map([[
543+
EVENT_1_ID, {
544+
id: EVENT_1_ID,
545+
invocationId,
546+
timestampInMillis: timestamp,
547+
}
548+
]]);
549+
const llmRequest = {prompt: 'test prompt'};
550+
const llmResponse = {response: 'test response'};
551+
mockEventService.getEventTraceResponse.next({
552+
'gcp.vertex.agent.llm_request': JSON.stringify(llmRequest),
553+
'gcp.vertex.agent.llm_response': JSON.stringify(llmResponse),
554+
});
555+
556+
component.clickEvent(0);
557+
558+
expect(mockEventService.getEventTrace).toHaveBeenCalledWith(EVENT_1_ID);
559+
expect(component.llmRequest).toEqual(llmRequest);
560+
expect(component.llmResponse).toEqual(llmResponse);
561+
});
528562
});
529563

530564
describe('when updateState() is called', () => {
@@ -607,13 +641,65 @@ describe('ChatComponent', () => {
607641
expect(messageCards[0].nativeElement.textContent)
608642
.toContain(TEST_MESSAGE);
609643
});
644+
645+
describe('when event contains multiple text parts', () => {
646+
it('should combine consecutive text parts into a single message',
647+
async () => {
648+
const sseEvent = {
649+
id: 'event-1',
650+
author: 'bot',
651+
content:
652+
{role: 'bot', parts: [{text: 'Hello '}, {text: 'World!'}]},
653+
};
654+
component.messages.set([]);
655+
component.userInput = 'test message';
656+
await component.sendMessage(
657+
new KeyboardEvent('keydown', {key: 'Enter'}));
658+
mockAgentService.runSseResponse.next(sseEvent);
659+
fixture.detectChanges();
660+
661+
const botMessages =
662+
component.messages().filter(m => m.role === 'bot');
663+
expect(botMessages.length).toBe(1);
664+
expect(botMessages[0].text).toBe('Hello World!');
665+
});
666+
667+
it('should not combine non-consecutive text parts', async () => {
668+
const sseEvent = {
669+
id: 'event-1',
670+
author: 'bot',
671+
content: {
672+
role: 'bot',
673+
parts: [
674+
{text: 'Hello '},
675+
{functionCall: {name: 'foo', args: {}}},
676+
{text: 'World!'},
677+
]
678+
},
679+
};
680+
component.messages.set([]);
681+
component.userInput = 'test message';
682+
await component.sendMessage(
683+
new KeyboardEvent('keydown', {key: 'Enter'}));
684+
mockAgentService.runSseResponse.next(sseEvent);
685+
fixture.detectChanges();
686+
687+
const botMessages =
688+
component.messages().filter(m => m.role === 'bot');
689+
expect(botMessages.length).toBe(3);
690+
expect(botMessages[0].text).toBe('Hello ');
691+
expect(botMessages[1].functionCall).toEqual({name: 'foo', args: {}});
692+
expect(botMessages[2].text).toBe('World!');
693+
});
694+
});
610695
});
611696

612697
describe('when chat-panel emits sendMessage', () => {
613698
const mockEvent = new KeyboardEvent('keydown', {key: 'Enter'});
614699
beforeEach(() => {
615700
spyOn(component, 'sendMessage').and.callThrough();
616-
mockAgentService.runSseResponse.next('');
701+
mockAgentService.runSseResponse.next(
702+
{content: {role: 'bot', parts: []}});
617703
const chatPanelDebugEl =
618704
fixture.debugElement.query(By.directive(ChatPanelComponent));
619705
chatPanelDebugEl.triggerEventHandler('sendMessage', mockEvent);

0 commit comments

Comments
 (0)