Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3.0.5 #14

Merged
merged 30 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5860a5b
feat: interrupted tool call
meta-d Dec 11, 2024
965dfc9
feat: sensitive tools
meta-d Dec 11, 2024
35d4ea0
feat: Interrupted chat
meta-d Dec 12, 2024
c227eba
feat: human in the loop
meta-d Dec 12, 2024
bedd050
feat: reject INTERRUPTED chat
meta-d Dec 12, 2024
b3ee9c0
feat: mutiple tool calls info
meta-d Dec 12, 2024
0e12a68
feat: sensitive operation tool call
meta-d Dec 13, 2024
44f2c35
Merge branch 'develop' into v3.0.4
meta-d Dec 13, 2024
82aaa73
version 3.0.4
meta-d Dec 13, 2024
6a37d8b
feat: primary agent not sensitive
meta-d Dec 13, 2024
d1c7743
feat: conversation summarize
meta-d Dec 14, 2024
7f3e7c7
feat: long term memory
meta-d Dec 15, 2024
4eb5b4b
feat: feeback to summarize long-term memory
meta-d Dec 16, 2024
2bd4bc2
feat: fix agent primary
meta-d Dec 16, 2024
37eef2c
feat: xpert's memory page
meta-d Dec 17, 2024
8d7d0ff
feat: long term memory of xpert
meta-d Dec 17, 2024
2aa1b8a
feat: use long-term memory in chat
meta-d Dec 18, 2024
ff0cc77
feat: add findMyAll in curd service
meta-d Dec 18, 2024
6ab7736
feat: register user
meta-d Dec 18, 2024
b33162c
feat: long-term memory every user
meta-d Dec 18, 2024
2a97e95
feat: long-term memory
meta-d Dec 18, 2024
49b660e
feat: execution tools events
meta-d Dec 18, 2024
73561a6
feat: fix xpert memory
meta-d Dec 18, 2024
3431705
feat: semantic search for long-term store
meta-d Dec 19, 2024
8dd154f
feat: long-term trigger events
meta-d Dec 19, 2024
803fc7a
feat: memory profile
meta-d Dec 20, 2024
7756017
version 3.0.5
meta-d Dec 20, 2024
867226a
feat: update mysql client to 2
meta-d Dec 20, 2024
c90a8ff
feat: memory of xpert
meta-d Dec 20, 2024
6c0cc56
fix: null memory store
meta-d Dec 20, 2024
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
Next Next commit
feat: interrupted tool call
  • Loading branch information
meta-d committed Dec 11, 2024
commit 5860a5bd9b9e0a38fac6d497a2309767e8798805
3 changes: 2 additions & 1 deletion apps/cloud/src/app/@shared/xpert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export * from './tool-provider-card/provider.component'
export * from './tool-name-input/input.component'
export * from './xpert-card/xpert-card.component'
export * from './execution-status/execution.component'
export * from './execution-accordion/execution.component'
export * from './execution-accordion/execution.component'
export * from './tool-call-confirm/confirm.component'
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@for (toolCall of toolCalls(); track toolCall.id; let index = $index) {
<label class="font-semibold pb-2">{{toolCall.name}}</label>
<div class="flex flex-col gap-1">
@for (param of toolCall.args | keyvalue; track param.key) {
<div class="flex justify-start items-start gap-2">
<div class="w-24 shrink-0 whitespace-normal">{{param.key}}</div>
<textarea class="flex-1 p-1 rounded-md border border-gray-300 focus:ring-primary-500 focus:border-primary-500 text-sm text-text-secondary"
[ngModel]="param.value"
(ngModelChange)="updateParam(index, param.key, $event)"
>{{param.value}}</textarea>
</div>
}
</div>
}

<div class="mt-4 flex justify-between space-x-2">
<button type="button" class="btn disabled:btn-disabled btn-primary h-10 flex-1 space-x-2"
(click)="onConfirm()"
>
<div class="w-full flex justify-center">{{ 'PAC.Xpert.Confirm' | translate: {Default: 'Confirm'} }}</div>
</button>

<button type="button" class="btn disabled:btn-disabled h-10 btn-danger pressable flex-1"
(click)="onReject()"
>
<div class="w-full text-center">
<i class="ri-stop-line"></i>
{{ 'PAC.Xpert.Reject' | translate: { Default: 'Reject' } }}
</div>
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:host {
@apply p-2 border border-solid border-divider-regular rounded-xl shadow-sm flex flex-col transition-all duration-200 ease-in-out;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { CommonModule, KeyValuePipe } from '@angular/common'
import { Component, computed, model, output } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { AIMessage, isAIMessage } from '@langchain/core/messages'
import { TranslateModule } from '@ngx-translate/core'

@Component({
standalone: true,
imports: [CommonModule, FormsModule, TranslateModule, KeyValuePipe],
selector: 'xpert-tool-call-confirm',
templateUrl: 'confirm.component.html',
styleUrls: ['confirm.component.scss']
})
export class ToolCallConfirmComponent {
// Inputs
readonly message = model<AIMessage>()

readonly toolCalls = computed(() => {
if (isAIMessage(this.message())) {
return this.message().tool_calls
}
return null
})

readonly confirm = output()
readonly reject = output()

onConfirm() {
this.confirm.emit()
}
onReject() {
this.reject.emit()
}

updateParam(index: number, key: string, value: string) {
this.message.update((message) => {
const calls = [...message.tool_calls]
calls[index] = {
...calls[index],
args: {
...calls[index].args,
[key]: value
}
}
return new AIMessage({
...message,
tool_calls: calls
})
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

<div class="mt-4 flex justify-between space-x-2">
<button type="button" class="btn disabled:btn-disabled btn-primary w-0 h-10 grow space-x-2"
[disabled]="loading()"
[disabled]="loading() || status() === eExecutionStatusEnum.INTERRUPTED"
(click)="startRunAgent()"
>
<div class="w-full flex justify-center">{{ 'PAC.Xpert.StartRun' | translate: {Default: 'Start run'} }}</div>
Expand All @@ -48,22 +48,26 @@
}
</div>

@if (execution(); as execution) {
<xpert-agent-execution-status class="p-2 w-full mt-4" [execution]="execution" />
}
@if (execution(); as execution) {
<xpert-agent-execution-status class="p-2 w-full mt-4" [execution]="execution" />
}

<div class="flex items-center my-4 leading-[18px] text-base font-semibold text-gray-500 uppercase">
<div class="mr-3">{{ 'PAC.Xpert.Output' | translate: {Default: 'Output'} }}</div>
<div class="grow w-0 h-px bg-divider-regular"></div>
</div>
<div class="flex items-center my-4 leading-[18px] text-base font-semibold text-gray-500 uppercase">
<div class="mr-3">{{ 'PAC.Xpert.Output' | translate: {Default: 'Output'} }}</div>
<div class="grow w-0 h-px bg-divider-regular"></div>
</div>

<div class="mt-4 p-2 text-sm min-h-[60px] max-h-80 overflow-auto rounded-xl bg-neutral-100 dark:bg-neutral-700">
<markdown class="ngm-copilot-markdown"
lineNumbers
[start]="5"
[data]="output()"
/>
</div>
@if (status() === eExecutionStatusEnum.INTERRUPTED && lastAIMessage()) {
<xpert-tool-call-confirm class="w-full" [(message)]="lastAIMessage" (confirm)="onConfirm()" />
}

<div class="mt-4 p-2 text-sm min-h-[60px] max-h-80 overflow-auto rounded-xl bg-neutral-100 dark:bg-neutral-700">
<markdown class="ngm-copilot-markdown"
lineNumbers
[start]="5"
[data]="output()"
/>
</div>

<div class="w-full flex flex-col mt-4">
<div class="flex items-center mb-4 leading-[18px] text-base font-semibold text-gray-500 uppercase">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Component,
computed,
DestroyRef,
effect,
inject,
input,
model,
Expand Down Expand Up @@ -34,7 +35,8 @@ import { XpertExecutionService } from '../../services/execution.service'
import { XpertStudioComponent } from '../../studio.component'
import { CopilotStoredMessageComponent } from 'apps/cloud/src/app/@shared/copilot'
import { MaterialModule } from 'apps/cloud/src/app/@shared/material.module'
import { XpertAgentExecutionStatusComponent, XpertParametersCardComponent } from 'apps/cloud/src/app/@shared/xpert'
import { ToolCallConfirmComponent, XpertAgentExecutionStatusComponent, XpertParametersCardComponent } from 'apps/cloud/src/app/@shared/xpert'
import { AIMessage, mapStoredMessageToChatMessage } from '@langchain/core/messages'

@Component({
selector: 'xpert-studio-panel-agent-execution',
Expand All @@ -51,15 +53,16 @@ import { XpertAgentExecutionStatusComponent, XpertParametersCardComponent } from
MarkdownModule,
CopilotStoredMessageComponent,
XpertAgentExecutionStatusComponent,
XpertParametersCardComponent
XpertParametersCardComponent,
ToolCallConfirmComponent
],
host: {
tabindex: '-1',
'[class.selected]': 'isSelected'
}
})
export class XpertStudioPanelAgentExecutionComponent {
eXpertAgentExecutionEnum = XpertAgentExecutionStatusEnum
eExecutionStatusEnum = XpertAgentExecutionStatusEnum

readonly xpertAgentService = inject(XpertAgentService)
readonly agentExecutionService = inject(XpertAgentExecutionService)
Expand Down Expand Up @@ -99,6 +102,18 @@ export class XpertStudioPanelAgentExecutionComponent {
return executions
})

readonly status = computed(() => this.execution()?.status)

readonly lastMessage = computed(() => {
const messages = this.execution()?.messages
if (messages) {
return messages[messages.length - 1]
}
return null
})

readonly lastAIMessage = model<AIMessage>(null)

readonly loading = signal(false)
#agentSubscription: Subscription = null

Expand All @@ -121,6 +136,15 @@ export class XpertStudioPanelAgentExecutionComponent {
this.#destroyRef.onDestroy(() => {
this.clearStatus()
})

effect(() => {
const message = this.lastMessage()
if (message) {
this.lastAIMessage.set(mapStoredMessageToChatMessage(message))
} else {
this.lastAIMessage.set(null)
}
}, { allowSignalWrites: true })
}

clearStatus() {
Expand All @@ -130,6 +154,7 @@ export class XpertStudioPanelAgentExecutionComponent {
}

startRunAgent() {
const executionId = this.execution()?.id
this.loading.set(true)
// Clear
this.clearStatus()
Expand All @@ -143,7 +168,8 @@ export class XpertStudioPanelAgentExecutionComponent {
},
agent: this.xpertAgent(),
xpert: this.xpert(),
executionId: this.executionId()
executionId,
message: this.lastAIMessage()?.toDict()
})
.subscribe({
next: (msg) => {
Expand Down Expand Up @@ -183,6 +209,10 @@ export class XpertStudioPanelAgentExecutionComponent {
return this.apiService.getNode(key)?.entity as IXpertAgent
}

onConfirm() {
this.input.set(null)
this.startRunAgent()
}
}

export function processEvents(event, executionService: XpertExecutionService) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class XpertExecutionService {

// readonly execution = signal<IXpertAgentExecution>(null)
readonly #agentExecutions = signal<Record<string, IXpertAgentExecution>>({})
readonly agentExecutions = computed(() => {
readonly agentExecutions = computed<Record<string, IXpertAgentExecution>>(() => {
const agentExecutions = {}
Object.values(this.#agentExecutions() ?? {}).forEach((execution) => {
execution.subExecutions?.forEach((item) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ f-flow {
&.error {
@apply border-red-500 dark:border-red-800;
}
&.interrupted {
@apply border-neutral-700 dark:border-neutral-500;
}
}

.f-selection-area {
Expand Down
4 changes: 3 additions & 1 deletion apps/cloud/src/assets/i18n/zh-Hans.json
Original file line number Diff line number Diff line change
Expand Up @@ -2034,7 +2034,9 @@
"ProtectYourKey": "为了防止 API 滥用,请保护好您的 API 密钥。避免在前端代码中将其用作纯文本。:)",
"CreateNewSecretKey": "创建新的密钥",
"DeleteApiKey": "删除此 API 密钥?",
"ActionUndone": "此操作无法撤消。"
"ActionUndone": "此操作无法撤消。",
"Confirm": "确认",
"Reject": "拒绝"
},
"title": {
"short": "Xpert AI"
Expand Down
5 changes: 5 additions & 0 deletions packages/contracts/src/ai/xpert-agent.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IKnowledgebase } from './knowledgebase.model'
import { TAvatar } from '../types'
import { IXpertToolset } from './xpert-toolset.model'
import { IXpert, TXpertParameter } from './xpert.model'
import { StoredMessage } from '@langchain/core/messages'

export type TXpertAgent = {
key: string
Expand Down Expand Up @@ -91,4 +92,8 @@ export type TChatAgentParams = {
agent: IXpertAgent
xpert: Partial<IXpert>
executionId: string
/**
* Message to update parameters of last tool call message
*/
message?: StoredMessage
}
2 changes: 1 addition & 1 deletion packages/contracts/src/ai/xpert.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export enum ChatMessageEventTypeEnum {
ON_RETRIEVER_START = 'on_retriever_start',
ON_RETRIEVER_END = 'on_retriever_end',
ON_RETRIEVER_ERROR = 'on_retriever_error',
ON_ERROR = 'on_error'
ON_ERROR = 'on_error',
}

export type TChatRequest = {
Expand Down
4 changes: 2 additions & 2 deletions packages/server-ai/src/copilot/graph/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BaseMessage } from '@langchain/core/messages'
import { StateGraphArgs } from '@langchain/langgraph'
import { messagesStateReducer, StateGraphArgs } from '@langchain/langgraph'

export interface AgentState {
input: string
Expand Down Expand Up @@ -29,7 +29,7 @@ export function createCopilotAgentState(): StateGraphArgs<AgentState>['channels'
default: () => ''
},
messages: {
value: (x: BaseMessage[], y: BaseMessage[]) => x.concat(y),
value: messagesStateReducer,
default: () => []
},
language: {
Expand Down
3 changes: 3 additions & 0 deletions packages/server-ai/src/xpert-agent/commands/chat.command.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StoredMessage } from '@langchain/core/messages'
import { IXpert, IXpertAgentExecution, TChatOptions } from '@metad/contracts'
import { ICommand } from '@nestjs/cqrs'

Expand All @@ -21,6 +22,8 @@ export class XpertAgentChatCommand implements ICommand {
* Use this execution or create a new record
*/
execution?: IXpertAgentExecution

message?: StoredMessage
}
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { lastValueFrom, Observable, reduce, Subscriber, tap } from 'rxjs'
import { z } from 'zod'
import { XpertAgentExecutionUpsertCommand } from '../../xpert-agent-execution/commands'
import { XpertAgentExecutionOneQuery } from '../../xpert-agent-execution/queries'
import { StoredMessage } from '@langchain/core/messages'

export class XpertAgentExecuteCommand implements ICommand {
static readonly type = '[Xpert Agent] Execute'
Expand All @@ -29,6 +30,8 @@ export class XpertAgentExecuteCommand implements ICommand {
execution: IXpertAgentExecution
// The subscriber response to client
subscriber: Subscriber<MessageEvent>

message?: StoredMessage
}
) {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { XpertAgentChatCommand } from '../chat.command'
import { XpertAgentExecuteCommand } from '../execute.command'
import { XpertAgentExecutionOneQuery } from '../../../xpert-agent-execution/queries'
import { NodeInterrupt } from '@langchain/langgraph'

@CommandHandler(XpertAgentChatCommand)
export class XpertAgentChatHandler implements ICommandHandler<XpertAgentChatCommand> {
Expand Down Expand Up @@ -76,8 +77,13 @@ export class XpertAgentChatHandler implements ICommandHandler<XpertAgentChatComm
}),
tap({
error: (err) => {
status = XpertAgentExecutionStatusEnum.ERROR
error = getErrorMessage(err)
if (err instanceof NodeInterrupt) {
status = XpertAgentExecutionStatusEnum.INTERRUPTED
error = null
} else {
status = XpertAgentExecutionStatusEnum.ERROR
error = getErrorMessage(err)
}
},
finalize: async () => {
try {
Expand Down
Loading