diff --git a/src/pages/playground/components/ground-images.tsx b/src/pages/playground/components/ground-images.tsx index 0bc5648d..2c7f534b 100644 --- a/src/pages/playground/components/ground-images.tsx +++ b/src/pages/playground/components/ground-images.tsx @@ -279,6 +279,10 @@ const GroundImages: React.FC = forwardRef((props, ref) => { setTokenResult(null); }; + const handleInputChange = (e: any) => { + setCurrentPrompt(e.target.value); + }; + const handleSendMessage = (message: Omit) => { const currentMessage = message.content ? message : undefined; submitMessage(currentMessage); @@ -527,6 +531,7 @@ const GroundImages: React.FC = forwardRef((props, ref) => { isEmpty={!imageList.length} handleSubmit={handleSendMessage} handleAbortFetch={handleStopConversation} + onInputChange={handleInputChange} shouldResetMessage={false} clearAll={handleClear} tools={ diff --git a/src/pages/playground/components/ground-reranker.tsx b/src/pages/playground/components/ground-reranker.tsx index 70c94b22..13405248 100644 --- a/src/pages/playground/components/ground-reranker.tsx +++ b/src/pages/playground/components/ground-reranker.tsx @@ -79,7 +79,6 @@ const GroundReranker: React.FC = forwardRef((props, ref) => { const [loading, setLoading] = useState(false); const [tokenResult, setTokenResult] = useState(null); const [collapse, setCollapse] = useState(false); - const contentRef = useRef(''); const scroller = useRef(null); const inputListRef = useRef(null); const paramsRef = useRef(null); @@ -198,7 +197,7 @@ const GroundReranker: React.FC = forwardRef((props, ref) => { setLoading(false); }; - const submitMessage = async (current?: { content: string }) => { + const submitMessage = async () => { await formRef.current?.form.validateFields(); if (!parameters.model) return; try { @@ -219,13 +218,11 @@ const GroundReranker: React.FC = forwardRef((props, ref) => { requestToken.current?.cancel?.(); requestToken.current = requestSource(); - contentRef.current = current?.content || ''; - const result: any = await rerankerQuery( { model: parameters.model, top_n: parameters.top_n, - query: contentRef.current, + query: queryValueRef.current, documents: [ ...textList.map((item) => item.text), ...fileList.map((item) => item.text) @@ -309,24 +306,9 @@ const GroundReranker: React.FC = forwardRef((props, ref) => { setLoading(false); } }; - const handleClear = () => { - if (!messageList.length) { - return; - } - setMessageId(); - setMessageList([]); - setTokenResult(null); - }; - - const handleSendMessage = (message: Omit) => { - submitMessage(message); - }; const handleSearch = (val: string) => { - if (!val) { - return; - } - submitMessage({ content: val }); + submitMessage(); }; const handleQueryChange = (e: any) => { @@ -630,7 +612,7 @@ const GroundReranker: React.FC = forwardRef((props, ref) => { }} parameters={{ ...parameters, - query: contentRef.current + query: queryValueRef.current }} onCancel={handleCloseViewCode} title={intl.formatMessage({ id: 'playground.viewcode' })} diff --git a/src/pages/playground/components/ground-stt.tsx b/src/pages/playground/components/ground-stt.tsx index c3789960..a5ed09fb 100644 --- a/src/pages/playground/components/ground-stt.tsx +++ b/src/pages/playground/components/ground-stt.tsx @@ -28,7 +28,7 @@ import '../style/speech-to-text.less'; import '../style/system-message-wrap.less'; import AudioInput from './audio-input'; import DynamicParams from './dynamic-params'; -import ViewCodeModal from './view-code-modal'; +import ViewSTTCode from './view-stt-code'; interface MessageProps { modelList: Global.BaseOption[]; @@ -450,15 +450,15 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { - + > ); }); diff --git a/src/pages/playground/components/ground-tts.tsx b/src/pages/playground/components/ground-tts.tsx index 6c4442e9..67f0ef63 100644 --- a/src/pages/playground/components/ground-tts.tsx +++ b/src/pages/playground/components/ground-tts.tsx @@ -26,7 +26,7 @@ import '../style/ground-left.less'; import '../style/system-message-wrap.less'; import DynamicParams from './dynamic-params'; import MessageInput from './message-input'; -import ViewCodeModal from './view-code-modal'; +import ViewTTSCode from './view-tts-code'; interface MessageProps { modelList: Global.BaseOption[]; @@ -132,6 +132,10 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { setLoading(false); }; + const handleInputChange = (e: any) => { + setCurrentPrompt(e.target.value); + }; + const submitMessage = async (current?: { role: string; content: string }) => { await formRef.current?.form.validateFields(); if (!parameters.model) return; @@ -420,6 +424,7 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { isEmpty={true} handleSubmit={handleSendMessage} handleAbortFetch={handleStopConversation} + onInputChange={handleInputChange} clearAll={handleClear} shouldResetMessage={false} submitIcon={} @@ -445,17 +450,17 @@ const GroundLeft: React.FC = forwardRef((props, ref) => { - + > ); }); diff --git a/src/pages/playground/components/message-input.tsx b/src/pages/playground/components/message-input.tsx index 09afe96c..9fecb3f7 100644 --- a/src/pages/playground/components/message-input.tsx +++ b/src/pages/playground/components/message-input.tsx @@ -85,6 +85,7 @@ interface MessageInputProps { submitIcon?: React.ReactNode; presetPrompt?: (list: CurrentMessage[]) => void; addMessage?: (message: CurrentMessage) => void; + onInputChange?: (e: any) => void; title?: React.ReactNode; tools?: React.ReactNode; loading: boolean; @@ -108,6 +109,7 @@ const MessageInput: React.FC = forwardRef( updateLayout, addMessage, onCheck, + onInputChange, title, loading, disabled, @@ -150,7 +152,7 @@ const MessageInput: React.FC = forwardRef( }; const handleInputChange = (e: any) => { - console.log('input change:', e.target?.value); + onInputChange?.(e); setMessage({ ...message, content: e.target?.value @@ -179,6 +181,7 @@ const MessageInput: React.FC = forwardRef( const handleClearAll = (e: any) => { e.stopPropagation(); clearAll(); + handleInputChange({ target: { value: '' } }); setMessage({ role: Roles.User, content: '', diff --git a/src/pages/playground/components/view-stt-code.tsx b/src/pages/playground/components/view-stt-code.tsx new file mode 100644 index 00000000..aaebe63a --- /dev/null +++ b/src/pages/playground/components/view-stt-code.tsx @@ -0,0 +1,201 @@ +import EditorWrap from '@/components/editor-wrap'; +import HighlightCode from '@/components/highlight-code'; +import { BulbOutlined } from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; +import { Button, Modal } from 'antd'; +import _ from 'lodash'; +import React, { useMemo, useState } from 'react'; + +type ViewModalProps = { + systemMessage?: string; + messageList?: any[]; + payload: Record; + parameters: any; + title: string; + api: string; + clientType: string; + logcommand?: string; + open: boolean; + onCancel: () => void; +}; + +const langMap = { + shell: 'bash', + python: 'python', + javascript: 'javascript' +}; + +const langOptions = [ + { label: 'Curl', value: langMap.shell }, + { label: 'Python', value: langMap.python }, + { label: 'Nodejs', value: langMap.javascript } +]; + +const ViewCodeModal: React.FC = (props) => { + const { + title, + open, + api, + clientType, + logcommand, + onCancel, + payload, + parameters = {} + } = props || {}; + + const intl = useIntl(); + const [lang, setLang] = useState(langMap.shell); + + const BaseURL = `${window.location.origin}/v1-openai`; + + const formatPyParams = (params: any) => { + return _.keys(params).reduce((acc: string, key: string) => { + if (params[key] === null) { + return acc; + } + const value = + typeof params[key] === 'object' + ? JSON.stringify(params[key], null, 2) + : `"${params[key]}"`; + return acc + ` ${key}=${value},\n`; + }, ' file=audio_file\n'); + }; + + const codeValue = useMemo(() => { + const consoleLog = `console.log(response.text);\n`; + + const printLog = logcommand ? `print(response.${logcommand})` : ''; + + if (lang === langMap.shell) { + const code = `curl ${window.location.origin}/v1-openai/${api} \\\n-H "Content-Type: multipart/form-data" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-F file="@/path/to/file/audio.mp3;type=audio/mpeg" \\\n-F model="${parameters.model}" \\\n-F language="${parameters.language}"`; + return code; + } + if (lang === langMap.javascript) { + const paramStr = JSON.stringify( + { + ...parameters, + ...payload, + file: `fs.createReadStream(audio.mp3)` + }, + null, + 4 + ); + const params = paramStr.replace( + /"fs.createReadStream\(audio.mp3\)"/g, + 'fs.createReadStream("audio.mp3")' + ); + + const code = `const fs = require("fs")\nconst OpenAI = require("openai");\n\nconst openai = new OpenAI({\n "apiKey": "YOUR_GPUSTACK_API_KEY",\n "baseURL": "${BaseURL}"\n});\n\nasync function main(){\n const params = ${params};\nconst response = await openai.${clientType}(params);\n ${consoleLog}}\nmain();`; + + return code; + } + if (lang === langMap.python) { + const formattedParams = _.keys(parameters).reduce( + (acc: string, key: string) => { + if (parameters[key] === null) { + return acc; + } + const value = + typeof parameters[key] === 'string' + ? `"${parameters[key]}"` + : parameters[key]; + return acc + ` ${key}=${value},\n`; + }, + '' + ); + const params = formatPyParams(payload); + const code = `from openai import OpenAI\n\naudio_file = open("audio.mp3", "rb")\nclient = OpenAI(\n base_url="${BaseURL}", \n api_key="YOUR_GPUSTACK_API_KEY"\n)\n\nresponse = client.${clientType}(\n${formattedParams}${params})\nprint('response:', response.text)`; + return code; + } + return ''; + }, [lang, payload, parameters, api, clientType, logcommand]); + + const handleOnChangeLang = (value: string) => { + setLang(value); + }; + + const handleClose = () => { + setLang(langMap.shell); + onCancel(); + }; + + return ( + <> + +
+ {intl.formatMessage({ id: 'playground.viewcode.info' })} +
+
+ +
+ +
+
+
+ + + {intl.formatMessage( + { id: 'playground.viewcode.tips' }, + { + here: ( + + ) + } + )} + +
+
+
+ + ); +}; + +export default React.memo(ViewCodeModal); diff --git a/src/pages/playground/components/view-tts-code.tsx b/src/pages/playground/components/view-tts-code.tsx new file mode 100644 index 00000000..00f14614 --- /dev/null +++ b/src/pages/playground/components/view-tts-code.tsx @@ -0,0 +1,207 @@ +import EditorWrap from '@/components/editor-wrap'; +import HighlightCode from '@/components/highlight-code'; +import { BulbOutlined } from '@ant-design/icons'; +import { useIntl } from '@umijs/max'; +import { Button, Modal } from 'antd'; +import _ from 'lodash'; +import React, { useMemo, useState } from 'react'; + +type ViewModalProps = { + systemMessage?: string; + messageList?: any[]; + payload: Record; + parameters: any; + title: string; + api: string; + clientType: string; + logcommand?: string; + open: boolean; + onCancel: () => void; +}; + +const langMap = { + shell: 'bash', + python: 'python', + javascript: 'javascript' +}; + +const langOptions = [ + { label: 'Curl', value: langMap.shell }, + { label: 'Python', value: langMap.python }, + { label: 'Nodejs', value: langMap.javascript } +]; + +const ViewCodeModal: React.FC = (props) => { + const { + title, + open, + api, + clientType, + logcommand, + onCancel, + payload, + parameters = {} + } = props || {}; + + const intl = useIntl(); + const [lang, setLang] = useState(langMap.shell); + + const BaseURL = `${window.location.origin}/v1-openai`; + + const formatPyParams = (params: any) => { + return _.keys(params).reduce((acc: string, key: string) => { + if (params[key] === null) { + return acc; + } + const value = + typeof params[key] === 'object' + ? JSON.stringify(params[key], null, 2) + : `"${params[key]}"`; + return acc + ` ${key}=${value},\n`; + }, ''); + }; + + const codeValue = useMemo(() => { + const consoleLog = logcommand + ? `console.log(response.${logcommand});` + : `console.log(ouptFile);\n const buffer = Buffer.from(await response.arrayBuffer());\n await fs.promises.writeFile(ouptFile, buffer);\n`; + + const printLog = logcommand ? `print(response.${logcommand})` : ''; + + if (lang === langMap.shell) { + const code = `curl ${window.location.origin}/v1-openai/${api} \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify( + { + ...parameters, + ...payload + }, + null, + 2 + )}' \\\n--output output.${parameters.response_format}`; + return code; + } + if (lang === langMap.javascript) { + const code = `const fs = require("fs");\nconst path = require("path");\nconst OpenAI = require("openai");\n\nconst ouptFile = path.resolve("./output.${parameters.response_format}");\n\nconst openai = new OpenAI({\n "apiKey": "YOUR_GPUSTACK_API_KEY",\n "baseURL": "${BaseURL}"\n});\n\nasync function main(){\n const params = ${JSON.stringify( + { + ...parameters, + ...payload + }, + null, + 4 + )};\nconst response = await openai.${clientType}(params);\n ${consoleLog}}\nmain();`; + + return code; + } + if (lang === langMap.python) { + const formattedParams = _.keys(parameters).reduce( + (acc: string, key: string) => { + if (parameters[key] === null) { + return acc; + } + const value = + typeof parameters[key] === 'string' + ? `"${parameters[key]}"` + : parameters[key]; + return acc + ` ${key}=${value},\n`; + }, + '' + ); + const params = formatPyParams(payload); + const code = `from pathlib import Path\nfrom openai import OpenAI\n\nclient = OpenAI(\n base_url="${BaseURL}", \n api_key="YOUR_GPUSTACK_API_KEY"\n)\n\nouput_file_path = Path(__file__).parent\nresponse = client.${clientType}(\n${formattedParams}${params})\n${printLog}\nwith open(output_file_path, "wb") as f: + for chunk in response.iter_bytes(): + f.write(chunk) + +print(f"Audio saved to {output_file_path}")`; + return code; + } + return ''; + }, [lang, payload, parameters, api, clientType, logcommand]); + + const handleOnChangeLang = (value: string) => { + setLang(value); + }; + + const handleClose = () => { + setLang(langMap.shell); + onCancel(); + }; + + return ( + <> + +
+ {intl.formatMessage({ id: 'playground.viewcode.info' })} +
+
+ +
+ +
+
+
+ + + {intl.formatMessage( + { id: 'playground.viewcode.tips' }, + { + here: ( + + ) + } + )} + +
+
+
+ + ); +}; + +export default React.memo(ViewCodeModal);