chore: render katex in markdown
This commit is contained in:
+4
-1
@@ -17,6 +17,7 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.5.1",
|
||||
"@ant-design/pro-components": "^2.7.19",
|
||||
"@braintree/sanitize-url": "^7.1.1",
|
||||
"@huggingface/gguf": "^0.1.7",
|
||||
"@huggingface/hub": "^0.15.1",
|
||||
"@huggingface/tasks": "^0.11.6",
|
||||
@@ -48,7 +49,6 @@
|
||||
"lodash": "^4.17.21",
|
||||
"mammoth": "^1.8.0",
|
||||
"marked": "^14.1.0",
|
||||
"marked-katex-extension": "^5.1.4",
|
||||
"ml-dataset-iris": "^1.2.1",
|
||||
"ml-pca": "^4.1.1",
|
||||
"numeral": "^2.0.6",
|
||||
@@ -64,7 +64,10 @@
|
||||
"react-hotkeys-hook": "^4.5.0",
|
||||
"react-markdown": "^9.0.3",
|
||||
"rehype-katex": "^7.0.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-math": "^6.0.0",
|
||||
"simplebar-react": "^3.2.6",
|
||||
"styled-components": "^6.1.15",
|
||||
"umi-presets-pro": "^2.0.3",
|
||||
"wavesurfer.js": "^7.8.8",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||
|
||||
Generated
+399
-108
File diff suppressed because it is too large
Load Diff
@@ -82,6 +82,10 @@
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.m-t-6 {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import classNames from 'classnames';
|
||||
import hljs from 'highlight.js';
|
||||
import { memo, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import CopyButton from '../copy-button';
|
||||
import { escapeHtml } from './utils';
|
||||
|
||||
@@ -15,9 +16,64 @@ interface CodeViewerProps {
|
||||
theme?: 'light' | 'dark';
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
interface CodeHeaderProps {
|
||||
copyValue: string;
|
||||
copyable: boolean;
|
||||
lang: string;
|
||||
theme: 'light' | 'dark';
|
||||
}
|
||||
|
||||
const CodeHeaderWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
font-size: 12px;
|
||||
color: var(--ant-color-text-tertiary);
|
||||
background-color: #fafafa;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
&.dark {
|
||||
background-color: #383838;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
border-radius: var(--border-radius-mini);
|
||||
`;
|
||||
|
||||
const CodeHeader: React.FC<CodeHeaderProps> = ({
|
||||
copyValue,
|
||||
lang,
|
||||
theme,
|
||||
copyable
|
||||
}) => {
|
||||
if (!copyable) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<CodeHeaderWrapper
|
||||
className={classNames({
|
||||
dark: theme === 'dark',
|
||||
light: theme === 'light'
|
||||
})}
|
||||
>
|
||||
<span>{lang}</span>
|
||||
<CopyButton
|
||||
text={copyValue}
|
||||
size="small"
|
||||
style={{ color: '#abb2bf' }}
|
||||
></CopyButton>
|
||||
</CodeHeaderWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const CodeViewer: React.FC<CodeViewerProps> = (props) => {
|
||||
const {
|
||||
code,
|
||||
code = '',
|
||||
copyValue,
|
||||
lang,
|
||||
autodetect = true,
|
||||
@@ -63,38 +119,39 @@ const CodeViewer: React.FC<CodeViewerProps> = (props) => {
|
||||
}, [code, lang, autodetect, ignoreIllegals]);
|
||||
|
||||
return (
|
||||
<pre
|
||||
className={classNames(
|
||||
'code-pre custome-scrollbar custom-scrollbar-horizontal ',
|
||||
{
|
||||
dark: props.theme === 'dark',
|
||||
light: props.theme === 'light',
|
||||
copyable: copyable
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
height: height,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
<code
|
||||
style={{ minHeight: height }}
|
||||
className={classNames(highlightedCode.className, {
|
||||
dark: props.theme === 'dark',
|
||||
light: props.theme === 'light'
|
||||
})}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: highlightedCode.value
|
||||
<Wrapper>
|
||||
<CodeHeader
|
||||
copyValue={copyValue || code}
|
||||
lang={lang}
|
||||
copyable={copyable}
|
||||
theme={props.theme || 'light'}
|
||||
></CodeHeader>
|
||||
<pre
|
||||
className={classNames(
|
||||
'code-pre custome-scrollbar custom-scrollbar-horizontal ',
|
||||
{
|
||||
dark: props.theme === 'dark',
|
||||
light: props.theme === 'light'
|
||||
}
|
||||
)}
|
||||
style={{
|
||||
marginBottom: 0,
|
||||
height: height,
|
||||
...style
|
||||
}}
|
||||
></code>
|
||||
{copyable && (
|
||||
<CopyButton
|
||||
text={copyValue || code}
|
||||
size="small"
|
||||
style={{ color: '#abb2bf' }}
|
||||
></CopyButton>
|
||||
)}
|
||||
</pre>
|
||||
>
|
||||
<code
|
||||
style={{ minHeight: height }}
|
||||
className={classNames(highlightedCode.className, {
|
||||
dark: props.theme === 'dark',
|
||||
light: props.theme === 'light'
|
||||
})}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: highlightedCode.value
|
||||
}}
|
||||
></code>
|
||||
</pre>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
|
||||
.code-pre {
|
||||
padding-inline: 12px 12px;
|
||||
border-radius: 0 0 var(--border-radius-mini) var(--border-radius-mini);
|
||||
position: relative;
|
||||
border-radius: var(--border-radius-mini);
|
||||
|
||||
code {
|
||||
line-height: 1.5;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import React, { useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import styled from 'styled-components';
|
||||
import HighlightCode from '../highlight-code';
|
||||
import './index.less';
|
||||
import { escapeBrackets, escapeDollarNumber, escapeMhchem } from './utils';
|
||||
|
||||
interface FullMarkdownProps {
|
||||
content: string;
|
||||
theme?: 'light' | 'dark';
|
||||
}
|
||||
|
||||
const Wrapper = styled.div.attrs(() => ({
|
||||
className: 'markdown-viewer'
|
||||
}))`
|
||||
line-height: 2;
|
||||
`;
|
||||
|
||||
const CodeViewer = (props: any) => {
|
||||
const { children, className, node, theme, ...rest } = props;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
return match ? (
|
||||
<HighlightCode code={children} lang={match[1]} theme={theme} />
|
||||
) : (
|
||||
<code {...rest} className={className}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
};
|
||||
|
||||
const FullMarkdown: React.FC<FullMarkdownProps> = ({
|
||||
content,
|
||||
theme = 'light'
|
||||
}) => {
|
||||
const escapedContent = useMemo(() => {
|
||||
return escapeMhchem(escapeBrackets(escapeDollarNumber(content)));
|
||||
}, [content]);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[[remarkMath], remarkGfm]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
code(props) {
|
||||
return <CodeViewer {...props} theme={theme}></CodeViewer>;
|
||||
},
|
||||
a: ({ href, children, ...props }) => (
|
||||
<a
|
||||
href={sanitizeUrl(href)}
|
||||
{...props}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
)
|
||||
}}
|
||||
>
|
||||
{escapedContent}
|
||||
</ReactMarkdown>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default FullMarkdown;
|
||||
@@ -22,6 +22,7 @@
|
||||
font-weight: 700;
|
||||
font-weight: var(--font-weight-bold);
|
||||
font-size: var(--font-size-small);
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.hj-wrapper {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { EyeOutlined } from '@ant-design/icons';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import { Checkbox, Image, Typography } from 'antd';
|
||||
import { unescape } from 'lodash';
|
||||
import { TokensList, marked } from 'marked';
|
||||
@@ -52,7 +53,8 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
|
||||
}, []);
|
||||
|
||||
const generateImgSrc = useCallback(
|
||||
(src: string | null) => {
|
||||
(url: string | null) => {
|
||||
const src = sanitizeUrl(url || '');
|
||||
if (!src) {
|
||||
return '';
|
||||
}
|
||||
@@ -186,6 +188,11 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
|
||||
return;
|
||||
}
|
||||
const imgs = document.querySelectorAll('.markdown-viewer img');
|
||||
const links = document.querySelectorAll('.markdown-viewer a');
|
||||
links.forEach((link) => {
|
||||
// set target blank for all links
|
||||
link.setAttribute('target', '_blank');
|
||||
});
|
||||
imgs.forEach((img) => {
|
||||
const src = img.getAttribute('src');
|
||||
img.setAttribute('src', generateImgSrc(src));
|
||||
|
||||
@@ -14,3 +14,42 @@ export const unescape = (str = '') => {
|
||||
? str.replace(reEscapedHtml, (entity) => htmlUnescapes[entity] || "'")
|
||||
: str;
|
||||
};
|
||||
|
||||
export function escapeDollarNumber(text: string) {
|
||||
let escapedText = '';
|
||||
|
||||
for (let i = 0; i < text.length; i += 1) {
|
||||
let char = text[i];
|
||||
const nextChar = text[i + 1] || ' ';
|
||||
|
||||
if (char === '$' && nextChar >= '0' && nextChar <= '9') {
|
||||
char = '\\$';
|
||||
}
|
||||
|
||||
escapedText += char;
|
||||
}
|
||||
|
||||
return escapedText;
|
||||
}
|
||||
|
||||
export function escapeBrackets(text: string) {
|
||||
const pattern =
|
||||
/(```[\S\s]*?```|`.*?`)|\\\[([\S\s]*?[^\\])\\]|\\\((.*?)\\\)/g;
|
||||
return text.replaceAll(
|
||||
pattern,
|
||||
(match, codeBlock, squareBracket, roundBracket) => {
|
||||
if (codeBlock) {
|
||||
return codeBlock;
|
||||
} else if (squareBracket) {
|
||||
return `$$${squareBracket}$$`;
|
||||
} else if (roundBracket) {
|
||||
return `$${roundBracket}$`;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function escapeMhchem(text: string) {
|
||||
return text.replaceAll('$\\ce{', '$\\\\ce{').replaceAll('$\\pu{', '$\\\\pu{');
|
||||
}
|
||||
|
||||
@@ -516,15 +516,19 @@ const Models: React.FC<ModelsProps> = ({
|
||||
);
|
||||
|
||||
const getModelInstances = async (row: any, options?: any) => {
|
||||
const params = {
|
||||
id: row.id,
|
||||
page: 1,
|
||||
perPage: 100
|
||||
};
|
||||
const data = await queryModelInstancesList(params, {
|
||||
token: options?.token
|
||||
});
|
||||
return data.items || [];
|
||||
try {
|
||||
const params = {
|
||||
id: row.id,
|
||||
page: 1,
|
||||
perPage: 100
|
||||
};
|
||||
const data = await queryModelInstancesList(params, {
|
||||
token: options?.token
|
||||
});
|
||||
return data.items || [];
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const generateChildrenRequestAPI = (params: any) => {
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { request } from '@umijs/max';
|
||||
|
||||
export const CHAT_API = '/v1-openai/chat/completions';
|
||||
export const OPENAI_COMPATIBLE = 'v1-openai';
|
||||
|
||||
export const CREAT_IMAGE_API = '/v1-openai/images/generations';
|
||||
export const EDIT_IMAGE_API = '/v1-openai/images/edits';
|
||||
export const CHAT_API = `/${OPENAI_COMPATIBLE}/chat/completions`;
|
||||
|
||||
export const EMBEDDING_API = '/v1-openai/embeddings';
|
||||
export const CREAT_IMAGE_API = `/${OPENAI_COMPATIBLE}/images/generations`;
|
||||
export const EDIT_IMAGE_API = `/${OPENAI_COMPATIBLE}/images/edits`;
|
||||
|
||||
export const OPENAI_MODELS = '/v1-openai/models';
|
||||
export const EMBEDDING_API = `/${OPENAI_COMPATIBLE}/embeddings`;
|
||||
|
||||
export const OPENAI_MODELS = `/${OPENAI_COMPATIBLE}/models`;
|
||||
|
||||
export const RERANKER_API = '/rerank';
|
||||
|
||||
export const AUDIO_TEXT_TO_SPEECH_API = '/v1-openai/audio/speech';
|
||||
export const AUDIO_TEXT_TO_SPEECH_API = `/${OPENAI_COMPATIBLE}/audio/speech`;
|
||||
|
||||
export const AUDIO_SPEECH_TO_TEXT_API = '/v1-openai/audio/transcriptions';
|
||||
export const AUDIO_SPEECH_TO_TEXT_API = `/${OPENAI_COMPATIBLE}/audio/transcriptions`;
|
||||
|
||||
export async function execChatCompletions(params: any) {
|
||||
return request(`${CHAT_API}`, {
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
useState
|
||||
} from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { handleEmbedding } from '../apis';
|
||||
import { EMBEDDING_API, handleEmbedding } from '../apis';
|
||||
import { ParamsSchema } from '../config/types';
|
||||
import '../style/ground-left.less';
|
||||
import '../style/rerank.less';
|
||||
@@ -128,7 +128,7 @@ const GroundEmbedding: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
|
||||
const viewCodeContent = useMemo(() => {
|
||||
return generateEmbeddingCode({
|
||||
api: '/v1-openai/embeddings',
|
||||
api: EMBEDDING_API,
|
||||
parameters: {
|
||||
...parameters,
|
||||
input: [
|
||||
|
||||
@@ -236,7 +236,7 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
const viewCodeContent = useMemo(() => {
|
||||
if (isOpenaiCompatible) {
|
||||
return generateOpenaiImageCode({
|
||||
api: '/v1-openai/images/generations',
|
||||
api: CREAT_IMAGE_API,
|
||||
parameters: {
|
||||
...finalParameters,
|
||||
prompt: currentPrompt
|
||||
@@ -244,7 +244,7 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
});
|
||||
}
|
||||
return generateImageCode({
|
||||
api: '/v1-openai/images/generations',
|
||||
api: CREAT_IMAGE_API,
|
||||
parameters: {
|
||||
...finalParameters,
|
||||
prompt: currentPrompt
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import { speechToText } from '../apis';
|
||||
import { AUDIO_SPEECH_TO_TEXT_API, speechToText } from '../apis';
|
||||
import { SpeechToTextFormat } from '../config';
|
||||
import { RealtimeParamsConfig as paramsConfig } from '../config/params-config';
|
||||
import '../style/ground-left.less';
|
||||
@@ -96,7 +96,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
|
||||
const viewCodeContent = useMemo(() => {
|
||||
return speechToTextCode({
|
||||
api: '/v1-openai/audio/transcriptions',
|
||||
api: AUDIO_SPEECH_TO_TEXT_API,
|
||||
parameters: {
|
||||
...parameters
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
useRef,
|
||||
useState
|
||||
} from 'react';
|
||||
import { CHAT_API, textToSpeech } from '../apis';
|
||||
import { AUDIO_TEXT_TO_SPEECH_API, CHAT_API, textToSpeech } from '../apis';
|
||||
import { TTSParamsConfig as paramsConfig } from '../config/params-config';
|
||||
import { MessageItem, ParamsSchema } from '../config/types';
|
||||
import '../style/ground-left.less';
|
||||
@@ -97,7 +97,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
||||
|
||||
const viewCodeContent = useMemo(() => {
|
||||
return TextToSpeechCode({
|
||||
api: '/v1-openai/audio/speech',
|
||||
api: AUDIO_TEXT_TO_SPEECH_API,
|
||||
parameters: {
|
||||
...parameters,
|
||||
input: currentPrompt
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import MarkdownViewer from '@/components/markdown-viewer';
|
||||
import FullMarkdown from '@/components/markdown-viewer/full-markdown';
|
||||
import { Input } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
@@ -35,7 +35,9 @@ const MessageBody: React.FC<MessageBodyProps> = ({
|
||||
thinkerRef.current = new ThinkParser();
|
||||
}
|
||||
if (actions?.includes('markdown')) {
|
||||
return thinkerRef.current.parse(data.content);
|
||||
const res = thinkerRef.current.parse(data.content);
|
||||
console.log('markdown parse:', res);
|
||||
return res;
|
||||
}
|
||||
return {
|
||||
thought: '',
|
||||
@@ -206,12 +208,9 @@ const MessageBody: React.FC<MessageBodyProps> = ({
|
||||
{actions?.includes('markdown') ? (
|
||||
<>
|
||||
<ThinkContent content={content.thought}></ThinkContent>
|
||||
<div style={{ paddingInline: 4 }}>
|
||||
<MarkdownViewer
|
||||
content={content.result || ''}
|
||||
theme="light"
|
||||
/>
|
||||
</div>
|
||||
<FullMarkdown
|
||||
content={`${content.result || ''}`}
|
||||
></FullMarkdown>
|
||||
</>
|
||||
) : (
|
||||
<Input.TextArea
|
||||
|
||||
@@ -18,31 +18,31 @@ class ThinkParser {
|
||||
|
||||
if (!this.collecting) {
|
||||
if (endIndex !== -1 && (startIndex === -1 || endIndex < startIndex)) {
|
||||
// 1 发现 `</think>`,但之前没有 `<think>`:
|
||||
// 将 `result` + `</think>` 之前的内容作为 `thought`
|
||||
// 1 Found `</think>`, but there was no `<think>` before:
|
||||
// Take `result` + the content before `</think>` as `thought`
|
||||
this.thought =
|
||||
this.result + chunk.substring(this.lastCheckedIndex, endIndex);
|
||||
this.result = ''; // **清空 result**
|
||||
this.lastCheckedIndex = endIndex + 8; // 跳过 `</think>`
|
||||
this.result = ''; // **clear result**
|
||||
this.lastCheckedIndex = endIndex + 8; // Skip `</think>`
|
||||
} else if (startIndex !== -1) {
|
||||
// 2 发现 `<think>`,进入思考模式:
|
||||
// 2 Found `<think>`, start thinking mode:
|
||||
this.result += chunk.substring(this.lastCheckedIndex, startIndex);
|
||||
this.collecting = true;
|
||||
this.lastCheckedIndex = startIndex + 7; // 跳过 `<think>`
|
||||
this.lastCheckedIndex = startIndex + 7; // Skip `<think>`
|
||||
} else {
|
||||
// 3 没有 `<think>` 也没有 `</think>`,直接追加到 `result`
|
||||
// 3 Still in normal mode, append to `result`
|
||||
this.result += chunk.substring(this.lastCheckedIndex);
|
||||
this.lastCheckedIndex = chunk.length;
|
||||
}
|
||||
} else {
|
||||
if (endIndex !== -1) {
|
||||
// 4 发现 `</think>`,结束思考模式:
|
||||
// 4 Found `</think>`, end thinking mode:
|
||||
this.thought += chunk.substring(this.lastCheckedIndex, endIndex);
|
||||
|
||||
this.collecting = false;
|
||||
this.lastCheckedIndex = endIndex + 8; // 跳过 `</think>`
|
||||
this.lastCheckedIndex = endIndex + 8; // Skip `</think>`
|
||||
} else {
|
||||
// 5 仍在思考模式中,追加到 `thought`
|
||||
// 5 Still in thinking mode, append to `thought`
|
||||
this.thought += chunk.substring(this.lastCheckedIndex);
|
||||
this.lastCheckedIndex = chunk.length;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useIntl } from '@umijs/max';
|
||||
import { Button, Modal } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { OPENAI_COMPATIBLE } from '../apis';
|
||||
|
||||
type ViewModalProps = {
|
||||
systemMessage?: string;
|
||||
@@ -46,7 +47,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
||||
const intl = useIntl();
|
||||
const [lang, setLang] = useState(langMap.shell);
|
||||
|
||||
const BaseURL = `${window.location.origin}/v1-openai`;
|
||||
const BaseURL = `${window.location.origin}/${OPENAI_COMPATIBLE}`;
|
||||
|
||||
const formatPyParams = (params: any) => {
|
||||
return _.keys(params).reduce((acc: string, key: string) => {
|
||||
@@ -69,7 +70,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
||||
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(
|
||||
const code = `curl ${window.location.origin}/${OPENAI_COMPATIBLE}/${api} \\\n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{YOUR_GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify(
|
||||
{
|
||||
...parameters,
|
||||
...payload
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { map } from 'lodash';
|
||||
import { CREAT_IMAGE_API } from '../apis';
|
||||
import { MessageItem } from './types';
|
||||
|
||||
export const Roles = {
|
||||
@@ -121,7 +122,7 @@ export const OpenAIViewCode = {
|
||||
logcommand: 'data[0].b64_json'
|
||||
},
|
||||
imageAdvanced: {
|
||||
api: '/v1-openai/images/generations',
|
||||
api: `${CREAT_IMAGE_API}`,
|
||||
clientType: 'images.generate',
|
||||
logcommand: {
|
||||
python: "json()['data'][0]['b64_json']",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { OPENAI_COMPATIBLE } from '../apis';
|
||||
import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils';
|
||||
|
||||
export const speechToTextCode = ({ api, parameters }: Record<string, any>) => {
|
||||
@@ -18,7 +19,7 @@ ${formatCurlArgs(parameters, true)}`
|
||||
from openai import OpenAI\n
|
||||
audio_file = open("audio.mp3", "rb")
|
||||
client = OpenAI(
|
||||
base_url="${host}/v1-openai",
|
||||
base_url="${host}/${OPENAI_COMPATIBLE}",
|
||||
api_key="YOUR_GPUSTACK_API_KEY"
|
||||
)
|
||||
|
||||
@@ -43,7 +44,7 @@ const OpenAI = require("openai");
|
||||
|
||||
const openai = new OpenAI({
|
||||
"apiKey": "YOUR_GPUSTACK_API_KEY",
|
||||
"baseURL": "${host}/v1-openai"
|
||||
"baseURL": "${host}/${OPENAI_COMPATIBLE}"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
@@ -77,7 +78,7 @@ from pathlib import Path
|
||||
from openai import OpenAI\n
|
||||
output_file_path = Path(__file__).parent / "output.mp3"
|
||||
client = OpenAI(
|
||||
base_url="${host}/v1-openai",
|
||||
base_url="${host}/${OPENAI_COMPATIBLE}",
|
||||
api_key="YOUR_GPUSTACK_API_KEY"
|
||||
)
|
||||
|
||||
@@ -102,7 +103,7 @@ const ouptFile = path.resolve("./output.mp3");
|
||||
|
||||
const openai = new OpenAI({
|
||||
"apiKey": "YOUR_GPUSTACK_API_KEY",
|
||||
"baseURL": "${host}/v1-openai"
|
||||
"baseURL": "${host}/${OPENAI_COMPATIBLE}"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { OPENAI_COMPATIBLE } from '../apis';
|
||||
import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils';
|
||||
|
||||
export const generateEmbeddingCode = ({
|
||||
@@ -17,7 +18,7 @@ ${formatCurlArgs(parameters, false)}`.trim();
|
||||
const pythonCode = `
|
||||
from openai import OpenAI\n
|
||||
client = OpenAI(
|
||||
base_url="${host}/v1-openai",
|
||||
base_url="${host}/${OPENAI_COMPATIBLE}",
|
||||
api_key="YOUR_GPUSTACK_API_KEY"
|
||||
)
|
||||
|
||||
@@ -34,7 +35,7 @@ const OpenAI = require("openai");
|
||||
|
||||
const openai = new OpenAI({
|
||||
"apiKey": "YOUR_GPUSTACK_API_KEY",
|
||||
"baseURL": "${host}/v1-openai"
|
||||
"baseURL": "${host}/${OPENAI_COMPATIBLE}"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { OPENAI_COMPATIBLE } from '../apis';
|
||||
import { fomatNodeJsParams, formatCurlArgs, formatPyParams } from './utils';
|
||||
|
||||
export const generateImageCode = ({
|
||||
@@ -44,7 +45,7 @@ print(response.json()['data'][0]['b64_json'])`.trim();
|
||||
const nodeJsCode = `
|
||||
const axios = require('axios');
|
||||
|
||||
const url = "http://localhost/v1-openai/images/generations";
|
||||
const url = "${host}/${OPENAI_COMPATIBLE}/images/generations";
|
||||
const headers = {
|
||||
"Content-type": "application/json",
|
||||
"Authorization": "Bearer $\{YOUR_GPUSTACK_API_KEY}"
|
||||
@@ -92,7 +93,7 @@ ${formatCurlArgs(_.omit(parameters, ['mask', 'image']), isFormdata)}`
|
||||
const pythonCode = `
|
||||
from openai import OpenAI\n
|
||||
client = OpenAI(
|
||||
base_url="${host}/v1-openai",
|
||||
base_url="${host}/${OPENAI_COMPATIBLE}",
|
||||
api_key="YOUR_GPUSTACK_API_KEY"
|
||||
)
|
||||
|
||||
@@ -109,7 +110,7 @@ const OpenAI = require("openai");
|
||||
|
||||
const openai = new OpenAI({
|
||||
"apiKey": "YOUR_GPUSTACK_API_KEY",
|
||||
"baseURL": "${host}/v1-openai"
|
||||
"baseURL": "${host}/${OPENAI_COMPATIBLE}"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
|
||||
@@ -41,12 +41,13 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
width={700}
|
||||
styles={{
|
||||
body: {
|
||||
height: 550
|
||||
height: 620
|
||||
}
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<Tabs
|
||||
size="small"
|
||||
items={items}
|
||||
activeKey={activeKey}
|
||||
type="card"
|
||||
|
||||
@@ -67,9 +67,10 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
</span>
|
||||
<WarningOutlined
|
||||
style={{ color: 'var(--ant-color-warning)' }}
|
||||
className="font-size-14 m-l-5"
|
||||
className="font-size-14 m-l-5 m-r-5"
|
||||
/>
|
||||
<Button
|
||||
style={{ padding: 0 }}
|
||||
type="link"
|
||||
size="small"
|
||||
href="https://docs.gpustack.ai/latest/installation/installation-requirements/"
|
||||
@@ -85,7 +86,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
theme="dark"
|
||||
lang="bash"
|
||||
></HighlightCode>
|
||||
<h3>
|
||||
<h3 className="m-t-10">
|
||||
2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
|
||||
<span
|
||||
className="font-size-12"
|
||||
@@ -97,7 +98,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
}}
|
||||
></span>
|
||||
</h3>
|
||||
<div className="m-b-20">
|
||||
<div className="m-b-16">
|
||||
<Radio.Group
|
||||
block
|
||||
options={containerInstallOptions}
|
||||
@@ -117,8 +118,8 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
<HighlightCode theme="dark" code={code} lang="sh"></HighlightCode>
|
||||
<h3 className="m-b-0">
|
||||
<HighlightCode theme="dark" code={code} lang="bash"></HighlightCode>
|
||||
<h3 className="m-b-0 m-t-10">
|
||||
3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -18,12 +18,12 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
code={addWorkerGuide.mac.getToken}
|
||||
theme="dark"
|
||||
></HighlightCode>
|
||||
<h4>Windows </h4>
|
||||
<h4 className="m-t-6">Windows </h4>
|
||||
<HighlightCode
|
||||
code={addWorkerGuide.win.getToken}
|
||||
theme="dark"
|
||||
></HighlightCode>
|
||||
<h3>
|
||||
<h3 className="m-t-10">
|
||||
2. {intl.formatMessage({ id: 'resources.worker.add.step2' })}{' '}
|
||||
<span
|
||||
className="font-size-12"
|
||||
@@ -43,7 +43,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
})}
|
||||
theme="dark"
|
||||
></HighlightCode>
|
||||
<h4>Windows </h4>
|
||||
<h4 className="m-t-6">Windows </h4>
|
||||
<HighlightCode
|
||||
theme="dark"
|
||||
code={addWorkerGuide.win.registerWorker({
|
||||
@@ -51,7 +51,7 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
||||
token: '${mytoken}'
|
||||
})}
|
||||
></HighlightCode>
|
||||
<h3 className="m-b-0">
|
||||
<h3 className="m-b-0 m-t-10">
|
||||
3. {intl.formatMessage({ id: 'resources.worker.add.step3' })}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
border-left: 2px solid var(--ant-color-split);
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 16px;
|
||||
padding-left: 16px;
|
||||
background: var(--color-fill-sider);
|
||||
border-radius: 2px;
|
||||
|
||||
Reference in New Issue
Block a user