mirror of https://github.com/msgbyte/tailchat
feat: add AI Assistant plugin for Tailchat with ChatGPT
parent
eb00379a75
commit
060d07ae8e
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"label": "AI Assistant",
|
||||||
|
"name": "com.msgbyte.ai-assistant",
|
||||||
|
"url": "/plugins/com.msgbyte.ai-assistant/index.js",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"author": "moonrailgun",
|
||||||
|
"description": "Add chatgpt into Tailchat",
|
||||||
|
"requireRestart": true
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@plugins/com.msgbyte.ai-assistant",
|
||||||
|
"main": "src/index.tsx",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Add chatgpt into Tailchat",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"sync:declaration": "tailchat declaration github"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/styled-components": "^5.1.26",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"styled-components": "^5.3.6"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { regChatInputButton } from '@capital/common';
|
||||||
|
import { BaseChatInputButton } from '@capital/component';
|
||||||
|
import React from 'react';
|
||||||
|
import { AssistantPopover } from './popover';
|
||||||
|
|
||||||
|
const PLUGIN_ID = 'com.msgbyte.ai-assistant';
|
||||||
|
const PLUGIN_NAME = 'AI Assistant';
|
||||||
|
|
||||||
|
console.log(`Plugin ${PLUGIN_NAME}(${PLUGIN_ID}) is loaded`);
|
||||||
|
|
||||||
|
regChatInputButton({
|
||||||
|
render: () => {
|
||||||
|
return (
|
||||||
|
<BaseChatInputButton
|
||||||
|
icon="eos-icons:ai"
|
||||||
|
popoverContent={({ hidePopover }) => (
|
||||||
|
<AssistantPopover onCompleted={hidePopover} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
@ -0,0 +1,116 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Translate } from './translate';
|
||||||
|
import { useAsyncRequest } from '@capital/common';
|
||||||
|
import {
|
||||||
|
LoadingSpinner,
|
||||||
|
useChatInputActionContext,
|
||||||
|
Tag,
|
||||||
|
} from '@capital/component';
|
||||||
|
import axios from 'axios';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import {
|
||||||
|
improveTextPrompt,
|
||||||
|
longerTextPrompt,
|
||||||
|
shorterTextPrompt,
|
||||||
|
translateTextPrompt,
|
||||||
|
} from './prompt';
|
||||||
|
|
||||||
|
const Root = styled.div`
|
||||||
|
padding: 0.5rem;
|
||||||
|
max-width: 300px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Tip = styled.div`
|
||||||
|
margin-bottom: 4px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ActionButton = styled.div`
|
||||||
|
min-width: 180px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const AssistantPopover: React.FC<{
|
||||||
|
onCompleted: () => void;
|
||||||
|
}> = React.memo((props) => {
|
||||||
|
const { message } = useChatInputActionContext();
|
||||||
|
const [{ loading, value }, handleCallAI] = useAsyncRequest(
|
||||||
|
async (question: string) => {
|
||||||
|
// TODO: wait for replace
|
||||||
|
const { data } = await axios.post('https://uui1ik.laf.dev/chatgpt', {
|
||||||
|
question,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Root>
|
||||||
|
<LoadingSpinner />
|
||||||
|
</Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Root>
|
||||||
|
<Tip>{Translate.helpMeTo}</Tip>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{typeof value === 'object' &&
|
||||||
|
(value.result ? (
|
||||||
|
<div>
|
||||||
|
<div>{value.answer}</div>
|
||||||
|
<Tag color="green">
|
||||||
|
{Translate.usage}: {value.usage}ms
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<div>{Translate.serviceBusy}</div>
|
||||||
|
<Tag color="red">{Translate.callError}</Tag>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{typeof message === 'string' && message.length > 0 && (
|
||||||
|
<>
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => handleCallAI(improveTextPrompt + message)}
|
||||||
|
>
|
||||||
|
{Translate.improveText}
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => handleCallAI(shorterTextPrompt + message)}
|
||||||
|
>
|
||||||
|
{Translate.makeShorter}
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => handleCallAI(longerTextPrompt + message)}
|
||||||
|
>
|
||||||
|
{Translate.makeLonger}
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton
|
||||||
|
onClick={() => handleCallAI(translateTextPrompt + message)}
|
||||||
|
>
|
||||||
|
{Translate.translateInputText}
|
||||||
|
</ActionButton>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* <ActionButton onClick={() => handleCallAI('')}>
|
||||||
|
{Translate.summaryMessages}
|
||||||
|
</ActionButton> */}
|
||||||
|
</Root>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
AssistantPopover.displayName = 'AssistantPopover';
|
@ -0,0 +1,8 @@
|
|||||||
|
export const improveTextPrompt =
|
||||||
|
"You are a text embellisher, you can only embellish the text, don't interpret it. Now i need you embellish it and keep my origin language:";
|
||||||
|
export const shorterTextPrompt =
|
||||||
|
"You are a text embellisher, you can only shorter the text, don't interpret it. Now i need you shorter it and keep my origin language:";
|
||||||
|
export const longerTextPrompt =
|
||||||
|
"You are a text embellisher, you can only longer the text, don't interpret it. Now i need you longer it and keep my origin language:";
|
||||||
|
export const translateTextPrompt =
|
||||||
|
'You are a program responsible for translating text. Your task is to output the specified target language based on the input text. Please do not output any text other than the translation. Target language is english, and if you receive text is english, please translate to chinese(no need pinyin), then its my text:';
|
@ -0,0 +1,44 @@
|
|||||||
|
import { localTrans } from '@capital/common';
|
||||||
|
|
||||||
|
export const Translate = {
|
||||||
|
name: localTrans({
|
||||||
|
'zh-CN': 'AI Assistant',
|
||||||
|
'en-US': 'AI Assistant',
|
||||||
|
}),
|
||||||
|
helpMeTo: localTrans({
|
||||||
|
'zh-CN': '帮我:',
|
||||||
|
'en-US': 'Help me to:',
|
||||||
|
}),
|
||||||
|
improveText: localTrans({
|
||||||
|
'zh-CN': '改进文本',
|
||||||
|
'en-US': 'Improve Text',
|
||||||
|
}),
|
||||||
|
makeShorter: localTrans({
|
||||||
|
'zh-CN': '精简内容',
|
||||||
|
'en-US': 'Make Shorter',
|
||||||
|
}),
|
||||||
|
makeLonger: localTrans({
|
||||||
|
'zh-CN': '扩写内容',
|
||||||
|
'en-US': 'Make Longer',
|
||||||
|
}),
|
||||||
|
summaryMessages: localTrans({
|
||||||
|
'zh-CN': '总结内容',
|
||||||
|
'en-US': 'Summary Messages',
|
||||||
|
}),
|
||||||
|
translateInputText: localTrans({
|
||||||
|
'zh-CN': '翻译输入内容',
|
||||||
|
'en-US': 'Translate Input',
|
||||||
|
}),
|
||||||
|
usage: localTrans({
|
||||||
|
'zh-CN': '用时',
|
||||||
|
'en-US': 'Usage',
|
||||||
|
}),
|
||||||
|
serviceBusy: localTrans({
|
||||||
|
'zh-CN': '服务器忙,请稍后再试',
|
||||||
|
'en-US': 'Server is busy, please try again later',
|
||||||
|
}),
|
||||||
|
callError: localTrans({
|
||||||
|
'zh-CN': '调用失败',
|
||||||
|
'en-US': 'Call Error',
|
||||||
|
}),
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "react",
|
||||||
|
"importsNotUsedAsValues": "error"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
declare module '@capital/common';
|
||||||
|
declare module '@capital/component';
|
Loading…
Reference in New Issue