feat: Add hint for operators, round to square, input variable, readable operator ID. #3056 (#3057)

### What problem does this PR solve?

feat: Add hint for operators, round to square, input variable, readable
operator ID. #3056

### Type of change

- [ ] Bug Fix (non-breaking change which fixes an issue)
- [x] New Feature (non-breaking change which adds functionality)
- [ ] Documentation Update
- [ ] Refactoring
- [ ] Performance Improvement
- [ ] Other (please describe):
This commit is contained in:
balibabu 2024-10-28 14:31:19 +08:00 committed by GitHub
parent f93f485696
commit 396feadd4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 1368 additions and 585 deletions

View File

@ -0,0 +1,9 @@
<svg t="1729651878356" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6162"
width="200" height="200">
<path
d="M512 62C263.5 62 62 263.5 62 512s201.5 450 450 450 450-201.5 450-450S760.5 62 512 62z m272.8 722.9c-35.5 35.5-76.7 63.3-122.6 82.7-47.5 20.1-98 30.3-150.2 30.3-52.1 0-102.7-10.1-150.2-30.3-45.9-19.4-87.2-47.2-122.6-82.7-35.5-35.5-63.3-76.7-82.7-122.6-20.1-47.5-30.3-98-30.3-150.2 0-52.1 10.1-102.7 30.3-150.2 19.4-45.9 47.2-87.2 82.7-122.6 35.5-35.5 76.7-63.3 122.6-82.7 47.5-20.2 98-30.3 150.2-30.3 52.1 0 102.7 10.1 150.2 30.3 45.9 19.4 87.2 47.2 122.6 82.7 35.5 35.5 63.3 76.7 82.7 122.6 20.1 47.5 30.3 98 30.3 150.2 0 52.1-10.1 102.7-30.3 150.2-19.4 45.9-47.2 87.2-82.7 122.6z"
p-id="6163" fill="#4f51d6"></path>
<path
d="M738.1 492.5l-322.2-186c-15-8.7-33.8 2.2-33.8 19.5v372c0 17.3 18.7 28.1 33.8 19.5l322.2-186c15-8.7 15-30.3 0-39z m-111.7 29.2l-163.1 94.1c-7.5 4.3-16.9-1.1-16.9-9.7V417.9c0-8.7 9.4-14.1 16.9-9.7l163.1 94.1c7.5 4.3 7.5 15.1 0 19.4z"
p-id="6164" fill="#4f51d6"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,7 +1,7 @@
<svg t="1727330687293" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4353"
width="200" height="200">
<path d="M254.350222 268.970667v108.259555h185.571556V268.970667H254.350222z" p-id="4354" fill="#3b76f4"></path>
<path d="M254.350222 268.970667v108.259555h185.571556V268.970667H254.350222z" p-id="4354" fill="#32d2a3"></path>
<path
d="M187.904 106.666667a85.333333 85.333333 0 0 0-85.333333 85.333333v411.192889a85.333333 85.333333 0 0 0 85.333333 85.333333h43.434667v95.317334a69.006222 69.006222 0 1 0 49.123555 0v-95.288889h121.6v95.288889a69.006222 69.006222 0 1 0 49.152 0v-95.288889h121.6v95.288889a69.006222 69.006222 0 1 0 49.123556 0v-95.288889h121.628444v95.288889a69.006222 69.006222 0 1 0 49.123556 0v-95.288889h43.406222a85.333333 85.333333 0 0 0 85.333333-85.333334V192a85.333333 85.333333 0 0 0-85.333333-85.333333H187.904z m25.884444 430.222222h596.394667a40.533333 40.533333 0 0 1 0 81.066667H213.816889a40.533333 40.533333 0 0 1 0-81.066667z m0-348.984889h266.666667c22.385778 0 40.533333 18.147556 40.533333 40.533333v189.326223c0 22.385778-18.147556 40.533333-40.533333 40.533333H213.816889a40.533333 40.533333 0 0 1-40.533333-40.533333V228.437333c0-22.385778 18.147556-40.533333 40.533333-40.533333z m439.381334 0c22.385778 0 40.533333 18.147556 40.533333 40.533333v189.326223a40.533333 40.533333 0 0 1-81.066667 0V228.437333c0-22.385778 18.119111-40.533333 40.533334-40.533333z m116.48 40.533333a40.533333 40.533333 0 0 1 81.066666 0v189.326223a40.533333 40.533333 0 0 1-81.066666 0V228.437333z"
p-id="4355" fill="#3b76f4"></path>
p-id="4355" fill="#32d2a3"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -2,8 +2,8 @@
p-id="11640" width="200" height="200">
<path
d="M419.999992 631.999988v-136.699998c74.443999-50.009999 119.999998-134.033997 119.999997-223.299995C539.999989 123.113998 418.885992 0 269.999995 0S0 123.113998 0 271.999995c0 89.267998 45.555999 173.291997 119.999998 223.299995V871.999983c0 7.968 3.164 15.586 8.789999 21.21l119.999998 121.999997A29.903999 29.903999 0 0 0 269.999995 1023.99998a29.899999 29.899999 0 0 0 21.209999-8.79l119.999998-121.999997c11.718-11.718 11.718-30.703999 0-42.422L372.421993 811.999984l38.789999-38.789999c11.718-11.718 11.718-30.703999 0-42.421999L372.421993 691.999986l38.789999-38.789999A29.979999 29.979999 0 0 0 419.999992 631.999988z m-149.999997-329.999994c-49.627999 0-89.999998-40.371999-89.999999-89.999998s40.371999-89.999998 89.999999-89.999998 89.999998 40.371999 89.999998 89.999998-40.371999 89.999998-89.999998 89.999998z"
p-id="11641" fill="#3b76f4"></path>
p-id="11641" fill="#0f0e0f"></path>
<path
d="M933.999982 361.999993c-39.059999 0-72.047999 25.156-84.473999 59.999999H753.999985a30.011999 30.011999 0 0 0-26.835999 16.581999L675.455987 541.999989H509.99999c-16.582 0-29.999999 13.418-29.999999 30s13.418 29.999999 29.999999 29.999999h183.999996a30.011999 30.011999 0 0 0 26.836-16.581999L772.543985 481.999991h76.981998c12.426 34.843999 45.413999 59.999999 84.473999 59.999998 49.627999 0 89.999998-40.371999 89.999998-89.999998s-40.371999-89.999998-89.999998-89.999998zM933.999982 601.999988c-39.059999 0-72.047999 25.156-84.473999 59.999999H509.99999c-16.582 0-29.999999 13.418-29.999999 29.999999s13.418 29.999999 29.999999 30h339.525993c12.426 34.843999 45.413999 59.999999 84.473999 59.999999 49.627999 0 89.999998-40.371999 89.999998-89.999999s-40.371999-89.999998-89.999998-89.999998zM933.999982 841.999984c-39.059999 0-72.047999 25.156-84.473999 59.999998h-76.981998l-51.707999-103.417998A30.011999 30.011999 0 0 0 693.999986 781.999985h-183.999996c-16.582 0-29.999999 13.418-29.999999 29.999999s13.418 29.999999 29.999999 30h165.455997l51.707999 103.417998C732.261986 955.583981 742.631985 963.999981 753.999985 963.999981h95.525998c12.426 34.843999 45.413999 59.999999 84.473999 59.999999 49.627999 0 89.999998-42.371999 89.999998-91.999998s-40.371999-89.999998-89.999998-89.999998zM933.999982 121.999998c-39.059999 0-72.047999 25.156-84.473999 59.999998H753.999985c-10.02 0-19.394 5.01-24.959999 13.36L617.945988 361.999993H569.999989c-16.582 0-29.999999 13.418-30 29.999999s13.418 29.999999 30 30h63.999999c10.02 0 19.394-5.01 24.959999-13.36L770.053985 241.999995h79.471998c12.426 34.843999 45.413999 59.999999 84.473999 59.999999 49.627999 0 89.999998-40.371999 89.999998-89.999998s-40.371999-89.999998-89.999998-89.999998z"
p-id="11642" fill="#3b76f4"></path>
p-id="11642" fill="#0f0e0f"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -0,0 +1,7 @@
<svg width="207" height="144" viewBox="0 0 207 144" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.9375 107.812V0H32.1875V143.75H25.9375L7.1875 35.9375V143.75H0.9375V0H7.1875L25.9375 107.812Z" fill="#3C593D"/>
<path d="M64.0234 137.5C65.7161 137.5 67.1484 136.914 68.3203 135.742C69.6224 134.57 70.2734 133.073 70.2734 131.25V12.5C70.2734 10.8073 69.6224 9.375 68.3203 8.20312C67.1484 6.90104 65.7161 6.25 64.0234 6.25C62.2005 6.25 60.7031 6.90104 59.5312 8.20312C58.3594 9.375 57.7734 10.8073 57.7734 12.5V131.25C57.7734 133.073 58.3594 134.57 59.5312 135.742C60.7031 136.914 62.2005 137.5 64.0234 137.5ZM64.0234 143.75C60.5078 143.75 57.513 142.578 55.0391 140.234C52.6953 137.76 51.5234 134.766 51.5234 131.25V12.5C51.5234 9.11458 52.6953 6.1849 55.0391 3.71094C57.513 1.23698 60.5078 0 64.0234 0C67.4089 0 70.3385 1.23698 72.8125 3.71094C75.2865 6.1849 76.5234 9.11458 76.5234 12.5V131.25C76.5234 134.766 75.2865 137.76 72.8125 140.234C70.3385 142.578 67.4089 143.75 64.0234 143.75Z" fill="#3C593D"/>
<path d="M131.797 0H138.047V143.75H131.797V25L116.172 87.5L100.547 25V143.75H94.2969V0H100.547L116.172 62.5L131.797 0Z" fill="#3C593D"/>
<path d="M156.602 143.75V0H162.852V143.75H156.602Z" fill="#3C593D"/>
<path d="M193.906 143.75C190.391 143.75 187.396 142.578 184.922 140.234C182.578 137.76 181.406 134.766 181.406 131.25V12.5C181.406 9.11458 182.578 6.1849 184.922 3.71094C187.396 1.23698 190.391 0 193.906 0C197.292 0 200.221 1.23698 202.695 3.71094C205.169 6.1849 206.406 9.11458 206.406 12.5V25H200.156V12.5C200.156 10.8073 199.505 9.375 198.203 8.20312C197.031 6.90104 195.599 6.25 193.906 6.25C192.083 6.25 190.586 6.90104 189.414 8.20312C188.242 9.375 187.656 10.8073 187.656 12.5V131.25C187.656 133.073 188.242 134.57 189.414 135.742C190.586 136.914 192.083 137.5 193.906 137.5C195.599 137.5 197.031 136.914 198.203 135.742C199.505 134.57 200.156 133.073 200.156 131.25V118.75H206.406V131.25C206.406 134.766 205.169 137.76 202.695 140.234C200.221 142.578 197.292 143.75 193.906 143.75Z" fill="#3C593D"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,29 @@
<svg t="1729751461368" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="17570" width="200" height="200">
<path
d="M961.536 62.4a47.36 47.36 0 1 1-94.72-0.064 47.36 47.36 0 0 1 94.72 0z m-159.04 840.96a47.488 47.488 0 1 1-94.912 3.968 47.488 47.488 0 0 1 94.912-3.968z"
fill="#8AC9FE" p-id="17571"></path>
<path
d="M961.536 253.44a47.424 47.424 0 1 1-94.912 0 47.424 47.424 0 0 1 94.912 0z m-383.36 708.16a47.36 47.36 0 1 1-94.72 0 47.36 47.36 0 0 1 94.72 0z m363.072-194.176a47.424 47.424 0 1 1-94.72-0.256 47.36 47.36 0 0 1 94.784 0.128l-0.064 0.128z"
fill="#FDB441" p-id="17572"></path>
<path d="M157.248 629.12a47.36 47.36 0 1 1-94.72-0.128 47.36 47.36 0 0 1 94.72 0.192z" fill="#8AC9FE" p-id="17573">
</path>
<path
d="M348.352 380.416a47.424 47.424 0 1 1-94.848-0.192 47.424 47.424 0 0 1 94.848 0.192z m0 248.768a47.36 47.36 0 1 1-94.72-0.192 47.36 47.36 0 0 1 94.72 0.192z m461.76-248.768a47.36 47.36 0 1 1-94.72-0.128 47.36 47.36 0 0 1 94.72 0.128z m0 248.768a47.36 47.36 0 1 1-94.656-0.192 47.36 47.36 0 0 1 94.656 0.192zM578.112 235.008a47.36 47.36 0 1 1-94.72 0 47.36 47.36 0 0 1 94.72 0z m0 535.552a47.36 47.36 0 1 1-94.72 0 47.36 47.36 0 0 1 94.72 0z"
fill="#EE6161" p-id="17574"></path>
<path
d="M961.536 62.4a47.424 47.424 0 0 1-91.52 17.216 47.36 47.36 0 0 0 51.776-64 47.36 47.36 0 0 1 39.744 46.72z m-172.864 874.496a47.424 47.424 0 0 1-77.696-16.384 47.296 47.296 0 0 0 51.712-63.872 47.36 47.36 0 0 1 25.984 80.256z"
fill="#60B7FF" p-id="17575"></path>
<path
d="M961.536 253.44a47.424 47.424 0 0 1-91.52 17.152 47.36 47.36 0 0 0 51.776-63.936 47.36 47.36 0 0 1 39.744 46.784z m-383.36 708.16a47.36 47.36 0 0 1-91.52 17.28 47.424 47.424 0 0 0 51.712-64 47.36 47.36 0 0 1 39.744 46.72z m349.248-160.768a47.36 47.36 0 0 1-77.696-16.192 47.68 47.68 0 0 0 51.84-64 47.36 47.36 0 0 1 25.856 80.192z"
fill="#FEA613" p-id="17576"></path>
<path
d="M157.312 629.12a47.36 47.36 0 0 1-91.52 17.088 47.424 47.424 0 0 0 51.712-63.872 47.36 47.36 0 0 1 39.808 46.848z"
fill="#60B7FF" p-id="17577"></path>
<path
d="M348.352 380.416a47.424 47.424 0 0 1-91.52 17.088 47.36 47.36 0 0 0 51.712-64 47.36 47.36 0 0 1 39.808 46.912z m0 248.768a47.36 47.36 0 0 1-91.52 17.024 47.488 47.488 0 0 0 51.712-64 47.616 47.616 0 0 1 39.808 46.976z m461.76-248.768a47.424 47.424 0 0 1-91.52 17.088 47.36 47.36 0 0 0 51.712-64 47.36 47.36 0 0 1 39.808 46.912z m0 248.768a47.36 47.36 0 0 1-91.52 17.024 47.488 47.488 0 0 0 51.712-64 47.616 47.616 0 0 1 39.808 46.976zM578.112 234.88a47.36 47.36 0 0 1-91.52 17.152 47.424 47.424 0 0 0 51.712-64 47.296 47.296 0 0 1 39.808 46.912z m0 535.616a47.36 47.36 0 0 1-91.52 17.152 47.424 47.424 0 0 0 51.712-64 47.36 47.36 0 0 1 39.808 46.848z"
fill="#E94444" p-id="17578"></path>
<path
d="M929.088 193.024V122.88a62.464 62.464 0 0 0 47.36-60.48 62.464 62.464 0 0 0-124.736 0c0 29.248 20.288 53.76 47.424 60.48v70.144a62.272 62.272 0 0 0-41.344 87.04l-61.184 48a62.656 62.656 0 0 0-61.952-3.264L592.448 244.416a62.464 62.464 0 0 0-108.992-49.984 62.272 62.272 0 0 0-14.272 49.92l-46.464 26.176a15.104 15.104 0 0 0 7.552 28.16c2.56 0 4.992-0.704 7.232-2.048l43.264-24.512a62.592 62.592 0 0 0 100.032 0l130.112 73.6a62.08 62.08 0 0 0 36.864 95.168v127.616a62.72 62.72 0 0 0-38.656 92.224l-128.32 72.512a62.272 62.272 0 0 0-100.032 0l-126.656-71.68a62.592 62.592 0 0 0-38.144-93.056V440.896a62.464 62.464 0 0 0 36.288-96l24.96-14.144a15.104 15.104 0 0 0 5.76-20.48 15.104 15.104 0 0 0-20.48-5.632l-34.496 19.456a62.464 62.464 0 1 0-41.984 116.736v127.68a62.656 62.656 0 0 0-45.568 45.632H170.432a62.272 62.272 0 1 0 0 29.952h70.016a62.336 62.336 0 0 0 91.136 39.232l137.6 77.76a62.272 62.272 0 0 0 46.592 69.888v70.144a62.272 62.272 0 1 0 30.016 0v-70.144a62.272 62.272 0 0 0 46.72-69.888l138.624-78.336a61.76 61.76 0 0 0 59.136 2.24l50.24 50.176a62.72 62.72 0 0 0 0 64.512l-51.52 51.328a62.528 62.528 0 0 0-78.016 8.192 62.464 62.464 0 0 0 44.16 106.432 62.464 62.464 0 0 0 54.336-92.8l52.16-52.032a62.08 62.08 0 0 0 88.128-25.6 14.976 14.976 0 0 0-12.416-21.696 15.04 15.04 0 0 0-14.464 8.256 32.128 32.128 0 0 1-61.184-12.416 32.448 32.448 0 0 1 48.896-29.76 14.912 14.912 0 0 0 22.4-16.32 15.104 15.104 0 0 0-6.784-9.28 62.016 62.016 0 0 0-64.448 0l-48.512-48.384a62.656 62.656 0 0 0-35.52-97.088V440.832a62.528 62.528 0 0 0 39.488-90.816l59.52-46.656a62.464 62.464 0 0 0 99.712-49.856 62.336 62.336 0 0 0-47.36-60.544v0.064zM530.752 267.328a32.512 32.512 0 0 1-12.416-62.336 32.448 32.448 0 1 1 12.416 62.336zM268.608 380.416a32.448 32.448 0 1 1 64.768 0 32.448 32.448 0 1 1-64.768 0z m-158.72 281.088a32.512 32.512 0 1 1-3.264-64.896 32.512 32.512 0 0 1 3.264 64.896z m158.72-32.448a32.448 32.448 0 0 1 64.768 0 32.896 32.896 0 0 1-32.384 32.448 32.448 32.448 0 0 1-32.384-32.448zM563.2 961.6a32.384 32.384 0 1 1-64.768 0 32.384 32.384 0 1 1 64.768 0z m-32.448-158.72a32.448 32.448 0 1 1 0.192-64.832 32.448 32.448 0 0 1-0.192 64.832z m247.296 123.328a32.448 32.448 0 1 1-45.76-45.76 32.384 32.384 0 0 1 45.76 45.76zM881.728 62.464a32.384 32.384 0 1 1 64.64-3.392 32.384 32.384 0 0 1-64.64 3.392z m-86.592 566.72c0 17.28-14.848 32.32-32.448 32.32a32.384 32.384 0 1 1 32.448-32.32z m-32.448-216.448a32.448 32.448 0 1 1-3.328-64.768 32.448 32.448 0 0 1 3.328 64.768z m151.424-126.848a32.384 32.384 0 1 1 0-64.768 32.448 32.448 0 0 1 0 64.832v-0.064z"
fill="#000000" p-id="17579"></path>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,13 @@
<svg t="1729749189370" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="11709" width="200" height="200">
<path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#FC011A" p-id="11710"></path>
<path
d="M544.045176 616.267294c32.075294-39.454118 41.923765-71.499294 29.605648-96.165647-14.787765-27.105882-34.514824-49.302588-69.059765-54.241882-2.439529 22.196706-4.909176 41.923765-7.378824 54.241882 17.257412 14.787765 32.045176 24.666353 39.454118 36.984471 9.848471 14.787765-2.469647 36.984471-19.727059 36.98447-34.514824 0-61.650824-29.575529-56.711529-64.12047 0-7.378824 0-12.318118 2.469647-19.696942 17.257412-78.908235-34.544941-106.014118-93.726118-106.014117-41.893647 0-83.817412 7.378824-125.741176 9.84847 4.939294-34.544941 34.514824-37.014588 64.12047-37.014588 49.302588-2.439529 96.165647-4.909176 145.468236-9.84847 17.257412-2.469647 36.984471-4.939294 34.514823-29.57553H295.002353c4.939294-12.348235 9.878588-22.196706 17.257412-37.014588C292.532706 290.785882 275.275294 283.407059 253.108706 271.058824c-12.348235 69.029647-54.272 91.256471-118.362353 69.029647-4.939294 56.711529 39.454118 34.544941 69.029647 46.863058C164.321882 436.254118 129.807059 480.647529 90.352941 529.950118c36.984471 7.408941 59.181176-19.696941 93.696-36.984471-14.787765 76.438588-27.105882 145.468235-41.923765 214.528 29.605647 7.408941 54.241882-4.939294 66.56-34.514823 7.408941-19.727059 9.878588-39.454118 17.28753-56.71153 22.196706-69.029647 96.165647-118.362353 172.574118-101.104941-2.439529 9.878588-2.439529 17.257412-4.909177 27.105882-19.727059 0-39.454118 0-56.711529 2.499765-32.045176 4.909176-59.181176 19.727059-66.56 54.211765-7.408941 32.075294 2.439529 59.211294 29.575529 78.908235 39.454118 27.136 86.317176 37.014588 138.089412 37.014588 4.939294-27.105882 7.378824-54.241882 12.348235-83.847529 12.318118 7.408941 19.696941 12.348235 29.57553 14.787765 71.499294 39.454118 145.468235 73.999059 229.315764 83.847529 81.347765 9.878588 152.877176-14.787765 209.588706-73.968941 4.939294-4.939294 9.848471-14.787765 12.348235-22.196706-138.119529 73.968941-263.860706 51.802353-387.162353-17.257412z m-155.346823-162.755765c-51.772235 19.727059-103.544471 39.454118-152.877177 56.741647-4.909176-34.544941 0-51.802353 36.984471-59.181176 27.136-7.408941 56.741647-7.408941 83.847529-7.408941h12.348236c29.575529 0 19.696941 9.848471 19.696941 9.84847z m-7.378824 143.028706c-4.939294 24.666353-7.408941 49.302588-12.348235 73.968941H356.653176c-2.469647 0-2.469647 0-4.939294-2.469647-2.469647 0-4.909176-2.469647-4.909176-2.469647s-2.469647 0-2.469647-2.439529c0 0-2.469647 0-2.469647-2.469647 0 0-2.469647 0-2.469647-2.469647h-2.469647l-2.469647-2.469647c-2.469647 0-2.469647-2.469647-2.469647-2.469647-2.439529-2.469647-4.909176-4.939294-4.909177-7.408941l-2.469647-2.43953s0-2.469647-2.469647-2.469647c-2.469647-4.939294-2.469647-12.348235-2.469647-17.257412v-2.469647c2.469647-27.105882 24.666353-34.514824 61.650823-24.666353z"
fill="#FFFFFF" p-id="11711"></path>
<path
d="M901.601882 335.179294c-27.105882 2.469647-51.802353 2.469647-83.847529 4.909177 12.348235-17.257412 19.727059-27.105882 27.105882-39.454118-19.696941-9.848471-36.954353-17.257412-54.211764-24.636235-29.605647 19.727059-32.075294 61.650824-71.529412 69.029647-4.939294-22.196706-7.378824-44.393412-9.848471-64.120471-51.802353 7.408941-54.241882 12.348235-44.393412 66.590118h-78.908235c0 32.045176 7.408941 39.454118 41.923765 39.454117h71.499294c0 2.469647 2.469647 7.408941 2.469647 9.848471-14.787765 4.939294-32.045176 9.878588-46.832941 17.287529-14.817882 7.378824-39.454118 17.257412-41.923765 29.57553-12.348235 54.241882-17.257412 110.953412-24.666353 170.164706 73.968941 46.832941 155.346824 39.454118 241.633883 27.105882 4.939294-32.075294 7.408941-59.181176 12.348235-86.317176 2.469647-27.105882 9.848471-54.241882 9.84847-81.377883 2.469647-39.454118-12.348235-56.711529-49.302588-64.090353-14.787765-2.469647-32.075294-4.939294-46.863059-7.408941 0-2.469647-2.469647-4.939294-2.469647-7.378823 14.817882-4.939294 29.605647-9.878588 46.863059-12.348236 27.105882-4.939294 54.241882-4.939294 78.908235-9.84847 12.348235-7.408941 29.575529-12.348235 22.196706-36.984471z m-130.710588 263.830588c-41.893647 17.257412-81.347765 12.348235-120.801882 0 0-2.469647 2.469647-4.939294 4.939294-7.408941 2.439529-2.469647 2.439529-4.909176 4.909176-7.378823l2.469647-2.469647 2.469647-2.469647 2.469648-2.469648c2.469647-2.469647 4.939294-2.469647 7.408941-4.939294 0 0 2.439529 0 2.439529-2.469647 4.939294-2.439529 7.408941-2.439529 12.348235-4.909176h51.772236c9.878588 0 19.727059 2.469647 32.045176 4.909176-2.469647 0 29.605647 7.408941-2.469647 29.605647z m-4.909176-76.438588s-2.469647 0 0 0h-7.408942c-4.939294 0-7.378824 2.469647-12.348235 2.469647-7.378824 2.469647-17.227294 2.469647-24.636235 4.909177l-51.802353 7.408941c-4.909176 0-7.378824 2.469647-12.318118 2.469647V527.510588c0-2.469647 0-4.939294 2.469647-7.408941 4.939294-9.848471 12.348235-14.787765 24.666353-19.727059 2.469647 0 2.469647 0 4.939294-2.469647h17.257412c24.666353-2.469647 49.302588 0 73.968941 0 36.984471 0-2.469647 19.727059-14.787764 24.666353z m17.257411-71.499294c-39.454118 7.378824-78.908235 14.757647-115.892705 22.166588-4.939294-22.166588 4.939294-34.514824 29.575529-39.454117 44.393412-12.318118 71.529412-7.378824 86.317176 17.287529z m-273.709176-32.075294l7.408941-59.181177c44.363294 9.878588 71.499294 56.711529 59.181177 103.574589-12.348235-7.408941-22.196706-14.787765-32.075295-22.196706-9.848471-7.378824-22.196706-12.348235-34.514823-22.196706z m-189.861647 224.37647c2.469647 2.469647 2.469647 4.939294 4.939294 7.408942 0-2.469647-2.469647-4.939294-4.939294-7.378824z m49.302588 27.136c-19.727059-4.939294-34.514824-9.848471-44.363294-19.727058 9.848471 9.878588 24.636235 17.257412 44.363294 19.727058z"
fill="#FFFFFF" p-id="11712"></path>
<path
d="M659.937882 581.752471c-2.469647 2.469647-4.909176 4.939294-4.909176 7.378823 0-2.439529 2.439529-4.909176 4.909176-7.378823z m9.878589-41.923765c-4.939294 0-7.408941 3.523765-12.348236 3.523765 4.939294-3.523765 7.408941-3.523765 12.348236-3.523765z m91.226353-17.257412c0 2.469647 0 2.469647 0 0-4.939294 2.469647-9.848471 2.469647-14.787765 4.939294 4.939294-2.469647 9.848471-2.469647 14.787765-4.939294 0 2.469647 0 2.469647 0 0z m4.879058 1.264941s-7.890824 0 0 0c-7.890824 0 0 0 0 0z m12.378353-25.961411c-24.666353 0-49.302588-7.920941-73.968941 0h-7.408941 7.408941c24.666353-7.920941 51.802353 0 73.968941 0z"
fill="#FFFFFF" p-id="11713"></path>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -1,5 +1,7 @@
<svg t="1727489529326" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4261"
width="200" height="200">
<circle cx="512" cy="512" r="400" fill="white" />
<path
d="M448 448H192v128h256v256h128V576h256V448H576V192H448v256z m64 576A512 512 0 1 1 512 0a512 512 0 0 1 0 1024z"
fill="#1677ff" p-id="4262"></path>

Before

Width:  |  Height:  |  Size: 330 B

After

Width:  |  Height:  |  Size: 385 B

View File

@ -0,0 +1,5 @@
<svg t="1729507531032" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4259"
width="200" height="200">
<path d="M474 152m8 0l60 0q8 0 8 8l0 704q0 8-8 8l-60 0q-8 0-8-8l0-704q0-8 8-8Z" fill="#ffffff" p-id="4260"></path>
<path d="M168 474m8 0l672 0q8 0 8 8l0 60q0 8-8 8l-672 0q-8 0-8-8l0-60q0-8 8-8Z" fill="#ffffff" p-id="4261"></path>
</svg>

After

Width:  |  Height:  |  Size: 395 B

View File

@ -0,0 +1,6 @@
<svg t="1729759831217" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="22909" width="200" height="200">
<path
d="M319.20128 974.56128L348.16 1003.52l655.36-655.36-28.95872-28.95872-655.36 655.36zM675.84 1003.52l327.68-327.68-28.95872-28.95872-327.68 327.68L675.84 1003.52z"
fill="#000000" p-id="22910"></path>
</svg>

After

Width:  |  Height:  |  Size: 384 B

View File

@ -2,5 +2,5 @@
width="200" height="200">
<path
d="M128 522.666667c17.066667 0 32-14.933333 32-32v-170.666667c0-6.4 4.266667-10.666667 10.666667-10.666667h652.8l-83.2 83.2c-12.8 12.8-12.8 34.133333 0 46.933334 6.4 6.4 14.933333 10.666667 23.466666 10.666666s17.066667-4.266667 23.466667-10.666666l145.066667-145.066667c12.8-12.8 12.8-34.133333 0-46.933333l-145.066667-145.066667c-12.8-12.8-34.133333-12.8-46.933333 0-12.8 12.8-12.8 34.133333 0 46.933333l93.866666 93.866667H170.666667c-40.533333 0-74.666667 34.133333-74.666667 74.666667v170.666666c0 19.2 14.933333 34.133333 32 34.133334zM906.666667 501.333333c-17.066667 0-32 14.933333-32 32v170.666667c0 6.4-4.266667 10.666667-10.666667 10.666667H211.2l83.2-83.2c12.8-12.8 12.8-34.133333 0-46.933334-12.8-12.8-34.133333-12.8-46.933333 0l-145.066667 145.066667c-12.8 12.8-12.8 34.133333 0 46.933333l145.066667 145.066667c6.4 6.4 14.933333 10.666667 23.466666 10.666667s17.066667-4.266667 23.466667-10.666667c12.8-12.8 12.8-34.133333 0-46.933333l-93.866667-93.866667h663.466667c40.533333 0 74.666667-34.133333 74.666667-74.666667v-170.666666c0-19.2-12.8-34.133333-32-34.133334z"
fill="#3b76f4" p-id="4300"></path>
fill="#b548f8" p-id="4300"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,6 +1,7 @@
import { useTranslate } from '@/hooks/common-hooks';
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { Form, Select } from 'antd';
import { UserOutlined } from '@ant-design/icons';
import { Avatar, Form, Select, Space } from 'antd';
const KnowledgeBaseItem = () => {
const { t } = useTranslate('chat');
@ -8,7 +9,12 @@ const KnowledgeBaseItem = () => {
const { list: knowledgeList } = useNextFetchKnowledgeList(true);
const knowledgeOptions = knowledgeList.map((x) => ({
label: x.name,
label: (
<Space>
<Avatar size={20} icon={<UserOutlined />} src={x.avatar} />
{x.name}
</Space>
),
value: x.id,
}));

View File

@ -0,0 +1,3 @@
.llmLabel {
font-size: 14px;
}

View File

@ -7,9 +7,10 @@ interface IProps {
id?: string;
value?: string;
onChange?: (value: string) => void;
disabled?: boolean;
}
const LLMSelect = ({ id, value, onChange }: IProps) => {
const LLMSelect = ({ id, value, onChange, disabled }: IProps) => {
const modelOptions = useComposeLlmOptionsByModelTypes([
LlmModelType.Chat,
LlmModelType.Image2text,
@ -38,6 +39,7 @@ const LLMSelect = ({ id, value, onChange }: IProps) => {
id={id}
value={value}
onChange={onChange}
disabled={disabled}
/>
</Popover>
);

View File

@ -0,0 +1,31 @@
import { LlmModelType } from '@/constants/knowledge';
import { useComposeLlmOptionsByModelTypes } from '@/hooks/llm-hooks';
import { useMemo } from 'react';
interface IProps {
id?: string;
value?: string;
onChange?: (value: string) => void;
disabled?: boolean;
}
const LLMLabel = ({ value }: IProps) => {
const modelOptions = useComposeLlmOptionsByModelTypes([
LlmModelType.Chat,
LlmModelType.Image2text,
]);
const label = useMemo(() => {
for (const item of modelOptions) {
for (const option of item.options) {
if (option.value === value) {
return option.label;
}
}
}
}, [modelOptions, value]);
return <div>{label}</div>;
};
export default LLMLabel;

View File

@ -1,5 +1,8 @@
import Icon from '@ant-design/icons';
import { IconMap } from '@/constants/setting';
import Icon, { UserOutlined } from '@ant-design/icons';
import { IconComponentProps } from '@ant-design/icons/lib/components/Icon';
import { Avatar } from 'antd';
import { AvatarSize } from 'antd/es/avatar/AvatarContext';
const importAll = (requireContext: __WebpackModuleApi.RequireContext) => {
const list = requireContext.keys().map((key) => {
@ -36,4 +39,24 @@ const SvgIcon = ({ name, width, height, ...restProps }: IProps) => {
);
};
export const LlmIcon = ({
name,
height = 48,
width = 48,
size = 'large',
}: {
name: string;
height?: number;
width?: number;
size?: AvatarSize;
}) => {
const icon = IconMap[name as keyof typeof IconMap];
return icon ? (
<SvgIcon name={`llm/${icon}`} width={width} height={height}></SvgIcon>
) : (
<Avatar shape="square" size={size} icon={<UserOutlined />} />
);
};
export default SvgIcon;

View File

@ -19,6 +19,56 @@ export const UserSettingRouteMap = {
[UserSettingRouteKey.Logout]: 'Log out',
};
// Please lowercase the file name
export const IconMap = {
'Tongyi-Qianwen': 'tongyi',
Moonshot: 'moonshot',
OpenAI: 'openai',
'ZHIPU-AI': 'zhipu',
: 'wenxin',
Ollama: 'ollama',
Xinference: 'xinference',
DeepSeek: 'deepseek',
VolcEngine: 'volc_engine',
BaiChuan: 'baichuan',
Jina: 'jina',
MiniMax: 'chat-minimax',
Mistral: 'mistral',
'Azure-OpenAI': 'azure',
Bedrock: 'bedrock',
Gemini: 'gemini',
Groq: 'groq-next',
OpenRouter: 'open-router',
LocalAI: 'local-ai',
StepFun: 'stepfun',
NVIDIA: 'nvidia',
'LM-Studio': 'lm-studio',
'OpenAI-API-Compatible': 'openai-api',
cohere: 'cohere',
LeptonAI: 'lepton-ai',
TogetherAI: 'together-ai',
PerfXCloud: 'perfx-cloud',
Upstage: 'upstage',
'novita.ai': 'novita-ai',
SILICONFLOW: 'siliconflow',
'01.AI': 'yi',
Replicate: 'replicate',
'Tencent Hunyuan': 'hunyuan',
'XunFei Spark': 'spark',
BaiduYiyan: 'yiyan',
'Fish Audio': 'fish-audio',
'Tencent Cloud': 'tencent-cloud',
Anthropic: 'anthropic',
'Voyage AI': 'voyage',
'Google Cloud': 'google-cloud',
HuggingFace: 'huggingface',
Youdao: 'youdao',
BAAI: 'baai',
'nomic-ai': 'nomic-ai',
jinaai: 'jina',
'sentence-transformers': 'sentence-transformers',
};
export const TimezoneList = [
'UTC-11\tPacific/Midway',
'UTC-11\tPacific/Niue',

View File

@ -1,3 +1,4 @@
import { LlmIcon } from '@/components/svg-icon';
import { LlmModelType } from '@/constants/knowledge';
import { ResponseGetType } from '@/interfaces/database/base';
import {
@ -13,7 +14,7 @@ import {
import userService from '@/services/user-service';
import { sortLLmFactoryListBySpecifiedOrder } from '@/utils/common-util';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { message } from 'antd';
import { Flex, message } from 'antd';
import { DefaultOptionType } from 'antd/es/select';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@ -53,6 +54,14 @@ export const useSelectLlmOptions = () => {
return embeddingModelOptions;
};
const getLLMIconName = (fid: string, llm_name: string) => {
if (fid === 'FastEmbed') {
return llm_name.split('/').at(0) ?? '';
}
return fid;
};
export const useSelectLlmOptionsByModelType = () => {
const llmInfo: IThirdOAIModelCollection = useFetchLlmList();
@ -71,7 +80,17 @@ export const useSelectLlmOptionsByModelType = () => {
x.available,
)
.map((x) => ({
label: x.llm_name,
label: (
<Flex align="center" gap={6}>
<LlmIcon
name={getLLMIconName(x.fid, x.llm_name)}
width={26}
height={26}
size={'small'}
/>
<span>{x.llm_name}</span>
</Flex>
),
value: `${x.llm_name}@${x.fid}`,
disabled: !x.available,
})),

View File

@ -728,18 +728,20 @@ The above is the content you need to summarize.`,
'The window size of conversation history that needed to be seen by LLM. The larger the better. But be careful with the maximum content length of LLM.',
wikipedia: 'Wikipedia',
pubMed: 'PubMed',
pubMedDescription:
'This component is used to get search result from https://pubmed.ncbi.nlm.nih.gov/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt. E-mail is a required field.',
email: 'Email',
emailTip:
'This component is used to get search result from https://pubmed.ncbi.nlm.nih.gov/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt. E-mail is a required field.',
arXiv: 'ArXiv',
arXivTip:
arXivDescription:
'This component is used to get search result from https://arxiv.org/. Typically, it performs as a supplement to knowledgebases. Top N specifies the number of search results you need to adapt.',
sortBy: 'Sort by',
submittedDate: 'Submitted date',
lastUpdatedDate: 'Last updated date',
relevance: 'Relevance',
google: 'Google',
googleTip:
googleDescription:
'This component is used to get search result fromhttps://www.google.com/ . Typically, it performs as a supplement to knowledgebases. Top N and SerpApi API key specifies the number of search results you need to adapt.',
bing: 'Bing',
bingTip:

View File

@ -680,18 +680,21 @@ export default {
messageHistoryWindowSizeTip:
'LLM需要查看的對話記錄的視窗大小。越大越好。但要注意LLM的最大內容長度。',
wikipedia: '維基百科',
pubMed: 'PubMed',
pubMedDescription:
'此元件用於從 https://pubmed.ncbi.nlm.nih.gov/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。電子郵件是必填欄位。',
email: '信箱',
emailTip:
'此元件用於從 https://pubmed.ncbi.nlm.nih.gov/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。電子郵件是必填欄位。',
arXiv: 'ArXiv',
arXivTip:
arXivDescription:
'此元件用於從 https://arxiv.org/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 指定您需要適應的搜尋結果的數量。',
sortBy: '排序方式',
submittedDate: '提交日期',
lastUpdatedDate: '最後更新日期',
relevance: '關聯',
google: 'Google',
googleTip:
googleDescription:
'此元件用於從https://www.google.com/取得搜尋結果。通常,它作為知識庫的補充。 Top N 和 SerpApi API 金鑰指定您需要調整的搜尋結果數量。',
bing: 'Bing',
bingTip:

View File

@ -688,7 +688,7 @@ export default {
keywordExtract: '关键词',
keywordExtractDescription: `该组件用于从用户的问题中提取关键词。Top N指定需要提取的关键词数量。`,
baidu: '百度',
baiduDescription: `元件用於取得www.baidu.com的搜尋結果。通常作為知識庫的補充。 Top N指定您需要適配的搜尋結果數`,
baiduDescription: `组件用于从 www.baidu.com 获取搜索结果。通常它作为知识库的补充。Top N 指定您需要调整的搜索结果数量`,
duckDuckGo: 'DuckDuckGo',
duckDuckGoDescription:
'此元件用於從 www.duckduckgo.com 取得搜尋結果。通常,它作為知識庫的補充。 Top N 指定您需要調整的搜尋結果數。',
@ -700,18 +700,21 @@ export default {
messageHistoryWindowSizeTip:
'LLM 需要查看的对话历史窗口大小。越大越好。但要注意 LLM 的最大内容长度。',
wikipedia: '维基百科',
email: '邮箱',
emailTip:
'此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。',
email: '邮箱',
pubMed: 'PubMed',
pubMedDescription:
'此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。',
arXiv: 'ArXiv',
arXivTip:
arXivDescription:
'此组件用于从 https://arxiv.org/ 获取搜索结果。通常它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。',
sortBy: '排序方式',
submittedDate: '提交日期',
lastUpdatedDate: '最后更新日期',
relevance: '关联',
google: 'Google',
googleTip:
googleDescription:
'此组件用于从https://www.google.com/获取搜索结果。通常它作为知识库的补充。Top N 和 SerpApi API 密钥指定您需要调整的搜索结果数量。',
bing: 'Bing',
bingTip:

View File

@ -22,9 +22,15 @@ import styles from './index.less';
import { RagNode } from './node';
import { BeginNode } from './node/begin-node';
import { CategorizeNode } from './node/categorize-node';
import { GenerateNode } from './node/generate-node';
import { KeywordNode } from './node/keyword-node';
import { LogicNode } from './node/logic-node';
import { MessageNode } from './node/message-node';
import NoteNode from './node/note-node';
import { RelevantNode } from './node/relevant-node';
import { RetrievalNode } from './node/retrieval-node';
import { RewriteNode } from './node/rewrite-node';
import { SwitchNode } from './node/switch-node';
const nodeTypes = {
ragNode: RagNode,
@ -33,6 +39,12 @@ const nodeTypes = {
relevantNode: RelevantNode,
logicNode: LogicNode,
noteNode: NoteNode,
switchNode: SwitchNode,
generateNode: GenerateNode,
retrievalNode: RetrievalNode,
messageNode: MessageNode,
rewriteNode: RewriteNode,
keywordNode: KeywordNode,
};
const edgeTypes = {

View File

@ -1,25 +1,24 @@
import { useTranslate } from '@/hooks/common-hooks';
import { Flex } from 'antd';
import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst';
import { useTranslation } from 'react-i18next';
import { Handle, NodeProps, Position } from 'reactflow';
import { Operator, operatorMap } from '../../constant';
import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import { RightHandleStyle } from './handle-icon';
import styles from './index.less';
// TODO: do not allow other nodes to connect to this node
export function BeginNode({ id, data, selected }: NodeProps<NodeData>) {
const { t } = useTranslate('flow');
export function BeginNode({ selected, data }: NodeProps<NodeData>) {
const { t } = useTranslation();
return (
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
style={{
backgroundColor: operatorMap[data.label as Operator].backgroundColor,
color: 'white',
width: 50,
height: 50,
width: 100,
}}
>
<Handle
@ -27,13 +26,17 @@ export function BeginNode({ id, data, selected }: NodeProps<NodeData>) {
position={Position.Right}
isConnectable
className={styles.handle}
style={RightHandleStyle}
></Handle>
<Flex vertical align="center" justify="center" gap={6}>
<span className={styles.type}>{t(lowerFirst(data.label))}</span>
<Flex align="center" justify={'space-around'}>
<OperatorIcon
name={data.label as Operator}
fontSize={24}
color={operatorMap[data.label as Operator].color}
></OperatorIcon>
<div className={styles.nodeTitle}>{t(`flow.begin`)}</div>
</Flex>
<section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div>
</section>
</section>
);
}

View File

@ -1,22 +1,17 @@
import { useTranslate } from '@/hooks/common-hooks';
import LLMLabel from '@/components/llm-select/llm-label';
import { Flex } from 'antd';
import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
import { Operator, SwitchElseTo, operatorMap } from '../../constant';
import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import CategorizeHandle from './categorize-handle';
import NodeDropdown from './dropdown';
import { RightHandleStyle } from './handle-icon';
import { useBuildCategorizeHandlePositions } from './hooks';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');
const { positions } = useBuildCategorizeHandlePositions({ data, id });
const operatorName = data.label;
return (
<NodePopover nodeId={id}>
@ -24,10 +19,6 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
style={{
backgroundColor: style.backgroundColor,
color: style.color,
}}
>
<Handle
type="target"
@ -36,47 +27,35 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) {
className={styles.handle}
id={'a'}
></Handle>
<Handle
type="target"
position={Position.Top}
isConnectable
className={styles.handle}
id={'b'}
></Handle>
<Handle
type="target"
position={Position.Bottom}
isConnectable
className={styles.handle}
id={'c'}
></Handle>
{operatorName === Operator.Switch && (
<CategorizeHandle top={50} right={-4} id={SwitchElseTo}>
To
</CategorizeHandle>
)}
{positions.map((position, idx) => {
return (
<CategorizeHandle
top={position.top}
right={position.right}
key={idx}
id={position.text}
idx={idx}
></CategorizeHandle>
);
})}
<Flex vertical align="center" justify="center" gap={6}>
<OperatorIcon
name={data.label as Operator}
fontSize={24}
></OperatorIcon>
<span className={styles.type}>{t(lowerFirst(data.label))}</span>
<NodeDropdown id={id}></NodeDropdown>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={styles.nodeHeader}
></NodeHeader>
<Flex vertical gap={8}>
<div className={styles.nodeText}>
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
</div>
{positions.map((position, idx) => {
return (
<div key={idx}>
<div className={styles.nodeText}>{position.text}</div>
<Handle
key={position.text}
id={position.text}
type="source"
position={Position.Right}
isConnectable
className={styles.handle}
style={{ ...RightHandleStyle, top: position.top }}
></Handle>
</div>
);
})}
</Flex>
<section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div>
</section>
</section>
</NodePopover>
);

View File

@ -38,7 +38,7 @@ const NodeDropdown = ({ id, iconFontColor }: IProps) => {
return (
<OperateDropdown
iconFontSize={14}
iconFontSize={22}
height={14}
deleteItem={deleteNode}
items={items}

View File

@ -0,0 +1,74 @@
import LLMLabel from '@/components/llm-select/llm-label';
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
import { useGetComponentLabelByValue } from '../../hooks';
import { IGenerateParameter, NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function GenerateNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const parameters: IGenerateParameter[] = get(data, 'form.parameters', []);
const getLabel = useGetComponentLabelByValue(id);
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
>
<Handle
id="c"
type="source"
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
style={RightHandleStyle}
id="b"
></Handle>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={styles.nodeHeader}
></NodeHeader>
<div className={styles.nodeText}>
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
</div>
<Flex gap={8} vertical className={styles.generateParameters}>
{parameters.map((x) => (
<Flex
key={x.id}
align="center"
gap={6}
className={styles.conditionBlock}
>
<label htmlFor="">{x.key}</label>
<span className={styles.parameterValue}>
{getLabel(x.component_id)}
</span>
</Flex>
))}
</Flex>
</section>
</NodePopover>
);
}

View File

@ -0,0 +1,20 @@
import { PlusOutlined } from '@ant-design/icons';
import { CSSProperties } from 'react';
export const HandleIcon = () => {
return (
<PlusOutlined
style={{ fontSize: 6, color: 'white', position: 'absolute', zIndex: 10 }}
/>
);
};
export const RightHandleStyle: CSSProperties = {
right: -5,
};
export const LeftHandleStyle: CSSProperties = {
left: -7,
};
export default HandleIcon;

View File

@ -1,14 +1,13 @@
import get from 'lodash/get';
import pick from 'lodash/pick';
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo } from 'react';
import { useUpdateNodeInternals } from 'reactflow';
import { Operator } from '../../constant';
import { IPosition, NodeData } from '../../interface';
import { SwitchElseTo } from '../../constant';
import {
buildNewPositionMap,
generateSwitchHandleText,
isKeysEqual,
} from '../../utils';
ICategorizeItemResult,
ISwitchCondition,
NodeData,
} from '../../interface';
import { generateSwitchHandleText } from '../../utils';
export const useBuildCategorizeHandlePositions = ({
data,
@ -17,85 +16,86 @@ export const useBuildCategorizeHandlePositions = ({
id: string;
data: NodeData;
}) => {
const operatorName = data.label as Operator;
const updateNodeInternals = useUpdateNodeInternals();
const [positionMap, setPositionMap] = useState<Record<string, IPosition>>({});
const categoryData = useMemo(() => {
if (operatorName === Operator.Categorize) {
return get(data, `form.category_description`, {});
} else if (operatorName === Operator.Switch) {
return get(data, 'form.conditions', []);
}
return {};
}, [data, operatorName]);
const categoryData: ICategorizeItemResult = useMemo(() => {
return get(data, `form.category_description`, {});
}, [data]);
const positions = useMemo(() => {
return Object.keys(categoryData)
.map((x, idx) => {
const position = positionMap[x];
let text = x;
if (operatorName === Operator.Switch) {
text = generateSwitchHandleText(idx);
}
return { text, ...position };
})
.filter((x) => typeof x?.right === 'number');
}, [categoryData, positionMap, operatorName]);
const list: Array<{
text: string;
top: number;
idx: number;
}> = [];
useEffect(() => {
// Cache used coordinates
setPositionMap((state) => {
const categoryDataKeys = Object.keys(categoryData);
const stateKeys = Object.keys(state);
if (!isKeysEqual(categoryDataKeys, stateKeys)) {
const { newPositionMap, intersectionKeys } = buildNewPositionMap(
categoryDataKeys,
state,
);
const nextPositionMap = {
...pick(state, intersectionKeys),
...newPositionMap,
};
return nextPositionMap;
}
return state;
Object.keys(categoryData).forEach((x, idx) => {
list.push({
text: x,
idx,
top: idx === 0 ? 98 : list[idx - 1].top + 8 + 26,
});
});
return list;
}, [categoryData]);
useEffect(() => {
updateNodeInternals(id);
}, [id, updateNodeInternals, positionMap]);
}, [id, updateNodeInternals, categoryData]);
return { positions };
};
// export const useBuildSwitchHandlePositions = ({
// data,
// id,
// }: {
// id: string;
// data: NodeData;
// }) => {
// const [positionMap, setPositionMap] = useState<Record<string, IPosition>>({});
// const conditions = useMemo(() => get(data, 'form.conditions', []), [data]);
// const updateNodeInternals = useUpdateNodeInternals();
export const useBuildSwitchHandlePositions = ({
data,
id,
}: {
id: string;
data: NodeData;
}) => {
const updateNodeInternals = useUpdateNodeInternals();
// const positions = useMemo(() => {
// return conditions
// .map((x, idx) => {
// const text = `Item ${idx}`;
// const position = positionMap[text];
// return { text: text, ...position };
// })
// .filter((x) => typeof x?.right === 'number');
// }, [conditions, positionMap]);
const conditions: ISwitchCondition[] = useMemo(() => {
return get(data, 'form.conditions', []);
}, [data]);
// useEffect(() => {
// updateNodeInternals(id);
// }, [id, updateNodeInternals, positionMap]);
const positions = useMemo(() => {
const list: Array<{
text: string;
top: number;
idx: number;
condition?: ISwitchCondition;
}> = [];
// return { positions };
// };
[...conditions, ''].forEach((x, idx) => {
let top = idx === 0 ? 58 : list[idx - 1].top + 32; // case number (Case 1) height + flex gap
if (idx - 1 >= 0) {
const previousItems = conditions[idx - 1]?.items ?? [];
if (previousItems.length > 0) {
top += 12; // ConditionBlock padding
top += previousItems.length * 22; // condition variable height
top += (previousItems.length - 1) * 25; // operator height
}
}
list.push({
text:
idx < conditions.length
? generateSwitchHandleText(idx)
: SwitchElseTo,
idx,
top,
condition: typeof x === 'string' ? undefined : x,
});
});
return list;
}, [conditions]);
useEffect(() => {
updateNodeInternals(id);
}, [id, updateNodeInternals, conditions]);
return { positions };
};

View File

@ -3,22 +3,16 @@
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
padding: 10px;
border-radius: 10px;
background: white;
width: 200px;
}
.ragNode {
position: relative;
.commonNode();
padding: 5px;
border-radius: 5px;
background: white;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
// align-items: center;
// justify-self: center;
justify-content: center;
.nodeName {
font-size: 10px;
color: black;
@ -28,23 +22,10 @@
color: #777;
font-size: 12px;
}
.type {
// font-size: 12px;
}
.description {
font-size: 10px;
}
.bottomBox {
position: absolute;
bottom: -34px;
background: white;
padding: 2px 5px;
border-radius: 5px;
box-shadow:
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
}
.categorizeAnchorPointText {
position: absolute;
top: -4px;
@ -53,14 +34,25 @@
}
}
@lightBackgroundColor: rgba(150, 150, 150, 0.1);
@darkBackgroundColor: rgba(150, 150, 150, 0.2);
.selectedNode {
border: 1px solid rgb(59, 118, 244);
border: 1.5px solid rgb(59, 118, 244);
}
.handle {
display: inline-flex;
text-align: center;
// align-items: center;
align-items: center;
justify-content: center;
width: 12px;
height: 12px;
background: rgb(59, 88, 253);
border: 1px solid white;
z-index: 1;
background-image: url('@/assets/svg/plus.svg');
background-size: cover;
background-position: center;
}
.jsonView {
@ -71,19 +63,8 @@
}
.logicNode {
position: relative;
.commonNode();
padding: 5px;
border-radius: 5px;
background: white;
width: 100px;
height: 100px;
border-radius: 50%;
display: flex;
// align-items: center;
// justify-self: center;
justify-content: center;
.nodeName {
font-size: 10px;
color: black;
@ -93,41 +74,122 @@
color: #777;
font-size: 12px;
}
.type {
// font-size: 12px;
}
.description {
font-size: 10px;
}
.bottomBox {
position: absolute;
bottom: -34px;
background: white;
padding: 2px 5px;
border-radius: 5px;
box-shadow:
-6px 0 12px 0 rgba(179, 177, 177, 0.08),
-3px 0 6px -4px rgba(0, 0, 0, 0.12),
-6px 0 16px 6px rgba(0, 0, 0, 0.05);
}
.categorizeAnchorPointText {
position: absolute;
top: -4px;
left: 8px;
white-space: nowrap;
}
.relevantSourceLabel {
font-size: 10px;
}
}
.noteNode {
.commonNode();
width: 140px;
padding: 4px 6px 6px;
min-width: 140px;
width: auto;
height: 100%;
padding: 0;
border-radius: 10px;
background-color: #dbf8f4;
min-height: 128px;
.noteTitle {
background-color: #edfcff;
font-size: 12px;
padding: 6px 6px 4px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.noteForm {
margin-top: 4px;
height: calc(100% - 50px);
}
.noteName {
padding: 0px 4px;
}
.noteTextarea {
resize: none;
border: 0;
border-radius: 0;
height: 100%;
&:focus {
border: none;
box-shadow: none;
}
}
}
.nodeText {
padding-inline: 0.4em;
padding-block: 0.2em 0.1em;
background: @lightBackgroundColor;
border-radius: 3px;
min-height: 22px;
.textEllipsis();
}
.nodeTitle {
font-weight: 600;
text-align: center;
.textEllipsis();
}
.nodeHeader {
padding-bottom: 12px;
}
.zeroDivider {
margin: 0 !important;
}
.conditionBlock {
border-radius: 4px;
padding: 6px;
background: @lightBackgroundColor;
}
.conditionLine {
border-radius: 4px;
padding: 0 4px;
background: @darkBackgroundColor;
.textEllipsis();
}
.conditionKey {
flex: 1;
}
.conditionOperator {
padding: 0 2px;
text-align: center;
}
.relevantLabel {
text-align: right;
}
.knowledgeNodeName {
.textEllipsis();
}
.messageNodeContainer {
overflow-y: auto;
max-height: 300px;
}
.generateParameters {
padding-top: 8px;
label {
flex: 2;
.textEllipsis();
}
.parameterValue {
flex: 3;
.conditionLine;
}
}

View File

@ -1,12 +1,9 @@
import { Flex } from 'antd';
import classNames from 'classnames';
import pick from 'lodash/pick';
import { Handle, NodeProps, Position } from 'reactflow';
import { Operator, operatorMap } from '../../constant';
import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import NodeDropdown from './dropdown';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function RagNode({
@ -15,17 +12,12 @@ export function RagNode({
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const style = operatorMap[data.label as Operator];
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.ragNode, {
[styles.selectedNode]: selected,
})}
style={{
...pick(style, ['backgroundColor', 'color']),
}}
>
<Handle
id="c"
@ -33,39 +25,17 @@ export function RagNode({
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle type="source" position={Position.Top} id="d" isConnectable />
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
id="b"
style={RightHandleStyle}
></Handle>
<Handle type="source" position={Position.Bottom} id="a" isConnectable />
<Flex vertical align="center" justify={'space-around'}>
<Flex flex={1} justify="center" align="center">
<label htmlFor=""> </label>
</Flex>
<Flex flex={1}>
<OperatorIcon
name={data.label as Operator}
fontSize={style?.iconFontSize ?? 16}
width={style?.iconWidth}
></OperatorIcon>
</Flex>
<Flex flex={1}>
<NodeDropdown
id={id}
iconFontColor={style?.moreIconColor}
></NodeDropdown>
</Flex>
</Flex>
<section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div>
</section>
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
</section>
</NodePopover>
);

View File

@ -0,0 +1,54 @@
import LLMLabel from '@/components/llm-select/llm-label';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function KeywordNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
>
<Handle
id="c"
type="source"
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
style={RightHandleStyle}
id="b"
></Handle>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={styles.nodeHeader}
></NodeHeader>
<div className={styles.nodeText}>
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
</div>
</section>
</NodePopover>
);
}

View File

@ -1,38 +1,23 @@
import { useTranslate } from '@/hooks/common-hooks';
import { Flex } from 'antd';
import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst';
import pick from 'lodash/pick';
import { Handle, NodeProps, Position } from 'reactflow';
import { Operator, operatorMap } from '../../constant';
import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import NodeDropdown from './dropdown';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
const ZeroGapOperators = [
Operator.RewriteQuestion,
Operator.KeywordExtract,
Operator.ArXiv,
];
export function LogicNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
>
<Handle
id="c"
@ -40,49 +25,17 @@ export function LogicNode({
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle type="source" position={Position.Top} id="d" isConnectable />
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
style={RightHandleStyle}
id="b"
></Handle>
<Handle type="source" position={Position.Bottom} id="a" isConnectable />
<Flex
vertical
align="center"
justify={'space-around'}
gap={ZeroGapOperators.some((x) => x === data.label) ? 0 : 6}
>
<Flex flex={1} justify="center" align="center">
<OperatorIcon
name={data.label as Operator}
fontSize={style?.iconFontSize ?? 24}
width={style?.iconWidth}
></OperatorIcon>
</Flex>
<Flex flex={1}>
<span
className={styles.type}
style={{ fontSize: style?.fontSize ?? 14 }}
>
{t(lowerFirst(data.label))}
</span>
</Flex>
<Flex flex={1}>
<NodeDropdown
id={id}
iconFontColor={style?.moreIconColor}
></NodeDropdown>
</Flex>
</Flex>
<section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div>
</section>
<NodeHeader id={id} name={data.name} label={data.label}></NodeHeader>
</section>
</NodePopover>
);

View File

@ -0,0 +1,63 @@
import { Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function MessageNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const messages: string[] = get(data, 'form.messages', []);
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
>
<Handle
id="c"
type="source"
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
style={RightHandleStyle}
id="b"
></Handle>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={classNames({
[styles.nodeHeader]: messages.length > 0,
})}
></NodeHeader>
<Flex vertical gap={8} className={styles.messageNodeContainer}>
{messages.map((message, idx) => {
return (
<div className={styles.nodeText} key={idx}>
{message}
</div>
);
})}
</Flex>
</section>
</NodePopover>
);
}

View File

@ -0,0 +1,35 @@
import { Flex } from 'antd';
import { Operator, operatorMap } from '../../constant';
import OperatorIcon from '../../operator-icon';
import NodeDropdown from './dropdown';
import styles from './index.less';
interface IProps {
id: string;
label: string;
name: string;
gap?: number;
className?: string;
}
const NodeHeader = ({ label, id, name, gap = 4, className }: IProps) => {
return (
<Flex
flex={1}
align="center"
justify={'space-between'}
gap={gap}
className={className}
>
<OperatorIcon
name={label as Operator}
color={operatorMap[label as Operator].color}
></OperatorIcon>
<span className={styles.nodeTitle}>{name}</span>
<NodeDropdown id={id}></NodeDropdown>
</Flex>
);
};
export default NodeHeader;

View File

@ -1,20 +1,33 @@
import { Flex, Form, Input, Space } from 'antd';
import { NodeProps } from 'reactflow';
import { Flex, Form, Input } from 'antd';
import classNames from 'classnames';
import { NodeProps, NodeResizeControl } from 'reactflow';
import { NodeData } from '../../interface';
import NodeDropdown from './dropdown';
import SvgIcon from '@/components/svg-icon';
import { useEffect } from 'react';
import { memo, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useHandleFormValuesChange } from '../../hooks';
import {
useHandleFormValuesChange,
useHandleNodeNameChange,
} from '../../hooks';
import styles from './index.less';
const { TextArea } = Input;
const controlStyle = {
background: 'transparent',
border: 'none',
};
function NoteNode({ data, id }: NodeProps<NodeData>) {
const { t } = useTranslation();
const [form] = Form.useForm();
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
id,
data,
});
const { handleValuesChange } = useHandleFormValuesChange(id);
useEffect(() => {
@ -22,25 +35,51 @@ function NoteNode({ data, id }: NodeProps<NodeData>) {
}, [form, data?.form]);
return (
<section className={styles.noteNode}>
<Flex justify={'space-between'}>
<Space size={'small'}>
<>
<NodeResizeControl style={controlStyle} minWidth={190} minHeight={128}>
<SvgIcon
name="resize"
width={12}
style={{
position: 'absolute',
right: 5,
bottom: 5,
cursor: 'nwse-resize',
}}
></SvgIcon>
</NodeResizeControl>
<section className={styles.noteNode}>
<Flex
justify={'space-between'}
className={classNames(styles.noteTitle, 'note-drag-handle')}
align="center"
gap={6}
>
<SvgIcon name="note" width={14}></SvgIcon>
<span className={styles.noteTitle}>{t('flow.note')}</span>
</Space>
<NodeDropdown id={id}></NodeDropdown>
</Flex>
<Form
onValuesChange={handleValuesChange}
form={form}
className={styles.noteForm}
>
<Form.Item name="text" noStyle>
<TextArea rows={3} placeholder={t('flow.notePlaceholder')} />
</Form.Item>
</Form>
</section>
<Input
value={name ?? t('flow.note')}
onBlur={handleNameBlur}
onChange={handleNameChange}
className={styles.noteName}
></Input>
<NodeDropdown id={id}></NodeDropdown>
</Flex>
<Form
onValuesChange={handleValuesChange}
form={form}
className={styles.noteForm}
>
<Form.Item name="text" noStyle>
<TextArea
rows={3}
placeholder={t('flow.notePlaceholder')}
className={styles.noteTextarea}
/>
</Form.Item>
</Form>
</section>
</>
);
}
export default NoteNode;
export default memo(NoteNode);

View File

@ -1,28 +1,23 @@
import { useTranslate } from '@/hooks/common-hooks';
import { Flex } from 'antd';
import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst';
import pick from 'lodash/pick';
import { Handle, NodeProps, Position } from 'reactflow';
import { Operator, operatorMap } from '../../constant';
import { NodeData } from '../../interface';
import OperatorIcon from '../../operator-icon';
import NodeDropdown from './dropdown';
import CategorizeHandle from './categorize-handle';
import styles from './index.less';
import { RightHandleStyle } from './handle-icon';
import NodePopover from './popover';
import { get } from 'lodash';
import styles from './index.less';
import NodeHeader from './node-header';
export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
const style = operatorMap[data.label as Operator];
const { t } = useTranslate('flow');
const yes = get(data, 'form.yes');
const no = get(data, 'form.no');
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
style={pick(style, ['backgroundColor', 'width', 'height', 'color'])}
>
<Handle
type="target"
@ -32,43 +27,38 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) {
id={'a'}
></Handle>
<Handle
type="target"
position={Position.Top}
type="source"
position={Position.Right}
isConnectable
className={styles.handle}
id={'b'}
id={'yes'}
style={{ ...RightHandleStyle, top: 59 }}
></Handle>
<Handle
type="target"
position={Position.Bottom}
type="source"
position={Position.Right}
isConnectable
className={styles.handle}
id={'c'}
id={'no'}
style={{ ...RightHandleStyle, top: 112 }}
></Handle>
<CategorizeHandle top={20} right={6} id={'yes'}></CategorizeHandle>
<CategorizeHandle top={80} right={6} id={'no'}></CategorizeHandle>
<Flex vertical align="center" justify="center" gap={0}>
<Flex flex={1}>
<OperatorIcon
name={data.label as Operator}
fontSize={style.iconFontSize}
></OperatorIcon>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={styles.nodeHeader}
></NodeHeader>
<Flex vertical gap={10}>
<Flex vertical>
<div className={styles.relevantLabel}>Yes</div>
<div className={styles.nodeText}>{yes}</div>
</Flex>
<Flex flex={1}>
<span
className={styles.type}
style={{ fontSize: style.fontSize ?? 14 }}
>
{t(lowerFirst(data.label))}
</span>
</Flex>
<Flex flex={1}>
<NodeDropdown id={id}></NodeDropdown>
<Flex vertical>
<div className={styles.relevantLabel}>No</div>
<div className={styles.nodeText}>{no}</div>
</Flex>
</Flex>
<section className={styles.bottomBox}>
<div className={styles.nodeName}>{data.name}</div>
</section>
</section>
</NodePopover>
);

View File

@ -0,0 +1,85 @@
import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { UserOutlined } from '@ant-design/icons';
import { Avatar, Flex } from 'antd';
import classNames from 'classnames';
import { get } from 'lodash';
import { useMemo } from 'react';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function RetrievalNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []);
const { list: knowledgeList } = useNextFetchKnowledgeList(true);
const knowledgeBases = useMemo(() => {
return knowledgeBaseIds.map((x) => {
const item = knowledgeList.find((y) => x === y.id);
return {
name: item?.name,
avatar: item?.avatar,
id: x,
};
});
}, [knowledgeList, knowledgeBaseIds]);
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
>
<Handle
id="c"
type="source"
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
style={RightHandleStyle}
id="b"
></Handle>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={classNames({
[styles.nodeHeader]: knowledgeBaseIds.length > 0,
})}
></NodeHeader>
<Flex vertical gap={8}>
{knowledgeBases.map((knowledge) => {
return (
<div className={styles.nodeText} key={knowledge.id}>
<Flex align={'center'} gap={6}>
<Avatar
size={26}
icon={<UserOutlined />}
src={knowledge.avatar}
/>
<Flex className={styles.knowledgeNodeName} flex={1}>
{knowledge.name}
</Flex>
</Flex>
</div>
);
})}
</Flex>
</section>
</NodePopover>
);
}

View File

@ -0,0 +1,54 @@
import LLMLabel from '@/components/llm-select/llm-label';
import classNames from 'classnames';
import { get } from 'lodash';
import { Handle, NodeProps, Position } from 'reactflow';
import { NodeData } from '../../interface';
import { LeftHandleStyle, RightHandleStyle } from './handle-icon';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
export function RewriteNode({
id,
data,
isConnectable = true,
selected,
}: NodeProps<NodeData>) {
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
>
<Handle
id="c"
type="source"
position={Position.Left}
isConnectable={isConnectable}
className={styles.handle}
style={LeftHandleStyle}
></Handle>
<Handle
type="source"
position={Position.Right}
isConnectable={isConnectable}
className={styles.handle}
style={RightHandleStyle}
id="b"
></Handle>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={styles.nodeHeader}
></NodeHeader>
<div className={styles.nodeText}>
<LLMLabel value={get(data, 'form.llm_id')}></LLMLabel>
</div>
</section>
</NodePopover>
);
}

View File

@ -0,0 +1,112 @@
import { Divider, Flex } from 'antd';
import classNames from 'classnames';
import { Handle, NodeProps, Position } from 'reactflow';
import { useGetComponentLabelByValue } from '../../hooks';
import { ISwitchCondition, NodeData } from '../../interface';
import { RightHandleStyle } from './handle-icon';
import { useBuildSwitchHandlePositions } from './hooks';
import styles from './index.less';
import NodeHeader from './node-header';
import NodePopover from './popover';
const getConditionKey = (idx: number, length: number) => {
if (idx === 0 && length !== 1) {
return 'If';
} else if (idx === length - 1) {
return 'Else';
}
return 'ElseIf';
};
const ConditionBlock = ({
condition,
nodeId,
}: {
condition: ISwitchCondition;
nodeId: string;
}) => {
const items = condition?.items ?? [];
const getLabel = useGetComponentLabelByValue(nodeId);
return (
<Flex vertical className={styles.conditionBlock}>
{items.map((x, idx) => (
<div key={idx}>
<Flex>
<div
className={classNames(styles.conditionLine, styles.conditionKey)}
>
{getLabel(x?.cpn_id)}
</div>
<span className={styles.conditionOperator}>{x?.operator}</span>
<Flex flex={1} className={styles.conditionLine}>
{x?.value}
</Flex>
</Flex>
{idx + 1 < items.length && (
<Divider orientationMargin="0" className={styles.zeroDivider}>
{condition?.logical_operator}
</Divider>
)}
</div>
))}
</Flex>
);
};
export function SwitchNode({ id, data, selected }: NodeProps<NodeData>) {
const { positions } = useBuildSwitchHandlePositions({ data, id });
return (
<NodePopover nodeId={id}>
<section
className={classNames(styles.logicNode, {
[styles.selectedNode]: selected,
})}
>
<Handle
type="target"
position={Position.Left}
isConnectable
className={styles.handle}
id={'a'}
></Handle>
<NodeHeader
id={id}
name={data.name}
label={data.label}
className={styles.nodeHeader}
></NodeHeader>
<Flex vertical gap={10}>
{positions.map((position, idx) => {
return (
<div key={idx}>
<Flex vertical>
<Flex justify={'space-between'}>
<span>{idx < positions.length - 1 && position.text}</span>
<span>{getConditionKey(idx, positions.length)}</span>
</Flex>
{position.condition && (
<ConditionBlock
nodeId={id}
condition={position.condition}
></ConditionBlock>
)}
</Flex>
<Handle
key={position.text}
id={position.text}
type="source"
position={Position.Right}
isConnectable
className={styles.handle}
style={{ ...RightHandleStyle, top: position.top }}
></Handle>
</div>
);
})}
</Flex>
</section>
</NodePopover>
);
}

View File

@ -2,6 +2,7 @@ import { ReactComponent as AkShareIcon } from '@/assets/svg/akshare.svg';
import { ReactComponent as ArXivIcon } from '@/assets/svg/arxiv.svg';
import { ReactComponent as baiduFanyiIcon } from '@/assets/svg/baidu-fanyi.svg';
import { ReactComponent as BaiduIcon } from '@/assets/svg/baidu.svg';
import { ReactComponent as BeginIcon } from '@/assets/svg/begin.svg';
import { ReactComponent as BingIcon } from '@/assets/svg/bing.svg';
import { ReactComponent as ConcentratorIcon } from '@/assets/svg/concentrator.svg';
import { ReactComponent as CrawlerIcon } from '@/assets/svg/crawler.svg';
@ -39,7 +40,6 @@ import {
MessageOutlined,
RocketOutlined,
SendOutlined,
SlidersOutlined,
} from '@ant-design/icons';
import upperFirst from 'lodash/upperFirst';
@ -85,7 +85,7 @@ export const operatorIconMap = {
[Operator.Retrieval]: RocketOutlined,
[Operator.Generate]: MergeCellsOutlined,
[Operator.Answer]: SendOutlined,
[Operator.Begin]: SlidersOutlined,
[Operator.Begin]: BeginIcon,
[Operator.Categorize]: DatabaseOutlined,
[Operator.Message]: MessageOutlined,
[Operator.Relevant]: BranchesOutlined,
@ -142,7 +142,7 @@ export const operatorMap: Record<
},
[Operator.Answer]: {
backgroundColor: '#f4816d',
color: 'white',
color: '#f4816d',
},
[Operator.Begin]: {
backgroundColor: '#4f51d6',
@ -157,7 +157,7 @@ export const operatorMap: Record<
},
[Operator.Relevant]: {
backgroundColor: '#9fd94d',
color: 'white',
color: '#8ef005',
width: 70,
height: 70,
fontSize: 12,
@ -165,7 +165,7 @@ export const operatorMap: Record<
},
[Operator.RewriteQuestion]: {
backgroundColor: '#f8c7f8',
color: 'white',
color: '#f32bf3',
width: 70,
height: 70,
fontSize: 12,
@ -175,7 +175,7 @@ export const operatorMap: Record<
width: 70,
height: 70,
backgroundColor: '#0f0e0f',
color: '#e1dcdc',
color: '#0f0e0f',
fontSize: 12,
iconWidth: 16,
// iconFontSize: 16,
@ -221,14 +221,14 @@ export const operatorMap: Record<
[Operator.BaiduFanyi]: { backgroundColor: '#e5f2d3' },
[Operator.QWeather]: { backgroundColor: '#a4bbf3' },
[Operator.ExeSQL]: { backgroundColor: '#b9efe8' },
[Operator.Switch]: { backgroundColor: '#dbaff6' },
[Operator.Switch]: { backgroundColor: '#dbaff6', color: '#dbaff6' },
[Operator.WenCai]: { backgroundColor: '#faac5b' },
[Operator.AkShare]: { backgroundColor: '#8085f5' },
[Operator.YahooFinance]: { backgroundColor: '#b474ff' },
[Operator.Jin10]: { backgroundColor: '#a0b9f8' },
[Operator.Concentrator]: {
backgroundColor: '#32d2a3',
color: 'white',
color: '#32d2a3',
width: 70,
height: 70,
fontSize: 10,
@ -586,18 +586,19 @@ export const RestrictedUpstreamMap = {
[Operator.Concentrator]: [Operator.Begin],
[Operator.TuShare]: [Operator.Begin],
[Operator.Crawler]: [Operator.Begin],
[Operator.Note]: [],
};
export const NodeMap = {
[Operator.Begin]: 'beginNode',
[Operator.Categorize]: 'categorizeNode',
[Operator.Retrieval]: 'logicNode',
[Operator.Generate]: 'logicNode',
[Operator.Retrieval]: 'retrievalNode',
[Operator.Generate]: 'generateNode',
[Operator.Answer]: 'logicNode',
[Operator.Message]: 'logicNode',
[Operator.Message]: 'messageNode',
[Operator.Relevant]: 'relevantNode',
[Operator.RewriteQuestion]: 'logicNode',
[Operator.KeywordExtract]: 'logicNode',
[Operator.RewriteQuestion]: 'rewriteNode',
[Operator.KeywordExtract]: 'keywordNode',
[Operator.DuckDuckGo]: 'ragNode',
[Operator.Baidu]: 'ragNode',
[Operator.Wikipedia]: 'ragNode',
@ -611,7 +612,7 @@ export const NodeMap = {
[Operator.BaiduFanyi]: 'ragNode',
[Operator.QWeather]: 'ragNode',
[Operator.ExeSQL]: 'ragNode',
[Operator.Switch]: 'categorizeNode',
[Operator.Switch]: 'switchNode',
[Operator.Concentrator]: 'logicNode',
[Operator.WenCai]: 'ragNode',
[Operator.AkShare]: 'ragNode',

View File

@ -7,3 +7,9 @@
font-weight: 600;
}
}
.operatorDescription {
font-size: 14px;
padding-top: 16px;
font-weight: normal;
}

View File

@ -3,7 +3,7 @@ import { IModalProps } from '@/interfaces/common';
import { Drawer, Flex, Form, Input } from 'antd';
import { useEffect } from 'react';
import { Node } from 'reactflow';
import { Operator } from '../constant';
import { Operator, operatorMap } from '../constant';
import AkShareForm from '../form/akshare-form';
import AnswerForm from '../form/answer-form';
import ArXivForm from '../form/arxiv-form';
@ -36,6 +36,8 @@ import YahooFinanceForm from '../form/yahoo-finance-form';
import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks';
import OperatorIcon from '../operator-icon';
import { CloseOutlined } from '@ant-design/icons';
import { lowerFirst } from 'lodash';
import styles from './index.less';
interface IProps {
@ -74,7 +76,7 @@ const FormMap = {
[Operator.Crawler]: CrawlerForm,
};
const EmptyContent = () => <div>empty</div>;
const EmptyContent = () => <div></div>;
const FlowDrawer = ({
visible,
@ -84,8 +86,10 @@ const FlowDrawer = ({
const operatorName: Operator = node?.data.label;
const OperatorForm = FormMap[operatorName] ?? EmptyContent;
const [form] = Form.useForm();
const { name, handleNameBlur, handleNameChange } =
useHandleNodeNameChange(node);
const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({
id: node?.id,
data: node?.data,
});
const { t } = useTranslate('flow');
const { handleValuesChange } = useHandleFormValuesChange(node?.id);
@ -99,18 +103,27 @@ const FlowDrawer = ({
return (
<Drawer
title={
<Flex gap={'middle'} align="center">
<OperatorIcon name={operatorName}></OperatorIcon>
<Flex align="center" gap={'small'} flex={1}>
<label htmlFor="" className={styles.title}>
{t('title')}
</label>
<Input
value={name}
onBlur={handleNameBlur}
onChange={handleNameChange}
></Input>
<Flex vertical>
<Flex gap={'middle'} align="center">
<OperatorIcon
name={operatorName}
color={operatorMap[operatorName]?.color}
></OperatorIcon>
<Flex align="center" gap={'small'} flex={1}>
<label htmlFor="" className={styles.title}>
{t('title')}
</label>
<Input
value={name}
onBlur={handleNameBlur}
onChange={handleNameChange}
></Input>
</Flex>
<CloseOutlined onClick={hideModal} />
</Flex>
<span className={styles.operatorDescription}>
{t(`${lowerFirst(operatorName)}Description`)}
</span>
</Flex>
}
placement="right"
@ -119,6 +132,7 @@ const FlowDrawer = ({
getContainer={false}
mask={false}
width={470}
closeIcon={null}
>
<section className={styles.formWrapper}>
{visible && (

View File

@ -3,7 +3,7 @@ import { Card, Divider, Flex, Layout, Tooltip } from 'antd';
import classNames from 'classnames';
import lowerFirst from 'lodash/lowerFirst';
import React from 'react';
import { Operator, componentMenuList } from '../constant';
import { Operator, componentMenuList, operatorMap } from '../constant';
import { useHandleDrag } from '../hooks';
import OperatorIcon from '../operator-icon';
import styles from './index.less';
@ -53,7 +53,10 @@ const FlowSide = ({ setCollapsed, collapsed }: IProps) => {
onDragStart={handleDragStart(x.name)}
>
<Flex align="center" gap={15}>
<OperatorIcon name={x.name}></OperatorIcon>
<OperatorIcon
name={x.name}
color={operatorMap[x.name].color}
></OperatorIcon>
<section>
<Tooltip title={t(`${lowerFirst(x.name)}Description`)}>
<b>{t(lowerFirst(x.name))}</b>

View File

@ -3,19 +3,6 @@ import { useCallback, useMemo } from 'react';
import { Operator, RestrictedUpstreamMap } from './constant';
import useGraphStore from './store';
const ExcludedNodesMap = {
// exclude some nodes downstream of the classification node
[Operator.Categorize]: [
Operator.Categorize,
Operator.Answer,
Operator.Begin,
Operator.Relevant,
],
[Operator.Relevant]: [Operator.Begin, Operator.Answer, Operator.Relevant],
[Operator.Generate]: [Operator.Begin],
[Operator.Switch]: [Operator.Begin],
};
export const useBuildFormSelectOptions = (
operatorName: Operator,
selfId?: string, // exclude the current node
@ -24,8 +11,10 @@ export const useBuildFormSelectOptions = (
const buildCategorizeToOptions = useCallback(
(toList: string[]) => {
const excludedNodes: Operator[] =
RestrictedUpstreamMap[operatorName] ?? [];
const excludedNodes: Operator[] = [
Operator.Note,
...(RestrictedUpstreamMap[operatorName] ?? []),
];
return nodes
.filter(
(x) =>

View File

@ -1,6 +1,14 @@
import { useTranslate } from '@/hooks/common-hooks';
import { CloseOutlined } from '@ant-design/icons';
import { Button, Card, Form, FormListFieldData, Input, Select } from 'antd';
import {
Button,
Card,
Flex,
Form,
FormListFieldData,
Input,
Select,
} from 'antd';
import { FormInstance } from 'antd/lib';
import { humanId } from 'human-id';
import trim from 'lodash/trim';
@ -15,6 +23,8 @@ import { useUpdateNodeInternals } from 'reactflow';
import { Operator } from '../../constant';
import { useBuildFormSelectOptions } from '../../form-hooks';
import styles from './index.less';
interface IProps {
nodeId?: string;
}
@ -105,13 +115,12 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
if (nodeId) updateNodeInternals(nodeId);
};
return (
<div
style={{ display: 'flex', rowGap: 10, flexDirection: 'column' }}
>
<Flex gap={18} vertical>
{fields.map((field) => (
<Card
size="small"
key={field.key}
className={styles.caseCard}
extra={
<CloseOutlined
onClick={() => {
@ -172,10 +181,15 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
</Card>
))}
<Button type="dashed" onClick={handleAdd} block>
<Button
type="dashed"
onClick={handleAdd}
block
className={styles.addButton}
>
+ {t('addItem')}
</Button>
</div>
</Flex>
);
}}
</Form.List>

View File

@ -0,0 +1,11 @@
@lightBackgroundColor: rgba(150, 150, 150, 0.07);
@darkBackgroundColor: rgba(150, 150, 150, 0.12);
.caseCard {
background-color: @darkBackgroundColor;
}
.addButton {
color: rgb(22, 119, 255);
font-weight: 600;
}

View File

@ -90,6 +90,7 @@ const DynamicParameters = ({ nodeId }: IProps) => {
components={components}
rowClassName={() => styles.editableRow}
scroll={{ x: true }}
bordered
/>
</section>
);

View File

@ -0,0 +1,21 @@
@lightBackgroundColor: rgba(150, 150, 150, 0.07);
@darkBackgroundColor: rgba(150, 150, 150, 0.12);
.caseCard {
background-color: @lightBackgroundColor;
}
.conditionCard {
background-color: @darkBackgroundColor;
}
.elseCase {
background-color: @lightBackgroundColor;
padding: 12px;
border-radius: 8px;
}
.addButton {
color: rgb(22, 119, 255);
font-weight: 600;
}

View File

@ -13,6 +13,8 @@ import { useBuildComponentIdSelectOptions } from '../../hooks';
import { IOperatorForm, ISwitchForm } from '../../interface';
import { getOtherFieldValues } from '../../utils';
import styles from './index.less';
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
const { t } = useTranslation();
const buildCategorizeToOptions = useBuildFormSelectOptions(
@ -55,112 +57,134 @@ const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
<Form.List name="conditions">
{(fields, { add, remove }) => (
<div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
{fields.map((field) => (
<Card
size="small"
title={`Case ${field.name + 1}`}
key={field.key}
extra={
<CloseOutlined
onClick={() => {
remove(field.name);
}}
/>
}
>
<Form.Item noStyle dependencies={[field.name, 'items']}>
{({ getFieldValue }) =>
getFieldValue(['conditions', field.name, 'items'])?.length >
1 && (
<Form.Item
label={t('flow.logicalOperator')}
name={[field.name, 'logical_operator']}
>
<Select options={switchLogicOperatorOptions} />
</Form.Item>
)
{fields.map((field) => {
return (
<Card
size="small"
title={`Case ${field.name + 1}`}
key={field.key}
className={styles.caseCard}
extra={
<CloseOutlined
onClick={() => {
remove(field.name);
}}
/>
}
</Form.Item>
<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
<Select
allowClear
options={buildCategorizeToOptions([
form?.getFieldValue(SwitchElseTo),
...getOtherFieldValues(form!, 'conditions', field, 'to'),
])}
/>
</Form.Item>
<Form.Item label="Condition">
<Form.List name={[field.name, 'items']}>
{(subFields, subOpt) => (
<div
style={{
display: 'flex',
flexDirection: 'column',
rowGap: 16,
}}
>
{subFields.map((subField) => (
<Card
key={subField.key}
title={null}
size="small"
extra={
<CloseOutlined
onClick={() => {
subOpt.remove(subField.name);
}}
/>
}
>
<Form.Item
label={t('flow.componentId')}
name={[subField.name, 'cpn_id']}
>
<Select
placeholder={t('flow.componentId')}
options={componentIdOptions}
/>
</Form.Item>
<Form.Item
label={t('flow.operator')}
name={[subField.name, 'operator']}
>
<Select
placeholder={t('flow.operator')}
options={switchOperatorOptions}
/>
</Form.Item>
<Form.Item
label={t('flow.value')}
name={[subField.name, 'value']}
>
<Input placeholder={t('flow.value')} />
</Form.Item>
</Card>
))}
<Button
type="dashed"
onClick={() => subOpt.add()}
block
>
<Form.Item noStyle dependencies={[field.name, 'items']}>
{({ getFieldValue }) =>
getFieldValue(['conditions', field.name, 'items'])
?.length > 1 && (
<Form.Item
label={t('flow.logicalOperator')}
name={[field.name, 'logical_operator']}
>
+ Add Condition
</Button>
</div>
)}
</Form.List>
</Form.Item>
</Card>
))}
<Select options={switchLogicOperatorOptions} />
</Form.Item>
)
}
</Form.Item>
<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
<Select
allowClear
options={buildCategorizeToOptions([
form?.getFieldValue(SwitchElseTo),
...getOtherFieldValues(
form!,
'conditions',
field,
'to',
),
])}
/>
</Form.Item>
<Form.Item label="Condition">
<Form.List name={[field.name, 'items']}>
{(subFields, subOpt) => (
<div
style={{
display: 'flex',
flexDirection: 'column',
rowGap: 16,
}}
>
{subFields.map((subField) => (
<Card
key={subField.key}
title={null}
size="small"
className={styles.conditionCard}
bordered
extra={
<CloseOutlined
onClick={() => {
subOpt.remove(subField.name);
}}
/>
}
>
<Form.Item
label={t('flow.componentId')}
name={[subField.name, 'cpn_id']}
>
<Select
placeholder={t('flow.componentId')}
options={componentIdOptions}
/>
</Form.Item>
<Form.Item
label={t('flow.operator')}
name={[subField.name, 'operator']}
>
<Select
placeholder={t('flow.operator')}
options={switchOperatorOptions}
/>
</Form.Item>
<Form.Item
label={t('flow.value')}
name={[subField.name, 'value']}
>
<Input placeholder={t('flow.value')} />
</Form.Item>
</Card>
))}
<Button
onClick={() => {
form?.setFieldValue(
['conditions', field.name, 'logical_operator'],
SwitchLogicOperatorOptions[0],
);
subOpt.add({
operator: SwitchOperatorOptions[0].value,
});
}}
block
className={styles.addButton}
>
+ Add Condition
</Button>
</div>
)}
</Form.List>
</Form.Item>
</Card>
);
})}
<Button type="dashed" onClick={() => add()} block>
<Button onClick={() => add()} block className={styles.addButton}>
+ Add Case
</Button>
</div>
)}
</Form.List>
<Divider />
<Form.Item label={'ELSE'} name={[SwitchElseTo]}>
<Form.Item
label={'ELSE'}
name={[SwitchElseTo]}
className={styles.elseCase}
>
<Select
allowClear
options={buildCategorizeToOptions(getSelectedConditionTos())}

View File

@ -69,6 +69,7 @@ import useGraphStore, { RFState } from './store';
import {
buildDslComponentsByGraph,
generateSwitchHandleText,
getNodeDragHandle,
receiveMessageError,
replaceIdWithText,
} from './utils';
@ -250,6 +251,7 @@ export const useHandleDrop = () => {
},
sourcePosition: Position.Right,
targetPosition: Position.Left,
dragHandle: getNodeDragHandle(type),
};
addNode(newNode);
@ -448,11 +450,16 @@ export const useValidateConnection = () => {
return isValidConnection;
};
export const useHandleNodeNameChange = (node?: Node) => {
export const useHandleNodeNameChange = ({
id,
data,
}: {
id?: string;
data: any;
}) => {
const [name, setName] = useState<string>('');
const { updateNodeName, nodes } = useGraphStore((state) => state);
const previousName = node?.data.name;
const id = node?.id;
const previousName = data?.name;
const handleNameBlur = useCallback(() => {
const existsSameName = nodes.some((x) => x.data.name === name);
@ -639,6 +646,7 @@ const ExcludedNodes = [
Operator.Relevant,
Operator.Begin,
Operator.Answer,
Operator.Note,
];
export const useBuildComponentIdSelectOptions = (nodeId?: string) => {
@ -655,3 +663,15 @@ export const useBuildComponentIdSelectOptions = (nodeId?: string) => {
return options;
};
export const useGetComponentLabelByValue = (nodeId: string) => {
const options = useBuildComponentIdSelectOptions(nodeId);
const getLabel = useCallback(
(val?: string) => {
return options.find((x) => x.value === val)?.label;
},
[options],
);
return getLabel;
};

View File

@ -64,20 +64,20 @@ export interface IRelevantForm extends IGenerateForm {
no: string;
}
interface Condition {
items: Item[];
export interface ISwitchCondition {
items: ISwitchItem[];
logical_operator: string;
to: string;
}
interface Item {
export interface ISwitchItem {
cpn_id: string;
operator: string;
value: string;
}
export interface ISwitchForm {
conditions: Condition[];
conditions: ISwitchCondition[];
end_cpn_id: string;
no: string;
}

View File

@ -7,12 +7,17 @@ interface IProps {
name: Operator;
fontSize?: number;
width?: number;
color?: string;
}
const OperatorIcon = ({ name, fontSize, width }: IProps) => {
const OperatorIcon = ({ name, fontSize, width, color }: IProps) => {
const Icon = operatorIconMap[name] || React.Fragment;
return (
<Icon className={styles.icon} style={{ fontSize }} width={width}></Icon>
<Icon
className={styles.icon}
style={{ fontSize, color }}
width={width}
></Icon>
);
};

View File

@ -23,7 +23,7 @@ import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { Operator, SwitchElseTo } from './constant';
import { NodeData } from './interface';
import { getOperatorIndex, isEdgeEqual } from './utils';
import { getNodeDragHandle, getOperatorIndex, isEdgeEqual } from './utils';
export type RFState = {
nodes: Node<NodeData>[];
@ -241,6 +241,7 @@ const useGraphStore = create<RFState>()(
dragging: false,
id: `${node?.data?.label}:${humanId()}`,
position,
dragHandle: getNodeDragHandle(node?.data?.label),
});
},
deleteEdge: () => {

View File

@ -236,3 +236,7 @@ export const getOtherFieldValues = (
export const generateSwitchHandleText = (idx: number) => {
return `Case ${idx + 1}`;
};
export const getNodeDragHandle = (nodeType?: string) => {
return nodeType === Operator.Note ? '.note-drag-handle' : undefined;
};

View File

@ -1,48 +1,3 @@
// Please lowercase the file name
export const IconMap = {
'Tongyi-Qianwen': 'tongyi',
Moonshot: 'moonshot',
OpenAI: 'openai',
'ZHIPU-AI': 'zhipu',
: 'wenxin',
Ollama: 'ollama',
Xinference: 'xinference',
DeepSeek: 'deepseek',
VolcEngine: 'volc_engine',
BaiChuan: 'baichuan',
Jina: 'jina',
MiniMax: 'chat-minimax',
Mistral: 'mistral',
'Azure-OpenAI': 'azure',
Bedrock: 'bedrock',
Gemini: 'gemini',
Groq: 'groq-next',
OpenRouter: 'open-router',
LocalAI: 'local-ai',
StepFun: 'stepfun',
NVIDIA: 'nvidia',
'LM-Studio': 'lm-studio',
'OpenAI-API-Compatible': 'openai-api',
cohere: 'cohere',
LeptonAI: 'lepton-ai',
TogetherAI: 'together-ai',
PerfXCloud: 'perfx-cloud',
Upstage: 'upstage',
'novita.ai': 'novita-ai',
SILICONFLOW: 'siliconflow',
'01.AI': 'yi',
Replicate: 'replicate',
'Tencent Hunyuan': 'hunyuan',
'XunFei Spark': 'spark',
BaiduYiyan: 'yiyan',
'Fish Audio': 'fish-audio',
'Tencent Cloud': 'tencent-cloud',
Anthropic: 'anthropic',
'Voyage AI': 'voyage',
'Google Cloud': 'google-cloud',
HuggingFace: 'huggingface',
};
export const BedrockRegionList = [
'us-east-1',
'us-west-2',

View File

@ -1,14 +1,9 @@
import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg';
import SvgIcon from '@/components/svg-icon';
import { LlmIcon } from '@/components/svg-icon';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { LlmItem, useSelectLlmList } from '@/hooks/llm-hooks';
import { CloseCircleOutlined, SettingOutlined } from '@ant-design/icons';
import {
CloseCircleOutlined,
SettingOutlined,
UserOutlined,
} from '@ant-design/icons';
import {
Avatar,
Button,
Card,
Col,
@ -31,7 +26,6 @@ import TencentCloudModal from './Tencent-modal';
import ApiKeyModal from './api-key-modal';
import AzureOpenAIModal from './azure-openai-modal';
import BedrockModal from './bedrock-modal';
import { IconMap } from './constant';
import FishAudioModal from './fish-audio-modal';
import GoogleModal from './google-modal';
import {
@ -58,16 +52,6 @@ import SystemModelSettingModal from './system-model-setting-modal';
import VolcEngineModal from './volcengine-modal';
import YiyanModal from './yiyan-modal';
const LlmIcon = ({ name }: { name: string }) => {
const icon = IconMap[name as keyof typeof IconMap];
return icon ? (
<SvgIcon name={`llm/${icon}`} width={48} height={48}></SvgIcon>
) : (
<Avatar shape="square" size="large" icon={<UserOutlined />} />
);
};
const { Text } = Typography;
interface IModelCardProps {
item: LlmItem;