Quellcode durchsuchen

add show debug (#7390)

### What problem does this PR solve?

add show debug
![Recording2025-04-28142829-ezgif
com-video-to-gif-converter](https://github.com/user-attachments/assets/0c67da34-c2b6-428f-ae9b-b5b21464885c)

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
tags/v0.19.0
so95 vor 5 Monaten
Ursprung
Commit
514c08a932
Es ist kein Account mit der E-Mail-Adresse des Committers verbunden

+ 2
- 0
.gitignore Datei anzeigen

@@ -42,3 +42,5 @@ nltk_data/
# Exclude hash-like temporary files like 9b5ad71b2ce5302211f9c61530b329a4922fc6a4
*[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]*
.lh/
.venv
docker/data

+ 4
- 2
agent/canvas.py Datei anzeigen

@@ -17,7 +17,6 @@ import logging
import json
from copy import deepcopy
from functools import partial

import pandas as pd

from agent.component import component_class
@@ -362,4 +361,7 @@ class Canvas:
return self.components["begin"]["obj"]._param.query

def get_component_input_elements(self, cpnnm):
return self.components[cpnnm]["obj"].get_input_elements()
return self.components[cpnnm]["obj"].get_input_elements()
def set_component_infor(self, cpn_id, infor):
self.components[cpn_id]["obj"].set_infor(infor)

+ 4
- 1
agent/component/base.py Datei anzeigen

@@ -34,6 +34,7 @@ _IS_RAW_CONF = "_is_raw_conf"
class ComponentParamBase(ABC):
def __init__(self):
self.output_var_name = "output"
self.infor_var_name = "infor"
self.message_history_window_size = 22
self.query = []
self.inputs = []
@@ -462,6 +463,9 @@ class ComponentBase(ABC):
def set_output(self, v):
setattr(self._param, self._param.output_var_name, v)

def set_infor(self, v):
setattr(self._param, self._param.infor_var_name, v)
def _fetch_outputs_from(self, sources: list[dict[str, Any]]) -> list[pd.DataFrame]:
outs = []
for q in sources:
@@ -488,7 +492,6 @@ class ComponentBase(ABC):
elif q.get("value"):
outs.append(pd.DataFrame([{"content": q["value"]}]))
return outs

def get_input(self):
if self._param.debug_inputs:
return pd.DataFrame([{"content": v["value"]} for v in self._param.debug_inputs if v.get("value")])

+ 2
- 0
agent/component/categorize.py Datei anzeigen

@@ -85,6 +85,8 @@ class Categorize(Generate, ABC):
input = self.get_input()
input = " - ".join(input["content"]) if "content" in input else ""
chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
self._canvas.set_component_infor(self._id, {"prompt":self._param.get_prompt(input),"messages": [{"role": "user", "content": "\nCategory: "}],"conf": self._param.gen_conf()})

ans = chat_mdl.chat(self._param.get_prompt(input), [{"role": "user", "content": "\nCategory: "}],
self._param.gen_conf())
logging.debug(f"input: {input}, answer: {str(ans)}")

+ 2
- 2
agent/component/generate.py Datei anzeigen

@@ -201,7 +201,7 @@ class Generate(ComponentBase):
msg.append({"role": "user", "content": "Output: "})
ans = chat_mdl.chat(msg[0]["content"], msg[1:], self._param.gen_conf())
ans = re.sub(r"^.*</think>", "", ans, flags=re.DOTALL)
self._canvas.set_component_infor(self._id, {"prompt":msg[0]["content"],"messages": msg[1:],"conf": self._param.gen_conf()})
if self._param.cite and "chunks" in retrieval_res.columns:
res = self.set_cite(retrieval_res, ans)
return pd.DataFrame([res])
@@ -234,7 +234,7 @@ class Generate(ComponentBase):
if self._param.cite and "chunks" in retrieval_res.columns:
res = self.set_cite(retrieval_res, answer)
yield res
self._canvas.set_component_infor(self._id, {"prompt":msg[0]["content"],"messages": msg[1:],"conf": self._param.gen_conf()})
self.set_output(Generate.be_output(res))

def debug(self, **kwargs):

+ 2
- 0
agent/component/keyword.py Datei anzeigen

@@ -58,6 +58,8 @@ class KeywordExtract(Generate, ABC):


chat_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.CHAT, self._param.llm_id)
self._canvas.set_component_infor(self._id, {"prompt":self._param.get_prompt(),"messages": [{"role": "user", "content": query}],"conf": self._param.gen_conf()})

ans = chat_mdl.chat(self._param.get_prompt(), [{"role": "user", "content": query}],
self._param.gen_conf())


+ 0
- 2
api/apps/canvas_app.py Datei anzeigen

@@ -26,7 +26,6 @@ from api.utils.api_utils import get_json_result, server_error_response, validate
from agent.canvas import Canvas
from peewee import MySQLDatabase, PostgresqlDatabase
from api.db.db_models import APIToken
import logging
import time

@manager.route('/templates', methods=['GET']) # noqa: F821
@@ -89,7 +88,6 @@ def save():
@login_required
def get(canvas_id):
e, c = UserCanvasService.get_by_tenant_id(canvas_id)
logging.info(f"get canvas_id: {canvas_id} c: {c}")
if not e:
return get_data_error_result(message="canvas not found.")
return get_json_result(data=c)

+ 1
- 0
web/src/locales/en.ts Datei anzeigen

@@ -1258,6 +1258,7 @@ This delimiter is used to split the input text into several text pieces echo of
promptTip:
'Use the system prompt to describe the task for the LLM, specify how it should respond, and outline other miscellaneous requirements. The system prompt is often used in conjunction with keys (variables), which serve as various data inputs for the LLM. Use a forward slash `/` or the (x) button to show the keys to use.',
promptMessage: 'Prompt is required',
infor: 'Information run',
knowledgeBasesTip:
'Select the knowledge bases to associate with this chat assistant, or choose variables containing knowledge base IDs below.',
knowledgeBaseVars: 'Knowledge base variables',

+ 250
- 58
web/src/pages/flow/canvas/node/popover.tsx Datei anzeigen

@@ -1,11 +1,15 @@
import { useFetchFlow } from '@/hooks/flow-hooks';
import get from 'lodash/get';
import React, { MouseEventHandler, useCallback, useMemo } from 'react';
import React, {
MouseEventHandler,
useCallback,
useMemo,
useState,
} from 'react';
import JsonView from 'react18-json-view';
import 'react18-json-view/src/style.css';
import { useReplaceIdWithText } from '../../hooks';

import { useTheme } from '@/components/theme-provider';
import {
Popover,
PopoverContent,
@@ -20,6 +24,17 @@ import {
TableRow,
} from '@/components/ui/table';
import { useTranslate } from '@/hooks/common-hooks';
import {
Button,
Card,
Col,
Input,
Row,
Space,
Tabs,
Typography,
message,
} from 'antd';
import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';

interface IProps extends React.PropsWithChildren {
@@ -31,7 +46,8 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
const { t } = useTranslate('flow');

const { data } = useFetchFlow();
const { theme } = useTheme();
console.log(data);

const component = useMemo(() => {
return get(data, ['dsl', 'components', nodeId], {});
}, [nodeId, data]);
@@ -42,6 +58,11 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
[],
);
const output = get(component, ['obj', 'output'], {});
const { conf, messages, prompt } = get(
component,
['obj', 'params', 'infor'],
{},
);
const { replacedOutput } = useReplaceIdWithText(output);
const stopPropagation: MouseEventHandler = useCallback((e) => {
e.stopPropagation();
@@ -49,6 +70,13 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {

const getLabel = useGetComponentLabelByValue(nodeId);

const [inputPage, setInputPage] = useState(1);
const pageSize = 3;
const pagedInputs = inputs.slice(
(inputPage - 1) * pageSize,
inputPage * pageSize,
);

return (
<Popover>
<PopoverTrigger onClick={stopPropagation} asChild>
@@ -59,62 +87,226 @@ export function NextNodePopover({ children, nodeId, name }: IProps) {
side={'right'}
sideOffset={20}
onClick={stopPropagation}
className="w-[400px]"
className="w-[800px] p-4"
style={{ maxHeight: 600, overflow: 'auto' }}
>
<div className="mb-3 font-semibold text-[16px]">
{name} {t('operationResults')}
</div>
<div className="flex w-full gap-4 flex-col">
<div className="flex flex-col space-y-1.5">
<span className="font-semibold text-[14px]">{t('input')}</span>
<div
style={
theme === 'dark'
? {
backgroundColor: 'rgba(150, 150, 150, 0.2)',
}
: {}
}
className={`bg-gray-100 p-1 rounded`}
>
<Table>
<TableHeader>
<TableRow>
<TableHead>{t('componentId')}</TableHead>
<TableHead className="w-[60px]">{t('content')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{inputs.map((x, idx) => (
<TableRow key={idx}>
<TableCell>{getLabel(x.component_id)}</TableCell>
<TableCell className="truncate">{x.content}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
<div className="flex flex-col space-y-1.5">
<span className="font-semibold text-[14px]">{t('output')}</span>
<div
style={
theme === 'dark'
? {
backgroundColor: 'rgba(150, 150, 150, 0.2)',
}
: {}
}
className="bg-gray-100 p-1 rounded"
>
<JsonView
src={replacedOutput}
displaySize={30}
className="w-full max-h-[300px] break-words overflow-auto"
/>
</div>
</div>
</div>
<Card
bordered={false}
style={{ marginBottom: 16, padding: 0 }}
bodyStyle={{ padding: 0 }}
>
<Typography.Title
level={5}
style={{
marginBottom: 16,
fontWeight: 600,
fontSize: 18,
borderBottom: '1px solid #f0f0f0',
paddingBottom: 8,
}}
>
{name} {t('operationResults')}
</Typography.Title>
</Card>
<Tabs
defaultActiveKey="input"
items={[
{
key: 'input',
label: t('input'),
children: (
<Card
size="small"
className="bg-gray-50 dark:bg-gray-800"
style={{ borderRadius: 8, border: '1px solid #e5e7eb' }}
bodyStyle={{ padding: 16 }}
>
<Table>
<TableHeader>
<TableRow>
<TableHead>{t('componentId')}</TableHead>
<TableHead className="w-[60px]">
{t('content')}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{pagedInputs.map((x, idx) => (
<TableRow key={idx + (inputPage - 1) * pageSize}>
<TableCell>{getLabel(x.component_id)}</TableCell>
<TableCell className="truncate">
{x.content}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{/* Pagination */}
{inputs.length > pageSize && (
<Row justify="end" style={{ marginTop: 8 }}>
<Space>
<Button
size="small"
disabled={inputPage === 1}
onClick={() => setInputPage(inputPage - 1)}
>
Prev
</Button>
<span className="mx-2 text-sm">
{inputPage} / {Math.ceil(inputs.length / pageSize)}
</span>
<Button
size="small"
disabled={
inputPage === Math.ceil(inputs.length / pageSize)
}
onClick={() => setInputPage(inputPage + 1)}
>
Next
</Button>
</Space>
</Row>
)}
</Card>
),
},
{
key: 'output',
label: t('output'),
children: (
<Card
size="small"
className="bg-gray-50 dark:bg-gray-800"
style={{ borderRadius: 8, border: '1px solid #e5e7eb' }}
bodyStyle={{ padding: 16 }}
>
<JsonView
src={replacedOutput}
displaySize={30}
className="w-full max-h-[300px] break-words overflow-auto"
/>
</Card>
),
},
{
key: 'infor',
label: t('infor'),
children: (
<Card
size="small"
className="bg-gray-50 dark:bg-gray-800"
style={{ borderRadius: 8, border: '1px solid #e5e7eb' }}
bodyStyle={{ padding: 16 }}
>
<Row gutter={16}>
<Col span={12}>
{conf && (
<Card
size="small"
bordered={false}
style={{
marginBottom: 16,
background: 'transparent',
}}
bodyStyle={{ padding: 0 }}
>
<Typography.Text
strong
style={{
color: '#888',
marginBottom: 8,
display: 'block',
}}
>
Configuration:
</Typography.Text>
<JsonView
src={conf}
displaySize={30}
className="w-full max-h-[120px] break-words overflow-auto"
/>
</Card>
)}
{prompt && (
<Card
size="small"
bordered={false}
style={{ background: 'transparent' }}
bodyStyle={{ padding: 0 }}
>
<Row
align="middle"
justify="space-between"
style={{ marginBottom: 8 }}
>
<Col>
<Typography.Text strong style={{ color: '#888' }}>
Prompt:
</Typography.Text>
</Col>
<Col>
<Button
size="small"
onClick={() => {
const inlineString = prompt
.replace(/\s+/g, ' ')
.trim();
navigator.clipboard.writeText(inlineString);
message.success(
'Prompt copied as single line!',
);
}}
>
Copy as single line
</Button>
</Col>
</Row>
<Input.TextArea
value={prompt}
readOnly
autoSize={{ minRows: 2, maxRows: 6 }}
className="bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700"
/>
</Card>
)}
</Col>
<Col span={12}>
{messages && (
<Card
size="small"
bordered={false}
style={{
marginBottom: 16,
background: 'transparent',
}}
bodyStyle={{ padding: 0 }}
>
<Typography.Text
strong
style={{
color: '#888',
marginBottom: 8,
display: 'block',
}}
>
Messages:
</Typography.Text>
<div className="max-h-[300px] overflow-auto">
<JsonView
src={messages}
displaySize={30}
className="w-full break-words"
/>
</div>
</Card>
)}
</Col>
</Row>
</Card>
),
},
]}
/>
</PopoverContent>
</Popover>
);

Laden…
Abbrechen
Speichern