mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-02 23:03:57 +08:00
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "monkey-code-ui",
|
||||
"version": "0.0.0",
|
||||
"version": "0.6.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,59 +1,123 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import MonacoEditor from '@monaco-editor/react';
|
||||
import { getBaseLanguageId } from '@/utils';
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
|
||||
const CHAR_WIDTH = 8; // 估算每个字符宽度,实际可根据字体调整
|
||||
const MIN_WIDTH = 200;
|
||||
const MAX_WIDTH = 1060;
|
||||
const MAX_HEIGHT = 420;
|
||||
|
||||
const Code = ({
|
||||
data,
|
||||
language,
|
||||
options,
|
||||
autoHeight = true,
|
||||
autoWidth = true,
|
||||
}: {
|
||||
data: string;
|
||||
language: string;
|
||||
options?: any;
|
||||
autoHeight?: boolean;
|
||||
autoWidth?: boolean;
|
||||
}) => {
|
||||
const editorRef = useRef<any>(null);
|
||||
const [height, setHeight] = useState(100);
|
||||
const [width, setWidth] = useState(MAX_WIDTH);
|
||||
|
||||
// 动态调整高度和宽度
|
||||
const updateSize = () => {
|
||||
if (!editorRef.current) return;
|
||||
const model = editorRef.current.getModel();
|
||||
if (!model) return;
|
||||
// 获取视觉高度(自适应视觉行数)
|
||||
if (autoHeight) {
|
||||
const contentHeight = editorRef.current.getContentHeight();
|
||||
const newHeight = Math.min(contentHeight, MAX_HEIGHT);
|
||||
setHeight(newHeight);
|
||||
}
|
||||
|
||||
if (autoWidth) {
|
||||
const lines = model.getLinesContent();
|
||||
|
||||
const maxLineLength = lines.reduce(
|
||||
(max: number, line: string) => Math.max(max, line.length),
|
||||
0
|
||||
);
|
||||
const newWidth = Math.min(
|
||||
Math.max(maxLineLength * CHAR_WIDTH + 40, MIN_WIDTH),
|
||||
MAX_WIDTH
|
||||
);
|
||||
setWidth(newWidth);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateSize();
|
||||
}, [data]);
|
||||
|
||||
// 监听编辑器内容变化和布局变化,动态调整高度
|
||||
const handleEditorDidMount = (editor: any) => {
|
||||
editorRef.current = editor;
|
||||
updateSize();
|
||||
editor.onDidContentSizeChange(() => {
|
||||
updateSize();
|
||||
});
|
||||
// 隐藏光标
|
||||
const editorDom = editor.getDomNode();
|
||||
if (editorDom) {
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `.monaco-editor .cursor { display: none !important; }`;
|
||||
editorDom.appendChild(style);
|
||||
}
|
||||
};
|
||||
|
||||
const Code = () => {
|
||||
return (
|
||||
<div style={{ height: 420 }}>
|
||||
<MonacoEditor
|
||||
height='100%'
|
||||
language={getBaseLanguageId(data?.program_language || 'plaintext')}
|
||||
value={editorValue}
|
||||
theme='vs-dark'
|
||||
options={{
|
||||
readOnly: true,
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
lineNumbers: 'on',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
overviewRulerLanes: 0,
|
||||
guides: {
|
||||
indentation: true,
|
||||
highlightActiveIndentation: true,
|
||||
highlightActiveBracketPair: false,
|
||||
},
|
||||
renderLineHighlight: 'none',
|
||||
cursorStyle: 'line',
|
||||
cursorBlinking: 'solid',
|
||||
cursorWidth: 0,
|
||||
contextmenu: false,
|
||||
selectionHighlight: false,
|
||||
selectOnLineNumbers: false,
|
||||
occurrencesHighlight: 'off',
|
||||
links: false,
|
||||
hover: { enabled: false },
|
||||
codeLens: false,
|
||||
dragAndDrop: false,
|
||||
mouseWheelZoom: false,
|
||||
accessibilitySupport: 'off',
|
||||
bracketPairColorization: { enabled: false },
|
||||
matchBrackets: 'never',
|
||||
}}
|
||||
onMount={(editor) => {
|
||||
editorRef.current = editor;
|
||||
setEditorReady(true);
|
||||
// 隐藏光标
|
||||
const editorDom = editor.getDomNode();
|
||||
if (editorDom) {
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `.monaco-editor .cursor { display: none !important; }`;
|
||||
editorDom.appendChild(style);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<MonacoEditor
|
||||
height={height}
|
||||
width={width}
|
||||
language={getBaseLanguageId(language || 'plaintext')}
|
||||
value={data}
|
||||
theme='vs-dark'
|
||||
options={{
|
||||
readOnly: true,
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
overviewRulerLanes: 0,
|
||||
guides: {
|
||||
indentation: true,
|
||||
highlightActiveIndentation: true,
|
||||
highlightActiveBracketPair: false,
|
||||
},
|
||||
renderLineHighlight: 'none',
|
||||
cursorStyle: 'line',
|
||||
cursorBlinking: 'solid',
|
||||
cursorWidth: 0,
|
||||
contextmenu: false,
|
||||
selectionHighlight: false,
|
||||
selectOnLineNumbers: false,
|
||||
occurrencesHighlight: 'off',
|
||||
links: false,
|
||||
hover: { enabled: false },
|
||||
codeLens: false,
|
||||
dragAndDrop: false,
|
||||
mouseWheelZoom: false,
|
||||
accessibilitySupport: 'off',
|
||||
bracketPairColorization: { enabled: false },
|
||||
matchBrackets: 'never',
|
||||
lineNumbers: 'on',
|
||||
verticalScrollbarSize: 0,
|
||||
horizontalScrollbarSize: 0,
|
||||
scrollbar: {
|
||||
vertical: 'hidden',
|
||||
},
|
||||
...options,
|
||||
}}
|
||||
onMount={handleEditorDidMount}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
// import { ToolInfo } from '@/api';
|
||||
import { Icon, message } from '@c-x/ui';
|
||||
import { Box, Button, IconButton, Stack, useTheme, alpha } from '@mui/material';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { message } from '@c-x/ui';
|
||||
import { Box, useTheme } from '@mui/material';
|
||||
import React from 'react';
|
||||
import ReactMarkdown, { Components } from 'react-markdown';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import {
|
||||
github,
|
||||
anOldHope,
|
||||
} from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
||||
import remarkBreaks from 'remark-breaks';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import ExpandMoreRoundedIcon from '@mui/icons-material/ExpandMoreRounded';
|
||||
import { getBaseLanguageId } from '@/utils';
|
||||
import Diff from './diff';
|
||||
import { visit } from 'unist-util-visit';
|
||||
import Code from './code';
|
||||
|
||||
interface ExtendedComponents extends Components {
|
||||
tools?: React.ComponentType<any>;
|
||||
@@ -38,11 +32,47 @@ export const toolNames = [
|
||||
'switch_mode',
|
||||
'new_task',
|
||||
'fetch_instructions',
|
||||
'follow_up',
|
||||
];
|
||||
|
||||
// 去掉下划线的标签名,用于Markdown渲染
|
||||
export const toolTagNames = toolNames.map((name) => name.replace(/_/g, ''));
|
||||
|
||||
// 提取 <write_to_file> 块,解析 path、content、language,支持多个 <content>,每个加唯一 id,返回 newText 和 contentMap
|
||||
export interface WriteToFileContentMap {
|
||||
[id: string]: {
|
||||
code: string;
|
||||
path: string;
|
||||
language: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function preprocessWriteToFile(text: string) {
|
||||
let contentIndex = 0;
|
||||
const contentMap: WriteToFileContentMap = {};
|
||||
// 替换所有 <content>...</content> 为 <content id="content-x"></content> 并存入 contentMap
|
||||
const newText = text.replace(
|
||||
/<content>([\s\S]*?)(<\/content>|$)/g,
|
||||
(match, code) => {
|
||||
const id = `content-${contentIndex++}`;
|
||||
// 尝试提取 path
|
||||
let path = '';
|
||||
let language = '';
|
||||
// 向前查找最近的 <path> 标签
|
||||
const pathMatch = text
|
||||
.slice(0, text.indexOf(match))
|
||||
.match(/<path>([\s\S]*?)<\/path>/);
|
||||
if (pathMatch) {
|
||||
path = pathMatch[1].trim();
|
||||
language = getBaseLanguageId(path.split('.').pop() || 'plaintext');
|
||||
}
|
||||
contentMap[id] = { code: code.trim(), path, language };
|
||||
return `<content id="${id}"></content>`;
|
||||
}
|
||||
);
|
||||
return { newText, contentMap };
|
||||
}
|
||||
|
||||
// 支持多组 diff 分隔符,容错处理
|
||||
function parseAndMergeDiffs(diffText: string) {
|
||||
const diffBlocks: { search: string; replace: string }[] = [];
|
||||
@@ -195,7 +225,9 @@ const MarkDown = ({
|
||||
|
||||
// 预处理 markdown,提取 diffMap
|
||||
const { newMd, diffMap } = preprocessMarkdown(content);
|
||||
const answer = processContent(newMd);
|
||||
const { newText, contentMap: writeToFileContentMap } =
|
||||
preprocessWriteToFile(newMd);
|
||||
const answerMd = processContent(newText);
|
||||
|
||||
if (content.length === 0) return null;
|
||||
|
||||
@@ -218,15 +250,23 @@ const MarkDown = ({
|
||||
tagNames: [
|
||||
...(defaultSchema.tagNames! as string[]),
|
||||
'command',
|
||||
'attemptcompletion',
|
||||
...toolTagNames,
|
||||
'diff',
|
||||
'suggest',
|
||||
'content',
|
||||
],
|
||||
},
|
||||
],
|
||||
]}
|
||||
components={
|
||||
{
|
||||
followup: (props: any) => {
|
||||
return <ul>{props.children}</ul>;
|
||||
},
|
||||
suggest: (props: any) => {
|
||||
return <li>{props.children}</li>;
|
||||
},
|
||||
|
||||
diff: (props: any) => {
|
||||
const { node } = props;
|
||||
// 去掉 user-content- 前缀
|
||||
@@ -294,20 +334,15 @@ const MarkDown = ({
|
||||
},
|
||||
command: ({ children }: React.HTMLAttributes<HTMLElement>) => {
|
||||
return (
|
||||
<SyntaxHighlighter
|
||||
<Code
|
||||
data={String(children).replace(/\n$/, '')}
|
||||
language={'shell'}
|
||||
style={github}
|
||||
onClick={() => {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(
|
||||
String(children).replace(/\n$/, '')
|
||||
);
|
||||
message.success('复制成功');
|
||||
}
|
||||
options={{
|
||||
lineNumbers: 'off',
|
||||
}}
|
||||
>
|
||||
{String(children)}
|
||||
</SyntaxHighlighter>
|
||||
autoHeight
|
||||
autoWidth
|
||||
/>
|
||||
);
|
||||
},
|
||||
attemptcompletion: (props: React.HTMLAttributes<HTMLElement>) => {
|
||||
@@ -326,6 +361,7 @@ const MarkDown = ({
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
code({
|
||||
children,
|
||||
className,
|
||||
@@ -333,22 +369,15 @@ const MarkDown = ({
|
||||
}: React.HTMLAttributes<HTMLElement>) {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
return match ? (
|
||||
<SyntaxHighlighter
|
||||
showLineNumbers
|
||||
{...rest}
|
||||
language={match[1] || 'bash'}
|
||||
style={anOldHope}
|
||||
onClick={() => {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(
|
||||
String(children).replace(/\n$/, '')
|
||||
);
|
||||
message.success('复制成功');
|
||||
}
|
||||
<Code
|
||||
data={String(children).replace(/\n$/, '')}
|
||||
language={match?.[1] || 'plaintext'}
|
||||
autoHeight
|
||||
autoWidth
|
||||
options={{
|
||||
lineNumbers: 'off',
|
||||
}}
|
||||
>
|
||||
{String(children).replace(/\n$/, '')}
|
||||
</SyntaxHighlighter>
|
||||
/>
|
||||
) : (
|
||||
<code
|
||||
{...rest}
|
||||
@@ -364,10 +393,26 @@ const MarkDown = ({
|
||||
</code>
|
||||
);
|
||||
},
|
||||
content: (props: any) => {
|
||||
const id = props.node?.properties?.id?.replace(
|
||||
/^user-content-/,
|
||||
''
|
||||
);
|
||||
const block = id ? writeToFileContentMap[id] : undefined;
|
||||
if (!block) return null;
|
||||
return (
|
||||
<Code
|
||||
data={block.code}
|
||||
language={block.language || 'text'}
|
||||
autoHeight
|
||||
autoWidth
|
||||
/>
|
||||
);
|
||||
},
|
||||
} as ExtendedComponents
|
||||
}
|
||||
>
|
||||
{answer}
|
||||
{answerMd}
|
||||
</ReactMarkdown>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -44,9 +44,7 @@ const Version = () => {
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
window.open(
|
||||
'https://pandawiki.docs.baizhi.cloud/node/01971615-05b8-7924-9af7-15f73784f893'
|
||||
);
|
||||
window.open('https://monkeycode.docs.baizhi.cloud/welcome');
|
||||
}}
|
||||
>
|
||||
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
|
||||
|
||||
@@ -26,9 +26,9 @@ const User = ({
|
||||
<Stack direction={'row'} alignItems={'center'} gap={1}>
|
||||
<Avatar
|
||||
name={username}
|
||||
sx={{ width: 22, height: 22, fontSize: 14 }}
|
||||
sx={{ width: 20, height: 20, fontSize: 12 }}
|
||||
/>
|
||||
<Typography>{username}</Typography>
|
||||
<Typography sx={{ pt: '2px' }}>{username}</Typography>
|
||||
</Stack>
|
||||
</Link>
|
||||
{email && (
|
||||
|
||||
@@ -190,7 +190,7 @@ const AdminTable = () => {
|
||||
title: '最近活跃时间',
|
||||
dataIndex: 'last_active_at',
|
||||
render: (text) => {
|
||||
return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
|
||||
return text === 0 ? '从未使用' : dayjs.unix(text).fromNow();
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -98,7 +98,12 @@ const ChatDetailModal = ({
|
||||
对话记录-{data?.user?.username}
|
||||
</Ellipsis>
|
||||
}
|
||||
width={1200}
|
||||
sx={{
|
||||
'.MuiDialog-paper': {
|
||||
maxWidth: 1300,
|
||||
},
|
||||
}}
|
||||
width={1300}
|
||||
open={open}
|
||||
onCancel={onClose}
|
||||
footer={null}
|
||||
|
||||
@@ -5,16 +5,16 @@ import MonacoEditor from '@monaco-editor/react';
|
||||
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { DomainCompletionRecord } from '@/api/types';
|
||||
import { getBaseLanguageId } from '@/utils';
|
||||
|
||||
function getBaseLanguageId(languageId: string): string {
|
||||
const map: Record<string, string> = {
|
||||
typescriptreact: 'typescript',
|
||||
javascriptreact: 'javascript',
|
||||
tailwindcss: 'css',
|
||||
'vue-html': 'vue',
|
||||
};
|
||||
return map[languageId] || languageId;
|
||||
}
|
||||
// 删除 <|im_start|> 和 <|im_end|> 及其间内容的工具函数
|
||||
const removeImBlocks = (text: string) => {
|
||||
// 匹配前后可能的换行符
|
||||
return text.replace(
|
||||
/(^[ \t]*\r?\n)?<\|im_start\|>[\s\S]*?<\|im_end\|>(\r?\n)?/g,
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
const ChatDetailModal = ({
|
||||
data,
|
||||
@@ -33,7 +33,8 @@ const ChatDetailModal = ({
|
||||
const getChatDetailModal = () => {
|
||||
if (!data) return;
|
||||
getCompletionInfo({ id: data.id! }).then((res) => {
|
||||
const rawPrompt = res.prompt || '';
|
||||
// 先去除 <|im_start|> 和 <|im_end|> 及其间内容
|
||||
const rawPrompt = removeImBlocks(res.prompt || '');
|
||||
const content = res.content || '';
|
||||
// 找到三个特殊标记的位置
|
||||
const prefixTag = '<|fim_prefix|>';
|
||||
|
||||
@@ -82,15 +82,16 @@ const Completion = () => {
|
||||
is_accept?: 'accepted' | 'unaccepted' | '';
|
||||
}) => {
|
||||
setLoading(true);
|
||||
const isAccept = params.is_accept || filterAccept;
|
||||
const res = await getListCompletionRecord({
|
||||
page: params.page || page,
|
||||
size: params.size || size,
|
||||
language: params.language || filterLang,
|
||||
author: params.author || filterUser,
|
||||
is_accept:
|
||||
params.is_accept === 'accepted'
|
||||
isAccept === 'accepted'
|
||||
? true
|
||||
: params.is_accept === 'unaccepted'
|
||||
: isAccept === 'unaccepted'
|
||||
? false
|
||||
: undefined,
|
||||
});
|
||||
|
||||
@@ -17,8 +17,8 @@ const ContributionModal = ({
|
||||
open={open}
|
||||
onCancel={onCancel}
|
||||
title='用户贡献榜'
|
||||
showCancel={false}
|
||||
okText='关闭'
|
||||
footer={false}
|
||||
onOk={onCancel}
|
||||
>
|
||||
<Box
|
||||
gap={0.5}
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
// import {
|
||||
// addModel,
|
||||
// getModelByProviderBrand,
|
||||
// ModelItem,
|
||||
// testModel,
|
||||
// updateModel,
|
||||
// } from '@/api';
|
||||
import {
|
||||
postCreateModel,
|
||||
putUpdateModel,
|
||||
@@ -14,9 +7,9 @@ import {
|
||||
import Card from '@/components/card';
|
||||
import { ModelProvider } from '../constant';
|
||||
import { Icon, message, Modal } from '@c-x/ui';
|
||||
import { StyledFormLabel } from '@/components/form';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
MenuItem,
|
||||
Stack,
|
||||
TextField,
|
||||
@@ -26,7 +19,6 @@ import {
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import {
|
||||
DomainModelBasic,
|
||||
ConstsModelType,
|
||||
DomainUpdateModelReq,
|
||||
DomainCreateModelReq,
|
||||
@@ -51,7 +43,6 @@ const ModelModal = ({
|
||||
modelType,
|
||||
}: AddModelProps) => {
|
||||
const theme = useTheme();
|
||||
const spaceId = 1;
|
||||
const {
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
@@ -64,10 +55,7 @@ const ModelModal = ({
|
||||
provider: data?.provider || 'DeepSeek',
|
||||
api_base: data?.api_base || ModelProvider.DeepSeek.defaultBaseUrl,
|
||||
model_name: data?.model_name || '',
|
||||
// api_version: data?.api_version || '',
|
||||
api_key: data?.api_key || '',
|
||||
// api_header_key: data?.api_header?.split('=')[0] || '',
|
||||
// api_header_value: data?.api_header?.split('=')[1] || '',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -78,34 +66,6 @@ const ModelModal = ({
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modelLoading, setModelLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
// const getModel = (value: AddModelForm) => {
|
||||
// let header = '';
|
||||
// if (value.api_header_key && value.api_header_value) {
|
||||
// header = value.api_header_key + '=' + value.api_header_value;
|
||||
// }
|
||||
// setModelLoading(true);
|
||||
// getModelByProviderBrand({
|
||||
// space_id: spaceId,
|
||||
// api_key: value.api_key,
|
||||
// base_url: value.base_url,
|
||||
// provider_brand: value.provider_brand,
|
||||
// api_header: header,
|
||||
// })
|
||||
// .then((res) => {
|
||||
// setModelUserList(res.models || []);
|
||||
// if (data && (res.models || []).find((it) => it.model === data.model)) {
|
||||
// setValue('model', data.model);
|
||||
// } else {
|
||||
// setValue('model', res.models?.[0]?.model || '');
|
||||
// }
|
||||
// setSuccess(true);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// setModelLoading(false);
|
||||
// });
|
||||
// };
|
||||
|
||||
const onCreateModel = (value: DomainCreateModelReq) => {
|
||||
return postCreateModel({
|
||||
@@ -131,10 +91,6 @@ const ModelModal = ({
|
||||
};
|
||||
|
||||
const onSubmit = (value: Required<DomainCreateModelReq>) => {
|
||||
const header = '';
|
||||
// if (value.api_header_key && value.api_header_value) {
|
||||
// header = value.api_header_key + '=' + value.api_header_value;
|
||||
// }
|
||||
setError('');
|
||||
setLoading(true);
|
||||
postCheckModel({
|
||||
@@ -162,26 +118,12 @@ const ModelModal = ({
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (data) {
|
||||
if (data.provider_brand && data.provider_brand !== 'Other') {
|
||||
// getModel({
|
||||
// api_key: data.api_key || '',
|
||||
// base_url: data.base_url || '',
|
||||
// model: data.model || '',
|
||||
// provider_brand: data.provider_brand || '',
|
||||
// api_version: data.api_version || '',
|
||||
// api_header_key: data.api_header?.split('=')[0] || '',
|
||||
// api_header_value: data.api_header?.split('=')[1] || '',
|
||||
// });
|
||||
}
|
||||
reset(
|
||||
{
|
||||
provider: data.provider || 'Other',
|
||||
model_name: data.model_name || '',
|
||||
api_base: data.api_base || '',
|
||||
api_key: data.api_key || '',
|
||||
// api_version: data.api_version || '',
|
||||
// api_header_key: data.api_header?.split('=')[0] || '',
|
||||
// api_header_value: data.api_header?.split('=')[1] || '',
|
||||
},
|
||||
{
|
||||
keepDefaultValues: true,
|
||||
@@ -213,10 +155,15 @@ const ModelModal = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (currentModelList.length > 0) {
|
||||
setValue('api_base', currentModelList[0].api_base || '');
|
||||
setValue('model_name', currentModelList[0].name || '');
|
||||
if (data) {
|
||||
setValue('api_base', data.api_base || '');
|
||||
setValue('model_name', data.model_name || '');
|
||||
} else {
|
||||
setValue('api_base', currentModelList[0].api_base || '');
|
||||
setValue('model_name', currentModelList[0].name || '');
|
||||
}
|
||||
}
|
||||
}, [currentModelList]);
|
||||
}, [currentModelList, data]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -226,7 +173,6 @@ const ModelModal = ({
|
||||
onCancel={() => {
|
||||
reset();
|
||||
setModelUserList([]);
|
||||
setSuccess(false);
|
||||
setLoading(false);
|
||||
setError('');
|
||||
onClose();
|
||||
@@ -277,17 +223,13 @@ const ModelModal = ({
|
||||
}}
|
||||
onClick={() => {
|
||||
if (data) return;
|
||||
// setModelUserList([]);
|
||||
setError('');
|
||||
reset(
|
||||
{
|
||||
provider: it.provider as keyof typeof ModelProvider,
|
||||
api_base: '',
|
||||
model_name: '',
|
||||
// api_version: '',
|
||||
api_key: '',
|
||||
// api_header_key: '',
|
||||
// api_header_value: '',
|
||||
},
|
||||
{
|
||||
keepDefaultValues: true,
|
||||
@@ -306,12 +248,7 @@ const ModelModal = ({
|
||||
))}
|
||||
</Stack>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px' }}>
|
||||
API 地址{' '}
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
*
|
||||
</Box>
|
||||
</Box>
|
||||
<StyledFormLabel required>API 地址</StyledFormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_base'
|
||||
@@ -344,15 +281,11 @@ const ModelModal = ({
|
||||
justifyContent={'space-between'}
|
||||
sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}
|
||||
>
|
||||
<Box>
|
||||
<StyledFormLabel
|
||||
required={ModelProvider[providerBrand].secretRequired}
|
||||
>
|
||||
API Secret
|
||||
{ModelProvider[providerBrand].secretRequired && (
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
{' '}
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</StyledFormLabel>
|
||||
{ModelProvider[providerBrand].modelDocumentUrl && (
|
||||
<Box
|
||||
component={'span'}
|
||||
@@ -396,131 +329,32 @@ const ModelModal = ({
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{/* {providerBrand === 'AzureOpenAI' && (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
API Version
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_version'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='2024-10-21'
|
||||
error={!!errors.api_version}
|
||||
helperText={errors.api_version?.message}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
setModelUserList([]);
|
||||
setValue('model', '');
|
||||
setSuccess(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)} */}
|
||||
{providerBrand === 'Other' ? (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
模型名称{' '}
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
*
|
||||
</Box>
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='model_name'
|
||||
rules={{
|
||||
required: '模型名称不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder=''
|
||||
error={!!errors.model_name}
|
||||
helperText={errors.model_name?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Box sx={{ fontSize: 12, color: 'error.main', mt: 1 }}>
|
||||
需要与模型供应商提供的名称完全一致,不要随便填写
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
模型名称{' '}
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
*
|
||||
</Box>
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='model_name'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
select
|
||||
size='small'
|
||||
placeholder=''
|
||||
error={!!errors.model_name}
|
||||
helperText={errors.model_name?.message}
|
||||
>
|
||||
{currentModelList.map((it) => (
|
||||
<MenuItem key={it.name} value={it.name}>
|
||||
{it.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
/>
|
||||
{/* {ModelProvider[providerBrand].customHeader && (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
Header
|
||||
</Box>
|
||||
<Stack direction={'row'} gap={1}>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_header_key'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='key'
|
||||
error={!!errors.api_header_key}
|
||||
helperText={errors.api_header_key?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '36px' }}>=</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_header_value'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='value'
|
||||
error={!!errors.api_header_value}
|
||||
helperText={errors.api_header_value?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
)} */}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<StyledFormLabel required>模型名称</StyledFormLabel>
|
||||
</Box>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name='model_name'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
select
|
||||
size='small'
|
||||
placeholder=''
|
||||
error={!!errors.model_name}
|
||||
helperText={errors.model_name?.message}
|
||||
>
|
||||
{currentModelList.map((it) => (
|
||||
<MenuItem key={it.name} value={it.name}>
|
||||
{it.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
/>
|
||||
{error && (
|
||||
<Card
|
||||
sx={{
|
||||
|
||||
@@ -246,3 +246,17 @@ export const getRecent24HoursData = (
|
||||
}
|
||||
return { xData, yData };
|
||||
};
|
||||
|
||||
export const getBaseLanguageId = (languageId: string): string => {
|
||||
const map: Record<string, string> = {
|
||||
typescriptreact: 'typescript',
|
||||
javascriptreact: 'javascript',
|
||||
tailwindcss: 'css',
|
||||
shellscript: 'shell',
|
||||
'vue-html': 'vue',
|
||||
tsx: 'typescript',
|
||||
jsx: 'javascript',
|
||||
py: 'python',
|
||||
};
|
||||
return map[languageId] || languageId;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user