Skip to content

Commit

Permalink
Merge pull request #17 from xpert-ai/develop
Browse files Browse the repository at this point in the history
version 3.0.8
  • Loading branch information
tiven-w authored Dec 28, 2024
2 parents 93b8c8b + 0605af3 commit cf468a4
Show file tree
Hide file tree
Showing 157 changed files with 2,901 additions and 1,030 deletions.
4 changes: 3 additions & 1 deletion .deploy/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
ARG HOST
ARG PORT
ARG LOG_LEVEL

FROM node:18-alpine AS build

Expand Down Expand Up @@ -66,7 +67,8 @@ ENV NODE_OPTIONS=${NODE_OPTIONS:-"--max-old-space-size=2048"} \
DB_SSL_MODE=${DB_SSL_MODE} \
HOST=${HOST:-0.0.0.0} \
PORT=${PORT:-3000} \
DEMO=${DEMO:-false}
DEMO=${DEMO:-false} \
LOG_LEVEL=${LOG_LEVEL}

WORKDIR /srv/pangolin

Expand Down
2 changes: 2 additions & 0 deletions .env.local
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# set true if running inside Docker container
IS_DOCKER=false

# LOG_LEVEL=verbose

# set true if running as a Demo
DEMO=false
ENABLE_LOCAL_AGENT=false
Expand Down
39 changes: 22 additions & 17 deletions apps/cloud/src/app/@core/services/copilot-server.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { OrganizationBaseCrudService } from '@metad/cloud/state'
import { NGXLogger } from 'ngx-logger'

import { effect, inject, Injectable, signal } from '@angular/core'
import { inject, Injectable } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { OrganizationBaseCrudService } from '@metad/cloud/state'
import { toParams } from '@metad/core'
import {
BehaviorSubject,
Observable,
shareReplay,
switchMap
} from 'rxjs'
import { NGXLogger } from 'ngx-logger'
import { BehaviorSubject, Observable, shareReplay, switchMap } from 'rxjs'
import { API_COPILOT } from '../constants/app.constants'
import { ICopilotWithProvider, ICopilot as IServerCopilot, AiModelTypeEnum, ParameterRule, IAiProviderEntity, ICopilot, AiProviderRole } from '../types'

import {
AiModelTypeEnum,
AiProviderRole,
IAiProviderEntity,
ICopilot,
ICopilotWithProvider,
ParameterRule
} from '../types'

@Injectable({ providedIn: 'root' })
export class CopilotServerService extends OrganizationBaseCrudService<ICopilot> {
Expand All @@ -21,11 +21,16 @@ export class CopilotServerService extends OrganizationBaseCrudService<ICopilot>
readonly refresh$ = new BehaviorSubject(false)

private readonly modelsByType = new Map<AiModelTypeEnum, Observable<ICopilotWithProvider[]>>()
private readonly aiProviders$ = this.httpClient.get<IAiProviderEntity[]>(API_COPILOT + `/providers`).pipe(shareReplay(1))
private readonly aiProviders$ = this.httpClient
.get<IAiProviderEntity[]>(API_COPILOT + `/providers`)
.pipe(shareReplay(1))

/**
* All available copilots (enabled or tenant free quota)
*/
private readonly copilots$ = this.refresh$.pipe(
switchMap(() => this.selectOrganizationId()),
switchMap(() => this.httpClient.get<ICopilot[]>(API_COPILOT)),
switchMap(() => this.httpClient.get<ICopilot[]>(this.apiBaseUrl + `/availables`)),
shareReplay(1)
)

Expand Down Expand Up @@ -81,8 +86,8 @@ export class CopilotServerService extends OrganizationBaseCrudService<ICopilot>
}

export function injectAiProviders() {
const service = inject(CopilotServerService)
return toSignal(service.getAiProviders())
const service = inject(CopilotServerService)
return toSignal(service.getAiProviders())
}

export function injectCopilotServer() {
Expand All @@ -92,4 +97,4 @@ export function injectCopilotServer() {
export function injectCopilots() {
const server = injectCopilotServer()
return toSignal(server.getCopilots())
}
}
6 changes: 5 additions & 1 deletion apps/cloud/src/app/@core/services/xpert.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { toParams } from '@metad/ocap-angular/core'
import { NGXLogger } from 'ngx-logger'
import { BehaviorSubject, tap } from 'rxjs'
import { API_XPERT_ROLE } from '../constants/app.constants'
import { ICopilotStore, IIntegration, IUser, IXpert, IXpertAgentExecution, OrderTypeEnum, TChatRequest, TDeleteResult, TXpertTeamDraft, XpertTypeEnum } from '../types'
import { ICopilotStore, IIntegration, IUser, IXpert, IXpertAgentExecution, OrderTypeEnum, TChatRequest, TDeleteResult, TStateVariable, TXpertTeamDraft, XpertTypeEnum } from '../types'
import { XpertWorkspaceBaseCrudService } from './xpert-workspace.service'
import { injectApiBaseUrl } from '../providers'
import { injectFetchEventSource } from './fetch-event-source'
Expand Down Expand Up @@ -116,6 +116,10 @@ export class XpertService extends XpertWorkspaceBaseCrudService<IXpert> {
clearMemory(id: string) {
return this.httpClient.delete<TDeleteResult>(this.apiBaseUrl + `/${id}/memory`,)
}

getVariables(id: string, agentKey: string) {
return this.httpClient.get<TStateVariable[]>(this.apiBaseUrl + `/${id}/agent/${agentKey}/variables`)
}
}

export function injectXpertService() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<input
class="block px-3 w-full h-9 bg-gray-100 text-sm rounded-lg border border-transparent appearance-none outline-none caret-primary-600 hover:border-[rgba(0,0,0,0.08)] hover:bg-gray-50 focus:bg-white focus:border-gray-300 focus:shadow-xs placeholder:text-sm placeholder:text-gray-400 false false"
[placeholder]="credential.placeholder | i18n"
type="text"
type="password"
[formControlName]="credential.variable"
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
[regex]="regex"
[content]="prompt()"
customClasses="inline-block text-primary-500 bg-transparent"
(keyup)="onKeyup($event)"
>
</div>
</div>
Expand All @@ -63,4 +64,16 @@
<div class="w-5 h-[3px] rounded-sm bg-gray-300"></div>
</div>
</div>
</div>
</div>

<ng-template #suggestionsTemplate>
<div cdkMenu class="cdk-menu__medium">
<ul>
@for (variable of variables(); track variable.name) {
<li cdkMenuItem class="max-w-sm space-x-2 truncate" (click)="selectVariable(variable)">
{{ variable.name }} <span class="px-1 text-sm rounded-md bg-gray-50 text-primary-300">{{variable.type}}</span> <span class="text-sm italic">{{variable.description}}</span>
</li>
}
</ul>
</div>
</ng-template>
146 changes: 121 additions & 25 deletions apps/cloud/src/app/@shared/copilot/prompt-editor/editor.component.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,64 @@
import { CdkMenuModule } from '@angular/cdk/menu'
import { Overlay, OverlayRef } from '@angular/cdk/overlay'
import { TemplatePortal } from '@angular/cdk/portal'
import { CommonModule } from '@angular/common'
import { ChangeDetectionStrategy, Component, computed, effect, ElementRef, HostListener, inject, input, model, numberAttribute, viewChild } from '@angular/core'
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
ElementRef,
HostListener,
inject,
input,
model,
numberAttribute,
signal,
TemplateRef,
ViewChild,
ViewContainerRef
} from '@angular/core'
import { FormsModule } from '@angular/forms'
import { MatDialog } from '@angular/material/dialog'
import { MatTooltipModule } from '@angular/material/tooltip'
import { NgmHighlightVarDirective } from '@metad/ocap-angular/common'
import { TranslateModule } from '@ngx-translate/core'
import { CopilotPromptGeneratorComponent } from '../prompt-generator/generator.component'
import { TStateVariable } from '../../../@core'

@Component({
selector: 'copilot-prompt-editor',
templateUrl: './editor.component.html',
styleUrls: ['./editor.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule, CdkMenuModule, FormsModule, TranslateModule, MatTooltipModule, NgmHighlightVarDirective],
imports: [CommonModule, CdkMenuModule, FormsModule, TranslateModule, MatTooltipModule, NgmHighlightVarDirective]
})
export class CopilotPromptEditorComponent {

readonly #dialog = inject(MatDialog)
readonly #vcr = inject(ViewContainerRef)

readonly regex = `{{(.*?)}}`

readonly initHeight = input<number, number | string>(210, {
transform: numberAttribute
})
readonly tooltip = input<string>()
readonly variables = input<TStateVariable[]>()

@ViewChild('editablePrompt', { static: true }) editablePrompt!: ElementRef
@ViewChild('suggestionsTemplate', { static: true }) suggestionsTemplate!: TemplateRef<any>
overlayRef: OverlayRef | null = null

readonly prompt = model<string>()
readonly promptLength = computed(() => this.prompt()?.length)

height = this.initHeight()
private isResizing = false
private startY = 0
private startHeight = 0

height = this.initHeight();
private isResizing = false;
private startY = 0;
private startHeight = 0;

constructor() {
constructor(private overlay: Overlay) {
effect(() => {
if (this.initHeight()) {
this.height = this.initHeight()
Expand All @@ -45,15 +67,18 @@ export class CopilotPromptEditorComponent {
}

generate() {
this.#dialog.open(CopilotPromptGeneratorComponent, {
this.#dialog
.open(CopilotPromptGeneratorComponent, {
panelClass: 'large'
}).afterClosed().subscribe({
next: (result) => {
if (result) {
this.prompt.set(result)
})
.afterClosed()
.subscribe({
next: (result) => {
if (result) {
this.prompt.set(result)
}
}
}
})
})
}

onPromptChange(editor: HTMLDivElement) {
Expand All @@ -63,26 +88,97 @@ export class CopilotPromptEditorComponent {
this.prompt.set(formatInnerHTML(editor.innerHTML))
}

onKeyup(event: KeyboardEvent) {
if (event.key === 'Escape') {
return this.hideSuggestions()
}

const inputText = (event.target as HTMLElement).innerText
const regex = /{{(?=\s+\S*)|{{$/

if (regex.test(inputText)) {
this.showSuggestions()
} else {
this.hideSuggestions()
}
}

selectVariable(variable: TStateVariable) {
const editablePrompt: HTMLDivElement = this.editablePrompt.nativeElement
const text = editablePrompt.innerText
const regex = /{{(?=\s+\S*)|{{$/
const updatedText = text.replace(regex, `{{${variable.name}}}`)
editablePrompt.innerText = updatedText
this.hideSuggestions()

// focus the edit div and insert cursor in end
editablePrompt.focus()
const range = document.createRange()
const selection = window.getSelection()
range.selectNodeContents(editablePrompt)
range.collapse(false)
selection.removeAllRanges()
selection.addRange(range)
}

getCaretCoordinates(): { top: number; left: number } {
const selection = window.getSelection()
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0).cloneRange()
const rect = range.getClientRects()[0]
if (rect) {
return { top: rect.top, left: rect.left }
}
}
return { top: 0, left: 0 }
}

showSuggestions() {
if (!this.variables()?.length) {
return
}
const caretCoords = this.getCaretCoordinates()
const positionStrategy = this.overlay
.position()
.flexibleConnectedTo(caretCoords.left ? { x: caretCoords.left, y: caretCoords.top } : this.editablePrompt.nativeElement)
.withPositions([{ originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom' }])

if (!this.overlayRef) {
this.overlayRef = this.overlay.create({ positionStrategy })
const portal = new TemplatePortal(this.suggestionsTemplate, this.#vcr)
this.overlayRef.attach(portal)
} else {
this.overlayRef.updatePositionStrategy(positionStrategy)
}
}

hideSuggestions() {
if (this.overlayRef) {
this.overlayRef.detach()
this.overlayRef = null
}
}

onMouseDown(event: MouseEvent): void {
this.isResizing = true;
this.startY = event.clientY;
this.startHeight = this.height;
event.preventDefault();
this.isResizing = true
this.startY = event.clientY
this.startHeight = this.height
event.preventDefault()
}

@HostListener('document:mousemove', ['$event'])
onMouseMove(event: MouseEvent): void {
if (this.isResizing) {
const offset = event.clientY - this.startY;
this.height = this.startHeight + offset;
if (this.height < 50) this.height = 50; // 设置最小高度
event.preventDefault();
const offset = event.clientY - this.startY
this.height = this.startHeight + offset
if (this.height < 50) this.height = 50 // 设置最小高度
event.preventDefault()
}
}

@HostListener('document:mouseup')
onMouseUp(): void {
this.isResizing = false;
this.isResizing = false
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/cloud/src/app/@shared/model/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './formula.component'
export * from './variable-form/variable.component'
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<header cdkDrag cdkDragRootElement=".cdk-overlay-pane" cdkDragHandle class="pb-2 cursor-move">
<h4 class="text-xl pointer-events-none">
{{ 'PAC.MODEL.EditCubeVariable' | translate: {Default: 'Edit cube variable'} }}
</h4>
</header>

<div class="flex flex-col">
<div class="flex justify-between item-center gap-4">
<ngm-input class="" [label]="'PAC.KEY_WORDS.Name' | translate: {Default: 'Name'}" disabled [ngModel]="name()"/>
<ngm-input [label]="'PAC.KEY_WORDS.Caption' | translate: {Default: 'Caption'}" [(ngModel)]="caption"/>
</div>

<ngm-variable [label]="variable().caption" displayDensity="cosy"
[dataSettings]="dataSettings()"
[variable]="variable()"
[(ngModel)]="defaultLow"
/>
</div>

<div class="w-full flex justify-end items-center">
<div ngmButtonGroup>
<button mat-button (click)="cancel()">
{{ 'COMPONENTS.COMMON.CANCEL' | translate: {Default: 'Cancel'} }}
</button>
<button mat-raised-button color="accent" cdkFocusInitial (click)="apply()">
{{ 'COMPONENTS.COMMON.Confirm' | translate: {Default: 'Confirm'} }}
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
@apply flex flex-col bg-components-card-bg p-4 rounded-2xl;
}
Loading

0 comments on commit cf468a4

Please sign in to comment.