Merge branch 'main' of github.com:infiniflow/ragflow into meta-filter

This commit is contained in:
bill 2025-11-21 15:44:37 +08:00
commit db037f5df9
25 changed files with 347 additions and 360 deletions

View file

@ -86,7 +86,7 @@ Try our demo at [https://demo.ragflow.io](https://demo.ragflow.io).
## 🔥 Latest Updates ## 🔥 Latest Updates
- 2025-11-19 Supports Gemini 3 Pro. - 2025-11-19 Supports Gemini 3 Pro.
- 2025-11-12 Supports data synchronization from Confluence, AWS S3, Discord, Google Drive. - 2025-11-12 Supports data synchronization from Confluence, S3, Notion, Discord, Google Drive.
- 2025-10-23 Supports MinerU & Docling as document parsing methods. - 2025-10-23 Supports MinerU & Docling as document parsing methods.
- 2025-10-15 Supports orchestrable ingestion pipeline. - 2025-10-15 Supports orchestrable ingestion pipeline.
- 2025-08-08 Supports OpenAI's latest GPT-5 series models. - 2025-08-08 Supports OpenAI's latest GPT-5 series models.

View file

@ -86,7 +86,7 @@ Coba demo kami di [https://demo.ragflow.io](https://demo.ragflow.io).
## 🔥 Pembaruan Terbaru ## 🔥 Pembaruan Terbaru
- 2025-11-19 Mendukung Gemini 3 Pro. - 2025-11-19 Mendukung Gemini 3 Pro.
- 2025-11-12 Mendukung sinkronisasi data dari Confluence, AWS S3, Discord, Google Drive. - 2025-11-12 Mendukung sinkronisasi data dari Confluence, S3, Notion, Discord, Google Drive.
- 2025-10-23 Mendukung MinerU & Docling sebagai metode penguraian dokumen. - 2025-10-23 Mendukung MinerU & Docling sebagai metode penguraian dokumen.
- 2025-10-15 Dukungan untuk jalur data yang terorkestrasi. - 2025-10-15 Dukungan untuk jalur data yang terorkestrasi.
- 2025-08-08 Mendukung model seri GPT-5 terbaru dari OpenAI. - 2025-08-08 Mendukung model seri GPT-5 terbaru dari OpenAI.

View file

@ -67,7 +67,7 @@
## 🔥 最新情報 ## 🔥 最新情報
- 2025-11-19 Gemini 3 Proをサポートしています - 2025-11-19 Gemini 3 Proをサポートしています
- 2025-11-12 Confluence、AWS S3、Discord、Google Drive からのデータ同期をサポートします。 - 2025-11-12 Confluence、S3、Notion、Discord、Google Drive からのデータ同期をサポートします。
- 2025-10-23 ドキュメント解析方法として MinerU と Docling をサポートします。 - 2025-10-23 ドキュメント解析方法として MinerU と Docling をサポートします。
- 2025-10-15 オーケストレーションされたデータパイプラインのサポート。 - 2025-10-15 オーケストレーションされたデータパイプラインのサポート。
- 2025-08-08 OpenAI の最新 GPT-5 シリーズモデルをサポートします。 - 2025-08-08 OpenAI の最新 GPT-5 シリーズモデルをサポートします。

View file

@ -68,7 +68,7 @@
## 🔥 업데이트 ## 🔥 업데이트
- 2025-11-19 Gemini 3 Pro를 지원합니다. - 2025-11-19 Gemini 3 Pro를 지원합니다.
- 2025-11-12 Confluence, AWS S3, Discord, Google Drive에서 데이터 동기화를 지원합니다. - 2025-11-12 Confluence, S3, Notion, Discord, Google Drive에서 데이터 동기화를 지원합니다.
- 2025-10-23 문서 파싱 방법으로 MinerU 및 Docling을 지원합니다. - 2025-10-23 문서 파싱 방법으로 MinerU 및 Docling을 지원합니다.
- 2025-10-15 조정된 데이터 파이프라인 지원. - 2025-10-15 조정된 데이터 파이프라인 지원.
- 2025-08-08 OpenAI의 최신 GPT-5 시리즈 모델을 지원합니다. - 2025-08-08 OpenAI의 최신 GPT-5 시리즈 모델을 지원합니다.

View file

@ -87,7 +87,7 @@ Experimente nossa demo em [https://demo.ragflow.io](https://demo.ragflow.io).
## 🔥 Últimas Atualizações ## 🔥 Últimas Atualizações
- 19-11-2025 Suporta Gemini 3 Pro. - 19-11-2025 Suporta Gemini 3 Pro.
- 12-11-2025 Suporta a sincronização de dados do Confluence, AWS S3, Discord e Google Drive. - 12-11-2025 Suporta a sincronização de dados do Confluence, S3, Notion, Discord e Google Drive.
- 23-10-2025 Suporta MinerU e Docling como métodos de análise de documentos. - 23-10-2025 Suporta MinerU e Docling como métodos de análise de documentos.
- 15-10-2025 Suporte para pipelines de dados orquestrados. - 15-10-2025 Suporte para pipelines de dados orquestrados.
- 08-08-2025 Suporta a mais recente série GPT-5 da OpenAI. - 08-08-2025 Suporta a mais recente série GPT-5 da OpenAI.

View file

@ -86,7 +86,7 @@
## 🔥 近期更新 ## 🔥 近期更新
- 2025-11-19 支援 Gemini 3 Pro. - 2025-11-19 支援 Gemini 3 Pro.
- 2025-11-12 支援從 Confluence、AWS S3、Discord、Google Drive 進行資料同步。 - 2025-11-12 支援從 Confluence、S3、Notion、Discord、Google Drive 進行資料同步。
- 2025-10-23 支援 MinerU 和 Docling 作為文件解析方法。 - 2025-10-23 支援 MinerU 和 Docling 作為文件解析方法。
- 2025-10-15 支援可編排的資料管道。 - 2025-10-15 支援可編排的資料管道。
- 2025-08-08 支援 OpenAI 最新的 GPT-5 系列模型。 - 2025-08-08 支援 OpenAI 最新的 GPT-5 系列模型。

View file

@ -86,7 +86,7 @@
## 🔥 近期更新 ## 🔥 近期更新
- 2025-11-19 支持 Gemini 3 Pro. - 2025-11-19 支持 Gemini 3 Pro.
- 2025-11-12 支持从 Confluence、AWS S3、Discord、Google Drive 进行数据同步。 - 2025-11-12 支持从 Confluence、S3、Notion、Discord、Google Drive 进行数据同步。
- 2025-10-23 支持 MinerU 和 Docling 作为文档解析方法。 - 2025-10-23 支持 MinerU 和 Docling 作为文档解析方法。
- 2025-10-15 支持可编排的数据管道。 - 2025-10-15 支持可编排的数据管道。
- 2025-08-08 支持 OpenAI 最新的 GPT-5 系列模型。 - 2025-08-08 支持 OpenAI 最新的 GPT-5 系列模型。

View file

@ -32,7 +32,7 @@ class IterationParam(ComponentParamBase):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.items_ref = "" self.items_ref = ""
self.veriable={} self.variable={}
def get_input_form(self) -> dict[str, dict]: def get_input_form(self) -> dict[str, dict]:
return { return {

View file

@ -24,7 +24,7 @@ from flasgger import Swagger
from itsdangerous.url_safe import URLSafeTimedSerializer as Serializer from itsdangerous.url_safe import URLSafeTimedSerializer as Serializer
from quart_cors import cors from quart_cors import cors
from common.constants import StatusEnum from common.constants import StatusEnum
from api.db.db_models import close_connection from api.db.db_models import close_connection, APIToken
from api.db.services import UserService from api.db.services import UserService
from api.utils.json_encode import CustomJSONEncoder from api.utils.json_encode import CustomJSONEncoder
from api.utils import commands from api.utils import commands
@ -124,6 +124,10 @@ def _load_user():
user = UserService.query( user = UserService.query(
access_token=access_token, status=StatusEnum.VALID.value access_token=access_token, status=StatusEnum.VALID.value
) )
if not user and len(authorization.split()) == 2:
objs = APIToken.query(token=authorization.split()[1])
if objs:
user = UserService.query(id=objs[0].tenant_id, status=StatusEnum.VALID.value)
if user: if user:
if not user[0].access_token or not user[0].access_token.strip(): if not user[0].access_token or not user[0].access_token.strip():
logging.warning(f"User {user[0].email} has empty access_token in database") logging.warning(f"User {user[0].email} has empty access_token in database")

View file

@ -1434,6 +1434,7 @@ async def retrieval_test(tenant_id):
question = req["question"] question = req["question"]
doc_ids = req.get("document_ids", []) doc_ids = req.get("document_ids", [])
use_kg = req.get("use_kg", False) use_kg = req.get("use_kg", False)
toc_enhance = req.get("toc_enhance", False)
langs = req.get("cross_languages", []) langs = req.get("cross_languages", [])
if not isinstance(doc_ids, list): if not isinstance(doc_ids, list):
return get_error_data_result("`documents` should be a list") return get_error_data_result("`documents` should be a list")
@ -1487,6 +1488,11 @@ async def retrieval_test(tenant_id):
highlight=highlight, highlight=highlight,
rank_feature=label_question(question, kbs), rank_feature=label_question(question, kbs),
) )
if toc_enhance:
chat_mdl = LLMBundle(kb.tenant_id, LLMType.CHAT)
cks = settings.retriever.retrieval_by_toc(question, ranks["chunks"], tenant_ids, chat_mdl, size)
if cks:
ranks["chunks"] = cks
if use_kg: if use_kg:
ck = settings.kg_retriever.retrieval(question, [k.tenant_id for k in kbs], kb_ids, embd_mdl, LLMBundle(kb.tenant_id, LLMType.CHAT)) ck = settings.kg_retriever.retrieval(question, [k.tenant_id for k in kbs], kb_ids, embd_mdl, LLMBundle(kb.tenant_id, LLMType.CHAT))
if ck["content_with_weight"]: if ck["content_with_weight"]:

View file

@ -1091,7 +1091,7 @@ class RAGFlowPdfParser:
logging.debug("Images converted.") logging.debug("Images converted.")
self.is_english = [ self.is_english = [
re.search(r"[a-zA-Z0-9,/¸;:'\[\]\(\)!@#$%^&*\"?<>._-]{30,}", "".join(random.choices([c["text"] for c in self.page_chars[i]], k=min(100, len(self.page_chars[i]))))) re.search(r"[ a-zA-Z0-9,/¸;:'\[\]\(\)!@#$%^&*\"?<>._-]{30,}", "".join(random.choices([c["text"] for c in self.page_chars[i]], k=min(100, len(self.page_chars[i])))))
for i in range(len(self.page_chars)) for i in range(len(self.page_chars))
] ]
if sum([1 if e else 0 for e in self.is_english]) > len(self.page_images) / 2: if sum([1 if e else 0 for e in self.is_english]) > len(self.page_images) / 2:
@ -1148,7 +1148,7 @@ class RAGFlowPdfParser:
if not self.is_english and not any([c for c in self.page_chars]) and self.boxes: if not self.is_english and not any([c for c in self.page_chars]) and self.boxes:
bxes = [b for bxs in self.boxes for b in bxs] bxes = [b for bxs in self.boxes for b in bxs]
self.is_english = re.search(r"[\na-zA-Z0-9,/¸;:'\[\]\(\)!@#$%^&*\"?<>._-]{30,}", "".join([b["text"] for b in random.choices(bxes, k=min(30, len(bxes)))])) self.is_english = re.search(r"[ \na-zA-Z0-9,/¸;:'\[\]\(\)!@#$%^&*\"?<>._-]{30,}", "".join([b["text"] for b in random.choices(bxes, k=min(30, len(bxes)))]))
logging.debug(f"Is it English: {self.is_english}") logging.debug(f"Is it English: {self.is_english}")

View file

@ -2072,6 +2072,7 @@ Retrieves chunks from specified datasets.
- `"cross_languages"`: `list[string]` - `"cross_languages"`: `list[string]`
- `"metadata_condition"`: `object` - `"metadata_condition"`: `object`
- `"use_kg"`: `boolean` - `"use_kg"`: `boolean`
- `"toc_enhance"`: `boolean`
##### Request example ##### Request example
```bash ```bash
@ -2122,6 +2123,8 @@ curl --request POST \
The number of chunks engaged in vector cosine computation. Defaults to `1024`. The number of chunks engaged in vector cosine computation. Defaults to `1024`.
- `"use_kg"`: (*Body parameter*), `boolean` - `"use_kg"`: (*Body parameter*), `boolean`
The search includes text chunks related to the knowledge graph of the selected dataset to handle complex multi-hop queries. Defaults to `False`. The search includes text chunks related to the knowledge graph of the selected dataset to handle complex multi-hop queries. Defaults to `False`.
- `"toc_enhance"`: (*Body parameter*), `boolean`
The search includes table of content enhancement in order to boost rank of relevant chunks. Files parsed with `TOC Enhance` enabled is prerequisite. Defaults to `False`.
- `"rerank_id"`: (*Body parameter*), `integer` - `"rerank_id"`: (*Body parameter*), `integer`
The ID of the rerank model. The ID of the rerank model.
- `"keyword"`: (*Body parameter*), `boolean` - `"keyword"`: (*Body parameter*), `boolean`
@ -2136,6 +2139,9 @@ curl --request POST \
The languages that should be translated into, in order to achieve keywords retrievals in different languages. The languages that should be translated into, in order to achieve keywords retrievals in different languages.
- `"metadata_condition"`: (*Body parameter*), `object` - `"metadata_condition"`: (*Body parameter*), `object`
The metadata condition used for filtering chunks: The metadata condition used for filtering chunks:
- `"logic"`: (*Body parameter*), `string`
- `"and"` Intersection of the result from each condition (default).
- `"or"` union of the result from each condition.
- `"conditions"`: (*Body parameter*), `array` - `"conditions"`: (*Body parameter*), `array`
A list of metadata filter conditions. A list of metadata filter conditions.
- `"name"`: `string` - The metadata field name to filter by, e.g., `"author"`, `"company"`, `"url"`. Ensure this parameter before use. See [Set metadata](../guides/dataset/set_metadata.md) for details. - `"name"`: `string` - The metadata field name to filter by, e.g., `"author"`, `"company"`, `"url"`. Ensure this parameter before use. See [Set metadata](../guides/dataset/set_metadata.md) for details.

View file

@ -437,16 +437,16 @@ def not_title(txt):
return re.search(r"[,;,。;!!]", txt) return re.search(r"[,;,。;!!]", txt)
def tree_merge(bull, sections, depth): def tree_merge(bull, sections, depth):
if not sections or bull < 0: if not sections or bull < 0:
return sections return sections
if isinstance(sections[0], type("")): if isinstance(sections[0], type("")):
sections = [(s, "") for s in sections] sections = [(s, "") for s in sections]
# filter out position information in pdf sections # filter out position information in pdf sections
sections = [(t, o) for t, o in sections if sections = [(t, o) for t, o in sections if
t and len(t.split("@")[0].strip()) > 1 and not re.match(r"[0-9]+$", t.split("@")[0].strip())] t and len(t.split("@")[0].strip()) > 1 and not re.match(r"[0-9]+$", t.split("@")[0].strip())]
def get_level(bull, section): def get_level(bull, section):
text, layout = section text, layout = section
text = re.sub(r"\u3000", " ", text).strip() text = re.sub(r"\u3000", " ", text).strip()
@ -465,7 +465,7 @@ def tree_merge(bull, sections, depth):
level, text = get_level(bull, section) level, text = get_level(bull, section)
if not text.strip("\n"): if not text.strip("\n"):
continue continue
lines.append((level, text)) lines.append((level, text))
level_set.add(level) level_set.add(level)
@ -608,6 +608,26 @@ def naive_merge(sections: str | list, chunk_token_num=128, delimiter="\n。
cks[-1] += t cks[-1] += t
tk_nums[-1] += tnum tk_nums[-1] += tnum
custom_delimiters = [m.group(1) for m in re.finditer(r"`([^`]+)`", delimiter)]
has_custom = bool(custom_delimiters)
if has_custom:
custom_pattern = "|".join(re.escape(t) for t in sorted(set(custom_delimiters), key=len, reverse=True))
cks, tk_nums = [], []
for sec, pos in sections:
split_sec = re.split(r"(%s)" % custom_pattern, sec, flags=re.DOTALL)
for sub_sec in split_sec:
if re.fullmatch(custom_pattern, sub_sec or ""):
continue
text = "\n" + sub_sec
local_pos = pos
if num_tokens_from_string(text) < 8:
local_pos = ""
if local_pos and text.find(local_pos) < 0:
text += local_pos
cks.append(text)
tk_nums.append(num_tokens_from_string(text))
return cks
dels = get_delimiters(delimiter) dels = get_delimiters(delimiter)
for sec, pos in sections: for sec, pos in sections:
if num_tokens_from_string(sec) < chunk_token_num: if num_tokens_from_string(sec) < chunk_token_num:
@ -657,6 +677,29 @@ def naive_merge_with_images(texts, images, chunk_token_num=128, delimiter="\n。
result_images[-1] = concat_img(result_images[-1], image) result_images[-1] = concat_img(result_images[-1], image)
tk_nums[-1] += tnum tk_nums[-1] += tnum
custom_delimiters = [m.group(1) for m in re.finditer(r"`([^`]+)`", delimiter)]
has_custom = bool(custom_delimiters)
if has_custom:
custom_pattern = "|".join(re.escape(t) for t in sorted(set(custom_delimiters), key=len, reverse=True))
cks, result_images, tk_nums = [], [], []
for text, image in zip(texts, images):
text_str = text[0] if isinstance(text, tuple) else text
text_pos = text[1] if isinstance(text, tuple) and len(text) > 1 else ""
split_sec = re.split(r"(%s)" % custom_pattern, text_str)
for sub_sec in split_sec:
if re.fullmatch(custom_pattern, sub_sec or ""):
continue
text_seg = "\n" + sub_sec
local_pos = text_pos
if num_tokens_from_string(text_seg) < 8:
local_pos = ""
if local_pos and text_seg.find(local_pos) < 0:
text_seg += local_pos
cks.append(text_seg)
result_images.append(image)
tk_nums.append(num_tokens_from_string(text_seg))
return cks, result_images
dels = get_delimiters(delimiter) dels = get_delimiters(delimiter)
for text, image in zip(texts, images): for text, image in zip(texts, images):
# if text is tuple, unpack it # if text is tuple, unpack it
@ -748,6 +791,23 @@ def naive_merge_docx(sections, chunk_token_num=128, delimiter="\n。"):
images[-1] = concat_img(images[-1], image) images[-1] = concat_img(images[-1], image)
tk_nums[-1] += tnum tk_nums[-1] += tnum
custom_delimiters = [m.group(1) for m in re.finditer(r"`([^`]+)`", delimiter)]
has_custom = bool(custom_delimiters)
if has_custom:
custom_pattern = "|".join(re.escape(t) for t in sorted(set(custom_delimiters), key=len, reverse=True))
cks, images, tk_nums = [], [], []
pattern = r"(%s)" % custom_pattern
for sec, image in sections:
split_sec = re.split(pattern, sec)
for sub_sec in split_sec:
if not sub_sec or re.fullmatch(custom_pattern, sub_sec):
continue
text_seg = "\n" + sub_sec
cks.append(text_seg)
images.append(image)
tk_nums.append(num_tokens_from_string(text_seg))
return cks, images
dels = get_delimiters(delimiter) dels = get_delimiters(delimiter)
pattern = r"(%s)" % dels pattern = r"(%s)" % dels
@ -789,7 +849,7 @@ class Node:
self.level = level self.level = level
self.depth = depth self.depth = depth
self.texts = texts or [] self.texts = texts or []
self.children = [] self.children = []
def add_child(self, child_node): def add_child(self, child_node):
self.children.append(child_node) self.children.append(child_node)
@ -835,7 +895,7 @@ class Node:
return self return self
def get_tree(self): def get_tree(self):
tree_list = [] tree_list = []
self._dfs(self, tree_list, []) self._dfs(self, tree_list, [])
return tree_list return tree_list
@ -860,7 +920,7 @@ class Node:
# A leaf title within depth emits its title path as a chunk (header-only section) # A leaf title within depth emits its title path as a chunk (header-only section)
elif not child and (1 <= level <= self.depth): elif not child and (1 <= level <= self.depth):
tree_list.append("\n".join(path_titles)) tree_list.append("\n".join(path_titles))
# Recurse into children with the updated title path # Recurse into children with the updated title path
for c in child: for c in child:
self._dfs(c, tree_list, path_titles) self._dfs(c, tree_list, path_titles)

View file

@ -1,6 +1,9 @@
import { ModelVariableType } from '@/constants/knowledge'; import {
ModelVariableType,
settledModelVariableMap,
} from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-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';
@ -25,6 +28,13 @@ import { useHandleFreedomChange } from './use-watch-change';
interface LlmSettingFieldItemsProps { interface LlmSettingFieldItemsProps {
prefix?: string; prefix?: string;
options?: any[]; options?: any[];
showFields?: Array<
| 'temperature'
| 'top_p'
| 'presence_penalty'
| 'frequency_penalty'
| 'max_tokens'
>;
} }
export const LLMIdFormField = { export const LLMIdFormField = {
@ -56,6 +66,13 @@ export const LlmSettingSchema = {
export function LlmSettingFieldItems({ export function LlmSettingFieldItems({
prefix, prefix,
options, options,
showFields = [
'temperature',
'top_p',
'presence_penalty',
'frequency_penalty',
'max_tokens',
],
}: LlmSettingFieldItemsProps) { }: LlmSettingFieldItemsProps) {
const form = useFormContext(); const form = useFormContext();
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
@ -72,14 +89,53 @@ 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 { label: string; value: ModelVariableType | 'Custom' }[];
parameterOptions.push({
label: t(camelCase('Custom')),
value: 'Custom',
});
const checkParameterIsEqual = () => {
const [
parameter,
topPValue,
frequencyPenaltyValue,
temperatureValue,
presencePenaltyValue,
maxTokensValue,
] = form.getValues([
getFieldWithPrefix('parameter'),
getFieldWithPrefix('temperature'),
getFieldWithPrefix('top_p'),
getFieldWithPrefix('frequency_penalty'),
getFieldWithPrefix('presence_penalty'),
getFieldWithPrefix('max_tokens'),
]);
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,
max_tokens: maxTokensValue,
};
if (!isEqual(parameterValue, parameterRealValue)) {
form.setValue(getFieldWithPrefix('parameter'), 'Custom');
}
}
};
return ( return (
<div className="space-y-5"> <div className="space-y-5">
<LLMFormField options={options}></LLMFormField> <LLMFormField
options={options}
name={getFieldWithPrefix('llm_id')}
></LLMFormField>
<FormField <FormField
control={form.control} control={form.control}
name={'parameter'} name={getFieldWithPrefix('parameter')}
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex justify-between items-center"> <FormItem className="flex justify-between items-center">
<FormLabel className="flex-1">{t('freedom')}</FormLabel> <FormLabel className="flex-1">{t('freedom')}</FormLabel>
@ -107,45 +163,71 @@ export function LlmSettingFieldItems({
</FormItem> </FormItem>
)} )}
/> />
<SliderInputSwitchFormField {showFields.some((item) => item === 'temperature') && (
name={getFieldWithPrefix('temperature')} <SliderInputSwitchFormField
checkName="temperatureEnabled" name={getFieldWithPrefix('temperature')}
label="temperature" checkName="temperatureEnabled"
max={1} label="temperature"
step={0.01} max={1}
min={0} step={0.01}
></SliderInputSwitchFormField> min={0}
<SliderInputSwitchFormField onChange={() => {
name={getFieldWithPrefix('top_p')} checkParameterIsEqual();
checkName="topPEnabled" }}
label="topP" ></SliderInputSwitchFormField>
max={1} )}
step={0.01} {showFields.some((item) => item === 'top_p') && (
min={0} <SliderInputSwitchFormField
></SliderInputSwitchFormField> name={getFieldWithPrefix('top_p')}
<SliderInputSwitchFormField checkName="topPEnabled"
name={getFieldWithPrefix('presence_penalty')} label="topP"
checkName="presencePenaltyEnabled" max={1}
label="presencePenalty" step={0.01}
max={1} min={0}
step={0.01} onChange={() => {
min={0} checkParameterIsEqual();
></SliderInputSwitchFormField> }}
<SliderInputSwitchFormField ></SliderInputSwitchFormField>
name={getFieldWithPrefix('frequency_penalty')} )}
checkName="frequencyPenaltyEnabled" {showFields.some((item) => item === 'presence_penalty') && (
label="frequencyPenalty" <SliderInputSwitchFormField
max={1} name={getFieldWithPrefix('presence_penalty')}
step={0.01} checkName="presencePenaltyEnabled"
min={0} label="presencePenalty"
></SliderInputSwitchFormField> max={1}
<SliderInputSwitchFormField step={0.01}
name={getFieldWithPrefix('max_tokens')} min={0}
checkName="maxTokensEnabled" onChange={() => {
label="maxTokens" checkParameterIsEqual();
max={128000} }}
min={0} ></SliderInputSwitchFormField>
></SliderInputSwitchFormField> )}
{showFields.some((item) => item === 'frequency_penalty') && (
<SliderInputSwitchFormField
name={getFieldWithPrefix('frequency_penalty')}
checkName="frequencyPenaltyEnabled"
label="frequencyPenalty"
max={1}
step={0.01}
min={0}
onChange={() => {
checkParameterIsEqual();
}}
></SliderInputSwitchFormField>
)}
{showFields.some((item) => item === 'max_tokens') && (
<SliderInputSwitchFormField
name={getFieldWithPrefix('max_tokens')}
checkName="maxTokensEnabled"
numberInputClassName="w-20"
label="maxTokens"
max={128000}
min={0}
onChange={() => {
checkParameterIsEqual();
}}
></SliderInputSwitchFormField>
)}
</div> </div>
); );
} }

View file

@ -22,6 +22,7 @@ type SliderInputSwitchFormFieldProps = {
onChange?: (value: number) => void; onChange?: (value: number) => void;
className?: string; className?: string;
checkName: string; checkName: string;
numberInputClassName?: string;
}; };
export function SliderInputSwitchFormField({ export function SliderInputSwitchFormField({
@ -34,6 +35,7 @@ export function SliderInputSwitchFormField({
onChange, onChange,
className, className,
checkName, checkName,
numberInputClassName,
}: SliderInputSwitchFormFieldProps) { }: SliderInputSwitchFormFieldProps) {
const form = useFormContext(); const form = useFormContext();
const disabled = !form.watch(checkName); const disabled = !form.watch(checkName);
@ -81,7 +83,10 @@ export function SliderInputSwitchFormField({
<FormControl> <FormControl>
<NumberInput <NumberInput
disabled={disabled} disabled={disabled}
className="h-7 w-20" className={cn(
'h-6 w-10 p-1 border border-border-button rounded-sm',
numberInputClassName,
)}
max={max} max={max}
min={min} min={min}
step={step} step={step}

View file

@ -32,13 +32,13 @@ const Input = function ({
type={type} type={type}
data-slot="input" data-slot="input"
className={cn( className={cn(
'border-input file:text-foreground placeholder:text-muted-foreground/70 flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50', 'border-border-button file:text-foreground placeholder:text-text-disabled flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]', 'focus-visible:border-border-button focus-visible:ring-text-primary/50 focus-visible:ring-1',
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive', 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
type === 'search' && type === 'search' &&
'[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none', '[&::-webkit-search-cancel-button]:appearance-none [&::-webkit-search-decoration]:appearance-none [&::-webkit-search-results-button]:appearance-none [&::-webkit-search-results-decoration]:appearance-none',
type === 'file' && type === 'file' &&
'text-muted-foreground/70 file:border-input file:text-foreground p-0 pr-3 italic file:me-3 file:h-full file:border-0 file:border-r file:border-solid file:bg-transparent file:px-3 file:text-sm file:font-medium file:not-italic', 'text-text-disabled file:border-input file:text-foreground p-0 pr-3 italic file:me-3 file:h-full file:border-0 file:border-r file:border-solid file:bg-transparent file:px-3 file:text-sm file:font-medium file:not-italic',
icon && iconPosition === 'left' && 'pl-7', icon && iconPosition === 'left' && 'pl-7',
icon && iconPosition === 'right' && 'pr-7', icon && iconPosition === 'right' && 'pr-7',
className, className,

View file

@ -221,10 +221,12 @@ const RaptorFormFields = ({
defaultValue={0} defaultValue={0}
type="number" type="number"
suffix={ suffix={
<Shuffle <div className="w-7 flex justify-center items-center">
className="size-3.5 cursor-pointer" <Shuffle
onClick={handleGenerate} className="size-3.5 cursor-pointer"
/> onClick={handleGenerate}
/>
</div>
} }
/> />
</FormControl> </FormControl>

View file

@ -59,6 +59,7 @@ interface SimilaritySliderFormFieldProps {
similarityName?: string; similarityName?: string;
vectorSimilarityWeightName?: string; vectorSimilarityWeightName?: string;
isTooltipShown?: boolean; isTooltipShown?: boolean;
numberInputClassName?: string;
} }
export const initialSimilarityThresholdValue = { export const initialSimilarityThresholdValue = {
@ -86,6 +87,7 @@ export function SimilaritySliderFormField({
similarityName = 'similarity_threshold', similarityName = 'similarity_threshold',
vectorSimilarityWeightName = 'vector_similarity_weight', vectorSimilarityWeightName = 'vector_similarity_weight',
isTooltipShown, isTooltipShown,
numberInputClassName,
}: SimilaritySliderFormFieldProps) { }: SimilaritySliderFormFieldProps) {
const { t } = useTranslate('knowledgeDetails'); const { t } = useTranslate('knowledgeDetails');
const form = useFormContext(); const form = useFormContext();
@ -101,6 +103,7 @@ export function SimilaritySliderFormField({
step={0.01} step={0.01}
layout={FormLayout.Vertical} layout={FormLayout.Vertical}
tooltip={isTooltipShown && t('similarityThresholdTip')} tooltip={isTooltipShown && t('similarityThresholdTip')}
numberInputClassName={numberInputClassName}
></SliderInputFormField> ></SliderInputFormField>
<FormField <FormField
control={form.control} control={form.control}
@ -124,7 +127,7 @@ export function SimilaritySliderFormField({
isVector ? 'vectorSimilarityWeight' : 'keywordSimilarityWeight', isVector ? 'vectorSimilarityWeight' : 'keywordSimilarityWeight',
)} )}
</FormLabel> </FormLabel>
<div className={cn('flex items-end gap-14 justify-between')}> <div className={cn('flex items-end gap-4 justify-between')}>
<FormControl> <FormControl>
<div className="flex flex-col flex-1 gap-2"> <div className="flex flex-col flex-1 gap-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
@ -158,6 +161,7 @@ export function SimilaritySliderFormField({
className={cn( className={cn(
'h-6 w-10 p-0 text-center bg-bg-input border-border-default border text-text-secondary', 'h-6 w-10 p-0 text-center bg-bg-input border-border-default border text-text-secondary',
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none', '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
numberInputClassName,
)} )}
max={1} max={1}
min={0} min={0}

View file

@ -25,6 +25,7 @@ type SliderInputFormFieldProps = {
tooltip?: ReactNode; tooltip?: ReactNode;
defaultValue?: number; defaultValue?: number;
className?: string; className?: string;
numberInputClassName?: string;
} & FormLayoutType; } & FormLayoutType;
export function SliderInputFormField({ export function SliderInputFormField({
@ -36,6 +37,7 @@ export function SliderInputFormField({
tooltip, tooltip,
defaultValue, defaultValue,
className, className,
numberInputClassName,
layout = FormLayout.Horizontal, layout = FormLayout.Horizontal,
}: SliderInputFormFieldProps) { }: SliderInputFormFieldProps) {
const form = useFormContext(); const form = useFormContext();
@ -61,7 +63,7 @@ export function SliderInputFormField({
</FormLabel> </FormLabel>
<div <div
className={cn( className={cn(
'flex items-center gap-14 justify-between', 'flex items-center gap-4 justify-between',
{ 'w-3/4': isHorizontal }, { 'w-3/4': isHorizontal },
className, className,
)} )}
@ -82,6 +84,7 @@ export function SliderInputFormField({
className={cn( className={cn(
'h-6 w-10 p-0 text-center bg-bg-input border border-border-default text-text-secondary', 'h-6 w-10 p-0 text-center bg-bg-input border border-border-default text-text-secondary',
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none', '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
numberInputClassName,
)} )}
max={max} max={max}
min={min} min={min}

View file

@ -47,7 +47,7 @@ const CommandInput = React.forwardRef<
<CommandPrimitive.Input <CommandPrimitive.Input
ref={ref} ref={ref}
className={cn( className={cn(
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', 'flex min-h-8 w-full rounded-md bg-transparent py-2 text-sm outline-none placeholder:text-text-secondary disabled:cursor-not-allowed disabled:opacity-50',
className, className,
)} )}
{...props} {...props}

View file

@ -17,7 +17,7 @@ const Divider: React.FC<DividerProps> = ({
direction = 'horizontal', direction = 'horizontal',
type = 'horizontal', type = 'horizontal',
text, text,
color = 'border-muted-foreground/50', color = 'border-border-button',
margin = 'my-4', margin = 'my-4',
className = '', className = '',
}) => { }) => {

View file

@ -2,7 +2,7 @@ import * as React from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Eye, EyeOff, Search } from 'lucide-react'; import { Eye, EyeOff, Search } from 'lucide-react';
import { useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
export interface InputProps export interface InputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> { extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
@ -17,6 +17,20 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
const { defaultValue, ...restProps } = props; const { defaultValue, ...restProps } = props;
const inputValue = isControlled ? value : defaultValue; const inputValue = isControlled ? value : defaultValue;
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const [prefixWidth, setPrefixWidth] = useState(0);
const [suffixWidth, setSuffixWidth] = useState(0);
const prefixRef = useRef<HTMLSpanElement>(null);
const suffixRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
if (prefixRef.current) {
setPrefixWidth(prefixRef.current.offsetWidth);
}
if (suffixRef.current) {
setSuffixWidth(suffixRef.current.offsetWidth);
}
}, [prefix, suffix, prefixRef, suffixRef]);
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => { const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
if (type === 'number') { if (type === 'number') {
const numValue = e.target.value === '' ? '' : Number(e.target.value); const numValue = e.target.value === '' ? '' : Number(e.target.value);
@ -34,42 +48,60 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
const isPasswordInput = type === 'password'; const isPasswordInput = type === 'password';
const inputEl = ( const inputEl = useMemo(
<input () => (
ref={ref} <input
type={isPasswordInput && showPassword ? 'text' : type} ref={ref}
className={cn( type={isPasswordInput && showPassword ? 'text' : type}
'peer/input', className={cn(
'flex h-8 w-full rounded-md border-0.5 border-border-button bg-bg-input px-3 py-2 outline-none text-sm text-text-primary', 'peer/input',
'file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-text-disabled', 'flex h-8 w-full rounded-md border-0.5 border-border-button bg-bg-input px-3 py-2 outline-none text-sm text-text-primary',
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent-primary', 'file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-text-disabled',
'disabled:cursor-not-allowed disabled:opacity-50 transition-colors', 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent-primary',
{ 'disabled:cursor-not-allowed disabled:opacity-50 transition-colors',
'pl-[calc(1em+1.25rem)]': !!prefix, type === 'number' &&
'pr-[calc(1em+1.25rem)]': !!suffix || isPasswordInput, '[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
'pr-[calc(2em+2rem)]': !!suffix && isPasswordInput, className,
}, )}
type === 'number' && style={{
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none', paddingLeft: !!prefix ? `${prefixWidth}px` : '',
className, paddingRight: isPasswordInput
)} ? '40px'
value={inputValue ?? ''} : !!suffix
onChange={handleChange} ? `${suffixWidth}px`
{...restProps} : '',
/> }}
value={inputValue ?? ''}
onChange={handleChange}
{...restProps}
/>
),
[
prefixWidth,
suffixWidth,
isPasswordInput,
inputValue,
className,
handleChange,
restProps,
],
); );
if (prefix || suffix || isPasswordInput) { if (prefix || suffix || isPasswordInput) {
return ( return (
<div className="relative"> <div className="relative">
{prefix && ( {prefix && (
<span className="absolute left-0 top-[50%] translate-y-[-50%]"> <span
ref={prefixRef}
className="absolute left-0 top-[50%] translate-y-[-50%]"
>
{prefix} {prefix}
</span> </span>
)} )}
{inputEl} {inputEl}
{suffix && ( {suffix && (
<span <span
ref={suffixRef}
className={cn('absolute right-0 top-[50%] translate-y-[-50%]', { className={cn('absolute right-0 top-[50%] translate-y-[-50%]', {
'right-14': isPasswordInput, 'right-14': isPasswordInput,
})} })}

View file

@ -289,12 +289,12 @@ export const MultiSelect = React.forwardRef<
{...props} {...props}
onClick={handleTogglePopover} onClick={handleTogglePopover}
className={cn( className={cn(
'flex w-full p-1 rounded-md border min-h-10 h-auto items-center justify-between bg-inherit hover:bg-inherit [&_svg]:pointer-events-auto', 'flex w-full p-1 rounded-md border border-border-button min-h-10 h-auto placeholder:text-text-disabled items-center justify-between bg-bg-input hover:bg-bg-input [&_svg]:pointer-events-auto',
className, className,
)} )}
> >
{selectedValues.length > 0 ? ( {selectedValues.length > 0 ? (
<div className="flex justify-between items-center w-full"> <div className="flex justify-between items-center w-full group">
<div className="flex flex-wrap items-center"> <div className="flex flex-wrap items-center">
{selectedValues?.slice(0, maxCount)?.map((value) => { {selectedValues?.slice(0, maxCount)?.map((value) => {
const option = flatOptions.find((o) => o.value === value); const option = flatOptions.find((o) => o.value === value);
@ -348,9 +348,9 @@ export const MultiSelect = React.forwardRef<
</Badge> </Badge>
)} )}
</div> </div>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between ">
<XIcon <XIcon
className="h-4 mx-2 cursor-pointer text-muted-foreground" className="h-4 mx-2 cursor-pointer text-text-secondary hidden group-hover:block"
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
handleClear(); handleClear();
@ -358,17 +358,17 @@ export const MultiSelect = React.forwardRef<
/> />
<Separator <Separator
orientation="vertical" orientation="vertical"
className="flex min-h-6 h-full" className="min-h-6 h-full hidden group-hover:flex"
/> />
<ChevronDown className="h-4 mx-2 cursor-pointer text-muted-foreground" /> <ChevronDown className="h-4 mx-2 cursor-pointer text-text-secondary" />
</div> </div>
</div> </div>
) : ( ) : (
<div className="flex items-center justify-between w-full mx-auto"> <div className="flex items-center justify-between w-full mx-auto">
<span className="text-sm text-muted-foreground mx-3"> <span className="text-sm text-text-secondary mx-3">
{placeholder} {placeholder}
</span> </span>
<ChevronDown className="h-4 cursor-pointer text-muted-foreground mx-2" /> <ChevronDown className="h-4 cursor-pointer text-text-secondary mx-2" />
</div> </div>
)} )}
</Button> </Button>
@ -379,14 +379,16 @@ export const MultiSelect = React.forwardRef<
onEscapeKeyDown={() => setIsPopoverOpen(false)} onEscapeKeyDown={() => setIsPopoverOpen(false)}
> >
<Command className="p-5 pb-8"> <Command className="p-5 pb-8">
<CommandInput {options && options.length > 0 && (
placeholder={t('common.search') + '...'} <CommandInput
onKeyDown={handleInputKeyDown} placeholder={t('common.search') + '...'}
/> onKeyDown={handleInputKeyDown}
/>
)}
<CommandList className="mt-2"> <CommandList className="mt-2">
<CommandEmpty>No results found.</CommandEmpty> <CommandEmpty>No results found.</CommandEmpty>
<CommandGroup> <CommandGroup>
{showSelectAll && ( {showSelectAll && options && options.length > 0 && (
<CommandItem <CommandItem
key="all" key="all"
onSelect={toggleAll} onSelect={toggleAll}
@ -454,12 +456,14 @@ export const MultiSelect = React.forwardRef<
/> />
</> </>
)} )}
<CommandItem {options && options.length > 0 && (
onSelect={() => setIsPopoverOpen(false)} <CommandItem
className="flex-1 justify-center cursor-pointer max-w-full" onSelect={() => setIsPopoverOpen(false)}
> className="flex-1 justify-center cursor-pointer max-w-full"
{t('common.close')} >
</CommandItem> {t('common.close')}
</CommandItem>
)}
</div> </div>
</CommandGroup> </CommandGroup>
</CommandList> </CommandList>

View file

@ -1,236 +0,0 @@
import { SliderInputSwitchFormField } from '@/components/llm-setting-items/slider';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import {
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
LlmModelType,
ModelVariableType,
settledModelVariableMap,
} from '@/constants/knowledge';
import { useTranslate } from '@/hooks/common-hooks';
import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks';
import { camelCase, isEqual } from 'lodash';
import { useCallback } from 'react';
import { useFormContext } from 'react-hook-form';
import { z } from 'zod';
interface LlmSettingFieldItemsProps {
prefix?: string;
options?: any[];
}
const LlmSettingEnableSchema = {
temperatureEnabled: z.boolean(),
topPEnabled: z.boolean(),
presencePenaltyEnabled: z.boolean(),
frequencyPenaltyEnabled: z.boolean(),
};
export const LlmSettingSchema = {
llm_id: z.string(),
parameter: z.string().optional(),
temperature: z.coerce.number().optional(),
top_p: z.coerce.number().optional(),
presence_penalty: z.coerce.number().optional(),
frequency_penalty: z.coerce.number().optional(),
...LlmSettingEnableSchema,
// maxTokensEnabled: z.boolean(),
};
export function LlmSettingFieldItems({
prefix,
options,
}: LlmSettingFieldItemsProps) {
const form = useFormContext();
const { t } = useTranslate('chat');
const modelOptions = useComposeLlmOptionsByModelTypes([
LlmModelType.Chat,
LlmModelType.Image2text,
]);
const handleChange = useCallback(
(parameter: string) => {
const values =
settledModelVariableMap[
parameter as keyof typeof settledModelVariableMap
];
const enabledKeys = Object.keys(LlmSettingEnableSchema);
for (const key in values) {
if (Object.prototype.hasOwnProperty.call(values, key)) {
const element = values[key as keyof typeof values];
form.setValue(`${prefix}.${key}`, element);
}
}
if (enabledKeys && enabledKeys.length) {
for (const key of enabledKeys) {
form.setValue(`${prefix}.${key}`, true);
}
}
},
[form, prefix],
);
const parameterOptions = Object.values(ModelVariableType).map((x) => ({
label: t(camelCase(x)),
value: x,
})) as unknown as { label: string; value: ModelVariableType | 'Custom' }[];
parameterOptions.push({
label: t(camelCase('Custom')),
value: 'Custom',
});
const getFieldWithPrefix = useCallback(
(name: string) => {
return prefix ? `${prefix}.${name}` : name;
},
[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 (
<div className="space-y-5">
<FormField
control={form.control}
name={getFieldWithPrefix('llm_id')}
render={({ field }) => (
<FormItem>
<FormLabel>
<span className="text-destructive mr-1"> *</span>
{t('model')}
</FormLabel>
<FormControl>
<SelectWithSearch
options={options || modelOptions}
triggerClassName="!bg-bg-input"
{...field}
></SelectWithSearch>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={getFieldWithPrefix('parameter')}
render={({ field }) => (
<FormItem className="flex justify-between gap-4 items-center">
<FormLabel>{t('freedom')}</FormLabel>
<FormControl>
<div className="w-28">
<Select
{...field}
onValueChange={(val) => {
handleChange(val);
field.onChange(val);
}}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{parameterOptions.map((x) => (
<SelectItem value={x.value} key={x.value}>
{x.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<SliderInputSwitchFormField
name={getFieldWithPrefix('temperature')}
checkName={getFieldWithPrefix('temperatureEnabled')}
label="temperature"
max={1}
min={0}
step={0.01}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField>
<SliderInputSwitchFormField
name={getFieldWithPrefix('top_p')}
checkName={getFieldWithPrefix('topPEnabled')}
label="topP"
max={1}
step={0.01}
min={0}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField>
<SliderInputSwitchFormField
name={getFieldWithPrefix('presence_penalty')}
checkName={getFieldWithPrefix('presencePenaltyEnabled')}
label="presencePenalty"
max={1}
step={0.01}
min={0}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField>
<SliderInputSwitchFormField
name={getFieldWithPrefix('frequency_penalty')}
checkName={getFieldWithPrefix('frequencyPenaltyEnabled')}
label="frequencyPenalty"
max={1}
step={0.01}
min={0}
onChange={() => {
checkParameterIsEquel();
}}
></SliderInputSwitchFormField>
{/* <SliderInputSwitchFormField
name={getFieldWithPrefix('max_tokens')}
checkName="maxTokensEnabled"
label="maxTokens"
max={128000}
></SliderInputSwitchFormField> */}
</div>
);
}

View file

@ -1,6 +1,10 @@
// src/pages/next-search/search-setting.tsx // src/pages/next-search/search-setting.tsx
import { AvatarUpload } from '@/components/avatar-upload'; import { AvatarUpload } from '@/components/avatar-upload';
import {
LlmSettingFieldItems,
LlmSettingSchema,
} from '@/components/llm-setting-items/next';
import { import {
MetadataFilter, MetadataFilter,
MetadataFilterSchema, MetadataFilterSchema,
@ -46,10 +50,10 @@ import {
IllmSettingProps, IllmSettingProps,
useUpdateSearch, useUpdateSearch,
} from '../next-searches/hooks'; } from '../next-searches/hooks';
import { // import {
LlmSettingFieldItems, // LlmSettingFieldItems,
LlmSettingSchema, // LlmSettingSchema,
} from './search-setting-aisummery-config'; // } from './search-setting-aisummery-config';
interface SearchSettingProps { interface SearchSettingProps {
open: boolean; open: boolean;
@ -397,6 +401,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
isTooltipShown isTooltipShown
similarityName="search_config.similarity_threshold" similarityName="search_config.similarity_threshold"
vectorSimilarityWeightName="search_config.vector_similarity_weight" vectorSimilarityWeightName="search_config.vector_similarity_weight"
numberInputClassName="rounded-sm"
></SimilaritySliderFormField> ></SimilaritySliderFormField>
{/* Rerank Model */} {/* Rerank Model */}
<FormField <FormField
@ -462,7 +467,7 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
<FormControl> <FormControl>
<Input <Input
type={'number'} type={'number'}
className="h-7 w-20 bg-bg-card" className="h-7 w-20 bg-bg-card border border-border-button rounded-sm"
max={2048} max={2048}
min={0} min={0}
step={1} step={1}
@ -493,9 +498,19 @@ const SearchSetting: React.FC<SearchSettingProps> = ({
)} )}
/> />
{aiSummaryDisabled && ( {aiSummaryDisabled && (
// <LlmSettingFieldItems
// prefix="search_config.llm_setting"
// options={aiSummeryModelOptions}
// ></LlmSettingFieldItems>
<LlmSettingFieldItems <LlmSettingFieldItems
prefix="search_config.llm_setting" prefix="search_config.llm_setting"
options={aiSummeryModelOptions} options={aiSummeryModelOptions}
showFields={[
'temperature',
'top_p',
'presence_penalty',
'frequency_penalty',
]}
></LlmSettingFieldItems> ></LlmSettingFieldItems>
)} )}
{/* Feature Controls */} {/* Feature Controls */}