Compare commits

..

4 Commits

Author SHA1 Message Date
adbb038a87 Fix: Place the invitation reminder icon in a separate file #9634 (#9662)
### What problem does this PR solve?

Fix: Place the invitation reminder icon in a separate file #9634
Fix: After receiving the agent message, pull the agent data to highlight
the edges passed #9538

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-22 20:08:55 +08:00
3947da10ae Fix: unexpected LLM parameters (#9661)
### What problem does this PR solve?

Remove unexpected LLM parameters.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-22 19:33:09 +08:00
4862be28ad Fix: Search app AI summary ERROR And The tag set cannot be selected #9649 #9652 (#9664)
### What problem does this PR solve?
Fix: Search app AI summary ERROR And The tag set cannot be selected
#9649 #9652
- Search app AI summary ERROR: 'dict' object has no attribute 'split'
#9649
- fix The tag set cannot be selected in the knowledge base. #9652
- Added custom parameter options to the LlmSettingFieldItems component
- Adjusted the document preview height to improve page layout
adaptability

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-22 19:32:32 +08:00
035e8ed0f7 Fix: code executor timeout (#9671)
### What problem does this PR solve?

Code executor timeout.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-08-22 19:31:49 +08:00
15 changed files with 148 additions and 64 deletions

View File

@ -79,7 +79,7 @@ def main() -> dict:
return { return {
"result": fibonacci_recursive(100), "result": fibonacci_recursive(100),
} }
Here's a code example for Javascript(`main` function MUST be included and exported): Here's a code example for Javascript(`main` function MUST be included and exported):
const axios = require('axios'); const axios = require('axios');
async function main(args) { async function main(args) {
@ -156,7 +156,7 @@ class CodeExec(ToolBase, ABC):
self.set_output("_ERROR", "construct code request error: " + str(e)) self.set_output("_ERROR", "construct code request error: " + str(e))
try: try:
resp = requests.post(url=f"http://{settings.SANDBOX_HOST}:9385/run", json=code_req, timeout=10) resp = requests.post(url=f"http://{settings.SANDBOX_HOST}:9385/run", json=code_req, timeout=os.environ.get("COMPONENT_EXEC_TIMEOUT", 10*60))
logging.info(f"http://{settings.SANDBOX_HOST}:9385/run", code_req, resp.status_code) logging.info(f"http://{settings.SANDBOX_HOST}:9385/run", code_req, resp.status_code)
if resp.status_code != 200: if resp.status_code != 200:
resp.raise_for_status() resp.raise_for_status()

View File

@ -112,6 +112,32 @@ class Base(ABC):
def _clean_conf(self, gen_conf): def _clean_conf(self, gen_conf):
if "max_tokens" in gen_conf: if "max_tokens" in gen_conf:
del gen_conf["max_tokens"] del gen_conf["max_tokens"]
allowed_conf = {
"temperature",
"max_completion_tokens",
"top_p",
"stream",
"stream_options",
"stop",
"n",
"presence_penalty",
"frequency_penalty",
"functions",
"function_call",
"logit_bias",
"user",
"response_format",
"seed",
"tools",
"tool_choice",
"logprobs",
"top_logprobs",
"extra_headers",
}
gen_conf = {k: v for k, v in gen_conf.items() if k in allowed_conf}
return gen_conf return gen_conf
def _chat(self, history, gen_conf, **kwargs): def _chat(self, history, gen_conf, **kwargs):

View File

@ -22,7 +22,7 @@ from util import format_timeout_duration, parse_timeout_duration
from core.container import init_containers, teardown_containers from core.container import init_containers, teardown_containers
from core.logger import logger from core.logger import logger
TIMEOUT = 10 TIMEOUT = parse_timeout_duration(os.getenv("SANDBOX_TIMEOUT", "10s"))
@asynccontextmanager @asynccontextmanager
@ -39,6 +39,5 @@ async def _lifespan(app: FastAPI):
def init(): def init():
TIMEOUT = parse_timeout_duration(os.getenv("SANDBOX_TIMEOUT"))
logger.info(f"Global timeout: {format_timeout_duration(TIMEOUT)}") logger.info(f"Global timeout: {format_timeout_duration(TIMEOUT)}")
return _lifespan return _lifespan

View File

@ -19,6 +19,7 @@ type SliderInputSwitchFormFieldProps = {
name: string; name: string;
label: string; label: string;
defaultValue?: number; defaultValue?: number;
onChange?: (value: number) => void;
className?: string; className?: string;
checkName: string; checkName: string;
}; };
@ -30,6 +31,7 @@ export function SliderInputSwitchFormField({
label, label,
name, name,
defaultValue, defaultValue,
onChange,
className, className,
checkName, checkName,
}: SliderInputSwitchFormFieldProps) { }: SliderInputSwitchFormFieldProps) {
@ -66,6 +68,10 @@ export function SliderInputSwitchFormField({
<FormControl> <FormControl>
<SingleFormSlider <SingleFormSlider
{...field} {...field}
onChange={(value: number) => {
onChange?.(value);
field.onChange(value);
}}
max={max} max={max}
min={min} min={min}
step={step} step={step}
@ -80,6 +86,10 @@ export function SliderInputSwitchFormField({
min={min} min={min}
step={step} step={step}
{...field} {...field}
onChange={(value: number) => {
onChange?.(value);
field.onChange(value);
}}
></NumberInput> ></NumberInput>
</FormControl> </FormControl>
</div> </div>

View File

@ -209,8 +209,10 @@ export const MultiSelect = React.forwardRef<
const [isAnimating, setIsAnimating] = React.useState(false); const [isAnimating, setIsAnimating] = React.useState(false);
React.useEffect(() => { React.useEffect(() => {
setSelectedValues(defaultValue); if (selectedValues === undefined) {
}, [defaultValue]); setSelectedValues(defaultValue);
}
}, [defaultValue, selectedValues]);
const flatOptions = React.useMemo(() => { const flatOptions = React.useMemo(() => {
return options.flatMap((option) => return options.flatMap((option) =>

View File

@ -0,0 +1,28 @@
import { Button } from '@/components/ui/button';
import { useNavigateWithFromState } from '@/hooks/route-hook';
import { useListTenant } from '@/hooks/use-user-setting-request';
import { TenantRole } from '@/pages/user-setting/constants';
import { BellRing } from 'lucide-react';
import { useCallback, useMemo } from 'react';
export function BellButton() {
const { data } = useListTenant();
const navigate = useNavigateWithFromState();
const showBell = useMemo(() => {
return data.some((x) => x.role === TenantRole.Invite);
}, [data]);
const handleBellClick = useCallback(() => {
navigate('/user-setting/team');
}, [navigate]);
return showBell ? (
<Button variant={'ghost'} onClick={handleBellClick}>
<div className="relative">
<BellRing className="size-4 " />
<span className="absolute size-1 rounded -right-1 -top-1 bg-red-600"></span>
</div>
</Button>
) : null;
}

View File

@ -12,13 +12,10 @@ import { LanguageList, LanguageMap, ThemeEnum } from '@/constants/common';
import { useChangeLanguage } from '@/hooks/logic-hooks'; import { useChangeLanguage } from '@/hooks/logic-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useNavigateWithFromState } from '@/hooks/route-hook'; import { useNavigateWithFromState } from '@/hooks/route-hook';
import { useListTenant } from '@/hooks/use-user-setting-request';
import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { TenantRole } from '@/pages/user-setting/constants';
import { Routes } from '@/routes'; import { Routes } from '@/routes';
import { camelCase } from 'lodash'; import { camelCase } from 'lodash';
import { import {
BellRing,
ChevronDown, ChevronDown,
CircleHelp, CircleHelp,
Cpu, Cpu,
@ -34,6 +31,7 @@ import {
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useLocation } from 'umi'; import { useLocation } from 'umi';
import { BellButton } from './bell-button';
const handleDocHelpCLick = () => { const handleDocHelpCLick = () => {
window.open('https://ragflow.io/docs/dev/category/guides', 'target'); window.open('https://ragflow.io/docs/dev/category/guides', 'target');
@ -56,12 +54,6 @@ export function Header() {
changeLanguage(key); changeLanguage(key);
}; };
const { data } = useListTenant();
const showBell = useMemo(() => {
return data.some((x) => x.role === TenantRole.Invite);
}, [data]);
const items = LanguageList.map((x) => ({ const items = LanguageList.map((x) => ({
key: x, key: x,
label: <span>{LanguageMap[x as keyof typeof LanguageMap]}</span>, label: <span>{LanguageMap[x as keyof typeof LanguageMap]}</span>,
@ -71,10 +63,6 @@ export function Header() {
setTheme(theme === ThemeEnum.Dark ? ThemeEnum.Light : ThemeEnum.Dark); setTheme(theme === ThemeEnum.Dark ? ThemeEnum.Light : ThemeEnum.Dark);
}, [setTheme, theme]); }, [setTheme, theme]);
const handleBellClick = useCallback(() => {
navigate('/user-setting/team');
}, [navigate]);
const tagsData = useMemo( const tagsData = useMemo(
() => [ () => [
{ path: Routes.Root, name: t('header.Root'), icon: House }, { path: Routes.Root, name: t('header.Root'), icon: House },
@ -163,14 +151,7 @@ export function Header() {
<Button variant={'ghost'} onClick={onThemeClick}> <Button variant={'ghost'} onClick={onThemeClick}>
{theme === 'light' ? <Sun /> : <Moon />} {theme === 'light' ? <Sun /> : <Moon />}
</Button> </Button>
{showBell && ( <BellButton></BellButton>
<Button variant={'ghost'} onClick={handleBellClick}>
<div className="relative">
<BellRing className="size-4 " />
<span className="absolute size-1 rounded -right-1 -top-1 bg-red-600"></span>
</div>
</Button>
)}
<div className="relative"> <div className="relative">
<RAGFlowAvatar <RAGFlowAvatar
name={nickname} name={nickname}

View File

@ -479,6 +479,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
improvise: 'Improvise', improvise: 'Improvise',
precise: 'Precise', precise: 'Precise',
balance: 'Balance', balance: 'Balance',
custom: 'Custom',
freedomTip: `A shortcut to 'Temperature', 'Top P', 'Presence penalty', and 'Frequency penalty' settings, indicating the freedom level of the model. This parameter has three options: Select 'Improvise' to produce more creative responses; select 'Precise' (default) to produce more conservative responses; 'Balance' is a middle ground between 'Improvise' and 'Precise'.`, freedomTip: `A shortcut to 'Temperature', 'Top P', 'Presence penalty', and 'Frequency penalty' settings, indicating the freedom level of the model. This parameter has three options: Select 'Improvise' to produce more creative responses; select 'Precise' (default) to produce more conservative responses; 'Balance' is a middle ground between 'Improvise' and 'Precise'.`,
temperature: 'Temperature', temperature: 'Temperature',
temperatureMessage: 'Temperature is required', temperatureMessage: 'Temperature is required',

View File

@ -454,6 +454,7 @@ export default {
improvise: '即興創作', improvise: '即興創作',
precise: '精確', precise: '精確',
balance: '平衡', balance: '平衡',
custom: '自定義',
freedomTip: `“精確”意味著法學碩士會保守並謹慎地回答你的問題。“即興發揮”意味著你希望法學碩士能夠自由地暢所欲言。“平衡”是謹慎與自由之間的平衡。`, freedomTip: `“精確”意味著法學碩士會保守並謹慎地回答你的問題。“即興發揮”意味著你希望法學碩士能夠自由地暢所欲言。“平衡”是謹慎與自由之間的平衡。`,
temperature: '溫度', temperature: '溫度',
temperatureMessage: '溫度是必填項', temperatureMessage: '溫度是必填項',

View File

@ -477,6 +477,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
improvise: '即兴创作', improvise: '即兴创作',
precise: '精确', precise: '精确',
balance: '平衡', balance: '平衡',
custom: '自定义',
freedomTip: `“精确”意味着大语言模型会保守并谨慎地回答你的问题。 “即兴发挥”意味着你希望大语言模型能够自由地畅所欲言。 “平衡”是谨慎与自由之间的平衡。`, freedomTip: `“精确”意味着大语言模型会保守并谨慎地回答你的问题。 “即兴发挥”意味着你希望大语言模型能够自由地畅所欲言。 “平衡”是谨慎与自由之间的平衡。`,
temperature: '温度', temperature: '温度',
temperatureMessage: '温度是必填项', temperatureMessage: '温度是必填项',

View File

@ -4,6 +4,7 @@ import {
useHandleMessageInputChange, useHandleMessageInputChange,
useSelectDerivedMessages, useSelectDerivedMessages,
} from '@/hooks/logic-hooks'; } from '@/hooks/logic-hooks';
import { useFetchAgent } from '@/hooks/use-agent-request';
import { import {
IEventList, IEventList,
IInputEvent, IInputEvent,
@ -188,11 +189,7 @@ export const useSendAgentMessage = (
return answerList[0]?.message_id; return answerList[0]?.message_id;
}, [answerList]); }, [answerList]);
useEffect(() => { const { refetch } = useFetchAgent();
if (answerList[0]?.session_id) {
setSessionId(answerList[0]?.session_id);
}
}, [answerList]);
const { findReferenceByMessageId } = useFindMessageReference(answerList); const { findReferenceByMessageId } = useFindMessageReference(answerList);
const prologue = useGetBeginNodePrologue(); const prologue = useGetBeginNodePrologue();
@ -250,7 +247,7 @@ export const useSendAgentMessage = (
setValue(message.content); setValue(message.content);
removeLatestMessage(); removeLatestMessage();
} else { } else {
// refetch(); // pull the message list after sending the message successfully refetch(); // pull the message list after sending the message successfully
} }
} catch (error) { } catch (error) {
console.log('🚀 ~ useSendAgentMessage ~ error:', error); console.log('🚀 ~ useSendAgentMessage ~ error:', error);
@ -258,28 +255,30 @@ export const useSendAgentMessage = (
}, },
[ [
agentId, agentId,
sessionId,
send,
clearUploadResponseList,
inputs, inputs,
beginParams, beginParams,
uploadResponseList, uploadResponseList,
sessionId,
send,
clearUploadResponseList,
setValue, setValue,
removeLatestMessage, removeLatestMessage,
refetch,
], ],
); );
const sendFormMessage = useCallback( const sendFormMessage = useCallback(
(body: { id?: string; inputs: Record<string, BeginQuery> }) => { async (body: { id?: string; inputs: Record<string, BeginQuery> }) => {
send({ ...body, session_id: sessionId });
addNewestOneQuestion({ addNewestOneQuestion({
content: Object.entries(body.inputs) content: Object.entries(body.inputs)
.map(([key, val]) => `${key}: ${val.value}`) .map(([key, val]) => `${key}: ${val.value}`)
.join('<br/>'), .join('<br/>'),
role: MessageType.User, role: MessageType.User,
}); });
await send({ ...body, session_id: sessionId });
refetch();
}, },
[addNewestOneQuestion, send, sessionId], [addNewestOneQuestion, refetch, send, sessionId],
); );
// reset session // reset session
@ -346,6 +345,12 @@ export const useSendAgentMessage = (
} }
}, [addEventList, answerList, addEventListFun, messageId]); }, [addEventList, answerList, addEventListFun, messageId]);
useEffect(() => {
if (answerList[0]?.session_id) {
setSessionId(answerList[0]?.session_id);
}
}, [answerList]);
return { return {
value, value,
sendLoading: !done, sendLoading: !done,

View File

@ -35,7 +35,7 @@
.documentPreview { .documentPreview {
// width: 40%; // width: 40%;
height: calc(100vh - 130px); height: calc(100vh - 180px);
overflow: auto; overflow: auto;
} }

View File

@ -21,7 +21,7 @@ import {
} from '@/constants/knowledge'; } from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks'; import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks';
import { camelCase } from 'lodash'; import { camelCase, isEqual } from 'lodash';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
@ -61,20 +61,15 @@ export function LlmSettingFieldItems({
const handleChange = useCallback( const handleChange = useCallback(
(parameter: string) => { (parameter: string) => {
const currentValues = { ...form.getValues() };
console.log('currentValues', currentValues);
const values = const values =
settledModelVariableMap[ settledModelVariableMap[
parameter as keyof typeof settledModelVariableMap parameter as keyof typeof settledModelVariableMap
]; ];
const enabledKeys = Object.keys(LlmSettingEnableSchema); const enabledKeys = Object.keys(LlmSettingEnableSchema);
// const nextValues = { ...currentValues, ...values };
for (const key in values) { for (const key in values) {
if (Object.prototype.hasOwnProperty.call(values, key)) { if (Object.prototype.hasOwnProperty.call(values, key)) {
const element = values[key]; const element = values[key as keyof typeof values];
form.setValue(`${prefix}.${key}`, element); form.setValue(`${prefix}.${key}`, element);
} }
} }
@ -90,7 +85,11 @@ export function LlmSettingFieldItems({
const parameterOptions = Object.values(ModelVariableType).map((x) => ({ const parameterOptions = Object.values(ModelVariableType).map((x) => ({
label: t(camelCase(x)), label: t(camelCase(x)),
value: x, value: x,
})); })) as unknown as { label: string; value: ModelVariableType | 'Custom' }[];
parameterOptions.push({
label: t(camelCase('Custom')),
value: 'Custom',
});
const getFieldWithPrefix = useCallback( const getFieldWithPrefix = useCallback(
(name: string) => { (name: string) => {
@ -99,6 +98,35 @@ export function LlmSettingFieldItems({
[prefix], [prefix],
); );
const checkParameterIsEquel = () => {
const [
parameter,
topPValue,
frequencyPenaltyValue,
temperatureValue,
presencePenaltyValue,
] = form.getValues([
getFieldWithPrefix('parameter'),
getFieldWithPrefix('temperature'),
getFieldWithPrefix('top_p'),
getFieldWithPrefix('frequency_penalty'),
getFieldWithPrefix('presence_penalty'),
]);
if (parameter && parameter !== 'Custom') {
const parameterValue =
settledModelVariableMap[parameter as keyof typeof ModelVariableType];
const parameterRealValue = {
top_p: topPValue,
temperature: temperatureValue,
frequency_penalty: frequencyPenaltyValue,
presence_penalty: presencePenaltyValue,
};
if (!isEqual(parameterValue, parameterRealValue)) {
form.setValue(getFieldWithPrefix('parameter'), 'Custom');
}
}
};
return ( return (
<div className="space-y-5"> <div className="space-y-5">
<FormField <FormField
@ -159,6 +187,9 @@ export function LlmSettingFieldItems({
label="temperature" label="temperature"
max={1} max={1}
step={0.01} step={0.01}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField> ></SliderInputSwitchFormField>
<SliderInputSwitchFormField <SliderInputSwitchFormField
name={getFieldWithPrefix('top_p')} name={getFieldWithPrefix('top_p')}
@ -166,6 +197,9 @@ export function LlmSettingFieldItems({
label="topP" label="topP"
max={1} max={1}
step={0.01} step={0.01}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField> ></SliderInputSwitchFormField>
<SliderInputSwitchFormField <SliderInputSwitchFormField
name={getFieldWithPrefix('presence_penalty')} name={getFieldWithPrefix('presence_penalty')}
@ -173,6 +207,9 @@ export function LlmSettingFieldItems({
label="presencePenalty" label="presencePenalty"
max={1} max={1}
step={0.01} step={0.01}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField> ></SliderInputSwitchFormField>
<SliderInputSwitchFormField <SliderInputSwitchFormField
name={getFieldWithPrefix('frequency_penalty')} name={getFieldWithPrefix('frequency_penalty')}
@ -180,6 +217,9 @@ export function LlmSettingFieldItems({
label="frequencyPenalty" label="frequencyPenalty"
max={1} max={1}
step={0.01} step={0.01}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField> ></SliderInputSwitchFormField>
{/* <SliderInputSwitchFormField {/* <SliderInputSwitchFormField
name={getFieldWithPrefix('max_tokens')} name={getFieldWithPrefix('max_tokens')}

View File

@ -136,9 +136,9 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
use_rerank: search_config?.rerank_id ? true : false, use_rerank: search_config?.rerank_id ? true : false,
top_k: search_config?.top_k || 1024, top_k: search_config?.top_k || 1024,
summary: search_config?.summary || false, summary: search_config?.summary || false,
chat_id: '', chat_id: search_config?.chat_id || '',
llm_setting: { llm_setting: {
llm_id: llm_setting?.llm_id || '', llm_id: search_config?.chat_id || '',
parameter: llm_setting?.parameter, parameter: llm_setting?.parameter,
temperature: llm_setting?.temperature, temperature: llm_setting?.temperature,
top_p: llm_setting?.top_p, top_p: llm_setting?.top_p,
@ -159,7 +159,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
meta_data_filter: search_config?.meta_data_filter, meta_data_filter: search_config?.meta_data_filter,
}, },
}); });
}, [data, search_config, llm_setting, formMethods]); }, [data, search_config, llm_setting, formMethods, descriptionDefaultValue]);
useEffect(() => { useEffect(() => {
resetForm(); resetForm();
@ -255,7 +255,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
...other_config ...other_config
} = search_config; } = search_config;
const llmSetting = { const llmSetting = {
llm_id: llm_setting.llm_id, // llm_id: llm_setting.llm_id,
parameter: llm_setting.parameter, parameter: llm_setting.parameter,
temperature: llm_setting.temperature, temperature: llm_setting.temperature,
top_p: llm_setting.top_p, top_p: llm_setting.top_p,
@ -263,22 +263,11 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
presence_penalty: llm_setting.presence_penalty, presence_penalty: llm_setting.presence_penalty,
} as IllmSettingProps; } as IllmSettingProps;
if (!llm_setting.frequencyPenaltyEnabled) {
delete llmSetting.frequency_penalty;
}
if (!llm_setting.presencePenaltyEnabled) {
delete llmSetting.presence_penalty;
}
if (!llm_setting.temperatureEnabled) {
delete llmSetting.temperature;
}
if (!llm_setting.topPEnabled) {
delete llmSetting.top_p;
}
await updateSearch({ await updateSearch({
...other_formdata, ...other_formdata,
search_config: { search_config: {
...other_config, ...other_config,
chat_id: llm_setting.llm_id,
vector_similarity_weight: 1 - vector_similarity_weight, vector_similarity_weight: 1 - vector_similarity_weight,
rerank_id: use_rerank ? rerank_id : '', rerank_id: use_rerank ? rerank_id : '',
llm_setting: { ...llmSetting }, llm_setting: { ...llmSetting },

View File

@ -154,6 +154,7 @@ export interface ISearchAppDetailProps {
search_config: { search_config: {
cross_languages: string[]; cross_languages: string[];
doc_ids: string[]; doc_ids: string[];
chat_id: string;
highlight: boolean; highlight: boolean;
kb_ids: string[]; kb_ids: string[];
keyword: boolean; keyword: boolean;