8000 fix(chat): hide the trace UI when isTraceEnabled() is false by nanchen-goog · Pull Request #537 · google/adk-web · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/app/components/chat/chat.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -489,10 +489,12 @@
} } @if (appName != "") {
<div class="chat-sub-toolbar">
@if (!(uiEvents().length === 0 && agentReadme && chatType() !== 'eval-result')) {
<mat-button-toggle-group [value]="viewMode()" (change)="onViewModeChange($event.value)" hideSingleSelectionIndicator>
@if (isTraceEnabled()) {
<mat-button-toggle-group [value]="effectiveViewMode()" (change)="onViewModeChange($event.value)" hideSingleSelectionIndicator>
<mat-button-toggle value="events">Events</mat-button-toggle>
<mat-button-toggle value="traces">Traces</mat-button-toggle>
</mat-button-toggle-group>
}

<div class="filter-bar-container" (click)="openAddFilterMenu($event)">
@if (invocationIdFilterActive()) {
Expand Down Expand Up @@ -601,7 +603,7 @@
</button>
}

@let hasBranchesOption = viewMode() !== 'traces';
@let hasBranchesOption = effectiveViewMode() !== 'traces';
@let hasStreamingOption = (isTokenStreamingEnabledObs | async) && canEditSession();
@if (hasBranchesOption || hasStreamingOption) {
<button
Expand Down Expand Up @@ -684,7 +686,7 @@
(longRunningResponseComplete)="sendMessage($event)"
[sessionName]="sessionId"
[invocationDisplayMap]="invocationDisplayMap()"
[viewMode]="viewMode()"
[viewMode]="effectiveViewMode()"
[shouldShowEvent]="shouldShowEventFn"
(manualScroll)="onManualScroll()"
></app-chat-panel>
Expand Down Expand Up @@ -719,7 +721,7 @@
(editFunctionArgs)="editFunctionArgs($event)"
[sessionName]="sessionId"
[invocationDisplayMap]="invocationDisplayMap()"
[viewMode]="viewMode()"
[viewMode]="effectiveViewMode()"
[shouldShowEvent]="shouldShowEventFn"
></app-chat-panel>
}
Expand Down Expand Up @@ -825,7 +827,7 @@
[sessionId]="sessionId"
[sessionName]="sessionId"
[invocationDisplayMap]="invocationDisplayMap()"
[viewMode]="viewMode()"
[viewMode]="effectiveViewMode()"
[shouldShowEvent]="shouldShowEventFn"
(manualScroll)="onManualScroll()"
></app-chat-panel>
Expand Down Expand Up @@ -880,7 +882,7 @@
(longRunningResponseComplete)="sendMessage($event)"
[sessionName]="sessionId"
[invocationDisplayMap]="invocationDisplayMap()"
[viewMode]="viewMode()"
[viewMode]="effectiveViewMode()"
[shouldShowEvent]="shouldShowEventFn"
(manualScroll)="onManualScroll()"
></app-chat-panel>
Expand All @@ -903,7 +905,7 @@
[sessionId]="sessionId"
[sessionName]="sessionId"
[invocationDisplayMap]="invocationDisplayMap()"
[viewMode]="viewMode()"
[viewMode]="effectiveViewMode()"
[shouldShowEvent]="shouldShowEventFn"
(manualScroll)="onManualScroll()"
></app-chat-panel>
Expand Down
69 changes: 69 additions & 0 deletions src/app/components/chat/chat.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ describe('ChatComponent', () => {
mockFeatureFlagService.isInfinityMessageScrollingEnabledResponse.next(
false);
mockFeatureFlagService.isNewSessionButtonEnabledResponse.next(true);
mockFeatureFlagService.isTraceEnabledResponse.next(true);

mockDialog = jasmine.createSpyObj('MatDialog', ['open']);
mockDialog.open.and.returnValue({
Expand Down Expand Up @@ -1092,6 +1093,74 @@ describe('ChatComponent', () => {
});
});

describe('Events/Traces toggle', () => {
const toggleSelector =
By.css('.chat-sub-toolbar mat-button-toggle-group');

describe('when isTraceEnabled is true', () => {
beforeEach(() => {
fixture.detectChanges();
});

it('should be visible', () => {
expect(fixture.debugElement.query(toggleSelector)).toBeTruthy();
});
});

describe('when isTraceEnabled is false', () => {
beforeEach(() => {
mockFeatureFlagService.isTraceEnabledResponse.next(false);
fixture.detectChanges();
});

it('should not be visible', () => {
expect(fixture.debugElement.query(toggleSelector)).toBeFalsy();
});
});
});

describe('trace view', () => {
function chatPanelViewMode() {
return fixture.debugElement.query(By.directive(ChatPanelComponent))
.componentInstance.viewMode;
}

describe('when switched to while traces are enabled', () => {
beforeEach(() => {
component.switchToTraceView();
fixture.detectChanges();
});

it('should be shown', () => {
expect(chatPanelViewMode()).toBe('traces');
});

describe('and traces are then disabled', () => {
beforeEach(() => {
mockFeatureFlagService.isTraceEnabledResponse.next(false);
fixture.detectChanges();
});

it('should fall back to events', () => {
expect(chatPanelViewMode()).toBe('events');
});
});
});

describe('when switched to while traces are disabled', () => {
beforeEach(() => {
mockFeatureFlagService.isTraceEnabledResponse.next(false);
component.switchToTraceView();
mockFeatureFlagService.isTraceEnabledResponse.next(true);
fixture.detectChanges();
});

it('should not be selected', () => {
expect(chatPanelViewMode()).toBe('events');
});
});
});

describe('when removeStateUpdate() is called', () => {
beforeEach(() => {
component.updatedSessionState.set({[STATE_KEY]: STATE_VALUE});
Expand Down
5 changes: 5 additions & 0 deletions src/app/components/chat/chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
protected readonly showBranches = signal(false);
protected readonly expectedUiEvents = signal<UiEvent[]>([]);
protected readonly viewMode = signal<'events' | 'traces'>((this.storageService.getItem('chat-view-mode') as 'events' | 'traces') || 'events');
/** The view shown; a stored `traces` choice is ignored while traces are disabled. */
protected readonly effectiveViewMode = computed(() => this.isTraceEnabled() ? this.viewMode() : 'events');
protected readonly invocationIdFilterActive = signal<boolean>(false);
protected readonly nodePathFilterActive = signal<boolean>(false);
protected readonly invocationIdFilter = signal<string>('');
Expand Down Expand Up @@ -800,6 +802,8 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.featureFlagService.isNewSessionButtonEnabled();
readonly isEventFilteringEnabled =
toSignal(this.featureFlagService.isEventFilteringEnabled());
readonly isTraceEnabled =
toSignal(this.featureFlagService.isTraceEnabled());
readonly isApplicationSelectorEnabled =
toSignal(this.featureFlagService.isApplicationSelectorEnabled());
readonly isDeleteSessionEnabledObs: Observable<boolean> =
Expand Down Expand Up @@ -979,6 +983,7 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
}

switchToTraceView() {
if (!this.isTraceEnabled()) return;
this.onViewModeChange('traces');
}

Expand Down
5 changes: 5 additions & 0 deletions src/app/components/event-tab/event-tab.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {InfoTable} from '../info-table/info-table';

import {Event, Part} from '../../core/models/types';
import {UI_STATE_SERVICE} from '../../core/services/interfaces/ui-state';
import {FEATURE_FLAG_SERVICE} from '../../core/services/interfaces/feature-flag';
import {SidePanelMessagesInjectionToken} from '../side-panel/side-panel.component.i18n';
import {Span} from '../../core/models/Trace';
import {TRACE_SERVICE} from '../../core/services/interfaces/trace';
Expand Down Expand Up @@ -161,12 +162,16 @@ export class EventTabComponent {
protected readonly uiStateService = inject(UI_STATE_SERVICE);
protected readonly traceService = inject(TRACE_SERVICE);
private readonly storageService = inject(STORAGE_SERVICE);
private readonly featureFlagService = inject(FEATURE_FLAG_SERVICE);
readonly i18n = inject(SidePanelMessagesInjectionToken);

readonly isEventRequestResponseLoadingSignal = toSignal(
this.uiStateService.isEventRequestResponseLoading(), {initialValue: false});
readonly isTraceEnabled = toSignal(this.featureFlagService.isTraceEnabled());

// Each span links to the trace view, so none are listed while traces are disabled.
readonly associatedSpans = computed(() => {
if (!this.isTraceEnabled()) return [];
const ev = this.selectedEvent();
if (!ev || !ev.id) return [];

Expand Down
37 changes: 37 additions & 0 deletions src/app/components/side-panel/side-panel.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {of} from 'rxjs';

import {EvalCase} from '../../core/models/Eval';
import {Session} from '../../core/models/Session';
import {GCP_VERTEX_AGENT_EVENT_ID, Span, SpanValidator} from '../../core/models/Trace';
import {AgentService} from '../../core/services/agent.service';
import {ArtifactService} from '../../core/services/artifact.service';
import {AudioRecordingService} from '../../core/services/audio-recording.service';
Expand Down Expand Up @@ -72,6 +73,23 @@ const EVAL_TAB_SELECTOR = By.css('app-eval-tab');
const DETAILS_PANEL_CLOSE_BUTTON_SELECTOR =
By.css('.details-panel-container mat-icon');
const EVENT_GRAPH_SELECTOR = By.css('.event-graph-container div');
const SPAN_LINK_SELECTOR = By.css('app-event-tab .span-link');

/** Routes a raw span through `SpanValidator` so `attrEventId` is populated. */
function makeSpan(attributes: Record<string, unknown>): Span {
const result = SpanValidator.safeParse({
name: 'test-span',
trace_id: 'trace-id',
span_id: 'span-id',
start_time: 1,
end_time: 2,
attributes,
});
if (!result.success) {
throw new Error(`Failed to build test span: ${result.error.message}`);
}
return result.data;
}

const EVAL_TAB_INDEX = 3;

Expand Down Expand Up @@ -403,6 +421,25 @@ describe('SidePanelComponent', () => {
});
});

describe('associated spans', () => {
beforeEach(() => {
fixture.componentRef.setInput(
'traceData', [makeSpan({[GCP_VERTEX_AGENT_EVENT_ID]: 'event1'})]);
});

it('links the spans of the selected event', () => {
fixture.detectChanges();
expect(fixture.debugElement.queryAll(SPAN_LINK_SELECTOR).length)
.toBe(1);
});

it('links no spans when isTraceEnabled is false', () => {
mockFeatureFlagService.isTraceEnabledResponse.next(false);
fixture.detectChanges();
expect(fixture.debugElement.queryAll(SPAN_LINK_SELECTOR).length)
.toBe(0);
});
});
});

describe('Loading state', () => {
Expand Down
Loading
0