Compare commits
9 Commits
v0.22.0
...
ccb255919a
| Author | SHA1 | Date | |
|---|---|---|---|
| ccb255919a | |||
| b68c84b52e | |||
| 93cf0258c3 | |||
| b79fef1ca8 | |||
| 2b50de3186 | |||
| d8ef22db68 | |||
| 592f3b1555 | |||
| 3404469e2a | |||
| 63d7382dc9 |
15
web/src/assets/svg/data-flow/data-icon-bri.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M35.3194 10.6367H20.4258C19.4857 10.6367 18.7236 11.3988 18.7236 12.3388V34.892C18.7236 35.8321 19.4857 36.5942 20.4258 36.5942H35.3194C36.2594 36.5942 37.0215 35.8321 37.0215 34.892V12.3388C37.0215 11.3988 36.2594 10.6367 35.3194 10.6367Z" fill="url(#paint0_linear_488_37636)"/>
|
||||||
|
<path d="M31.0639 4.25391H5.10642C4.16637 4.25391 3.4043 5.01597 3.4043 5.95603V18.2965C3.4043 19.2365 4.16637 19.9986 5.10642 19.9986H31.0639C32.0039 19.9986 32.766 19.2365 32.766 18.2965V5.95603C32.766 5.01597 32.0039 4.25391 31.0639 4.25391Z" fill="#00BEB4" fill-opacity="0.1"/>
|
||||||
|
<path d="M31.0639 4.25391C32.0039 4.25391 32.766 5.01597 32.766 5.95603V18.2965C32.766 19.2365 32.0039 19.9986 31.0639 19.9986H5.10642C4.16637 19.9986 3.4043 19.2365 3.4043 18.2965V5.95603C3.4043 5.01597 4.16637 4.25391 5.10642 4.25391H31.0639ZM31.0639 4.67944H5.10642C4.40138 4.67944 3.82983 5.25099 3.82983 5.95603V18.2965C3.82983 19.0015 4.40138 19.5731 5.10642 19.5731H31.0639C31.7689 19.5731 32.3405 19.0015 32.3405 18.2965V5.95603C32.3405 5.25099 31.7689 4.67944 31.0639 4.67944Z" fill="#00BEB4"/>
|
||||||
|
<path d="M31.0639 22.5547H5.10642C4.16637 22.5547 3.4043 23.3168 3.4043 24.2568V34.8951C3.4043 35.8352 4.16637 36.5972 5.10642 36.5972H31.0639C32.0039 36.5972 32.766 35.8352 32.766 34.8951V24.2568C32.766 23.3168 32.0039 22.5547 31.0639 22.5547Z" fill="#00BEB4" fill-opacity="0.1"/>
|
||||||
|
<path d="M31.0639 22.5547C32.0039 22.5547 32.766 23.3168 32.766 24.2568V34.8951C32.766 35.8352 32.0039 36.5972 31.0639 36.5972H5.10642C4.16637 36.5972 3.4043 35.8352 3.4043 34.8951V24.2568C3.4043 23.3168 4.16637 22.5547 5.10642 22.5547H31.0639ZM31.0639 22.9802H5.10642C4.40138 22.9802 3.82983 23.5518 3.82983 24.2568V34.8951C3.82983 35.6002 4.40138 36.1717 5.10642 36.1717H31.0639C31.7689 36.1717 32.3405 35.6002 32.3405 34.8951V24.2568C32.3405 23.5518 31.7689 22.9802 31.0639 22.9802Z" fill="#00BEB4"/>
|
||||||
|
<path d="M10.6384 14.8949C12.2835 14.8949 13.6171 13.5613 13.6171 11.9162C13.6171 10.2711 12.2835 8.9375 10.6384 8.9375C8.99329 8.9375 7.65967 10.2711 7.65967 11.9162C7.65967 13.5613 8.99329 14.8949 10.6384 14.8949Z" fill="#00BEB4"/>
|
||||||
|
<path d="M10.6384 32.766C12.2835 32.766 13.6171 31.4324 13.6171 29.7873C13.6171 28.1422 12.2835 26.8086 10.6384 26.8086C8.99329 26.8086 7.65967 28.1422 7.65967 29.7873C7.65967 31.4324 8.99329 32.766 10.6384 32.766Z" fill="#00BEB4"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_488_37636" x1="933.617" y1="10.6367" x2="933.617" y2="2606.38" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#C9F1EF"/>
|
||||||
|
<stop offset="1" stop-color="#00BEB4"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
15
web/src/assets/svg/data-flow/data-icon.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M35.3194 10.6387H20.4258C19.4857 10.6387 18.7236 11.4007 18.7236 12.3408V34.894C18.7236 35.834 19.4857 36.5961 20.4258 36.5961H35.3194C36.2594 36.5961 37.0215 35.834 37.0215 34.894V12.3408C37.0215 11.4007 36.2594 10.6387 35.3194 10.6387Z" fill="url(#paint0_linear_491_41413)"/>
|
||||||
|
<path d="M31.0639 4.25586H5.10642C4.16637 4.25586 3.4043 5.01793 3.4043 5.95799V18.2984C3.4043 19.2385 4.16637 20.0005 5.10642 20.0005H31.0639C32.0039 20.0005 32.766 19.2385 32.766 18.2984V5.95799C32.766 5.01793 32.0039 4.25586 31.0639 4.25586Z" fill="#00BEB4" fill-opacity="0.2"/>
|
||||||
|
<path d="M31.0639 4.25586C32.0039 4.25586 32.766 5.01793 32.766 5.95799V18.2984C32.766 19.2385 32.0039 20.0005 31.0639 20.0005H5.10642C4.16637 20.0005 3.4043 19.2385 3.4043 18.2984V5.95799C3.4043 5.01793 4.16637 4.25586 5.10642 4.25586H31.0639ZM31.0639 4.68139H5.10642C4.40138 4.68139 3.82983 5.25294 3.82983 5.95799V18.2984C3.82983 19.0035 4.40138 19.575 5.10642 19.575H31.0639C31.7689 19.575 32.3405 19.0035 32.3405 18.2984V5.95799C32.3405 5.25294 31.7689 4.68139 31.0639 4.68139Z" fill="#226365"/>
|
||||||
|
<path d="M31.0639 22.5527H5.10642C4.16637 22.5527 3.4043 23.3148 3.4043 24.2549V34.8932C3.4043 35.8332 4.16637 36.5953 5.10642 36.5953H31.0639C32.0039 36.5953 32.766 35.8332 32.766 34.8932V24.2549C32.766 23.3148 32.0039 22.5527 31.0639 22.5527Z" fill="#3A9093" fill-opacity="0.2"/>
|
||||||
|
<path d="M31.0639 22.5527C32.0039 22.5527 32.766 23.3148 32.766 24.2549V34.8932C32.766 35.8332 32.0039 36.5953 31.0639 36.5953H5.10642C4.16637 36.5953 3.4043 35.8332 3.4043 34.8932V24.2549C3.4043 23.3148 4.16637 22.5527 5.10642 22.5527H31.0639ZM31.0639 22.9783H5.10642C4.40138 22.9783 3.82983 23.5498 3.82983 24.2549V34.8932C3.82983 35.5982 4.40138 36.1698 5.10642 36.1698H31.0639C31.7689 36.1698 32.3405 35.5982 32.3405 34.8932V24.2549C32.3405 23.5498 31.7689 22.9783 31.0639 22.9783Z" fill="#226365"/>
|
||||||
|
<path d="M10.6384 14.893C12.2835 14.893 13.6171 13.5594 13.6171 11.9143C13.6171 10.2692 12.2835 8.93555 10.6384 8.93555C8.99329 8.93555 7.65967 10.2692 7.65967 11.9143C7.65967 13.5594 8.99329 14.893 10.6384 14.893Z" fill="#3A9093"/>
|
||||||
|
<path d="M10.6384 32.766C12.2835 32.766 13.6171 31.4324 13.6171 29.7873C13.6171 28.1422 12.2835 26.8086 10.6384 26.8086C8.99329 26.8086 7.65967 28.1422 7.65967 29.7873C7.65967 31.4324 8.99329 32.766 10.6384 32.766Z" fill="#3A9093"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_491_41413" x1="933.617" y1="10.6387" x2="933.617" y2="2606.38" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#1B3C3D"/>
|
||||||
|
<stop offset="1" stop-color="#164142"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@ -1 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1756884949583" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11332" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M190.464 489.472h327.68v40.96h-327.68z" fill="#C7DCFE" p-id="11333"></path><path d="M482.34496 516.5056l111.26784-308.20352 38.54336 13.9264L520.86784 530.432z" fill="#C7DCFE" p-id="11334"></path><path d="M620.544 196.608m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" fill="#8FB8FC" p-id="11335"></path><path d="M182.272 509.952m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" fill="#C7DCFE" p-id="11336"></path><path d="M558.65344 520.9088l283.77088 163.84-20.48 35.47136-283.77088-163.84z" fill="#C7DCFE" p-id="11337"></path><path d="M841.728 686.08m-122.88 0a122.88 122.88 0 1 0 245.76 0 122.88 122.88 0 1 0-245.76 0Z" fill="#B3CEFE" p-id="11338"></path><path d="M448.67584 803.77856l49.60256-323.91168 40.48896 6.20544-49.60256 323.91168z" fill="#C7DCFE" p-id="11339"></path><path d="M512 530.432m-143.36 0a143.36 143.36 0 1 0 286.72 0 143.36 143.36 0 1 0-286.72 0Z" fill="#4185FF" p-id="11340"></path><path d="M462.848 843.776m-102.4 0a102.4 102.4 0 1 0 204.8 0 102.4 102.4 0 1 0-204.8 0Z" fill="#8FB8FC" p-id="11341"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
6
web/src/assets/svg/data-flow/processing-icon-bri.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.8074 21.9283L30.4051 33.9033C30.9531 34.667 30.7785 35.7307 30.0148 36.2787C29.7258 36.4865 29.3785 36.5982 29.0223 36.5982H11.8273C10.8871 36.5982 10.125 35.8361 10.125 34.8963C10.125 34.54 10.2367 34.1928 10.4445 33.9033L19.0422 21.9283C19.5902 21.1646 20.6539 20.99 21.4176 21.5385C21.5676 21.6463 21.6996 21.7779 21.8074 21.9283Z" fill="#C6EFED"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.94336 3.39844H34.0285C35.9086 3.39844 37.4328 4.92266 37.4328 6.80273V27.2281C37.4328 29.1082 35.9086 30.6324 34.0285 30.6324H5.94336C4.06328 30.6324 2.53906 29.1082 2.53906 27.2281V6.80273C2.53906 4.92266 4.06328 3.39844 5.94336 3.39844Z" fill="#00BEB4" fill-opacity="0.2"/>
|
||||||
|
<path d="M34.0422 3.40625C35.9223 3.40625 37.4465 4.93047 37.4465 6.81055V27.2359C37.4465 29.116 35.9223 30.6402 34.0422 30.6402H5.95703C4.07695 30.6402 2.55273 29.116 2.55273 27.2359V6.81055C2.55273 4.93047 4.07695 3.40625 5.95703 3.40625H34.0422ZM34.0422 3.83164H5.95703C4.31211 3.83164 2.97852 5.16523 2.97852 6.81055V27.2359C2.97852 28.8812 4.31211 30.2148 5.95703 30.2148H34.0422C35.6871 30.2148 37.0207 28.8812 37.0207 27.2359V6.81055C37.0207 5.16523 35.6871 3.83164 34.0422 3.83164Z" fill="#00BEB4"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9785 11.6797C20.6836 11.6797 21.2551 12.2512 21.2551 12.9562V21.0414C21.2551 21.7465 20.6836 22.318 19.9785 22.318C19.2734 22.318 18.702 21.7465 18.702 21.0414V12.9562C18.702 12.2512 19.2734 11.6797 19.9785 11.6797ZM11.0422 11.6797C11.7473 11.6797 12.3187 12.2512 12.3187 12.9562V21.0414C12.3187 21.7465 11.7473 22.318 11.0422 22.318C10.3371 22.318 9.76562 21.7465 9.76562 21.0414V12.9562C9.76562 12.2512 10.3371 11.6797 11.0422 11.6797ZM28.9145 11.6797C29.6195 11.6797 30.191 12.2512 30.191 12.9562V21.0414C30.191 21.7465 29.6195 22.318 28.9145 22.318C28.2094 22.318 27.6379 21.7465 27.6379 21.0414V12.9562C27.6379 12.2512 28.2094 11.6797 28.9145 11.6797Z" fill="#00BEB4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
6
web/src/assets/svg/data-flow/processing-icon.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.8074 21.9264L30.4051 33.9014C30.9531 34.665 30.7785 35.7287 30.0148 36.2767C29.7258 36.4846 29.3785 36.5963 29.0223 36.5963H11.8273C10.8871 36.5963 10.125 35.8342 10.125 34.8943C10.125 34.5381 10.2367 34.1908 10.4445 33.9014L19.0422 21.9264C19.5902 21.1627 20.6539 20.9881 21.4176 21.5365C21.5676 21.6443 21.6996 21.776 21.8074 21.9264Z" fill="#1C3C3D"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.94336 3.39844H34.0285C35.9086 3.39844 37.4328 4.92266 37.4328 6.80273V27.2281C37.4328 29.1082 35.9086 30.6324 34.0285 30.6324H5.94336C4.06328 30.6324 2.53906 29.1082 2.53906 27.2281V6.80273C2.53906 4.92266 4.06328 3.39844 5.94336 3.39844Z" fill="#00BEB4" fill-opacity="0.2"/>
|
||||||
|
<path d="M34.0422 3.4043C35.9223 3.4043 37.4465 4.92852 37.4465 6.80859V27.234C37.4465 29.1141 35.9223 30.6383 34.0422 30.6383H5.95703C4.07695 30.6383 2.55273 29.1141 2.55273 27.234V6.80859C2.55273 4.92852 4.07695 3.4043 5.95703 3.4043H34.0422ZM34.0422 3.82969H5.95703C4.31211 3.82969 2.97852 5.16328 2.97852 6.80859V27.234C2.97852 28.8793 4.31211 30.2129 5.95703 30.2129H34.0422C35.6871 30.2129 37.0207 28.8793 37.0207 27.234V6.80859C37.0207 5.16328 35.6871 3.82969 34.0422 3.82969Z" fill="#1B3B3C"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9785 11.6797C20.6836 11.6797 21.2551 12.2512 21.2551 12.9562V21.0414C21.2551 21.7465 20.6836 22.318 19.9785 22.318C19.2734 22.318 18.702 21.7465 18.702 21.0414V12.9562C18.702 12.2512 19.2734 11.6797 19.9785 11.6797ZM11.0422 11.6797C11.7473 11.6797 12.3187 12.2512 12.3187 12.9562V21.0414C12.3187 21.7465 11.7473 22.318 11.0422 22.318C10.3371 22.318 9.76562 21.7465 9.76562 21.0414V12.9562C9.76562 12.2512 10.3371 11.6797 11.0422 11.6797ZM28.9145 11.6797C29.6195 11.6797 30.191 12.2512 30.191 12.9562V21.0414C30.191 21.7465 29.6195 22.318 28.9145 22.318C28.2094 22.318 27.6379 21.7465 27.6379 21.0414V12.9562C27.6379 12.2512 28.2094 11.6797 28.9145 11.6797Z" fill="#00BEB4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 36 KiB |
6
web/src/assets/svg/data-flow/total-files-icon-bri.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0291 4.67969C11.8025 4.67969 12.4787 5.20078 12.6752 5.94844L13.3494 8.50937H31.4275C33.1599 8.50937 34.6158 9.81055 34.8103 11.5316L37.0205 31.1062C37.231 32.9746 35.8877 34.6602 34.0193 34.8711C33.8927 34.8852 33.765 34.8926 33.6377 34.8926H6.30289C4.92476 34.8926 3.79547 33.7988 3.75094 32.4215L3.11734 12.7746H3.115L2.90719 6.4375C2.87633 5.49805 3.61304 4.71133 4.5525 4.68047C4.57086 4.68008 4.58961 4.67969 4.60836 4.67969H11.0291Z" fill="#00BEB4" fill-opacity="0.1"/>
|
||||||
|
<path d="M11.0291 4.67969C11.8025 4.67969 12.4787 5.20078 12.6752 5.94844L13.349 8.50937H31.4275C33.1599 8.50937 34.6158 9.81055 34.8103 11.5316L37.0205 31.1062C37.231 32.9746 35.8877 34.6602 34.0193 34.8711C33.8927 34.8852 33.765 34.8926 33.6377 34.8926H6.30289C4.92476 34.8926 3.79547 33.7988 3.75094 32.4215L3.11656 12.7742L2.90719 6.4375C2.87633 5.49805 3.61304 4.71133 4.5525 4.68047L4.58023 4.67969H11.0291ZM11.0291 5.10508H4.59078L4.56656 5.10586C3.86187 5.12891 3.30914 5.71914 3.33219 6.42344L3.54195 12.7605L4.17633 32.4078C4.21344 33.5555 5.15445 34.4668 6.30289 34.4668H33.6377C33.749 34.4668 33.8607 34.4605 33.9716 34.448C35.6064 34.2637 36.7822 32.7887 36.5974 31.1539L34.3873 11.5797C34.2173 10.0734 32.9431 8.93516 31.4275 8.93516H13.0209L12.9377 8.61758L12.2638 6.05703C12.1162 5.49609 11.6091 5.10508 11.0291 5.10508Z" fill="#00BEB4"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.72812 12.7656H36.6539C38.0637 12.7656 39.207 13.9086 39.207 15.3188C39.207 15.4328 39.1992 15.5465 39.184 15.6594L36.9922 31.943C36.7648 33.6324 35.323 34.8934 33.6184 34.8934H6.37969C4.96953 34.8934 3.82617 33.75 3.82617 32.3398C3.82617 32.2102 3.83633 32.0801 3.85586 31.952L6.36367 15.6523C6.61914 13.9914 8.04805 12.7656 9.72812 12.7656Z" fill="#CAF2F0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.98438 14.6172H20.4848C20.899 14.6172 21.2348 14.9529 21.2348 15.3672C21.2348 15.7814 20.899 16.1172 20.4848 16.1172H8.98438C8.57013 16.1172 8.23438 15.7814 8.23438 15.3672C8.23438 14.9529 8.57013 14.6172 8.98438 14.6172Z" fill="#00BEB4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
6
web/src/assets/svg/data-flow/total-files-icon.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0291 4.68164C11.8025 4.68164 12.4787 5.20273 12.6752 5.95039L13.3494 8.51133H31.4275C33.1599 8.51133 34.6158 9.8125 34.8103 11.5336L37.0205 31.1082C37.231 32.9766 35.8877 34.6621 34.0193 34.873C33.8927 34.8871 33.765 34.8945 33.6377 34.8945H6.30289C4.92476 34.8945 3.79547 33.8008 3.75094 32.4234L3.11734 12.7766H3.115L2.90719 6.43945C2.87633 5.5 3.61304 4.71328 4.5525 4.68242C4.57086 4.68203 4.58961 4.68164 4.60836 4.68164H11.0291Z" fill="#1F3232"/>
|
||||||
|
<path d="M11.0291 4.68164C11.8025 4.68164 12.4787 5.20273 12.6752 5.95039L13.349 8.51133H31.4275C33.1599 8.51133 34.6158 9.8125 34.8103 11.5336L37.0205 31.1082C37.231 32.9766 35.8877 34.6621 34.0193 34.873C33.8927 34.8871 33.765 34.8945 33.6377 34.8945H6.30289C4.92476 34.8945 3.79547 33.8008 3.75094 32.4234L3.11656 12.7762L2.90719 6.43945C2.87633 5.5 3.61304 4.71328 4.5525 4.68242L4.58023 4.68164H11.0291ZM11.0291 5.10703H4.59078L4.56656 5.10781C3.86187 5.13086 3.30914 5.72109 3.33219 6.42539L3.54195 12.7625L4.17633 32.4098C4.21344 33.5574 5.15445 34.4687 6.30289 34.4687H33.6377C33.749 34.4687 33.8607 34.4625 33.9716 34.45C35.6064 34.2656 36.7822 32.7906 36.5974 31.1559L34.3873 11.5816C34.2173 10.0754 32.9431 8.93711 31.4275 8.93711H13.0209L12.9377 8.61953L12.2638 6.05898C12.1162 5.49805 11.6091 5.10703 11.0291 5.10703Z" fill="#1B3B3C"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.72812 12.7656H36.6539C38.0637 12.7656 39.207 13.9086 39.207 15.3188C39.207 15.4328 39.1992 15.5465 39.184 15.6594L36.9922 31.943C36.7648 33.6324 35.323 34.8934 33.6184 34.8934H6.37969C4.96953 34.8934 3.82617 33.75 3.82617 32.3398C3.82617 32.2102 3.83633 32.0801 3.85586 31.952L6.36367 15.6523C6.61914 13.9914 8.04805 12.7656 9.72812 12.7656Z" fill="#1B3B3C"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.98438 14.6172H20.4848C20.899 14.6172 21.2348 14.9529 21.2348 15.3672C21.2348 15.7814 20.899 16.1172 20.4848 16.1172H8.98438C8.57013 16.1172 8.23438 15.7814 8.23438 15.3672C8.23438 14.9529 8.57013 14.6172 8.98438 14.6172Z" fill="#00BEB4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@ -30,11 +30,13 @@ const options = Languages.map((x) => ({
|
|||||||
type CrossLanguageItemProps = {
|
type CrossLanguageItemProps = {
|
||||||
name?: string;
|
name?: string;
|
||||||
vertical?: boolean;
|
vertical?: boolean;
|
||||||
|
label?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CrossLanguageFormField = ({
|
export const CrossLanguageFormField = ({
|
||||||
name = 'prompt_config.cross_languages',
|
name = 'prompt_config.cross_languages',
|
||||||
vertical = true,
|
vertical = true,
|
||||||
|
label,
|
||||||
}: CrossLanguageItemProps) => {
|
}: CrossLanguageItemProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
@ -53,7 +55,7 @@ export const CrossLanguageFormField = ({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<FormLabel tooltip={t('chat.crossLanguageTip')}>
|
<FormLabel tooltip={t('chat.crossLanguageTip')}>
|
||||||
{t('chat.crossLanguage')}
|
{label || t('chat.crossLanguage')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
|
|||||||
76
web/src/components/data-pipeline-select/index.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import { buildSelectOptions } from '@/utils/component-util';
|
||||||
|
import { ArrowUpRight } from 'lucide-react';
|
||||||
|
import { useFormContext } from 'react-hook-form';
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '../ui/form';
|
||||||
|
import { RAGFlowSelect } from '../ui/select';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
toDataPipeline?: () => void;
|
||||||
|
formFieldName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{ id: '1', name: 'data-pipeline-1' },
|
||||||
|
{ id: '2', name: 'data-pipeline-2' },
|
||||||
|
{ id: '3', name: 'data-pipeline-3' },
|
||||||
|
{ id: '4', name: 'data-pipeline-4' },
|
||||||
|
];
|
||||||
|
export function DataFlowItem(props: IProps) {
|
||||||
|
const { toDataPipeline, formFieldName } = props;
|
||||||
|
const { t } = useTranslate('knowledgeConfiguration');
|
||||||
|
const form = useFormContext();
|
||||||
|
const toDataPipLine = () => {
|
||||||
|
// window.open('/data-pipeline');
|
||||||
|
|
||||||
|
toDataPipeline?.();
|
||||||
|
};
|
||||||
|
const options = buildSelectOptions(data, 'id', 'name');
|
||||||
|
return (
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name={formFieldName}
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className=" items-center space-y-0 ">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="flex gap-2 justify-between ">
|
||||||
|
<FormLabel
|
||||||
|
tooltip={t('dataFlowTip')}
|
||||||
|
className="text-sm text-text-primary whitespace-wrap "
|
||||||
|
>
|
||||||
|
{t('dataFlow')}
|
||||||
|
</FormLabel>
|
||||||
|
<div
|
||||||
|
className="text-sm flex text-text-primary cursor-pointer"
|
||||||
|
onClick={toDataPipLine}
|
||||||
|
>
|
||||||
|
{t('buildItFromScratch')}
|
||||||
|
<ArrowUpRight size={14} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-muted-foreground">
|
||||||
|
<FormControl>
|
||||||
|
<RAGFlowSelect
|
||||||
|
{...field}
|
||||||
|
placeholder={t('dataFlowPlaceholder')}
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex pt-1">
|
||||||
|
<div className="w-1/4"></div>
|
||||||
|
<FormMessage />
|
||||||
|
</div>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,24 +1,28 @@
|
|||||||
// src/pages/dataset/file-logs/file-status-badge.tsx
|
// src/pages/dataset/file-logs/file-status-badge.tsx
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
/**
|
||||||
|
* params: status: 0 not run yet 1 running, 2 cancel, 3 success, 4 fail
|
||||||
|
*/
|
||||||
interface StatusBadgeProps {
|
interface StatusBadgeProps {
|
||||||
status: 'Success' | 'Failed' | 'Running' | 'Pending';
|
// status: 'Success' | 'Failed' | 'Running' | 'Pending';
|
||||||
|
status: 0 | 1 | 2 | 3 | 4;
|
||||||
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileStatusBadge: FC<StatusBadgeProps> = ({ status }) => {
|
const FileStatusBadge: FC<StatusBadgeProps> = ({ status, name }) => {
|
||||||
const getStatusColor = () => {
|
const getStatusColor = () => {
|
||||||
// #3ba05c → rgb(59, 160, 92) // state-success
|
// #3ba05c → rgb(59, 160, 92) // state-success
|
||||||
// #d8494b → rgb(216, 73, 75) // state-error
|
// #d8494b → rgb(216, 73, 75) // state-error
|
||||||
// #00beb4 → rgb(0, 190, 180) // accent-primary
|
// #00beb4 → rgb(0, 190, 180) // accent-primary
|
||||||
// #faad14 → rgb(250, 173, 20) // state-warning
|
// #faad14 → rgb(250, 173, 20) // state-warning
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'Success':
|
case 3:
|
||||||
return `bg-[rgba(59,160,92,0.1)] text-state-success`;
|
return `bg-[rgba(59,160,92,0.1)] text-state-success`;
|
||||||
case 'Failed':
|
case 4:
|
||||||
return `bg-[rgba(216,73,75,0.1)] text-state-error`;
|
return `bg-[rgba(216,73,75,0.1)] text-state-error`;
|
||||||
case 'Running':
|
case 1:
|
||||||
return `bg-[rgba(0,190,180,0.1)] text-accent-primary`;
|
return `bg-[rgba(0,190,180,0.1)] text-accent-primary`;
|
||||||
case 'Pending':
|
case 0:
|
||||||
return `bg-[rgba(250,173,20,0.1)] text-state-warning`;
|
return `bg-[rgba(250,173,20,0.1)] text-state-warning`;
|
||||||
default:
|
default:
|
||||||
return 'bg-gray-500/10 text-white';
|
return 'bg-gray-500/10 text-white';
|
||||||
@ -31,13 +35,13 @@ const FileStatusBadge: FC<StatusBadgeProps> = ({ status }) => {
|
|||||||
// #00beb4 → rgb(0, 190, 180) // accent-primary
|
// #00beb4 → rgb(0, 190, 180) // accent-primary
|
||||||
// #faad14 → rgb(250, 173, 20) // state-warning
|
// #faad14 → rgb(250, 173, 20) // state-warning
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'Success':
|
case 3:
|
||||||
return `bg-[rgba(59,160,92,1)] text-state-success`;
|
return `bg-[rgba(59,160,92,1)] text-state-success`;
|
||||||
case 'Failed':
|
case 4:
|
||||||
return `bg-[rgba(216,73,75,1)] text-state-error`;
|
return `bg-[rgba(216,73,75,1)] text-state-error`;
|
||||||
case 'Running':
|
case 1:
|
||||||
return `bg-[rgba(0,190,180,1)] text-accent-primary`;
|
return `bg-[rgba(0,190,180,1)] text-accent-primary`;
|
||||||
case 'Pending':
|
case 0:
|
||||||
return `bg-[rgba(250,173,20,1)] text-state-warning`;
|
return `bg-[rgba(250,173,20,1)] text-state-warning`;
|
||||||
default:
|
default:
|
||||||
return 'bg-gray-500/10 text-white';
|
return 'bg-gray-500/10 text-white';
|
||||||
@ -46,10 +50,10 @@ const FileStatusBadge: FC<StatusBadgeProps> = ({ status }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`inline-flex items-center w-[75px] px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(0.1)}`}
|
className={`inline-flex items-center w-[75px] px-2 py-1 rounded-full text-xs font-medium ${getStatusColor()}`}
|
||||||
>
|
>
|
||||||
<div className={`w-1 h-1 mr-1 rounded-full ${getBgStatusColor()}`}></div>
|
<div className={`w-1 h-1 mr-1 rounded-full ${getBgStatusColor()}`}></div>
|
||||||
{status}
|
{name || ''}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { getExtension } from '@/utils/document-util';
|
|||||||
|
|
||||||
type IconFontType = {
|
type IconFontType = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -13,6 +14,23 @@ export const IconFont = ({ name, className }: IconFontType) => (
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export function IconFontFill({
|
||||||
|
name,
|
||||||
|
className,
|
||||||
|
isFill = true,
|
||||||
|
}: IconFontType & { isFill?: boolean }) {
|
||||||
|
return (
|
||||||
|
<span className={cn('size-4', className)}>
|
||||||
|
<svg
|
||||||
|
className={cn('size-4', className)}
|
||||||
|
style={{ fill: isFill ? 'currentColor' : '' }}
|
||||||
|
>
|
||||||
|
<use xlinkHref={`#icon-${name}`} />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function FileIcon({
|
export function FileIcon({
|
||||||
name,
|
name,
|
||||||
className,
|
className,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { LlmModelType } from '@/constants/knowledge';
|
import { LlmModelType } from '@/constants/knowledge';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
import { useSelectLlmOptionsByModelType } from '@/hooks/llm-hooks';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
import { camelCase } from 'lodash';
|
import { camelCase } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
@ -18,7 +19,13 @@ export const enum DocumentType {
|
|||||||
PlainText = 'Plain Text',
|
PlainText = 'Plain Text',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LayoutRecognizeFormField() {
|
export function LayoutRecognizeFormField({
|
||||||
|
name = 'parser_config.layout_recognize',
|
||||||
|
horizontal = true,
|
||||||
|
}: {
|
||||||
|
name?: string;
|
||||||
|
horizontal?: boolean;
|
||||||
|
}) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
|
|
||||||
const { t } = useTranslate('knowledgeDetails');
|
const { t } = useTranslate('knowledgeDetails');
|
||||||
@ -53,33 +60,32 @@ export function LayoutRecognizeFormField() {
|
|||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="parser_config.layout_recognize"
|
name={name}
|
||||||
render={({ field }) => {
|
render={({ field }) => {
|
||||||
if (typeof field.value === 'undefined') {
|
|
||||||
// default value set
|
|
||||||
form.setValue(
|
|
||||||
'parser_config.layout_recognize',
|
|
||||||
form.formState.defaultValues?.parser_config?.layout_recognize ??
|
|
||||||
'DeepDOC',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<FormItem className=" items-center space-y-0 ">
|
<FormItem className={'items-center space-y-0 '}>
|
||||||
<div className="flex items-center">
|
<div
|
||||||
|
className={cn('flex', {
|
||||||
|
'flex-col ': !horizontal,
|
||||||
|
'items-center': horizontal,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FormLabel
|
<FormLabel
|
||||||
tooltip={t('layoutRecognizeTip')}
|
tooltip={t('layoutRecognizeTip')}
|
||||||
className="text-sm text-muted-foreground whitespace-wrap w-1/4"
|
className={cn('text-sm text-muted-foreground whitespace-wrap', {
|
||||||
|
['w-1/4']: horizontal,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
{t('layoutRecognize')}
|
{t('layoutRecognize')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<div className="w-3/4">
|
<div className={horizontal ? 'w-3/4' : 'w-full'}>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<RAGFlowSelect {...field} options={options}></RAGFlowSelect>
|
<RAGFlowSelect {...field} options={options}></RAGFlowSelect>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex pt-1">
|
<div className="flex pt-1">
|
||||||
<div className="w-1/4"></div>
|
<div className={horizontal ? 'w-1/4' : 'w-full'}></div>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
25
web/src/components/llm-setting-items/llm-form-field.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { LlmModelType } from '@/constants/knowledge';
|
||||||
|
import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { SelectWithSearch } from '../originui/select-with-search';
|
||||||
|
import { RAGFlowFormItem } from '../ragflow-form';
|
||||||
|
|
||||||
|
type LLMFormFieldProps = {
|
||||||
|
options?: any[];
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function LLMFormField({ options, name }: LLMFormFieldProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const modelOptions = useComposeLlmOptionsByModelTypes([
|
||||||
|
LlmModelType.Chat,
|
||||||
|
LlmModelType.Image2text,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem name={name || 'llm_id'} label={t('chat.model')}>
|
||||||
|
<SelectWithSearch options={options || modelOptions}></SelectWithSearch>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,11 +1,9 @@
|
|||||||
import { LlmModelType, ModelVariableType } from '@/constants/knowledge';
|
import { ModelVariableType } from '@/constants/knowledge';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks';
|
|
||||||
import { camelCase } from 'lodash';
|
import { camelCase } 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';
|
||||||
import { SelectWithSearch } from '../originui/select-with-search';
|
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormField,
|
FormField,
|
||||||
@ -20,6 +18,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '../ui/select';
|
} from '../ui/select';
|
||||||
|
import { LLMFormField } from './llm-form-field';
|
||||||
import { SliderInputSwitchFormField } from './slider';
|
import { SliderInputSwitchFormField } from './slider';
|
||||||
import { useHandleFreedomChange } from './use-watch-change';
|
import { useHandleFreedomChange } from './use-watch-change';
|
||||||
|
|
||||||
@ -61,11 +60,6 @@ export function LlmSettingFieldItems({
|
|||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
const modelOptions = useComposeLlmOptionsByModelTypes([
|
|
||||||
LlmModelType.Chat,
|
|
||||||
LlmModelType.Image2text,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const getFieldWithPrefix = useCallback(
|
const getFieldWithPrefix = useCallback(
|
||||||
(name: string) => {
|
(name: string) => {
|
||||||
return prefix ? `${prefix}.${name}` : name;
|
return prefix ? `${prefix}.${name}` : name;
|
||||||
@ -82,22 +76,7 @@ export function LlmSettingFieldItems({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<FormField
|
<LLMFormField options={options}></LLMFormField>
|
||||||
control={form.control}
|
|
||||||
name={'llm_id'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('model')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<SelectWithSearch
|
|
||||||
options={options || modelOptions}
|
|
||||||
{...field}
|
|
||||||
></SelectWithSearch>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={'parameter'}
|
name={'parameter'}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ type RAGFlowFormItemProps = {
|
|||||||
children: ReactNode | ((field: ControllerRenderProps) => ReactNode);
|
children: ReactNode | ((field: ControllerRenderProps) => ReactNode);
|
||||||
horizontal?: boolean;
|
horizontal?: boolean;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
|
labelClassName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RAGFlowFormItem({
|
export function RAGFlowFormItem({
|
||||||
@ -25,6 +26,7 @@ export function RAGFlowFormItem({
|
|||||||
children,
|
children,
|
||||||
horizontal = false,
|
horizontal = false,
|
||||||
required = false,
|
required = false,
|
||||||
|
labelClassName,
|
||||||
}: RAGFlowFormItemProps) {
|
}: RAGFlowFormItemProps) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
return (
|
return (
|
||||||
@ -40,7 +42,7 @@ export function RAGFlowFormItem({
|
|||||||
<FormLabel
|
<FormLabel
|
||||||
required={required}
|
required={required}
|
||||||
tooltip={tooltip}
|
tooltip={tooltip}
|
||||||
className={cn({ 'w-1/4': horizontal })}
|
className={cn({ 'w-1/4': horizontal }, labelClassName)}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|||||||
@ -187,7 +187,7 @@ const Modal: ModalType = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* content */}
|
{/* content */}
|
||||||
<div className="py-2 px-6 overflow-y-auto max-h-[80vh] focus-visible:!outline-none">
|
<div className="py-2 px-6 overflow-y-auto scrollbar-auto max-h-[80vh] focus-visible:!outline-none">
|
||||||
{destroyOnClose && !open ? null : children}
|
{destroyOnClose && !open ? null : children}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,7 @@ export enum LlmModelType {
|
|||||||
export enum KnowledgeSearchParams {
|
export enum KnowledgeSearchParams {
|
||||||
DocumentId = 'doc_id',
|
DocumentId = 'doc_id',
|
||||||
KnowledgeId = 'id',
|
KnowledgeId = 'id',
|
||||||
|
Type = 'type',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DocumentType {
|
export enum DocumentType {
|
||||||
|
|||||||
@ -87,7 +87,7 @@ export const useNavigatePage = () => {
|
|||||||
(id: string, knowledgeId?: string) => () => {
|
(id: string, knowledgeId?: string) => () => {
|
||||||
navigate(
|
navigate(
|
||||||
// `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`,
|
// `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`,
|
||||||
`${Routes.ParsedResult}/chunks?id=${knowledgeId}&doc_id=${id}`,
|
`${Routes.DataflowResult}?id=${knowledgeId}&doc_id=${id}&type=chunk`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[navigate],
|
[navigate],
|
||||||
@ -129,7 +129,7 @@ export const useNavigatePage = () => {
|
|||||||
(id: string, knowledgeId?: string) => () => {
|
(id: string, knowledgeId?: string) => () => {
|
||||||
navigate(
|
navigate(
|
||||||
// `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`,
|
// `${Routes.ParsedResult}/${id}?${QueryStringMap.KnowledgeId}=${knowledgeId}`,
|
||||||
`${Routes.DataflowResult}/${id}`,
|
`${Routes.DataflowResult}?id=${knowledgeId}&doc_id=${id}&type=dataflow`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[navigate],
|
[navigate],
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export const useGetKnowledgeSearchParams = () => {
|
|||||||
const [currentQueryParameters] = useSearchParams();
|
const [currentQueryParameters] = useSearchParams();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
type: currentQueryParameters.get(KnowledgeSearchParams.Type) || '',
|
||||||
documentId:
|
documentId:
|
||||||
currentQueryParameters.get(KnowledgeSearchParams.DocumentId) || '',
|
currentQueryParameters.get(KnowledgeSearchParams.DocumentId) || '',
|
||||||
knowledgeId:
|
knowledgeId:
|
||||||
|
|||||||
127
web/src/hooks/use-dataflow-request.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import message from '@/components/ui/message';
|
||||||
|
import { IFlow } from '@/interfaces/database/agent';
|
||||||
|
import { Operator } from '@/pages/data-flow/constant';
|
||||||
|
import dataflowService from '@/services/dataflow-service';
|
||||||
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useParams } from 'umi';
|
||||||
|
|
||||||
|
export const enum DataflowApiAction {
|
||||||
|
ListDataflow = 'listDataflow',
|
||||||
|
RemoveDataflow = 'removeDataflow',
|
||||||
|
FetchDataflow = 'fetchDataflow',
|
||||||
|
RunDataflow = 'runDataflow',
|
||||||
|
SetDataflow = 'setDataflow',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EmptyDsl = {
|
||||||
|
graph: {
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: Operator.Begin,
|
||||||
|
type: 'beginNode',
|
||||||
|
position: {
|
||||||
|
x: 50,
|
||||||
|
y: 200,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
label: 'Begin',
|
||||||
|
name: Operator.Begin,
|
||||||
|
},
|
||||||
|
sourcePosition: 'left',
|
||||||
|
targetPosition: 'right',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [],
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
begin: {
|
||||||
|
obj: {
|
||||||
|
component_name: 'Begin',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
downstream: [], // other edge target is downstream, edge source is current node id
|
||||||
|
upstream: [], // edge source is upstream, edge target is current node id
|
||||||
|
},
|
||||||
|
},
|
||||||
|
retrieval: [], // reference
|
||||||
|
history: [],
|
||||||
|
path: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRemoveDataflow = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [DataflowApiAction.RemoveDataflow],
|
||||||
|
mutationFn: async (ids: string[]) => {
|
||||||
|
const { data } = await dataflowService.removeDataflow({
|
||||||
|
canvas_ids: ids,
|
||||||
|
});
|
||||||
|
if (data.code === 0) {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [DataflowApiAction.ListDataflow],
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success(t('message.deleted'));
|
||||||
|
}
|
||||||
|
return data.code;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, removeDataflow: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetDataflow = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [DataflowApiAction.SetDataflow],
|
||||||
|
mutationFn: async (params: Partial<IFlow>) => {
|
||||||
|
const { data } = await dataflowService.setDataflow(params);
|
||||||
|
if (data.code === 0) {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [DataflowApiAction.FetchDataflow],
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success(t(`message.${params.id ? 'modified' : 'created'}`));
|
||||||
|
}
|
||||||
|
return data?.code;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, setDataflow: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchDataflow = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isFetching: loading,
|
||||||
|
refetch,
|
||||||
|
} = useQuery<IFlow>({
|
||||||
|
queryKey: [DataflowApiAction.FetchDataflow, id],
|
||||||
|
gcTime: 0,
|
||||||
|
initialData: {} as IFlow,
|
||||||
|
enabled: !!id,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await dataflowService.fetchDataflow(id);
|
||||||
|
|
||||||
|
return data?.data ?? ({} as IFlow);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, refetch };
|
||||||
|
};
|
||||||
@ -261,12 +261,13 @@ export default {
|
|||||||
reRankModelWaring: 'Re-rank model is very time consuming.',
|
reRankModelWaring: 'Re-rank model is very time consuming.',
|
||||||
},
|
},
|
||||||
knowledgeConfiguration: {
|
knowledgeConfiguration: {
|
||||||
|
default: 'Default',
|
||||||
|
dataPipeline: 'Data Pipeline',
|
||||||
|
linkDataPipeline: 'Link Data Pipeline',
|
||||||
enableAutoGenerate: 'Enable Auto Generate',
|
enableAutoGenerate: 'Enable Auto Generate',
|
||||||
teamPlaceholder: 'Please select a team.',
|
teamPlaceholder: 'Please select a team.',
|
||||||
dataFlowPlaceholder: 'Please select a data flow.',
|
dataFlowPlaceholder: 'Please select a data flow.',
|
||||||
buildItFromScratch: 'Build it from scratch',
|
buildItFromScratch: 'Build it from scratch',
|
||||||
useRAPTORToEnhanceRetrieval: 'Use RAPTOR to Enhance Retrieval',
|
|
||||||
extractKnowledgeGraph: 'Extract Knowledge Graph',
|
|
||||||
dataFlow: 'Data Flow',
|
dataFlow: 'Data Flow',
|
||||||
parseType: 'Parse Type',
|
parseType: 'Parse Type',
|
||||||
manualSetup: 'Manual Setup',
|
manualSetup: 'Manual Setup',
|
||||||
@ -1635,6 +1636,11 @@ This delimiter is used to split the input text into several text pieces echo of
|
|||||||
chunkerDescription: 'Chunker',
|
chunkerDescription: 'Chunker',
|
||||||
tokenizer: 'Tokenizer',
|
tokenizer: 'Tokenizer',
|
||||||
tokenizerDescription: 'Tokenizer',
|
tokenizerDescription: 'Tokenizer',
|
||||||
|
outputFormat: 'Output format',
|
||||||
|
lang: 'Language',
|
||||||
|
fileFormats: 'File formats',
|
||||||
|
fields: 'Fields',
|
||||||
|
addParser: 'Add Parser',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -246,12 +246,13 @@ export default {
|
|||||||
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
|
theDocumentBeingParsedCannotBeDeleted: '正在解析的文档不能被删除',
|
||||||
},
|
},
|
||||||
knowledgeConfiguration: {
|
knowledgeConfiguration: {
|
||||||
|
default: '默认',
|
||||||
|
dataPipeline: '数据流',
|
||||||
|
linkDataPipeline: '关联数据流',
|
||||||
enableAutoGenerate: '是否启用自动生成',
|
enableAutoGenerate: '是否启用自动生成',
|
||||||
teamPlaceholder: '请选择团队',
|
teamPlaceholder: '请选择团队',
|
||||||
dataFlowPlaceholder: '请选择数据流',
|
dataFlowPlaceholder: '请选择数据流',
|
||||||
buildItFromScratch: '去Scratch构建',
|
buildItFromScratch: '去Scratch构建',
|
||||||
useRAPTORToEnhanceRetrieval: '使用 RAPTOR 提升检索效果',
|
|
||||||
extractKnowledgeGraph: '知识图谱提取',
|
|
||||||
dataFlow: '数据流',
|
dataFlow: '数据流',
|
||||||
parseType: '切片方法',
|
parseType: '切片方法',
|
||||||
manualSetup: '手动设置',
|
manualSetup: '手动设置',
|
||||||
@ -1543,6 +1544,11 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
|||||||
chunkerDescription: '分块器',
|
chunkerDescription: '分块器',
|
||||||
tokenizer: '分词器',
|
tokenizer: '分词器',
|
||||||
tokenizerDescription: '分词器',
|
tokenizerDescription: '分词器',
|
||||||
|
outputFormat: '输出格式',
|
||||||
|
lang: '语言',
|
||||||
|
fileFormats: '文件格式',
|
||||||
|
fields: '字段',
|
||||||
|
addParser: '增加解析器',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { useSetModalState } from '@/hooks/common-hooks';
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request';
|
import { useSetAgent } from '@/hooks/use-agent-request';
|
||||||
import { DSL } from '@/interfaces/database/agent';
|
import { EmptyDsl, useSetDataflow } from '@/hooks/use-dataflow-request';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { FlowType } from '../constant';
|
import { FlowType } from '../constant';
|
||||||
import { FormSchemaType } from '../create-agent-form';
|
import { FormSchemaType } from '../create-agent-form';
|
||||||
|
|
||||||
export function useCreateAgentOrPipeline() {
|
export function useCreateAgentOrPipeline() {
|
||||||
const { loading, setAgent } = useSetAgent();
|
const { loading, setAgent } = useSetAgent();
|
||||||
|
const { loading: dataflowLoading, setDataflow } = useSetDataflow();
|
||||||
const {
|
const {
|
||||||
visible: creatingVisible,
|
visible: creatingVisible,
|
||||||
hideModal: hideCreatingModal,
|
hideModal: hideCreatingModal,
|
||||||
@ -15,7 +16,7 @@ export function useCreateAgentOrPipeline() {
|
|||||||
|
|
||||||
const createAgent = useCallback(
|
const createAgent = useCallback(
|
||||||
async (name: string) => {
|
async (name: string) => {
|
||||||
return setAgent({ title: name, dsl: EmptyDsl as DSL });
|
return setAgent({ title: name, dsl: EmptyDsl });
|
||||||
},
|
},
|
||||||
[setAgent],
|
[setAgent],
|
||||||
);
|
);
|
||||||
@ -27,13 +28,18 @@ export function useCreateAgentOrPipeline() {
|
|||||||
if (ret.code === 0) {
|
if (ret.code === 0) {
|
||||||
hideCreatingModal();
|
hideCreatingModal();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setDataflow({
|
||||||
|
title: data.name,
|
||||||
|
dsl: EmptyDsl,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[createAgent, hideCreatingModal],
|
[createAgent, hideCreatingModal, setDataflow],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loading,
|
loading: loading || dataflowLoading,
|
||||||
creatingVisible,
|
creatingVisible,
|
||||||
hideCreatingModal,
|
hideCreatingModal,
|
||||||
showCreatingModal,
|
showCreatingModal,
|
||||||
|
|||||||
@ -44,6 +44,7 @@ import { CategorizeNode } from './node/categorize-node';
|
|||||||
import ChunkerNode from './node/chunker-node';
|
import ChunkerNode from './node/chunker-node';
|
||||||
import { InnerNextStepDropdown } from './node/dropdown/next-step-dropdown';
|
import { InnerNextStepDropdown } from './node/dropdown/next-step-dropdown';
|
||||||
import { GenerateNode } from './node/generate-node';
|
import { GenerateNode } from './node/generate-node';
|
||||||
|
import { HierarchicalMergerNode } from './node/hierarchical-merger-node';
|
||||||
import { InvokeNode } from './node/invoke-node';
|
import { InvokeNode } from './node/invoke-node';
|
||||||
import { IterationNode, IterationStartNode } from './node/iteration-node';
|
import { IterationNode, IterationStartNode } from './node/iteration-node';
|
||||||
import { KeywordNode } from './node/keyword-node';
|
import { KeywordNode } from './node/keyword-node';
|
||||||
@ -54,6 +55,7 @@ import ParserNode from './node/parser-node';
|
|||||||
import { RelevantNode } from './node/relevant-node';
|
import { RelevantNode } from './node/relevant-node';
|
||||||
import { RetrievalNode } from './node/retrieval-node';
|
import { RetrievalNode } from './node/retrieval-node';
|
||||||
import { RewriteNode } from './node/rewrite-node';
|
import { RewriteNode } from './node/rewrite-node';
|
||||||
|
import { SplitterNode } from './node/splitter-node';
|
||||||
import { SwitchNode } from './node/switch-node';
|
import { SwitchNode } from './node/switch-node';
|
||||||
import { TemplateNode } from './node/template-node';
|
import { TemplateNode } from './node/template-node';
|
||||||
import TokenizerNode from './node/tokenizer-node';
|
import TokenizerNode from './node/tokenizer-node';
|
||||||
@ -82,6 +84,8 @@ export const nodeTypes: NodeTypes = {
|
|||||||
parserNode: ParserNode,
|
parserNode: ParserNode,
|
||||||
chunkerNode: ChunkerNode,
|
chunkerNode: ChunkerNode,
|
||||||
tokenizerNode: TokenizerNode,
|
tokenizerNode: TokenizerNode,
|
||||||
|
splitterNode: SplitterNode,
|
||||||
|
hierarchicalMergerNode: HierarchicalMergerNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
const edgeTypes = {
|
const edgeTypes = {
|
||||||
|
|||||||
@ -141,6 +141,8 @@ function AccordionOperators({
|
|||||||
Operator.Parser,
|
Operator.Parser,
|
||||||
Operator.Chunker,
|
Operator.Chunker,
|
||||||
Operator.Tokenizer,
|
Operator.Tokenizer,
|
||||||
|
Operator.Splitter,
|
||||||
|
Operator.HierarchicalMerger,
|
||||||
]}
|
]}
|
||||||
isCustomDropdown={isCustomDropdown}
|
isCustomDropdown={isCustomDropdown}
|
||||||
mousePosition={mousePosition}
|
mousePosition={mousePosition}
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export { RagNode as HierarchicalMergerNode } from './index';
|
||||||
1
web/src/pages/data-flow/canvas/node/splitter-node.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { RagNode as SplitterNode } from './index';
|
||||||
@ -66,6 +66,8 @@ export enum Operator {
|
|||||||
Parser = 'Parser',
|
Parser = 'Parser',
|
||||||
Chunker = 'Chunker',
|
Chunker = 'Chunker',
|
||||||
Tokenizer = 'Tokenizer',
|
Tokenizer = 'Tokenizer',
|
||||||
|
Splitter = 'Splitter',
|
||||||
|
HierarchicalMerger = 'HierarchicalMerger',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SwitchLogicOperatorOptions = ['and', 'or'];
|
export const SwitchLogicOperatorOptions = ['and', 'or'];
|
||||||
@ -74,20 +76,6 @@ export const CommonOperatorList = Object.values(Operator).filter(
|
|||||||
(x) => x !== Operator.Note,
|
(x) => x !== Operator.Note,
|
||||||
);
|
);
|
||||||
|
|
||||||
export const AgentOperatorList = [
|
|
||||||
Operator.Retrieval,
|
|
||||||
Operator.Categorize,
|
|
||||||
Operator.Message,
|
|
||||||
Operator.RewriteQuestion,
|
|
||||||
Operator.KeywordExtract,
|
|
||||||
Operator.Switch,
|
|
||||||
Operator.Concentrator,
|
|
||||||
Operator.Iteration,
|
|
||||||
Operator.WaitingDialogue,
|
|
||||||
Operator.Note,
|
|
||||||
Operator.Agent,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const SwitchOperatorOptions = [
|
export const SwitchOperatorOptions = [
|
||||||
{ value: '=', label: 'equal', icon: 'equal' },
|
{ value: '=', label: 'equal', icon: 'equal' },
|
||||||
{ value: '≠', label: 'notEqual', icon: 'not-equals' },
|
{ value: '≠', label: 'notEqual', icon: 'not-equals' },
|
||||||
@ -388,7 +376,11 @@ export const initialStringTransformValues = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialParserValues = { outputs: {} };
|
export const initialParserValues = { outputs: {}, parser: [] };
|
||||||
|
|
||||||
|
export const initialSplitterValues = { outputs: {}, chunk_token_size: 512 };
|
||||||
|
|
||||||
|
export const initialHierarchicalMergerValues = { outputs: {} };
|
||||||
|
|
||||||
export const CategorizeAnchorPointPositions = [
|
export const CategorizeAnchorPointPositions = [
|
||||||
{ top: 1, right: 34 },
|
{ top: 1, right: 34 },
|
||||||
@ -473,6 +465,8 @@ export const NodeMap = {
|
|||||||
[Operator.Parser]: 'parserNode',
|
[Operator.Parser]: 'parserNode',
|
||||||
[Operator.Chunker]: 'chunkerNode',
|
[Operator.Chunker]: 'chunkerNode',
|
||||||
[Operator.Tokenizer]: 'tokenizerNode',
|
[Operator.Tokenizer]: 'tokenizerNode',
|
||||||
|
[Operator.Splitter]: 'splitterNode',
|
||||||
|
[Operator.HierarchicalMerger]: 'hierarchicalMergerNode',
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum BeginQueryType {
|
export enum BeginQueryType {
|
||||||
@ -523,3 +517,15 @@ export enum AgentExceptionMethod {
|
|||||||
Comment = 'comment',
|
Comment = 'comment',
|
||||||
Goto = 'goto',
|
Goto = 'goto',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum FileType {
|
||||||
|
PDF = 'pdf',
|
||||||
|
Spreadsheet = 'spreadsheet',
|
||||||
|
Image = 'image',
|
||||||
|
Email = 'email',
|
||||||
|
TextMarkdown = 'text&markdown',
|
||||||
|
Docx = 'docx',
|
||||||
|
PowerPoint = 'ppt',
|
||||||
|
Video = 'video',
|
||||||
|
Audio = 'audio',
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import CodeForm from '../form/code-form';
|
|||||||
import CrawlerForm from '../form/crawler-form';
|
import CrawlerForm from '../form/crawler-form';
|
||||||
import EmailForm from '../form/email-form';
|
import EmailForm from '../form/email-form';
|
||||||
import ExeSQLForm from '../form/exesql-form';
|
import ExeSQLForm from '../form/exesql-form';
|
||||||
|
import HierarchicalMergerForm from '../form/hierarchical-merger-form';
|
||||||
import InvokeForm from '../form/invoke-form';
|
import InvokeForm from '../form/invoke-form';
|
||||||
import IterationForm from '../form/iteration-form';
|
import IterationForm from '../form/iteration-form';
|
||||||
import IterationStartForm from '../form/iteration-start-from';
|
import IterationStartForm from '../form/iteration-start-from';
|
||||||
@ -16,6 +17,7 @@ import ParserForm from '../form/parser-form';
|
|||||||
import RelevantForm from '../form/relevant-form';
|
import RelevantForm from '../form/relevant-form';
|
||||||
import RetrievalForm from '../form/retrieval-form/next';
|
import RetrievalForm from '../form/retrieval-form/next';
|
||||||
import RewriteQuestionForm from '../form/rewrite-question-form';
|
import RewriteQuestionForm from '../form/rewrite-question-form';
|
||||||
|
import SplitterForm from '../form/splitter-form';
|
||||||
import StringTransformForm from '../form/string-transform-form';
|
import StringTransformForm from '../form/string-transform-form';
|
||||||
import SwitchForm from '../form/switch-form';
|
import SwitchForm from '../form/switch-form';
|
||||||
import TokenizerForm from '../form/tokenizer-form';
|
import TokenizerForm from '../form/tokenizer-form';
|
||||||
@ -94,4 +96,10 @@ export const FormConfigMap = {
|
|||||||
[Operator.Tokenizer]: {
|
[Operator.Tokenizer]: {
|
||||||
component: TokenizerForm,
|
component: TokenizerForm,
|
||||||
},
|
},
|
||||||
|
[Operator.Splitter]: {
|
||||||
|
component: SplitterForm,
|
||||||
|
},
|
||||||
|
[Operator.HierarchicalMerger]: {
|
||||||
|
component: HierarchicalMergerForm,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
158
web/src/pages/data-flow/form/hierarchical-merger-form/index.tsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
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, CardTitle } from '@/components/ui/card';
|
||||||
|
import { Form } from '@/components/ui/form';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { useFieldArray, useForm, useFormContext } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { initialHierarchicalMergerValues } from '../../constant';
|
||||||
|
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);
|
||||||
|
|
||||||
|
enum Hierarchy {
|
||||||
|
H1 = '1',
|
||||||
|
H2 = '2',
|
||||||
|
H3 = '3',
|
||||||
|
H4 = '4',
|
||||||
|
H5 = '5',
|
||||||
|
}
|
||||||
|
|
||||||
|
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.number(),
|
||||||
|
levels: z.array(
|
||||||
|
z.object({
|
||||||
|
expressions: z.array(z.object({ expression: z.string() })),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
type RegularExpressionsProps = {
|
||||||
|
index: number;
|
||||||
|
parentName: string;
|
||||||
|
removeParent: (index: number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function RegularExpressions({
|
||||||
|
index,
|
||||||
|
parentName,
|
||||||
|
removeParent,
|
||||||
|
}: RegularExpressionsProps) {
|
||||||
|
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">
|
||||||
|
<CardTitle>H{index}</CardTitle>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={'ghost'}
|
||||||
|
onClick={() => removeParent(index)}
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
</Button>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<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>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={'ghost'}
|
||||||
|
onClick={() => remove(index)}
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
<BlockButton
|
||||||
|
onClick={() => append({ expression: '' })}
|
||||||
|
className="mt-6"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</BlockButton>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const HierarchicalMergerForm = ({ node }: INextOperatorForm) => {
|
||||||
|
const defaultValues = useFormValues(initialHierarchicalMergerValues, node);
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
defaultValues,
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
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={'hierarchy'}>
|
||||||
|
<SelectWithSearch options={HierarchyOptions}></SelectWithSearch>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<div key={field.id} className="flex items-center gap-2">
|
||||||
|
<div className="space-y-2 flex-1">
|
||||||
|
<RegularExpressions
|
||||||
|
parentName={name}
|
||||||
|
index={index}
|
||||||
|
removeParent={remove}
|
||||||
|
></RegularExpressions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<BlockButton onClick={() => append({ expressions: [] })}>
|
||||||
|
Add
|
||||||
|
</BlockButton>
|
||||||
|
</FormWrapper>
|
||||||
|
<div className="p-5">
|
||||||
|
<Output list={outputList}></Output>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(HierarchicalMergerForm);
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
|
||||||
|
import { LLMFormField } from '@/components/llm-setting-items/llm-form-field';
|
||||||
|
import {
|
||||||
|
SelectWithSearch,
|
||||||
|
SelectWithSearchFlagOptionType,
|
||||||
|
} from '@/components/originui/select-with-search';
|
||||||
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
|
import { buildOptions } from '@/utils/form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FileType } from '../../constant';
|
||||||
|
import { OutputFormatMap } from './constant';
|
||||||
|
import { CommonProps } from './interface';
|
||||||
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
|
||||||
|
function buildOutputOptionsFormatMap() {
|
||||||
|
return Object.entries(OutputFormatMap).reduce<
|
||||||
|
Record<string, SelectWithSearchFlagOptionType[]>
|
||||||
|
>((pre, [key, value]) => {
|
||||||
|
pre[key] = buildOptions(value);
|
||||||
|
return pre;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OutputFormatFormFieldProps = CommonProps & {
|
||||||
|
fileType: FileType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function OutputFormatFormField({
|
||||||
|
prefix,
|
||||||
|
fileType,
|
||||||
|
}: OutputFormatFormFieldProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={buildFieldNameWithPrefix(`output_format`, prefix)}
|
||||||
|
label={t('dataflow.outputFormat')}
|
||||||
|
>
|
||||||
|
<SelectWithSearch
|
||||||
|
options={buildOutputOptionsFormatMap()[fileType]}
|
||||||
|
></SelectWithSearch>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ParserMethodFormField({ prefix }: CommonProps) {
|
||||||
|
return (
|
||||||
|
<LayoutRecognizeFormField
|
||||||
|
name={buildFieldNameWithPrefix(`parse_method`, prefix)}
|
||||||
|
horizontal={false}
|
||||||
|
></LayoutRecognizeFormField>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={buildFieldNameWithPrefix(`parse_method`, prefix)}
|
||||||
|
label="parse_method"
|
||||||
|
>
|
||||||
|
<SelectWithSearch options={[]}></SelectWithSearch>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LargeModelFormField({ prefix }: CommonProps) {
|
||||||
|
return (
|
||||||
|
<LLMFormField
|
||||||
|
name={buildFieldNameWithPrefix('llm_id', prefix)}
|
||||||
|
></LLMFormField>
|
||||||
|
);
|
||||||
|
}
|
||||||
52
web/src/pages/data-flow/form/parser-form/constant.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { FileType } from '../../constant';
|
||||||
|
|
||||||
|
export enum PdfOutputFormat {
|
||||||
|
Json = 'json',
|
||||||
|
Markdown = 'markdown',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SpreadsheetOutputFormat {
|
||||||
|
Json = 'json',
|
||||||
|
Html = 'html',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ImageOutputFormat {
|
||||||
|
Text = 'text',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EmailOutputFormat {
|
||||||
|
Json = 'json',
|
||||||
|
Text = 'text',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TextMarkdownOutputFormat {
|
||||||
|
Text = 'text',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DocxOutputFormat {
|
||||||
|
Markdown = 'markdown',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PptOutputFormat {
|
||||||
|
Json = 'json',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VideoOutputFormat {
|
||||||
|
Json = 'json',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AudioOutputFormat {
|
||||||
|
Text = 'text',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OutputFormatMap = {
|
||||||
|
[FileType.PDF]: PdfOutputFormat,
|
||||||
|
[FileType.Spreadsheet]: SpreadsheetOutputFormat,
|
||||||
|
[FileType.Image]: ImageOutputFormat,
|
||||||
|
[FileType.Email]: EmailOutputFormat,
|
||||||
|
[FileType.TextMarkdown]: TextMarkdownOutputFormat,
|
||||||
|
[FileType.Docx]: DocxOutputFormat,
|
||||||
|
[FileType.PowerPoint]: PptOutputFormat,
|
||||||
|
[FileType.Video]: VideoOutputFormat,
|
||||||
|
[FileType.Audio]: AudioOutputFormat,
|
||||||
|
};
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
|
import { buildOptions } from '@/utils/form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FileType } from '../../constant';
|
||||||
|
import { OutputFormatFormField } from './common-form-fields';
|
||||||
|
import { CommonProps } from './interface';
|
||||||
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
|
||||||
|
const options = buildOptions([
|
||||||
|
'from',
|
||||||
|
'to',
|
||||||
|
'cc',
|
||||||
|
'bcc',
|
||||||
|
'date',
|
||||||
|
'subject',
|
||||||
|
'body',
|
||||||
|
'attachments',
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function EmailFormFields({ prefix }: CommonProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RAGFlowFormItem
|
||||||
|
name={buildFieldNameWithPrefix(`fields`, prefix)}
|
||||||
|
label={t('dataflow.fields')}
|
||||||
|
>
|
||||||
|
<SelectWithSearch options={options}></SelectWithSearch>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
<OutputFormatFormField
|
||||||
|
prefix={prefix}
|
||||||
|
fileType={FileType.Email}
|
||||||
|
></OutputFormatFormField>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { FileType } from '../../constant';
|
||||||
|
import {
|
||||||
|
LargeModelFormField,
|
||||||
|
OutputFormatFormField,
|
||||||
|
ParserMethodFormField,
|
||||||
|
} from './common-form-fields';
|
||||||
|
import { CommonProps } from './interface';
|
||||||
|
|
||||||
|
export function ImageFormFields({ prefix }: CommonProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
|
||||||
|
{/* Multimodal Model */}
|
||||||
|
<LargeModelFormField prefix={prefix}></LargeModelFormField>
|
||||||
|
<OutputFormatFormField
|
||||||
|
prefix={prefix}
|
||||||
|
fileType={FileType.Image}
|
||||||
|
></OutputFormatFormField>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,135 +1,172 @@
|
|||||||
import { FormContainer } from '@/components/form-container';
|
|
||||||
import NumberInput from '@/components/originui/number-input';
|
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
import {
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
Form,
|
import { BlockButton, Button } from '@/components/ui/button';
|
||||||
FormControl,
|
import { Form } from '@/components/ui/form';
|
||||||
FormField,
|
import { Separator } from '@/components/ui/separator';
|
||||||
FormItem,
|
import { cn } from '@/lib/utils';
|
||||||
FormLabel,
|
import { buildOptions } from '@/utils/form';
|
||||||
FormMessage,
|
|
||||||
} from '@/components/ui/form';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { memo } from 'react';
|
import { useHover } from 'ahooks';
|
||||||
import { useForm, useFormContext } from 'react-hook-form';
|
import { Trash2 } from 'lucide-react';
|
||||||
|
import { memo, useCallback, useMemo, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
UseFieldArrayRemove,
|
||||||
|
useFieldArray,
|
||||||
|
useForm,
|
||||||
|
useFormContext,
|
||||||
|
} from 'react-hook-form';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { initialParserValues } from '../../constant';
|
import { FileType, initialParserValues } from '../../constant';
|
||||||
import { useFormValues } from '../../hooks/use-form-values';
|
import { useFormValues } from '../../hooks/use-form-values';
|
||||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||||
import { INextOperatorForm } from '../../interface';
|
import { INextOperatorForm } from '../../interface';
|
||||||
import { GoogleCountryOptions, GoogleLanguageOptions } from '../../options';
|
|
||||||
import { buildOutputList } from '../../utils/build-output-list';
|
import { buildOutputList } from '../../utils/build-output-list';
|
||||||
import { ApiKeyField } from '../components/api-key-field';
|
|
||||||
import { FormWrapper } from '../components/form-wrapper';
|
|
||||||
import { Output } from '../components/output';
|
import { Output } from '../components/output';
|
||||||
import { QueryVariable } from '../components/query-variable';
|
import { OutputFormatFormField } from './common-form-fields';
|
||||||
|
import { EmailFormFields } from './email-form-fields';
|
||||||
|
import { ImageFormFields } from './image-form-fields';
|
||||||
|
import { PdfFormFields } from './pdf-form-fields';
|
||||||
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
import { VideoFormFields } from './video-form-fields';
|
||||||
|
|
||||||
const outputList = buildOutputList(initialParserValues.outputs);
|
const outputList = buildOutputList(initialParserValues.outputs);
|
||||||
|
|
||||||
export const GoogleFormPartialSchema = {
|
const FileFormatOptions = buildOptions(FileType);
|
||||||
api_key: z.string(),
|
|
||||||
country: z.string(),
|
const FileFormatWidgetMap = {
|
||||||
language: z.string(),
|
[FileType.PDF]: PdfFormFields,
|
||||||
|
[FileType.Video]: VideoFormFields,
|
||||||
|
[FileType.Audio]: VideoFormFields,
|
||||||
|
[FileType.Email]: EmailFormFields,
|
||||||
|
[FileType.Image]: ImageFormFields,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ParserItemProps = {
|
||||||
|
name: string;
|
||||||
|
index: number;
|
||||||
|
fieldLength: number;
|
||||||
|
remove: UseFieldArrayRemove;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormSchema = z.object({
|
export const FormSchema = z.object({
|
||||||
...GoogleFormPartialSchema,
|
parser: z.array(
|
||||||
q: z.string(),
|
z.object({
|
||||||
start: z.number(),
|
fileFormat: z.string().nullish(),
|
||||||
num: z.number(),
|
output_format: z.string().optional(),
|
||||||
|
parse_method: z.string().optional(),
|
||||||
|
llm_id: z.string().optional(),
|
||||||
|
lang: z.string().optional(),
|
||||||
|
fields: z.array(z.string()).optional(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function GoogleFormWidgets() {
|
export type FormSchemaType = z.infer<typeof FormSchema>;
|
||||||
const form = useFormContext();
|
|
||||||
const { t } = useTranslate('flow');
|
|
||||||
|
|
||||||
|
function ParserItem({ name, index, fieldLength, remove }: ParserItemProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const form = useFormContext<FormSchemaType>();
|
||||||
|
const ref = useRef(null);
|
||||||
|
const isHovering = useHover(ref);
|
||||||
|
|
||||||
|
const prefix = `${name}.${index}`;
|
||||||
|
const fileFormat = form.getValues(`parser.${index}.fileFormat`);
|
||||||
|
|
||||||
|
const values = form.getValues();
|
||||||
|
const parserList = values.parser.slice(); // Adding, deleting, or modifying the parser array will not change the reference.
|
||||||
|
|
||||||
|
const filteredFileFormatOptions = useMemo(() => {
|
||||||
|
const otherFileFormatList = parserList
|
||||||
|
.filter((_, idx) => idx !== index)
|
||||||
|
.map((x) => x.fileFormat);
|
||||||
|
|
||||||
|
return FileFormatOptions.filter((x) => {
|
||||||
|
return !otherFileFormatList.includes(x.value);
|
||||||
|
});
|
||||||
|
}, [index, parserList]);
|
||||||
|
|
||||||
|
const Widget =
|
||||||
|
typeof fileFormat === 'string' && fileFormat in FileFormatWidgetMap
|
||||||
|
? FileFormatWidgetMap[fileFormat as keyof typeof FileFormatWidgetMap]
|
||||||
|
: OutputFormatFormField;
|
||||||
return (
|
return (
|
||||||
<>
|
<section
|
||||||
<FormField
|
className={cn('space-y-5 px-5 py-2.5 rounded-md', {
|
||||||
control={form.control}
|
'bg-state-error-5': isHovering,
|
||||||
name={`country`}
|
})}
|
||||||
render={({ field }) => (
|
>
|
||||||
<FormItem className="flex-1">
|
<div className="flex justify-between items-center">
|
||||||
<FormLabel>{t('country')}</FormLabel>
|
<span className="text-text-primary text-sm font-medium">
|
||||||
<FormControl>
|
Parser {index}
|
||||||
<SelectWithSearch
|
</span>
|
||||||
{...field}
|
{index > 0 && (
|
||||||
options={GoogleCountryOptions}
|
<Button variant={'ghost'} onClick={() => remove(index)} ref={ref}>
|
||||||
></SelectWithSearch>
|
<Trash2 />
|
||||||
</FormControl>
|
</Button>
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
)}
|
||||||
/>
|
</div>
|
||||||
<FormField
|
<RAGFlowFormItem
|
||||||
control={form.control}
|
name={buildFieldNameWithPrefix(`fileFormat`, prefix)}
|
||||||
name={`language`}
|
label={t('dataflow.fileFormats')}
|
||||||
render={({ field }) => (
|
>
|
||||||
<FormItem className="flex-1">
|
<SelectWithSearch
|
||||||
<FormLabel>{t('language')}</FormLabel>
|
options={filteredFileFormatOptions}
|
||||||
<FormControl>
|
></SelectWithSearch>
|
||||||
<SelectWithSearch
|
</RAGFlowFormItem>
|
||||||
{...field}
|
<Widget prefix={prefix} fileType={fileFormat as FileType}></Widget>
|
||||||
options={GoogleLanguageOptions}
|
{index < fieldLength - 1 && <Separator />}
|
||||||
></SelectWithSearch>
|
</section>
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ParserForm = ({ node }: INextOperatorForm) => {
|
const ParserForm = ({ node }: INextOperatorForm) => {
|
||||||
const { t } = useTranslate('flow');
|
const { t } = useTranslation();
|
||||||
const defaultValues = useFormValues(initialParserValues, node);
|
const defaultValues = useFormValues(initialParserValues, node);
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema),
|
||||||
|
shouldUnregister: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const name = 'parser';
|
||||||
|
const { fields, remove, append } = useFieldArray({
|
||||||
|
name,
|
||||||
|
control: form.control,
|
||||||
|
});
|
||||||
|
|
||||||
|
const add = useCallback(() => {
|
||||||
|
append({
|
||||||
|
fileFormat: null,
|
||||||
|
output_format: '',
|
||||||
|
parse_method: '',
|
||||||
|
llm_id: '',
|
||||||
|
lang: '',
|
||||||
|
fields: [],
|
||||||
|
});
|
||||||
|
}, [append]);
|
||||||
|
|
||||||
useWatchFormChange(node?.id, form);
|
useWatchFormChange(node?.id, form);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<FormWrapper>
|
<form className="px-5">
|
||||||
<FormContainer>
|
{fields.map((field, index) => {
|
||||||
<QueryVariable name="q"></QueryVariable>
|
return (
|
||||||
</FormContainer>
|
<ParserItem
|
||||||
<FormContainer>
|
key={field.id}
|
||||||
<ApiKeyField placeholder={t('apiKeyPlaceholder')}></ApiKeyField>
|
name={name}
|
||||||
<FormField
|
index={index}
|
||||||
control={form.control}
|
fieldLength={fields.length}
|
||||||
name={`start`}
|
remove={remove}
|
||||||
render={({ field }) => (
|
></ParserItem>
|
||||||
<FormItem>
|
);
|
||||||
<FormLabel>{t('flowStart')}</FormLabel>
|
})}
|
||||||
<FormControl>
|
<BlockButton onClick={add} type="button" className="mt-2.5">
|
||||||
<NumberInput {...field} className="w-full"></NumberInput>
|
{t('dataflow.addParser')}
|
||||||
</FormControl>
|
</BlockButton>
|
||||||
<FormMessage />
|
</form>
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={`num`}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel>{t('flowNum')}</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<NumberInput {...field} className="w-full"></NumberInput>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<GoogleFormWidgets></GoogleFormWidgets>
|
|
||||||
</FormContainer>
|
|
||||||
</FormWrapper>
|
|
||||||
<div className="p-5">
|
<div className="p-5">
|
||||||
<Output list={outputList}></Output>
|
<Output list={outputList}></Output>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
3
web/src/pages/data-flow/form/parser-form/interface.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export type CommonProps = {
|
||||||
|
prefix: string;
|
||||||
|
};
|
||||||
29
web/src/pages/data-flow/form/parser-form/pdf-form-fields.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { CrossLanguageFormField } from '@/components/cross-language-form-field';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FileType } from '../../constant';
|
||||||
|
import {
|
||||||
|
LargeModelFormField,
|
||||||
|
OutputFormatFormField,
|
||||||
|
ParserMethodFormField,
|
||||||
|
} from './common-form-fields';
|
||||||
|
import { CommonProps } from './interface';
|
||||||
|
import { buildFieldNameWithPrefix } from './utils';
|
||||||
|
|
||||||
|
export function PdfFormFields({ prefix }: CommonProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ParserMethodFormField prefix={prefix}></ParserMethodFormField>
|
||||||
|
{/* Multimodal Model */}
|
||||||
|
<LargeModelFormField prefix={prefix}></LargeModelFormField>
|
||||||
|
<CrossLanguageFormField
|
||||||
|
name={buildFieldNameWithPrefix(`lang`, prefix)}
|
||||||
|
label={t('dataflow.lang')}
|
||||||
|
></CrossLanguageFormField>
|
||||||
|
<OutputFormatFormField
|
||||||
|
prefix={prefix}
|
||||||
|
fileType={FileType.Image}
|
||||||
|
></OutputFormatFormField>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
web/src/pages/data-flow/form/parser-form/utils.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function buildFieldNameWithPrefix(name: string, prefix: string) {
|
||||||
|
return `${prefix}.${name}`;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import {
|
||||||
|
LargeModelFormField,
|
||||||
|
OutputFormatFormField,
|
||||||
|
OutputFormatFormFieldProps,
|
||||||
|
} from './common-form-fields';
|
||||||
|
|
||||||
|
export function VideoFormFields({
|
||||||
|
prefix,
|
||||||
|
fileType,
|
||||||
|
}: OutputFormatFormFieldProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Multimodal Model */}
|
||||||
|
<LargeModelFormField prefix={prefix}></LargeModelFormField>
|
||||||
|
<OutputFormatFormField
|
||||||
|
prefix={prefix}
|
||||||
|
fileType={fileType}
|
||||||
|
></OutputFormatFormField>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
95
web/src/pages/data-flow/form/splitter-form/index.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { FormContainer } from '@/components/form-container';
|
||||||
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
|
import { SliderInputFormField } from '@/components/slider-input-form-field';
|
||||||
|
import { BlockButton, Button } from '@/components/ui/button';
|
||||||
|
import { Form } from '@/components/ui/form';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { X } from 'lucide-react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { useFieldArray, useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { initialChunkerValues, initialSplitterValues } from '../../constant';
|
||||||
|
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(initialSplitterValues.outputs);
|
||||||
|
|
||||||
|
export const FormSchema = z.object({
|
||||||
|
chunk_token_size: z.number(),
|
||||||
|
delimiters: z.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string().optional(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
overlapped_percent: z.number(), // 0.0 - 0.3
|
||||||
|
});
|
||||||
|
|
||||||
|
const SplitterForm = ({ node }: INextOperatorForm) => {
|
||||||
|
const defaultValues = useFormValues(initialChunkerValues, node);
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
defaultValues,
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
});
|
||||||
|
const name = 'delimiters';
|
||||||
|
|
||||||
|
const { fields, append, remove } = useFieldArray({
|
||||||
|
name: name,
|
||||||
|
control: form.control,
|
||||||
|
});
|
||||||
|
|
||||||
|
useWatchFormChange(node?.id, form);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<FormWrapper>
|
||||||
|
<FormContainer>
|
||||||
|
<SliderInputFormField
|
||||||
|
name="chunk_token_size"
|
||||||
|
max={2048}
|
||||||
|
label="chunk_token_size"
|
||||||
|
></SliderInputFormField>
|
||||||
|
<SliderInputFormField
|
||||||
|
name="overlapped_percent"
|
||||||
|
max={0.3}
|
||||||
|
min={0.1}
|
||||||
|
step={0.01}
|
||||||
|
label="overlapped_percent"
|
||||||
|
></SliderInputFormField>
|
||||||
|
<span>delimiters</span>
|
||||||
|
{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"
|
||||||
|
>
|
||||||
|
<Input className="!m-0"></Input>
|
||||||
|
</RAGFlowFormItem>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={'ghost'}
|
||||||
|
onClick={() => remove(index)}
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<BlockButton onClick={() => append({ value: '' })}>Add</BlockButton>
|
||||||
|
</FormContainer>
|
||||||
|
</FormWrapper>
|
||||||
|
<div className="p-5">
|
||||||
|
<Output list={outputList}></Output>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SplitterForm);
|
||||||
@ -18,6 +18,7 @@ import {
|
|||||||
initialCrawlerValues,
|
initialCrawlerValues,
|
||||||
initialEmailValues,
|
initialEmailValues,
|
||||||
initialExeSqlValues,
|
initialExeSqlValues,
|
||||||
|
initialHierarchicalMergerValues,
|
||||||
initialInvokeValues,
|
initialInvokeValues,
|
||||||
initialIterationStartValues,
|
initialIterationStartValues,
|
||||||
initialIterationValues,
|
initialIterationValues,
|
||||||
@ -28,6 +29,7 @@ import {
|
|||||||
initialRelevantValues,
|
initialRelevantValues,
|
||||||
initialRetrievalValues,
|
initialRetrievalValues,
|
||||||
initialRewriteQuestionValues,
|
initialRewriteQuestionValues,
|
||||||
|
initialSplitterValues,
|
||||||
initialStringTransformValues,
|
initialStringTransformValues,
|
||||||
initialSwitchValues,
|
initialSwitchValues,
|
||||||
initialTokenizerValues,
|
initialTokenizerValues,
|
||||||
@ -82,6 +84,8 @@ export const useInitializeOperatorParams = () => {
|
|||||||
[Operator.Parser]: initialParserValues,
|
[Operator.Parser]: initialParserValues,
|
||||||
[Operator.Chunker]: initialChunkerValues,
|
[Operator.Chunker]: initialChunkerValues,
|
||||||
[Operator.Tokenizer]: initialTokenizerValues,
|
[Operator.Tokenizer]: initialTokenizerValues,
|
||||||
|
[Operator.Splitter]: initialSplitterValues,
|
||||||
|
[Operator.HierarchicalMerger]: initialHierarchicalMergerValues,
|
||||||
};
|
};
|
||||||
}, [llmId]);
|
}, [llmId]);
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
QueryStringMap,
|
QueryStringMap,
|
||||||
useNavigatePage,
|
useNavigatePage,
|
||||||
} from '@/hooks/logic-hooks/navigate-hooks';
|
} from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
|
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
||||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||||
import { ChunkerContainer } from './chunker';
|
import { ChunkerContainer } from './chunker';
|
||||||
import { useGetDocumentUrl } from './components/document-preview/hooks';
|
import { useGetDocumentUrl } from './components/document-preview/hooks';
|
||||||
@ -60,6 +61,7 @@ const Chunk = () => {
|
|||||||
const handleStepChange = (id: number | string) => {
|
const handleStepChange = (id: number | string) => {
|
||||||
setActiveStepId(id);
|
setActiveStepId(id);
|
||||||
};
|
};
|
||||||
|
const { type } = useGetKnowledgeSearchParams();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
@ -87,12 +89,14 @@ const Chunk = () => {
|
|||||||
</BreadcrumbList>
|
</BreadcrumbList>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div className=" absolute ml-[50%] translate-x-[-50%] top-4 flex justify-center">
|
{type === 'dataflow' && (
|
||||||
<TimelineDataFlow
|
<div className=" absolute ml-[50%] translate-x-[-50%] top-4 flex justify-center">
|
||||||
activeFunc={handleStepChange}
|
<TimelineDataFlow
|
||||||
activeId={activeStepId}
|
activeFunc={handleStepChange}
|
||||||
/>
|
activeId={activeStepId}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={styles.chunkPage}>
|
<div className={styles.chunkPage}>
|
||||||
<div className="flex flex-none gap-8 border border-border mt-[26px] p-3 rounded-lg h-[calc(100vh-100px)]">
|
<div className="flex flex-none gap-8 border border-border mt-[26px] p-3 rounded-lg h-[calc(100vh-100px)]">
|
||||||
<div className="w-2/5">
|
<div className="w-2/5">
|
||||||
@ -110,7 +114,8 @@ const Chunk = () => {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-dvh border-r -mt-3"></div>
|
<div className="h-dvh border-r -mt-3"></div>
|
||||||
{activeStepId === TimelineNodeObj.chunker.id && <ChunkerContainer />}
|
{(activeStepId === TimelineNodeObj.chunker.id ||
|
||||||
|
type === 'chunk') && <ChunkerContainer />}
|
||||||
{activeStepId === TimelineNodeObj.parser.id && <ParserContainer />}
|
{activeStepId === TimelineNodeObj.parser.id && <ParserContainer />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ export enum LogTabs {
|
|||||||
DATASET_LOGS = 'datasetLogs',
|
DATASET_LOGS = 'datasetLogs',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum processingType {
|
export enum ProcessingType {
|
||||||
knowledgeGraph = 'knowledgeGraph',
|
knowledgeGraph = 'knowledgeGraph',
|
||||||
raptor = 'raptor',
|
raptor = 'raptor',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import {
|
import SvgIcon from '@/components/svg-icon';
|
||||||
CircleQuestionMark,
|
import { useIsDarkTheme } from '@/components/theme-provider';
|
||||||
Cpu,
|
import { toFixed } from '@/utils/common-util';
|
||||||
FileChartLine,
|
import { CircleQuestionMark } from 'lucide-react';
|
||||||
HardDriveDownload,
|
import { FC, useMemo, useState } from 'react';
|
||||||
} from 'lucide-react';
|
|
||||||
import { FC, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { LogTabs } from './dataset-common';
|
import { LogTabs } from './dataset-common';
|
||||||
import { DatasetFilter } from './dataset-filter';
|
import { DatasetFilter } from './dataset-filter';
|
||||||
@ -37,44 +35,43 @@ const StatCard: FC<StatCardProps> = ({ title, value, children, icon }) => {
|
|||||||
|
|
||||||
interface CardFooterProcessProps {
|
interface CardFooterProcessProps {
|
||||||
total: number;
|
total: number;
|
||||||
completed: number;
|
|
||||||
success: number;
|
success: number;
|
||||||
failed: number;
|
failed: number;
|
||||||
}
|
}
|
||||||
const CardFooterProcess: FC<CardFooterProcessProps> = ({
|
const CardFooterProcess: FC<CardFooterProcessProps> = ({
|
||||||
total,
|
total,
|
||||||
completed,
|
success = 0,
|
||||||
success,
|
failed = 0,
|
||||||
failed,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const successPrecentage = (success / total) * 100;
|
const successPrecentage = total ? (success / total) * 100 : 0;
|
||||||
const failedPrecentage = (failed / total) * 100;
|
const failedPrecentage = total ? (failed / total) * 100 : 0;
|
||||||
|
const completedPercentage = total ? ((success + failed) / total) * 100 : 0;
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center flex-col gap-2">
|
<div className="flex items-center flex-col gap-2">
|
||||||
<div className="flex justify-between w-full text-sm text-text-secondary">
|
<div className="flex justify-between w-full text-sm text-text-secondary">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{success}
|
{success || 0}
|
||||||
<span>{t('knowledgeDetails.success')}</span>
|
<span>{t('knowledgeDetails.success')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{failed}
|
{failed || 0}
|
||||||
<span>{t('knowledgeDetails.failed')}</span>
|
<span>{t('knowledgeDetails.failed')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
{completed}
|
{toFixed(completedPercentage) as string}%
|
||||||
<span>{t('knowledgeDetails.completed')}</span>
|
<span>{t('knowledgeDetails.completed')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex rounded-full h-3 bg-bg-card text-sm font-bold text-text-primary">
|
<div className="w-full flex rounded-full h-1.5 bg-bg-card text-sm font-bold text-text-primary">
|
||||||
<div
|
<div
|
||||||
className=" rounded-full h-3 bg-accent-primary"
|
className=" rounded-full h-1.5 bg-accent-primary"
|
||||||
style={{ width: successPrecentage + '%' }}
|
style={{ width: successPrecentage + '%' }}
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
className=" rounded-full h-3 bg-state-error"
|
className=" rounded-full h-1.5 bg-state-error"
|
||||||
style={{ width: failedPrecentage + '%' }}
|
style={{ width: failedPrecentage + '%' }}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
@ -86,24 +83,67 @@ const FileLogsPage: FC = () => {
|
|||||||
const [active, setActive] = useState<(typeof LogTabs)[keyof typeof LogTabs]>(
|
const [active, setActive] = useState<(typeof LogTabs)[keyof typeof LogTabs]>(
|
||||||
LogTabs.FILE_LOGS,
|
LogTabs.FILE_LOGS,
|
||||||
);
|
);
|
||||||
const mockData = Array(30)
|
const topMockData = {
|
||||||
.fill(0)
|
totalFiles: {
|
||||||
.map((_, i) => ({
|
value: 2827,
|
||||||
id: i === 0 ? '#952734' : `14`,
|
precent: 12.5,
|
||||||
fileName: 'PRD for DealBees 1.2 (1).txt',
|
},
|
||||||
source: 'GitHub',
|
downloads: {
|
||||||
pipeline: i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kiki’s demo',
|
value: 28,
|
||||||
startDate: '14/03/2025 14:53:39',
|
success: 8,
|
||||||
task: i === 0 ? 'Parse' : 'Parser',
|
failed: 2,
|
||||||
status:
|
},
|
||||||
i === 0
|
processing: {
|
||||||
? 'Success'
|
value: 156,
|
||||||
: i === 1
|
success: 8,
|
||||||
? 'Failed'
|
failed: 2,
|
||||||
: i === 2
|
},
|
||||||
? 'Running'
|
};
|
||||||
: 'Pending',
|
const mockData = useMemo(() => {
|
||||||
}));
|
if (active === LogTabs.FILE_LOGS) {
|
||||||
|
return Array(30)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, i) => ({
|
||||||
|
id: i === 0 ? '952734' : `14`,
|
||||||
|
fileName: 'PRD for DealBees 1.2 (1).txt',
|
||||||
|
source: 'GitHub',
|
||||||
|
pipeline:
|
||||||
|
i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kiki’s demo',
|
||||||
|
startDate: '14/03/2025 14:53:39',
|
||||||
|
task: i === 0 ? 'chunck' : 'Parser',
|
||||||
|
status: i === 0 ? 3 : i === 1 ? 4 : i === 2 ? 1 : 0,
|
||||||
|
statusName:
|
||||||
|
i === 0
|
||||||
|
? 'Success'
|
||||||
|
: i === 1
|
||||||
|
? 'Failed'
|
||||||
|
: i === 2
|
||||||
|
? 'Running'
|
||||||
|
: 'Pending',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (active === LogTabs.DATASET_LOGS) {
|
||||||
|
return Array(8)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, i) => ({
|
||||||
|
id: i === 0 ? '952734' : `14`,
|
||||||
|
fileName: 'PRD for DealBees 1.2 (1).txt',
|
||||||
|
source: 'GitHub',
|
||||||
|
startDate: '14/03/2025 14:53:39',
|
||||||
|
task: i === 0 ? 'chunck' : 'Parser',
|
||||||
|
pipeline:
|
||||||
|
i === 0 ? 'data demo for...' : i === 1 ? 'test' : 'kiki’s demo',
|
||||||
|
status:
|
||||||
|
i === 0
|
||||||
|
? 'Success'
|
||||||
|
: i === 1
|
||||||
|
? 'Failed'
|
||||||
|
: i === 2
|
||||||
|
? 'Running'
|
||||||
|
: 'Pending',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [active]);
|
||||||
|
|
||||||
const pagination = {
|
const pagination = {
|
||||||
current: 1,
|
current: 1,
|
||||||
@ -118,23 +158,63 @@ const FileLogsPage: FC = () => {
|
|||||||
console.log('Pagination changed:', { page, pageSize });
|
console.log('Pagination changed:', { page, pageSize });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isDark = useIsDarkTheme();
|
||||||
return (
|
return (
|
||||||
<div className="p-5 min-w-[880px] border-border border rounded-lg mr-5">
|
<div className="p-5 min-w-[880px] border-border border rounded-lg mr-5">
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<div className="grid grid-cols-3 md:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-3 md:grid-cols-3 gap-4 mb-6">
|
||||||
<StatCard title="Total Files" value={2827} icon={<FileChartLine />}>
|
<StatCard
|
||||||
<div>+7% from last week</div>
|
title="Total Files"
|
||||||
|
value={topMockData.totalFiles.value}
|
||||||
|
icon={
|
||||||
|
isDark ? (
|
||||||
|
<SvgIcon name="data-flow/total-files-icon" width={40} />
|
||||||
|
) : (
|
||||||
|
<SvgIcon name="data-flow/total-files-icon-bri" width={40} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span className="text-accent-primary">
|
||||||
|
{topMockData.totalFiles.precent > 0 ? '+' : ''}
|
||||||
|
{topMockData.totalFiles.precent}%{' '}
|
||||||
|
</span>
|
||||||
|
from last week
|
||||||
|
</div>
|
||||||
</StatCard>
|
</StatCard>
|
||||||
<StatCard title="Downloading" value={28} icon={<HardDriveDownload />}>
|
<StatCard
|
||||||
|
title="Downloading"
|
||||||
|
value={topMockData.downloads.value}
|
||||||
|
icon={
|
||||||
|
isDark ? (
|
||||||
|
<SvgIcon name="data-flow/data-icon" width={40} />
|
||||||
|
) : (
|
||||||
|
<SvgIcon name="data-flow/data-icon-bri" width={40} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
<CardFooterProcess
|
<CardFooterProcess
|
||||||
total={100}
|
total={topMockData.downloads.value}
|
||||||
success={8}
|
success={topMockData.downloads.success}
|
||||||
failed={2}
|
failed={topMockData.downloads.failed}
|
||||||
completed={15}
|
|
||||||
/>
|
/>
|
||||||
</StatCard>
|
</StatCard>
|
||||||
<StatCard title="Processing" value={156} icon={<Cpu />}>
|
<StatCard
|
||||||
<CardFooterProcess total={20} success={8} failed={2} completed={15} />
|
title="Processing"
|
||||||
|
value={topMockData.processing.value}
|
||||||
|
icon={
|
||||||
|
isDark ? (
|
||||||
|
<SvgIcon name="data-flow/processing-icon" width={40} />
|
||||||
|
) : (
|
||||||
|
<SvgIcon name="data-flow/processing-icon-bri" width={40} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CardFooterProcess
|
||||||
|
total={topMockData.processing.value}
|
||||||
|
success={topMockData.processing.success}
|
||||||
|
failed={topMockData.processing.failed}
|
||||||
|
/>
|
||||||
</StatCard>
|
</StatCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import {
|
|||||||
} from '@/components/ui/table';
|
} from '@/components/ui/table';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
import ProcessLogModal from '@/pages/datasets/process-log-modal';
|
|
||||||
import {
|
import {
|
||||||
ColumnDef,
|
ColumnDef,
|
||||||
ColumnFiltersState,
|
ColumnFiltersState,
|
||||||
|
Row,
|
||||||
SortingState,
|
SortingState,
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
@ -29,7 +29,8 @@ import {
|
|||||||
import { TFunction } from 'i18next';
|
import { TFunction } from 'i18next';
|
||||||
import { ClipboardList, Eye } from 'lucide-react';
|
import { ClipboardList, Eye } from 'lucide-react';
|
||||||
import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react';
|
import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react';
|
||||||
import { LogTabs, processingType } from './dataset-common';
|
import ProcessLogModal from '../process-log-modal';
|
||||||
|
import { LogTabs, ProcessingType } from './dataset-common';
|
||||||
|
|
||||||
interface DocumentLog {
|
interface DocumentLog {
|
||||||
id: string;
|
id: string;
|
||||||
@ -144,7 +145,12 @@ export const getFileLogsTableColumns = (
|
|||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
header: t('status'),
|
header: t('status'),
|
||||||
cell: ({ row }) => <FileStatusBadge status={row.original.status} />,
|
cell: ({ row }) => (
|
||||||
|
<FileStatusBadge
|
||||||
|
status={row.original.status}
|
||||||
|
name={row.original.statusName}
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'operations',
|
id: 'operations',
|
||||||
@ -179,7 +185,7 @@ export const getFileLogsTableColumns = (
|
|||||||
|
|
||||||
export const getDatasetLogsTableColumns = (
|
export const getDatasetLogsTableColumns = (
|
||||||
t: TFunction<'translation', string>,
|
t: TFunction<'translation', string>,
|
||||||
setIsModalVisible: Dispatch<SetStateAction<boolean>>,
|
showLog: (row: Row<DocumentLog>, active: LogTabs) => void,
|
||||||
) => {
|
) => {
|
||||||
// const { t } = useTranslate('knowledgeDetails');
|
// const { t } = useTranslate('knowledgeDetails');
|
||||||
const columns: ColumnDef<DocumentLog>[] = [
|
const columns: ColumnDef<DocumentLog>[] = [
|
||||||
@ -221,10 +227,10 @@ export const getDatasetLogsTableColumns = (
|
|||||||
header: t('processingType'),
|
header: t('processingType'),
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center gap-2 text-text-primary">
|
<div className="flex items-center gap-2 text-text-primary">
|
||||||
{processingType.knowledgeGraph === row.original.processingType && (
|
{ProcessingType.knowledgeGraph === row.original.processingType && (
|
||||||
<SvgIcon name={`data-flow/knowledgegraph`} width={24}></SvgIcon>
|
<SvgIcon name={`data-flow/knowledgegraph`} width={24}></SvgIcon>
|
||||||
)}
|
)}
|
||||||
{processingType.raptor === row.original.processingType && (
|
{ProcessingType.raptor === row.original.processingType && (
|
||||||
<SvgIcon name={`data-flow/raptor`} width={24}></SvgIcon>
|
<SvgIcon name={`data-flow/raptor`} width={24}></SvgIcon>
|
||||||
)}
|
)}
|
||||||
{row.original.processingType}
|
{row.original.processingType}
|
||||||
@ -234,7 +240,12 @@ export const getDatasetLogsTableColumns = (
|
|||||||
{
|
{
|
||||||
accessorKey: 'status',
|
accessorKey: 'status',
|
||||||
header: t('status'),
|
header: t('status'),
|
||||||
cell: ({ row }) => <FileStatusBadge status={row.original.status} />,
|
cell: ({ row }) => (
|
||||||
|
<FileStatusBadge
|
||||||
|
status={row.original.status}
|
||||||
|
name={row.original.statusName}
|
||||||
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'operations',
|
id: 'operations',
|
||||||
@ -246,7 +257,7 @@ export const getDatasetLogsTableColumns = (
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="p-1"
|
className="p-1"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsModalVisible(true);
|
showLog(row, LogTabs.DATASET_LOGS);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Eye />
|
<Eye />
|
||||||
@ -259,6 +270,18 @@ export const getDatasetLogsTableColumns = (
|
|||||||
return columns;
|
return columns;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const taskInfo = {
|
||||||
|
taskId: '#9527',
|
||||||
|
fileName: 'PRD for DealBees 1.2 (1).text',
|
||||||
|
fileSize: '2.4G',
|
||||||
|
source: 'Github',
|
||||||
|
task: 'Parse',
|
||||||
|
state: 'Running',
|
||||||
|
startTime: '14/03/2025 14:53:39',
|
||||||
|
duration: '800',
|
||||||
|
details:
|
||||||
|
'\n17:43:21 Task has been received.\n17:43:25 Page(1~100000001): Start to parse.\n17:43:25 Page(1~100000001): Start to tag for every chunk ...\n17:43:45 Page(1~100000001): Tagging 2 chunks completed in 18.99s\n17:43:45 Page(1~100000001): Generate 2 chunks\n17:43:55 Page(1~100000001): Embedding chunks (10.60s)\n17:43:55 Page(1~100000001): Indexing done (0.07s). Task done (33.97s)\n17:43:58 created task raptor\n17:43:58 Task has been received.\n17:44:36 Cluster one layer: 2 -> 1\n17:44:36 Indexing done (0.05s). Task done (37.88s)\n17:44:40 created task graphrag\n17:44:41 Task has been received.\n17:50:57 Entities extraction of chunk 0 1/3 done, 25 nodes, 26 edges, 14893 tokens.\n17:56:01 [ERROR][Exception]: Operation timed out after 7200 seconds and 1 attempts.',
|
||||||
|
};
|
||||||
const FileLogsTable: FC<FileLogsTableProps> = ({
|
const FileLogsTable: FC<FileLogsTableProps> = ({
|
||||||
data,
|
data,
|
||||||
pagination,
|
pagination,
|
||||||
@ -272,11 +295,16 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
|
|||||||
const { t } = useTranslate('knowledgeDetails');
|
const { t } = useTranslate('knowledgeDetails');
|
||||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
const { navigateToDataflowResult } = useNavigatePage();
|
const { navigateToDataflowResult } = useNavigatePage();
|
||||||
|
const [logInfo, setLogInfo] = useState(taskInfo);
|
||||||
|
const showLog = (row: Row<DocumentLog>, active: LogTabs) => {
|
||||||
|
setLogInfo(row.original);
|
||||||
|
setIsModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
const columns = useMemo(() => {
|
const columns = useMemo(() => {
|
||||||
console.log('columns', active);
|
|
||||||
return active === LogTabs.FILE_LOGS
|
return active === LogTabs.FILE_LOGS
|
||||||
? getFileLogsTableColumns(t, setIsModalVisible, navigateToDataflowResult)
|
? getFileLogsTableColumns(t, setIsModalVisible, navigateToDataflowResult)
|
||||||
: getDatasetLogsTableColumns(t, setIsModalVisible);
|
: getDatasetLogsTableColumns(t, showLog);
|
||||||
}, [active, t]);
|
}, [active, t]);
|
||||||
|
|
||||||
const currentPagination = useMemo(
|
const currentPagination = useMemo(
|
||||||
@ -308,17 +336,6 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
|
|||||||
? Math.ceil(pagination.total / pagination.pageSize)
|
? Math.ceil(pagination.total / pagination.pageSize)
|
||||||
: 0,
|
: 0,
|
||||||
});
|
});
|
||||||
const taskInfo = {
|
|
||||||
taskId: '#9527',
|
|
||||||
fileName: 'PRD for DealBees 1.2 (1).text',
|
|
||||||
fileSize: '2.4G',
|
|
||||||
source: 'Github',
|
|
||||||
task: 'Parse',
|
|
||||||
state: 'Running',
|
|
||||||
startTime: '14/03/2025 14:53:39',
|
|
||||||
duration: '800',
|
|
||||||
details: 'PRD for DealBees 1.2 (1).text',
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-[calc(100vh-350px)]">
|
<div className="w-full h-[calc(100vh-350px)]">
|
||||||
<Table rootClassName="max-h-[calc(100vh-380px)]">
|
<Table rootClassName="max-h-[calc(100vh-380px)]">
|
||||||
@ -363,7 +380,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
|
|||||||
)}
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
<div className="flex items-center justify-end py-4 absolute bottom-3 right-12">
|
<div className="flex items-center justify-end absolute bottom-3 right-12">
|
||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
<RAGFlowPagination
|
<RAGFlowPagination
|
||||||
{...{ current: pagination.current, pageSize: pagination.pageSize }}
|
{...{ current: pagination.current, pageSize: pagination.pageSize }}
|
||||||
@ -375,7 +392,7 @@ const FileLogsTable: FC<FileLogsTableProps> = ({
|
|||||||
<ProcessLogModal
|
<ProcessLogModal
|
||||||
visible={isModalVisible}
|
visible={isModalVisible}
|
||||||
onCancel={() => setIsModalVisible(false)}
|
onCancel={() => setIsModalVisible(false)}
|
||||||
taskInfo={taskInfo}
|
logInfo={logInfo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { useFormContext } from 'react-hook-form';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { NaiveConfiguration } from './naive';
|
|
||||||
import { SavingButton } from './saving-button';
|
|
||||||
|
|
||||||
export function ChunkMethodForm() {
|
|
||||||
const form = useFormContext();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="h-full flex flex-col">
|
|
||||||
<div className="overflow-auto flex-1 min-h-0">
|
|
||||||
<NaiveConfiguration></NaiveConfiguration>
|
|
||||||
</div>
|
|
||||||
<div className="text-right pt-4 flex justify-end gap-3">
|
|
||||||
<Button
|
|
||||||
type="reset"
|
|
||||||
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
|
|
||||||
onClick={() => {
|
|
||||||
form.reset();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('knowledgeConfiguration.cancel')}
|
|
||||||
</Button>
|
|
||||||
<SavingButton></SavingButton>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Link, Route, Settings2, Unlink } from 'lucide-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
interface DataPipelineItemProps {
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
isDefault?: boolean;
|
||||||
|
linked?: boolean;
|
||||||
|
}
|
||||||
|
const DataPipelineItem = (props: DataPipelineItemProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { name, avatar, isDefault, linked } = props;
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between gap-1 px-2 rounded-lg border">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<RAGFlowAvatar avatar={avatar} name={name} className="size-4" />
|
||||||
|
<div>{name}</div>
|
||||||
|
{isDefault && (
|
||||||
|
<div className="text-xs bg-text-secondary text-bg-base px-2 py-1 rounded-md">
|
||||||
|
{t('knowledgeConfiguration.default')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-1 items-center">
|
||||||
|
<Button variant={'transparent'} className="border-none">
|
||||||
|
<Settings2 />
|
||||||
|
</Button>
|
||||||
|
<Button variant={'transparent'} className="border-none">
|
||||||
|
{linked ? <Link /> : <Unlink />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const LinkDataPipeline = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const testNode = [
|
||||||
|
{
|
||||||
|
name: 'Data Pipeline 1',
|
||||||
|
avatar: 'https://avatars.githubusercontent.com/u/10656201?v=4',
|
||||||
|
isDefault: true,
|
||||||
|
linked: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Data Pipeline 2',
|
||||||
|
avatar: 'https://avatars.githubusercontent.com/u/10656201?v=4',
|
||||||
|
linked: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<section className="flex flex-col">
|
||||||
|
<div className="flex items-center gap-1 text-text-primary text-sm">
|
||||||
|
<Route className="size-4" />
|
||||||
|
{t('knowledgeConfiguration.dataPipeline')}
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="text-center text-xs text-text-secondary">
|
||||||
|
Manage data pipeline linkage with this dataset
|
||||||
|
</div>
|
||||||
|
<Button variant={'transparent'}>
|
||||||
|
<Link />
|
||||||
|
<span className="text-xs text-text-primary">
|
||||||
|
{t('knowledgeConfiguration.linkDataPipeline')}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section className="flex flex-col gap-2">
|
||||||
|
{testNode.map((item) => (
|
||||||
|
<DataPipelineItem key={item.name} {...item} />
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default LinkDataPipeline;
|
||||||
@ -10,7 +10,6 @@ import { RAGFlowSelect } from '@/components/ui/select';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { ArrowUpRight } from 'lucide-react';
|
|
||||||
import { useFormContext } from 'react-hook-form';
|
import { useFormContext } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
useHasParsedDocument,
|
useHasParsedDocument,
|
||||||
@ -70,8 +69,13 @@ export function EmbeddingModelItem({ line = 1 }: { line?: 1 | 2 }) {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name={'embd_id'}
|
name={'embd_id'}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className=" items-center space-y-0 ">
|
<FormItem className={cn(' items-center space-y-0 ')}>
|
||||||
<div className={cn({ 'flex items-center': line === 1 })}>
|
<div
|
||||||
|
className={cn('flex', {
|
||||||
|
' items-center': line === 1,
|
||||||
|
'flex-col gap-1': line === 2,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<FormLabel
|
<FormLabel
|
||||||
required
|
required
|
||||||
tooltip={t('embeddingModelTip')}
|
tooltip={t('embeddingModelTip')}
|
||||||
@ -142,155 +146,6 @@ export function ParseTypeItem() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataFlowItem() {
|
|
||||||
const { t } = useTranslate('knowledgeConfiguration');
|
|
||||||
const form = useFormContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={'data_flow'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className=" items-center space-y-0 ">
|
|
||||||
<div className="">
|
|
||||||
<div className="flex gap-2 justify-between ">
|
|
||||||
<FormLabel
|
|
||||||
tooltip={t('dataFlowTip')}
|
|
||||||
className="text-sm text-text-primary whitespace-wrap "
|
|
||||||
>
|
|
||||||
{t('dataFlow')}
|
|
||||||
</FormLabel>
|
|
||||||
<div className="text-sm flex text-text-primary">
|
|
||||||
{t('buildItFromScratch')}
|
|
||||||
<ArrowUpRight size={14} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
placeholder={t('dataFlowPlaceholder')}
|
|
||||||
options={[{ value: '0', label: t('dataFlowDefault') }]}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex pt-1">
|
|
||||||
<div className="w-1/4"></div>
|
|
||||||
<FormMessage />
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DataExtractKnowledgeItem() {
|
|
||||||
const { t } = useTranslate('knowledgeConfiguration');
|
|
||||||
const form = useFormContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={'extractKnowledgeGraph'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className=" items-center space-y-0 ">
|
|
||||||
<div className="">
|
|
||||||
<FormLabel
|
|
||||||
tooltip={t('extractKnowledgeGraphTip')}
|
|
||||||
className="text-sm whitespace-wrap "
|
|
||||||
>
|
|
||||||
{t('extractKnowledgeGraph')}
|
|
||||||
</FormLabel>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex pt-1">
|
|
||||||
<div className="w-1/4"></div>
|
|
||||||
<FormMessage />
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>{' '}
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={'useRAPTORToEnhanceRetrieval'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className=" items-center space-y-0 ">
|
|
||||||
<div className="">
|
|
||||||
<FormLabel
|
|
||||||
tooltip={t('useRAPTORToEnhanceRetrievalTip')}
|
|
||||||
className="text-sm whitespace-wrap "
|
|
||||||
>
|
|
||||||
{t('useRAPTORToEnhanceRetrieval')}
|
|
||||||
</FormLabel>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex pt-1">
|
|
||||||
<div className="w-1/4"></div>
|
|
||||||
<FormMessage />
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TeamItem() {
|
|
||||||
const { t } = useTranslate('knowledgeConfiguration');
|
|
||||||
const form = useFormContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={'team'}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className=" items-center space-y-0 ">
|
|
||||||
<div className="">
|
|
||||||
<FormLabel
|
|
||||||
tooltip={t('teamTip')}
|
|
||||||
className="text-sm whitespace-wrap "
|
|
||||||
>
|
|
||||||
<span className="text-destructive mr-1"> *</span>
|
|
||||||
{t('team')}
|
|
||||||
</FormLabel>
|
|
||||||
<div className="text-muted-foreground">
|
|
||||||
<FormControl>
|
|
||||||
<RAGFlowSelect
|
|
||||||
{...field}
|
|
||||||
placeholder={t('teamPlaceholder')}
|
|
||||||
options={[{ value: '0', label: t('teamDefault') }]}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex pt-1">
|
|
||||||
<div className="w-1/4"></div>
|
|
||||||
<FormMessage />
|
|
||||||
</div>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnableAutoGenerateItem() {
|
export function EnableAutoGenerateItem() {
|
||||||
const { t } = useTranslate('knowledgeConfiguration');
|
const { t } = useTranslate('knowledgeConfiguration');
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export function GeneralForm() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="space-y-4">
|
<>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
@ -87,6 +87,6 @@ export function GeneralForm() {
|
|||||||
/>
|
/>
|
||||||
<PermissionFormField></PermissionFormField>
|
<PermissionFormField></PermissionFormField>
|
||||||
<EmbeddingModelItem></EmbeddingModelItem>
|
<EmbeddingModelItem></EmbeddingModelItem>
|
||||||
</section>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
||||||
|
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import Divider from '@/components/ui/divider';
|
||||||
import { Form } from '@/components/ui/form';
|
import { Form } from '@/components/ui/form';
|
||||||
import { DocumentParserType } from '@/constants/knowledge';
|
import { DocumentParserType } from '@/constants/knowledge';
|
||||||
import { PermissionRole } from '@/constants/permission';
|
import { PermissionRole } from '@/constants/permission';
|
||||||
@ -6,11 +10,12 @@ import { useForm } from 'react-hook-form';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { TopTitle } from '../dataset-title';
|
import { TopTitle } from '../dataset-title';
|
||||||
import { ChunkMethodForm } from './chunk-method-form';
|
import LinkDataPipeline from './components/link-data-pipeline';
|
||||||
|
import { MainContainer } from './configuration-form-container';
|
||||||
import { formSchema } from './form-schema';
|
import { formSchema } from './form-schema';
|
||||||
import { GeneralForm } from './general-form';
|
import { GeneralForm } from './general-form';
|
||||||
import { useFetchKnowledgeConfigurationOnMount } from './hooks';
|
import { useFetchKnowledgeConfigurationOnMount } from './hooks';
|
||||||
|
import { SavingButton } from './saving-button';
|
||||||
const enum DocumentType {
|
const enum DocumentType {
|
||||||
DeepDOC = 'DeepDOC',
|
DeepDOC = 'DeepDOC',
|
||||||
PlainText = 'Plain Text',
|
PlainText = 'Plain Text',
|
||||||
@ -76,9 +81,29 @@ export default function DatasetSettings() {
|
|||||||
onSubmit={form.handleSubmit(onSubmit)}
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
className="space-y-6 flex-1"
|
className="space-y-6 flex-1"
|
||||||
>
|
>
|
||||||
<div className="w-[768px]">
|
<div className="w-[768px] h-[calc(100vh-240px)] pr-1 overflow-y-auto scrollbar-auto">
|
||||||
<GeneralForm></GeneralForm>
|
<MainContainer>
|
||||||
<ChunkMethodForm></ChunkMethodForm>
|
<GeneralForm></GeneralForm>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<GraphRagItems className="border-none p-0"></GraphRagItems>
|
||||||
|
<Divider />
|
||||||
|
<RaptorFormFields></RaptorFormFields>
|
||||||
|
<Divider />
|
||||||
|
<LinkDataPipeline />
|
||||||
|
</MainContainer>
|
||||||
|
</div>
|
||||||
|
<div className="text-right items-center flex justify-end gap-3 w-[768px]">
|
||||||
|
<Button
|
||||||
|
type="reset"
|
||||||
|
className="bg-transparent text-color-white hover:bg-transparent border-gray-500 border-[1px]"
|
||||||
|
onClick={() => {
|
||||||
|
form.reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('knowledgeConfiguration.cancel')}
|
||||||
|
</Button>
|
||||||
|
<SavingButton></SavingButton>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
import GraphRagItems from '@/components/parse-configuration/graph-rag-form-fields';
|
|
||||||
import RaptorFormFields from '@/components/parse-configuration/raptor-form-fields';
|
|
||||||
import {
|
|
||||||
ConfigurationFormContainer,
|
|
||||||
MainContainer,
|
|
||||||
} from './configuration-form-container';
|
|
||||||
import { EnableAutoGenerateItem } from './configuration/common-item';
|
|
||||||
|
|
||||||
export function NaiveConfiguration() {
|
|
||||||
return (
|
|
||||||
<MainContainer>
|
|
||||||
<GraphRagItems className="border-none p-0"></GraphRagItems>
|
|
||||||
<ConfigurationFormContainer>
|
|
||||||
<RaptorFormFields></RaptorFormFields>
|
|
||||||
</ConfigurationFormContainer>
|
|
||||||
<EnableAutoGenerateItem />
|
|
||||||
{/* <ConfigurationFormContainer>
|
|
||||||
<ChunkMethodItem></ChunkMethodItem>
|
|
||||||
<LayoutRecognizeFormField></LayoutRecognizeFormField>
|
|
||||||
|
|
||||||
<MaxTokenNumberFormField initialValue={512}></MaxTokenNumberFormField>
|
|
||||||
<DelimiterFormField></DelimiterFormField>
|
|
||||||
</ConfigurationFormContainer>
|
|
||||||
<ConfigurationFormContainer>
|
|
||||||
<PageRankFormField></PageRankFormField>
|
|
||||||
<AutoKeywordsFormField></AutoKeywordsFormField>
|
|
||||||
<AutoQuestionsFormField></AutoQuestionsFormField>
|
|
||||||
<ExcelToHtmlFormField></ExcelToHtmlFormField>
|
|
||||||
<TagItems></TagItems>
|
|
||||||
</ConfigurationFormContainer> */}
|
|
||||||
</MainContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -29,6 +29,8 @@ import { useFetchDocumentList } from '@/hooks/use-document-request';
|
|||||||
import { getExtension } from '@/utils/document-util';
|
import { getExtension } from '@/utils/document-util';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import ProcessLogModal from '../process-log-modal';
|
||||||
|
import { useShowLog } from './hooks';
|
||||||
import { SetMetaDialog } from './set-meta-dialog';
|
import { SetMetaDialog } from './set-meta-dialog';
|
||||||
import { useChangeDocumentParser } from './use-change-document-parser';
|
import { useChangeDocumentParser } from './use-change-document-parser';
|
||||||
import { useDatasetTableColumns } from './use-dataset-table-columns';
|
import { useDatasetTableColumns } from './use-dataset-table-columns';
|
||||||
@ -81,11 +83,13 @@ export function DatasetTable({
|
|||||||
onSetMetaModalOk,
|
onSetMetaModalOk,
|
||||||
metaRecord,
|
metaRecord,
|
||||||
} = useSaveMeta();
|
} = useSaveMeta();
|
||||||
|
const { showLog, logInfo, logVisible, hideLog } = useShowLog(documents);
|
||||||
|
|
||||||
const columns = useDatasetTableColumns({
|
const columns = useDatasetTableColumns({
|
||||||
showChangeParserModal,
|
showChangeParserModal,
|
||||||
showRenameModal,
|
showRenameModal,
|
||||||
showSetMetaModal,
|
showSetMetaModal,
|
||||||
|
showLog,
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentPagination = useMemo(() => {
|
const currentPagination = useMemo(() => {
|
||||||
@ -207,6 +211,13 @@ export function DatasetTable({
|
|||||||
initialMetaData={metaRecord.meta_fields}
|
initialMetaData={metaRecord.meta_fields}
|
||||||
></SetMetaDialog>
|
></SetMetaDialog>
|
||||||
)}
|
)}
|
||||||
|
{logVisible && (
|
||||||
|
<ProcessLogModal
|
||||||
|
visible={logVisible}
|
||||||
|
onCancel={() => hideLog()}
|
||||||
|
logInfo={logInfo}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
113
web/src/pages/dataset/dataset/generate-button/generate.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { IconFontFill } from '@/components/icon-font';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
|
import { toFixed } from '@/utils/common-util';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { lowerFirst } from 'lodash';
|
||||||
|
import { CirclePause, WandSparkles } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { generateStatus, useFetchGenerateData } from './hook';
|
||||||
|
|
||||||
|
const MenuItem: React.FC<{ name: 'KnowledgeGraph' | 'Raptor' }> = ({
|
||||||
|
name,
|
||||||
|
}) => {
|
||||||
|
console.log(name, 'pppp');
|
||||||
|
const iconKeyMap = {
|
||||||
|
KnowledgeGraph: 'knowledgegraph',
|
||||||
|
Raptor: 'dataflow-01',
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
data: { percent, type },
|
||||||
|
pauseGenerate,
|
||||||
|
} = useFetchGenerateData();
|
||||||
|
return (
|
||||||
|
<div className="flex items-start gap-2 flex-col w-full">
|
||||||
|
<div className="flex justify-start text-text-primary items-center gap-2">
|
||||||
|
<IconFontFill name={iconKeyMap[name]} className="text-accent-primary" />
|
||||||
|
{t(`knowledgeDetails.${lowerFirst(name)}`)}
|
||||||
|
</div>
|
||||||
|
{type === generateStatus.start && (
|
||||||
|
<div className="text-text-secondary text-sm">
|
||||||
|
{t(`knowledgeDetails.generate${name}`)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{type === generateStatus.running && (
|
||||||
|
<div className="flex justify-between items-center w-full">
|
||||||
|
<div className="w-[calc(100%-100px)] bg-border-button h-1 rounded-full">
|
||||||
|
<div
|
||||||
|
className="h-1 bg-accent-primary rounded-full"
|
||||||
|
style={{ width: `${toFixed(percent)}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span>{toFixed(percent) as string}%</span>
|
||||||
|
<span
|
||||||
|
className="text-state-error"
|
||||||
|
onClick={() => {
|
||||||
|
pauseGenerate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CirclePause />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Generate: React.FC = () => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleOpenChange = (isOpen: boolean) => {
|
||||||
|
setOpen(isOpen);
|
||||||
|
console.log('Dropdown is now', isOpen ? 'open' : 'closed');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="generate">
|
||||||
|
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant={'transparent'}
|
||||||
|
onClick={() => {
|
||||||
|
handleOpenChange(!open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<WandSparkles className="mr-2" />
|
||||||
|
{t('knowledgeDetails.generate')}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-[380px] p-5 ">
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="border cursor-pointer p-2 rounded-md hover:border-accent-primary hover:bg-[rgba(59,160,92,0.1)]"
|
||||||
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem name="KnowledgeGraph" />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="border cursor-pointer p-2 rounded-md mt-3 hover:border-accent-primary hover:bg-[rgba(59,160,92,0.1)]"
|
||||||
|
onSelect={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem name="Raptor" />
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Generate;
|
||||||
32
web/src/pages/dataset/dataset/generate-button/hook.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
export const generateStatus = {
|
||||||
|
running: 'running',
|
||||||
|
completed: 'completed',
|
||||||
|
start: 'start',
|
||||||
|
};
|
||||||
|
const useFetchGenerateData = () => {
|
||||||
|
let number = 10;
|
||||||
|
// TODO: 获取数据
|
||||||
|
const { data, isFetching: loading } = useQuery({
|
||||||
|
queryKey: ['generateData', 'id'],
|
||||||
|
initialData: { id: 0, percent: 0, type: 'running' },
|
||||||
|
gcTime: 0,
|
||||||
|
refetchInterval: 3000,
|
||||||
|
queryFn: async () => {
|
||||||
|
number += Math.random() * 10;
|
||||||
|
const data = {
|
||||||
|
id: Math.random(),
|
||||||
|
percent: number,
|
||||||
|
type: generateStatus.running,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const pauseGenerate = useCallback(() => {
|
||||||
|
// TODO: pause generate
|
||||||
|
console.log('pause generate');
|
||||||
|
}, []);
|
||||||
|
return { data, loading, pauseGenerate };
|
||||||
|
};
|
||||||
|
export { useFetchGenerateData };
|
||||||
@ -1,68 +0,0 @@
|
|||||||
import SvgIcon from '@/components/svg-icon';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { lowerFirst, toLower } from 'lodash';
|
|
||||||
import { WandSparkles } from 'lucide-react';
|
|
||||||
|
|
||||||
const MenuItem: React.FC<{ name: 'KnowledgeGraph' | 'Raptor' }> = ({
|
|
||||||
name,
|
|
||||||
}) => {
|
|
||||||
console.log(name, 'pppp');
|
|
||||||
return (
|
|
||||||
<div className="flex items-start gap-2 flex-col">
|
|
||||||
<div className="flex justify-start text-text-primary items-center gap-2">
|
|
||||||
<SvgIcon name={`data-flow/${toLower(name)}`} width={24}></SvgIcon>
|
|
||||||
{t(`knowledgeDetails.${lowerFirst(name)}`)}
|
|
||||||
</div>
|
|
||||||
<div className="text-text-secondary text-sm">
|
|
||||||
{t(`knowledgeDetails.generate${name}`)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Generate: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<div className="generate">
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant={'transparent'}>
|
|
||||||
<WandSparkles className="mr-2" />
|
|
||||||
{t('knowledgeDetails.generate')}
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="w-[380px] p-2 ">
|
|
||||||
<DropdownMenuItem className="border cursor-pointer p-2 rounded-md hover:border-accent-primary hover:bg-[rgba(59,160,92,0.1)]">
|
|
||||||
<MenuItem name="KnowledgeGraph" />
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="border cursor-pointer p-2 rounded-md mt-3 hover:border-accent-primary hover:bg-[rgba(59,160,92,0.1)]"
|
|
||||||
onSelect={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem name="Raptor" />
|
|
||||||
{/* <div className="flex items-start gap-2 flex-col">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<SvgIcon name={`data-flow/raptor`} width={24}></SvgIcon>
|
|
||||||
{t('knowledgeDetails.raptor')}
|
|
||||||
</div>
|
|
||||||
<div>{t('knowledgeDetails.generateRaptor')}</div>
|
|
||||||
</div> */}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Generate;
|
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { useSetModalState } from '@/hooks/common-hooks';
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
import { useNextWebCrawl } from '@/hooks/document-hooks';
|
import { useNextWebCrawl } from '@/hooks/document-hooks';
|
||||||
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
||||||
import { useCallback, useState } from 'react';
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'umi';
|
import { useNavigate } from 'umi';
|
||||||
|
import { ILogInfo } from '../process-log-modal';
|
||||||
|
|
||||||
export const useNavigateToOtherPage = () => {
|
export const useNavigateToOtherPage = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -58,3 +60,41 @@ export const useHandleWebCrawl = () => {
|
|||||||
showWebCrawlUploadModal,
|
showWebCrawlUploadModal,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useShowLog = (documents: IDocumentInfo[]) => {
|
||||||
|
const { showModal, hideModal, visible } = useSetModalState();
|
||||||
|
const [record, setRecord] = useState<IDocumentInfo>();
|
||||||
|
const logInfo = useMemo(() => {
|
||||||
|
const findRecord = documents.find(
|
||||||
|
(item: IDocumentInfo) => item.id === record?.id,
|
||||||
|
);
|
||||||
|
let log: ILogInfo = {
|
||||||
|
taskId: record?.id,
|
||||||
|
fileName: record?.name || '-',
|
||||||
|
details: record?.progress_msg || '-',
|
||||||
|
};
|
||||||
|
if (findRecord) {
|
||||||
|
log = {
|
||||||
|
taskId: findRecord.id,
|
||||||
|
fileName: findRecord.name,
|
||||||
|
fileSize: findRecord.size + '',
|
||||||
|
source: findRecord.source_type,
|
||||||
|
task: findRecord.status,
|
||||||
|
state: findRecord.run,
|
||||||
|
startTime: findRecord.process_begin_at,
|
||||||
|
endTime: findRecord.process_begin_at,
|
||||||
|
duration: findRecord.process_duration + 's',
|
||||||
|
details: findRecord.progress_msg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return log;
|
||||||
|
}, [record, documents]);
|
||||||
|
const showLog = useCallback(
|
||||||
|
(data: IDocumentInfo) => {
|
||||||
|
setRecord(data);
|
||||||
|
showModal();
|
||||||
|
},
|
||||||
|
[showModal],
|
||||||
|
);
|
||||||
|
return { showLog, hideLog: hideModal, logVisible: visible, logInfo };
|
||||||
|
};
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { useFetchDocumentList } from '@/hooks/use-document-request';
|
|||||||
import { Upload } from 'lucide-react';
|
import { Upload } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DatasetTable } from './dataset-table';
|
import { DatasetTable } from './dataset-table';
|
||||||
import Generate from './generate';
|
import Generate from './generate-button/generate';
|
||||||
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
|
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
|
||||||
import { useCreateEmptyDocument } from './use-create-empty-document';
|
import { useCreateEmptyDocument } from './use-create-empty-document';
|
||||||
import { useSelectDatasetFilters } from './use-select-filters';
|
import { useSelectDatasetFilters } from './use-select-filters';
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { RunningStatus, RunningStatusMap } from './constant';
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
record: IDocumentInfo;
|
record: IDocumentInfo;
|
||||||
|
handleShowLog?: (record: IDocumentInfo) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Dot({ run }: { run: RunningStatus }) {
|
function Dot({ run }: { run: RunningStatus }) {
|
||||||
@ -85,11 +86,16 @@ export const PopoverContent = ({ record }: IProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ParsingCard({ record }: IProps) {
|
export function ParsingCard({ record, handleShowLog }: IProps) {
|
||||||
return (
|
return (
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<Button variant={'transparent'} className="border-none" size={'sm'}>
|
<Button
|
||||||
|
variant={'transparent'}
|
||||||
|
className="border-none"
|
||||||
|
size={'sm'}
|
||||||
|
onClick={() => handleShowLog?.(record)}
|
||||||
|
>
|
||||||
<Dot run={record.run}></Dot>
|
<Dot run={record.run}></Dot>
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
|
||||||
|
import { IconFontFill } from '@/components/icon-font';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@ -14,7 +15,7 @@ import {
|
|||||||
import { Progress } from '@/components/ui/progress';
|
import { Progress } from '@/components/ui/progress';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
import { CircleX, RefreshCw } from 'lucide-react';
|
import { CircleX } from 'lucide-react';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DocumentType, RunningStatus } from './constant';
|
import { DocumentType, RunningStatus } from './constant';
|
||||||
@ -28,16 +29,26 @@ const IconMap = {
|
|||||||
<div className="w-0 h-0 border-l-[10px] border-l-accent-primary border-t-8 border-r-4 border-b-8 border-transparent"></div>
|
<div className="w-0 h-0 border-l-[10px] border-l-accent-primary border-t-8 border-r-4 border-b-8 border-transparent"></div>
|
||||||
),
|
),
|
||||||
[RunningStatus.RUNNING]: <CircleX size={14} color="var(--state-error)" />,
|
[RunningStatus.RUNNING]: <CircleX size={14} color="var(--state-error)" />,
|
||||||
[RunningStatus.CANCEL]: <RefreshCw size={14} color="var(--accent-primary)" />,
|
[RunningStatus.CANCEL]: (
|
||||||
[RunningStatus.DONE]: <RefreshCw size={14} color="var(--accent-primary)" />,
|
<IconFontFill name="reparse" className="text-accent-primary" />
|
||||||
[RunningStatus.FAIL]: <RefreshCw size={14} color="var(--accent-primary)" />,
|
),
|
||||||
|
[RunningStatus.DONE]: (
|
||||||
|
<IconFontFill name="reparse" className="text-accent-primary" />
|
||||||
|
),
|
||||||
|
[RunningStatus.FAIL]: (
|
||||||
|
<IconFontFill name="reparse" className="text-accent-primary" />
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ParsingStatusCell({
|
export function ParsingStatusCell({
|
||||||
record,
|
record,
|
||||||
showChangeParserModal,
|
showChangeParserModal,
|
||||||
showSetMetaModal,
|
showSetMetaModal,
|
||||||
}: { record: IDocumentInfo } & UseChangeDocumentParserShowType &
|
showLog,
|
||||||
|
}: {
|
||||||
|
record: IDocumentInfo;
|
||||||
|
showLog: (record: IDocumentInfo) => void;
|
||||||
|
} & UseChangeDocumentParserShowType &
|
||||||
UseSaveMetaShowType) {
|
UseSaveMetaShowType) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { run, parser_id, progress, chunk_num, id } = record;
|
const { run, parser_id, progress, chunk_num, id } = record;
|
||||||
@ -65,6 +76,9 @@ export function ParsingStatusCell({
|
|||||||
return record.type !== DocumentType.Virtual;
|
return record.type !== DocumentType.Virtual;
|
||||||
}, [record]);
|
}, [record]);
|
||||||
|
|
||||||
|
const handleShowLog = (record: IDocumentInfo) => {
|
||||||
|
showLog(record);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<section className="flex gap-8 items-center">
|
<section className="flex gap-8 items-center">
|
||||||
<div className="w-[100px] text-ellipsis overflow-hidden flex items-center justify-between">
|
<div className="w-[100px] text-ellipsis overflow-hidden flex items-center justify-between">
|
||||||
@ -85,41 +99,64 @@ export function ParsingStatusCell({
|
|||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
{showParse && (
|
{showParse && (
|
||||||
<>
|
<div className="flex items-center gap-3">
|
||||||
<ConfirmDeleteDialog
|
<Separator orientation="vertical" className="h-2.5" />
|
||||||
title={t(`knowledgeDetails.redo`, { chunkNum: chunk_num })}
|
{!isParserRunning(run) && (
|
||||||
hidden={isZeroChunk || isRunning}
|
<ConfirmDeleteDialog
|
||||||
onOk={handleOperationIconClick(true)}
|
title={t(`knowledgeDetails.redo`, { chunkNum: chunk_num })}
|
||||||
onCancel={handleOperationIconClick(false)}
|
hidden={isZeroChunk || isRunning}
|
||||||
>
|
onOk={handleOperationIconClick(true)}
|
||||||
<div
|
onCancel={handleOperationIconClick(false)}
|
||||||
className="cursor-pointer flex items-center gap-3"
|
|
||||||
onClick={
|
|
||||||
isZeroChunk || isRunning
|
|
||||||
? handleOperationIconClick(false)
|
|
||||||
: () => {}
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<Separator orientation="vertical" className="h-2.5" />
|
<div
|
||||||
{operationIcon}
|
className="cursor-pointer flex items-center gap-3"
|
||||||
</div>
|
onClick={
|
||||||
</ConfirmDeleteDialog>
|
isZeroChunk || isRunning
|
||||||
{isParserRunning(run) ? (
|
? handleOperationIconClick(false)
|
||||||
<HoverCard>
|
: () => {}
|
||||||
<HoverCardTrigger asChild>
|
}
|
||||||
<div className="flex items-center gap-1">
|
>
|
||||||
<Progress value={p} className="h-1 flex-1 min-w-10" />
|
{!isParserRunning(run) && operationIcon}
|
||||||
{p}%
|
</div>
|
||||||
</div>
|
</ConfirmDeleteDialog>
|
||||||
</HoverCardTrigger>
|
|
||||||
<HoverCardContent className="w-[40vw]">
|
|
||||||
<PopoverContent record={record}></PopoverContent>
|
|
||||||
</HoverCardContent>
|
|
||||||
</HoverCard>
|
|
||||||
) : (
|
|
||||||
<ParsingCard record={record}></ParsingCard>
|
|
||||||
)}
|
)}
|
||||||
</>
|
{isParserRunning(run) ? (
|
||||||
|
<>
|
||||||
|
<HoverCard>
|
||||||
|
<HoverCardTrigger asChild>
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-1"
|
||||||
|
onClick={() => handleShowLog(record)}
|
||||||
|
>
|
||||||
|
<Progress value={p} className="h-1 flex-1 min-w-10" />
|
||||||
|
{p}%
|
||||||
|
</div>
|
||||||
|
</HoverCardTrigger>
|
||||||
|
<HoverCardContent className="w-[40vw]">
|
||||||
|
<PopoverContent
|
||||||
|
record={record}
|
||||||
|
handleShowLog={handleShowLog}
|
||||||
|
></PopoverContent>
|
||||||
|
</HoverCardContent>
|
||||||
|
</HoverCard>
|
||||||
|
<div
|
||||||
|
className="cursor-pointer flex items-center gap-3"
|
||||||
|
onClick={
|
||||||
|
isZeroChunk || isRunning
|
||||||
|
? handleOperationIconClick(false)
|
||||||
|
: () => {}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{operationIcon}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<ParsingCard
|
||||||
|
record={record}
|
||||||
|
handleShowLog={handleShowLog}
|
||||||
|
></ParsingCard>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -23,12 +23,13 @@ import { UseSaveMetaShowType } from './use-save-meta';
|
|||||||
|
|
||||||
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
|
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
|
||||||
UseRenameDocumentShowType &
|
UseRenameDocumentShowType &
|
||||||
UseSaveMetaShowType;
|
UseSaveMetaShowType & { showLog: (record: IDocumentInfo) => void };
|
||||||
|
|
||||||
export function useDatasetTableColumns({
|
export function useDatasetTableColumns({
|
||||||
showChangeParserModal,
|
showChangeParserModal,
|
||||||
showRenameModal,
|
showRenameModal,
|
||||||
showSetMetaModal,
|
showSetMetaModal,
|
||||||
|
showLog,
|
||||||
}: UseDatasetTableColumnsType) {
|
}: UseDatasetTableColumnsType) {
|
||||||
const { t } = useTranslation('translation', {
|
const { t } = useTranslation('translation', {
|
||||||
keyPrefix: 'knowledgeDetails',
|
keyPrefix: 'knowledgeDetails',
|
||||||
@ -151,6 +152,7 @@ export function useDatasetTableColumns({
|
|||||||
record={row.original}
|
record={row.original}
|
||||||
showChangeParserModal={showChangeParserModal}
|
showChangeParserModal={showChangeParserModal}
|
||||||
showSetMetaModal={showSetMetaModal}
|
showSetMetaModal={showSetMetaModal}
|
||||||
|
showLog={showLog}
|
||||||
></ParsingStatusCell>
|
></ParsingStatusCell>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
132
web/src/pages/dataset/process-log-modal.tsx
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import FileStatusBadge from '@/components/file-status-badge';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Modal } from '@/components/ui/modal/modal';
|
||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import React from 'react';
|
||||||
|
import reactStringReplace from 'react-string-replace';
|
||||||
|
export interface ILogInfo {
|
||||||
|
taskId?: string;
|
||||||
|
fileName: string;
|
||||||
|
fileSize?: string;
|
||||||
|
source?: string;
|
||||||
|
task?: string;
|
||||||
|
state?: 'Running' | 'Success' | 'Failed' | 'Pending';
|
||||||
|
startTime?: string;
|
||||||
|
endTime?: string;
|
||||||
|
duration?: string;
|
||||||
|
details: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProcessLogModalProps {
|
||||||
|
visible: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
logInfo: ILogInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InfoItem: React.FC<{
|
||||||
|
label: string;
|
||||||
|
value: string | React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}> = ({ label, value, className = '' }) => {
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-col mb-4 ${className}`}>
|
||||||
|
<span className="text-text-secondary text-sm">{label}</span>
|
||||||
|
<span className="text-text-primary mt-1">{value}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
|
||||||
|
visible,
|
||||||
|
onCancel,
|
||||||
|
logInfo,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslate('knowledgeDetails');
|
||||||
|
const blackKeyList = [''];
|
||||||
|
const replaceText = (text: string) => {
|
||||||
|
// Remove duplicate \n
|
||||||
|
const nextText = text.replace(/(\n)\1+/g, '$1');
|
||||||
|
|
||||||
|
const replacedText = reactStringReplace(
|
||||||
|
nextText,
|
||||||
|
/(\[ERROR\].+\s)/g,
|
||||||
|
(match, i) => {
|
||||||
|
return (
|
||||||
|
<span key={i} className={'text-red-600'}>
|
||||||
|
{match}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return replacedText;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t('processLog')}
|
||||||
|
open={visible}
|
||||||
|
onCancel={onCancel}
|
||||||
|
footer={
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button onClick={onCancel}>{t('close')}</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
className="process-log-modal"
|
||||||
|
>
|
||||||
|
<div className=" rounded-lg">
|
||||||
|
<div className="flex flex-wrap ">
|
||||||
|
{Object.keys(logInfo).map((key) => {
|
||||||
|
if (blackKeyList.includes(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (key === 'details') {
|
||||||
|
return (
|
||||||
|
<div className="w-full" key={key}>
|
||||||
|
<InfoItem
|
||||||
|
label={t(key)}
|
||||||
|
value={
|
||||||
|
<div className="w-full whitespace-pre-line text-wrap bg-bg-card rounded-lg h-fit max-h-[350px] overflow-y-auto scrollbar-auto p-2.5">
|
||||||
|
{replaceText(logInfo.details)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (key === 'Status') {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col" key={key}>
|
||||||
|
<span className="text-text-secondary text-sm">Status</span>
|
||||||
|
<div className="mt-1">
|
||||||
|
<FileStatusBadge status={logInfo.state} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="w-1/2" key={key}>
|
||||||
|
<InfoItem
|
||||||
|
label={t(key)}
|
||||||
|
value={logInfo[key as keyof typeof logInfo]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{/* <InfoItem label="Details" value={logInfo.details} /> */}
|
||||||
|
{/* <div>
|
||||||
|
<div>Details</div>
|
||||||
|
<div>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
<div className={'w-full whitespace-pre-line text-wrap '}>
|
||||||
|
{replaceText(logInfo.details)}
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProcessLogModal;
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { DataFlowItem } from '@/components/data-pipeline-select';
|
||||||
import { ButtonLoading } from '@/components/ui/button';
|
import { ButtonLoading } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -15,17 +16,15 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { useForm, useWatch } from 'react-hook-form';
|
import { useForm, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import {
|
import {
|
||||||
DataExtractKnowledgeItem,
|
|
||||||
DataFlowItem,
|
|
||||||
EmbeddingModelItem,
|
EmbeddingModelItem,
|
||||||
ParseTypeItem,
|
ParseTypeItem,
|
||||||
TeamItem,
|
|
||||||
} from '../dataset/dataset-setting/configuration/common-item';
|
} from '../dataset/dataset-setting/configuration/common-item';
|
||||||
|
|
||||||
const FormId = 'dataset-creating-form';
|
const FormId = 'dataset-creating-form';
|
||||||
@ -58,6 +57,7 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
|||||||
control: form.control,
|
control: form.control,
|
||||||
name: 'parseType',
|
name: 'parseType',
|
||||||
});
|
});
|
||||||
|
const { navigateToAgents } = useNavigatePage();
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form
|
<form
|
||||||
@ -88,9 +88,10 @@ export function InputForm({ onOk }: IModalProps<any>) {
|
|||||||
<ParseTypeItem />
|
<ParseTypeItem />
|
||||||
{parseType === 2 && (
|
{parseType === 2 && (
|
||||||
<>
|
<>
|
||||||
<DataFlowItem />
|
<DataFlowItem
|
||||||
<DataExtractKnowledgeItem />
|
toDataPipeline={navigateToAgents}
|
||||||
<TeamItem />
|
formFieldName="data_flow"
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,116 +0,0 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Modal } from '@/components/ui/modal/modal';
|
|
||||||
import { useTranslate } from '@/hooks/common-hooks';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
interface ProcessLogModalProps {
|
|
||||||
visible: boolean;
|
|
||||||
onCancel: () => void;
|
|
||||||
taskInfo: {
|
|
||||||
taskId: string;
|
|
||||||
fileName: string;
|
|
||||||
fileSize: string;
|
|
||||||
source: string;
|
|
||||||
task: string;
|
|
||||||
state: 'Running' | 'Completed' | 'Failed' | 'Pending';
|
|
||||||
startTime: string;
|
|
||||||
endTime?: string;
|
|
||||||
duration?: string;
|
|
||||||
details: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const StatusTag: React.FC<{ state: string }> = ({ state }) => {
|
|
||||||
const getTagStyle = () => {
|
|
||||||
switch (state) {
|
|
||||||
case 'Running':
|
|
||||||
return 'bg-green-500 text-green-100';
|
|
||||||
case 'Completed':
|
|
||||||
return 'bg-blue-500 text-blue-100';
|
|
||||||
case 'Failed':
|
|
||||||
return 'bg-red-500 text-red-100';
|
|
||||||
case 'Pending':
|
|
||||||
return 'bg-yellow-500 text-yellow-100';
|
|
||||||
default:
|
|
||||||
return 'bg-gray-500 text-gray-100';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getTagStyle()}`}
|
|
||||||
>
|
|
||||||
<span className="w-1.5 h-1.5 rounded-full mr-1 bg-current"></span>
|
|
||||||
{state}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const InfoItem: React.FC<{
|
|
||||||
label: string;
|
|
||||||
value: string | React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}> = ({ label, value, className = '' }) => {
|
|
||||||
return (
|
|
||||||
<div className={`flex flex-col mb-4 ${className}`}>
|
|
||||||
<span className="text-text-secondary text-sm">{label}</span>
|
|
||||||
<span className="text-text-primary mt-1">{value}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ProcessLogModal: React.FC<ProcessLogModalProps> = ({
|
|
||||||
visible,
|
|
||||||
onCancel,
|
|
||||||
taskInfo,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslate('knowledgeDetails');
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={t('processLog')}
|
|
||||||
open={visible}
|
|
||||||
onCancel={onCancel}
|
|
||||||
footer={
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Button onClick={onCancel}>{t('close')}</Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
className="process-log-modal"
|
|
||||||
>
|
|
||||||
<div className="p-6 rounded-lg">
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
||||||
{/* Left Column */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<InfoItem label="Task ID" value={taskInfo.taskId} />
|
|
||||||
<InfoItem label="File Name" value={taskInfo.fileName} />
|
|
||||||
<InfoItem label="File Size" value={taskInfo.fileSize} />
|
|
||||||
<InfoItem label="Source" value={taskInfo.source} />
|
|
||||||
<InfoItem label="Task" value={taskInfo.task} />
|
|
||||||
<InfoItem label="Details" value={taskInfo.details} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right Column */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<span className="text-text-secondary text-sm">States</span>
|
|
||||||
<div className="mt-1">
|
|
||||||
<StatusTag state={taskInfo.state} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<InfoItem label="Start Time" value={taskInfo.startTime} />
|
|
||||||
|
|
||||||
<InfoItem label="End Time" value={taskInfo.endTime || '-'} />
|
|
||||||
|
|
||||||
<InfoItem
|
|
||||||
label="Duration"
|
|
||||||
value={taskInfo.duration ? `${taskInfo.duration}s` : '-'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ProcessLogModal;
|
|
||||||
@ -287,7 +287,7 @@ const routes = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${Routes.DataflowResult}/:id`,
|
path: `${Routes.DataflowResult}`,
|
||||||
layout: false,
|
layout: false,
|
||||||
component: `@/pages${Routes.DataflowResult}`,
|
component: `@/pages${Routes.DataflowResult}`,
|
||||||
},
|
},
|
||||||
|
|||||||
37
web/src/services/dataflow-service.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import api from '@/utils/api';
|
||||||
|
import { registerNextServer } from '@/utils/register-server';
|
||||||
|
|
||||||
|
const {
|
||||||
|
listDataflow,
|
||||||
|
removeDataflow,
|
||||||
|
fetchDataflow,
|
||||||
|
runDataflow,
|
||||||
|
setDataflow,
|
||||||
|
} = api;
|
||||||
|
|
||||||
|
const methods = {
|
||||||
|
listDataflow: {
|
||||||
|
url: listDataflow,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
removeDataflow: {
|
||||||
|
url: removeDataflow,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
fetchDataflow: {
|
||||||
|
url: fetchDataflow,
|
||||||
|
method: 'get',
|
||||||
|
},
|
||||||
|
runDataflow: {
|
||||||
|
url: runDataflow,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
setDataflow: {
|
||||||
|
url: setDataflow,
|
||||||
|
method: 'post',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const dataflowService = registerNextServer<keyof typeof methods>(methods);
|
||||||
|
|
||||||
|
export default dataflowService;
|
||||||
@ -190,4 +190,12 @@ export default {
|
|||||||
mindmapShare: `${ExternalApi}${api_host}/searchbots/mindmap`,
|
mindmapShare: `${ExternalApi}${api_host}/searchbots/mindmap`,
|
||||||
getRelatedQuestionsShare: `${ExternalApi}${api_host}/searchbots/related_questions`,
|
getRelatedQuestionsShare: `${ExternalApi}${api_host}/searchbots/related_questions`,
|
||||||
retrievalTestShare: `${ExternalApi}${api_host}/searchbots/retrieval_test`,
|
retrievalTestShare: `${ExternalApi}${api_host}/searchbots/retrieval_test`,
|
||||||
|
|
||||||
|
// data pipeline
|
||||||
|
|
||||||
|
fetchDataflow: (id: string) => `${api_host}/dataflow/get/${id}`,
|
||||||
|
setDataflow: `${api_host}/dataflow/set`,
|
||||||
|
removeDataflow: `${api_host}/dataflow/rm`,
|
||||||
|
listDataflow: `${api_host}/dataflow/list`,
|
||||||
|
runDataflow: `${api_host}/dataflow/run`,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,3 +1,10 @@
|
|||||||
export function buildSelectOptions(list: Array<string>) {
|
export function buildSelectOptions(
|
||||||
|
list: Array<any>,
|
||||||
|
keyName?: string,
|
||||||
|
valueName?: string,
|
||||||
|
) {
|
||||||
|
if (keyName && valueName) {
|
||||||
|
return list.map((x) => ({ label: x[valueName], value: x[keyName] }));
|
||||||
|
}
|
||||||
return list.map((x) => ({ label: x, value: x }));
|
return list.map((x) => ({ label: x, value: x }));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,9 @@ export function buildOptions(
|
|||||||
) {
|
) {
|
||||||
if (t) {
|
if (t) {
|
||||||
return Object.values(data).map((val) => ({
|
return Object.values(data).map((val) => ({
|
||||||
label: t(`${prefix ? prefix + '.' : ''}${val.toLowerCase()}`),
|
label: t(
|
||||||
|
`${prefix ? prefix + '.' : ''}${typeof val === 'string' ? val.toLowerCase() : val}`,
|
||||||
|
),
|
||||||
value: val,
|
value: val,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,10 @@ module.exports = {
|
|||||||
'bg-accent': 'var(--bg-accent)',
|
'bg-accent': 'var(--bg-accent)',
|
||||||
'state-success': 'var(--state-success)',
|
'state-success': 'var(--state-success)',
|
||||||
'state-warning': 'var(--state-warning)',
|
'state-warning': 'var(--state-warning)',
|
||||||
'state-error': 'var(--state-error)',
|
'state-error': {
|
||||||
|
DEFAULT: 'rgb(var(--state-error) / <alpha-value>)',
|
||||||
|
5: 'rgba(var(--state-error) / 0.05)', // 5%
|
||||||
|
},
|
||||||
'team-group': 'var(--team-group)',
|
'team-group': 'var(--team-group)',
|
||||||
'team-member': 'var(--team-member)',
|
'team-member': 'var(--team-member)',
|
||||||
'team-department': 'var(--team-department)',
|
'team-department': 'var(--team-department)',
|
||||||
|
|||||||
@ -119,7 +119,7 @@
|
|||||||
|
|
||||||
--state-success: #3ba05c;
|
--state-success: #3ba05c;
|
||||||
--state-warning: #faad14;
|
--state-warning: #faad14;
|
||||||
--state-error: #d8494b;
|
--state-error: 216 73 75;
|
||||||
|
|
||||||
--team-group: #5ab77e;
|
--team-group: #5ab77e;
|
||||||
--team-member: #5c96c8;
|
--team-member: #5c96c8;
|
||||||
@ -291,8 +291,8 @@
|
|||||||
@layer utilities {
|
@layer utilities {
|
||||||
.scrollbar-auto {
|
.scrollbar-auto {
|
||||||
/* hide scrollbar */
|
/* hide scrollbar */
|
||||||
scrollbar-width: none;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: transparent transparent;
|
scrollbar-color: var(--border-default) var(--bg-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollbar-auto::-webkit-scrollbar {
|
.scrollbar-auto::-webkit-scrollbar {
|
||||||
|
|||||||