Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
11baff67
Unverified
Commit
11baff67
authored
Jul 07, 2023
by
Joel
Committed by
GitHub
Jul 07, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: text generation application support run batch (#529)
parent
cde1797c
Changes
24
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
1009 additions
and
172 deletions
+1009
-172
index.tsx
web/app/components/app/text-generate/item/index.tsx
+21
-1
csv.svg
web/app/components/base/icons/assets/public/files/csv.svg
+24
-0
download-02.svg
...ts/base/icons/assets/vender/solid/general/download-02.svg
+3
-0
Csv.json
web/app/components/base/icons/src/public/files/Csv.json
+181
-0
Csv.tsx
web/app/components/base/icons/src/public/files/Csv.tsx
+14
-0
index.ts
web/app/components/base/icons/src/public/files/index.ts
+1
-0
Download02.json
...nents/base/icons/src/vender/solid/general/Download02.json
+29
-0
Download02.tsx
...onents/base/icons/src/vender/solid/general/Download02.tsx
+14
-0
index.ts
...p/components/base/icons/src/vender/solid/general/index.ts
+1
-0
index.tsx
web/app/components/base/tab-header/index.tsx
+29
-19
index.tsx
web/app/components/share/text-generation/index.tsx
+221
-120
content.tsx
web/app/components/share/text-generation/result/content.tsx
+34
-0
index.tsx
web/app/components/share/text-generation/result/index.tsx
+195
-24
index.tsx
...ts/share/text-generation/run-batch/csv-download/index.tsx
+70
-0
index.tsx
...ents/share/text-generation/run-batch/csv-reader/index.tsx
+70
-0
style.module.css
...are/text-generation/run-batch/csv-reader/style.module.css
+11
-0
index.tsx
web/app/components/share/text-generation/run-batch/index.tsx
+53
-0
index.tsx
web/app/components/share/text-generation/run-once/index.tsx
+4
-4
app-debug.en.ts
web/i18n/lang/app-debug.en.ts
+2
-0
app-debug.zh.ts
web/i18n/lang/app-debug.zh.ts
+1
-0
share-app.en.ts
web/i18n/lang/share-app.en.ts
+15
-2
share-app.zh.ts
web/i18n/lang/share-app.zh.ts
+14
-1
package.json
web/package.json
+1
-0
base.ts
web/service/base.ts
+1
-1
No files found.
web/app/components/app/text-generate/item/index.tsx
View file @
11baff67
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useState
}
from
'react'
import
React
,
{
use
Effect
,
use
State
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
cn
from
'classnames'
import
copy
from
'copy-to-clipboard'
import
{
HandThumbDownIcon
,
HandThumbUpIcon
}
from
'@heroicons/react/24/outline'
import
{
useBoolean
}
from
'ahooks'
import
{
HashtagIcon
}
from
'@heroicons/react/24/solid'
import
{
Markdown
}
from
'@/app/components/base/markdown'
import
Loading
from
'@/app/components/base/loading'
import
Toast
from
'@/app/components/base/toast'
...
...
@@ -27,6 +28,8 @@ export type IGenerationItemProps = {
isMobile
?:
boolean
isInstalledApp
:
boolean
installedAppId
?:
string
taskId
?:
string
controlClearMoreLikeThis
?:
number
}
export
const
SimpleBtn
=
({
className
,
onClick
,
children
}:
{
...
...
@@ -81,6 +84,8 @@ const GenerationItem: FC<IGenerationItemProps> = ({
isMobile
,
isInstalledApp
,
installedAppId
,
taskId
,
controlClearMoreLikeThis
,
})
=>
{
const
{
t
}
=
useTranslation
()
const
isTop
=
depth
===
1
...
...
@@ -112,6 +117,7 @@ const GenerationItem: FC<IGenerationItemProps> = ({
isMobile
,
isInstalledApp
,
installedAppId
,
controlClearMoreLikeThis
,
}
const
handleMoreLikeThis
=
async
()
=>
{
...
...
@@ -138,6 +144,14 @@ const GenerationItem: FC<IGenerationItemProps> = ({
return
res
})()
useEffect
(()
=>
{
if
(
controlClearMoreLikeThis
)
{
setChildMessageId
(
null
)
setCompletionRes
(
''
)
}
},
[
controlClearMoreLikeThis
])
return
(
<
div
className=
{
cn
(
className
,
isTop
?
'rounded-xl border border-gray-200 bg-white'
:
'rounded-br-xl !mt-0'
)
}
style=
{
isTop
...
...
@@ -155,6 +169,12 @@ const GenerationItem: FC<IGenerationItemProps> = ({
className=
{
cn
(
!
isTop
&&
'rounded-br-xl border-l-2 border-primary-400'
,
'p-4'
)
}
style=
{
mainStyle
}
>
{
(
isTop
&&
taskId
)
&&
(
<
div
className=
'mb-2 text-gray-500 border border-gray-200 box-border flex items-center rounded-md italic text-[11px] pl-1 pr-1.5 font-medium w-fit group-hover:opacity-100'
>
<
HashtagIcon
className=
'w-3 h-3 text-gray-400 fill-current mr-1 stroke-current stroke-1'
/>
{
taskId
}
</
div
>)
}
<
Markdown
content=
{
content
}
/>
{
messageId
&&
(
<
div
className=
'flex items-center justify-between mt-3'
>
...
...
web/app/components/base/icons/assets/public/files/csv.svg
0 → 100644
View file @
11baff67
<svg
width=
"32"
height=
"34"
viewBox=
"0 0 32 34"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<g
id=
"File Icons/csv"
>
<g
id=
"sharp"
filter=
"url(#filter0_d_6816_769)"
>
<path
d=
"M4 7.73398C4 5.49377 4 4.37367 4.43597 3.51802C4.81947 2.76537 5.43139 2.15345 6.18404 1.76996C7.03969 1.33398 8.15979 1.33398 10.4 1.33398H18.6667L28 10.6673V24.2673C28 26.5075 28 27.6276 27.564 28.4833C27.1805 29.2359 26.5686 29.8478 25.816 30.2313C24.9603 30.6673 23.8402 30.6673 21.6 30.6673H10.4C8.15979 30.6673 7.03969 30.6673 6.18404 30.2313C5.43139 29.8478 4.81947 29.2359 4.43597 28.4833C4 27.6276 4 26.5075 4 24.2673V7.73398Z"
fill=
"#169951"
/>
</g>
<g
id=
"CSV"
opacity=
"0.96"
>
<path
d=
"M13.0846 21.8908C12.8419 23.3562 11.8246 24.0562 10.5646 24.0562C9.78992 24.0562 9.20192 23.7948 8.71659 23.3095C8.01659 22.6095 8.04459 21.6762 8.04459 20.6775C8.04459 19.6788 8.01659 18.7455 8.71659 18.0455C9.20192 17.5602 9.78992 17.2988 10.5646 17.2988C11.8246 17.2988 12.8419 17.9988 13.0846 19.4642H11.4233C11.3206 19.0908 11.1153 18.7548 10.5739 18.7548C10.2753 18.7548 10.0513 18.8762 9.92992 19.0348C9.78059 19.2308 9.67792 19.4642 9.67792 20.6775C9.67792 21.8908 9.78059 22.1242 9.92992 22.3202C10.0513 22.4788 10.2753 22.6002 10.5739 22.6002C11.1153 22.6002 11.3206 22.2642 11.4233 21.8908H13.0846Z"
fill=
"white"
/>
<path
d=
"M18.4081 21.9655C18.4081 23.3188 17.2414 24.0562 15.8414 24.0562C14.8241 24.0562 13.9934 23.8695 13.3214 23.1788L14.3668 22.1335C14.7121 22.4788 15.3188 22.6002 15.8508 22.6002C16.4948 22.6002 16.8028 22.3855 16.8028 22.0028C16.8028 21.8442 16.7654 21.7135 16.6721 21.6108C16.5881 21.5268 16.4481 21.4615 16.2334 21.4335L15.4308 21.3215C14.8428 21.2375 14.3948 21.0415 14.0961 20.7335C13.7881 20.4162 13.6388 19.9682 13.6388 19.3988C13.6388 18.1855 14.5534 17.2988 16.0654 17.2988C17.0174 17.2988 17.7361 17.5228 18.3054 18.0922L17.2788 19.1188C16.8588 18.6988 16.3081 18.7268 16.0188 18.7268C15.4494 18.7268 15.2161 19.0535 15.2161 19.3428C15.2161 19.4268 15.2441 19.5482 15.3468 19.6508C15.4308 19.7348 15.5708 19.8188 15.8041 19.8468L16.6068 19.9588C17.2041 20.0428 17.6334 20.2295 17.9134 20.5095C18.2681 20.8548 18.4081 21.3495 18.4081 21.9655Z"
fill=
"white"
/>
<path
d=
"M24.4166 17.3548L22.214 24.0002H21.0006L18.8073 17.3548H20.4966L21.6166 21.0695L22.718 17.3548H24.4166Z"
fill=
"white"
/>
</g>
<path
id=
"bevel"
opacity=
"0.5"
d=
"M18.6667 1.33398L28.0001 10.6673H21.3334C19.8607 10.6673 18.6667 9.47341 18.6667 8.00065V1.33398Z"
fill=
"white"
/>
</g>
<defs>
<filter
id=
"filter0_d_6816_769"
x=
"2"
y=
"0.333984"
width=
"28"
height=
"33.334"
filterUnits=
"userSpaceOnUse"
color-interpolation-filters=
"sRGB"
>
<feFlood
flood-opacity=
"0"
result=
"BackgroundImageFix"
/>
<feColorMatrix
in=
"SourceAlpha"
type=
"matrix"
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result=
"hardAlpha"
/>
<feOffset
dy=
"1"
/>
<feGaussianBlur
stdDeviation=
"1"
/>
<feColorMatrix
type=
"matrix"
values=
"0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"
/>
<feBlend
mode=
"normal"
in2=
"BackgroundImageFix"
result=
"effect1_dropShadow_6816_769"
/>
<feBlend
mode=
"normal"
in=
"SourceGraphic"
in2=
"effect1_dropShadow_6816_769"
result=
"shape"
/>
</filter>
</defs>
</svg>
web/app/components/base/icons/assets/vender/solid/general/download-02.svg
0 → 100644
View file @
11baff67
<svg
width=
"24"
height=
"24"
viewBox=
"0 0 24 24"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M21 21H3M18 11L12 17M12 17L6 11M12 17V3"
stroke=
"black"
stroke-width=
"2"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/base/icons/src/public/files/Csv.json
0 → 100644
View file @
11baff67
{
"icon"
:
{
"type"
:
"element"
,
"isRootNode"
:
true
,
"name"
:
"svg"
,
"attributes"
:
{
"width"
:
"32"
,
"height"
:
"34"
,
"viewBox"
:
"0 0 32 34"
,
"fill"
:
"none"
,
"xmlns"
:
"http://www.w3.org/2000/svg"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"g"
,
"attributes"
:
{
"id"
:
"File Icons/csv"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"g"
,
"attributes"
:
{
"id"
:
"sharp"
,
"filter"
:
"url(#filter0_d_6816_769)"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M4 7.73398C4 5.49377 4 4.37367 4.43597 3.51802C4.81947 2.76537 5.43139 2.15345 6.18404 1.76996C7.03969 1.33398 8.15979 1.33398 10.4 1.33398H18.6667L28 10.6673V24.2673C28 26.5075 28 27.6276 27.564 28.4833C27.1805 29.2359 26.5686 29.8478 25.816 30.2313C24.9603 30.6673 23.8402 30.6673 21.6 30.6673H10.4C8.15979 30.6673 7.03969 30.6673 6.18404 30.2313C5.43139 29.8478 4.81947 29.2359 4.43597 28.4833C4 27.6276 4 26.5075 4 24.2673V7.73398Z"
,
"fill"
:
"#169951"
},
"children"
:
[]
}
]
},
{
"type"
:
"element"
,
"name"
:
"g"
,
"attributes"
:
{
"id"
:
"CSV"
,
"opacity"
:
"0.96"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M13.0846 21.8908C12.8419 23.3562 11.8246 24.0562 10.5646 24.0562C9.78992 24.0562 9.20192 23.7948 8.71659 23.3095C8.01659 22.6095 8.04459 21.6762 8.04459 20.6775C8.04459 19.6788 8.01659 18.7455 8.71659 18.0455C9.20192 17.5602 9.78992 17.2988 10.5646 17.2988C11.8246 17.2988 12.8419 17.9988 13.0846 19.4642H11.4233C11.3206 19.0908 11.1153 18.7548 10.5739 18.7548C10.2753 18.7548 10.0513 18.8762 9.92992 19.0348C9.78059 19.2308 9.67792 19.4642 9.67792 20.6775C9.67792 21.8908 9.78059 22.1242 9.92992 22.3202C10.0513 22.4788 10.2753 22.6002 10.5739 22.6002C11.1153 22.6002 11.3206 22.2642 11.4233 21.8908H13.0846Z"
,
"fill"
:
"white"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M18.4081 21.9655C18.4081 23.3188 17.2414 24.0562 15.8414 24.0562C14.8241 24.0562 13.9934 23.8695 13.3214 23.1788L14.3668 22.1335C14.7121 22.4788 15.3188 22.6002 15.8508 22.6002C16.4948 22.6002 16.8028 22.3855 16.8028 22.0028C16.8028 21.8442 16.7654 21.7135 16.6721 21.6108C16.5881 21.5268 16.4481 21.4615 16.2334 21.4335L15.4308 21.3215C14.8428 21.2375 14.3948 21.0415 14.0961 20.7335C13.7881 20.4162 13.6388 19.9682 13.6388 19.3988C13.6388 18.1855 14.5534 17.2988 16.0654 17.2988C17.0174 17.2988 17.7361 17.5228 18.3054 18.0922L17.2788 19.1188C16.8588 18.6988 16.3081 18.7268 16.0188 18.7268C15.4494 18.7268 15.2161 19.0535 15.2161 19.3428C15.2161 19.4268 15.2441 19.5482 15.3468 19.6508C15.4308 19.7348 15.5708 19.8188 15.8041 19.8468L16.6068 19.9588C17.2041 20.0428 17.6334 20.2295 17.9134 20.5095C18.2681 20.8548 18.4081 21.3495 18.4081 21.9655Z"
,
"fill"
:
"white"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M24.4166 17.3548L22.214 24.0002H21.0006L18.8073 17.3548H20.4966L21.6166 21.0695L22.718 17.3548H24.4166Z"
,
"fill"
:
"white"
},
"children"
:
[]
}
]
},
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"id"
:
"bevel"
,
"opacity"
:
"0.5"
,
"d"
:
"M18.6667 1.33398L28.0001 10.6673H21.3334C19.8607 10.6673 18.6667 9.47341 18.6667 8.00065V1.33398Z"
,
"fill"
:
"white"
},
"children"
:
[]
}
]
},
{
"type"
:
"element"
,
"name"
:
"defs"
,
"attributes"
:
{},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"filter"
,
"attributes"
:
{
"id"
:
"filter0_d_6816_769"
,
"x"
:
"2"
,
"y"
:
"0.333984"
,
"width"
:
"28"
,
"height"
:
"33.334"
,
"filterUnits"
:
"userSpaceOnUse"
,
"color-interpolation-filters"
:
"sRGB"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"feFlood"
,
"attributes"
:
{
"flood-opacity"
:
"0"
,
"result"
:
"BackgroundImageFix"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"feColorMatrix"
,
"attributes"
:
{
"in"
:
"SourceAlpha"
,
"type"
:
"matrix"
,
"values"
:
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
,
"result"
:
"hardAlpha"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"feOffset"
,
"attributes"
:
{
"dy"
:
"1"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"feGaussianBlur"
,
"attributes"
:
{
"stdDeviation"
:
"1"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"feColorMatrix"
,
"attributes"
:
{
"type"
:
"matrix"
,
"values"
:
"0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"feBlend"
,
"attributes"
:
{
"mode"
:
"normal"
,
"in2"
:
"BackgroundImageFix"
,
"result"
:
"effect1_dropShadow_6816_769"
},
"children"
:
[]
},
{
"type"
:
"element"
,
"name"
:
"feBlend"
,
"attributes"
:
{
"mode"
:
"normal"
,
"in"
:
"SourceGraphic"
,
"in2"
:
"effect1_dropShadow_6816_769"
,
"result"
:
"shape"
},
"children"
:
[]
}
]
}
]
}
]
},
"name"
:
"Csv"
}
\ No newline at end of file
web/app/components/base/icons/src/public/files/Csv.tsx
0 → 100644
View file @
11baff67
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import
*
as
React
from
'react'
import
data
from
'./Csv.json'
import
IconBase
from
'@/app/components/base/icons/IconBase'
import
type
{
IconBaseProps
,
IconData
}
from
'@/app/components/base/icons/IconBase'
const
Icon
=
React
.
forwardRef
<
React
.
MutableRefObject
<
SVGElement
>
,
Omit
<
IconBaseProps
,
'data'
>>
((
props
,
ref
,
)
=>
<
IconBase
{
...
props
}
ref=
{
ref
}
data=
{
data
as
IconData
}
/>)
export
default
Icon
web/app/components/base/icons/src/public/files/index.ts
View file @
11baff67
export
{
default
as
Csv
}
from
'./Csv'
export
{
default
as
Md
}
from
'./Md'
web/app/components/base/icons/src/vender/solid/general/Download02.json
0 → 100644
View file @
11baff67
{
"icon"
:
{
"type"
:
"element"
,
"isRootNode"
:
true
,
"name"
:
"svg"
,
"attributes"
:
{
"width"
:
"24"
,
"height"
:
"24"
,
"viewBox"
:
"0 0 24 24"
,
"fill"
:
"none"
,
"xmlns"
:
"http://www.w3.org/2000/svg"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M21 21H3M18 11L12 17M12 17L6 11M12 17V3"
,
"stroke"
:
"currentColor"
,
"stroke-width"
:
"2"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
},
"children"
:
[]
}
]
},
"name"
:
"Download02"
}
\ No newline at end of file
web/app/components/base/icons/src/vender/solid/general/Download02.tsx
0 → 100644
View file @
11baff67
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import
*
as
React
from
'react'
import
data
from
'./Download02.json'
import
IconBase
from
'@/app/components/base/icons/IconBase'
import
type
{
IconBaseProps
,
IconData
}
from
'@/app/components/base/icons/IconBase'
const
Icon
=
React
.
forwardRef
<
React
.
MutableRefObject
<
SVGElement
>
,
Omit
<
IconBaseProps
,
'data'
>>
((
props
,
ref
,
)
=>
<
IconBase
{
...
props
}
ref=
{
ref
}
data=
{
data
as
IconData
}
/>)
export
default
Icon
web/app/components/base/icons/src/vender/solid/general/index.ts
0 → 100644
View file @
11baff67
export
{
default
as
Download02
}
from
'./Download02'
web/app/components/base/tab-header/index.tsx
View file @
11baff67
'use client'
import
React
,
{
FC
}
from
'react'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
cn
from
'classnames'
import
s
from
'./style.module.css'
export
interface
ITabHeaderProps
{
items
:
{
type
Item
=
{
id
:
string
name
:
string
isRight
?:
boolean
extra
?:
React
.
ReactNode
}[]
}
export
type
ITabHeaderProps
=
{
items
:
Item
[]
value
:
string
onChange
:
(
value
:
string
)
=>
void
}
...
...
@@ -17,20 +21,26 @@ export interface ITabHeaderProps {
const
TabHeader
:
FC
<
ITabHeaderProps
>
=
({
items
,
value
,
onChange
onChange
,
})
=>
{
return
(
<
div
className=
'flex space-x-4 border-b border-gray-200 '
>
{
items
.
map
(({
id
,
name
,
extra
})
=>
(
const
renderItem
=
({
id
,
name
,
extra
}:
Item
)
=>
(
<
div
key=
{
id
}
className=
{
cn
(
id
===
value
?
`${s.itemActive} text-gray-900`
:
'text-gray-500'
,
'relative flex items-center pb-1.5 leading-6 cursor-pointer'
)
}
onClick=
{
()
=>
onChange
(
id
)
}
>
<
div
className=
'text-base font-semibold'
>
{
name
}
</
div
>
{
extra
?
extra
:
''
}
{
extra
||
''
}
</
div
>
)
return
(
<
div
className=
'flex justify-between border-b border-gray-200 '
>
<
div
className=
'flex space-x-4'
>
{
items
.
filter
(
item
=>
!
item
.
isRight
).
map
(
renderItem
)
}
</
div
>
<
div
className=
'flex space-x-4'
>
{
items
.
filter
(
item
=>
item
.
isRight
).
map
(
renderItem
)
}
</
div
>
))
}
</
div
>
)
}
...
...
web/app/components/share/text-generation/index.tsx
View file @
11baff67
This diff is collapsed.
Click to expand it.
web/app/components/share/text-generation/result/content.tsx
0 → 100644
View file @
11baff67
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
Header
from
'./header'
import
type
{
Feedbacktype
}
from
'@/app/components/app/chat'
import
{
format
}
from
'@/service/base'
export
type
IResultProps
=
{
content
:
string
showFeedback
:
boolean
feedback
:
Feedbacktype
onFeedback
:
(
feedback
:
Feedbacktype
)
=>
void
}
const
Result
:
FC
<
IResultProps
>
=
({
content
,
showFeedback
,
feedback
,
onFeedback
,
})
=>
{
return
(
<
div
className=
'basis-3/4 h-max'
>
<
Header
result=
{
content
}
showFeedback=
{
showFeedback
}
feedback=
{
feedback
}
onFeedback=
{
onFeedback
}
/>
<
div
className=
'mt-4 w-full flex text-sm leading-5 overflow-scroll font-normal text-gray-900'
style=
{
{
maxHeight
:
'70vh'
,
}
}
dangerouslySetInnerHTML=
{
{
__html
:
format
(
content
),
}
}
></
div
>
</
div
>
)
}
export
default
React
.
memo
(
Result
)
web/app/components/share/text-generation/result/index.tsx
View file @
11baff67
'use client'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
Header
from
'./header'
import
{
Feedbacktype
}
from
'@/app/components/app/chat'
import
{
format
}
from
'@/service/base'
import
React
,
{
useEffect
,
useState
}
from
'react'
import
{
useBoolean
}
from
'ahooks'
import
{
t
}
from
'i18next'
import
cn
from
'classnames'
import
TextGenerationRes
from
'@/app/components/app/text-generate/item'
import
NoData
from
'@/app/components/share/text-generation/no-data'
import
Toast
from
'@/app/components/base/toast'
import
{
sendCompletionMessage
,
updateFeedback
}
from
'@/service/share'
import
type
{
Feedbacktype
}
from
'@/app/components/app/chat'
import
Loading
from
'@/app/components/base/loading'
import
type
{
PromptConfig
}
from
'@/models/debug'
import
type
{
InstalledApp
}
from
'@/models/explore'
export
type
IResultProps
=
{
content
:
string
showFeedback
:
boolean
feedback
:
Feedbacktype
onFeedback
:
(
feedback
:
Feedbacktype
)
=>
void
isCallBatchAPI
:
boolean
isPC
:
boolean
isMobile
:
boolean
isInstalledApp
:
boolean
installedAppInfo
?:
InstalledApp
promptConfig
:
PromptConfig
|
null
moreLikeThisEnabled
:
boolean
inputs
:
Record
<
string
,
any
>
query
:
string
controlSend
?:
number
controlStopResponding
?:
number
onShowRes
:
()
=>
void
handleSaveMessage
:
(
messageId
:
string
)
=>
void
taskId
?:
number
onCompleted
:
(
taskId
?:
number
,
success
?:
boolean
)
=>
void
}
const
Result
:
FC
<
IResultProps
>
=
({
content
,
showFeedback
,
feedback
,
onFeedback
isCallBatchAPI
,
isPC
,
isMobile
,
isInstalledApp
,
installedAppInfo
,
promptConfig
,
moreLikeThisEnabled
,
inputs
,
query
,
controlSend
,
controlStopResponding
,
onShowRes
,
handleSaveMessage
,
taskId
,
onCompleted
,
})
=>
{
const
[
isResponsing
,
{
setTrue
:
setResponsingTrue
,
setFalse
:
setResponsingFalse
}]
=
useBoolean
(
false
)
useEffect
(()
=>
{
if
(
controlStopResponding
)
setResponsingFalse
()
},
[
controlStopResponding
])
const
[
completionRes
,
setCompletionRes
]
=
useState
(
''
)
const
{
notify
}
=
Toast
const
isNoData
=
!
completionRes
const
[
messageId
,
setMessageId
]
=
useState
<
string
|
null
>
(
null
)
const
[
feedback
,
setFeedback
]
=
useState
<
Feedbacktype
>
({
rating
:
null
,
})
const
handleFeedback
=
async
(
feedback
:
Feedbacktype
)
=>
{
await
updateFeedback
({
url
:
`/messages/
${
messageId
}
/feedbacks`
,
body
:
{
rating
:
feedback
.
rating
}
},
isInstalledApp
,
installedAppInfo
?.
id
)
setFeedback
(
feedback
)
}
const
logError
=
(
message
:
string
)
=>
{
notify
({
type
:
'error'
,
message
})
}
const
checkCanSend
=
()
=>
{
// batch will check outer
if
(
isCallBatchAPI
)
return
true
const
prompt_variables
=
promptConfig
?.
prompt_variables
if
(
!
prompt_variables
||
prompt_variables
?.
length
===
0
)
return
true
let
hasEmptyInput
=
false
const
requiredVars
=
prompt_variables
?.
filter
(({
key
,
name
,
required
})
=>
{
const
res
=
(
!
key
||
!
key
.
trim
())
||
(
!
name
||
!
name
.
trim
())
||
(
required
||
required
===
undefined
||
required
===
null
)
return
res
})
||
[]
// compatible with old version
requiredVars
.
forEach
(({
key
})
=>
{
if
(
hasEmptyInput
)
return
if
(
!
inputs
[
key
])
hasEmptyInput
=
true
})
if
(
hasEmptyInput
)
{
logError
(
t
(
'appDebug.errorMessage.valueOfVarRequired'
))
return
false
}
return
!
hasEmptyInput
}
const
handleSend
=
async
()
=>
{
if
(
isResponsing
)
{
notify
({
type
:
'info'
,
message
:
t
(
'appDebug.errorMessage.waitForResponse'
)
})
return
false
}
if
(
!
checkCanSend
())
return
if
(
!
query
)
{
logError
(
t
(
'appDebug.errorMessage.queryRequired'
))
return
false
}
const
data
=
{
inputs
,
query
,
}
setMessageId
(
null
)
setFeedback
({
rating
:
null
,
})
setCompletionRes
(
''
)
const
res
:
string
[]
=
[]
let
tempMessageId
=
''
if
(
!
isPC
)
onShowRes
()
setResponsingTrue
()
sendCompletionMessage
(
data
,
{
onData
:
(
data
:
string
,
_isFirstMessage
:
boolean
,
{
messageId
}:
any
)
=>
{
tempMessageId
=
messageId
res
.
push
(
data
)
setCompletionRes
(
res
.
join
(
''
))
},
onCompleted
:
()
=>
{
setResponsingFalse
()
setMessageId
(
tempMessageId
)
onCompleted
(
taskId
,
true
)
},
onError
()
{
setResponsingFalse
()
onCompleted
(
taskId
,
false
)
},
},
isInstalledApp
,
installedAppInfo
?.
id
)
}
const
[
controlClearMoreLikeThis
,
setControlClearMoreLikeThis
]
=
useState
(
0
)
useEffect
(()
=>
{
if
(
controlSend
)
{
handleSend
()
setControlClearMoreLikeThis
(
Date
.
now
())
}
},
[
controlSend
])
const
renderTextGenerationRes
=
()
=>
(
<
TextGenerationRes
className=
'mt-3'
content=
{
completionRes
}
messageId=
{
messageId
}
isInWebApp
moreLikeThis=
{
moreLikeThisEnabled
}
onFeedback=
{
handleFeedback
}
feedback=
{
feedback
}
onSave=
{
handleSaveMessage
}
isMobile=
{
isMobile
}
isInstalledApp=
{
isInstalledApp
}
installedAppId=
{
installedAppInfo
?.
id
}
isLoading=
{
isCallBatchAPI
?
(
!
completionRes
&&
isResponsing
)
:
false
}
taskId=
{
isCallBatchAPI
?
((
taskId
as
number
)
<
10
?
`0${taskId}`
:
`${taskId}`
)
:
undefined
}
controlClearMoreLikeThis=
{
controlClearMoreLikeThis
}
/>
)
return
(
<
div
className=
'basis-3/4 h-max'
>
<
Header
result=
{
content
}
showFeedback=
{
showFeedback
}
feedback=
{
feedback
}
onFeedback=
{
onFeedback
}
/>
<
div
className=
'mt-4 w-full flex text-sm leading-5 overflow-scroll font-normal text-gray-900'
style=
{
{
maxHeight
:
'70vh'
}
}
dangerouslySetInnerHTML=
{
{
__html
:
format
(
content
)
}
}
></
div
>
<
div
className=
{
cn
(
isNoData
&&
!
isCallBatchAPI
&&
'h-full'
)
}
>
{
!
isCallBatchAPI
&&
(
(
isResponsing
&&
!
completionRes
)
?
(
<
div
className=
'flex h-full w-full justify-center items-center'
>
<
Loading
type=
'area'
/>
</
div
>)
:
(
<>
{
isNoData
?
<
NoData
/>
:
renderTextGenerationRes
()
}
</>
)
)
}
{
isCallBatchAPI
&&
(
<
div
className=
'mt-2'
>
{
renderTextGenerationRes
()
}
</
div
>
)
}
</
div
>
)
}
...
...
web/app/components/share/text-generation/run-batch/csv-download/index.tsx
0 → 100644
View file @
11baff67
'use client'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
{
useCSVDownloader
,
}
from
'react-papaparse'
import
{
useTranslation
}
from
'react-i18next'
import
{
Download02
as
DownloadIcon
}
from
'@/app/components/base/icons/src/vender/solid/general'
export
type
ICSVDownloadProps
=
{
vars
:
{
name
:
string
}[]
}
const
CSVDownload
:
FC
<
ICSVDownloadProps
>
=
({
vars
,
})
=>
{
const
{
t
}
=
useTranslation
()
const
{
CSVDownloader
,
Type
}
=
useCSVDownloader
()
const
addQueryContentVars
=
[...
vars
,
{
name
:
t
(
'share.generation.queryTitle'
)
}]
const
template
=
(()
=>
{
const
res
:
Record
<
string
,
string
>
=
{}
addQueryContentVars
.
forEach
((
item
)
=>
{
res
[
item
.
name
]
=
''
})
return
res
})()
return
(
<
div
className=
'mt-6'
>
<
div
className=
'text-sm text-gray-900 font-medium'
>
{
t
(
'share.generation.csvStructureTitle'
)
}
</
div
>
<
div
className=
'mt-2 max-h-[500px] overflow-auto'
>
<
table
className=
'w-full border-separate border-spacing-0 border border-gray-200 rounded-lg text-xs'
>
<
thead
className=
'text-gray-500'
>
<
tr
>
{
addQueryContentVars
.
map
((
item
,
i
)
=>
(
<
td
key=
{
i
}
className=
'h-9 pl-4 border-b border-gray-200'
>
{
item
.
name
}
</
td
>
))
}
</
tr
>
</
thead
>
<
tbody
className=
'text-gray-300'
>
<
tr
>
{
addQueryContentVars
.
map
((
item
,
i
)
=>
(
<
td
key=
{
i
}
className=
'h-9 pl-4'
>
{
item
.
name
}
{
t
(
'share.generation.field'
)
}
</
td
>
))
}
</
tr
>
</
tbody
>
</
table
>
</
div
>
<
CSVDownloader
className=
"block mt-2 cursor-pointer"
type=
{
Type
.
Link
}
filename=
{
'template'
}
bom=
{
true
}
config=
{
{
// delimiter: ';',
}
}
data=
{
[
template
,
]
}
>
<
div
className=
'flex items-center h-[18px] space-x-1 text-[#155EEF] text-xs font-medium'
>
<
DownloadIcon
className=
'w-3 h-3'
/>
<
span
>
{
t
(
'share.generation.downloadTemplate'
)
}
</
span
>
</
div
>
</
CSVDownloader
>
</
div
>
)
}
export
default
React
.
memo
(
CSVDownload
)
web/app/components/share/text-generation/run-batch/csv-reader/index.tsx
0 → 100644
View file @
11baff67
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useState
}
from
'react'
import
{
useCSVReader
,
}
from
'react-papaparse'
import
cn
from
'classnames'
import
{
useTranslation
}
from
'react-i18next'
import
s
from
'./style.module.css'
import
{
Csv
as
CSVIcon
}
from
'@/app/components/base/icons/src/public/files'
export
type
Props
=
{
onParsed
:
(
data
:
string
[][])
=>
void
}
const
CSVReader
:
FC
<
Props
>
=
({
onParsed
,
})
=>
{
const
{
t
}
=
useTranslation
()
const
{
CSVReader
}
=
useCSVReader
()
const
[
zoneHover
,
setZoneHover
]
=
useState
(
false
)
return
(
<
CSVReader
onUploadAccepted=
{
(
results
:
any
)
=>
{
onParsed
(
results
.
data
)
setZoneHover
(
false
)
}
}
onDragOver=
{
(
event
:
DragEvent
)
=>
{
event
.
preventDefault
()
setZoneHover
(
true
)
}
}
onDragLeave=
{
(
event
:
DragEvent
)
=>
{
event
.
preventDefault
()
setZoneHover
(
false
)
}
}
>
{
({
getRootProps
,
acceptedFile
,
}:
any
)
=>
(
<>
<
div
{
...
getRootProps
()}
className=
{
cn
(
s
.
zone
,
zoneHover
&&
s
.
zoneHover
,
acceptedFile
?
'px-6'
:
'justify-center border-dashed text-gray-500'
)
}
>
{
acceptedFile
?
(
<
div
className=
'w-full flex items-center space-x-2'
>
<
CSVIcon
className=
"shrink-0"
/>
<
div
className=
'flex w-0 grow'
>
<
span
className=
'max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-gray-800'
>
{
acceptedFile
.
name
.
replace
(
/.csv$/
,
''
)
}
</
span
>
<
span
className=
'shrink-0 text-gray-500'
>
.csv
</
span
>
</
div
>
</
div
>
)
:
(
<
div
className=
'flex items-center justify-center space-x-2'
>
<
CSVIcon
className=
"shrink-0"
/>
<
div
className=
'text-gray-500'
>
{
t
(
'share.generation.csvUploadTitle'
)
}
<
span
className=
'text-primary-400'
>
{
t
(
'share.generation.browse'
)
}
</
span
></
div
>
</
div
>
)
}
</
div
>
</>
)
}
</
CSVReader
>
)
}
export
default
React
.
memo
(
CSVReader
)
web/app/components/share/text-generation/run-batch/csv-reader/style.module.css
0 → 100644
View file @
11baff67
.zone
{
@apply
flex
items-center
h-20
rounded-xl
bg-gray-50
border
border-gray-200
cursor-pointer
text-sm
font-normal;
}
.zoneHover
{
@apply
border-solid
bg-gray-100;
}
.info
{
@apply
text-gray-800
text-sm;
}
\ No newline at end of file
web/app/components/share/text-generation/run-batch/index.tsx
0 → 100644
View file @
11baff67
'use client'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
{
PlayIcon
,
}
from
'@heroicons/react/24/solid'
import
{
useTranslation
}
from
'react-i18next'
import
CSVReader
from
'./csv-reader'
import
CSVDownload
from
'./csv-download'
import
Button
from
'@/app/components/base/button'
export
type
IRunBatchProps
=
{
vars
:
{
name
:
string
}[]
onSend
:
(
data
:
string
[][])
=>
void
}
const
RunBatch
:
FC
<
IRunBatchProps
>
=
({
vars
,
onSend
,
})
=>
{
const
{
t
}
=
useTranslation
()
const
[
csvData
,
setCsvData
]
=
React
.
useState
<
string
[][]
>
([])
const
[
isParsed
,
setIsParsed
]
=
React
.
useState
(
false
)
const
handleParsed
=
(
data
:
string
[][])
=>
{
setCsvData
(
data
)
// console.log(data)
setIsParsed
(
true
)
}
const
handleSend
=
()
=>
{
onSend
(
csvData
)
}
return
(
<
div
className=
'pt-4'
>
<
CSVReader
onParsed=
{
handleParsed
}
/>
<
CSVDownload
vars=
{
vars
}
/>
<
div
className=
'mt-4 h-[1px] bg-gray-100'
></
div
>
<
div
className=
'flex justify-end'
>
<
Button
type=
"primary"
className=
'mt-4 !h-8 !pl-3 !pr-4'
onClick=
{
handleSend
}
disabled=
{
!
isParsed
}
>
<
PlayIcon
className=
"shrink-0 w-4 h-4 mr-1"
aria
-
hidden=
"true"
/>
<
span
className=
'uppercase text-[13px]'
>
{
t
(
'share.generation.run'
)
}
</
span
>
</
Button
>
</
div
>
</
div
>
)
}
export
default
React
.
memo
(
RunBatch
)
web/app/components/share/text-generation/
config-sce
nce/index.tsx
→
web/app/components/share/text-generation/
run-o
nce/index.tsx
View file @
11baff67
...
...
@@ -10,7 +10,7 @@ import type { PromptConfig } from '@/models/debug'
import
Button
from
'@/app/components/base/button'
import
{
DEFAULT_VALUE_MAX_LEN
}
from
'@/config'
export
type
I
ConfigSe
nceProps
=
{
export
type
I
RunO
nceProps
=
{
siteInfo
:
SiteInfo
promptConfig
:
PromptConfig
inputs
:
Record
<
string
,
any
>
...
...
@@ -19,7 +19,7 @@ export type IConfigSenceProps = {
onQueryChange
:
(
query
:
string
)
=>
void
onSend
:
()
=>
void
}
const
ConfigSence
:
FC
<
IConfigSe
nceProps
>
=
({
const
RunOnce
:
FC
<
IRunO
nceProps
>
=
({
promptConfig
,
inputs
,
onInputsChange
,
...
...
@@ -85,7 +85,7 @@ const ConfigSence: FC<IConfigSenceProps> = ({
</
div
>
<
Button
type=
"primary"
className=
'
w-[80px] !h-8 !p-0
'
className=
'
!h-8 !pl-3 !pr-4
'
onClick=
{
onSend
}
disabled=
{
!
query
||
query
===
''
}
>
...
...
@@ -100,4 +100,4 @@ const ConfigSence: FC<IConfigSenceProps> = ({
</
div
>
)
}
export
default
React
.
memo
(
ConfigSe
nce
)
export
default
React
.
memo
(
RunO
nce
)
web/i18n/lang/app-debug.en.ts
View file @
11baff67
...
...
@@ -86,6 +86,8 @@ const translation = {
queryRequired
:
'Request text is required.'
,
waitForResponse
:
'Please wait for the response to the previous message to complete.'
,
waitForBatchResponse
:
'Please wait for the response to the batch task to complete.'
,
},
chatSubTitle
:
'Pre Prompt'
,
completionSubTitle
:
'Prefix Prompt'
,
...
...
web/i18n/lang/app-debug.zh.ts
View file @
11baff67
...
...
@@ -84,6 +84,7 @@ const translation = {
valueOfVarRequired
:
'变量值必填'
,
queryRequired
:
'主要文本必填'
,
waitForResponse
:
'请等待上条信息响应完成'
,
waitForBatchResponse
:
'请等待批量任务完成'
,
},
chatSubTitle
:
'对话前提示词'
,
completionSubTitle
:
'前缀提示词'
,
...
...
web/i18n/lang/share-app.en.ts
View file @
11baff67
...
...
@@ -30,7 +30,8 @@ const translation = {
},
generation
:
{
tabs
:
{
create
:
'Create'
,
create
:
'Run Once'
,
batch
:
'Run Batch'
,
saved
:
'Saved'
,
},
savedNoData
:
{
...
...
@@ -41,10 +42,22 @@ const translation = {
title
:
'AI Completion'
,
queryTitle
:
'Query content'
,
queryPlaceholder
:
'Write your query content...'
,
run
:
'
RUN
'
,
run
:
'
Execute
'
,
copy
:
'Copy'
,
resultTitle
:
'AI Completion'
,
noData
:
'AI will give you what you want here.'
,
csvUploadTitle
:
'Drag and drop your CSV file here, or '
,
browse
:
'browse'
,
csvStructureTitle
:
'The CSV file must conform to the following structure:'
,
downloadTemplate
:
'Download the template here'
,
field
:
'Field'
,
errorMsg
:
{
empty
:
'Please input content in the uploaded file.'
,
fileStructNotMatch
:
'The uploaded CSV file not match the struct.'
,
emptyLine
:
'Row {{rowIndex}} is empty'
,
invalidLine
:
'Row {{rowIndex}}: variables value can not be empty'
,
atLeastOne
:
'Please input at least one row in the uploaded file.'
,
},
},
}
...
...
web/i18n/lang/share-app.zh.ts
View file @
11baff67
...
...
@@ -26,7 +26,8 @@ const translation = {
},
generation
:
{
tabs
:
{
create
:
'创建'
,
create
:
'运行一次'
,
batch
:
'批量运行'
,
saved
:
'已保存'
,
},
savedNoData
:
{
...
...
@@ -41,6 +42,18 @@ const translation = {
copy
:
'拷贝'
,
resultTitle
:
'AI 书写'
,
noData
:
'AI 会在这里给你惊喜。'
,
csvUploadTitle
:
'将您的 CSV 文件拖放到此处,或'
,
browse
:
'浏览'
,
csvStructureTitle
:
'CSV 文件必须符合以下结构:'
,
downloadTemplate
:
'下载模板'
,
field
:
''
,
errorMsg
:
{
empty
:
'上传文件的内容不能为空'
,
fileStructNotMatch
:
'上传文件的内容与结构不匹配'
,
emptyLine
:
'第 {{rowIndex}} 行的内容为空'
,
invalidLine
:
'第 {{rowIndex}} 行: 变量值必填'
,
atLeastOne
:
'上传文件的内容不能少于一条'
,
},
},
}
...
...
web/package.json
View file @
11baff67
...
...
@@ -61,6 +61,7 @@
"react-i18next"
:
"^12.2.0"
,
"react-infinite-scroll-component"
:
"^6.1.0"
,
"react-markdown"
:
"^8.0.6"
,
"react-papaparse"
:
"^4.1.0"
,
"react-slider"
:
"^2.0.4"
,
"react-sortablejs"
:
"^6.1.4"
,
"react-syntax-highlighter"
:
"^15.5.0"
,
...
...
web/service/base.ts
View file @
11baff67
...
...
@@ -308,13 +308,13 @@ export const ssePost = (url: string, fetchOptions: any, { isPublicAPI = false, o
}
return
handleStream
(
res
,
(
str
:
string
,
isFirstMessage
:
boolean
,
moreInfo
:
IOnDataMoreInfo
)
=>
{
if
(
moreInfo
.
errorMessage
)
{
onError
?.(
moreInfo
.
errorMessage
)
Toast
.
notify
({
type
:
'error'
,
message
:
moreInfo
.
errorMessage
})
return
}
onData
?.(
str
,
isFirstMessage
,
moreInfo
)
},
onCompleted
)
}).
catch
((
e
)
=>
{
// debugger
Toast
.
notify
({
type
:
'error'
,
message
:
e
})
onError
?.(
e
)
})
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment