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
2bfb77ea
Commit
2bfb77ea
authored
Jul 25, 2023
by
Joel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feat/universal-chat-fe' into deploy/dev
parents
64f7e622
9be4b804
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
130 additions
and
60 deletions
+130
-60
index.tsx
web/app/components/app/chat/thought/index.tsx
+0
-2
index.tsx
...app/configuration/dataset-config/select-dataset/index.tsx
+6
-2
checked.svg
...app/components/base/icons/assets/public/model/checked.svg
+3
-0
Checked.json
web/app/components/base/icons/src/public/model/Checked.json
+29
-0
Checked.tsx
web/app/components/base/icons/src/public/model/Checked.tsx
+14
-0
index.ts
web/app/components/base/icons/src/public/model/index.ts
+1
-0
markdown.tsx
web/app/components/base/markdown.tsx
+48
-40
index.tsx
...ents/explore/universal-chat/config/model-config/index.tsx
+11
-4
index.tsx
...ts/explore/universal-chat/config/plugins-config/index.tsx
+1
-1
index.tsx
web/app/components/explore/universal-chat/index.tsx
+13
-7
index.tsx
web/app/components/share/chat/sidebar/index.tsx
+2
-2
index.tsx
web/app/components/share/chat/sidebar/list/index.tsx
+1
-1
index.tsx
web/app/components/share/chatbot/sidebar/list/index.tsx
+1
-1
No files found.
web/app/components/app/chat/thought/index.tsx
View file @
2bfb77ea
...
...
@@ -9,8 +9,6 @@ import { DataSet as DataSetIcon, Loading as LodingIcon, Search, ThoughtList, Web
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
import
type
{
DataSet
}
from
'@/models/datasets'
// https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/
const
urlRegex
=
/
(
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z
]{2,}(\.[
a-zA-Z
]{2,})(\.[
a-zA-Z
]{2,})?\/[
a-zA-Z0-9
]{2,}
|
((
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z
]{2,}(\.[
a-zA-Z
]{2,})(\.[
a-zA-Z
]{2,})?)
|
(
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z0-9
]{2,}\.[
a-zA-Z0-9
]{2,}\.[
a-zA-Z0-9
]{2,}(\.[
a-zA-Z0-9
]{2,})?
/gi
export
type
IThoughtProps
=
{
list
:
ThoughtItem
[]
isThinking
?:
boolean
...
...
web/app/components/app/configuration/dataset-config/select-dataset/index.tsx
View file @
2bfb77ea
...
...
@@ -128,8 +128,12 @@ const SelectDataSet: FC<ISelectDataSetProps> = ({
<
div
className=
'max-w-[200px] text-[13px] font-medium text-gray-800 overflow-hidden text-ellipsis whitespace-nowrap'
>
{
item
.
name
}
</
div
>
</
div
>
<
div
className=
'max-w-[140px] flex text-xs text-gray-500 overflow-hidden text-ellipsis whitespace-nowrap'
>
{
formatNumber
(
item
.
word_count
)
}
{
t
(
'appDebug.feature.dataSet.words'
)
}
·
{
formatNumber
(
item
.
document_count
)
}
{
t
(
'appDebug.feature.dataSet.textBlocks'
)
}
<
div
className=
'flex text-xs text-gray-500 overflow-hidden whitespace-nowrap'
>
<
span
className=
'max-w-[100px] overflow-hidden text-ellipsis whitespace-nowrap'
>
{
formatNumber
(
item
.
word_count
)
}
</
span
>
{
t
(
'appDebug.feature.dataSet.words'
)
}
<
span
className=
'px-0.5'
>
·
</
span
>
<
span
className=
'max-w-[100px] min-w-[8px] overflow-hidden text-ellipsis whitespace-nowrap'
>
{
formatNumber
(
item
.
document_count
)
}
</
span
>
{
t
(
'appDebug.feature.dataSet.textBlocks'
)
}
</
div
>
</
div
>
))
}
...
...
web/app/components/base/icons/assets/public/model/checked.svg
0 → 100644
View file @
2bfb77ea
<svg
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M13.3332 4L5.99984 11.3333L2.6665 8"
stroke=
"#155EEF"
stroke-width=
"2"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/base/icons/src/public/model/Checked.json
0 → 100644
View file @
2bfb77ea
{
"icon"
:
{
"type"
:
"element"
,
"isRootNode"
:
true
,
"name"
:
"svg"
,
"attributes"
:
{
"width"
:
"16"
,
"height"
:
"16"
,
"viewBox"
:
"0 0 16 16"
,
"fill"
:
"none"
,
"xmlns"
:
"http://www.w3.org/2000/svg"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M13.3332 4L5.99984 11.3333L2.6665 8"
,
"stroke"
:
"#155EEF"
,
"stroke-width"
:
"2"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
},
"children"
:
[]
}
]
},
"name"
:
"Checked"
}
\ No newline at end of file
web/app/components/base/icons/src/public/model/Checked.tsx
0 → 100644
View file @
2bfb77ea
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import
*
as
React
from
'react'
import
data
from
'./Checked.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/model/index.ts
0 → 100644
View file @
2bfb77ea
export
{
default
as
Checked
}
from
'./Checked'
web/app/components/base/markdown.tsx
View file @
2bfb77ea
import
ReactMarkdown
from
"react-markdown"
;
import
"katex/dist/katex.min.css"
;
import
RemarkMath
from
"remark-math"
;
import
RemarkBreaks
from
"remark-breaks"
;
import
RehypeKatex
from
"rehype-katex"
;
import
RemarkGfm
from
"remark-gfm"
;
import
ReactMarkdown
from
'react-markdown'
import
'katex/dist/katex.min.css'
import
RemarkMath
from
'remark-math'
import
RemarkBreaks
from
'remark-breaks'
import
RehypeKatex
from
'rehype-katex'
import
RemarkGfm
from
'remark-gfm'
import
SyntaxHighlighter
from
'react-syntax-highlighter'
import
{
atelierHeathLight
}
from
'react-syntax-highlighter/dist/esm/styles/hljs'
import
{
useRef
,
useState
,
RefObject
,
useEffect
}
from
"react"
;
import
type
{
RefObject
}
from
'react'
import
{
useEffect
,
useRef
,
useState
}
from
'react'
// import { copyToClipboard } from "../utils";
// https://txtfiddle.com/~hlshwya/extract-urls-from-text
const
urlRegex
=
/
\b((
https
?
|ftp|file
)
:
\/\/
|
(
www|ftp
)\.)[
-A-Z0-9+&@#
\/
%?=~_|$!:,.;
]
*
[
A-Z0-9+&@#
\/
%=~_|$
]
/ig
function
highlightURL
(
content
:
string
)
{
return
content
.
replace
(
urlRegex
,
'[$&]($&)'
)
}
export
function
PreCode
(
props
:
{
children
:
any
})
{
const
ref
=
useRef
<
HTMLPreElement
>
(
null
)
;
const
ref
=
useRef
<
HTMLPreElement
>
(
null
)
return
(
<
pre
ref=
{
ref
}
>
...
...
@@ -18,38 +24,37 @@ export function PreCode(props: { children: any }) {
className=
"copy-code-button"
onClick=
{
()
=>
{
if
(
ref
.
current
)
{
const
code
=
ref
.
current
.
innerText
;
const
code
=
ref
.
current
.
innerText
// copyToClipboard(code);
}
}
}
></
span
>
{
props
.
children
}
</
pre
>
)
;
)
}
const
useLazyLoad
=
(
ref
:
RefObject
<
Element
>
):
boolean
=>
{
const
[
isIntersecting
,
setIntersecting
]
=
useState
<
boolean
>
(
false
)
;
const
[
isIntersecting
,
setIntersecting
]
=
useState
<
boolean
>
(
false
)
useEffect
(()
=>
{
const
observer
=
new
IntersectionObserver
(([
entry
])
=>
{
if
(
entry
.
isIntersecting
)
{
setIntersecting
(
true
)
;
observer
.
disconnect
()
;
setIntersecting
(
true
)
observer
.
disconnect
()
}
})
;
})
if
(
ref
.
current
)
{
observer
.
observe
(
ref
.
current
);
}
if
(
ref
.
current
)
observer
.
observe
(
ref
.
current
)
return
()
=>
{
observer
.
disconnect
()
;
}
;
},
[
ref
])
;
observer
.
disconnect
()
}
},
[
ref
])
return
isIntersecting
;
}
;
return
isIntersecting
}
export
function
Markdown
(
props
:
{
content
:
string
})
{
return
(
...
...
@@ -62,26 +67,29 @@ export function Markdown(props: { content: string }) {
components=
{
{
code
({
node
,
inline
,
className
,
children
,
...
props
})
{
const
match
=
/language-
(\w
+
)
/
.
exec
(
className
||
''
)
return
!
inline
&&
match
?
(
<
SyntaxHighlighter
{
...
props
}
children=
{
String
(
children
).
replace
(
/
\n
$/
,
''
)
}
style=
{
atelierHeathLight
}
language=
{
match
[
1
]
}
showLineNumbers
PreTag=
"div"
/>
)
:
(
<
code
{
...
props
}
className=
{
className
}
>
{
children
}
</
code
>
)
}
return
(
!
inline
&&
match
)
?
(
<
SyntaxHighlighter
{
...
props
}
children=
{
String
(
children
).
replace
(
/
\n
$/
,
''
)
}
style=
{
atelierHeathLight
}
language=
{
match
[
1
]
}
showLineNumbers
PreTag=
"div"
/>
)
:
(
<
code
{
...
props
}
className=
{
className
}
>
{
children
}
</
code
>
)
},
}
}
linkTarget=
{
"_blank"
}
linkTarget=
{
'_blank'
}
>
{
props
.
content
}
{
/* Markdown detect has problem. */
}
{
highlightURL
(
props
.
content
)
}
</
ReactMarkdown
>
</
div
>
)
;
)
}
web/app/components/explore/universal-chat/config/model-config/index.tsx
View file @
2bfb77ea
...
...
@@ -7,7 +7,7 @@ import { ChevronDownIcon } from '@heroicons/react/24/outline'
import
{
useTranslation
}
from
'react-i18next'
import
ModelIcon
from
'@/app/components/app/configuration/config-model/model-icon'
import
{
UNIVERSAL_CHAT_MODEL_LIST
as
MODEL_LIST
}
from
'@/config'
import
{
Checked
as
CheckedIcon
}
from
'@/app/components/base/icons/src/public/model'
export
type
IModelConfigProps
=
{
modelId
:
string
onChange
?:
(
model
:
string
)
=>
void
...
...
@@ -32,17 +32,24 @@ const ModelConfig: FC<IModelConfigProps> = ({
<
div
className=
'flex items-center justify-between h-[52px] px-3 rounded-xl bg-gray-50'
>
<
div
className=
'text-sm font-semibold text-gray-800'
>
{
t
(
'explore.universalChat.model'
)
}
</
div
>
<
div
className=
"relative z-10"
>
<
div
ref=
{
triggerRef
}
onClick=
{
()
=>
!
readonly
&&
toogleOption
()
}
className=
{
cn
(
readonly
?
'cursor-not-allowed'
:
'cursor-pointer'
,
'flex items-center h-9 px-3 space-x-2 rounded-lg bg-gray-50 '
)
}
>
<
div
ref=
{
triggerRef
}
onClick=
{
()
=>
!
readonly
&&
toogleOption
()
}
className=
{
cn
(
readonly
?
'cursor-not-allowed'
:
'cursor-pointer'
,
'flex items-center h-9 px-3 space-x-2 rounded-lg'
,
isShowOption
&&
'bg-gray-100'
,
)
}
>
<
ModelIcon
modelId=
{
currModel
?.
id
as
string
}
/>
<
div
className=
"text-sm gray-900"
>
{
currModel
?.
name
}
</
div
>
{
!
readonly
&&
<
ChevronDownIcon
className=
{
cn
(
isShowOption
&&
'rotate-180'
,
'w-[14px] h-[14px] text-gray-500'
)
}
/>
}
</
div
>
{
isShowOption
&&
(
<
div
className=
{
cn
(
'
min-w-[159px] absolute right-0 bg-gray-50
rounded-lg shadow'
)
}
>
<
div
className=
{
cn
(
'
absolute top-10 right-0 bg-white
rounded-lg shadow'
)
}
>
{
MODEL_LIST
.
map
(
item
=>
(
<
div
key=
{
item
.
id
}
onClick=
{
()
=>
onChange
?.(
item
.
id
)
}
className=
"
flex items-center h-9 px-3
rounded-lg cursor-pointer hover:bg-gray-100"
>
<
div
key=
{
item
.
id
}
onClick=
{
()
=>
onChange
?.(
item
.
id
)
}
className=
"
w-[232px] flex items-center h-9 px-4
rounded-lg cursor-pointer hover:bg-gray-100"
>
<
ModelIcon
className=
'shrink-0 mr-2'
modelId=
{
item
?.
id
}
/>
<
div
className=
"text-sm gray-900 whitespace-nowrap"
>
{
item
.
name
}
</
div
>
{
(
item
.
id
===
currModel
?.
id
)
&&
<
CheckedIcon
className=
'absolute right-4'
/>
}
</
div
>
))
}
</
div
>
...
...
web/app/components/explore/universal-chat/config/plugins-config/index.tsx
View file @
2bfb77ea
...
...
@@ -78,7 +78,7 @@ const Plugins: FC<IPluginsProps> = ({
>
{
isLoading
?
(
<
div
className=
'flex items-center h-[
200
px]'
>
<
div
className=
'flex items-center h-[
166
px]'
>
<
Loading
type=
'area'
/>
</
div
>
)
...
...
web/app/components/explore/universal-chat/index.tsx
View file @
2bfb77ea
...
...
@@ -40,7 +40,7 @@ import ConfigSummary from '@/app/components/explore/universal-chat/config-view/s
import
ConfigDetail
from
'@/app/components/explore/universal-chat/config-view/detail'
import
{
fetchDatasets
}
from
'@/service/datasets'
const
APP_ID
=
'universal-chat'
const
DEFAULT_MODEL_ID
=
'
claude-2
'
// gpt-4, claude-2
const
DEFAULT_MODEL_ID
=
'
gpt-3.5-turbo
'
// gpt-4, claude-2
const
DEFAULT_PLUGIN
=
{
google_search
:
false
,
web_reader
:
true
,
...
...
@@ -235,11 +235,14 @@ const Main: FC<IMainProps> = () => {
})
})
setChatList
(
newChatList
)
setErrorHappened
(
false
)
})
}
if
(
isNewConversation
)
if
(
isNewConversation
)
{
setChatList
(
generateNewChatListWithOpenstatement
())
setErrorHappened
(
false
)
}
setControlFocus
(
Date
.
now
())
}
...
...
@@ -405,7 +408,7 @@ const Main: FC<IMainProps> = () => {
const
[
suggestQuestions
,
setSuggestQuestions
]
=
useState
<
string
[]
>
([])
const
[
messageTaskId
,
setMessageTaskId
]
=
useState
(
''
)
const
[
hasStopResponded
,
setHasStopResponded
,
getHasStopResponded
]
=
useGetState
(
false
)
const
[
errorHappened
,
setErrorHappened
]
=
useState
(
false
)
const
handleSend
=
async
(
message
:
string
)
=>
{
if
(
isResponsing
)
{
notify
({
type
:
'info'
,
message
:
t
(
'appDebug.errorMessage.waitForResponse'
)
})
...
...
@@ -462,6 +465,7 @@ const Main: FC<IMainProps> = () => {
setHasStopResponded
(
false
)
setResponsingTrue
()
setErrorHappened
(
false
)
setIsShowSuggestion
(
false
)
sendChatMessage
(
data
,
{
...
...
@@ -509,8 +513,8 @@ const Main: FC<IMainProps> = () => {
if
(
thought
.
thought
===
'[DONE]'
)
return
responseItem
.
id
=
thought
.
message_id
;
// thought finished then start to return message
(
responseItem
as
any
).
agent_thoughts
.
push
(
thought
)
// thought finished then start to return message
. Warning: use push agent_thoughts.push would caused problem when the thought is more then 2
(
responseItem
as
any
).
agent_thoughts
=
[...(
responseItem
as
any
).
agent_thoughts
,
thought
]
//
.push(thought)
const
newListWithAnswer
=
produce
(
getChatList
().
filter
(
item
=>
item
.
id
!==
responseItem
.
id
&&
item
.
id
!==
placeholderAnswerId
),
(
draft
)
=>
{
...
...
@@ -521,7 +525,9 @@ const Main: FC<IMainProps> = () => {
setChatList
(
newListWithAnswer
)
},
onError
()
{
setErrorHappened
(
true
)
setResponsingFalse
()
// role back placeholder answer
setChatList
(
produce
(
getChatList
(),
(
draft
)
=>
{
draft
.
splice
(
draft
.
findIndex
(
item
=>
item
.
id
===
placeholderAnswerId
),
1
)
...
...
@@ -625,7 +631,7 @@ const Main: FC<IMainProps> = () => {
)
}
>
<
div
className=
{
cn
(
doShowSuggestion
?
'pb-[140px]'
:
(
isResponsing
?
'pb-[113px]'
:
'pb-[76px]'
),
'relative grow h-[200px] mb-3.5 overflow-hidden'
)
}
>
{
(
!
isNewConversation
||
isResponsing
)
&&
(
{
(
!
isNewConversation
||
isResponsing
||
errorHappened
)
&&
(
<
div
className=
'absolute z-10 top-0 left-0 right-0 flex items-center justify-between border-b border-gray-100 mobile:h-12 tablet:h-16 px-8 bg-white'
>
<
div
className=
'text-gray-900'
>
{
conversationName
}
</
div
>
<
div
className=
'flex items-center shrink-0 ml-2 space-x-2'
>
...
...
@@ -637,7 +643,7 @@ const Main: FC<IMainProps> = () => {
)
}
<
div
className=
{
cn
((
!
isNewConversation
||
isResponsing
)
&&
'pt-[90px]'
,
'pc:w-[794px] max-w-full mobile:w-full mx-auto h-full overflow-y-auto'
)
}
ref=
{
chatListDomRef
}
>
<
Chat
isShowConfigElem=
{
isNewConversation
&&
!
chatList
.
some
(
item
=>
item
.
isAnswer
)
}
isShowConfigElem=
{
isNewConversation
&&
chatList
.
length
===
0
}
configElem=
{
<
Init
modelId=
{
modelId
}
onModelChange=
{
setModeId
}
...
...
web/app/components/share/chat/sidebar/index.tsx
View file @
2bfb77ea
...
...
@@ -79,13 +79,13 @@ const Sidebar: FC<ISidebarProps> = ({
checkHasPinned
()
},
[
controlUpdateList
])
const
maxListHeight
=
isInstalledApp
?
'max-h-[30vh]'
:
'max-h-[40vh]'
const
maxListHeight
=
(
isInstalledApp
||
isUniversalChat
)
?
'max-h-[30vh]'
:
'max-h-[40vh]'
return
(
<
div
className=
{
cn
(
isInstalledApp
?
'tablet:h-[calc(100vh_-_74px)]'
:
'tablet:h-[calc(100vh_-_3rem)]'
,
(
isInstalledApp
||
isUniversalChat
)
?
'tablet:h-[calc(100vh_-_74px)]'
:
'tablet:h-[calc(100vh_-_3rem)]'
,
'shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px] border-r border-gray-200 mobile:h-screen'
,
)
}
...
...
web/app/components/share/chat/sidebar/list/index.tsx
View file @
2bfb77ea
...
...
@@ -75,7 +75,7 @@ const List: FC<IListProps> = ({
return
(
<
nav
ref=
{
listRef
}
className=
{
cn
(
className
,
'shrink-0 space-y-1 bg-white
pb-[85px]
overflow-y-auto'
)
}
className=
{
cn
(
className
,
'shrink-0 space-y-1 bg-white overflow-y-auto'
)
}
>
{
list
.
map
((
item
)
=>
{
const
isCurrent
=
item
.
id
===
currentId
...
...
web/app/components/share/chatbot/sidebar/list/index.tsx
View file @
2bfb77ea
...
...
@@ -65,7 +65,7 @@ const List: FC<IListProps> = ({
return
(
<
nav
ref=
{
listRef
}
className=
{
cn
(
className
,
'shrink-0 space-y-1 bg-white
pb-[85px]
overflow-y-auto'
)
}
className=
{
cn
(
className
,
'shrink-0 space-y-1 bg-white overflow-y-auto'
)
}
>
{
list
.
map
((
item
)
=>
{
const
isCurrent
=
item
.
id
===
currentId
...
...
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