Skip to content

Commit

Permalink
feat: scroll to bottom & new api chat
Browse files Browse the repository at this point in the history
  • Loading branch information
meta-d committed Feb 1, 2024
1 parent 6832c5a commit b5e92bf
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 62 deletions.
4 changes: 4 additions & 0 deletions apps/cloud/src/styles/base.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
:root {
--ngm-copilot-font-family: Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
}

:root .default {
--ngm-app-background-color: white;
--ngm-color-main-container-background: white;
Expand Down
3 changes: 3 additions & 0 deletions libs/story-angular/story/story-widget/story-widget.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ export class NxStoryWidgetService extends ComponentSubStore<StoryWidget, StoryPo
return result
}

async chat({prompt}, options?: {action: string}) {
}

async preprocess(prompt: string, options?: { signal?: AbortSignal }): Promise<CopilotChatMessage[]> {
return []
}
Expand Down
12 changes: 0 additions & 12 deletions packages/angular/copilot/chat/chat.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,6 @@
[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()) {
<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()">
<div class="copilot-message-stop flex items-center">
<span>
{{ 'Ngm.Copilot.StopGenerating' | translate: {Default: 'Stop generating'} }}
</span>
</div>
</button>
</div>
} -->

<div class="flex flex-col p-2" >
<textarea #userInput matInput
Expand Down
1 change: 1 addition & 0 deletions packages/angular/copilot/chat/chat.component.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
:host {
background-color: var(--ngm-copilot-bg-color, theme('colors.white'));
font-family: var(--ngm-copilot-font-family);
@apply flex flex-col relative text-sm ;
}

Expand Down
67 changes: 26 additions & 41 deletions packages/angular/copilot/chat/chat.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
signal,
ViewChild
} from '@angular/core'
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop'
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'
import { MatAutocomplete, MatAutocompleteActivatedEvent, MatAutocompleteModule } from '@angular/material/autocomplete'
import { MatButtonModule } from '@angular/material/button'
Expand All @@ -37,7 +37,7 @@ import {
NgxPopperjsPlacements,
NgxPopperjsTriggers
} from 'ngx-popperjs'
import { BehaviorSubject, combineLatest, delay, firstValueFrom, map, startWith, Subscription } from 'rxjs'
import { BehaviorSubject, combineLatest, debounceTime, delay, firstValueFrom, map, startWith, throttleTime } from 'rxjs'
import { NgmCopilotEnableComponent } from '../enable/enable.component'
import { NgmCopilotEngineService } from '../services/'
import { CopilotChatTokenComponent } from '../token/token.component'
Expand Down Expand Up @@ -230,7 +230,6 @@ export class NgmCopilotChatComponent {
private readonly historyIndex = signal(-1)

#abortController: AbortController
#askSubscriber: Subscription

// Available models
private readonly _models$ = new BehaviorSubject<{ id: string; label: string }[]>([
Expand Down Expand Up @@ -312,23 +311,24 @@ export class NgmCopilotChatComponent {
| Subscribers
|--------------------------------------------------------------------------
*/
/**
* @deprecated use Signal
*/
private _copilotSub = this.copilotService.copilot$.pipe(delay(1000), takeUntilDestroyed()).subscribe(() => {
this._cdr.detectChanges()
})
private scrollSub = toObservable(this.conversations).pipe(throttleTime(500)).subscribe((conversations) => {
if (conversations.length && !this.scrollBack.visible()) {
this.scrollBottom()
}
})

constructor() {
effect(() => {
this.answering() ? this.promptControl.disable() : this.promptControl.enable()
},
{ allowSignalWrites: true }
)

effect(() => {
const conversations = this.conversations()
if (conversations.length && !this.scrollBack.visible()) {
this.scrollBottom()
}
})
}

refreshModels() {
Expand Down Expand Up @@ -361,38 +361,24 @@ export class NgmCopilotChatComponent {
if (this.copilotEngine) {
try {
this.#abortController = new AbortController()
this.#askSubscriber = this.copilotEngine.process({ prompt, newConversation, messages: [] }, {
const message = await this.#copilotEngine.chat({ prompt, newConversation, messages: [] }, {
abortController: this.#abortController
}).subscribe({
next: (message: CopilotChatMessage | string | void) => {
if (typeof message === 'string') {
this.copilotEngine.upsertMessage({
id: nanoid(),
role: 'info',
content: message
})
} else if (message) {
this.copilotEngine.upsertMessage(message)
}

this._cdr.detectChanges()
this.scrollBottom()
},
error: (err) => {
console.error(err)
this.answering.set(false)
this.conversationsChange.emit(this.conversations())
this._cdr.detectChanges()
},
complete: () => {
this.answering.set(false)
// Not cleared
if (this.conversations.length) {
this.conversationsChange.emit(this.conversations)
this._cdr.detectChanges()
}
}
})

this.answering.set(false)

if (typeof message === 'string') {
this.copilotEngine.upsertMessage({
id: nanoid(),
role: 'info',
content: message
})
} else if (message) {
this.copilotEngine.upsertMessage(message)
}

this._cdr.detectChanges()
this.scrollBottom()
} catch (err) {
this.answering.set(false)
this.conversationsChange.emit(this.conversations)
Expand All @@ -403,7 +389,6 @@ export class NgmCopilotChatComponent {

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

Expand Down
88 changes: 88 additions & 0 deletions packages/angular/copilot/services/engine.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,94 @@ export class NgmCopilotEngineService implements CopilotEngine {
return this.#commands()[name]
}

async chat(
data: { prompt: string; newConversation?: boolean; messages?: CopilotChatMessage[] },
options?: { action?: string; abortController?: AbortController }
) {
this.#logger?.debug(`process ask: ${data.prompt}`)

const { abortController } = options ?? {}
// New messages
const newMessages: CopilotChatMessage[] = []
const { command, prompt } = getCommandPrompt(data.prompt)
if (command) {
const _command = this.getCommand(command)

if (!_command) {
throw new Error(`Command '${command}' not found`)
}

if (_command.systemPrompt) {
newMessages.push({
id: nanoid(),
role: CopilotChatMessageRoleEnum.System,
content: _command.systemPrompt()
})
}
newMessages.push({
id: nanoid(),
role: CopilotChatMessageRoleEnum.User,
content: prompt,
command
})

// Last user messages before add new messages
const lastUserMessages = this.lastUserMessages()
// Append new messages to conversation
this.conversations$.update((state) => [...state, ...newMessages])

// Exec command implementation
if (_command.implementation) {
return await _command.implementation()
}

const functions = _command.actions
? entryPointsToChatCompletionFunctions(_command.actions.map((id) => this.#entryPoints()[id]))
: this.getChatCompletionFunctionDescriptions()

const body = {
...this.aiOptions
}
if (functions.length) {
body.functions = functions
}

await this.triggerRequest([...lastUserMessages, ...newMessages], {
options: {
body
}
}, {
abortController
})
} else {
// Last conversation messages before append new messages
const lastConversation = this.lastConversation()
newMessages.push({
id: nanoid(),
role: CopilotChatMessageRoleEnum.User,
content: prompt
})
// Append new messages to conversation
this.conversations$.update((state) => [...state, ...newMessages])

const functions = this.getChatCompletionFunctionDescriptions()
const body = {
...this.aiOptions
}
if (functions.length) {
body.functions = functions
}

await this.triggerRequest([...lastConversation, ...newMessages], {
options: {
body
}
}, {
abortController
})
}
}

process(
data: { prompt: string; newConversation?: boolean; messages?: CopilotChatMessage[] },
options?: { action?: string; abortController?: AbortController }
Expand Down
27 changes: 18 additions & 9 deletions packages/copilot/src/lib/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,20 @@ export interface CopilotEngine {
placeholder?: string

messages(): CopilotChatMessage[]


chat(
data: { prompt: string; newConversation?: boolean; messages?: CopilotChatMessage[] },
options?: { action?: string; abortController?: AbortController }
): Promise<CopilotChatMessage | string | void>

/**
* @deprecated use `chat` instead
*/
process(
data: { prompt: string; newConversation?: boolean; messages?: CopilotChatMessage[] },
options?: { action?: string; abortController?: AbortController }
): Observable<CopilotChatMessage | string | void>

/**
* @deprecated
*/
Expand Down Expand Up @@ -72,17 +81,17 @@ export interface CopilotEngine {

/**
* Update or insert the message into conversations
*
* @param message
*
* @param message
*/
upsertMessage?(message: CopilotChatMessage): void;
upsertMessage?(message: CopilotChatMessage): void

/**
* Delete message from conversation
*
* @param message
*
* @param message
*/
deleteMessage?(message: CopilotChatMessage): void;
deleteMessage?(message: CopilotChatMessage): void

/**
* Clear conversations
Expand All @@ -91,8 +100,8 @@ export interface CopilotEngine {

/**
* Update conversations value
*
* @param fn
*
* @param fn
*/
updateConversations?(fn: (conversations: CopilotChatMessage[]) => CopilotChatMessage[]): void
}

0 comments on commit b5e92bf

Please sign in to comment.