chore: render katex in markdown

This commit is contained in:
jialin
2025-02-18 19:51:22 +08:00
parent ea34431cf7
commit 4c77151877
26 changed files with 693 additions and 208 deletions
+9 -7
View File
@@ -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
+2 -1
View File
@@ -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']",
+5 -4
View File
@@ -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() {
+3 -2
View File
@@ -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() {
+4 -3
View File
@@ -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() {