feat: get the operator type from id #918 (#1323)

### What problem does this PR solve?

feat: get the operator type from id #918

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-07-01 10:27:32 +08:00 committed by GitHub
parent fc7cc1d36c
commit 4542346f18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 521 additions and 15 deletions

View File

@ -0,0 +1,483 @@
{
"edges": [
{
"id": "1a75afcd-05ea-4bf1-af5e-adff4f870100",
"label": "",
"source": "begin",
"target": "answer:0",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "a88e567e-1c23-457f-a0db-a90f1ea14d21",
"label": "",
"source": "message:reject",
"target": "answer:0",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "ad5fdb81-568e-46c5-82b1-1f50b625ae23",
"label": "",
"source": "answer:0",
"target": "categorize:0",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "fbb4c147-0d4c-4dd2-9264-3088e02c40d1",
"label": "",
"source": "categorize:0",
"target": "message:introduction",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "40da6d94-7d51-4d77-a5dd-1d583daba147",
"label": "",
"source": "categorize:0",
"target": "generate:casual",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "e4c7f5dc-5ccb-45da-af0d-4fbcb9dbb6f9",
"label": "",
"source": "categorize:0",
"target": "message:reject",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "a8c0b4e5-9597-4335-bded-5b1dd46f7507",
"label": "",
"source": "categorize:0",
"target": "retrieval:0",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "223972c4-08f7-47ae-b9ce-9a52dba044a7",
"label": "",
"source": "message:introduction",
"target": "answer:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "ece123ba-a84c-41d6-a843-63df331f945e",
"label": "",
"source": "generate:aboutJob",
"target": "answer:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "fa07af7d-29de-4066-89b7-f1bee184e260",
"label": "",
"source": "generate:casual",
"target": "answer:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "0d7565d8-673c-4a5e-90f4-f4296384eef0",
"label": "",
"source": "generate:get_wechat",
"target": "answer:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "e615e3d2-8ba5-448f-b001-ab926078cca6",
"label": "",
"source": "generate:nowechat",
"target": "answer:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "a9cf0f0a-77b5-42a2-a71f-e71086a525f0",
"label": "",
"source": "answer:1",
"target": "categorize:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "4e4c5461-cdc6-46c1-afb1-5892cb54104a",
"label": "",
"source": "answer:0",
"target": "categorize:1",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "44c8cb42-a2d9-43df-83bb-175d4870a027",
"label": "",
"source": "categorize:1",
"target": "retrieval:0",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "86d2308d-c91a-4a21-a6d6-081c92fc517c",
"label": "",
"source": "categorize:1",
"target": "generate:casual",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "e5de8fc4-b801-44cf-8240-02f2aac6ae23",
"label": "",
"source": "categorize:1",
"target": "generate:get_wechat",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "a9511914-d696-46b4-90c2-a3a940205b5c",
"label": "",
"source": "categorize:1",
"target": "generate:nowechat",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "07db19d7-e356-4c24-8e0d-353b1e6e6ee4",
"label": "",
"source": "retrieval:0",
"target": "generate:aboutJob",
"markerEnd": {
"type": "arrow"
}
},
{
"id": "e17eb04f-c773-44b2-8a7b-7f770185c4fa",
"label": "",
"source": "relevant:0",
"target": "generate:aboutJob",
"markerEnd": {
"type": "arrow"
}
}
],
"nodes": [
{
"id": "begin",
"type": "beginNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Begin",
"params": {
"prologue": "您好我是AGI方向的猎头了解到您是这方面的大佬然后冒昧的就联系到您。这边有个机会想和您分享RAGFlow正在招聘您这个岗位的资深的工程师不知道您那边是不是感兴趣"
},
"downstream": ["answer:0"],
"upstream": []
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "answer:0",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Answer",
"params": {},
"downstream": ["categorize:0"],
"upstream": ["begin", "message:reject"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "categorize:0",
"type": "categorizeNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Categorize",
"params": {
"llm_id": "deepseek-chat",
"category_description": {
"about_job": {
"description": "该问题关于职位本身或公司的信息。",
"examples": "什么岗位?\n汇报对象是谁?\n公司多少人\n公司有啥产品\n具体工作内容是啥\n地点哪里\n双休吗",
"to": "retrieval:0"
},
"casual": {
"description": "该问题不关于职位本身或公司的信息,属于闲聊。",
"examples": "你好\n好久不见\n你男的女的\n你是猴子派来的救兵吗\n上午开会了?\n你叫啥\n最近市场如何?生意好做吗?",
"to": "generate:casual"
},
"interested": {
"description": "该回答表示他对于该职位感兴趣。",
"examples": "嗯\n说吧\n说说看\n还好吧\n是的\n哦\nyes\n具体说说",
"to": "message:introduction"
},
"answer": {
"description": "该回答表示他对于该职位不感兴趣,或感觉受到骚扰。",
"examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n我已经不干这个了\n我不是这个方向的",
"to": "message:reject"
}
}
},
"downstream": [
"message:introduction",
"generate:casual",
"message:reject",
"retrieval:0"
],
"upstream": ["answer:0"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "message:introduction",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Message",
"params": {
"messages": [
"我简单介绍以下:\nRAGFlow 是一款基于深度文档理解构建的开源 RAGRetrieval-Augmented Generation引擎。RAGFlow 可以为各种规模的企业及个人提供一套精简的 RAG 工作流程结合大语言模型LLM针对用户各类不同的复杂格式数据提供可靠的问答以及有理有据的引用。https://github.com/infiniflow/ragflow\n您那边还有什么要了解的"
]
},
"downstream": ["answer:1"],
"upstream": ["categorize:0"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "answer:1",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Answer",
"params": {},
"downstream": ["categorize:1"],
"upstream": [
"message:introduction",
"generate:aboutJob",
"generate:casual",
"generate:get_wechat",
"generate:nowechat"
]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "categorize:1",
"type": "categorizeNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Categorize",
"params": {
"llm_id": "deepseek-chat",
"category_description": {
"about_job": {
"description": "该问题关于职位本身或公司的信息。",
"examples": "什么岗位?\n汇报对象是谁?\n公司多少人\n公司有啥产品\n具体工作内容是啥\n地点哪里\n双休吗",
"to": "retrieval:0"
},
"casual": {
"description": "该问题不关于职位本身或公司的信息,属于闲聊。",
"examples": "你好\n好久不见\n你男的女的\n你是猴子派来的救兵吗\n上午开会了?\n你叫啥\n最近市场如何?生意好做吗?",
"to": "generate:casual"
},
"wechat": {
"description": "该回答表示他愿意加微信,或者已经报了微信号。",
"examples": "嗯\n可以\n是的\n哦\nyes\n15002333453\nwindblow_2231",
"to": "generate:get_wechat"
},
"giveup": {
"description": "该回答表示他不愿意加微信。",
"examples": "不需要\n不感兴趣\n暂时不看\n不要\nno\n不方便\n不知道还要加我微信",
"to": "generate:nowechat"
}
},
"message_history_window_size": 8
},
"downstream": [
"retrieval:0",
"generate:casual",
"generate:get_wechat",
"generate:nowechat"
],
"upstream": ["answer:0"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "generate:casual",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Generate",
"params": {
"llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头现在候选人的聊了和职位无关的话题请耐心的回应候选人并将话题往该AGI的职位上带最好能要到候选人微信号以便后面保持联系。",
"temperature": 0.9,
"message_history_window_size": 12,
"cite": false
},
"downstream": ["answer:1"],
"upstream": ["categorize:0", "categorize:1"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "retrieval:0",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Retrieval",
"params": {
"similarity_threshold": 0.2,
"keywords_similarity_weight": 0.3,
"top_n": 6,
"top_k": 1024,
"rerank_id": "BAAI/bge-reranker-v2-m3",
"kb_ids": ["869a236818b811ef91dffa163e197198"]
},
"downstream": ["generate:aboutJob"],
"upstream": ["categorize:0", "categorize:1"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "generate:aboutJob",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Generate",
"params": {
"llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头候选人问了有关职位或公司的问题你根据以下职位信息回答。如果职位信息中不包含候选人的问题就回答不清楚、不知道、有待确认等。回答完后引导候选人加微信号\n - 方便加一下微信吗我把JD发您看看\n - 微信号多少我把详细职位JD发您\n 职位信息如下:\n {input}\n 职位信息如上。",
"temperature": 0.02
},
"downstream": ["answer:1"],
"upstream": ["relevant:0"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "generate:get_wechat",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Generate",
"params": {
"llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头候选人表示不反感加微信如果对方已经报了微信号表示感谢和信任并表示马上会加上如果没有则问对方微信号多少。你的微信号是weixin_kevinE-mail是kkk@ragflow.com。说话不要重复。不要总是您好。",
"temperature": 0.1,
"message_history_window_size": 12,
"cite": false
},
"downstream": ["answer:1"],
"upstream": ["categorize:1"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "generate:nowechat",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Generate",
"params": {
"llm_id": "deepseek-chat",
"prompt": "你是AGI方向的猎头当你提出加微信时对方表示拒绝。你需要耐心礼貌的回应候选人表示对于保护隐私信息给予理解也可以询问他对该职位的看法和顾虑。并在恰当的时机再次询问微信联系方式。也可以鼓励候选人主动与你取得联系。你的微信号是weixin_kevinE-mail是kkk@ragflow.com。说话不要重复。不要总是您好。",
"temperature": 0.1,
"message_history_window_size": 12,
"cite": false
},
"downstream": ["answer:1"],
"upstream": ["categorize:1"]
},
"sourcePosition": "left",
"targetPosition": "right"
},
{
"id": "message:reject",
"type": "ragNode",
"position": {
"x": 0,
"y": 0
},
"data": {
"label": "Message",
"params": {
"messages": [
"好的,祝您生活愉快,工作顺利。",
"哦,好的,感谢您宝贵的时间!"
]
},
"downstream": ["answer:0"],
"upstream": ["categorize:0"]
},
"sourcePosition": "left",
"targetPosition": "right"
}
]
}

View File

@ -27,7 +27,7 @@ import { humanId } from 'human-id';
import { useParams } from 'umi';
import { NodeMap, Operator, RestrictedUpstreamMap } from './constant';
import useGraphStore, { RFState } from './store';
import { buildDslComponentsByGraph, getOperatorTypeFromId } from './utils';
import { buildDslComponentsByGraph } from './utils';
const selector = (state: RFState) => ({
nodes: state.nodes,
@ -249,7 +249,7 @@ export const useSetLlmSetting = (form?: FormInstance) => {
};
export const useValidateConnection = () => {
const edges = useGraphStore((state) => state.edges);
const { edges, getOperatorTypeFromId } = useGraphStore((state) => state);
// restricted lines cannot be connected successfully.
const isValidConnection = useCallback(
(connection: Connection) => {
@ -265,7 +265,7 @@ export const useValidateConnection = () => {
]?.every((x) => x !== getOperatorTypeFromId(connection.target));
return ret;
},
[edges],
[edges, getOperatorTypeFromId],
);
return isValidConnection;

View File

@ -2,7 +2,9 @@ import { useSetModalState } from '@/hooks/commonHooks';
import { useFetchFlowList, useSetFlow } from '@/hooks/flow-hooks';
import { useCallback, useState } from 'react';
import { useNavigate } from 'umi';
import { dsl } from '../mock';
// import { dsl } from '../mock';
import headhunterZhComponents from '../../../../../graph/test/dsl_examples/headhunter_zh.json';
import headhunter_zh from '../headhunter_zh.json';
export const useFetchDataOnMount = () => {
const { data, loading } = useFetchFlowList();
@ -22,7 +24,10 @@ export const useSaveFlow = () => {
const onFlowOk = useCallback(
async (title: string) => {
const ret = await setFlow({ title, dsl });
const ret = await setFlow({
title,
dsl: { ...headhunterZhComponents, graph: headhunter_zh },
});
if (ret?.retcode === 0) {
hideFlowSettingModal();

View File

@ -20,7 +20,6 @@ import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { Operator } from './constant';
import { NodeData } from './interface';
import { getOperatorTypeFromId } from './utils';
export type RFState = {
nodes: Node<NodeData>[];
@ -35,7 +34,7 @@ export type RFState = {
updateNodeForm: (nodeId: string, values: any) => void;
onSelectionChange: OnSelectionChangeFunc;
addNode: (nodes: Node) => void;
getNode: (id?: string) => Node | undefined;
getNode: (id?: string | null) => Node<NodeData> | undefined;
addEdge: (connection: Connection) => void;
getEdge: (id: string) => Edge | undefined;
deletePreviousEdgeOfClassificationNode: (connection: Connection) => void;
@ -46,6 +45,7 @@ export type RFState = {
deleteEdgeBySourceAndSourceHandle: (connection: Partial<Connection>) => void;
findNodeByName: (operatorName: Operator) => Node | undefined;
updateMutableNodeFormItem: (id: string, field: string, value: any) => void;
getOperatorTypeFromId: (id?: string | null) => string | undefined;
};
// this is our useStore hook that we can use in our components to get parts of the store and call actions
@ -87,9 +87,12 @@ const useGraphStore = create<RFState>()(
addNode: (node: Node) => {
set({ nodes: get().nodes.concat(node) });
},
getNode: (id?: string) => {
getNode: (id?: string | null) => {
return get().nodes.find((x) => x.id === id);
},
getOperatorTypeFromId: (id?: string | null) => {
return get().getNode(id)?.data?.label;
},
addEdge: (connection: Connection) => {
set({
edges: addEdge(connection, get().edges),
@ -101,7 +104,7 @@ const useGraphStore = create<RFState>()(
},
deletePreviousEdgeOfClassificationNode: (connection: Connection) => {
// Delete the edge on the classification node anchor when the anchor is connected to other nodes
const { edges } = get();
const { edges, getOperatorTypeFromId } = get();
if (getOperatorTypeFromId(connection.source) === Operator.Categorize) {
const previousEdge = edges.find(
(x) =>

View File

@ -1,3 +1,6 @@
import fs from 'fs';
import path from 'path';
import headhunter_zh from '../../../../graph/test/dsl_examples/headhunter_zh.json';
import { dsl } from './mock';
import { buildNodesAndEdgesFromDSLComponents } from './utils';
@ -28,3 +31,19 @@ test('buildNodesAndEdgesFromDSLComponents', () => {
]),
);
});
test('build nodes and edges from dsl', () => {
const { edges, nodes } = buildNodesAndEdgesFromDSLComponents(
headhunter_zh.components,
);
try {
fs.writeFileSync(
path.join(__dirname, 'headhunter_zh.json'),
JSON.stringify({ edges, nodes }, null, 4),
);
console.log('JSON data is saved.');
} catch (error) {
console.warn(error);
}
expect(nodes.length).toEqual(12);
});

View File

@ -5,7 +5,7 @@ import { curry, isEmpty } from 'lodash';
import pipe from 'lodash/fp/pipe';
import { Edge, MarkerType, Node, Position } from 'reactflow';
import { v4 as uuidv4 } from 'uuid';
import { Operator, initialFormValuesMap } from './constant';
import { NodeMap, Operator, initialFormValuesMap } from './constant';
import { NodeData } from './interface';
const buildEdges = (
@ -41,7 +41,7 @@ export const buildNodesAndEdgesFromDSLComponents = (data: DSLComponents) => {
const upstream = [...value.upstream];
nodes.push({
id: key,
type: 'ragNode',
type: NodeMap[value.obj.component_name as Operator] || 'ragNode',
position: { x: 0, y: 0 },
data: {
label: value.obj.component_name,
@ -162,7 +162,3 @@ export const buildDslComponentsByGraph = (
return components;
};
export const getOperatorTypeFromId = (id: string | null) => {
return id?.split(':')[0] as Operator | undefined;
};