Skip to content

Commit

Permalink
feat: copilot 1. avatar 2. abort 3. show tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
meta-d committed Feb 1, 2024
1 parent 7c7bd82 commit 6832c5a
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 153 deletions.
9 changes: 7 additions & 2 deletions apps/cloud/src/app/features/features.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@

<!-- <pac-copilot-global *ngIf="copilotEnabled$ | async" displayDensity="cosy"></pac-copilot-global> -->

@if(copilotEnabled$ | async) {
@if(copilotEnabled$()) {
<button mat-icon-button class="pac-copilot-trigger font-notoColorEmoji"
displayDensity="cosy"
[class.active]="copilotDrawer.opened"
Expand All @@ -108,10 +108,14 @@
<mat-drawer #copilotDrawer class="mat-elevation-z" position="end" mode="side" ngmResizer [resizerWidth]="380"
[(opened)]="copilotDrawerOpened"
>
@if(copilotEnabled$()) {
<ngm-copilot-chat #copilotChat class="pac-cdk-drop__list pac-cdk-drop__area h-full"
assistantAvatar="assets/images/copilot_sparkle_resting.gif"
[user]="user$()"
cdkDropList
(enableCopilot)="toEnableCopilot()"
></ngm-copilot-chat>
}

<div ngmResizerBar resizerBarPosition="left"
cdkDrag
Expand All @@ -122,8 +126,9 @@
[@routeAnimations]="o.isActivated && o.activatedRoute.routeConfig.data && o.activatedRoute.routeConfig.data.title">
<router-outlet #o="outlet"></router-outlet>

@if(copilotEnabled$()) {
<ngm-drawer-trigger class="absolute -right-2 top-1/2 -translate-y-1/2 z-40" [(opened)]="copilotDrawerOpened" side="right"></ngm-drawer-trigger>

}
</mat-drawer-content>
</mat-drawer-container>

Expand Down
4 changes: 3 additions & 1 deletion apps/cloud/src/app/features/features.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export class FeaturesComponent implements OnInit {
loading = false
isDark$ = this.appService.isDark$

public readonly copilotEnabled$ = this.appService.copilotEnabled$
// public readonly copilotEnabled$ = this.appService.copilotEnabled$
readonly copilotEnabled$ = toSignal(this.appService.copilotEnabled$)

copilotDrawerOpened = false

Expand All @@ -157,6 +158,7 @@ export class FeaturesComponent implements OnInit {
this.logger?.debug(value)
})

readonly user$ = toSignal(this.store.user$)

// private _sidebarContentIndexSub = this.appService.fullscreenIndex$.subscribe((fullscreenIndex) => this.zIndex = fullscreenIndex)
constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,6 @@ ${sharedDimensionsPrompt}
}
})

get user() {
return this.#store.user
}

copilotEngine: CopilotEngine = null

@ViewChild('copilotChat') copilotChat!: NgmCopilotChatComponent

@HostBinding('class.pac-fullscreen')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ The query should be returned in plain text, not in JSON.
constructor() {
super()
// 设置当前 Chat Copilot Engine
this.modelComponent.copilotEngine = this.copilotEngine
// this.modelComponent.copilotEngine = this.copilotEngine
}

onSelectionChange(event) {
Expand Down Expand Up @@ -729,7 +729,7 @@ The query should be returned in plain text, not in JSON.
}

ngOnDestroy(): void {
this.modelComponent.copilotEngine = null
// this.modelComponent.copilotEngine = null
}
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 31 additions & 39 deletions packages/angular/copilot/chat/chat.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
@case (CopilotChatMessageRoleEnum.Assistant) {
<div class="pl-2 pr-4 flex">
<div class="flex-1 flex items-start overflow-auto">
<div class="flex flex-col items-center">
<div class="w-8 h-8 rounded-full bg-bluegray-50 text-xl text-center font-notoColorEmoji">🤖</div>
<div class="flex flex-col items-center shrink-0">
@if (assistantAvatar) {
<img class="w-8 h-8 pointer-events-none" [src]="assistantAvatar"/>
} @else {
<div class="w-8 h-8 rounded-full bg-bluegray-50 text-xl text-center font-notoColorEmoji">🤖</div>
}
</div>
<div class="flex flex-col justify-start items-start overflow-auto relative p-[10px] min-h-[50px] min-w-[50px] group">
@if (conversation.content) {
Expand All @@ -35,7 +39,7 @@
{{ conversation.error }}
</div>
}
@if (conversation.content) {
@if (showTokenizer$() && conversation.content) {
<ngm-copilot-token [content]="conversation.content"></ngm-copilot-token>
}

Expand Down Expand Up @@ -77,7 +81,7 @@
@if (conversation.command) {
<span class="text-xs font-medium italic px-2.5 py-0.5 rounded bg-neutral-100 dark:bg-neutral-700">/{{conversation.command}}</span>
}
@if (conversation.content) {
@if (showTokenizer$() && conversation.content) {
<ngm-copilot-token [content]="conversation.content"></ngm-copilot-token>
}
</div>
Expand Down Expand Up @@ -117,32 +121,6 @@
</div>
}
</div>

<!-- <ng-container *ngIf="prompts?.length">
<div class="border-dashed border-t border-t-slate-200 w-full flex justify-center my-4">
<span class="-mt-3 px-2 bg-slate-50 inline-flex items-center">{{ 'Ngm.Copilot.ExamplesOfPrompts' | translate: {Default: 'Examples of Prompt'} }}
<button (click)="examplesOpened=!examplesOpened">
<svg data-accordion-icon class="w-6 h-6 shrink-0 text-neutral-300 hover:text-neutral-400"
[class.rotate-180]="!examplesOpened"
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
</button>
</span>
</div>
<div class="max-h-[30%] min-h-[100px] overflow-auto pl-4 flex flex-col items-end"
[class.min-h-0]="!examplesOpened"
[class.h-0]="!examplesOpened"
[class.min-h-[200px]]="examplesOpened"
>
<div *ngFor="let prompt of prompts" class="mb-2 mx-2 bg-slate-200 rounded-lg py-2 px-4">
<a class="cursor-pointer hover:underline"
(click)="askPredefinedPrompt(prompt)">
{{ 'Ngm.Copilot.Prompts.'+prompt | translate: {Default: prompt} }}</a>
</div>
</div>
</ng-container> -->

</div>

<ngm-scroll-back #scrollBack class="absolute bottom-0 left-1/2" [ngmTarget]="chatsContent" direction="bottom"></ngm-scroll-back>
Expand All @@ -152,7 +130,7 @@
[class.ngm-colpilot__active]="isFoucs(userInput)"
>
<mat-progress-bar mode="buffer" [class.opacity-100]="answering()" class="opacity-0 top-0 left-0 w-full transition duration-300" style="position: absolute; z-index: 1;"></mat-progress-bar>
@if (answering()) {
<!-- @if (answering()) {
<div class="ngm-copilot-chat__answering absolute inset-0 flex justify-center items-center">
<button mat-stroked-button displayDensity="cosy" class="rounded-full"
(click)="$event.stopPropagation();stopGenerating()">
Expand All @@ -163,7 +141,7 @@
</div>
</button>
</div>
}
} -->

<div class="flex flex-col p-2" >
<textarea #userInput matInput
Expand Down Expand Up @@ -218,13 +196,27 @@
</button>

<span class="flex-1"></span>

<button mat-icon-button displayDensity="cosy"
[disabled]="!prompt() || answering()"
(click)="askCopilotStream(promptControl.value)"
>
<mat-icon fontSet="material-icons-round">send</mat-icon>
</button>

@if (answering()) {
<button type="button" class="rounded-full m-1 border-2 border-gray-950 p-1 dark:border-gray-200" aria-label="停止生成"
(click)="stopGenerating()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="h-2 w-2 text-token-text-primary" height="16" width="16">
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2z" stroke-width="0"></path>
</svg>
</button>
} @else {
<button class="rounded-full bg-black md:bottom-3 md:right-3 dark:hover:bg-white right-2 disabled:opacity-10 disabled:text-gray-400 enabled:bg-black text-white p-0.5 border border-black dark:border-white dark:bg-white bottom-1.5 transition-colors" data-testid="send-button"
[disabled]="!prompt()"
(click)="askCopilotStream(promptControl.value)"
>
<span class="" data-state="closed">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="text-white dark:text-black">
<path d="M7 11L12 6L17 11M12 18V7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</span>
</button>
}

</div>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions packages/angular/copilot/chat/chat.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@
}

.ngm-copilot-chat__message-content {
@apply rounded-lg max-w-full py-2 px-4 overflow-auto bg-neutral-100/50 dark:bg-neutral-100/10;
@apply rounded-lg max-w-full py-2 px-4 overflow-auto;
}

.ngm-copilot__user-message {
@apply bg-gray-100/50 dark:bg-neutral-700;
@apply rounded-xl rounded-tr-sm bg-gray-200/50 dark:bg-neutral-700;
}

.ngm-copilot-chat__answering {
background-color: var(--ngm-copilot-bg-color);
background-color: var(--ngm-copilot-bg-color, white);
}

:host::ng-deep {
Expand Down
71 changes: 10 additions & 61 deletions packages/angular/copilot/chat/chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class NgmCopilotChatComponent {
@Input() welcomeTitle: string
@Input() welcomeSubTitle: string
@Input() placeholder: string
@Input() assistantAvatar: string

@Input() get copilotEngine(): CopilotEngine {
return this.#customEngine ?? this.#copilotEngine
Expand Down Expand Up @@ -212,6 +213,7 @@ export class NgmCopilotChatComponent {
| Signals
|--------------------------------------------------------------------------
*/
readonly showTokenizer$ = toSignal(this.copilotService.copilot$.pipe(map((copilot) => copilot?.showTokenizer)))
readonly conversations = computed(() => this.copilotEngine.messages().filter((message) => message.content || message.error))

/**
Expand All @@ -227,8 +229,8 @@ export class NgmCopilotChatComponent {
readonly historyQuestions = signal<string[]>([])
private readonly historyIndex = signal(-1)

askController: AbortController
askSubscriber: Subscription
#abortController: AbortController
#askSubscriber: Subscription

// Available models
private readonly _models$ = new BehaviorSubject<{ id: string; label: string }[]>([
Expand Down Expand Up @@ -353,50 +355,15 @@ export class NgmCopilotChatComponent {
// Clear prompt in input
this.promptControl.setValue('')

// Get last conversation messages
// const lastConversation = this.lastConversation()

// Append user question message
// this.conversations = this.conversations ?? []
// this.conversations = [
// ...this.conversations,
// {
// id: nanoid(),
// role: CopilotChatMessageRoleEnum.User,
// content: prompt
// }
// ]

// Assistant message
// const assistant: CopilotChatMessage = {
// id: nanoid(),
// role: CopilotChatMessageRoleEnum.Assistant,
// content: ''
// }

// Answering
this.answering.set(true)
// 由其他引擎接手处理
if (this.copilotEngine) {
// let userContent = prompt
// if (lastConversation.length > 0) {
// // 无论是否为新对话,都将最新的连续的提问消息内容汇总
// if (lastConversation[lastConversation.length - 1]?.role === CopilotChatMessageRoleEnum.User) {
// userContent = lastConversation[lastConversation.length - 1].content + '\n' + userContent
// lastConversation.splice(lastConversation.length - 1, 1)
// }

// // 如果是新会话,清空上一次的会话
// if (newConversation) {
// lastConversation.splice(0, lastConversation.length)
// }
// }

// const assistantIndex = this.conversations().length
// this.conversations = [...this.conversations, assistant]

try {
this.askSubscriber = this.copilotEngine.process({ prompt, newConversation, messages: [] }).subscribe({
this.#abortController = new AbortController()
this.#askSubscriber = this.copilotEngine.process({ prompt, newConversation, messages: [] }, {
abortController: this.#abortController
}).subscribe({
next: (message: CopilotChatMessage | string | void) => {
if (typeof message === 'string') {
this.copilotEngine.upsertMessage({
Expand All @@ -413,48 +380,30 @@ export class NgmCopilotChatComponent {
},
error: (err) => {
console.error(err)
// const conversations = [...this.conversations]
// conversations[assistantIndex] = { ...conversations[assistantIndex] }
// conversations[assistantIndex].content = null
// conversations[assistantIndex].error = getErrorMessage(err)
this.answering.set(false)
// this.conversations = conversations
this.conversationsChange.emit(this.conversations())
this._cdr.detectChanges()
},
complete: () => {
this.answering.set(false)
// Not cleared
if (this.conversations.length) {
// const conversations = [...this.conversations]
// if (!conversations[assistantIndex].content) {
// conversations.splice(assistantIndex, 1)
// }
// if (this.conversations[this.conversations.length - 1].role === CopilotChatMessageRoleEnum.Info) {
// conversations.splice(conversations.length - 1, 1)
// }
// this.conversations = conversations
this.conversationsChange.emit(this.conversations)
this._cdr.detectChanges()
}
}
})
} catch (err) {
// const conversations = [...this.conversations]
// conversations[assistantIndex] = { ...conversations[assistantIndex] }
// conversations[assistantIndex].content = null
// conversations[assistantIndex].error = getErrorMessage(err)
this.answering.set(false)
// this.conversations = conversations
this.conversationsChange.emit(this.conversations)
this._cdr.detectChanges()
}
}
}

stopGenerating() {
this.askController?.abort()
this.askSubscriber?.unsubscribe()
this.#abortController?.abort()
this.#askSubscriber?.unsubscribe()
this.answering.set(false)
this.conversationsChange.emit(this.conversations)

Expand Down
Loading

0 comments on commit 6832c5a

Please sign in to comment.