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
20 changes: 1 addition & 19 deletions src/app/components/call-controls/call-controls.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<div class="bar" [style.height.px]="4 + micVolume * 14"></div>
</div>
}
<div class="call-btn-container" (mouseenter)="showFlags = true" (mouseleave)="showFlags = false">
<div class="call-btn-container">
<button
mat-icon-button
(click)="onCallClick()"
Expand All @@ -26,22 +26,4 @@
>
<mat-icon>{{ isAudioRecording ? 'call_end' : 'call' }}</mat-icon>
</button>

@if (showFlags && !isAudioRecording && !disabled) {
<div class="flags-panel">
<div class="flags-title">Live Flags</div>
<div class="flag-item">
<mat-checkbox [checked]="flags.proactiveAudio" (change)="flags.proactiveAudio = $event.checked" [matTooltip]="i18n.proactiveAudioTooltip" matTooltipPosition="left" [disabled]="disabled">Proactive Audio</mat-checkbox>
</div>
<div class="flag-item">
<mat-checkbox [checked]="flags.enableAffectiveDialog" (change)="flags.enableAffectiveDialog = $event.checked" [matTooltip]="i18n.affectiveDialogTooltip" matTooltipPosition="left" [disabled]="disabled">Affective Dialog</mat-checkbox>
</div>
<div class="flag-item">
<mat-checkbox [checked]="flags.enableSessionResumption" (change)="flags.enableSessionResumption = $event.checked" [matTooltip]="i18n.sessionResumptionTooltip" matTooltipPosition="left" [disabled]="disabled">Session Resumption</mat-checkbox>
</div>
<div class="flag-item">
<mat-checkbox [checked]="flags.saveLiveBlob" (change)="flags.saveLiveBlob = $event.checked" [matTooltip]="i18n.saveLiveBlobTooltip" matTooltipPosition="left" [disabled]="disabled">Save Live Blob</mat-checkbox>
</div>
</div>
}
</div>
63 changes: 0 additions & 63 deletions src/app/components/call-controls/call-controls.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,66 +50,3 @@ button:disabled {
position: relative;
display: inline-block;
}

.flags-panel {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
margin-bottom: 8px;
background: var(--mat-sys-surface-container-highest);
border: 1px solid var(--mat-sys-outline-variant);
border-radius: 12px;
padding: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
z-index: 100;
width: 250px;
display: flex;
flex-direction: column;
gap: 8px;
animation: fadeIn 0.2s ease-out;

&::before {
content: '';
position: absolute;
bottom: -8px;
left: 0;
right: 0;
height: 8px;
background: transparent;
}

.flags-title {
font-weight: 600;
font-size: 14px;
color: var(--mat-sys-on-surface);
margin-bottom: 4px;
}

.flag-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--mat-sys-on-surface-variant);

.flag-label {
font-weight: 500;
}

mat-checkbox {
--mdc-checkbox-state-layer-size: 30px;
}
}
}

@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(-50%) translateY(10px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
17 changes: 3 additions & 14 deletions src/app/components/call-controls/call-controls.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ import {CommonModule} from '@angular/common';
import {MatButtonModule} from '@angular/material/button';
import {MatIconModule} from '@angular/material/icon';
import {MatTooltipModule} from '@angular/material/tooltip';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {ChatPanelMessagesInjectionToken} from '../chat-panel/chat-panel.component.i18n';
import {LiveFlags} from '../../core/services/interfaces/stream-chat';

@Component({
selector: 'app-call-controls',
standalone: true,
imports: [CommonModule, MatButtonModule, MatIconModule, MatTooltipModule, MatCheckboxModule],
imports: [CommonModule, MatButtonModule, MatIconModule, MatTooltipModule],
templateUrl: './call-controls.component.html',
styleUrl: './call-controls.component.scss',
})
Expand All @@ -42,21 +40,12 @@ export class CallControlsComponent {
@Input() isBidiStreamingEnabled: boolean | null = false;
@Input() disabled = false;

@Output() readonly toggleAudioRecording = new EventEmitter<LiveFlags>();
@Output() readonly toggleAudioRecording = new EventEmitter<void>();
@Output() readonly toggleVideoRecording = new EventEmitter<void>();

protected readonly i18n = inject(ChatPanelMessagesInjectionToken);

showFlags = false;
flags: LiveFlags = {
proactiveAudio: false,
enableAffectiveDialog: false,
enableSessionResumption: false,
saveLiveBlob: false,
};

onCallClick() {
this.showFlags = false;
this.toggleAudioRecording.emit(this.flags);
this.toggleAudioRecording.emit();
}
}
2 changes: 1 addition & 1 deletion src/app/components/chat-panel/chat-panel.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ <h3 style="margin: 0; color: var(--mat-sys-primary);">Evaluation Result</h3>
<app-call-controls [isAudioRecording]="isAudioRecording" [isVideoRecording]="isVideoRecording"
[micVolume]="micVolume" [isBidiStreamingEnabled]="(isBidiStreamingEnabledObs | async) ?? false"
[disabled]="isLoadingAgentResponse() ?? false"
(toggleAudioRecording)="toggleAudioRecording.emit($event)"
(toggleAudioRecording)="toggleAudioRecording.emit()"
(toggleVideoRecording)="toggleVideoRecording.emit()"></app-call-controls>
@if (isLoadingAgentResponse()) {
<button mat-icon-button (click)="stopMessage.emit($event)" class="stop-message-btn"
Expand Down
4 changes: 0 additions & 4 deletions src/app/components/chat-panel/chat-panel.component.i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ export const CHAT_PANEL_MESSAGES = {
turnOffCamTooltip: 'Turn off camera',
useCamTooltip: 'Use camera',
updatedSessionStateChipLabel: 'Updated session state',
proactiveAudioTooltip: 'Enable the model to speak spontaneously without waiting for user input',
affectiveDialogTooltip: 'Enable the model to respond with emotional expression',
sessionResumptionTooltip: 'Allow the session to resume from a previous state',
saveLiveBlobTooltip: 'Save the recorded live stream data',
};


Expand Down
3 changes: 1 addition & 2 deletions src/app/components/chat-panel/chat-panel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import { UI_STATE_SERVICE } from '../../core/services/interfaces/ui-state';
import { THEME_SERVICE } from '../../core/services/interfaces/theme';
import { JsonTooltipDirective } from '../../directives/html-tooltip.directive';
import { WorkflowGraphTooltipDirective } from '../../directives/workflow-graph-tooltip.directive';
import { LiveFlags } from '../../core/services/interfaces/stream-chat';
import { A2uiCanvasComponent } from '../a2ui-canvas/a2ui-canvas.component';
import { MediaType, } from '../artifact-tab/artifact-tab.component';
import { AudioPlayerComponent } from '../audio-player/audio-player.component';
Expand Down Expand Up @@ -175,7 +174,7 @@ export class ChatPanelComponent implements OnChanges, AfterViewInit {
@Output() readonly sendMessage = new EventEmitter<Event>();
@Output() readonly stopMessage = new EventEmitter<Event>();
@Output() readonly updateState = new EventEmitter<void>();
@Output() readonly toggleAudioRecording = new EventEmitter<LiveFlags>();
@Output() readonly toggleAudioRecording = new EventEmitter<void>();
@Output() readonly toggleVideoRecording = new EventEmitter<void>();
@Output() readonly longRunningResponseComplete = new EventEmitter<AgentRunRequest>();
@Output() readonly toggleHideIntermediateEvents = new EventEmitter<void>();
Expand Down
4 changes: 2 additions & 2 deletions src/app/components/chat/chat.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@
(sendMessage)="handleChatInput($event)"
(stopMessage)="handleStopMessage()"
(updateState)="updateState()"
(toggleAudioRecording)="toggleAudioRecording($event)"
(toggleAudioRecording)="toggleAudioRecording()"
(toggleVideoRecording)="toggleVideoRecording()"
(longRunningResponseComplete)="sendMessage($event)"
[sessionName]="sessionId"
Expand Down Expand Up @@ -801,7 +801,7 @@
(removeStateUpdate)="removeStateUpdate()"
(sendMessage)="handleChatInput($event)"
(updateState)="updateState()"
(toggleAudioRecording)="toggleAudioRecording($event)"
(toggleAudioRecording)="toggleAudioRecording()"
(toggleVideoRecording)="toggleVideoRecording()"
(longRunningResponseComplete)="sendMessage($event)"
[sessionName]="sessionId"
Expand Down
9 changes: 4 additions & 5 deletions src/app/components/chat/chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { GRAPH_SERVICE } from '../../core/services/interfaces/graph';
import { LOCAL_FILE_SERVICE } from '../../core/services/interfaces/localfile';
import { SAFE_VALUES_SERVICE } from '../../core/services/interfaces/safevalues';
import { SESSION_SERVICE } from '../../core/services/interfaces/session';
import { LiveFlags, STREAM_CHAT_SERVICE } from '../../core/services/interfaces/stream-chat';
import { STREAM_CHAT_SERVICE } from '../../core/services/interfaces/stream-chat';
import { AUDIO_RECORDING_SERVICE } from '../../core/services/interfaces/audio-recording';
import { AUDIO_PLAYING_SERVICE } from '../../core/services/interfaces/audio-playing';
import { STRING_TO_COLOR_SERVICE } from '../../core/services/interfaces/string-to-color';
Expand Down Expand Up @@ -1945,12 +1945,12 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
this.traceData = [];
}

async toggleAudioRecording(flags?: LiveFlags) {
async toggleAudioRecording() {
this.isAudioRecording ? this.stopAudioRecording() :
await this.startAudioRecording(flags);
await this.startAudioRecording();
}

async startAudioRecording(flags?: LiveFlags) {
async startAudioRecording() {
if (this.sessionId && this.activeBidiSessions.has(this.sessionId)) {
this.openSnackBar(BIDI_STREAMING_IN_PROGRESS_WARNING, 'OK');
return;
Expand All @@ -1968,7 +1968,6 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
appName: this.appName,
userId: this.userId,
sessionId: this.sessionId,
flags: flags,
});
this.changeDetectorRef.detectChanges();
}
Expand Down
9 changes: 0 additions & 9 deletions src/app/core/services/interfaces/stream-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,18 @@ export const STREAM_CHAT_SERVICE =
/**
* Service for supporting live streaming with audio/video.
*/
export interface LiveFlags {
proactiveAudio?: boolean;
enableAffectiveDialog?: boolean;
enableSessionResumption?: boolean;
saveLiveBlob?: boolean;
}

export declare abstract class StreamChatService {
abstract startAudioChat(options: {
appName: string;
userId: string;
sessionId: string;
flags?: LiveFlags;
}): Promise<void>;
abstract stopAudioChat(): void;
abstract startVideoChat(options: {
appName: string;
userId: string;
sessionId: string;
videoContainer: ElementRef;
flags?: LiveFlags;
}): Promise<void>;
abstract stopVideoChat(videoContainer: ElementRef): void;
abstract startVideoStreaming(videoContainer: ElementRef): Promise<void>;
Expand Down
30 changes: 6 additions & 24 deletions src/app/core/services/stream-chat.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {URLUtil} from '../../../utils/url-util';
import {LiveRequest} from '../models/LiveRequest';

import {AUDIO_RECORDING_SERVICE} from './interfaces/audio-recording';
import {LiveFlags, STREAM_CHAT_SERVICE, StreamChatService as StreamChatServiceInterface} from './interfaces/stream-chat';
import {STREAM_CHAT_SERVICE, StreamChatService as StreamChatServiceInterface} from './interfaces/stream-chat';
import {VIDEO_SERVICE} from './interfaces/video';
import {WEBSOCKET_SERVICE} from './interfaces/websocket';
import {VideoService} from './video.service';
Expand All @@ -42,33 +42,17 @@ export class StreamChatService implements StreamChatServiceInterface {

constructor() {}

private getWsUrl(appName: string, userId: string, sessionId: string, flags?: LiveFlags): string {
private getWsUrl(appName: string, userId: string, sessionId: string): string {
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
let url = `${protocol}://${URLUtil.getWSServerUrl()}/run_live?app_name=${appName}&user_id=${userId}&session_id=${sessionId}`;
if (flags) {
if (flags.proactiveAudio) {
url += `&proactive_audio=true`;
}
if (flags.enableAffectiveDialog) {
url += `&enable_affective_dialog=true`;
}
if (flags.enableSessionResumption) {
url += `&enable_session_resumption=true`;
}
if (flags.saveLiveBlob) {
url += `&save_live_blob=true`;
}
}
return url;
return `${protocol}://${URLUtil.getWSServerUrl()}/run_live?app_name=${appName}&user_id=${userId}&session_id=${sessionId}`;
}

async startAudioChat({
appName,
userId,
sessionId,
flags,
}: {appName: string; userId: string; sessionId: string; flags?: LiveFlags;}) {
this.webSocketService.connect(this.getWsUrl(appName, userId, sessionId, flags));
}: {appName: string; userId: string; sessionId: string;}) {
this.webSocketService.connect(this.getWsUrl(appName, userId, sessionId));

await this.startAudioStreaming();
}
Expand Down Expand Up @@ -112,13 +96,11 @@ export class StreamChatService implements StreamChatServiceInterface {
userId,
sessionId,
videoContainer,
flags,
}: {
appName: string; userId: string; sessionId: string;
videoContainer: ElementRef;
flags?: LiveFlags;
}) {
this.webSocketService.connect(this.getWsUrl(appName, userId, sessionId, flags));
this.webSocketService.connect(this.getWsUrl(appName, userId, sessionId));

await this.startAudioStreaming();
await this.startVideoStreaming(videoContainer);
Expand Down
Loading