Compare commits

...

5 Commits

Author SHA1 Message Date
1009819801 Fix: coroutine object has no attribute get (#11472)
### What problem does this PR solve?

Fix: coroutine object has no attribute get

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-11-24 12:21:33 +08:00
8fe782f4ea Fix:Modify the personal center style #10703 (#11470)
### What problem does this PR solve?

Fix:Modify the personal center style #10703

- All form-label font styles are no longer bold
- Menus are not highlighted on first visit to the personal center

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-11-24 12:20:48 +08:00
7140950e93 Feat: Implement temporary conversation removal logic in ConversationD… (#11454)
### What problem does this PR solve?

Implement temporary conversation removal logic in ConversationDropDown

Before modification:

<img width="2120" height="1034" alt="图片"
src="https://github.com/user-attachments/assets/21cf0a92-5660-401c-8b4c-31d85ec800f0"
/>

After modification:

<img width="2120" height="1034" alt="图片"
src="https://github.com/user-attachments/assets/0a3fffa5-dc9a-4af9-a3c6-c2e976e4bd6b"
/>
<img width="2120" height="1034" alt="图片"
src="https://github.com/user-attachments/assets/45473971-ba83-43e0-8941-64a5c6f552a2"
/>


### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
2025-11-24 10:27:22 +08:00
0181747881 Fix nginx startup failure in HTTPS mode (host not found) (#11455)
### Description
This PR fixes a bug where Nginx fails to start when using the
`ragflow.https.conf` configuration. The upstream host `ragflow` was not
resolving correctly inside the container context, causing an `[emerg]
host not found` error.

### Changes
- Updated `docker/nginx/ragflow.https.conf`: Changed upstream host from
`ragflow` to `localhost` for both the admin API and the main API.

### Related Issue
Fixes #11453

### Testing
- [x] Enabled HTTPS config in Docker.
- [x] Verified Nginx starts successfully without "host not found"
errors.
- [x] Verified API accessibility.
2025-11-24 10:21:27 +08:00
3c41159d26 Update logging for auto-generated SECRET_KEY (#11458)
Remove the code that exposes the generated key in the log, as it poses a
security risk.
 
<img width="1170" height="269" alt="image"
src="https://github.com/user-attachments/assets/03c42516-af1a-49a4-ade2-4ef3ee4b3cdd"
/>

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
2025-11-24 10:21:06 +08:00
16 changed files with 82 additions and 25 deletions

View File

@ -125,8 +125,8 @@ async def upload():
@validate_request("name")
async def create():
req = await request.json
pf_id = await request.json.get("parent_id")
input_file_type = await request.json.get("type")
pf_id = req.get("parent_id")
input_file_type = req.get("type")
if not pf_id:
root_folder = FileService.get_root_folder(current_user.id)
pf_id = root_folder["id"]

View File

@ -139,7 +139,7 @@ def _get_or_create_secret_key():
import logging
new_key = secrets.token_hex(32)
logging.warning(f"SECURITY WARNING: Using auto-generated SECRET_KEY. Generated key: {new_key}")
logging.warning("SECURITY WARNING: Using auto-generated SECRET_KEY.")
return new_key
class StorageFactory:

View File

@ -23,12 +23,12 @@ server {
gzip_disable "MSIE [1-6]\.";
location ~ ^/api/v1/admin {
proxy_pass http://ragflow:9381;
proxy_pass http://localhost:9381;
include proxy.conf;
}
location ~ ^/(v1|api) {
proxy_pass http://ragflow:9380;
proxy_pass http://localhost:9380;
include proxy.conf;
}

View File

@ -47,6 +47,7 @@ export type SelectWithSearchFlagProps = {
allowClear?: boolean;
disabled?: boolean;
placeholder?: string;
emptyData?: string;
};
function findLabelWithoutOptions(
@ -78,6 +79,7 @@ export const SelectWithSearch = forwardRef<
allowClear = false,
disabled = false,
placeholder = t('common.selectPlaceholder'),
emptyData = t('common.noDataFound'),
},
ref,
) => {
@ -183,8 +185,8 @@ export const SelectWithSearch = forwardRef<
className=" placeholder:text-text-disabled"
/>
)}
<CommandList className="mt-2">
<CommandEmpty>{t('common.noDataFound')}</CommandEmpty>
<CommandList className="mt-2 outline-none">
<CommandEmpty>{emptyData}</CommandEmpty>
{options.map((group, idx) => {
if (group.options) {
return (
@ -196,6 +198,9 @@ export const SelectWithSearch = forwardRef<
value={option.value}
disabled={option.disabled}
onSelect={handleSelect}
className={
value === option.value ? 'bg-bg-card' : ''
}
>
<span className="leading-none">{option.label}</span>

View File

@ -7,7 +7,7 @@ import * as React from 'react';
import { cn } from '@/lib/utils';
const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-text-secondary',
'text-sm font-normal leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-text-secondary',
);
const Label = React.forwardRef<

View File

@ -88,18 +88,18 @@ export const useShowDeleteConfirm = () => {
({ title, content, onOk, onCancel }: IProps): Promise<number> => {
return new Promise((resolve, reject) => {
Modal.show({
title: title ?? t('common.deleteModalTitle'),
// title: title ?? t('common.deleteModalTitle'),
closable: false,
visible: true,
onVisibleChange: () => {
Modal.hide();
},
footer: null,
closable: true,
maskClosable: false,
okText: t('common.yes'),
cancelText: t('common.no'),
style: {
width: '400px',
width: '450px',
},
okButtonClassName:
'bg-state-error text-white hover:bg-state-error hover:text-white',
@ -116,7 +116,14 @@ export const useShowDeleteConfirm = () => {
onCancel?.();
Modal.hide();
},
children: content,
children: (
<div className="flex flex-col justify-start items-start mt-3">
<div className="text-lg font-medium">
{title ?? t('common.deleteModalTitle')}
</div>
<div className="text-base font-normal">{content}</div>
</div>
),
});
});
},

View File

@ -696,6 +696,9 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
tocEnhanceTip: ` During the parsing of the document, table of contents information was generated (see the 'Enable Table of Contents Extraction' option in the General method). This allows the large model to return table of contents items relevant to the user's query, thereby using these items to retrieve related chunks and apply weighting to these chunks during the sorting process. This approach is derived from mimicking the behavioral logic of how humans search for knowledge in books.`,
},
setting: {
modelEmptyTip:
'No models available. Please add models from the panel on the right.',
sourceEmptyTip: 'No data sources added yet. Select one below to connect.',
seconds: 'seconds',
minutes: 'minutes',
edit: 'Edit',
@ -716,7 +719,7 @@ Example: https://fsn1.your-objectstorage.com`,
deleteSourceModalTitle: 'Delete data source',
deleteSourceModalContent: `
<p>Are you sure you want to delete this data source link?</p>`,
deleteSourceModalConfirmText: 'Comfirm',
deleteSourceModalConfirmText: 'Confirm',
errorMsg: 'Error message',
newDocs: 'New Docs',
timeStarted: 'Time started',

View File

@ -685,6 +685,8 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
tocEnhanceTip: `解析文档时生成了目录信息见General方法的启用目录抽取让大模型返回和用户问题相关的目录项从而利用目录项拿到相关chunk对这些chunk在排序中进行加权。这种方法来源于模仿人类查询书本中知识的行为逻辑`,
},
setting: {
modelEmptyTip: '暂无可用模型,请先在右侧面板添加模型。',
sourceEmptyTip: '暂未添加任何数据源,请从下方选择一个进行连接。',
seconds: '秒',
minutes: '分',
edit: '编辑',

View File

@ -14,16 +14,28 @@ import { useTranslation } from 'react-i18next';
export function ConversationDropdown({
children,
conversation,
removeTemporaryConversation,
}: PropsWithChildren & {
conversation: IConversation;
removeTemporaryConversation?: (conversationId: string) => void;
}) {
const { t } = useTranslation();
const { removeConversation } = useRemoveConversation();
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
removeConversation([conversation.id]);
}, [conversation.id, removeConversation]);
if (conversation.is_new && removeTemporaryConversation) {
removeTemporaryConversation(conversation.id);
removeConversation([]);
} else {
removeConversation([conversation.id]);
}
}, [
conversation.id,
conversation.is_new,
removeConversation,
removeTemporaryConversation,
]);
return (
<DropdownMenu>

View File

@ -29,6 +29,7 @@ export function Sessions({
const {
list: conversationList,
addTemporaryConversation,
removeTemporaryConversation,
handleInputChange,
searchString,
} = useSelectDerivedConversationList();
@ -97,7 +98,10 @@ export function Sessions({
>
<CardContent className="px-3 py-2 flex justify-between items-center group gap-1">
<div className="truncate">{x.name}</div>
<ConversationDropdown conversation={x}>
<ConversationDropdown
conversation={x}
removeTemporaryConversation={removeTemporaryConversation}
>
<MoreButton></MoreButton>
</ConversationDropdown>
</CardContent>

View File

@ -80,6 +80,14 @@ export const useSelectDerivedConversationList = () => {
});
}, [conversationList, dialogId, prologue, t, setNewConversationRouteParams]);
const removeTemporaryConversation = useCallback((conversationId: string) => {
setList((prevList) => {
return prevList.filter(
(conversation) => conversation.id !== conversationId,
);
});
}, []);
// When you first enter the page, select the top conversation card
useEffect(() => {
@ -89,6 +97,7 @@ export const useSelectDerivedConversationList = () => {
return {
list,
addTemporaryConversation,
removeTemporaryConversation,
loading,
handleInputChange,
searchString,

View File

@ -10,7 +10,7 @@ export const UserSettingHeader = ({
}) => {
return (
<>
<header className="flex flex-col gap-1 justify-between items-start p-0">
<header className="flex flex-col gap-1.5 justify-between items-start p-0">
<div className="text-2xl font-medium text-text-primary">{name}</div>
{description && (
<div className="text-sm text-text-secondary ">{description}</div>

View File

@ -170,7 +170,7 @@ const SourceDetailPage = () => {
</CardTitle>
</CardHeader>
<Separator className="border-border-button bg-border-button w-[calc(100%+2rem)] -translate-x-4 -translate-y-4" />
<CardContent className="p-2 flex flex-col gap-2 max-h-[calc(100vh-190px)] overflow-y-auto scrollbar-auto">
<CardContent className="p-2 flex flex-col gap-10 max-h-[calc(100vh-190px)] overflow-y-auto scrollbar-auto">
<div className="max-w-[1200px]">
<DynamicForm.Root
ref={formRef}
@ -181,8 +181,10 @@ const SourceDetailPage = () => {
defaultValues={defaultValues}
/>
</div>
<section className="flex flex-col gap-2 mt-6">
<div className="text-2xl text-text-primary">{t('setting.log')}</div>
<section className="flex flex-col gap-2">
<div className="text-2xl text-text-primary mb-2">
{t('setting.log')}
</div>
<DataSourceLogsTable refresh_freq={detail?.refresh_freq || false} />
</section>
</CardContent>

View File

@ -120,6 +120,11 @@ const DataSource = () => {
<div className="relative">
<div className=" flex flex-col gap-4 max-h-[calc(100vh-230px)] overflow-y-auto overflow-x-hidden scrollbar-auto">
<div className="flex flex-col gap-3">
{categorizedList?.length <= 0 && (
<div className="text-text-secondary w-full flex justify-center items-center h-20">
{t('setting.sourceEmptyTip')}
</div>
)}
{categorizedList.map((item, index) => (
<AddedSourceCard key={index} {...item} />
))}
@ -127,9 +132,9 @@ const DataSource = () => {
<section className="bg-transparent border-none mt-8">
<header className="flex flex-row items-center justify-between space-y-0 p-0 pb-4">
{/* <Users className="mr-2 h-5 w-5 text-[#1677ff]" /> */}
<CardTitle className="text-2xl font-semibold">
<CardTitle className="text-2xl font-semibold ">
{t('setting.availableSources')}
<div className="text-sm text-text-secondary font-normal">
<div className="text-sm text-text-secondary font-normal mt-1.5">
{t('setting.availableSourcesDescription')}
</div>
</CardTitle>

View File

@ -161,6 +161,7 @@ const SystemSetting = ({ onOk, loading }: IProps) => {
options={options}
onChange={(value) => handleFieldChange(id, value)}
placeholder={t('selectModelPlaceholder')}
emptyData={t('modelEmptyTip')}
/>
</div>
);

View File

@ -1,12 +1,19 @@
import { useLogout } from '@/hooks/login-hooks';
import { Routes } from '@/routes';
import { useCallback, useState } from 'react';
import { useNavigate } from 'umi';
import { useCallback, useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'umi';
export const useHandleMenuClick = () => {
const navigate = useNavigate();
const [active, setActive] = useState<Routes>();
const { logout } = useLogout();
const location = useLocation();
useEffect(() => {
const path = (location.pathname.split('/')?.[2] || '') as Routes;
if (path) {
setActive(('/' + path) as Routes);
}
}, [location]);
const handleMenuClick = useCallback(
(key: Routes) => () => {
@ -20,5 +27,5 @@ export const useHandleMenuClick = () => {
[logout, navigate],
);
return { handleMenuClick, active };
return { handleMenuClick, active, setActive };
};