Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
88 changes: 88 additions & 0 deletions modules/tool/packages/chatBI/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { defineTool } from '@tool/type';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
WorkflowIOValueTypeEnum
} from '@tool/type/fastgpt';
import { ToolTypeEnum } from '@tool/type/tool';

export default defineTool({
name: {
'zh-CN': 'chatBI',
en: 'Template tool'
},
type: ToolTypeEnum.tools,
description: {
'zh-CN': 'chatBI 工具',
en: 'chatBI Tool'
},
toolDescription:
'send user natural language instructions to ChatBI application for execution, sse stream interface returns results',
secretInputConfig: [
{
key: 'chatBIUrl',
label: 'chatBI 服务根地址',
description: '根地址,例如:http://example.com',
required: true,
inputType: 'secret'
},
{
key: 'sysAccessKey',
label: 'chatBI 系统AccessKey',
description: '系统AccessKey',
required: true,
inputType: 'secret'
},
{
key: 'corpId',
label: 'chatBI 企业ID',
description: '企业ID',
required: true,
inputType: 'secret'
}
],
versionList: [
{
value: '0.1.0',
description: 'Default version',
inputs: [
{
key: 'query',
label: '用户提问内容',
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true
},
{
key: 'appId',
label: 'chatBI 应用ID',
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true
},
{
key: 'appAccessKey',
label: 'chatBI 应用AccessKey',
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true
},
{
key: 'sessionId',
label: '会话id',
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: false
}
],
outputs: [
{
valueType: WorkflowIOValueTypeEnum.arrayAny,
key: 'displayContentList',
label: '展示内容列表',
description: 'ChatBI返回的核心展示内容,包含文本、图表、表格等多种类型'
}
]
}
]
});
10 changes: 10 additions & 0 deletions modules/tool/packages/chatBI/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import config from './config';
import { InputType, OutputType, tool as toolCb } from './src';
import { exportTool } from '@tool/utils/tool';

export default exportTool({
toolCb,
InputType,
OutputType,
config
});
1 change: 1 addition & 0 deletions modules/tool/packages/chatBI/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions modules/tool/packages/chatBI/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@fastgpt-plugins/tool-chat-bi",
"module": "index.ts",
"type": "module",
"scripts": {
"build": "bun ../../../../scripts/build.ts"
},
"devDependencies": {
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"dependencies": {
"zod": "^3.24.2"
}
}
163 changes: 163 additions & 0 deletions modules/tool/packages/chatBI/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { z } from 'zod';
import type { RunToolSecondParamsType } from '@tool/type/tool';
import { StreamDataAnswerTypeEnum } from '@tool/type/tool';
import { getErrText } from '@tool/utils/err';

type DataType = {
isFinished: boolean;
isSuccess: boolean;
errorMessage?: string;
processList: unknown[];
displayContentList: ContentType[];
streamData: string;
};

enum ContentTypeEnum {
text = 'TEXT'
}
type ContentType = {
content: string;
type: ContentTypeEnum;
};

export const InputType = z.object({
query: z.string(),
appId: z.string(),
appAccessKey: z.string(),
sessionId: z.string().optional(),
chatBIUrl: z.string(),
sysAccessKey: z.string(),
corpId: z.string()
});

export const OutputType = z.object({
// displayContentList: z.array(z.any())
});

export async function tool(
{
query,
appId,
appAccessKey,
sessionId,
chatBIUrl,
sysAccessKey,
corpId
}: z.infer<typeof InputType>,
{ systemVar, streamResponse }: RunToolSecondParamsType
): Promise<z.infer<typeof OutputType>> {
try {
const url = new URL('/v2/open/api/common/stream/chatbi/chatNew', chatBIUrl);

// userID get from systemVar
const userId = systemVar.user.username.split('-')[1];

if (!sessionId) {
sessionId = userId;
}

const response = await fetch(url.toString(), {
method: 'POST',
headers: {
Accept: 'text/event-stream',
'Content-Type': 'application/json'
},
body: JSON.stringify({
sysAccessKey,
corpId,
appId,
appAccessKey,
userId,
query,
sessionId
})
});

if (!response.ok) {
return Promise.reject(`HTTP ${response.status}: ${response.statusText}`);
}

const reader = response.body?.getReader();

if (!reader) {
return Promise.reject('无法获取响应流');
}

const decoder = new TextDecoder();
let sentListLen = 0;
let buffer = '';
let isFinished = false;

while (!isFinished) {
const { done, value } = await reader.read();
if (done) break;

buffer += decoder.decode(value, { stream: true });

const events = buffer.split('\n\n');

buffer = events.pop() || '';

for (const event of events) {
if (!event.trim()) continue;

const lines = event.split('\n');
let eventData = '';

for (const line of lines) {
if (line.startsWith('data:')) {
// remove "data:" and trim
eventData = line.slice(5).trim();
break;
}
}

if (eventData && eventData !== '') {
try {
const data: DataType = JSON.parse(eventData);

if (data.displayContentList && data.displayContentList.length > sentListLen) {
// only send the last items
const sendList = data.displayContentList.slice(sentListLen);
const texts = sendList
.filter((item) => item.type === 'TEXT')
.map((item) => item.content)
.join('\n');
const unTexts = sendList.filter((item) => item.type !== 'TEXT');
const content =
texts +
'\n' +
(unTexts.length > 0 ? `\`\`\`RENDER\n${JSON.stringify(unTexts)}\n\`\`\`\n` : '');

streamResponse({
content,
type: StreamDataAnswerTypeEnum.answer
});
sentListLen = data.displayContentList.length;
}

if (data.streamData) {
streamResponse({
content: data.streamData,
type: StreamDataAnswerTypeEnum.answer
});
}

if (data.isFinished) {
isFinished = true;
break;
}
} catch (e) {
continue;
}
}
}
}

return {
// displayContentList
};
} catch (error) {
return Promise.reject(getErrText(error));
}
}
8 changes: 8 additions & 0 deletions modules/tool/packages/chatBI/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { expect, test } from 'vitest';
import tool from '..';

test(async () => {
expect(tool.name).toBeDefined();
expect(tool.description).toBeDefined();
expect(tool.cb).toBeDefined();
});