mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-25 01:43:27 +08:00
Feat: Refact pipeline (#13826)
### What problem does this PR solve? ### Type of change - [x] New Feature (non-breaking change which adds functionality) - [x] Refactoring --------- Co-authored-by: Zhichang Yu <yuzhichang@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ export type LLMFormFieldProps = {
|
||||
name?: string;
|
||||
testId?: string;
|
||||
optionTestIdPrefix?: string;
|
||||
config?: any;
|
||||
};
|
||||
|
||||
export const useModelOptions = () => {
|
||||
@@ -26,6 +27,7 @@ export function LLMFormField({
|
||||
name,
|
||||
testId,
|
||||
optionTestIdPrefix,
|
||||
config,
|
||||
}: LLMFormFieldProps) {
|
||||
const { t } = useTranslation();
|
||||
const { modelOptions } = useModelOptions();
|
||||
@@ -36,6 +38,7 @@ export function LLMFormField({
|
||||
options={options || modelOptions}
|
||||
testId={testId}
|
||||
optionTestIdPrefix={optionTestIdPrefix}
|
||||
{...config}
|
||||
></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
|
||||
@@ -70,8 +70,8 @@ export enum DataflowOperator {
|
||||
Note = 'Note',
|
||||
Parser = 'Parser',
|
||||
Tokenizer = 'Tokenizer',
|
||||
Splitter = 'Splitter',
|
||||
HierarchicalMerger = 'HierarchicalMerger',
|
||||
TokenChunker = 'TokenChunker',
|
||||
TitleChunker = 'TitleChunker',
|
||||
Extractor = 'Extractor',
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ export enum Operator {
|
||||
File = 'File', // pipeline
|
||||
Parser = 'Parser',
|
||||
Tokenizer = 'Tokenizer',
|
||||
Splitter = 'Splitter',
|
||||
HierarchicalMerger = 'HierarchicalMerger',
|
||||
TokenChunker = 'TokenChunker',
|
||||
TitleChunker = 'TitleChunker',
|
||||
Extractor = 'Extractor',
|
||||
Loop = 'Loop',
|
||||
LoopStart = 'LoopItem',
|
||||
|
||||
@@ -2063,12 +2063,10 @@ export default {
|
||||
tokenizerRequired: 'الرجاء إضافة عقدة المفهرس أولاً',
|
||||
tokenizerDescription:
|
||||
'يحول النص إلى بنية البيانات المطلوبة (على سبيل المثال، التضمينات المتجهة لبحث التضمين) اعتمادًا على طريقة البحث المختارة.',
|
||||
splitter: 'رمز مميز',
|
||||
splitterDescription:
|
||||
tokenChunkerDescription:
|
||||
'قم بتقسيم النص إلى أجزاء حسب طول الرمز المميز باستخدام محددات وتداخلات اختيارية.',
|
||||
hierarchicalMergerDescription:
|
||||
titleChunkerDescription:
|
||||
'قم بتقسيم المستندات إلى أقسام حسب التسلسل الهرمي للعناوين باستخدام قواعد regex للتحكم بشكل أفضل.',
|
||||
hierarchicalMerger: 'عنوان',
|
||||
extractor: 'محول',
|
||||
extractorDescription:
|
||||
'استخدم LLM لاستخراج الرؤى المنظمة من أجزاء المستند، مثل الملخصات والتصنيفات وما إلى ذلك.',
|
||||
@@ -2080,6 +2078,8 @@ export default {
|
||||
image: 'صورة',
|
||||
email: 'بريد إلكتروني',
|
||||
'text&markdown': 'النص والترميز',
|
||||
code: 'Code',
|
||||
html: 'HTML',
|
||||
word: 'كلمة',
|
||||
slides: 'PPTX',
|
||||
audio: 'صوتي',
|
||||
|
||||
@@ -2118,12 +2118,10 @@ The above is the content you need to summarize.`,
|
||||
tokenizerRequired: 'Моля, първо добавете възел Индексатор',
|
||||
tokenizerDescription:
|
||||
'Трансформира текст в необходимата структура от данни (напр. векторни вграждания за Embedding Search) в зависимост от избрания метод за търсене.',
|
||||
splitter: 'Токен',
|
||||
splitterDescription:
|
||||
tokenChunkerDescription:
|
||||
'Разделя текст на фрагменти по дължина на токени с незадължителни разделители и припокриване.',
|
||||
hierarchicalMergerDescription:
|
||||
titleChunkerDescription:
|
||||
'Разделя документи на секции по йерархия на заглавия с regex правила за по-фин контрол.',
|
||||
hierarchicalMerger: 'Заглавие',
|
||||
extractor: 'Трансформатор',
|
||||
extractorDescription:
|
||||
'Използва LLM за извличане на структурирани прозрения от фрагменти на документи — като обобщения, класификации и др.',
|
||||
@@ -2135,6 +2133,8 @@ The above is the content you need to summarize.`,
|
||||
image: 'Изображение',
|
||||
email: 'Имейл',
|
||||
'text&markdown': 'Текст и маркиране',
|
||||
code: 'Code',
|
||||
html: 'HTML',
|
||||
word: 'Word',
|
||||
slides: 'PPTX',
|
||||
audio: 'Аудио',
|
||||
|
||||
@@ -2176,12 +2176,10 @@ Dieser Prozess aggregiert Variablen aus mehreren Zweigen in eine einzelne Variab
|
||||
tokenizerRequired: 'Bitte fügen Sie zuerst den Indexer-Knoten hinzu',
|
||||
tokenizerDescription:
|
||||
'Transformiert Text in die erforderliche Datenstruktur (z.B. Vektoreinbettungen für die Embedding-Suche) abhängig von der gewählten Suchmethode.',
|
||||
splitter: 'Token',
|
||||
splitterDescription:
|
||||
tokenChunkerDescription:
|
||||
'Teilt Text in Chunks nach Token-Länge mit optionalen Trennzeichen und Überlappung.',
|
||||
hierarchicalMergerDescription:
|
||||
titleChunkerDescription:
|
||||
'Teilt Dokumente in Abschnitte nach Titelhierarchie mit Regex-Regeln für feinere Kontrolle.',
|
||||
hierarchicalMerger: 'Titel',
|
||||
extractor: 'Transformer',
|
||||
extractorDescription:
|
||||
'Verwendet ein LLM, um strukturierte Erkenntnisse aus Dokument-Chunks zu extrahieren – wie Zusammenfassungen, Klassifizierungen usw.',
|
||||
@@ -2193,6 +2191,8 @@ Dieser Prozess aggregiert Variablen aus mehreren Zweigen in eine einzelne Variab
|
||||
image: 'Bild',
|
||||
email: 'E-Mail',
|
||||
'text&markdown': 'Text & Markup',
|
||||
code: 'Code',
|
||||
html: 'HTML',
|
||||
word: 'Word',
|
||||
slides: 'PPTX',
|
||||
audio: 'Audio',
|
||||
|
||||
@@ -2228,12 +2228,12 @@ This process aggregates variables from multiple branches into a single variable
|
||||
tokenizerRequired: 'Please add the Indexer node first',
|
||||
tokenizerDescription:
|
||||
'Transforms text into the required data structure (e.g., vector embeddings for Embedding Search) depending on the chosen search method.',
|
||||
splitter: 'Token',
|
||||
splitterDescription:
|
||||
tokenChunker: 'Token Chunker',
|
||||
tokenChunkerDescription:
|
||||
'Split text into chunks by token length with optional delimiters and overlap.',
|
||||
hierarchicalMergerDescription:
|
||||
titleChunkerDescription:
|
||||
'Split documents into sections by title hierarchy with regex rules for finer control.',
|
||||
hierarchicalMerger: 'Title',
|
||||
titleChunker: 'Title Chunker',
|
||||
extractor: 'Transformer',
|
||||
extractorDescription:
|
||||
'Use an LLM to extract structured insights from document chunks—such as summaries, classifications, etc.',
|
||||
@@ -2245,6 +2245,8 @@ This process aggregates variables from multiple branches into a single variable
|
||||
image: 'Image',
|
||||
email: 'Email',
|
||||
'text&markdown': 'Text & Markup',
|
||||
code: 'Code',
|
||||
html: 'HTML',
|
||||
word: 'Word',
|
||||
slides: 'PPTX',
|
||||
audio: 'Audio',
|
||||
|
||||
@@ -326,7 +326,7 @@ Prosedürel Bellek: Öğrenilen beceriler, alışkanlıklar ve otomatik prosedü
|
||||
action: 'İşlem',
|
||||
parsingStatus: 'Ayrıştırma durumu',
|
||||
parsingStatusTip:
|
||||
'Belge ayrıştırma süresi çeşitli faktörlere bağlıdır. Bilgi Grafiği, RAPTOR, Otomatik Soru Çıkarma veya Otomatik Anahtar Kelime Çıkarma gibi özelliklerin etkinleştirilmesi işlem süresini önemli ölçüde artıracaktır. İlerleme çubuğu durursa, lütfen şu SSS\'ye başvurun: https://ragflow.io/docs/dev/faq#why-does-my-document-parsing-stall-at-under-one-percent.',
|
||||
"Belge ayrıştırma süresi çeşitli faktörlere bağlıdır. Bilgi Grafiği, RAPTOR, Otomatik Soru Çıkarma veya Otomatik Anahtar Kelime Çıkarma gibi özelliklerin etkinleştirilmesi işlem süresini önemli ölçüde artıracaktır. İlerleme çubuğu durursa, lütfen şu SSS'ye başvurun: https://ragflow.io/docs/dev/faq#why-does-my-document-parsing-stall-at-under-one-percent.",
|
||||
processBeginAt: 'Başlangıç zamanı',
|
||||
processDuration: 'Süre',
|
||||
progressMsg: 'İlerleme',
|
||||
@@ -370,7 +370,7 @@ Prosedürel Bellek: Öğrenilen beceriler, alışkanlıklar ve otomatik prosedü
|
||||
toMessage: 'Bitiş sayfa numarası eksik (hariç)',
|
||||
layoutRecognize: 'PDF ayrıştırıcı',
|
||||
layoutRecognizeTip:
|
||||
'PDF düzen analizi için görsel model kullanın. Belge başlıklarını, metin bloklarını, görüntüleri ve tabloları etkili bir şekilde konumlandırır. Naive seçeneği seçilirse yalnızca PDF\'deki düz metin alınır. Bu seçeneğin şu anda YALNIZCA PDF belgeleri için çalıştığını lütfen unutmayın.',
|
||||
"PDF düzen analizi için görsel model kullanın. Belge başlıklarını, metin bloklarını, görüntüleri ve tabloları etkili bir şekilde konumlandırır. Naive seçeneği seçilirse yalnızca PDF'deki düz metin alınır. Bu seçeneğin şu anda YALNIZCA PDF belgeleri için çalıştığını lütfen unutmayın.",
|
||||
taskPageSize: 'Görev sayfa boyutu',
|
||||
taskPageSizeMessage: 'Lütfen görev sayfa boyutunu girin!',
|
||||
taskPageSizeTip: `Düzen tanıma sırasında bir PDF dosyası parçalara bölünür ve işleme hızını artırmak için paralel olarak işlenir. Bu parametre her parçanın boyutunu ayarlar. Daha büyük parça boyutu, sayfalar arasındaki sürekli metni bölme olasılığını azaltır.`,
|
||||
@@ -529,7 +529,8 @@ Prosedürel Bellek: Öğrenilen beceriler, alışkanlıklar ve otomatik prosedü
|
||||
chunkTokenNumberMessage: 'Metin için parça token sayısı gereklidir',
|
||||
embeddingModelTip: `Dataset tarafından kullanılan varsayılan embedding model. Dataset içinde parçalar varken embedding modelini değiştirirken, sistem uyumluluk kontrolü için rastgele birkaç parça örnekler, yeni embedding modeli ile yeniden gömer ve eski ile yeni vektörler arasındaki kosinüs benzerliğini hesaplar. Değiştirmeye ancak örneklerin ortalama benzerliği ≥ 0.9 olduğunda izin verilir. Aksi takdirde, değiştirmeden önce datasetteki tüm parçaları silmeniz gerekir.`,
|
||||
permissionsTip: `'Takım' olarak ayarlandığında, tüm takım üyeleri bu dataset'i yönetebilir.`,
|
||||
chunkTokenNumberTip: 'Bir parça oluşturmak için token eşiğini belirler. Bu eşiğin altındaki tokenlı bir bölüm, token sayısı eşiği aşana kadar sonraki bölümlerle birleştirilir ve bu noktada bir parça oluşturulur. Eşik aşılsa bile bir sınırlayıcı ile karşılaşılmadıkça yeni parça oluşturulmaz.',
|
||||
chunkTokenNumberTip:
|
||||
'Bir parça oluşturmak için token eşiğini belirler. Bu eşiğin altındaki tokenlı bir bölüm, token sayısı eşiği aşana kadar sonraki bölümlerle birleştirilir ve bu noktada bir parça oluşturulur. Eşik aşılsa bile bir sınırlayıcı ile karşılaşılmadıkça yeni parça oluşturulmaz.',
|
||||
chunkMethod: 'Parçalama yöntemi',
|
||||
chunkMethodTip: 'Sağdaki ipuçlarına bakın.',
|
||||
upload: 'Yükle',
|
||||
@@ -654,7 +655,7 @@ Prosedürel Bellek: Öğrenilen beceriler, alışkanlıklar ve otomatik prosedü
|
||||
`,
|
||||
useRaptor: 'RAPTOR',
|
||||
useRaptorTip:
|
||||
'RAPTOR çok adımlı soru-cevap görevleri için kullanılabilir. Dosyalar sayfasına gidin, Oluştur > RAPTOR\'a tıklayarak etkinleştirin. Ayrıntılar için bkz. https://ragflow.io/docs/dev/enable_raptor.',
|
||||
"RAPTOR çok adımlı soru-cevap görevleri için kullanılabilir. Dosyalar sayfasına gidin, Oluştur > RAPTOR'a tıklayarak etkinleştirin. Ayrıntılar için bkz. https://ragflow.io/docs/dev/enable_raptor.",
|
||||
prompt: 'İstem',
|
||||
promptTip:
|
||||
'Görevi tanımlamak, nasıl yanıt vermesi gerektiğini belirtmek ve diğer çeşitli gereksinimleri belirlemek için sistem istemini kullanın. Sistem istemi genellikle LLM için çeşitli veri girdileri olarak hizmet eden anahtarlarla (değişkenlerle) birlikte kullanılır. Kullanacağınız anahtarları göstermek için eğik çizgi `/` veya (x) düğmesini kullanın.',
|
||||
@@ -709,7 +710,7 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
resolutionTip: `Varlık tekilleştirme anahtarı. Etkinleştirildiğinde LLM benzer varlıkları birleştirir - örneğin '2025' ve '2025 yılı' veya 'BT' ve 'Bilgi Teknolojisi' - daha doğru bir grafik oluşturmak için`,
|
||||
community: 'Topluluk raporları',
|
||||
communityTip:
|
||||
'Bir bilgi grafiğinde, topluluk ilişkilerle bağlı varlıkların bir kümesidir. LLM\'in her topluluk için bir özet oluşturmasını sağlayabilirsiniz, bu topluluk raporu olarak bilinir. Daha fazla bilgi için bkz: https://www.microsoft.com/en-us/research/blog/graphrag-improving-global-search-via-dynamic-community-selection/',
|
||||
"Bir bilgi grafiğinde, topluluk ilişkilerle bağlı varlıkların bir kümesidir. LLM'in her topluluk için bir özet oluşturmasını sağlayabilirsiniz, bu topluluk raporu olarak bilinir. Daha fazla bilgi için bkz: https://www.microsoft.com/en-us/research/blog/graphrag-improving-global-search-via-dynamic-community-selection/",
|
||||
theDocumentBeingParsedCannotBeDeleted: 'Ayrıştırılan belge silinemez',
|
||||
lastWeek: 'geçen haftadan',
|
||||
},
|
||||
@@ -907,7 +908,8 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
'Çok adımlı soru-cevap için alım sırasında belirtilen dataset(lerdeki) bilgi grafikleri kullanılsın mı. Etkinleştirildiğinde varlık, ilişki ve topluluk raporu parçaları üzerinde yinelemeli aramalar içererek alım süresini önemli ölçüde artırır.',
|
||||
keyword: 'Anahtar kelime analizi',
|
||||
keywordTip: `Kullanıcının sorularını analiz etmek, alaka düzeyi hesaplaması sırasında vurgulanacak anahtar kelimeleri çıkarmak için LLM kullanın. Uzun sorgularda iyi çalışır ancak yanıt süresini artırır.`,
|
||||
languageTip: 'Belirtilen dille cümle yeniden yazmaya izin verir veya seçilmezse en son soruyu varsayılan olarak kullanır.',
|
||||
languageTip:
|
||||
'Belirtilen dille cümle yeniden yazmaya izin verir veya seçilmezse en son soruyu varsayılan olarak kullanır.',
|
||||
avatarHidden: 'Avatarı gizle',
|
||||
locale: 'Yerel ayar',
|
||||
selectLanguage: 'Dil seçin',
|
||||
@@ -1019,7 +1021,8 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
'Taranacak Google Drive klasör bağlantıları (virgülle ayrılmış).',
|
||||
gmailPrimaryAdminTip:
|
||||
'Gmail / Workspace erişimi olan birincil yönetici e-postası, alan kullanıcılarını listeleme ve varsayılan senkronizasyon hesabı olarak kullanılır.',
|
||||
gmailTokenTip: "Google Console'dan oluşturulan OAuth JSON'ını yükleyin. Yalnızca istemci kimlik bilgilerini içeriyorsa, uzun ömürlü yenileme tokenları oluşturmak için tarayıcı tabanlı doğrulamayı bir kez çalıştırın.",
|
||||
gmailTokenTip:
|
||||
"Google Console'dan oluşturulan OAuth JSON'ını yükleyin. Yalnızca istemci kimlik bilgilerini içeriyorsa, uzun ömürlü yenileme tokenları oluşturmak için tarayıcı tabanlı doğrulamayı bir kez çalıştırın.",
|
||||
dropboxDescription:
|
||||
"Seçilen bir hesaptan dosya ve klasörleri senkronize etmek için Dropbox'ınızı bağlayın.",
|
||||
bitbucketDescription:
|
||||
@@ -1045,12 +1048,13 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
imapDescription:
|
||||
'Bilgi alımı için e-postaları senkronize etmek üzere IMAP posta kutunuza bağlanın.',
|
||||
dropboxAccessTokenTip:
|
||||
'Dropbox Uygulama Konsolunda files.metadata.read, files.content.read ve sharing.read kapsamlarıyla uzun ömürlü erişim token\'ı oluşturun.',
|
||||
"Dropbox Uygulama Konsolunda files.metadata.read, files.content.read ve sharing.read kapsamlarıyla uzun ömürlü erişim token'ı oluşturun.",
|
||||
moodleDescription:
|
||||
"Ders içeriği, forumlar ve kaynakları senkronize etmek için Moodle LMS'nize bağlanın.",
|
||||
moodleUrlTip: 'Moodle örneğinizin temel URL\'si (örn. https://moodle.university.edu). /webservice veya /login eklemeyin.',
|
||||
moodleUrlTip:
|
||||
"Moodle örneğinizin temel URL'si (örn. https://moodle.university.edu). /webservice veya /login eklemeyin.",
|
||||
moodleTokenTip:
|
||||
'Moodle\'da bir web servisi token\'ı oluşturun: Site yönetimi → Sunucu → Web servisleri → Tokenları yönet bölümüne gidin. Kullanıcı senkronize etmek istediğiniz kurslara kayıtlı olmalıdır.',
|
||||
"Moodle'da bir web servisi token'ı oluşturun: Site yönetimi → Sunucu → Web servisleri → Tokenları yönet bölümüne gidin. Kullanıcı senkronize etmek istediğiniz kurslara kayıtlı olmalıdır.",
|
||||
seafileDescription:
|
||||
'SeaFile sunucunuza bağlanarak kitaplıklarınızdaki dosya ve belgeleri senkronize edin.',
|
||||
seafileUrlTip: "Protokol dahil SeaFile sunucunuzun tam URL'si.",
|
||||
@@ -1069,10 +1073,11 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
seafileValidationDirectoryPathRequired: 'Dizin Yolu gereklidir',
|
||||
seafileSyncScopeTip:
|
||||
'Nelerin senkronize edileceğini kontrol eder: ' +
|
||||
'(1) Tüm Hesap - Token\'larınızın erişebildiği tüm kitaplıkları senkronize eder. Hesap API Token\'ı gerektirir. ' +
|
||||
'(2) Tek Kitaplık - Belirli bir kitaplıktaki tüm dosyaları senkronize eder. Kitaplık Kimliği ve Hesap API Token\'ı veya Kitaplık API Token\'ı gerektirir. ' +
|
||||
'(3) Belirli Dizin - Bir kitaplık içindeki belirli bir klasördeki dosyaları senkronize eder. Kitaplık Kimliği, klasör yolu ve kimlik doğrulama token\'ı gerektirir.',
|
||||
seafileTokenTip: 'Hesap düzeyinde SeaFile API token\'ınız. ' +
|
||||
"(1) Tüm Hesap - Token'larınızın erişebildiği tüm kitaplıkları senkronize eder. Hesap API Token'ı gerektirir. " +
|
||||
"(2) Tek Kitaplık - Belirli bir kitaplıktaki tüm dosyaları senkronize eder. Kitaplık Kimliği ve Hesap API Token'ı veya Kitaplık API Token'ı gerektirir. " +
|
||||
"(3) Belirli Dizin - Bir kitaplık içindeki belirli bir klasördeki dosyaları senkronize eder. Kitaplık Kimliği, klasör yolu ve kimlik doğrulama token'ı gerektirir.",
|
||||
seafileTokenTip:
|
||||
"Hesap düzeyinde SeaFile API token'ınız. " +
|
||||
'Hesabınıza görünür tüm kitaplıklara erişim sağlar. ' +
|
||||
'Senkronizasyon kapsamı "Tüm Hesap" olduğunda gereklidir. ' +
|
||||
'"Tek Kitaplık" veya "Belirli Dizin" için bu token\'ı veya Kitaplık API Token\'ını kullanabilirsiniz.',
|
||||
@@ -2174,12 +2179,10 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
tokenizerRequired: 'Lütfen önce İndeksleyici düğümü ekleyin',
|
||||
tokenizerDescription:
|
||||
'Metni seçilen arama yöntemine bağlı olarak gerekli veri yapısına dönüştürür.',
|
||||
splitter: 'Token',
|
||||
splitterDescription:
|
||||
tokenChunkerDescription:
|
||||
'Metni isteğe bağlı sınırlayıcılar ve örtüşme ile token uzunluğuna göre parçalara böler.',
|
||||
hierarchicalMergerDescription:
|
||||
titleChunkerDescription:
|
||||
'Belgeleri daha ince kontrol için regex kurallarıyla başlık hiyerarşisine göre bölümlere böler.',
|
||||
hierarchicalMerger: 'Başlık',
|
||||
extractor: 'Dönüştürücü',
|
||||
extractorDescription:
|
||||
'Belge parçalarından yapılandırılmış içgörüler çıkarmak için LLM kullanır.',
|
||||
@@ -2191,6 +2194,8 @@ Bu otomatik etiketleme özelliği, mevcut datasete alanına özgü bilgi katman
|
||||
image: 'Görüntü',
|
||||
email: 'E-posta',
|
||||
'text&markdown': 'Metin ve Biçimlendirme',
|
||||
code: 'Code',
|
||||
html: 'HTML',
|
||||
word: 'Word',
|
||||
slides: 'PPTX',
|
||||
audio: 'Ses',
|
||||
|
||||
@@ -1927,17 +1927,30 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
tokenizerRequired: '请先添加Tokenizer节点',
|
||||
tokenizerDescription:
|
||||
'根据所选的搜索方法,将文本转换为所需的数据结构(例如,用于嵌入搜索的向量嵌入)。',
|
||||
splitter: '按字符分割',
|
||||
splitterDescription:
|
||||
tokenChunker: '按 Token 分块',
|
||||
tokenChunkerDescription:
|
||||
'根据分词器长度将文本拆分成块,并带有可选的分隔符和重叠。',
|
||||
hierarchicalMergerDescription:
|
||||
titleChunkerDescription:
|
||||
'使用正则表达式规则按标题层次结构将文档拆分成多个部分,以实现更精细的控制。',
|
||||
hierarchicalMerger: '按标题分割',
|
||||
titleChunker: '按标题分块',
|
||||
extractor: '提取器',
|
||||
extractorDescription:
|
||||
'使用 LLM 从文档块(例如摘要、分类等)中提取结构化见解。',
|
||||
outputFormat: '输出格式',
|
||||
fileFormats: '文件类型',
|
||||
fileFormatOptions: {
|
||||
pdf: 'PDF',
|
||||
spreadsheet: '表格',
|
||||
image: '图片',
|
||||
email: '邮件',
|
||||
'text&markdown': '文本与标记',
|
||||
code: '代码',
|
||||
html: 'HTML',
|
||||
word: 'Word',
|
||||
slides: 'PPTX',
|
||||
audio: '音频',
|
||||
video: '视频',
|
||||
},
|
||||
fields: '字段',
|
||||
addParser: '增加解析器',
|
||||
hierarchy: '层次结构',
|
||||
|
||||
@@ -54,6 +54,7 @@ import { RagNode } from './node';
|
||||
import { AgentNode } from './node/agent-node';
|
||||
import { BeginNode } from './node/begin-node';
|
||||
import { CategorizeNode } from './node/categorize-node';
|
||||
import { ChunkerNode } from './node/chunker-node';
|
||||
import { DataOperationsNode } from './node/data-operations-node';
|
||||
import { NextStepDropdown } from './node/dropdown/next-step-dropdown';
|
||||
import { ExitLoopNode } from './node/exit-loop-node';
|
||||
@@ -69,7 +70,6 @@ import ParserNode from './node/parser-node';
|
||||
import { PlaceholderNode } from './node/placeholder-node';
|
||||
import { RetrievalNode } from './node/retrieval-node';
|
||||
import { RewriteNode } from './node/rewrite-node';
|
||||
import { SplitterNode } from './node/splitter-node';
|
||||
import { SwitchNode } from './node/switch-node';
|
||||
import TokenizerNode from './node/tokenizer-node';
|
||||
import { ToolNode } from './node/tool-node';
|
||||
@@ -95,7 +95,7 @@ export const nodeTypes: NodeTypes = {
|
||||
fileNode: FileNode,
|
||||
parserNode: ParserNode,
|
||||
tokenizerNode: TokenizerNode,
|
||||
splitterNode: SplitterNode,
|
||||
chunkerNode: ChunkerNode,
|
||||
contextNode: ExtractorNode,
|
||||
dataOperationsNode: DataOperationsNode,
|
||||
listOperationsNode: ListOperationsNode,
|
||||
|
||||
@@ -11,7 +11,7 @@ import { NodeWrapper } from './node-wrapper';
|
||||
import { ToolBar } from './toolbar';
|
||||
|
||||
type RagNodeProps = NodeProps<IRagNode> & PropsWithChildren;
|
||||
function InnerSplitterNode({
|
||||
function InnerChunkerNode({
|
||||
id,
|
||||
data,
|
||||
isConnectable = true,
|
||||
@@ -47,7 +47,7 @@ function InnerSplitterNode({
|
||||
id={id}
|
||||
name={'Chunker'}
|
||||
label={data.label}
|
||||
icon={<OperatorIcon name={Operator.Splitter}></OperatorIcon>}
|
||||
icon={<OperatorIcon name={data.label as Operator}></OperatorIcon>}
|
||||
></NodeHeader>
|
||||
<LabelCard>{data.name}</LabelCard>
|
||||
</NodeWrapper>
|
||||
@@ -55,4 +55,4 @@ function InnerSplitterNode({
|
||||
);
|
||||
}
|
||||
|
||||
export const SplitterNode = memo(InnerSplitterNode);
|
||||
export const ChunkerNode = memo(InnerChunkerNode);
|
||||
@@ -176,8 +176,8 @@ export function PipelineAccordionOperators({
|
||||
const chunkerOperators = useMemo(() => {
|
||||
return [
|
||||
...restrictSingleOperatorOnCanvas([
|
||||
Operator.Splitter,
|
||||
Operator.HierarchicalMerger,
|
||||
Operator.TokenChunker,
|
||||
Operator.TitleChunker,
|
||||
]),
|
||||
];
|
||||
}, [restrictSingleOperatorOnCanvas]);
|
||||
|
||||
@@ -684,8 +684,8 @@ export const RestrictedUpstreamMap = {
|
||||
[Operator.VariableAssigner]: [Operator.Begin],
|
||||
[Operator.VariableAggregator]: [Operator.Begin],
|
||||
[Operator.Parser]: [Operator.Begin], // pipeline
|
||||
[Operator.Splitter]: [Operator.Begin],
|
||||
[Operator.HierarchicalMerger]: [Operator.Begin],
|
||||
[Operator.TokenChunker]: [Operator.Begin],
|
||||
[Operator.TitleChunker]: [Operator.Begin],
|
||||
[Operator.Tokenizer]: [Operator.Begin],
|
||||
[Operator.Extractor]: [Operator.Begin],
|
||||
[Operator.File]: [Operator.Begin],
|
||||
@@ -732,8 +732,8 @@ export const NodeMap = {
|
||||
[Operator.File]: 'fileNode',
|
||||
[Operator.Parser]: 'parserNode',
|
||||
[Operator.Tokenizer]: 'tokenizerNode',
|
||||
[Operator.Splitter]: 'splitterNode',
|
||||
[Operator.HierarchicalMerger]: 'splitterNode',
|
||||
[Operator.TokenChunker]: 'chunkerNode',
|
||||
[Operator.TitleChunker]: 'chunkerNode',
|
||||
[Operator.Extractor]: 'contextNode',
|
||||
[Operator.DataOperations]: 'dataOperationsNode',
|
||||
[Operator.ListOperations]: 'listOperationsNode',
|
||||
@@ -775,8 +775,8 @@ export const NoDebugOperatorsList = [
|
||||
Operator.File,
|
||||
Operator.Parser,
|
||||
Operator.Tokenizer,
|
||||
Operator.Splitter,
|
||||
Operator.HierarchicalMerger,
|
||||
Operator.TokenChunker,
|
||||
Operator.TitleChunker,
|
||||
Operator.Extractor,
|
||||
Operator.Tool,
|
||||
];
|
||||
@@ -785,8 +785,8 @@ export const NoCopyOperatorsList = [
|
||||
Operator.File,
|
||||
Operator.Parser,
|
||||
Operator.Tokenizer,
|
||||
Operator.Splitter,
|
||||
Operator.HierarchicalMerger,
|
||||
Operator.TokenChunker,
|
||||
Operator.TitleChunker,
|
||||
Operator.Extractor,
|
||||
];
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ export enum FileType {
|
||||
Image = 'image',
|
||||
Email = 'email',
|
||||
TextMarkdown = 'text&markdown',
|
||||
Code = 'code',
|
||||
Html = 'html',
|
||||
Docx = 'word',
|
||||
PowerPoint = 'slides',
|
||||
Video = 'video',
|
||||
@@ -62,6 +64,8 @@ export const OutputFormatMap = {
|
||||
[FileType.Image]: ImageOutputFormat,
|
||||
[FileType.Email]: EmailOutputFormat,
|
||||
[FileType.TextMarkdown]: TextMarkdownOutputFormat,
|
||||
[FileType.Code]: TextMarkdownOutputFormat,
|
||||
[FileType.Html]: TextMarkdownOutputFormat,
|
||||
[FileType.Docx]: DocxOutputFormat,
|
||||
[FileType.PowerPoint]: PptOutputFormat,
|
||||
[FileType.Video]: VideoOutputFormat,
|
||||
@@ -74,6 +78,8 @@ export const InitialOutputFormatMap = {
|
||||
[FileType.Image]: ImageOutputFormat.Text,
|
||||
[FileType.Email]: EmailOutputFormat.Text,
|
||||
[FileType.TextMarkdown]: TextMarkdownOutputFormat.Text,
|
||||
[FileType.Code]: TextMarkdownOutputFormat.Text,
|
||||
[FileType.Html]: TextMarkdownOutputFormat.Text,
|
||||
[FileType.Docx]: DocxOutputFormat.Json,
|
||||
[FileType.PowerPoint]: PptOutputFormat.Json,
|
||||
[FileType.Video]: VideoOutputFormat.Text,
|
||||
@@ -208,6 +214,16 @@ export const initialParserValues = {
|
||||
output_format: TextMarkdownOutputFormat.Text,
|
||||
preprocess: PreprocessValue.main_content,
|
||||
},
|
||||
{
|
||||
fileFormat: FileType.Code,
|
||||
output_format: TextMarkdownOutputFormat.Text,
|
||||
preprocess: PreprocessValue.main_content,
|
||||
},
|
||||
{
|
||||
fileFormat: FileType.Html,
|
||||
output_format: TextMarkdownOutputFormat.Text,
|
||||
preprocess: PreprocessValue.main_content,
|
||||
},
|
||||
{
|
||||
fileFormat: FileType.Docx,
|
||||
output_format: DocxOutputFormat.Json,
|
||||
@@ -222,7 +238,7 @@ export const initialParserValues = {
|
||||
],
|
||||
};
|
||||
|
||||
export const initialSplitterValues = {
|
||||
export const initialTokenChunkerValues = {
|
||||
outputs: {
|
||||
chunks: { type: 'Array<Object>', value: [] },
|
||||
},
|
||||
@@ -239,18 +255,74 @@ export enum Hierarchy {
|
||||
H4 = '4',
|
||||
H5 = '5',
|
||||
}
|
||||
|
||||
export const initialHierarchicalMergerValues = {
|
||||
const rules = [
|
||||
{
|
||||
// levels: [
|
||||
// { expression: '^#[^#]' },
|
||||
// { expression: '^##[^#]' },
|
||||
// { expression: '^###[^#]' },
|
||||
// { expression: '^####[^#]' },
|
||||
// ],
|
||||
levels: [
|
||||
{ expression: '^#[^#]' },
|
||||
{ expression: '^##[^#]' },
|
||||
{ expression: '^###[^#]' },
|
||||
{ expression: '^####[^#]' },
|
||||
],
|
||||
},
|
||||
{
|
||||
levels: [
|
||||
{ expression: '第[零一二三四五六七八九十百0-9]+(分?编|部分)' },
|
||||
{ expression: '第[零一二三四五六七八九十百0-9]+章' },
|
||||
{ expression: '第[零一二三四五六七八九十百0-9]+节' },
|
||||
{ expression: '第[零一二三四五六七八九十百0-9]+条' },
|
||||
{ expression: '[\\((][零一二三四五六七八九十百]+[\\))]' },
|
||||
],
|
||||
},
|
||||
{
|
||||
levels: [
|
||||
{ expression: '第[0-9]+章' },
|
||||
{ expression: '第[0-9]+节' },
|
||||
{ expression: '[0-9]{1,2}[\\. 、]' },
|
||||
{ expression: '[0-9]{1,2}\\.[0-9]{1,2}($|[^a-zA-Z/%~.-])' },
|
||||
{ expression: '[0-9]{1,2}\\.[0-9]{1,2}\\.[0-9]{1,2}' },
|
||||
],
|
||||
},
|
||||
{
|
||||
levels: [
|
||||
{ expression: '第[零一二三四五六七八九十百0-9]+章' },
|
||||
{ expression: '第[零一二三四五六七八九十百0-9]+节' },
|
||||
{ expression: '[零一二三四五六七八九十百]+[ 、]' },
|
||||
{ expression: '[\\((][零一二三四五六七八九十百]+[\\))]' },
|
||||
{ expression: '[\\((][0-9]{,2}[\\))]' },
|
||||
],
|
||||
},
|
||||
{
|
||||
levels: [
|
||||
{
|
||||
expression: 'PART (ONE|TWO|THREE|FOUR|FIVE|SIX|SEVEN|EIGHT|NINE|TEN)',
|
||||
},
|
||||
{ expression: 'Chapter (I+V?|VI*|XI|IX|X)' },
|
||||
{ expression: 'Section [0-9]+' },
|
||||
{ expression: 'Article [0-9]+' },
|
||||
],
|
||||
},
|
||||
];
|
||||
export const initialTitleChunkerValues = {
|
||||
outputs: {
|
||||
chunks: { type: 'Array<Object>', value: [] },
|
||||
},
|
||||
method: 'hierarchy',
|
||||
hierarchy: Hierarchy.H3,
|
||||
levels: [
|
||||
{ expressions: [{ expression: '^#[^#]' }] },
|
||||
{ expressions: [{ expression: '^##[^#]' }] },
|
||||
{ expressions: [{ expression: '^###[^#]' }] },
|
||||
{ expressions: [{ expression: '^####[^#]' }] },
|
||||
],
|
||||
include_heading_content: false,
|
||||
rules: rules,
|
||||
};
|
||||
|
||||
export const initialGroupValues = {
|
||||
method: 'group',
|
||||
hierarchy: '0',
|
||||
include_heading_content: false,
|
||||
rules: rules,
|
||||
};
|
||||
|
||||
export const initialExtractorValues = {
|
||||
@@ -269,6 +341,22 @@ export const FileTypeSuffixMap = {
|
||||
[FileType.Image]: ['jpg', 'jpeg', 'png', 'gif'],
|
||||
[FileType.Email]: ['eml', 'msg'],
|
||||
[FileType.TextMarkdown]: ['md', 'markdown', 'mdx', 'txt'],
|
||||
[FileType.Code]: [
|
||||
'py',
|
||||
'js',
|
||||
'java',
|
||||
'c',
|
||||
'cpp',
|
||||
'h',
|
||||
'php',
|
||||
'go',
|
||||
'ts',
|
||||
'sh',
|
||||
'cs',
|
||||
'kt',
|
||||
'sql',
|
||||
],
|
||||
[FileType.Html]: ['htm', 'html'],
|
||||
[FileType.Docx]: ['doc', 'docx'],
|
||||
[FileType.PowerPoint]: ['pptx', 'ppt'],
|
||||
[FileType.Video]: ['mp4', 'avi', 'mkv'],
|
||||
@@ -293,7 +381,7 @@ export const FileTypeSuffixMap = {
|
||||
|
||||
export const SingleOperators = [
|
||||
Operator.Tokenizer,
|
||||
Operator.Splitter,
|
||||
Operator.HierarchicalMerger,
|
||||
Operator.TokenChunker,
|
||||
Operator.TitleChunker,
|
||||
Operator.Parser,
|
||||
];
|
||||
|
||||
@@ -14,7 +14,6 @@ import ExtractorForm from '../form/extractor-form';
|
||||
import GithubForm from '../form/github-form';
|
||||
import GoogleForm from '../form/google-form';
|
||||
import GoogleScholarForm from '../form/google-scholar-form';
|
||||
import HierarchicalMergerForm from '../form/hierarchical-merger-form';
|
||||
import InvokeForm from '../form/invoke-form';
|
||||
import IterationForm from '../form/iteration-form';
|
||||
import IterationStartForm from '../form/iteration-start-from';
|
||||
@@ -27,11 +26,12 @@ import PubMedForm from '../form/pubmed-form';
|
||||
import RetrievalForm from '../form/retrieval-form/next';
|
||||
import RewriteQuestionForm from '../form/rewrite-question-form';
|
||||
import SearXNGForm from '../form/searxng-form';
|
||||
import SplitterForm from '../form/splitter-form';
|
||||
import StringTransformForm from '../form/string-transform-form';
|
||||
import SwitchForm from '../form/switch-form';
|
||||
import TavilyExtractForm from '../form/tavily-extract-form';
|
||||
import TavilyForm from '../form/tavily-form';
|
||||
import TitleChunkerForm from '../form/title-chunker-form';
|
||||
import TokenChunkerForm from '../form/token-chunker-form';
|
||||
import TokenizerForm from '../form/tokenizer-form';
|
||||
import ToolForm from '../form/tool-form';
|
||||
import UserFillUpForm from '../form/user-fill-up-form';
|
||||
@@ -154,11 +154,11 @@ export const FormConfigMap = {
|
||||
[Operator.Tokenizer]: {
|
||||
component: TokenizerForm,
|
||||
},
|
||||
[Operator.Splitter]: {
|
||||
component: SplitterForm,
|
||||
[Operator.TokenChunker]: {
|
||||
component: TokenChunkerForm,
|
||||
},
|
||||
[Operator.HierarchicalMerger]: {
|
||||
component: HierarchicalMergerForm,
|
||||
[Operator.TitleChunker]: {
|
||||
component: TitleChunkerForm,
|
||||
},
|
||||
[Operator.Extractor]: {
|
||||
component: ExtractorForm,
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { BlockButton, Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import { Form, FormLabel } from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Plus, Trash2 } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useFieldArray, useForm, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
Hierarchy,
|
||||
initialHierarchicalMergerValues,
|
||||
} from '../../constant/pipeline';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
|
||||
const outputList = buildOutputList(initialHierarchicalMergerValues.outputs);
|
||||
|
||||
const HierarchyOptions = [
|
||||
{ label: 'H1', value: Hierarchy.H1 },
|
||||
{ label: 'H2', value: Hierarchy.H2 },
|
||||
{ label: 'H3', value: Hierarchy.H3 },
|
||||
{ label: 'H4', value: Hierarchy.H4 },
|
||||
{ label: 'H5', value: Hierarchy.H5 },
|
||||
];
|
||||
|
||||
export const FormSchema = z.object({
|
||||
hierarchy: z.string(),
|
||||
levels: z.array(
|
||||
z.object({
|
||||
expressions: z.array(
|
||||
z.object({
|
||||
expression: z.string().refine(
|
||||
(val) => {
|
||||
try {
|
||||
// Try converting the string to a RegExp
|
||||
new RegExp(val);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
{
|
||||
message: 'Must be a valid regular expression string',
|
||||
},
|
||||
),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export type HierarchicalMergerFormSchemaType = z.infer<typeof FormSchema>;
|
||||
|
||||
type RegularExpressionsProps = {
|
||||
index: number;
|
||||
parentName: string;
|
||||
removeParent: (index: number) => void;
|
||||
isLatest: boolean;
|
||||
};
|
||||
|
||||
export function RegularExpressions({
|
||||
index,
|
||||
parentName,
|
||||
isLatest,
|
||||
removeParent,
|
||||
}: RegularExpressionsProps) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
|
||||
const name = `${parentName}.${index}.expressions`;
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
name: name,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex-row justify-between items-center">
|
||||
<span>H{index + 1}</span>
|
||||
{isLatest && (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => removeParent(index)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FormLabel required className="mb-2 text-text-secondary">
|
||||
{t('flow.regularExpressions')}
|
||||
</FormLabel>
|
||||
<section className="space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center gap-2">
|
||||
<div className="space-y-2 flex-1">
|
||||
<RAGFlowFormItem
|
||||
name={`${name}.${index}.expression`}
|
||||
label={'expression'}
|
||||
labelClassName="!hidden"
|
||||
>
|
||||
<Input className="!m-0"></Input>
|
||||
</RAGFlowFormItem>
|
||||
</div>
|
||||
{index === 0 ? (
|
||||
<Button
|
||||
onClick={() => append({ expression: '' })}
|
||||
variant={'ghost'}
|
||||
>
|
||||
<Plus></Plus>
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const HierarchicalMergerForm = ({ node }: INextOperatorForm) => {
|
||||
const { t } = useTranslation();
|
||||
const defaultValues = useFormValues(initialHierarchicalMergerValues, node);
|
||||
|
||||
const form = useForm<HierarchicalMergerFormSchemaType>({
|
||||
defaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const name = 'levels';
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
name: name,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
useWatchFormChange(node?.id, form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<RAGFlowFormItem name={'hierarchy'} label={t('flow.hierarchy')}>
|
||||
<SelectWithSearch options={HierarchyOptions}></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center">
|
||||
<div className="flex-1">
|
||||
<RegularExpressions
|
||||
parentName={name}
|
||||
index={index}
|
||||
removeParent={remove}
|
||||
isLatest={index === fields.length - 1}
|
||||
></RegularExpressions>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{fields.length < 5 && (
|
||||
<BlockButton
|
||||
onClick={() => append({ expressions: [{ expression: '' }] })}
|
||||
>
|
||||
{t('common.add')}
|
||||
</BlockButton>
|
||||
)}
|
||||
</FormWrapper>
|
||||
<div className="p-5">
|
||||
<Output list={outputList}></Output>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(HierarchicalMergerForm);
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SelectWithSearchFlagOptionType,
|
||||
} from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { upperCase, upperFirst } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -80,12 +81,55 @@ export function LargeModelFormField({
|
||||
}: CommonProps & Pick<LLMFormFieldProps, 'options'>) {
|
||||
return (
|
||||
<LLMFormField
|
||||
name={buildFieldNameWithPrefix('llm_id', prefix)}
|
||||
name={buildFieldNameWithPrefix('vlm.llm_id', prefix)}
|
||||
options={options}
|
||||
config={{ allowClear: true }}
|
||||
></LLMFormField>
|
||||
);
|
||||
}
|
||||
|
||||
export function TwoColumnCheckFormField({ prefix }: CommonProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={buildFieldNameWithPrefix(`enable_multi_column`, prefix)}
|
||||
label={t('flow.enableMultiColumn', 'Enable multi column')}
|
||||
horizontal={true}
|
||||
labelClassName="w-[200px]"
|
||||
>
|
||||
{(field) => (
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange?.(checked);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
}
|
||||
|
||||
export function RmdirFormField({ prefix }: CommonProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<RAGFlowFormItem
|
||||
name={buildFieldNameWithPrefix(`remove_toc`, prefix)}
|
||||
label={t('flow.remove_toc', 'Remove TOC')}
|
||||
horizontal={true}
|
||||
labelClassName="w-[200px]"
|
||||
>
|
||||
{(field) => (
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange?.(checked);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</RAGFlowFormItem>
|
||||
);
|
||||
}
|
||||
|
||||
export function LanguageFormField({ prefix }: CommonProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
@@ -39,8 +39,13 @@ import { ImageFormFields } from './image-form-fields';
|
||||
import { PdfFormFields } from './pdf-form-fields';
|
||||
import { PptFormFields } from './ppt-form-fields';
|
||||
import { SpreadsheetFormFields } from './spreadsheet-form-fields';
|
||||
import {
|
||||
HtmlFormFields,
|
||||
TextMarkdownFormFields,
|
||||
} from './text-html-form-fields';
|
||||
import { buildFieldNameWithPrefix } from './utils';
|
||||
import { AudioFormFields, VideoFormFields } from './video-form-fields';
|
||||
import { WordFormFields } from './word-form-fields';
|
||||
|
||||
const outputList = buildOutputList(initialParserValues.outputs);
|
||||
|
||||
@@ -72,6 +77,11 @@ const PreprocessOptionConfigsMap: Partial<
|
||||
{ value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
{ value: PreprocessValue.section_title },
|
||||
],
|
||||
[FileType.Code]: [{ value: MAIN_CONTENT_PREPROCESS_VALUE, required: true }],
|
||||
[FileType.Html]: [
|
||||
{ value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
{ value: PreprocessValue.section_title },
|
||||
],
|
||||
[FileType.Docx]: [
|
||||
{ value: MAIN_CONTENT_PREPROCESS_VALUE, required: true },
|
||||
{ value: PreprocessValue.section_title },
|
||||
@@ -116,10 +126,13 @@ const FileFormatWidgetMap = {
|
||||
[FileType.PDF]: PdfFormFields,
|
||||
[FileType.Spreadsheet]: SpreadsheetFormFields,
|
||||
[FileType.PowerPoint]: PptFormFields,
|
||||
[FileType.Docx]: WordFormFields,
|
||||
[FileType.Video]: VideoFormFields,
|
||||
[FileType.Audio]: AudioFormFields,
|
||||
[FileType.Email]: EmailFormFields,
|
||||
[FileType.Image]: ImageFormFields,
|
||||
[FileType.TextMarkdown]: TextMarkdownFormFields,
|
||||
[FileType.Html]: HtmlFormFields,
|
||||
};
|
||||
|
||||
type ParserItemProps = {
|
||||
@@ -139,10 +152,12 @@ export const FormSchema = z.object({
|
||||
parse_method: z.string().optional(),
|
||||
lang: z.string().optional(),
|
||||
fields: z.array(z.string()).optional(),
|
||||
llm_id: z.string().optional(),
|
||||
vlm: z.object({ llm_id: z.string().optional() }).optional(),
|
||||
system_prompt: z.string().optional(),
|
||||
table_result_type: z.string().optional(),
|
||||
markdown_image_response_type: z.string().optional(),
|
||||
enable_multi_column: z.boolean().optional(),
|
||||
remove_toc: z.boolean().optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -4,11 +4,19 @@ import {
|
||||
SelectWithSearchFlagOptionType,
|
||||
} from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { LanguageFormField, ParserMethodFormField } from './common-form-fields';
|
||||
import {
|
||||
LanguageFormField,
|
||||
LargeModelFormField,
|
||||
ParserMethodFormField,
|
||||
RmdirFormField,
|
||||
TwoColumnCheckFormField,
|
||||
} from './common-form-fields';
|
||||
import { CommonProps } from './interface';
|
||||
import { useSetInitialLanguage } from './use-set-initial-language';
|
||||
import { buildFieldNameWithPrefix } from './utils';
|
||||
@@ -28,7 +36,9 @@ export function PdfFormFields({ prefix }: CommonProps) {
|
||||
const form = useFormContext();
|
||||
|
||||
const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix);
|
||||
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
const parseMethod = useWatch({
|
||||
name: parseMethodName,
|
||||
});
|
||||
@@ -88,7 +98,14 @@ export function PdfFormFields({ prefix }: CommonProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TwoColumnCheckFormField prefix={prefix} />
|
||||
<RmdirFormField prefix={prefix} />
|
||||
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
|
||||
|
||||
<LargeModelFormField
|
||||
prefix={prefix}
|
||||
options={modelOptions}
|
||||
></LargeModelFormField>
|
||||
{languageShown && <LanguageFormField prefix={prefix}></LanguageFormField>}
|
||||
{tcadpOptionsShown && (
|
||||
<>
|
||||
|
||||
@@ -4,11 +4,16 @@ import {
|
||||
SelectWithSearchFlagOptionType,
|
||||
} from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ParserMethodFormField } from './common-form-fields';
|
||||
import {
|
||||
LargeModelFormField,
|
||||
ParserMethodFormField,
|
||||
} from './common-form-fields';
|
||||
import { CommonProps } from './interface';
|
||||
import { buildFieldNameWithPrefix } from './utils';
|
||||
|
||||
@@ -25,6 +30,9 @@ const markdownImageResponseTypeOptions: SelectWithSearchFlagOptionType[] = [
|
||||
export function SpreadsheetFormFields({ prefix }: CommonProps) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
|
||||
const parseMethodName = buildFieldNameWithPrefix('parse_method', prefix);
|
||||
|
||||
@@ -89,6 +97,10 @@ export function SpreadsheetFormFields({ prefix }: CommonProps) {
|
||||
prefix={prefix}
|
||||
optionsWithoutLLM={optionsWithoutLLM}
|
||||
></ParserMethodFormField>
|
||||
<LargeModelFormField
|
||||
prefix={prefix}
|
||||
options={modelOptions}
|
||||
></LargeModelFormField>
|
||||
{tcadpOptionsShown && (
|
||||
<>
|
||||
<RAGFlowFormItem
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import { LargeModelFormField, RmdirFormField } from './common-form-fields';
|
||||
import { CommonProps } from './interface';
|
||||
|
||||
export function TextMarkdownFormFields({ prefix }: CommonProps) {
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RmdirFormField prefix={prefix} />
|
||||
<LargeModelFormField
|
||||
prefix={prefix}
|
||||
options={modelOptions}
|
||||
></LargeModelFormField>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function HtmlFormFields({ prefix }: CommonProps) {
|
||||
return <RmdirFormField prefix={prefix} />;
|
||||
}
|
||||
24
web/src/pages/agent/form/parser-form/word-form-fields.tsx
Normal file
24
web/src/pages/agent/form/parser-form/word-form-fields.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { LlmModelType } from '@/constants/knowledge';
|
||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/use-llm-request';
|
||||
import {
|
||||
LargeModelFormField,
|
||||
OutputFormatFormFieldProps,
|
||||
RmdirFormField,
|
||||
} from './common-form-fields';
|
||||
|
||||
export function WordFormFields({ prefix }: OutputFormatFormFieldProps) {
|
||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||
LlmModelType.Image2text,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RmdirFormField prefix={prefix} />
|
||||
{/* Multimodal Model */}
|
||||
<LargeModelFormField
|
||||
prefix={prefix}
|
||||
options={modelOptions}
|
||||
></LargeModelFormField>
|
||||
</>
|
||||
);
|
||||
}
|
||||
197
web/src/pages/agent/form/title-chunker-form/hook.ts
Normal file
197
web/src/pages/agent/form/title-chunker-form/hook.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TitleChunkerFormSchemaType } from '.';
|
||||
import { Hierarchy, initialTitleChunkerValues } from '../../constant/pipeline';
|
||||
|
||||
// type initialValuesType = typeof initialHierarchicalMergerValues;
|
||||
|
||||
function transformLevelsToRules(levels: any[]) {
|
||||
if (!Array.isArray(levels)) {
|
||||
return initialTitleChunkerValues.rules;
|
||||
}
|
||||
|
||||
return levels
|
||||
.map((levelGroup) => {
|
||||
if (Array.isArray(levelGroup)) {
|
||||
const filteredExpressions = levelGroup.filter(
|
||||
(expression: string) => expression && expression.trim() !== '',
|
||||
);
|
||||
if (filteredExpressions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
levels: filteredExpressions.map((expression: string) => ({
|
||||
expression,
|
||||
})),
|
||||
};
|
||||
}
|
||||
return { levels: [{ expression: '' }] };
|
||||
})
|
||||
.filter((rule) => rule !== null);
|
||||
}
|
||||
|
||||
function filterEmptyRules(rules: any[]) {
|
||||
if (!Array.isArray(rules)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return rules
|
||||
.map((rule) => {
|
||||
if (!rule || !Array.isArray(rule.levels)) {
|
||||
return null;
|
||||
}
|
||||
const filteredLevels = rule.levels.filter(
|
||||
(level: any) => level.expression && level.expression.trim() !== '',
|
||||
);
|
||||
if (filteredLevels.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return { levels: filteredLevels };
|
||||
})
|
||||
.filter((rule) => rule !== null);
|
||||
}
|
||||
|
||||
// function isRulesFormatCorrect(rules: any): boolean {
|
||||
// if (!rules || !Array.isArray(rules)) {
|
||||
// return false;
|
||||
// }
|
||||
// if (rules.length === 0) {
|
||||
// return false;
|
||||
// }
|
||||
// if (!rules[0] || typeof rules[0] !== 'object') {
|
||||
// return false;
|
||||
// }
|
||||
// if (!Array.isArray(rules[0].levels)) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
function transformApiResponseToForm(
|
||||
apiData: Record<string, any>,
|
||||
): TitleChunkerFormSchemaType {
|
||||
if (!apiData) {
|
||||
return apiData;
|
||||
}
|
||||
|
||||
if (isEmpty(apiData)) {
|
||||
return apiData as TitleChunkerFormSchemaType;
|
||||
}
|
||||
|
||||
const method = apiData.method as 'hierarchy' | 'group';
|
||||
|
||||
let hierarchy = apiData.hierarchy;
|
||||
if (typeof hierarchy === 'number') {
|
||||
hierarchy = String(hierarchy);
|
||||
}
|
||||
if (method === 'group' && !hierarchy) {
|
||||
hierarchy = '0';
|
||||
}
|
||||
|
||||
let rules = apiData.rules;
|
||||
const hasLevelsData = apiData.levels && Array.isArray(apiData.levels);
|
||||
|
||||
if (hasLevelsData) {
|
||||
rules = transformLevelsToRules(apiData.levels);
|
||||
} else if (rules && Array.isArray(rules)) {
|
||||
rules = filterEmptyRules(rules);
|
||||
}
|
||||
|
||||
// const rulesFormatCorrect = isRulesFormatCorrect(rules);
|
||||
|
||||
// if (method === 'group') {
|
||||
// if (rulesFormatCorrect) {
|
||||
// return {
|
||||
// method,
|
||||
// hierarchy,
|
||||
// rules,
|
||||
// };
|
||||
// }
|
||||
// return {
|
||||
// method,
|
||||
// hierarchy,
|
||||
// rules,
|
||||
// };
|
||||
// }
|
||||
|
||||
// if (rulesFormatCorrect && method === 'hierarchy') {
|
||||
// return {
|
||||
// method,
|
||||
// hierarchy,
|
||||
// rules,
|
||||
// };
|
||||
// }
|
||||
|
||||
return {
|
||||
method,
|
||||
hierarchy,
|
||||
include_heading_content: Boolean(apiData.include_heading_content),
|
||||
rules,
|
||||
};
|
||||
}
|
||||
|
||||
type HierarchyOption = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
function getDynamicHierarchyOptions(maxLevel: number): HierarchyOption[] {
|
||||
if (maxLevel < 1) {
|
||||
maxLevel = 1;
|
||||
}
|
||||
return Array.from({ length: maxLevel }, (_, i) => ({
|
||||
label: `H${i + 1}`,
|
||||
value: String(i + 1) as Hierarchy,
|
||||
}));
|
||||
}
|
||||
|
||||
function calculateMaxLevelCount(
|
||||
rules: Array<{ levels: Array<{ expression: string }> }>,
|
||||
): number {
|
||||
if (!rules || rules.length === 0) {
|
||||
return 1;
|
||||
}
|
||||
return Math.max(...rules.map((rule) => rule.levels.length), 1);
|
||||
}
|
||||
|
||||
export function useDynamicHierarchyOptions(
|
||||
form: UseFormReturn<any>,
|
||||
name: string,
|
||||
): HierarchyOption[] {
|
||||
const { t } = useTranslation();
|
||||
const rules = useWatch({ name, control: form?.control });
|
||||
const method = useWatch({ name: 'method', control: form?.control });
|
||||
const currentHierarchy = form.watch('hierarchy');
|
||||
|
||||
const hierarchyOptions = useMemo(() => {
|
||||
const maxLevelCount = calculateMaxLevelCount(rules);
|
||||
const options = getDynamicHierarchyOptions(maxLevelCount);
|
||||
|
||||
if (method === 'group') {
|
||||
return [
|
||||
{ label: t('common.automatic', 'Automatic'), value: '0' },
|
||||
...options,
|
||||
];
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [method, rules, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentHierarchy || !form) {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxOptionValue = hierarchyOptions[hierarchyOptions.length - 1]?.value;
|
||||
|
||||
if (maxOptionValue && currentHierarchy > maxOptionValue) {
|
||||
form.setValue('hierarchy', maxOptionValue);
|
||||
}
|
||||
}, [currentHierarchy, hierarchyOptions, form]);
|
||||
|
||||
return hierarchyOptions;
|
||||
}
|
||||
|
||||
export { transformApiResponseToForm };
|
||||
376
web/src/pages/agent/form/title-chunker-form/index.tsx
Normal file
376
web/src/pages/agent/form/title-chunker-form/index.tsx
Normal file
@@ -0,0 +1,376 @@
|
||||
import { FormFieldType, RenderField } from '@/components/dynamic-form';
|
||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { BlockButton, Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { useFieldArray, useForm, useFormContext } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
Hierarchy,
|
||||
initialGroupValues,
|
||||
initialTitleChunkerValues,
|
||||
} from '../../constant/pipeline';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
import { transformApiResponseToForm, useDynamicHierarchyOptions } from './hook';
|
||||
|
||||
type FormModeValues = {
|
||||
hierarchy?: string;
|
||||
include_heading_content?: boolean;
|
||||
rules: Array<{ levels: Array<{ expression: string }> }>;
|
||||
};
|
||||
|
||||
const outputList = buildOutputList(initialTitleChunkerValues.outputs);
|
||||
|
||||
const rulesSchema = z.array(
|
||||
z.object({
|
||||
levels: z.array(
|
||||
z.object({
|
||||
expression: z.string().refine(
|
||||
(val) => {
|
||||
try {
|
||||
new RegExp(val);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
{
|
||||
message: 'Must be a valid regular expression string',
|
||||
},
|
||||
),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const FormSchema = z.object({
|
||||
method: z.enum(['hierarchy', 'group']),
|
||||
hierarchy: z.string().optional(),
|
||||
include_heading_content: z.boolean().optional(),
|
||||
rules: rulesSchema,
|
||||
});
|
||||
|
||||
export type TitleChunkerFormSchemaType = z.infer<typeof FormSchema>;
|
||||
|
||||
type LevelItemProps = {
|
||||
index: number;
|
||||
parentName: string;
|
||||
removeParent: (index: number) => void;
|
||||
isLatest: boolean;
|
||||
};
|
||||
|
||||
function LevelItem({
|
||||
index,
|
||||
parentName,
|
||||
isLatest,
|
||||
removeParent,
|
||||
}: LevelItemProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const name = `${parentName}.${index}.expression`;
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1">
|
||||
<RAGFlowFormItem
|
||||
name={name}
|
||||
label={`${t('flow.regularExpressions')} H${index + 1}`}
|
||||
// labelClassName="!hidden"
|
||||
>
|
||||
<Input className="!m-0" />
|
||||
</RAGFlowFormItem>
|
||||
</div>
|
||||
{isLatest && index > 0 && (
|
||||
<Button
|
||||
className="self-end"
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
size="sm"
|
||||
onClick={() => removeParent(index)}
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type CardBodyProps = {
|
||||
cardIndex: number;
|
||||
cardName: string;
|
||||
};
|
||||
|
||||
function CardBody({ cardName }: CardBodyProps) {
|
||||
const { t } = useTranslation();
|
||||
const form = useFormContext();
|
||||
|
||||
const levelsName = `${cardName}.levels`;
|
||||
|
||||
const {
|
||||
fields: levelFields,
|
||||
append: appendLevel,
|
||||
remove: removeLevel,
|
||||
} = useFieldArray({
|
||||
name: levelsName,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
return (
|
||||
<CardContent className="p-4">
|
||||
<div className="space-y-4">
|
||||
{levelFields.map((levelField, levelIndex) => (
|
||||
<LevelItem
|
||||
key={levelField.id}
|
||||
parentName={levelsName}
|
||||
index={levelIndex}
|
||||
removeParent={removeLevel}
|
||||
isLatest={levelIndex === levelFields.length - 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<BlockButton
|
||||
onClick={() => appendLevel({ expression: '' })}
|
||||
className="mt-4"
|
||||
>
|
||||
{t('flow.addLevel', 'Add Level')}
|
||||
</BlockButton>
|
||||
</CardContent>
|
||||
);
|
||||
}
|
||||
|
||||
// type GroupCardBodyProps = {
|
||||
// cardName: string;
|
||||
// };
|
||||
|
||||
// function GroupCardBody({ cardName }: GroupCardBodyProps) {
|
||||
// const { t } = useTranslation();
|
||||
// const form = useFormContext();
|
||||
|
||||
// const levelsName = `${cardName}.levels`;
|
||||
|
||||
// const { fields: levelFields } = useFieldArray({
|
||||
// name: levelsName,
|
||||
// control: form.control,
|
||||
// });
|
||||
|
||||
// return (
|
||||
// <CardContent className="p-4">
|
||||
// <div className="space-y-4">
|
||||
// {levelFields.map((levelField, levelIndex) => (
|
||||
// <RAGFlowFormItem
|
||||
// key={levelField.id}
|
||||
// name={`${levelsName}.${levelIndex}.expression`}
|
||||
// label={`${t('flow.regularExpressions')}`}
|
||||
// >
|
||||
// <Input />
|
||||
// </RAGFlowFormItem>
|
||||
// ))}
|
||||
// </div>
|
||||
// </CardContent>
|
||||
// );
|
||||
// }
|
||||
|
||||
const TitleChunkerForm = ({ node }: INextOperatorForm) => {
|
||||
const { t } = useTranslation();
|
||||
const initialValues = useFormValues(initialTitleChunkerValues, node);
|
||||
|
||||
const hierarchyModeValues = useRef<FormModeValues | null>(null);
|
||||
const groupValues = useRef<FormModeValues | null>(null);
|
||||
|
||||
const form = useForm<TitleChunkerFormSchemaType>({
|
||||
defaultValues: transformApiResponseToForm(initialValues),
|
||||
resolver: zodResolver(FormSchema),
|
||||
mode: 'onChange',
|
||||
});
|
||||
const isInitialized = useRef(false);
|
||||
const initialMode = useRef<string | undefined>(undefined);
|
||||
|
||||
const method = form.watch('method');
|
||||
const name = 'rules';
|
||||
const hierarchyOptions = useDynamicHierarchyOptions(form, name);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInitialized.current) {
|
||||
initialMode.current = method;
|
||||
isInitialized.current = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (method !== initialMode.current) {
|
||||
const currentMode = initialMode.current;
|
||||
const hierarchyValue = form.getValues('hierarchy');
|
||||
const rulesValue = form.getValues('rules');
|
||||
|
||||
if (currentMode === 'hierarchy') {
|
||||
hierarchyModeValues.current = {
|
||||
hierarchy: hierarchyValue,
|
||||
include_heading_content: form.getValues('include_heading_content'),
|
||||
rules: rulesValue,
|
||||
};
|
||||
} else if (currentMode === 'group') {
|
||||
groupValues.current = {
|
||||
hierarchy: hierarchyValue,
|
||||
include_heading_content: form.getValues('include_heading_content'),
|
||||
rules: rulesValue,
|
||||
};
|
||||
}
|
||||
|
||||
initialMode.current = method;
|
||||
|
||||
if (method === 'group') {
|
||||
const modeValues = groupValues.current;
|
||||
form.reset({
|
||||
method: 'group',
|
||||
hierarchy: modeValues?.hierarchy ?? '0',
|
||||
include_heading_content: false,
|
||||
rules: modeValues?.rules || initialGroupValues.rules,
|
||||
});
|
||||
} else {
|
||||
const defaultHierarchy = Hierarchy.H3;
|
||||
let modeValues: FormModeValues | null = null;
|
||||
modeValues = hierarchyModeValues.current;
|
||||
if (modeValues) {
|
||||
form.reset({
|
||||
method: method,
|
||||
hierarchy: modeValues.hierarchy || defaultHierarchy,
|
||||
include_heading_content:
|
||||
modeValues.include_heading_content || false,
|
||||
rules: modeValues.rules,
|
||||
});
|
||||
} else {
|
||||
const newModeValues: FormModeValues = {
|
||||
hierarchy: defaultHierarchy,
|
||||
include_heading_content: false,
|
||||
rules: JSON.parse(JSON.stringify(initialTitleChunkerValues.rules)),
|
||||
};
|
||||
|
||||
form.reset({
|
||||
method: method,
|
||||
hierarchy: defaultHierarchy,
|
||||
include_heading_content: newModeValues.include_heading_content,
|
||||
rules: newModeValues.rules,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [method, form]);
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
name: name,
|
||||
control: form.control,
|
||||
});
|
||||
|
||||
useWatchFormChange(node?.id, form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<RenderField
|
||||
field={{
|
||||
name: 'method',
|
||||
type: FormFieldType.Segmented,
|
||||
label: '',
|
||||
options: [
|
||||
{ label: t('flow.hierarchy'), value: 'hierarchy' },
|
||||
// { label: t('flow.tree', 'Tree'), value: 'tree' },
|
||||
{ label: t('flow.group', 'Group'), value: 'group' },
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<RAGFlowFormItem name={'hierarchy'} label={''}>
|
||||
<SelectWithSearch options={hierarchyOptions}></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
{method === 'hierarchy' && (
|
||||
<RAGFlowFormItem
|
||||
name="include_heading_content"
|
||||
label={t('flow.includeHeadingContent', 'Include heading content')}
|
||||
tooltip={t(
|
||||
'flow.includeHeadingContentTip',
|
||||
'When enabled, content directly under a heading is kept as its own chunk. Child chunks keep only the heading path.',
|
||||
)}
|
||||
horizontal={true}
|
||||
labelClassName="w-[200px]"
|
||||
>
|
||||
{(field) => (
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange?.(checked);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</RAGFlowFormItem>
|
||||
)}
|
||||
{/* {method === 'group' ? (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row justify-between items-center py-3 px-4 border-b bg-muted/20">
|
||||
<span className="font-medium text-sm">
|
||||
{t('flow.rule', 'Rule')} 1
|
||||
</span>
|
||||
</CardHeader>
|
||||
<GroupCardBody cardName={`${name}.0`} />
|
||||
</Card>
|
||||
) : ( */}
|
||||
<div className="space-y-4">
|
||||
{fields.map((cardField, cardIndex) => (
|
||||
<Card key={cardField.id}>
|
||||
<CardHeader className="flex flex-row justify-between items-center py-3 px-4 border-b bg-muted/20">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-sm">
|
||||
{t('flow.rule', 'Rule')} {cardIndex + 1}
|
||||
</span>
|
||||
</div>
|
||||
{fields.length > 1 && (
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
size="sm"
|
||||
onClick={() => remove(cardIndex)}
|
||||
className="h-7 w-7 p-0 text-muted-foreground hover:text-destructive"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardBody
|
||||
cardIndex={cardIndex}
|
||||
cardName={`${name}.${cardIndex}`}
|
||||
/>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
{/* )} */}
|
||||
{/* {method !== 'group' && ( */}
|
||||
<BlockButton
|
||||
onClick={() =>
|
||||
append({
|
||||
levels: [{ expression: '' }],
|
||||
})
|
||||
}
|
||||
className="mt-4"
|
||||
>
|
||||
{t('flow.rule', 'Add Rule')}
|
||||
</BlockButton>
|
||||
{/* )} */}
|
||||
</FormWrapper>
|
||||
<div className="p-5">
|
||||
<Output list={outputList}></Output>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(TitleChunkerForm);
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DelimiterInput } from '@/components/delimiter-form-field';
|
||||
import { FormFieldType, RenderField } from '@/components/dynamic-form';
|
||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
||||
import { BlockButton, Button } from '@/components/ui/button';
|
||||
@@ -10,7 +11,7 @@ import { memo } from 'react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
import { initialSplitterValues } from '../../constant/pipeline';
|
||||
import { initialTokenChunkerValues } from '../../constant/pipeline';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
@@ -18,7 +19,7 @@ import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
|
||||
const outputList = buildOutputList(initialSplitterValues.outputs);
|
||||
const outputList = buildOutputList(initialTokenChunkerValues.outputs);
|
||||
|
||||
export const FormSchema = z.object({
|
||||
chunk_token_size: z.number(),
|
||||
@@ -34,19 +35,27 @@ export const FormSchema = z.object({
|
||||
value: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
overlapped_percent: z.number(), // 0.0 - 0.3 , 0% - 30%
|
||||
overlapped_percent: z.number(),
|
||||
delimiter_mode: z.enum(['token_size', 'delimiter']).optional(),
|
||||
});
|
||||
|
||||
export type SplitterFormSchemaType = z.infer<typeof FormSchema>;
|
||||
export type TokenChunkerFormSchemaType = z.infer<typeof FormSchema>;
|
||||
|
||||
const SplitterForm = ({ node }: INextOperatorForm) => {
|
||||
const defaultValues = useFormValues(initialSplitterValues, node);
|
||||
const TokenChunkerForm = ({ node }: INextOperatorForm) => {
|
||||
const defaultValues = useFormValues(initialTokenChunkerValues, node);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const formDefaultValues = {
|
||||
...defaultValues,
|
||||
delimiter_mode: defaultValues.delimiter_mode || 'token_size',
|
||||
};
|
||||
|
||||
const form = useForm<SplitterFormSchemaType>({
|
||||
defaultValues,
|
||||
defaultValues: formDefaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
|
||||
const delimiterMode = form.watch('delimiter_mode');
|
||||
const name = 'delimiters';
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
@@ -64,52 +73,73 @@ const SplitterForm = ({ node }: INextOperatorForm) => {
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<SliderInputFormField
|
||||
name="chunk_token_size"
|
||||
max={2048}
|
||||
label={t('knowledgeConfiguration.chunkTokenNumber')}
|
||||
></SliderInputFormField>
|
||||
<SliderInputFormField
|
||||
name="overlapped_percent"
|
||||
max={30}
|
||||
min={0}
|
||||
label={t('flow.overlappedPercent')}
|
||||
></SliderInputFormField>
|
||||
<SliderInputFormField
|
||||
name="image_table_context_window"
|
||||
max={256}
|
||||
min={0}
|
||||
label={t('knowledgeConfiguration.imageTableContextWindow')}
|
||||
tooltip={t('knowledgeConfiguration.imageTableContextWindowTip')}
|
||||
></SliderInputFormField>
|
||||
<section>
|
||||
<span className="mb-2 inline-block">{t('flow.delimiters')}</span>
|
||||
<div className="space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center gap-2">
|
||||
<div className="space-y-2 flex-1">
|
||||
<RAGFlowFormItem
|
||||
name={`${name}.${index}.value`}
|
||||
label="delimiter"
|
||||
labelClassName="!hidden"
|
||||
>
|
||||
<DelimiterInput className="!m-0"></DelimiterInput>
|
||||
</RAGFlowFormItem>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
<RenderField
|
||||
field={{
|
||||
name: 'delimiter_mode',
|
||||
type: FormFieldType.Segmented,
|
||||
label: '',
|
||||
options: [
|
||||
{ label: 'Token Size', value: 'token_size' },
|
||||
{ label: t('flow.delimiters'), value: 'delimiter' },
|
||||
],
|
||||
}}
|
||||
/>
|
||||
|
||||
{delimiterMode === 'token_size' && (
|
||||
<>
|
||||
<SliderInputFormField
|
||||
name="chunk_token_size"
|
||||
max={2048}
|
||||
label={t('knowledgeConfiguration.chunkTokenNumber')}
|
||||
/>
|
||||
<SliderInputFormField
|
||||
name="overlapped_percent"
|
||||
max={30}
|
||||
min={0}
|
||||
label={t('flow.overlappedPercent')}
|
||||
/>
|
||||
<SliderInputFormField
|
||||
name="image_table_context_window"
|
||||
max={256}
|
||||
min={0}
|
||||
label={t('knowledgeConfiguration.imageTableContextWindow')}
|
||||
tooltip={t('knowledgeConfiguration.imageTableContextWindowTip')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{delimiterMode === 'delimiter' && (
|
||||
<>
|
||||
<section>
|
||||
<span className="mb-2 inline-block">{t('flow.delimiters')}</span>
|
||||
<div className="space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center gap-2">
|
||||
<div className="space-y-2 flex-1">
|
||||
<RAGFlowFormItem
|
||||
name={`${name}.${index}.value`}
|
||||
label="delimiter"
|
||||
labelClassName="!hidden"
|
||||
>
|
||||
<DelimiterInput className="!m-0"></DelimiterInput>
|
||||
</RAGFlowFormItem>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<BlockButton onClick={() => append({ value: '\n' })}>
|
||||
{t('common.add')}
|
||||
</BlockButton>
|
||||
</section>
|
||||
<BlockButton onClick={() => append({ value: '\n' })}>
|
||||
{t('common.add')}
|
||||
</BlockButton>
|
||||
</>
|
||||
)}
|
||||
|
||||
<fieldset>
|
||||
<div className="mb-2 flex justify-between items-center gap-1">
|
||||
@@ -171,4 +201,4 @@ const SplitterForm = ({ node }: INextOperatorForm) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(SplitterForm);
|
||||
export default memo(TokenChunkerForm);
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
initialGithubValues,
|
||||
initialGoogleScholarValues,
|
||||
initialGoogleValues,
|
||||
initialHierarchicalMergerValues,
|
||||
initialInvokeValues,
|
||||
initialIterationStartValues,
|
||||
initialIterationValues,
|
||||
@@ -38,11 +37,12 @@ import {
|
||||
initialRetrievalValues,
|
||||
initialRewriteQuestionValues,
|
||||
initialSearXNGValues,
|
||||
initialSplitterValues,
|
||||
initialStringTransformValues,
|
||||
initialSwitchValues,
|
||||
initialTavilyExtractValues,
|
||||
initialTavilyValues,
|
||||
initialTitleChunkerValues,
|
||||
initialTokenChunkerValues,
|
||||
initialTokenizerValues,
|
||||
initialUserFillUpValues,
|
||||
initialVariableAggregatorValues,
|
||||
@@ -165,8 +165,8 @@ export const useInitializeOperatorParams = () => {
|
||||
[Operator.File]: {},
|
||||
[Operator.Parser]: initialParserValues,
|
||||
[Operator.Tokenizer]: initialTokenizerValues,
|
||||
[Operator.Splitter]: initialSplitterValues,
|
||||
[Operator.HierarchicalMerger]: initialHierarchicalMergerValues,
|
||||
[Operator.TokenChunker]: initialTokenChunkerValues,
|
||||
[Operator.TitleChunker]: initialTitleChunkerValues,
|
||||
[Operator.Extractor]: {
|
||||
...initialExtractorValues,
|
||||
llm_id: llmId,
|
||||
|
||||
@@ -39,9 +39,9 @@ import {
|
||||
import { BeginFormSchemaType } from './form/begin-form/schema';
|
||||
import { DataOperationsFormSchemaType } from './form/data-operations-form';
|
||||
import { ExtractorFormSchemaType } from './form/extractor-form';
|
||||
import { HierarchicalMergerFormSchemaType } from './form/hierarchical-merger-form';
|
||||
import { ParserFormSchemaType } from './form/parser-form';
|
||||
import { SplitterFormSchemaType } from './form/splitter-form';
|
||||
import { TitleChunkerFormSchemaType } from './form/title-chunker-form';
|
||||
import { TokenChunkerFormSchemaType } from './form/token-chunker-form';
|
||||
import { BeginQuery, IPosition } from './interface';
|
||||
|
||||
function buildAgentExceptionGoto(edges: Edge[], nodeId: string) {
|
||||
@@ -211,7 +211,10 @@ function transformParserParams(params: ParserFormSchemaType) {
|
||||
>((pre, cur) => {
|
||||
if (cur.fileFormat) {
|
||||
let filteredSetup: Partial<
|
||||
ParserFormSchemaType['setups'][0] & { suffix: string[] }
|
||||
ParserFormSchemaType['setups'][0] & { suffix: string[] } & {
|
||||
two_column_check: boolean;
|
||||
enable_multi_column: boolean;
|
||||
}
|
||||
> = {
|
||||
output_format: cur.output_format,
|
||||
preprocess: cur.preprocess,
|
||||
@@ -224,6 +227,9 @@ function transformParserParams(params: ParserFormSchemaType) {
|
||||
...filteredSetup,
|
||||
parse_method: cur.parse_method,
|
||||
lang: cur.lang,
|
||||
vlm: { llm_id: cur.vlm?.llm_id },
|
||||
enable_multi_column: cur.enable_multi_column,
|
||||
remove_toc: cur.remove_toc,
|
||||
};
|
||||
// Only include TCADP parameters if TCADP Parser is selected
|
||||
if (cur.parse_method?.toLowerCase() === 'tcadp parser') {
|
||||
@@ -236,6 +242,7 @@ function transformParserParams(params: ParserFormSchemaType) {
|
||||
filteredSetup = {
|
||||
...filteredSetup,
|
||||
parse_method: cur.parse_method,
|
||||
vlm: { llm_id: cur.vlm?.llm_id },
|
||||
};
|
||||
// Only include TCADP parameters if TCADP Parser is selected
|
||||
if (cur.parse_method?.toLowerCase() === 'tcadp parser') {
|
||||
@@ -271,10 +278,12 @@ function transformParserParams(params: ParserFormSchemaType) {
|
||||
};
|
||||
break;
|
||||
case FileType.Video:
|
||||
case FileType.Docx:
|
||||
case FileType.Audio:
|
||||
case FileType.TextMarkdown:
|
||||
filteredSetup = {
|
||||
...filteredSetup,
|
||||
llm_id: cur.llm_id,
|
||||
vlm: { llm_id: cur.vlm?.llm_id },
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@@ -289,13 +298,16 @@ function transformParserParams(params: ParserFormSchemaType) {
|
||||
return { ...params, setups };
|
||||
}
|
||||
|
||||
function transformSplitterParams(params: SplitterFormSchemaType) {
|
||||
function transformTokenChunkerParams(params: TokenChunkerFormSchemaType) {
|
||||
const { image_table_context_window, ...rest } = params;
|
||||
const imageTableContextWindow = Number(image_table_context_window || 0);
|
||||
return {
|
||||
...rest,
|
||||
overlapped_percent: Number(params.overlapped_percent) / 100,
|
||||
delimiters: transformObjectArrayToPureArray(params.delimiters, 'value'),
|
||||
delimiters:
|
||||
params.delimiter_mode === 'delimiter'
|
||||
? transformObjectArrayToPureArray(params.delimiters, 'value')
|
||||
: [],
|
||||
table_context_size: imageTableContextWindow,
|
||||
image_context_size: imageTableContextWindow,
|
||||
|
||||
@@ -306,14 +318,17 @@ function transformSplitterParams(params: SplitterFormSchemaType) {
|
||||
};
|
||||
}
|
||||
|
||||
function transformHierarchicalMergerParams(
|
||||
params: HierarchicalMergerFormSchemaType,
|
||||
) {
|
||||
const levels = params.levels.map((x) =>
|
||||
transformObjectArrayToPureArray(x.expressions, 'expression'),
|
||||
function transformTitleChunkerParams(params: TitleChunkerFormSchemaType) {
|
||||
const levels = params.rules.map((rule) =>
|
||||
transformObjectArrayToPureArray(rule.levels, 'expression'),
|
||||
);
|
||||
|
||||
return { ...params, hierarchy: Number(params.hierarchy), levels };
|
||||
return {
|
||||
method: params.method,
|
||||
hierarchy: Number(params.hierarchy || 0),
|
||||
include_heading_content: Boolean(params.include_heading_content),
|
||||
levels,
|
||||
};
|
||||
}
|
||||
|
||||
function transformExtractorParams(params: ExtractorFormSchemaType) {
|
||||
@@ -437,12 +452,12 @@ export const buildDslComponentsByGraph = (
|
||||
params = transformParserParams(params);
|
||||
break;
|
||||
|
||||
case Operator.Splitter:
|
||||
params = transformSplitterParams(params);
|
||||
case Operator.TokenChunker:
|
||||
params = transformTokenChunkerParams(params);
|
||||
break;
|
||||
|
||||
case Operator.HierarchicalMerger:
|
||||
params = transformHierarchicalMergerParams(params);
|
||||
case Operator.TitleChunker:
|
||||
params = transformTitleChunkerParams(params);
|
||||
break;
|
||||
case Operator.Extractor:
|
||||
params = transformExtractorParams(params);
|
||||
|
||||
@@ -32,12 +32,12 @@ export const TimelineNodeObj = {
|
||||
title: 'Context Generator',
|
||||
icon: <LucideFileStack className="size-[1em]" />,
|
||||
},
|
||||
[TimelineNodeType.titleSplitter]: {
|
||||
title: 'Title Splitter',
|
||||
[TimelineNodeType.titleChunker]: {
|
||||
title: 'Title Chunker',
|
||||
icon: <LucideHeading className="size-[1em]" />,
|
||||
},
|
||||
[TimelineNodeType.characterSplitter]: {
|
||||
title: 'Character Splitter',
|
||||
[TimelineNodeType.tokenChunker]: {
|
||||
title: 'Token Chunker',
|
||||
icon: <LucideBlocks className="size-[1em]" />,
|
||||
},
|
||||
[TimelineNodeType.tokenizer]: {
|
||||
|
||||
@@ -7,8 +7,8 @@ export enum TimelineNodeType {
|
||||
begin = 'file',
|
||||
parser = 'parser',
|
||||
contextGenerator = 'extractor',
|
||||
titleSplitter = 'hierarchicalMerger',
|
||||
characterSplitter = 'splitter',
|
||||
titleChunker = 'titleChunker',
|
||||
tokenChunker = 'tokenChunker',
|
||||
tokenizer = 'tokenizer',
|
||||
end = 'end',
|
||||
}
|
||||
|
||||
@@ -233,10 +233,10 @@ export const useTimelineDataFlow = (data: IPipelineFileLogDetail) => {
|
||||
} else if (name === TimelineNodeType.tokenizer) {
|
||||
tempType = TimelineNodeType.tokenizer;
|
||||
} else if (
|
||||
name === TimelineNodeType.characterSplitter ||
|
||||
name === TimelineNodeType.titleSplitter
|
||||
name === TimelineNodeType.tokenChunker ||
|
||||
name === TimelineNodeType.titleChunker
|
||||
) {
|
||||
tempType = TimelineNodeType.characterSplitter;
|
||||
tempType = name;
|
||||
}
|
||||
const timeNode = {
|
||||
...TimelineNodeObj[name],
|
||||
|
||||
@@ -205,8 +205,8 @@ const DataflowResult = () => {
|
||||
)} */}
|
||||
{/* {currentTimeNode?.type === TimelineNodeType.parser && ( */}
|
||||
{(currentTimeNode?.type === TimelineNodeType.parser ||
|
||||
currentTimeNode?.type === TimelineNodeType.characterSplitter ||
|
||||
currentTimeNode?.type === TimelineNodeType.titleSplitter ||
|
||||
currentTimeNode?.type === TimelineNodeType.tokenChunker ||
|
||||
currentTimeNode?.type === TimelineNodeType.titleChunker ||
|
||||
currentTimeNode?.type === TimelineNodeType.contextGenerator) && (
|
||||
<ParserContainer
|
||||
isReadonly={isReadOnly}
|
||||
|
||||
@@ -130,8 +130,8 @@ const ParserContainer = (props: IProps) => {
|
||||
);
|
||||
|
||||
const isChunck =
|
||||
step?.type === TimelineNodeType.characterSplitter ||
|
||||
step?.type === TimelineNodeType.titleSplitter;
|
||||
step?.type === TimelineNodeType.tokenChunker ||
|
||||
step?.type === TimelineNodeType.titleChunker;
|
||||
|
||||
const handleCreateChunk = useCallback(
|
||||
(text: string) => {
|
||||
@@ -214,8 +214,8 @@ const ParserContainer = (props: IProps) => {
|
||||
isChunck={isChunck}
|
||||
textMode={textMode}
|
||||
isDelete={
|
||||
step?.type === TimelineNodeType.characterSplitter ||
|
||||
step?.type === TimelineNodeType.titleSplitter
|
||||
step?.type === TimelineNodeType.tokenChunker ||
|
||||
step?.type === TimelineNodeType.titleChunker
|
||||
}
|
||||
clickChunk={clickChunk}
|
||||
handleCheckboxClick={handleCheckboxClick}
|
||||
|
||||
Reference in New Issue
Block a user