diff options
Diffstat (limited to 'usecaseui-portal/src/app/views')
25 files changed, 821 insertions, 139 deletions
diff --git a/usecaseui-portal/src/app/views/maas/build/application-detail/application-detail.component.html b/usecaseui-portal/src/app/views/maas/build/application-detail/application-detail.component.html index 0827f477..e01cea5d 100644 --- a/usecaseui-portal/src/app/views/maas/build/application-detail/application-detail.component.html +++ b/usecaseui-portal/src/app/views/maas/build/application-detail/application-detail.component.html @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<nz-modal [(nzVisible)]="showModal" nzTitle="Knowledge Base Detail" (nzOnCancel)="handleCancel()" +<nz-modal [(nzVisible)]="showModal" nzTitle="Application Detail" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()" nzWidth="56%" nzHeight="800px" > <app-descriptions> <app-descriptions-item nzTitle="Application Name">{{applicationDetail.applicationName}} diff --git a/usecaseui-portal/src/app/views/maas/build/application-management.component.html b/usecaseui-portal/src/app/views/maas/build/application-management.component.html index 6496270a..53e112be 100644 --- a/usecaseui-portal/src/app/views/maas/build/application-management.component.html +++ b/usecaseui-portal/src/app/views/maas/build/application-management.component.html @@ -50,12 +50,14 @@ <td>{{data.largeModelName}}</td> <td> <i class="anticon anticon-menu-fold" (click)="displayApplicationDetails(data)"></i> - <i class="anticon anticon-delete" (click)="delete(data)"></i> + <i class="anticon anticon-edit" (click)="edit(data)"></i> + <i class="anticon anticon-delete" (click)="showDeleteConfirm(data)"></i> <i class="anticon anticon-link" (click)="navigateToDetail(data)"></i> </td> </tr> </tbody> </nz-table> </div> -<app-create-application-management *ngIf="createModalShow" [showModal]="createModalShow" (modalOpreation)="createModalClose($event)"></app-create-application-management> -<app-application-detail *ngIf="applicationShow" [showModal]="applicationShow" (modalOpreation)="applicationDetailClose()" [applicationDetail]="applicationDetail"></app-application-detail>
\ No newline at end of file +<app-create-application-management *ngIf="createModalShow" [showModal]="createModalShow" (modalOpreation)="createModalClose($event)" [existedNames]="existedNames"></app-create-application-management> +<app-application-detail *ngIf="applicationShow" [showModal]="applicationShow" (modalOpreation)="applicationDetailClose()" [applicationDetail]="applicationDetail"></app-application-detail> +<app-edit-application *ngIf="editModalShow" [showModal]="editModalShow" (modalOpreation)="editModalClose($event)" [applicationId]="applicationId"></app-edit-application>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/build/application-management.component.ts b/usecaseui-portal/src/app/views/maas/build/application-management.component.ts index 61471de4..00c0c405 100644 --- a/usecaseui-portal/src/app/views/maas/build/application-management.component.ts +++ b/usecaseui-portal/src/app/views/maas/build/application-management.component.ts @@ -1,8 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { NzMessageService } from "ng-zorro-antd"; +import { NzMessageService, NzModalService } from "ng-zorro-antd"; import { Router } from '@angular/router'; -import { MaasService } from '@src/app/core/services/maas.service'; +import { MaasApi } from '@src/app/api/maas.api'; import { Application } from './application.type'; +import { modalClose } from '../knowledge-base-management/knowledge-base.type'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-application-management', @@ -14,11 +16,16 @@ export class ApplicationManagementComponent implements OnInit { createModalShow = false; applicationShow = false; applicationDetail: Object = {}; + editModalShow = false; + applicationId = ''; + existedNames = []; constructor( - private myhttp: MaasService, + private myhttp: MaasApi, private message: NzMessageService, - private router: Router + private router: Router, + private modalService: NzModalService, + private translate: TranslateService ) { } ngOnInit() { @@ -30,6 +37,7 @@ export class ApplicationManagementComponent implements OnInit { .subscribe( (data) => { this.data = data.result_body + this.existedNames = this.data.map(item => item.applicationName); }, () => { this.message.error('Failed to obtain application data'); @@ -41,7 +49,7 @@ export class ApplicationManagementComponent implements OnInit { this.createModalShow = true; } - createModalClose($event: any): void { + createModalClose($event: modalClose): void { this.createModalShow = false; if ($event.cancel) { return; @@ -49,7 +57,7 @@ export class ApplicationManagementComponent implements OnInit { this.getAllApplicationData() } - delete(data): void { + delete(data: Application): void { this.myhttp.deleteApplicationById(data.applicationId).subscribe((data) => { this.getAllApplicationData() if (data.result_header.result_code === 200) { @@ -62,7 +70,7 @@ export class ApplicationManagementComponent implements OnInit { }); } - navigateToDetail(data): void { + navigateToDetail(data: Application): void { this.router.navigate(['maas/use'], { queryParams: { id: data.applicationId, name: data.applicationName } }); } @@ -70,7 +78,7 @@ export class ApplicationManagementComponent implements OnInit { this.applicationShow = false; } - displayApplicationDetails(data): void { + displayApplicationDetails(data: Application): void { this.applicationShow = true; this.myhttp.getApplicationById(data.applicationId) .subscribe( @@ -82,4 +90,30 @@ export class ApplicationManagementComponent implements OnInit { } ) } + + edit(data: Application) { + this.applicationId = data.applicationId; + this.editModalShow = true; + } + + showDeleteConfirm(data: Application): void { + this.modalService.error({ + nzTitle: this.translate.instant('maas.deleteTitle'), + nzContent: this.translate.instant('maas.application.deleteApplicationContent'), + nzOkText: 'Yes', + nzOkType: 'danger', + nzOnOk: () => this.delete(data), + nzCancelText: 'No', + nzIconType: 'warning', + }); + } + + editModalClose($event: modalClose): void { + this.editModalShow = false; + if ($event.cancel) { + return; + } + this.getAllApplicationData() + } + }
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/build/application.type.ts b/usecaseui-portal/src/app/views/maas/build/application.type.ts index e3224e7c..a0348d7b 100644 --- a/usecaseui-portal/src/app/views/maas/build/application.type.ts +++ b/usecaseui-portal/src/app/views/maas/build/application.type.ts @@ -1,3 +1,4 @@ +import { Response } from '../knowledge-base-management/knowledge-base.type'; export type Application = { "applicationId"?: string, "applicationName": string, @@ -14,6 +15,12 @@ export type Application = { "prompt": string, "temperature": number, "top_p": number, - "openingRemarks": string + "openingRemarks": string, } +export type ApplicationsResponse = Response<Application[]>; + +export type ApplicationResponse = Response<Application>; + + + diff --git a/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.html b/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.html index c7c9b216..5a5bb445 100644 --- a/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.html +++ b/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.html @@ -22,7 +22,12 @@ <nz-form-control [nzSpan]="12"> <input type="text" nz-input formControlName="name"> <nz-form-explain *ngIf="validateForm.get('name').dirty && validateForm.get('name').errors"> - Please input application name! + <ng-container *ngIf="validateForm.get('name').hasError('required')"> + Please input application name + </ng-container> + <ng-container *ngIf="validateForm.get('name').hasError('duplicated')"> + {{ 'maas.nameDuplicateTip' | translate}} + </ng-container> </nz-form-explain> </nz-form-control> </nz-form-item> @@ -98,15 +103,22 @@ </nz-form-control> </nz-form-item> <nz-form-item> - <nz-form-label [nzSpan]="8" nzFor="prompt">Prompt</nz-form-label> + <nz-form-label [nzSpan]="8" nzFor="prompt" nzRequired>Prompt</nz-form-label> <nz-form-control [nzSpan]="12"> - <textarea rows="2" nz-input formControlName="prompt"></textarea> + <textarea #myTextarea id="myTextarea" rows="2" nz-input formControlName="prompt" [placeholder]="'maas.application.promptTip' | translate" maxlength="1000" minlength="20" (input)="updateCharCount()"></textarea> + <div #charCount id="charCount">0/1000</div> + <nz-form-explain *ngIf="validateForm.get('prompt').dirty && validateForm.get('prompt').errors"> + {{ 'maas.application.promptTip' | translate}} + </nz-form-explain> </nz-form-control> </nz-form-item> <nz-form-item> - <nz-form-label [nzSpan]="8" nzFor="openingRemarks">Opening Remarks</nz-form-label> + <nz-form-label [nzSpan]="8" nzFor="openingRemarks" nzRequired>Opening Remarks</nz-form-label> <nz-form-control [nzSpan]="12"> <textarea rows="2" nz-input formControlName="openingRemarks"></textarea> + <nz-form-explain *ngIf="validateForm.get('openingRemarks').dirty && validateForm.get('openingRemarks').errors"> + Please input opening remarks! + </nz-form-explain> </nz-form-control> </nz-form-item> diff --git a/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.less b/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.less index e8e3fca4..9156f1f9 100644 --- a/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.less +++ b/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.less @@ -41,4 +41,15 @@ .nz-select-container { width: 300px; +} + +:host ::ng-deep #myTextarea { + position: relative; +} + +#charCount { + position: absolute; + top: 9px; + right: 15px; + line-height: 20px; }
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.ts b/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.ts index 1bbef527..a4dba970 100644 --- a/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.ts +++ b/usecaseui-portal/src/app/views/maas/build/create-application-management/create-application-management.component.ts @@ -1,7 +1,10 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { NzMessageService } from "ng-zorro-antd"; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { MaasService } from '@src/app/core/services/maas.service'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { MaasApi } from '@src/app/api/maas.api'; +import { KnowledgeBase, MaaSPlatform, ModelInformation, Operators } from '../../knowledge-base-management/knowledge-base.type'; +import { Subject } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; @Component({ selector: 'app-create-application-management', @@ -13,15 +16,19 @@ export class CreateApplicationManagementComponent implements OnInit { validateForm: FormGroup; @Input() showModal: boolean; @Output() modalOpreation = new EventEmitter(); - operators: any[] = []; - filteredPlatforms: any[] = []; - filteredModels: any[] = []; - knowledgeBases: any[] = []; + operators: Operators[] = []; + filteredPlatforms: MaaSPlatform[] = []; + filteredModels: ModelInformation[] = []; + knowledgeBases: KnowledgeBase[] = []; temperature = 3; top_p = 3; + private submitSubject = new Subject<void>(); + @ViewChild('myTextarea') myTextarea: ElementRef; + @ViewChild('charCount') charCount: ElementRef; + @Input() existedNames: string[] = []; constructor( - private myhttp: MaasService, + private myhttp: MaasApi, private message: NzMessageService, private fb: FormBuilder ) { } @@ -29,19 +36,28 @@ export class CreateApplicationManagementComponent implements OnInit { ngOnInit() { this.fetchOperators(); this.initFormData(); + this.submitSubject.pipe(debounceTime(6000)).subscribe(() => this.executeSubmit()); + } + + nameDuplicateValidator = (control: FormControl): { [s: string]: boolean } => { + if (!control.value) { + return { required: true }; + } else if (this.existedNames.includes(control.value)) { + return { duplicated: true, error: true }; + } } initFormData() { this.validateForm = this.fb.group({ - name: [null, [Validators.required]], + name: [null, [Validators.required, this.nameDuplicateValidator]], description: [null], applicationType: [null, [Validators.required]], selectedOperator: [null, [Validators.required]], selectedPlatform: [null, [Validators.required]], selectedModel: [null, [Validators.required]], selectKnowledgeBase: [null, [Validators.required]], - prompt: [null], - openingRemarks: [null], + prompt: [null, [Validators.required, Validators.minLength(20), Validators.maxLength(1000)]], + openingRemarks: [null, [Validators.required]], temperature: [3, [Validators.required]], temperatureSlider: [3], top_p: [3, [Validators.required]], @@ -60,7 +76,7 @@ export class CreateApplicationManagementComponent implements OnInit { ); } - handleOperatorChange(value: any): void { + handleOperatorChange(value: Operators): void { if (value) { this.filteredPlatforms = value.maaSPlatformList; } else { @@ -71,7 +87,7 @@ export class CreateApplicationManagementComponent implements OnInit { this.validateForm.get('selectKnowledgeBase').setValue(null); } - handleMaasChange(value: any): void { + handleMaasChange(value: MaaSPlatform): void { if (value) { this.filteredModels = value.modelList; this.fetchKnowledgeBase(value); @@ -99,11 +115,16 @@ export class CreateApplicationManagementComponent implements OnInit { } handleOk() { + this.submitSubject.next(); + } + + private executeSubmit() { this.submitForm(); if (this.validateForm.invalid) { this.showModal = true; return; } + this.myhttp.createApplication(this.constructBody()).subscribe( (response) => { this.showModal = false; @@ -120,6 +141,7 @@ export class CreateApplicationManagementComponent implements OnInit { } ) } + constructBody() { const requestBody = { applicationName: this.validateForm.value.name, @@ -167,4 +189,12 @@ export class CreateApplicationManagementComponent implements OnInit { toppInputChange(event: number): void { this.validateForm.controls.top_pSlider.setValue(event); } -}
\ No newline at end of file + + updateCharCount() { + const textarea = this.myTextarea.nativeElement as HTMLTextAreaElement; + const charCount = textarea.value.length; + const maxLength = textarea.getAttribute('maxlength'); + this.charCount.nativeElement.innerText = charCount + '/' + maxLength; + } +} + diff --git a/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.html b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.html new file mode 100644 index 00000000..d2fb553b --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.html @@ -0,0 +1,18 @@ +<nz-modal [(nzVisible)]="showModal" [nzTitle]="title" (nzOnCancel)="handleCancel()" + (nzOnOk)="submitForm()" nzWidth="648px" nzHeight="800px"> + <form nz-form [formGroup]="validateForm" (ngSubmit)="checkForm()"> + <nz-form-item> + <nz-form-label [nzSpan]="8" nzFor="name" nzRequired>Application Name</nz-form-label> + <nz-form-control [nzSpan]="12"> + <input nz-input formControlName="name" placeholder="Please input application name" /> + <nz-form-explain *ngIf="validateForm.get('name').dirty && validateForm.get('name').errors">Please input application name</nz-form-explain> + </nz-form-control> + </nz-form-item> + <nz-form-item> + <nz-form-label [nzSpan]="8" nzFor="description">Application Description</nz-form-label> + <nz-form-control [nzSpan]="12"> + <textarea rows="2" formControlName="description" nz-input></textarea> + </nz-form-control> + </nz-form-item> + </form> +</nz-modal>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.less b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.less new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.less diff --git a/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.spec.ts b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.spec.ts new file mode 100644 index 00000000..40fc6bd5 --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditApplicationComponent } from './edit-application.component'; + +describe('EditApplicationComponent', () => { + let component: EditApplicationComponent; + let fixture: ComponentFixture<EditApplicationComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EditApplicationComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditApplicationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.ts b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.ts new file mode 100644 index 00000000..380cd5ff --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/build/edit-application/edit-application.component.ts @@ -0,0 +1,109 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MaasApi } from '@src/app/api/maas.api'; +import { NzMessageService } from 'ng-zorro-antd'; +import { Application } from '../application.type'; + +@Component({ + selector: 'app-edit-application', + templateUrl: './edit-application.component.html', + styleUrls: ['./edit-application.component.less'] +}) +export class EditApplicationComponent implements OnInit { + title = 'Edit Application'; + @Input() showModal: boolean; + @Input() applicationId: string; + @Output() modalOpreation = new EventEmitter(); + validateForm: FormGroup; + defalutApplication: Application = { + 'applicationId': '', + 'applicationName': '', + 'applicationDescription': '', + 'applicationType': '', + 'operatorId': '', + 'operatorName': '', + 'maaSPlatformId': '', + 'maaSPlatformName': '', + 'knowledgeBaseName': '', + 'knowledgeBaseId': '', + 'largeModelName': '', + 'largeModelId': '', + 'prompt': '', + 'temperature': 3, + 'top_p': 3, + 'openingRemarks': '', + } + application: Application = this.defalutApplication; + constructor( + private myhttp: MaasApi, + private message: NzMessageService, + private fb: FormBuilder, + ) { } + + ngOnInit() { + this.validateForm = this.fb.group({ + name: [this.application.applicationName, [Validators.required]], + description: [this.application.applicationDescription], + }); + this.fetchApplication(); + } + + checkForm(): void { + for (const i in this.validateForm.controls) { + this.validateForm.controls[i].markAsDirty(); + this.validateForm.controls[i].updateValueAndValidity(); + } + } + + submitForm(): void { + this.checkForm(); + this.create(); + } + + fetchApplication(): void { + this.myhttp.getApplicationById(this.applicationId) + .subscribe( + (response) => { + if (response.result_header.result_code !== 200) { + this.message.error('get application error'); + return; + } + this.application = response.result_body; + this.validateForm.patchValue({ + name: this.application.applicationName, + description: this.application.applicationDescription + }); + }, + () => { + this.message.error('Failed to obtain knowledge base data'); + } + ) + } + + handleCancel(): void { + this.showModal = false; + this.modalOpreation.emit({ 'cancel': true }); + } + + create() { + const metaData = { + ...this.application, + applicationName: this.validateForm.get('name').value, + applicationDescription: this.validateForm.get('description').value, + }; + this.myhttp.updateApplication(metaData).subscribe( + (response) => { + if (response.result_header.result_code === 200) { + this.message.success('update knowledge base successfully'); + } else { + this.message.error(response.result_header.result_message); + } + this.modalOpreation.emit({ 'cancel': false }); + }, + (error) => { + console.log('Upload failed', error); + } + ); + } + +} diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/create-knowledge-base/create-knowledge-base.component.ts b/usecaseui-portal/src/app/views/maas/knowledge-base-management/create-knowledge-base/create-knowledge-base.component.ts index 891fb9bd..2757137b 100644 --- a/usecaseui-portal/src/app/views/maas/knowledge-base-management/create-knowledge-base/create-knowledge-base.component.ts +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/create-knowledge-base/create-knowledge-base.component.ts @@ -3,7 +3,9 @@ import { Util } from '../../../../shared/utils/utils'; import { NzMessageService, UploadFile } from 'ng-zorro-antd'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { MaasService } from '@src/app/core/services/maas.service'; +import { MaasApi } from '@src/app/api/maas.api'; +import { Operator } from 'rxjs'; +import { MaaSPlatform, Operators } from '../knowledge-base.type'; @Component({ selector: 'app-create-knowledge-base', @@ -14,16 +16,13 @@ export class CreateKnowledgeBaseComponent implements OnInit { title = 'Add Knowledge Base'; @Input() showModal: boolean; @Output() modalOpreation = new EventEmitter(); - - apiUrl = '/api/usecaseui-llm-adaptation/v1/knowledgeBase/create'; - maasUrl = '/api/usecaseui-llm-adaptation/v1/operator/maas/getAll' - fileList: UploadFile[] = []; - operators: any[] = []; - filteredPlatforms: any[] = []; + fileList: File[] = []; + operators: Operators[] = []; + filteredPlatforms: MaaSPlatform[] = []; validateForm: FormGroup; constructor( - private myhttp: MaasService, + private myhttp: MaasApi, private Util: Util, private message: NzMessageService, private http: HttpClient, @@ -40,7 +39,7 @@ export class CreateKnowledgeBaseComponent implements OnInit { }); } fetchOperators(): void { - this.http.get<any>(this.maasUrl).subscribe( + this.myhttp.getOperators().subscribe( (response) => { this.operators = response.result_body; }, @@ -55,7 +54,7 @@ export class CreateKnowledgeBaseComponent implements OnInit { this.validateForm.controls[i].updateValueAndValidity(); } } - handleOperatorChange(value: any): void { + handleOperatorChange(value: Operators): void { if (value) { this.filteredPlatforms = value.maaSPlatformList; } else { @@ -63,7 +62,7 @@ export class CreateKnowledgeBaseComponent implements OnInit { } this.validateForm.get('selectedPlatform').setValue(null); } - beforeUpload = (file: UploadFile): boolean => { + beforeUpload = (file: File): boolean => { this.fileList.push(file); return false; } @@ -84,7 +83,7 @@ export class CreateKnowledgeBaseComponent implements OnInit { }; const metaDataJson = JSON.stringify(metaData); formData.append('metaData', metaDataJson); - this.fileList.forEach((file: any) => { + this.fileList.forEach((file: File) => { formData.append('files', file); }); return formData @@ -96,7 +95,7 @@ export class CreateKnowledgeBaseComponent implements OnInit { this.showModal = true; return; } - this.http.post<any>(this.apiUrl, this.constructBody()).subscribe( + this.myhttp.createKnowledgeBase(this.constructBody()).subscribe( (response) => { if (response.result_header.result_code === 200) { this.message.success('Created successfully'); diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.html b/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.html new file mode 100644 index 00000000..feee9b8f --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.html @@ -0,0 +1,18 @@ +<nz-modal [(nzVisible)]="showModal" [nzTitle]="title" (nzOnCancel)="handleCancel()" + (nzOnOk)="submitForm()" nzWidth="648px" nzHeight="800px"> + <form nz-form [formGroup]="validateForm" (ngSubmit)="checkForm()"> + <nz-form-item> + <nz-form-label [nzSpan]="8" nzFor="name" nzRequired>Knowledge Base Name</nz-form-label> + <nz-form-control [nzSpan]="12"> + <input nz-input formControlName="name" placeholder="Please input knowledge base name" /> + <nz-form-explain *ngIf="validateForm.get('name').dirty && validateForm.get('name').errors">Please input knowledge base name</nz-form-explain> + </nz-form-control> + </nz-form-item> + <nz-form-item> + <nz-form-label [nzSpan]="8" nzFor="description">Knowledge Base Description</nz-form-label> + <nz-form-control [nzSpan]="12"> + <textarea rows="2" formControlName="description" nz-input></textarea> + </nz-form-control> + </nz-form-item> + </form> +</nz-modal>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.less b/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.less new file mode 100644 index 00000000..a5e73a17 --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.less @@ -0,0 +1,21 @@ +.intent-management-modal { + .ant-input { + width: 300px; + } +} + + +.resizable-textarea { + min-width: 200px; + min-height: 80px; + resize: vertical; +} + +.disabled-input { + color: #00000040; + background-color: #f5f5f5; + border-color: #d9d9d9; + box-shadow: none; + cursor: not-allowed; + opacity: 1; +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.ts b/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.ts new file mode 100644 index 00000000..3a71d800 --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component.ts @@ -0,0 +1,106 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { NzMessageService } from 'ng-zorro-antd'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { KnowledgeBase } from '../knowledge-base.type'; +import { MaasApi } from '@src/app/api/maas.api'; + +@Component({ + selector: 'app-edit-knowledge-base', + templateUrl: './edit-knowledge-base.component.html', + styleUrls: ['./edit-knowledge-base.component.less'] +}) +export class EditKnowledgeBaseComponent implements OnInit { + title = 'Edit Knowledge Base'; + @Input() showModal: boolean; + @Input() knowledgeBaseId: string; + @Output() modalOpreation = new EventEmitter(); + validateForm: FormGroup; + defalutKnowledgeBase: KnowledgeBase = { + knowledgeBaseName: '', + knowledgeBaseDescription: '', + knowledgeBaseId: '', + operatorName: '', + maaSPlatformName: '', + maaSPlatformId: '', + updateTime: '', + filesName: [], + operatorId: '' + } + knowledgeBase: KnowledgeBase = this.defalutKnowledgeBase; + + constructor( + private myhttp: MaasApi, + private message: NzMessageService, + private fb: FormBuilder, + ) { } + + ngOnInit() { + this.validateForm = this.fb.group({ + name: [this.knowledgeBase.knowledgeBaseName, [Validators.required]], + description: [this.knowledgeBase.knowledgeBaseDescription], + }); + this.fetchKnowledgeBase(); + } + + checkForm(): void { + for (const i in this.validateForm.controls) { + this.validateForm.controls[i].markAsDirty(); + this.validateForm.controls[i].updateValueAndValidity(); + } + } + + submitForm(): void { + this.checkForm(); + this.create(); + } + + fetchKnowledgeBase(): void { + this.myhttp.getKnowledgeBaseById(this.knowledgeBaseId) + .subscribe( + (response) => { + if (response.result_header.result_code !== 200) { + this.message.error('get Knowledge Base error'); + return; + } + this.knowledgeBase = response.result_body; + this.validateForm.patchValue({ + name: this.knowledgeBase.knowledgeBaseName, + description: this.knowledgeBase.knowledgeBaseDescription + }); + }, + () => { + this.message.error('Failed to obtain knowledge base data'); + } + ) + } + + handleCancel(): void { + this.showModal = false; + this.modalOpreation.emit({ "cancel": true }); + } + + create() { + const body = { + knowledgeBaseId: this.knowledgeBase.knowledgeBaseId, + knowledgeBaseName: this.validateForm.get('name').value, + knowledgeBaseDescription: this.validateForm.get('description').value, + operatorId: this.knowledgeBase.operatorId, + operatorName: this.knowledgeBase.operatorName, + maaSPlatformId: this.knowledgeBase.maaSPlatformId, + maaSPlatformName: this.knowledgeBase.maaSPlatformName + }; + this.myhttp.updateKnowledgeBase(body).subscribe( + (response) => { + if (response.result_header.result_code === 200) { + this.message.success('update knowledge base successfully'); + } else { + this.message.error(response.result_header.result_message); + } + this.modalOpreation.emit({ "cancel": false }); + }, + (error) => { + console.log('Upload failed', error); + } + ); + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-detail/knowledge-base-detail.component.ts b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-detail/knowledge-base-detail.component.ts index 3bcc1455..49b9de4a 100644 --- a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-detail/knowledge-base-detail.component.ts +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-detail/knowledge-base-detail.component.ts @@ -20,11 +20,11 @@ export class KnowledgeBaseDetailComponent implements OnInit { handleCancel(): void { this.showModal = false; - this.modalOpreation.emit({ "cancel": true }); + this.modalOpreation.emit(); } handleOk(): void { this.showModal = false; - this.modalOpreation.emit({ "cancel": true }); + this.modalOpreation.emit(); } }
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.html b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.html index 6186ef2f..eb7c232a 100644 --- a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.html +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.html @@ -35,8 +35,8 @@ <td>{{ data.knowledgeBaseDescription }}</td> <td> <i class="anticon anticon-menu-fold" (click)="displayKnowledgeDetails(data)"></i> - <!-- <i class="anticon anticon-edit" (click)="editKnowedgeBase(data)"></i> --> - <i class="anticon anticon-delete" (click)="deleteKnowledgeBase(data)"></i> + <i class="anticon anticon-edit" (click)="editKnowedgeBase(data)"></i> + <i class="anticon anticon-delete" (click)="showDeleteConfirm(data)"></i> </td> </tr> </tbody> @@ -45,8 +45,8 @@ <app-create-knowledge-base *ngIf="createModalShow" [showModal]="createModalShow" (modalOpreation)="createModalClose($event)"></app-create-knowledge-base> <app-knowledge-base-detail *ngIf="knowledgeBaseShow" [showModal]="knowledgeBaseShow" - (modalOpreation)="knowledgeBaseDetailClose($event)" + (modalOpreation)="knowledgeBaseDetailClose()" [knowledgeBaseDetail]="knowledgeBaseDetail"></app-knowledge-base-detail> -<!-- <app-edit-knowledge-base *ngIf="editKnowledgeBaseShow" [showModal]="editKnowledgeBaseShow" [knowledgeBaseId]="knowledgeBaseId" -(modalOpreation)="editKnowledgeBaseModuleClose($event)"></app-edit-knowledge-base> -->
\ No newline at end of file +<app-edit-knowledge-base *ngIf="editKnowledgeBaseShow" [showModal]="editKnowledgeBaseShow" [knowledgeBaseId]="knowledgeBaseId" +(modalOpreation)="editKnowledgeBaseModuleClose($event)"></app-edit-knowledge-base>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.ts b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.ts index bad5808c..26b672cc 100644 --- a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.ts +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base-management.component.ts @@ -1,7 +1,8 @@ -import { Component, OnInit } from '@angular/core'; -import { NzMessageService } from "ng-zorro-antd"; -import { MaasService } from '@src/app/core/services/maas.service'; -import { KnowledgeBase } from './knowledge-base.type'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { NzMessageService, NzModalService } from "ng-zorro-antd"; +import { MaasApi } from '@src/app/api/maas.api'; +import { KnowledgeBase, modalClose } from './knowledge-base.type'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-knowledge-base-management', @@ -17,8 +18,10 @@ export class KnowledgeBaseManagementComponent implements OnInit { knowledgeBaseDetail: Object = {}; constructor( - private myhttp: MaasService, - private message: NzMessageService + private myhttp: MaasApi, + private message: NzMessageService, + private modalService: NzModalService, + private translate: TranslateService ) { } ngOnInit() { @@ -40,7 +43,7 @@ export class KnowledgeBaseManagementComponent implements OnInit { create(): void { this.createModalShow = true; } - createModalClose($event: any): void { + createModalClose($event: modalClose): void { this.createModalShow = false; if ($event.cancel) { return; @@ -48,7 +51,7 @@ export class KnowledgeBaseManagementComponent implements OnInit { this.getKnowledgeBaseData() } - editKnowledgeBaseModuleClose($event: any): void { + editKnowledgeBaseModuleClose($event: modalClose): void { this.editKnowledgeBaseShow = false; if ($event.cancel) { return; @@ -56,11 +59,11 @@ export class KnowledgeBaseManagementComponent implements OnInit { this.getKnowledgeBaseData() } - knowledgeBaseDetailClose($event: any): void { + knowledgeBaseDetailClose(): void { this.knowledgeBaseShow = false; } - deleteKnowledgeBase(data): void { + deleteKnowledgeBase(data: KnowledgeBase): void { this.myhttp.deleteKnowledgeBaseData(data.knowledgeBaseId).subscribe((data) => { this.getKnowledgeBaseData() if (data.result_header.result_code === 200) { @@ -86,9 +89,20 @@ export class KnowledgeBaseManagementComponent implements OnInit { ) } - editKnowedgeBase(data) { + editKnowedgeBase(data: KnowledgeBase) { this.knowledgeBaseId = data.knowledgeBaseId; this.editKnowledgeBaseShow = true; } + showDeleteConfirm(data: KnowledgeBase): void { + this.modalService.confirm({ + nzTitle: this.translate.instant('maas.deleteTitle'), + nzContent: this.translate.instant('maas.knowledgeBase.deleteKnowledgeBaseContent'), + nzOkText: 'Yes', + nzOkType: 'danger', + nzOnOk: () => this.deleteKnowledgeBase(data), + nzCancelText: 'No' + }); + } + }
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base.type.ts b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base.type.ts index e6004ff1..b9ec432d 100644 --- a/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base.type.ts +++ b/usecaseui-portal/src/app/views/maas/knowledge-base-management/knowledge-base.type.ts @@ -10,11 +10,42 @@ export type KnowledgeBase = { knowledgeBaseId: string } -export type KnowledgeBaseResponse = { - result_body: KnowledgeBase, - result_header: { - result_code :number, +export type KnowledgeBaseResponse = Response<KnowledgeBase> + +export type KnowledgeBasesResponse = Response<Array<KnowledgeBase>> + +export type Operators = { + operatorId: string, + operatorName: string, + maaSPlatformList: Array<MaaSPlatform>, +} + +export type MaaSPlatform = { + maaSPlatformId: string, + maaSPlatformName: string, + operatorId: string, + operatorName: string, + modelList: Array<ModelInformation>; +} + +export type ModelInformation = { + modelId: string, + modelName: string, +} + +export type ResponseHeader = { + result_header:{ + result_code: number, result_message: string } } +export type OperatorsResponse = Response<Array<Operators>> + +export type Response<T> = { + result_body: T +} & ResponseHeader + +export type modalClose = { + cancel: boolean +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/maas-service.service.spec.ts b/usecaseui-portal/src/app/views/maas/maas-service.service.spec.ts new file mode 100644 index 00000000..69f472e2 --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/maas-service.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { MaasServiceService } from './maas-service.service'; + +describe('MaasServiceService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MaasServiceService] + }); + }); + + it('should be created', inject([MaasServiceService], (service: MaasServiceService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/usecaseui-portal/src/app/views/maas/maas-service.service.ts b/usecaseui-portal/src/app/views/maas/maas-service.service.ts new file mode 100644 index 00000000..257f7a1c --- /dev/null +++ b/usecaseui-portal/src/app/views/maas/maas-service.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class MaasService { + + constructor() { } + + generateUniqueId(): string { + const timestamp = new Date().getTime(); + const randomNum = Math.floor(Math.random() * 1000000); + return `${timestamp}${randomNum}`; + } + +} diff --git a/usecaseui-portal/src/app/views/maas/maas.module.ts b/usecaseui-portal/src/app/views/maas/maas.module.ts index 814cd635..8c666903 100644 --- a/usecaseui-portal/src/app/views/maas/maas.module.ts +++ b/usecaseui-portal/src/app/views/maas/maas.module.ts @@ -10,28 +10,34 @@ import { KnowledgeBaseManagementComponent } from './knowledge-base-management/kn import { CreateKnowledgeBaseComponent } from './knowledge-base-management/create-knowledge-base/create-knowledge-base.component'; import { KnowledgeBaseDetailComponent } from './knowledge-base-management/knowledge-base-detail/knowledge-base-detail.component'; import { SharedModule } from '@src/app/shared/module/sharded.module'; -import { MaasService } from '@src/app/core/services/maas.service'; -// import { EditKnowledgeBaseComponent } from './knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component'; +import { MaasApi } from '@src/app/api/maas.api'; +import { EditKnowledgeBaseComponent } from './knowledge-base-management/edit-knowledge-base/edit-knowledge-base.component'; +import { EditApplicationComponent } from './build/edit-application/edit-application.component'; +import { MaasService } from './maas-service.service'; +import { KnowledgeBaseService } from './knowledge-base-management/knowledge-base.service'; @NgModule({ providers: [ - MaasService + MaasApi, + MaasService, + KnowledgeBaseService ], imports: [ SharedModule, - MaasRoutingModule, + MaasRoutingModule ], declarations: [ ApplicationManagementComponent, - CreateApplicationManagementComponent, - UseApplicationComponent, - ApplicationDetailComponent, - DescriptionComponent, - DescriptionItemComponent, + CreateApplicationManagementComponent, + UseApplicationComponent, + ApplicationDetailComponent, + DescriptionComponent, + DescriptionItemComponent, KnowledgeBaseManagementComponent, - CreateKnowledgeBaseComponent, - KnowledgeBaseDetailComponent, - // EditKnowledgeBaseComponent + CreateKnowledgeBaseComponent, + KnowledgeBaseDetailComponent, + EditKnowledgeBaseComponent, + EditApplicationComponent ] }) export class MaasModule { } diff --git a/usecaseui-portal/src/app/views/maas/use/use-application.component.html b/usecaseui-portal/src/app/views/maas/use/use-application.component.html index 52322960..f8c65794 100644 --- a/usecaseui-portal/src/app/views/maas/use/use-application.component.html +++ b/usecaseui-portal/src/app/views/maas/use/use-application.component.html @@ -22,23 +22,46 @@ <div class="chat-container"> <div *ngFor="let chat of chatHistory"> <div class="question"> - <img src="assets/images/user.png"> - <span>{{ chat.question }}</span> + <div class="question-icon"> + <div class="question-icon-actions"> + <span class="anticon anticon-copy question-action" (click)="copy(chat.question)" + [nz-tooltip]="'maas.copy' | translate"></span> + <span *ngIf="chat.status==='finished'" class="anticon anticon-delete question-action" (click)="deleteQuestion(chat.questionId)" + [nz-tooltip]="'maas.delete' | translate"></span> + </div> + <div> + <img src="assets/images/user.png"> + </div> + </div> + <div class="question-container"> + <span class="question-text">{{ chat.question }}</span> + </div> </div> <br> - <div class="answer"> - <img src="assets/images/answer.png"> - <span>{{ chat.answer }}</span> + <div> + <div class="answer-icon"> + <img src="assets/images/answer.png"> + <span class="anticon anticon-copy answer-action" (click)="copy(chat.answer)" + [nz-tooltip]="'maas.copy' | translate"></span> + </div> + <span class="answer-text"> + <span class="answer" [ngClass]="{'hidden-cursor': chat.status==='finished'}" #answerText + id="answerText">{{chat.answer}}</span> + </span> </div> <br> </div> </div> <div class="input-wrapper"> - <textarea nz-tooltip nz-input [nzAutosize]="{ minRows: 2, maxRows: 2 }" [(ngModel)]="question" - class="text-input"></textarea> - <i class="icon" (click)="submitQuestion()"> - <img src="assets/images/send.png"> - </i> + <textarea nzAutosize nz-input [(ngModel)]="question" class="text-input question-input"></textarea> + <div class="send-wrapper" [ngClass]="{'stop-wrapper': isGeneratingAnswer}"> + <div class="icon" (click)="doAction()" + [ngClass]="{'send-disabled': !isGeneratingAnswer &&!question, 'send-enabled': question && !isGeneratingAnswer, 'stop-generating': isGeneratingAnswer}" + [nz-tooltip]="isGeneratingAnswer ? stopGenerating : send"> + <img [src]="isGeneratingAnswer ? 'assets/images/stop-generating.svg' : 'assets/images/send.svg'" + [alt]="isGeneratingAnswer ? stopGenerating : send"> + </div> + </div> </div> </div>
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/use/use-application.component.less b/usecaseui-portal/src/app/views/maas/use/use-application.component.less index e4570d4f..c2f926fb 100644 --- a/usecaseui-portal/src/app/views/maas/use/use-application.component.less +++ b/usecaseui-portal/src/app/views/maas/use/use-application.component.less @@ -7,76 +7,147 @@ display: flex; flex-direction: column; } + .fixed-select-wrapper { - position: fixed; - top: 20px; - left: 50%; - transform: translateX(-50%); - z-index: 1000; - } + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + z-index: 1000; +} + .chat-container { margin: 60px 0; padding: 0 100px; } + .question { display: flex; + flex-direction: column; + gap: 8px; + display: flex; + align-items: flex-end; } -.record-input{ + +.record-input { margin-left: 10px; resize: none; } -.question span { - background-color: white; - font-size: 16px; - margin-left: 10px; - padding: 10px; + +.question-icon { + display: flex; +} + +.question-icon-actions { + color: #8A95A7; + display: flex; border-radius: 8px; + overflow: hidden; + border: 1px solid #fff; + margin-right: 8px; + height: 24px; + margin-top:3px; } -.question img { - width: 28px; - height: 28px; + +.question-icon-actions span:not(:first-child) { + border-left: 1px solid #fff; } -.answer { - display: flex; +.question-action { + width: 24px; + padding-top: 4px; } -.answer span{ + +.question-container { + margin: 8px 0; +} + +.question-text { background-color: white; font-size: 16px; - margin-left: 10px; padding: 10px; border-radius: 8px; +} + +.answer-action { + height: 24px; + width: 24px; + border: 1px solid #fff; + border-radius: 8px; + padding-top: 3px; +} + +#answerText { + font-size: 16px; + padding: 10px; white-space: pre-line; + width: auto; + display: inline-block; + position: relative; + min-height: 40px; +} + +.answer::after { + content: ''; + display: inline-block; + width: 1px; + height: 16px; + background-color: black; + position: absolute; + animation: blink 1s step-end infinite; + margin-top: 5px; } -.answer img { + +.hidden-cursor::after { + display: none; +} + +.answer-text { + margin: 8px 0; + background-color: white; + display: inline-block; + border-radius: 8px; +} + +.answer-container img, .answer-icon img, .question img{ width: 28px; height: 28px; } +.showing-answer { + display: inline-block; +} + +.answer-done { + display: none; +} + .chat-input { - margin-top: 30px; - padding-left: 1100px; + margin-top: 30px; + padding-left: 1100px; } .input-wrapper { display: flex; justify-content: space-around; - width: e("calc(100% - 240px)"); margin: 0 100px; - margin-left: 138px; padding: 10px; background: #fff; border-radius: 8px; border: 2px solid #8a2be2; } +.question-input { + width: 100%; +} + .text-input { - width: e("calc(100% - 50px)"); background-color: white; color: black; text-align: left; border: 0; resize: none; + padding-right: 40px; &:focus { border: 0 !important; @@ -84,15 +155,68 @@ } } -.icon { - width: 50px; - height: 50px; +.send-wrapper { + position: relative; + right: 16px; + + .icon { + width: 32px; + height: 32px; + position: absolute; + // top: 50%; + left: 50%; + border-radius: 8px; + transform: translateX(-50%) translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + + >img { + width: 20px; + height: 20px; + } + } +} + +.stop-wrapper { + margin-right: 16px; +} + +.send-disabled { + cursor: not-allowed; + background: rgb(229, 229, 229); + top: 50%; +} + +.send-enabled { + // background: rgb(72, 127, 255); + background: #8a2be2; cursor: pointer; + top: 50%; +} - >img { - width: 35px; - height: 31px; - margin-top: 15px; - margin-left: 15px; +@keyframes zoomStopIcon { + 0% { + transform: scale(0.8); } + + 100% { + transform: scale(1.2); + } +} + +.stop-generating { + background: rgb(229, 229, 229); + animation: 0.4s ease 0s infinite alternate none running zoomStopIcon; + cursor: pointer; } + + +@keyframes blink { + from, to { + background-color: transparent; + } + 50% { + background-color: black; + } +}
\ No newline at end of file diff --git a/usecaseui-portal/src/app/views/maas/use/use-application.component.ts b/usecaseui-portal/src/app/views/maas/use/use-application.component.ts index 96b17fd0..e73463d6 100644 --- a/usecaseui-portal/src/app/views/maas/use/use-application.component.ts +++ b/usecaseui-portal/src/app/views/maas/use/use-application.component.ts @@ -1,10 +1,12 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, ElementRef, OnInit, Renderer2, ViewChild } from '@angular/core'; import { NzMessageService } from 'ng-zorro-antd'; -import { HttpClient } from '@angular/common/http'; import { SSE } from "sse.js"; import { ActivatedRoute } from '@angular/router'; -import { MaasService } from '@src/app/core/services/maas.service'; - +import { MaasApi } from '@src/app/api/maas.api'; +import { TranslateService } from '@ngx-translate/core'; +import { MaasService } from '../maas-service.service'; +export type StatusEnum = 'typing' | 'finished'; +export type Chat = { question: string, answer: string, questionId: string, status: StatusEnum }; @Component({ selector: 'app-use-application', templateUrl: './use-application.component.html', @@ -14,17 +16,23 @@ export class UseApplicationComponent implements OnInit { question: string; communicationMessage: string; - chatHistory: { question: string, answer: string }[] = []; + chatHistory: Chat[] = []; apiUrl = '/api/usecaseui-llm-adaptation/v1/application/chat'; queryParams: { id?: string; name?: string } = {}; selectedName: string | null = null; - options: any[] = []; - + options: Array<{ nzValue: string, nzLabel: string }> = []; + send = this.translate.instant('maas.send'); + private currentSSE: SSE | null = null; + isGeneratingAnswer: boolean = false; + stopGenerating = this.translate.instant('maas.stopGenerating'); + questionId = ''; constructor( - private http: HttpClient, private message: NzMessageService, private route: ActivatedRoute, - private myhttp: MaasService, + private myhttp: MaasApi, + private translate: TranslateService, + private maasService: MaasService, + private renderer: Renderer2 ) { } ngOnInit() { @@ -34,25 +42,66 @@ export class UseApplicationComponent implements OnInit { this.selectedName = this.queryParams.id; }); } + + close() { + if (this.currentSSE) { + this.currentSSE.close(); + } + } + + doAction() { + if (this.isGeneratingAnswer) { + this.close(); + this.chatHistory.forEach(item => {item.status = 'finished'}); + this.isGeneratingAnswer = false; + } else { + this.submitQuestion(); + } + } submitQuestion() { + if (!this.question) { + return; + } + this.isGeneratingAnswer = true; const chatParam = { applicationId: this.selectedName, - question: this.question + question: this.question, + questionId: this.maasService.generateUniqueId() }; - const source = new SSE(this.apiUrl, { headers: { 'Content-Type': 'application/json' }, payload: JSON.stringify(chatParam), method: 'POST' }); - const lin = this.question; - source.addEventListener('message', (event) => { - const existingEntryIndex = this.chatHistory.findIndex(entry => entry.question === lin); - if (existingEntryIndex !== -1) { - this.chatHistory[existingEntryIndex].answer += event.data.replace(/__SPACE__/g, ' '); - } else { - this.chatHistory.push({ question: lin, answer: event.data }); + this.currentSSE = new SSE(this.apiUrl, { headers: { 'Content-Type': 'application/json' }, payload: JSON.stringify(chatParam), method: 'POST' }); + const questionId = chatParam.questionId; + this.chatHistory.push({ question: chatParam.question, questionId: chatParam.questionId, answer: '', status: 'typing' }); + this.currentSSE.addEventListener('message', (event) => { + const chat = this.chatHistory.find(chatItem => chatItem.questionId === questionId); + if (chat) { + if (['[DONE]', 'Network Error'].includes(event.data)) { + chat.status = 'finished'; + this.isGeneratingAnswer = false; + if (event.data === 'Network Error') { + this.updateAnswer(event, chat); + } + this.close(); + } else { + this.updateAnswer(event, chat); + } } }); + this.currentSSE.addEventListener('error', () => { + this.currentSSE = null; + this.isGeneratingAnswer = false; + }); + this.currentSSE.addEventListener('close', () => { + this.currentSSE = null; + this.isGeneratingAnswer = false; + }); this.question = ''; } + updateAnswer(event: any, chat: Chat): void { + chat.answer += event.data.replace(/__SPACE__/g, ' '); + } + fetchAllApplication(): void { this.myhttp.getAllApplication() .subscribe( @@ -61,10 +110,24 @@ export class UseApplicationComponent implements OnInit { nzValue: item.applicationId, nzLabel: item.applicationName })); + this.selectedName = this.options.length > 0 ? this.options[0].nzValue : ''; }, () => { this.message.error('Failed to obtain intent data'); } ) } + + async copy(content: string): Promise<void> { + try { + await (navigator as any).clipboard.writeText(content); + this.message.success(this.translate.instant('maas.copy_to_clipboard')); + } catch (err) { + console.error(this.translate.instant('maas.copy_failed') + ': ', err); + } + } + + deleteQuestion(questionId: string): void { + this.chatHistory = this.chatHistory.filter(item => item.questionId !== questionId); + } } |