Files
LangBot/web/src/app/home/pipelines/page.tsx
T
devin-ai-integration[bot] 4f2ec195fc feat: add WebChat adapter for pipeline debugging (#1510)
* feat: add WebChat adapter for pipeline debugging

- Create WebChatAdapter for handling debug messages in pipeline testing
- Add HTTP API endpoints for debug message sending and retrieval
- Implement frontend debug dialog with session switching (private/group chat)
- Add Chinese i18n translations for debug interface
- Auto-create default WebChat bot during database initialization
- Support fixed session IDs: webchatperson and webchatgroup for testing

Co-Authored-By: Junyan Qin <Chin>, 秦骏言 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>

* perf: ui for webchat

* feat: complete webchat backend

* feat: core chat apis

* perf: button style in pipeline card

* perf: log btn in bot card

* perf: webchat entities definition

* fix: bugs

* perf: web chat

* perf: dialog styles

* perf: styles

* perf: styles

* fix: group invalid in webchat

* perf: simulate real im message

* perf: group timeout toast

* feat(webchat): add supports for mentioning bot in group

* perf(webchat): at component styles

* perf: at badge display in message

* fix: linter errors

* fix: webchat was listed on adapter list

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Junyan Qin <Chin>, 秦骏言 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>
2025-06-08 15:34:26 +08:00

175 lines
5.6 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import CreateCardComponent from '@/app/infra/basic-component/create-card-component/CreateCardComponent';
import PipelineFormComponent from './components/pipeline-form/PipelineFormComponent';
import { httpClient } from '@/app/infra/http/HttpClient';
import { PipelineCardVO } from '@/app/home/pipelines/components/pipeline-card/PipelineCardVO';
import PipelineCard from '@/app/home/pipelines/components/pipeline-card/PipelineCard';
import { PipelineFormEntity } from '@/app/infra/entities/pipeline';
import styles from './pipelineConfig.module.css';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { toast } from 'sonner';
import { useTranslation } from 'react-i18next';
import DebugDialog from './debug-dialog/DebugDialog';
export default function PluginConfigPage() {
const { t } = useTranslation();
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [isEditForm, setIsEditForm] = useState(false);
const [pipelineList, setPipelineList] = useState<PipelineCardVO[]>([]);
const [selectedPipelineId, setSelectedPipelineId] = useState('');
const [selectedPipelineFormValue, setSelectedPipelineFormValue] =
useState<PipelineFormEntity>({
basic: {},
ai: {},
trigger: {},
safety: {},
output: {},
});
const [disableForm, setDisableForm] = useState(false);
const [selectedPipelineIsDefault, setSelectedPipelineIsDefault] =
useState(false);
const [debugDialogOpen, setDebugDialogOpen] = useState(false);
const [debugPipelineId, setDebugPipelineId] = useState('');
useEffect(() => {
getPipelines();
}, []);
function getPipelines() {
httpClient
.getPipelines()
.then((value) => {
const currentTime = new Date();
const pipelineList = value.pipelines.map((pipeline) => {
const lastUpdatedTimeAgo = Math.floor(
(currentTime.getTime() -
new Date(
pipeline.updated_at ?? currentTime.getTime(),
).getTime()) /
1000 /
60 /
60 /
24,
);
const lastUpdatedTimeAgoText =
lastUpdatedTimeAgo > 0
? ` ${lastUpdatedTimeAgo} ${t('pipelines.daysAgo')}`
: t('pipelines.today');
return new PipelineCardVO({
lastUpdatedTimeAgo: lastUpdatedTimeAgoText,
description: pipeline.description,
id: pipeline.uuid ?? '',
name: pipeline.name,
isDefault: pipeline.is_default ?? false,
});
});
setPipelineList(pipelineList);
})
.catch((error) => {
console.log(error);
toast.error(t('pipelines.getPipelineListError') + error.message);
});
}
function getSelectedPipelineForm(id?: string) {
httpClient.getPipeline(id ?? selectedPipelineId).then((value) => {
setSelectedPipelineFormValue({
ai: value.pipeline.config.ai,
basic: {
description: value.pipeline.description,
name: value.pipeline.name,
},
output: value.pipeline.config.output,
safety: value.pipeline.config.safety,
trigger: value.pipeline.config.trigger,
});
setSelectedPipelineIsDefault(value.pipeline.is_default ?? false);
setDisableForm(false);
});
}
const handleDebug = (pipelineId: string) => {
setDebugPipelineId(pipelineId);
setDebugDialogOpen(true);
};
return (
<div className={styles.configPageContainer}>
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
<DialogContent className="w-[700px] max-h-[80vh] p-0 flex flex-col">
<DialogHeader className="px-6 pt-6 pb-4">
<DialogTitle>
{isEditForm
? t('pipelines.editPipeline')
: t('pipelines.createPipeline')}
</DialogTitle>
</DialogHeader>
<div className="flex-1 overflow-y-auto px-6">
<PipelineFormComponent
onNewPipelineCreated={(pipelineId) => {
setDisableForm(true);
setIsEditForm(true);
setModalOpen(true);
setSelectedPipelineId(pipelineId);
getSelectedPipelineForm(pipelineId);
}}
onFinish={() => {
getPipelines();
setModalOpen(false);
}}
isEditMode={isEditForm}
pipelineId={selectedPipelineId}
disableForm={disableForm}
initValues={selectedPipelineFormValue}
isDefaultPipeline={selectedPipelineIsDefault}
/>
</div>
</DialogContent>
</Dialog>
<div className={styles.pipelineListContainer}>
<CreateCardComponent
width={'100%'}
height={'10rem'}
plusSize={'90px'}
onClick={() => {
setIsEditForm(false);
setModalOpen(true);
}}
/>
{pipelineList.map((pipeline) => {
return (
<div
key={pipeline.id}
onClick={() => {
setDisableForm(true);
setIsEditForm(true);
setModalOpen(true);
setSelectedPipelineId(pipeline.id);
getSelectedPipelineForm(pipeline.id);
}}
>
<PipelineCard cardVO={pipeline} onDebug={handleDebug} />
</div>
);
})}
</div>
<DebugDialog
open={debugDialogOpen}
onOpenChange={setDebugDialogOpen}
pipelineId={debugPipelineId}
/>
</div>
);
}