智能会话和知识库
parent
0c72a94fed
commit
615976e315
@ -0,0 +1,50 @@
|
||||
// import StructuredData from '@/components/StructuredData';
|
||||
import { Locales } from '@/locales/resources';
|
||||
// import { ldModule } from '@/server/ld';
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
// import { DiscoverService } from '@/server/services/discover';
|
||||
import { translation } from '@/server/translation';
|
||||
// import { isMobileDevice } from '@/utils/responsive';
|
||||
import ApplicationSet from "./applicationset";
|
||||
|
||||
// import {Button} from "antd";
|
||||
|
||||
|
||||
type Props = { searchParams: { hl?: Locales } };
|
||||
|
||||
export const generateMetadata = async ({ searchParams }: Props) => {
|
||||
const { t, locale } = await translation('metadata', searchParams?.hl);
|
||||
return metadataModule.generate({
|
||||
alternate: true,
|
||||
description: t('files.description'),
|
||||
locale,
|
||||
title: t('files.title'),
|
||||
url: '/files',
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
// const { t, locale } = await translation('metadata', searchParams?.hl);
|
||||
// const mobile = isMobileDevice();
|
||||
|
||||
// const discoverService = new DiscoverService();
|
||||
// const items = await discoverService.getAssistantList(locale);
|
||||
|
||||
// const ld = ldModule.generate({
|
||||
// description: t('files.description'),
|
||||
// title: t('files.title'),
|
||||
// url: '/files',
|
||||
// webpage: {
|
||||
// enable: true,
|
||||
// search: '/files/search',
|
||||
// },
|
||||
// });
|
||||
|
||||
return (
|
||||
<ApplicationSet />
|
||||
);
|
||||
};
|
||||
|
||||
Page.DisplayName = 'DiscoverAssistants';
|
||||
|
||||
export default Page;
|
@ -0,0 +1,16 @@
|
||||
import { Markdown } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useContainerStyles } from '../style';
|
||||
|
||||
const Preview = memo<{ content: string }>(({ content }) => {
|
||||
const { styles } = useContainerStyles();
|
||||
|
||||
return (
|
||||
<div className={styles.preview} style={{ padding: 12 }}>
|
||||
<Markdown>{content}</Markdown>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Preview;
|
@ -0,0 +1,102 @@
|
||||
// import { Form, type FormItemProps, Icon, copyToClipboard } from '@lobehub/ui';
|
||||
import { Button } from 'antd';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
// import { CopyIcon } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
// import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { useIsMobile } from '@/hooks/useIsMobile';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentSelectors } from '@/store/agent/selectors';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { chatSelectors, topicSelectors } from '@/store/chat/selectors';
|
||||
import { FieldType } from './type';
|
||||
import { LOADING_FLAT } from '@/const/message';
|
||||
import { stringify as csvStringify } from 'csv-stringify';
|
||||
|
||||
const DEFAULT_FIELD_VALUE: FieldType = {
|
||||
includeTool: true,
|
||||
includeUser: true,
|
||||
withRole: true,
|
||||
withSystemRole: false,
|
||||
};
|
||||
|
||||
const ShareText = memo(() => {
|
||||
// const [fieldValue, setFieldValue] = useState(DEFAULT_FIELD_VALUE);
|
||||
const { t } = useTranslation(['chat', 'common']);
|
||||
|
||||
// const { message } = App.useApp();
|
||||
|
||||
const [systemRole] = useAgentStore((s) => [agentSelectors.currentAgentSystemRole(s)]);
|
||||
const messages = useChatStore(chatSelectors.currentChats, isEqual);
|
||||
const topic = useChatStore(topicSelectors.currentActiveTopic, isEqual);
|
||||
console.log(systemRole,messages,'83737373737')
|
||||
let messagesNew = messages
|
||||
.filter((m) => m.content !== LOADING_FLAT)
|
||||
.filter((m) => (!DEFAULT_FIELD_VALUE.includeUser ? m.role !== 'user' : true))
|
||||
.filter((m) => (!DEFAULT_FIELD_VALUE.includeTool ? m.role !== 'tool' : true))
|
||||
|
||||
console.log(messagesNew,'111111111111111')
|
||||
const topicTitle = topic?.title || t('shareModal.exportTitle');
|
||||
console.log(topicTitle,'22222222222222222')
|
||||
const isMobile = useIsMobile();
|
||||
const downloadCSV = () => {
|
||||
const data = [
|
||||
["question", "answer"],
|
||||
];
|
||||
let arrItem = [];
|
||||
messagesNew.forEach((chat)=> {
|
||||
if (chat.role === 'assistant') {
|
||||
arrItem.push(chat.content)
|
||||
} else if(chat.role === 'user') {
|
||||
arrItem.unshift(chat.content)
|
||||
}
|
||||
if(arrItem.length >= 2) {
|
||||
data.push(arrItem)
|
||||
arrItem = [];
|
||||
}
|
||||
})
|
||||
console.log(data,'9999111111')
|
||||
// 将数据转换为CSV字符串
|
||||
let csvContent = "";
|
||||
csvStringify(data, (err, output) => {
|
||||
if (err) {
|
||||
console.error('Error generating CSV:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
// 输出 CSV 字符串
|
||||
console.log(output,'66666666666666');
|
||||
csvContent = output
|
||||
|
||||
// 创建一个Blob对象
|
||||
const blob = new Blob([csvContent], { type: 'text/csv' });
|
||||
|
||||
// 创建一个URL并指向Blob对象
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// 创建一个隐藏的<a>标签并点击它以触发下载
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('hidden', '');
|
||||
a.setAttribute('href', url);
|
||||
a.setAttribute('download', topicTitle + '.csv');
|
||||
document.body.append(a);
|
||||
a.click();
|
||||
|
||||
// 移除<a>标签
|
||||
a.remove();
|
||||
|
||||
// 释放URL对象
|
||||
URL.revokeObjectURL(url);
|
||||
});
|
||||
}
|
||||
return (
|
||||
<Flexbox gap={16} horizontal={!isMobile}>
|
||||
<Button onClick={() => downloadCSV()} shape="round" style={{borderColor: '#2E62FF', color: "#2E62FF",fontSize: '13px',height: '30px'}}>会话导出</Button>
|
||||
</Flexbox>
|
||||
);
|
||||
});
|
||||
|
||||
export default ShareText;
|
@ -0,0 +1,178 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { LOADING_FLAT } from '@/const/message';
|
||||
import { ChatMessage } from '@/types/message';
|
||||
|
||||
import { generateMarkdown } from './template';
|
||||
|
||||
describe('generateMarkdown', () => {
|
||||
// 创建测试用的消息数据
|
||||
const mockMessages = [
|
||||
{
|
||||
id: '1',
|
||||
content: 'Hello',
|
||||
role: 'user',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
content: 'Hi there',
|
||||
role: 'assistant',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
content: LOADING_FLAT,
|
||||
role: 'assistant',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
content: '{"result": "tool data"}',
|
||||
role: 'tool',
|
||||
createdAt: Date.now(),
|
||||
tool_call_id: 'tool1',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
content: 'Message with tools',
|
||||
role: 'assistant',
|
||||
createdAt: Date.now(),
|
||||
tools: [{ name: 'calculator', result: '42' }],
|
||||
},
|
||||
] as ChatMessage[];
|
||||
|
||||
const defaultParams = {
|
||||
messages: mockMessages,
|
||||
title: 'Chat Title',
|
||||
includeTool: false,
|
||||
includeUser: true,
|
||||
withSystemRole: false,
|
||||
withRole: false,
|
||||
systemRole: '',
|
||||
};
|
||||
|
||||
it('should generate basic markdown with title', () => {
|
||||
const result = generateMarkdown(defaultParams);
|
||||
|
||||
expect(result).toContain('# Chat Title');
|
||||
expect(result).toContain('Hello');
|
||||
expect(result).toContain('Hi there');
|
||||
});
|
||||
|
||||
it('should include system role when withSystemRole is true', () => {
|
||||
const systemRole = 'I am a helpful assistant';
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
withSystemRole: true,
|
||||
systemRole,
|
||||
});
|
||||
|
||||
expect(result).toContain('````md\nI am a helpful assistant\n````');
|
||||
});
|
||||
|
||||
it('should not include system role when withSystemRole is false', () => {
|
||||
const systemRole = 'I am a helpful assistant';
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
withSystemRole: false,
|
||||
systemRole,
|
||||
});
|
||||
|
||||
expect(result).not.toContain('```\nI am a helpful assistant\n```');
|
||||
});
|
||||
|
||||
it('should add role labels when withRole is true', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
withRole: true,
|
||||
});
|
||||
|
||||
expect(result).toContain('##### User:');
|
||||
expect(result).toContain('##### Assistant:');
|
||||
});
|
||||
|
||||
it('should not add role labels when withRole is false', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
withRole: false,
|
||||
});
|
||||
|
||||
expect(result).not.toContain('##### User:');
|
||||
expect(result).not.toContain('##### Assistant:');
|
||||
});
|
||||
|
||||
it('should include tool messages when includeTool is true', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
includeTool: true,
|
||||
withRole: true,
|
||||
});
|
||||
|
||||
expect(result).toContain('##### Tools Calling:');
|
||||
expect(result).toContain('```json\n{"result": "tool data"}\n```');
|
||||
});
|
||||
|
||||
it('should exclude tool messages when includeTool is false', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
includeTool: false,
|
||||
});
|
||||
|
||||
expect(result).not.toContain('{"result": "tool data"}');
|
||||
});
|
||||
|
||||
it('should exclude user messages when includeUser is false', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
includeUser: false,
|
||||
});
|
||||
|
||||
expect(result).not.toContain('Hello');
|
||||
expect(result).toContain('Hi there');
|
||||
});
|
||||
|
||||
it('should filter out loading messages', () => {
|
||||
const result = generateMarkdown(defaultParams);
|
||||
|
||||
expect(result).not.toContain(LOADING_FLAT);
|
||||
});
|
||||
|
||||
it('should include tools data when includeTool is true', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
includeTool: true,
|
||||
});
|
||||
|
||||
expect(result).toContain('"name": "calculator"');
|
||||
expect(result).toContain('"result": "42"');
|
||||
});
|
||||
|
||||
it('should handle empty messages array', () => {
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
messages: [],
|
||||
});
|
||||
|
||||
expect(result).toContain('# Chat Title');
|
||||
// Should not throw error and should contain at least the title
|
||||
});
|
||||
|
||||
it('should handle messages with special characters', () => {
|
||||
const messagesWithSpecialChars = [
|
||||
{
|
||||
id: '1',
|
||||
content: '**Bold** *Italic* `Code`',
|
||||
role: 'user',
|
||||
createdAt: Date.now(),
|
||||
},
|
||||
] as ChatMessage[];
|
||||
|
||||
const result = generateMarkdown({
|
||||
...defaultParams,
|
||||
messages: messagesWithSpecialChars,
|
||||
});
|
||||
|
||||
expect(result).toContain('**Bold** *Italic* `Code`');
|
||||
});
|
||||
});
|
@ -0,0 +1,79 @@
|
||||
import { template } from 'lodash-es';
|
||||
|
||||
import { LOADING_FLAT } from '@/const/message';
|
||||
import { FieldType } from '@/features/ShareModal/ShareText/type';
|
||||
import { ChatMessage } from '@/types/message';
|
||||
|
||||
const markdownTemplate = template(
|
||||
`# {{title}}
|
||||
|
||||
<% if (systemRole) { %>
|
||||
\`\`\`\`md
|
||||
{{systemRole}}
|
||||
\`\`\`\`
|
||||
<% } %>
|
||||
|
||||
<% messages.forEach(function(chat) { %>
|
||||
|
||||
<% if (withRole) { %>
|
||||
|
||||
<% if (chat.role === 'user') { %>
|
||||
##### User:
|
||||
<% } else if (chat.role === 'assistant') { %>
|
||||
##### Assistant:
|
||||
<% } else if (chat.role === 'tool') { %>
|
||||
##### Tools Calling:
|
||||
<% } %>
|
||||
|
||||
<% } %>
|
||||
|
||||
<% if (chat.role === 'tool') { %>
|
||||
\`\`\`json
|
||||
{{chat.content}}
|
||||
\`\`\`
|
||||
<% } else { %>
|
||||
|
||||
{{chat.content}}
|
||||
|
||||
<% if (includeTool && chat.tools) { %>
|
||||
|
||||
\`\`\`json
|
||||
{{JSON.stringify(chat.tools, null, 2)}}
|
||||
\`\`\`
|
||||
|
||||
<% } %>
|
||||
<% } %>
|
||||
|
||||
<% }); %>
|
||||
`,
|
||||
{
|
||||
evaluate: /<%([\S\s]+?)%>/g,
|
||||
interpolate: /{{([\S\s]+?)}}/g,
|
||||
},
|
||||
);
|
||||
|
||||
interface MarkdownParams extends FieldType {
|
||||
messages: ChatMessage[];
|
||||
systemRole: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const generateMarkdown = ({
|
||||
messages,
|
||||
title,
|
||||
includeTool,
|
||||
includeUser,
|
||||
withSystemRole,
|
||||
withRole,
|
||||
systemRole,
|
||||
}: MarkdownParams) =>
|
||||
markdownTemplate({
|
||||
includeTool,
|
||||
messages: messages
|
||||
.filter((m) => m.content !== LOADING_FLAT)
|
||||
.filter((m) => (!includeUser ? m.role !== 'user' : true))
|
||||
.filter((m) => (!includeTool ? m.role !== 'tool' : true)),
|
||||
systemRole: withSystemRole ? systemRole : undefined,
|
||||
title,
|
||||
withRole,
|
||||
});
|
@ -0,0 +1,6 @@
|
||||
export type FieldType = {
|
||||
includeTool: boolean;
|
||||
includeUser: boolean;
|
||||
withRole: boolean;
|
||||
withSystemRole: boolean;
|
||||
};
|
@ -1,52 +0,0 @@
|
||||
import StructuredData from '@/components/StructuredData';
|
||||
import { Locales } from '@/locales/resources';
|
||||
import { ldModule } from '@/server/ld';
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { DiscoverService } from '@/server/services/discover';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/responsive';
|
||||
import Filescon from "./filescon";
|
||||
|
||||
import {Button} from "antd";
|
||||
|
||||
|
||||
type Props = { searchParams: { hl?: Locales } };
|
||||
|
||||
export const generateMetadata = async ({ searchParams }: Props) => {
|
||||
const { t, locale } = await translation('metadata', searchParams?.hl);
|
||||
return metadataModule.generate({
|
||||
alternate: true,
|
||||
description: t('files.description'),
|
||||
locale,
|
||||
title: t('files.title'),
|
||||
url: '/files',
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async ({ searchParams }: Props) => {
|
||||
const { t, locale } = await translation('metadata', searchParams?.hl);
|
||||
const mobile = isMobileDevice();
|
||||
|
||||
const discoverService = new DiscoverService();
|
||||
const items = await discoverService.getAssistantList(locale);
|
||||
|
||||
const ld = ldModule.generate({
|
||||
description: t('files.description'),
|
||||
title: t('files.title'),
|
||||
url: '/files',
|
||||
webpage: {
|
||||
enable: true,
|
||||
search: '/files/search',
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Filescon />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Page.DisplayName = 'DiscoverAssistants';
|
||||
|
||||
export default Page;
|
@ -0,0 +1,76 @@
|
||||
import { Col, Form, Input, Modal, Row } from 'antd';
|
||||
import React from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const FormItem = Form.Item;
|
||||
const useStyles = createStyles(({ css }) => ({
|
||||
formStyle: css`
|
||||
.ant-form-item-label {
|
||||
width: 100px !important;
|
||||
}
|
||||
`,
|
||||
modalStyle: css`
|
||||
.ant-modal-body {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
.ant-btn:nth-child(1) {
|
||||
width: 73px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
background: #EBF4FF;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #B8D8FF;
|
||||
color: #177FFF;
|
||||
}
|
||||
.ant-btn:nth-child(2) {
|
||||
width: 73px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
|
||||
background: #80B9FF;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
||||
const KnowledgeCreateForm = (props) => {
|
||||
const { styles } = useStyles();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { open, handleAdd, handleModalVisible } = props;
|
||||
|
||||
const okHandle = () => {
|
||||
form.validateFields()
|
||||
.then((fieldsValue) => {
|
||||
form.resetFields();
|
||||
handleAdd(fieldsValue);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal className={styles.modalStyle} destroyOnClose onCancel={() => handleModalVisible(false)} onOk={okHandle} open={open} title="创建知识库" >
|
||||
<Form className={styles.formStyle} form={form}>
|
||||
<Row gutter={{ lg: 24, md: 8, xl: 48 }}>
|
||||
<Col md={24} sm={24}>
|
||||
<FormItem label="知识库名称" name="name" rules={[{ message: '请输入!', min: 1, required: true}]}>
|
||||
<Input placeholder="请输入" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={{ lg: 24, md: 8, xl: 48 }}>
|
||||
<Col md={24} sm={24}>
|
||||
<FormItem label="知识库描述" name="description" rules={[{ message: '请输入!', min: 1 , required: false}]}>
|
||||
<TextArea maxLength={255} placeholder="请输入" rows={4} />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeCreateForm;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,83 @@
|
||||
import { Col, Form, Input, Modal, Row } from 'antd';
|
||||
import React from 'react';
|
||||
import { createStyles } from 'antd-style';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const FormItem = Form.Item;
|
||||
const useStyles = createStyles(({ css }) => ({
|
||||
formStyle: css`
|
||||
.ant-form-item-label {
|
||||
width: 100px !important;
|
||||
}
|
||||
`,
|
||||
modalStyle: css`
|
||||
.ant-modal-body {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.ant-modal-footer {
|
||||
.ant-btn:nth-child(1) {
|
||||
width: 73px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
background: #EBF4FF;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #B8D8FF;
|
||||
color: #177FFF;
|
||||
}
|
||||
.ant-btn:nth-child(2) {
|
||||
width: 73px;
|
||||
height: 36px;
|
||||
border-radius: 4px;
|
||||
opacity: 1;
|
||||
|
||||
background: #80B9FF;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
||||
const KnowledgeUpdateForm = (props) => {
|
||||
const { styles } = useStyles();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { handleUpdate, open, handleUpdateModalVisible, values } = props;
|
||||
|
||||
React.useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
description: props.values.description,
|
||||
id: props.values.id,
|
||||
title: props.values.title,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleLocalUpdate = () => {
|
||||
form.validateFields()
|
||||
.then((fieldsValue) => {
|
||||
handleUpdate({ ...values, ...fieldsValue });
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal afterClose={() => handleUpdateModalVisible()} className={styles.modalStyle} destroyOnClose onCancel={() => handleUpdateModalVisible(false, values)} onOk={() => handleLocalUpdate()} open={open} title="编辑知识库">
|
||||
<Form className={styles.formStyle} form={form}>
|
||||
<Row gutter={{ lg: 24, md: 8, xl: 48 }}>
|
||||
<Col md={24} sm={24}>
|
||||
<FormItem label="知识库名称" name="title" rules={[{ message: '请输入!', min: 1, required: true }]}>
|
||||
<Input placeholder="请输入" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={{ lg: 24, md: 8,xl: 48 }}>
|
||||
<Col md={24} sm={24}>
|
||||
<FormItem label="知识库描述" name="description" rules={[{ message: '请输入!', min: 1, required: false }]}>
|
||||
<TextArea maxLength={255} placeholder="请输入" rows={4} />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default KnowledgeUpdateForm;
|
@ -1,38 +0,0 @@
|
||||
import { WelcomeLogo } from '@/components/Branding';
|
||||
import StructuredData from '@/components/StructuredData';
|
||||
import { BRANDING_NAME } from '@/const/branding';
|
||||
import { ldModule } from '@/server/ld';
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/responsive';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const { t } = await translation('metadata');
|
||||
return metadataModule.generate({
|
||||
description: t('model.description', { appName: BRANDING_NAME }),
|
||||
title: t('model.title', { appName: BRANDING_NAME }),
|
||||
url: '/model',
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
const mobile = isMobileDevice();
|
||||
const { t } = await translation('metadata');
|
||||
const ld = ldModule.generate({
|
||||
description: t('model.description', { appName: BRANDING_NAME }),
|
||||
title: t('model.title', { appName: BRANDING_NAME }),
|
||||
url: '/model',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*<StructuredData ld={ld} />*/}
|
||||
{/*<WelcomeLogo mobile={mobile} />*/}
|
||||
<div>模型</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Page.displayName = 'Model';
|
||||
|
||||
export default Page;
|
@ -1,38 +0,0 @@
|
||||
import { WelcomeLogo } from '@/components/Branding';
|
||||
import StructuredData from '@/components/StructuredData';
|
||||
import { BRANDING_NAME } from '@/const/branding';
|
||||
import { ldModule } from '@/server/ld';
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { translation } from '@/server/translation';
|
||||
import { isMobileDevice } from '@/utils/responsive';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const { t } = await translation('metadata');
|
||||
return metadataModule.generate({
|
||||
description: t('plugins.description', { appName: BRANDING_NAME }),
|
||||
title: t('plugins.title', { appName: BRANDING_NAME }),
|
||||
url: '/plugins',
|
||||
});
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
const mobile = isMobileDevice();
|
||||
const { t } = await translation('metadata');
|
||||
const ld = ldModule.generate({
|
||||
description: t('plugins.description', { appName: BRANDING_NAME }),
|
||||
title: t('plugins.title', { appName: BRANDING_NAME }),
|
||||
url: '/plugins',
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{/*<StructuredData ld={ld} />*/}
|
||||
{/*<WelcomeLogo mobile={mobile} />*/}
|
||||
<div>插件</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Page.displayName = 'Robot';
|
||||
|
||||
export default Page;
|
@ -1,32 +1,144 @@
|
||||
'use client';
|
||||
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { SiDiscord, SiGithub, SiMedium, SiRss, SiX } from '@icons-pack/react-simple-icons';
|
||||
import { Form } from '@lobehub/ui';
|
||||
import { Divider } from 'antd';
|
||||
import { createStyles } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
import { INBOX_SESSION_ID } from '@/const/session';
|
||||
import { AgentSettingAbout } from '@/features/AgentSetting';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { settingsSelectors } from '@/store/user/selectors';
|
||||
import { BRANDING_NAME } from '@/const/branding';
|
||||
import {
|
||||
BLOG,
|
||||
DISCORD,
|
||||
EMAIL_BUSINESS,
|
||||
EMAIL_SUPPORT,
|
||||
GITHUB,
|
||||
MEDIDUM,
|
||||
OFFICIAL_SITE,
|
||||
PRIVACY_URL,
|
||||
TERMS_URL,
|
||||
X,
|
||||
mailTo,
|
||||
} from '@/const/url';
|
||||
import { useServerConfigStore } from '@/store/serverConfig';
|
||||
import { serverConfigSelectors } from '@/store/serverConfig/selectors';
|
||||
|
||||
const Page = memo(() => {
|
||||
const config = useUserStore(settingsSelectors.defaultAgentConfig, isEqual);
|
||||
const meta = useUserStore(settingsSelectors.defaultAgentMeta, isEqual);
|
||||
const [updateAgent] = useUserStore((s) => [s.updateDefaultAgent]);
|
||||
import AboutList from './features/AboutList';
|
||||
import Analytics from './features/Analytics';
|
||||
import ItemCard from './features/ItemCard';
|
||||
import ItemLink from './features/ItemLink';
|
||||
import Version from './features/Version';
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
title: css`
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: ${token.colorTextSecondary};
|
||||
`,
|
||||
}));
|
||||
|
||||
const Page = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
const { t } = useTranslation('common');
|
||||
const { styles } = useStyles();
|
||||
const enabledTelemetryChat = useServerConfigStore(serverConfigSelectors.enabledTelemetryChat);
|
||||
|
||||
return (
|
||||
<AgentSettingAbout
|
||||
config={config}
|
||||
id={INBOX_SESSION_ID}
|
||||
meta={meta}
|
||||
onConfigChange={(config) => {
|
||||
updateAgent({ config });
|
||||
}}
|
||||
onMetaChange={(meta) => {
|
||||
updateAgent({ meta });
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<Form.Group
|
||||
style={{ width: '100%' }}
|
||||
title={`${t('about')} ${BRANDING_NAME}`}
|
||||
variant={'pure'}
|
||||
>
|
||||
<Flexbox gap={20} paddingBlock={20} width={'100%'}>
|
||||
<div className={styles.title}>{t('version')}</div>
|
||||
<Version mobile={mobile} />
|
||||
<Divider style={{ marginBlock: 0 }} />
|
||||
<div className={styles.title}>{t('contact')}</div>
|
||||
<AboutList
|
||||
ItemRender={ItemLink}
|
||||
items={[
|
||||
{
|
||||
href: OFFICIAL_SITE,
|
||||
label: t('officialSite'),
|
||||
value: 'officialSite',
|
||||
},
|
||||
{
|
||||
href: mailTo(EMAIL_SUPPORT),
|
||||
label: t('mail.support'),
|
||||
value: 'support',
|
||||
},
|
||||
{
|
||||
href: mailTo(EMAIL_BUSINESS),
|
||||
label: t('mail.business'),
|
||||
value: 'business',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Divider style={{ marginBlock: 0 }} />
|
||||
<div className={styles.title}>{t('information')}</div>
|
||||
<AboutList
|
||||
ItemRender={ItemCard}
|
||||
grid
|
||||
items={[
|
||||
{
|
||||
href: BLOG,
|
||||
icon: SiRss,
|
||||
label: t('blog'),
|
||||
value: 'blog',
|
||||
},
|
||||
{
|
||||
href: GITHUB,
|
||||
icon: SiGithub,
|
||||
label: 'GitHub',
|
||||
value: 'feedback',
|
||||
},
|
||||
{
|
||||
href: DISCORD,
|
||||
icon: SiDiscord,
|
||||
label: 'Discord',
|
||||
value: 'discord',
|
||||
},
|
||||
{
|
||||
href: X,
|
||||
icon: SiX as any,
|
||||
label: 'X / Twitter',
|
||||
value: 'x',
|
||||
},
|
||||
|
||||
{
|
||||
href: MEDIDUM,
|
||||
icon: SiMedium,
|
||||
label: 'Medium',
|
||||
value: 'medium',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Divider style={{ marginBlock: 0 }} />
|
||||
<div className={styles.title}>{t('legal')}</div>
|
||||
<AboutList
|
||||
ItemRender={ItemLink}
|
||||
items={[
|
||||
{
|
||||
href: TERMS_URL,
|
||||
label: t('terms'),
|
||||
value: 'terms',
|
||||
},
|
||||
{
|
||||
href: PRIVACY_URL,
|
||||
label: t('privacy'),
|
||||
value: 'privacy',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Flexbox>
|
||||
</Form.Group>
|
||||
{enabledTelemetryChat && <Analytics />}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
Page.displayName = 'AboutSetting';
|
||||
|
||||
export default Page;
|
||||
|
@ -1,317 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import {memo, useEffect, useState} from 'react';
|
||||
import {Form, Select, Radio, Slider, InputNumber, Image} from "antd";
|
||||
import {createStyles} from "antd-style";
|
||||
|
||||
const ysOptions = [
|
||||
{id: '1', name: '男声', icon: '/images/per.png/'},
|
||||
{id: '2', name: '男声', icon: '/images/per.png/'},
|
||||
{id: '3', name: '男声', icon: '/images/per.png/'},
|
||||
{id: '4', name: '男声', icon: '/images/per.png/'},
|
||||
{id: '5', name: '男声', icon: '/images/per.png/'},
|
||||
{id: '6', name: '男声', icon: '/images/per.png/'},
|
||||
{id: '7', name: '男声', icon: '/images/per.png/'},
|
||||
]
|
||||
|
||||
const useStyles = createStyles(({ css, token }) => ({
|
||||
cricle: css`
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 8px;
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
background-color: #4F4F4F;
|
||||
`,
|
||||
onlyDiv: css`
|
||||
padding: 15px 40px;
|
||||
`,
|
||||
leftText: css`
|
||||
width: 200px;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
display: inline-block
|
||||
`,
|
||||
leftDir: css`
|
||||
color: #999999;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
`,
|
||||
rightText: css`
|
||||
width: calc(100% - 215px);
|
||||
text-align: right;
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
`,
|
||||
childrenLeftText: css`
|
||||
margin-left: 50px;
|
||||
`,
|
||||
childrenRightText: css`
|
||||
width: calc(100% - 265px);
|
||||
`,
|
||||
childrenlangText: css`
|
||||
margin-left: 50px;
|
||||
width: calc(100% - 65px);
|
||||
text-align: left;
|
||||
`,
|
||||
circleImg: css`
|
||||
width: 62px;
|
||||
height: 62px;
|
||||
line-height: 62px;
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
border-radius: 62px;
|
||||
background-color: #B8C8FF;
|
||||
border: 1px solid #B8C8FF;
|
||||
`,
|
||||
optName: css`
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
line-height: normal;
|
||||
font-size: 13px;
|
||||
background: #C1CFFF;
|
||||
border-radius: 18px;
|
||||
color: #3A61CB;
|
||||
bottom: -5px;
|
||||
left: 10px;
|
||||
padding: 2px 6px;
|
||||
`,
|
||||
circleSeleImg: css`
|
||||
border: 1px solid #0044FF;
|
||||
`,
|
||||
optSeleName: css`
|
||||
background: #0044FF;
|
||||
color: #fff;
|
||||
`,
|
||||
radioRigh: css`
|
||||
margin-right: 24px
|
||||
`,
|
||||
topTitle: css`
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
`,
|
||||
}))
|
||||
|
||||
const szrOptions = [
|
||||
{
|
||||
value: '1',
|
||||
label: 'Jack',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: 'Lucy',
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: 'yiminghe',
|
||||
},
|
||||
]
|
||||
|
||||
const yyOptions = [
|
||||
{
|
||||
label: '普通话',
|
||||
value: 'pth',
|
||||
},
|
||||
{
|
||||
label: '英文',
|
||||
value: 'yw',
|
||||
},
|
||||
{
|
||||
label: '法语',
|
||||
value: 'fy',
|
||||
},
|
||||
]
|
||||
|
||||
const Page = memo(() => {
|
||||
const [form] = Form.useForm()
|
||||
const { styles, cx } = useStyles()
|
||||
const [optValue, setOptValue] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
szr: '1',
|
||||
yy: 'pth',
|
||||
ys: '1.0',
|
||||
ysnum: '1.0',
|
||||
yd: '1.0',
|
||||
ydnum: '1.0',
|
||||
yl: '1.0',
|
||||
ylnum: '1.0',
|
||||
})
|
||||
})
|
||||
|
||||
const onChangeYs = (val) => {
|
||||
form.setFieldsValue({
|
||||
ysnum: val
|
||||
})
|
||||
}
|
||||
|
||||
const onChangeYd = (val) => {
|
||||
form.setFieldsValue({
|
||||
ydnum: val
|
||||
})
|
||||
}
|
||||
|
||||
const onChangeYl = (val) => {
|
||||
form.setFieldsValue({
|
||||
ylnum: val
|
||||
})
|
||||
}
|
||||
|
||||
const onClickYin = (e) => {
|
||||
setOptValue(e.id)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form form={form} style={{ marginBottom: '30px' }}>
|
||||
<div className={cx(styles.onlyDiv, styles.topTitle)}>
|
||||
<div className={cx(styles.leftText)}>数字人形象</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>选择数字人</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
<Form.Item label="" name="szr">
|
||||
<Select
|
||||
style={{ width: 120, textAlign: 'left' }}
|
||||
options={szrOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>数字人形象</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.rightText, styles.childrenlangText)} style={{ textAlgin: 'center' }}>
|
||||
<Image alt={'数字人形象'} src="/images/szrxx.png" preview={false} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv, styles.topTitle)}>
|
||||
<div className={cx(styles.leftText)}>声音设置</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>语言</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
<Form.Item label="" name="yy">
|
||||
<Select
|
||||
style={{ width: 120, textAlign: 'left' }}
|
||||
options={yyOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>风格</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
<Form.Item label="" name="fg">
|
||||
<Radio.Group disabled buttonStyle="solid">
|
||||
<Radio.Button className={styles.radioRigh} value="mr">默认</Radio.Button>
|
||||
<Radio.Button className={styles.radioRigh} value="xw">新闻</Radio.Button>
|
||||
<Radio.Button className={styles.radioRigh} value="xx">小说</Radio.Button>
|
||||
<Radio.Button className={styles.radioRigh} value="zs">知识</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>语速</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
<Form.Item style={{ display: 'inline-block'}} label="" name="ys">
|
||||
<Slider
|
||||
style={{ width: "120px" }}
|
||||
min={0}
|
||||
max={2}
|
||||
onChange={(e) => onChangeYs(e)}
|
||||
step={0.1}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item style={{ display: 'inline-block'}} label="" name="ysnum">
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={2}
|
||||
style={{
|
||||
margin: '0 16px',
|
||||
}}
|
||||
step={0.1}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>语调</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
<Form.Item style={{ display: 'inline-block'}} label="" name="yd">
|
||||
<Slider
|
||||
style={{ width: "120px" }}
|
||||
min={0}
|
||||
max={2}
|
||||
onChange={(e) => onChangeYd(e)}
|
||||
step={0.1}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item style={{ display: 'inline-block'}} label="" name="ydnum">
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={2}
|
||||
style={{
|
||||
margin: '0 16px',
|
||||
}}
|
||||
step={0.1}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>音量</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
<Form.Item style={{ display: 'inline-block'}} label="" name="yl">
|
||||
<Slider
|
||||
style={{ width: "120px" }}
|
||||
min={0}
|
||||
max={2}
|
||||
onChange={(e) => onChangeYl(e)}
|
||||
step={0.1}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item style={{ display: 'inline-block'}} label="" name="ylnum">
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={2}
|
||||
style={{
|
||||
margin: '0 16px',
|
||||
}}
|
||||
step={0.1}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(styles.onlyDiv)}>
|
||||
<div className={cx(styles.leftText)}>音色</div>
|
||||
<div className={cx(styles.rightText)}>
|
||||
{
|
||||
ysOptions.map(e => {
|
||||
return (
|
||||
<div className={cx(styles.circleImg, optValue==e.id&&styles.circleSeleImg)} onClick={() => onClickYin(e)}>
|
||||
<Image alt={e.name} src={e.icon} preview={false} />
|
||||
<span className={cx(styles.optName, optValue==e.id&&styles.optSeleName)}>{e.name}</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
Page.displayName = 'SzrSetting';
|
||||
|
||||
export default Page;
|
@ -1,12 +0,0 @@
|
||||
import { metadataModule } from '@/server/metadata';
|
||||
import { translation } from '@/server/translation';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const { t } = await translation('setting');
|
||||
return metadataModule.generate({
|
||||
description: t('header.desc'),
|
||||
title: t('tab.szr'),
|
||||
url: '/settings/szr',
|
||||
});
|
||||
};
|
||||
export { default } from './index';
|
@ -1,3 +1,3 @@
|
||||
export { AgentSettings, AgentSettingChat, AgentSettingLlm, AgentSettingTts, AgentSettingAbout } from './AgentSettings';
|
||||
export { AgentSettings } from './AgentSettings';
|
||||
export { AgentSettingsStore } from './AgentSettingsStore';
|
||||
export type { AgentSettingsInstance } from './hooks/useAgentSettings';
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@
|
||||
[2025-02-12 11:45:49.403] [INFO ] [kground-preinit] [ org.hibernate.validator.internal.util.Version:21] : HV000001: Hibernate Validator 6.2.5.Final
|
||||
[2025-02-12 11:45:49.424] [INFO ] [ main] [ com.pjilisense.flxai.FlxAiApplication:55] : Starting FlxAiApplication using Java 17.0.12 on DESKTOP-O4CA5AM with PID 20716 (D:\AInew\JZ_QGNB\flx-ai\target\classes started by sunboy in D:\AInew\JZ_QGNB\flx-ai)
|
||||
[2025-02-12 11:45:49.426] [INFO ] [ main] [ com.pjilisense.flxai.FlxAiApplication:637] : The following 1 profile is active: "dev"
|
||||
[2025-02-12 11:45:52.746] [INFO ] [ main] [ o.s.d.r.config.RepositoryConfigurationDelegate:262] : Multiple Spring Data modules found, entering strict repository configuration mode
|
||||
[2025-02-12 11:45:52.756] [INFO ] [ main] [ o.s.d.r.config.RepositoryConfigurationDelegate:132] : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
|
||||
[2025-02-12 11:45:52.815] [INFO ] [ main] [ o.s.d.r.config.RepositoryConfigurationDelegate:201] : Finished Spring Data repository scanning in 24 ms. Found 0 Redis repository interfaces.
|
||||
[2025-02-12 11:45:54.602] [INFO ] [ main] [ o.s.boot.web.embedded.tomcat.TomcatWebServer:108] : Tomcat initialized with port(s): 18080 (http)
|
||||
[2025-02-12 11:45:54.629] [INFO ] [ main] [ org.apache.coyote.http11.Http11NioProtocol:173] : Initializing ProtocolHandler ["http-nio-18080"]
|
||||
[2025-02-12 11:45:54.633] [INFO ] [ main] [ org.apache.catalina.core.StandardService:173] : Starting service [Tomcat]
|
||||
[2025-02-12 11:45:54.633] [INFO ] [ main] [ org.apache.catalina.core.StandardEngine:173] : Starting Servlet engine: [Apache Tomcat/9.0.79]
|
||||
[2025-02-12 11:45:54.915] [INFO ] [ main] [ o.a.c.c.C.[Tomcat].[localhost].[/flxai]:173] : Initializing Spring embedded WebApplicationContext
|
||||
[2025-02-12 11:45:54.920] [INFO ] [ main] [ o.s.b.w.s.c.ServletWebServerApplicationContext:292] : Root WebApplicationContext: initialization completed in 5350 ms
|
||||
[2025-02-12 11:45:55.134] [INFO ] [ main] [ c.a.d.s.b.a.DruidDataSourceAutoConfigure:55] : Init DruidDataSource
|
||||
[2025-02-12 11:45:56.557] [INFO ] [ main] [ com.alibaba.druid.pool.DruidDataSource:975] : {dataSource-1} inited
|
||||
[2025-02-12 11:45:59.480] [INFO ] [ main] [ o.s.b.actuate.endpoint.web.EndpointLinksResolver:58] : Exposing 14 endpoint(s) beneath base path '/actuator'
|
||||
[2025-02-12 11:46:01.227] [INFO ] [ main] [ org.apache.coyote.http11.Http11NioProtocol:173] : Starting ProtocolHandler ["http-nio-18080"]
|
||||
[2025-02-12 11:46:01.311] [INFO ] [ main] [ o.s.boot.web.embedded.tomcat.TomcatWebServer:220] : Tomcat started on port(s): 18080 (http) with context path '/flxai'
|
||||
[2025-02-12 11:46:03.003] [INFO ] [ main] [ com.pjilisense.flxai.FlxAiApplication:61] : Started FlxAiApplication in 15.061 seconds (JVM running for 17.224)
|
||||
[2025-02-12 11:46:06.247] [INFO ] [4)-192.168.56.1] [ o.a.c.c.C.[Tomcat].[localhost].[/flxai]:173] : Initializing Spring DispatcherServlet 'dispatcherServlet'
|
||||
[2025-02-12 11:46:06.252] [INFO ] [4)-192.168.56.1] [ org.springframework.web.servlet.DispatcherServlet:525] : Initializing Servlet 'dispatcherServlet'
|
||||
[2025-02-12 11:46:06.259] [INFO ] [4)-192.168.56.1] [ org.springframework.web.servlet.DispatcherServlet:547] : Completed initialization in 7 ms
|
||||
[2025-02-12 11:46:08.498] [WARN ] [oundedElastic-1] [ o.s.b.actuate.redis.RedisReactiveHealthIndicator:92] : Redis health check failed
|
||||
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1/<unresolved>:6379
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1689)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1597)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1383)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1366)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedReactiveConnection(LettuceConnectionFactory.java:1117)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getReactiveConnection(LettuceConnectionFactory.java:509)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getReactiveConnection(LettuceConnectionFactory.java:103)
|
||||
at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:86)
|
||||
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227)
|
||||
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
|
||||
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
|
||||
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
|
||||
at java.base/java.lang.Thread.run(Thread.java:840)
|
||||
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to 127.0.0.1/<unresolved>:6379
|
||||
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78)
|
||||
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56)
|
||||
at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:330)
|
||||
at io.lettuce.core.RedisClient.connect(RedisClient.java:216)
|
||||
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:115)
|
||||
at java.base/java.util.Optional.orElseGet(Optional.java:364)
|
||||
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:115)
|
||||
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1595)
|
||||
... 14 common frames omitted
|
||||
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: getsockopt: /127.0.0.1:6379
|
||||
Caused by: java.net.ConnectException: Connection refused: getsockopt
|
||||
at java.base/sun.nio.ch.Net.pollConnect(Native Method)
|
||||
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672)
|
||||
at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:946)
|
||||
at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337)
|
||||
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:334)
|
||||
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776)
|
||||
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
|
||||
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
|
||||
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
|
||||
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
|
||||
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
|
||||
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
|
||||
at java.base/java.lang.Thread.run(Thread.java:840)
|
Loading…
Reference in New Issue