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
c8757ef0
Commit
c8757ef0
authored
Mar 02, 2024
by
crazywoola
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'fix-explore-apps-search-params-issue' into deploy/dev
parents
6df111a6
5aae559b
Changes
28
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
632 additions
and
88 deletions
+632
-88
style.yml
.github/workflows/style.yml
+3
-2
.env.example
api/.env.example
+2
-2
commands.py
api/commands.py
+29
-6
llm.py
api/core/model_runtime/model_providers/localai/llm/llm.py
+5
-2
vector_factory.py
api/core/rag/datasource/vdb/vector_factory.py
+3
-3
weaviate_vector.py
api/core/rag/datasource/vdb/weaviate/weaviate_vector.py
+1
-1
icon.svg
api/core/tools/provider/builtin/arxiv/_assets/icon.svg
+1
-0
arxiv.py
api/core/tools/provider/builtin/arxiv/arxiv.py
+20
-0
arxiv.yaml
api/core/tools/provider/builtin/arxiv/arxiv.yaml
+10
-0
arxiv_search.py
api/core/tools/provider/builtin/arxiv/tools/arxiv_search.py
+37
-0
arxiv_search.yaml
...core/tools/provider/builtin/arxiv/tools/arxiv_search.yaml
+23
-0
gaode_weather.py
api/core/tools/provider/builtin/gaode/tools/gaode_weather.py
+1
-1
api_tool.py
api/core/tools/tool/api_tool.py
+63
-21
parser.py
api/core/tools/utils/parser.py
+5
-6
dataset.py
api/models/dataset.py
+4
-0
requirements.txt
api/requirements.txt
+2
-1
dataset_service.py
api/services/dataset_service.py
+1
-1
tools_manage_service.py
api/services/tools_manage_service.py
+2
-2
index.tsx
...ents/app/configuration/config/agent/agent-tools/index.tsx
+13
-12
setting-built-in-tool.tsx
...ration/config/agent/agent-tools/setting-built-in-tool.tsx
+24
-8
index.tsx
web/app/components/explore/app-list/index.tsx
+7
-6
category.tsx
web/app/components/explore/category.tsx
+18
-7
massive-component.tsx
web/app/components/share/chat/welcome/massive-component.tsx
+2
-2
index.tsx
...p/components/tools/edit-custom-collection-modal/index.tsx
+7
-0
test-api.tsx
...omponents/tools/edit-custom-collection-modal/test-api.tsx
+9
-2
index.tsx
web/app/components/tools/index.tsx
+14
-1
item.tsx
web/app/components/tools/tool-list/item.tsx
+4
-2
categories.ts
web/test/mocks/categories.ts
+322
-0
No files found.
.github/workflows/style.yml
View file @
c8757ef0
...
@@ -41,6 +41,8 @@ jobs:
...
@@ -41,6 +41,8 @@ jobs:
steps
:
steps
:
-
name
:
Checkout code
-
name
:
Checkout code
uses
:
actions/checkout@v4
uses
:
actions/checkout@v4
with
:
fetch-depth
:
0
-
name
:
Setup NodeJS
-
name
:
Setup NodeJS
uses
:
actions/setup-node@v4
uses
:
actions/setup-node@v4
...
@@ -60,11 +62,10 @@ jobs:
...
@@ -60,11 +62,10 @@ jobs:
yarn run lint
yarn run lint
-
name
:
Super-linter
-
name
:
Super-linter
uses
:
super-linter/super-linter/slim@v
5
uses
:
super-linter/super-linter/slim@v
6
env
:
env
:
BASH_SEVERITY
:
warning
BASH_SEVERITY
:
warning
DEFAULT_BRANCH
:
main
DEFAULT_BRANCH
:
main
ERROR_ON_MISSING_EXEC_BIT
:
true
GITHUB_TOKEN
:
${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN
:
${{ secrets.GITHUB_TOKEN }}
IGNORE_GENERATED_FILES
:
true
IGNORE_GENERATED_FILES
:
true
IGNORE_GITIGNORED_FILES
:
true
IGNORE_GITIGNORED_FILES
:
true
...
...
api/.env.example
View file @
c8757ef0
...
@@ -82,7 +82,7 @@ UPLOAD_IMAGE_FILE_SIZE_LIMIT=10
...
@@ -82,7 +82,7 @@ UPLOAD_IMAGE_FILE_SIZE_LIMIT=10
MULTIMODAL_SEND_IMAGE_FORMAT=base64
MULTIMODAL_SEND_IMAGE_FORMAT=base64
# Mail configuration, support: resend, smtp
# Mail configuration, support: resend, smtp
MAIL_TYPE=
resend
MAIL_TYPE=
MAIL_DEFAULT_SEND_FROM=no-reply <no-reply@dify.ai>
MAIL_DEFAULT_SEND_FROM=no-reply <no-reply@dify.ai>
RESEND_API_KEY=
RESEND_API_KEY=
RESEND_API_URL=https://api.resend.com
RESEND_API_URL=https://api.resend.com
...
@@ -131,4 +131,4 @@ UNSTRUCTURED_API_URL=
...
@@ -131,4 +131,4 @@ UNSTRUCTURED_API_URL=
SSRF_PROXY_HTTP_URL=
SSRF_PROXY_HTTP_URL=
SSRF_PROXY_HTTPS_URL=
SSRF_PROXY_HTTPS_URL=
BATCH_UPLOAD_LIMIT=10
BATCH_UPLOAD_LIMIT=10
\ No newline at end of file
api/commands.py
View file @
c8757ef0
...
@@ -131,6 +131,8 @@ def vdb_migrate():
...
@@ -131,6 +131,8 @@ def vdb_migrate():
"""
"""
click
.
echo
(
click
.
style
(
'Start migrate vector db.'
,
fg
=
'green'
))
click
.
echo
(
click
.
style
(
'Start migrate vector db.'
,
fg
=
'green'
))
create_count
=
0
create_count
=
0
skipped_count
=
0
total_count
=
0
config
=
current_app
.
config
config
=
current_app
.
config
vector_type
=
config
.
get
(
'VECTOR_STORE'
)
vector_type
=
config
.
get
(
'VECTOR_STORE'
)
page
=
1
page
=
1
...
@@ -143,14 +145,19 @@ def vdb_migrate():
...
@@ -143,14 +145,19 @@ def vdb_migrate():
page
+=
1
page
+=
1
for
dataset
in
datasets
:
for
dataset
in
datasets
:
total_count
=
total_count
+
1
click
.
echo
(
f
'Processing the {total_count} dataset {dataset.id}. '
+
f
'{create_count} created, ${skipped_count} skipped.'
)
try
:
try
:
click
.
echo
(
'Create dataset vdb index: {}'
.
format
(
dataset
.
id
))
click
.
echo
(
'Create dataset vdb index: {}'
.
format
(
dataset
.
id
))
if
dataset
.
index_struct_dict
:
if
dataset
.
index_struct_dict
:
if
dataset
.
index_struct_dict
[
'type'
]
==
vector_type
:
if
dataset
.
index_struct_dict
[
'type'
]
==
vector_type
:
skipped_count
=
skipped_count
+
1
continue
continue
collection_name
=
''
if
vector_type
==
"weaviate"
:
if
vector_type
==
"weaviate"
:
dataset_id
=
dataset
.
id
dataset_id
=
dataset
.
id
collection_name
=
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
collection_name
=
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
index_struct_dict
=
{
index_struct_dict
=
{
"type"
:
'weaviate'
,
"type"
:
'weaviate'
,
"vector_store"
:
{
"class_prefix"
:
collection_name
}
"vector_store"
:
{
"class_prefix"
:
collection_name
}
...
@@ -167,7 +174,7 @@ def vdb_migrate():
...
@@ -167,7 +174,7 @@ def vdb_migrate():
raise
ValueError
(
'Dataset Collection Bindings is not exist!'
)
raise
ValueError
(
'Dataset Collection Bindings is not exist!'
)
else
:
else
:
dataset_id
=
dataset
.
id
dataset_id
=
dataset
.
id
collection_name
=
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
collection_name
=
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
index_struct_dict
=
{
index_struct_dict
=
{
"type"
:
'qdrant'
,
"type"
:
'qdrant'
,
"vector_store"
:
{
"class_prefix"
:
collection_name
}
"vector_store"
:
{
"class_prefix"
:
collection_name
}
...
@@ -176,7 +183,7 @@ def vdb_migrate():
...
@@ -176,7 +183,7 @@ def vdb_migrate():
elif
vector_type
==
"milvus"
:
elif
vector_type
==
"milvus"
:
dataset_id
=
dataset
.
id
dataset_id
=
dataset
.
id
collection_name
=
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
collection_name
=
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
index_struct_dict
=
{
index_struct_dict
=
{
"type"
:
'milvus'
,
"type"
:
'milvus'
,
"vector_store"
:
{
"class_prefix"
:
collection_name
}
"vector_store"
:
{
"class_prefix"
:
collection_name
}
...
@@ -186,11 +193,17 @@ def vdb_migrate():
...
@@ -186,11 +193,17 @@ def vdb_migrate():
raise
ValueError
(
f
"Vector store {config.get('VECTOR_STORE')} is not supported."
)
raise
ValueError
(
f
"Vector store {config.get('VECTOR_STORE')} is not supported."
)
vector
=
Vector
(
dataset
)
vector
=
Vector
(
dataset
)
click
.
echo
(
f
"
vdb_migrate {dataset.id}
"
)
click
.
echo
(
f
"
Start to migrate dataset {dataset.id}.
"
)
try
:
try
:
vector
.
delete
()
vector
.
delete
()
click
.
echo
(
click
.
style
(
f
'Successfully delete vector index {collection_name} for dataset {dataset.id}.'
,
fg
=
'green'
))
except
Exception
as
e
:
except
Exception
as
e
:
click
.
echo
(
click
.
style
(
f
'Failed to delete vector index {collection_name} for dataset {dataset.id}.'
,
fg
=
'red'
))
raise
e
raise
e
dataset_documents
=
db
.
session
.
query
(
DatasetDocument
)
.
filter
(
dataset_documents
=
db
.
session
.
query
(
DatasetDocument
)
.
filter
(
...
@@ -201,6 +214,7 @@ def vdb_migrate():
...
@@ -201,6 +214,7 @@ def vdb_migrate():
)
.
all
()
)
.
all
()
documents
=
[]
documents
=
[]
segments_count
=
0
for
dataset_document
in
dataset_documents
:
for
dataset_document
in
dataset_documents
:
segments
=
db
.
session
.
query
(
DocumentSegment
)
.
filter
(
segments
=
db
.
session
.
query
(
DocumentSegment
)
.
filter
(
DocumentSegment
.
document_id
==
dataset_document
.
id
,
DocumentSegment
.
document_id
==
dataset_document
.
id
,
...
@@ -220,15 +234,22 @@ def vdb_migrate():
...
@@ -220,15 +234,22 @@ def vdb_migrate():
)
)
documents
.
append
(
document
)
documents
.
append
(
document
)
segments_count
=
segments_count
+
1
if
documents
:
if
documents
:
try
:
try
:
click
.
echo
(
click
.
style
(
f
'Start to created vector index with {len(documents)} documents of {segments_count} segments for dataset {dataset.id}.'
,
fg
=
'green'
))
vector
.
create
(
documents
)
vector
.
create
(
documents
)
click
.
echo
(
click
.
style
(
f
'Successfully created vector index for dataset {dataset.id}.'
,
fg
=
'green'
))
except
Exception
as
e
:
except
Exception
as
e
:
click
.
echo
(
click
.
style
(
f
'Failed to created vector index for dataset {dataset.id}.'
,
fg
=
'red'
))
raise
e
raise
e
click
.
echo
(
f
"Dataset {dataset.id} create successfully."
)
db
.
session
.
add
(
dataset
)
db
.
session
.
add
(
dataset
)
db
.
session
.
commit
()
db
.
session
.
commit
()
click
.
echo
(
f
'Successfully migrated dataset {dataset.id}.'
)
create_count
+=
1
create_count
+=
1
except
Exception
as
e
:
except
Exception
as
e
:
db
.
session
.
rollback
()
db
.
session
.
rollback
()
...
@@ -237,7 +258,9 @@ def vdb_migrate():
...
@@ -237,7 +258,9 @@ def vdb_migrate():
fg
=
'red'
))
fg
=
'red'
))
continue
continue
click
.
echo
(
click
.
style
(
'Congratulations! Create {} dataset indexes.'
.
format
(
create_count
),
fg
=
'green'
))
click
.
echo
(
click
.
style
(
f
'Congratulations! Create {create_count} dataset indexes, and skipped {skipped_count} datasets.'
,
fg
=
'green'
))
def
register_commands
(
app
):
def
register_commands
(
app
):
...
...
api/core/model_runtime/model_providers/localai/llm/llm.py
View file @
c8757ef0
from
collections.abc
import
Generator
from
collections.abc
import
Generator
from
os.path
import
join
from
typing
import
cast
from
typing
import
cast
from
urllib.parse
import
urljoin
from
httpx
import
Timeout
from
httpx
import
Timeout
from
openai
import
(
from
openai
import
(
...
@@ -313,10 +313,13 @@ class LocalAILarguageModel(LargeLanguageModel):
...
@@ -313,10 +313,13 @@ class LocalAILarguageModel(LargeLanguageModel):
:param credentials: credentials dict
:param credentials: credentials dict
:return: client kwargs
:return: client kwargs
"""
"""
if
not
credentials
[
'server_url'
]
.
endswith
(
'/'
):
credentials
[
'server_url'
]
+=
'/'
client_kwargs
=
{
client_kwargs
=
{
"timeout"
:
Timeout
(
315.0
,
read
=
300.0
,
write
=
10.0
,
connect
=
5.0
),
"timeout"
:
Timeout
(
315.0
,
read
=
300.0
,
write
=
10.0
,
connect
=
5.0
),
"api_key"
:
"1"
,
"api_key"
:
"1"
,
"base_url"
:
join
(
credentials
[
'server_url'
],
'v1'
),
"base_url"
:
url
join
(
credentials
[
'server_url'
],
'v1'
),
}
}
return
client_kwargs
return
client_kwargs
...
...
api/core/rag/datasource/vdb/vector_factory.py
View file @
c8757ef0
...
@@ -39,7 +39,7 @@ class Vector:
...
@@ -39,7 +39,7 @@ class Vector:
collection_name
=
class_prefix
collection_name
=
class_prefix
else
:
else
:
dataset_id
=
self
.
_dataset
.
id
dataset_id
=
self
.
_dataset
.
id
collection_name
=
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
collection_name
=
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
index_struct_dict
=
{
index_struct_dict
=
{
"type"
:
'weaviate'
,
"type"
:
'weaviate'
,
"vector_store"
:
{
"class_prefix"
:
collection_name
}
"vector_store"
:
{
"class_prefix"
:
collection_name
}
...
@@ -70,7 +70,7 @@ class Vector:
...
@@ -70,7 +70,7 @@ class Vector:
collection_name
=
class_prefix
collection_name
=
class_prefix
else
:
else
:
dataset_id
=
self
.
_dataset
.
id
dataset_id
=
self
.
_dataset
.
id
collection_name
=
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
collection_name
=
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
if
not
self
.
_dataset
.
index_struct_dict
:
if
not
self
.
_dataset
.
index_struct_dict
:
index_struct_dict
=
{
index_struct_dict
=
{
...
@@ -96,7 +96,7 @@ class Vector:
...
@@ -96,7 +96,7 @@ class Vector:
collection_name
=
class_prefix
collection_name
=
class_prefix
else
:
else
:
dataset_id
=
self
.
_dataset
.
id
dataset_id
=
self
.
_dataset
.
id
collection_name
=
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
collection_name
=
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
index_struct_dict
=
{
index_struct_dict
=
{
"type"
:
'milvus'
,
"type"
:
'milvus'
,
"vector_store"
:
{
"class_prefix"
:
collection_name
}
"vector_store"
:
{
"class_prefix"
:
collection_name
}
...
...
api/core/rag/datasource/vdb/weaviate/weaviate_vector.py
View file @
c8757ef0
...
@@ -70,7 +70,7 @@ class WeaviateVector(BaseVector):
...
@@ -70,7 +70,7 @@ class WeaviateVector(BaseVector):
return
class_prefix
return
class_prefix
dataset_id
=
dataset
.
id
dataset_id
=
dataset
.
id
return
"Vector_index_"
+
dataset_id
.
replace
(
"-"
,
"_"
)
+
'_Node'
return
Dataset
.
gen_collection_name_by_id
(
dataset_id
)
def
to_index_struct
(
self
)
->
dict
:
def
to_index_struct
(
self
)
->
dict
:
return
{
return
{
...
...
api/core/tools/provider/builtin/arxiv/_assets/icon.svg
0 → 100644
View file @
c8757ef0
<svg
id=
"logomark"
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 17.732 24.269"
><g
id=
"tiny"
><path
d=
"M573.549,280.916l2.266,2.738,6.674-7.84c.353-.47.52-.717.353-1.117a1.218,1.218,0,0,0-1.061-.748h0a.953.953,0,0,0-.712.262Z"
transform=
"translate(-566.984 -271.548)"
fill=
"#bdb9b4"
/><path
d=
"M579.525,282.225l-10.606-10.174a1.413,1.413,0,0,0-.834-.5,1.09,1.09,0,0,0-1.027.66c-.167.4-.047.681.319,1.206l8.44,10.242h0l-6.282,7.716a1.336,1.336,0,0,0-.323,1.3,1.114,1.114,0,0,0,1.04.69A.992.992,0,0,0,571,293l8.519-7.92A1.924,1.924,0,0,0,579.525,282.225Z"
transform=
"translate(-566.984 -271.548)"
fill=
"#b31b1b"
/><path
d=
"M584.32,293.912l-8.525-10.275,0,0L573.53,280.9l-1.389,1.254a2.063,2.063,0,0,0,0,2.965l10.812,10.419a.925.925,0,0,0,.742.282,1.039,1.039,0,0,0,.953-.667A1.261,1.261,0,0,0,584.32,293.912Z"
transform=
"translate(-566.984 -271.548)"
fill=
"#bdb9b4"
/></g></svg>
\ No newline at end of file
api/core/tools/provider/builtin/arxiv/arxiv.py
0 → 100644
View file @
c8757ef0
from
core.tools.errors
import
ToolProviderCredentialValidationError
from
core.tools.provider.builtin.arxiv.tools.arxiv_search
import
ArxivSearchTool
from
core.tools.provider.builtin_tool_provider
import
BuiltinToolProviderController
class
ArxivProvider
(
BuiltinToolProviderController
):
def
_validate_credentials
(
self
,
credentials
:
dict
)
->
None
:
try
:
ArxivSearchTool
()
.
fork_tool_runtime
(
meta
=
{
"credentials"
:
credentials
,
}
)
.
invoke
(
user_id
=
''
,
tool_parameters
=
{
"query"
:
"John Doe"
,
},
)
except
Exception
as
e
:
raise
ToolProviderCredentialValidationError
(
str
(
e
))
\ No newline at end of file
api/core/tools/provider/builtin/arxiv/arxiv.yaml
0 → 100644
View file @
c8757ef0
identity
:
author
:
Yash Parmar
name
:
arxiv
label
:
en_US
:
ArXiv
zh_Hans
:
ArXiv
description
:
en_US
:
Access to a vast repository of scientific papers and articles in various fields of research.
zh_Hans
:
访问各个研究领域大量科学论文和文章的存储库。
icon
:
icon.svg
api/core/tools/provider/builtin/arxiv/tools/arxiv_search.py
0 → 100644
View file @
c8757ef0
from
typing
import
Any
from
langchain.utilities
import
ArxivAPIWrapper
from
pydantic
import
BaseModel
,
Field
from
core.tools.entities.tool_entities
import
ToolInvokeMessage
from
core.tools.tool.builtin_tool
import
BuiltinTool
class
ArxivSearchInput
(
BaseModel
):
query
:
str
=
Field
(
...
,
description
=
"Search query."
)
class
ArxivSearchTool
(
BuiltinTool
):
"""
A tool for searching articles on Arxiv.
"""
def
_invoke
(
self
,
user_id
:
str
,
tool_parameters
:
dict
[
str
,
Any
])
->
ToolInvokeMessage
|
list
[
ToolInvokeMessage
]:
"""
Invokes the Arxiv search tool with the given user ID and tool parameters.
Args:
user_id (str): The ID of the user invoking the tool.
tool_parameters (dict[str, Any]): The parameters for the tool, including the 'query' parameter.
Returns:
ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation, which can be a single message or a list of messages.
"""
query
=
tool_parameters
.
get
(
'query'
,
''
)
if
not
query
:
return
self
.
create_text_message
(
'Please input query'
)
arxiv
=
ArxivAPIWrapper
()
response
=
arxiv
.
run
(
query
)
return
self
.
create_text_message
(
self
.
summary
(
user_id
=
user_id
,
content
=
response
))
api/core/tools/provider/builtin/arxiv/tools/arxiv_search.yaml
0 → 100644
View file @
c8757ef0
identity
:
name
:
arxiv_search
author
:
Yash Parmar
label
:
en_US
:
Arxiv Search
zh_Hans
:
Arxiv 搜索
description
:
human
:
en_US
:
A tool for searching scientific papers and articles from the Arxiv repository. Input can be an Arxiv ID or an author's name.
zh_Hans
:
一个用于从Arxiv存储库搜索科学论文和文章的工具。 输入可以是Arxiv ID或作者姓名。
llm
:
A tool for searching scientific papers and articles from the Arxiv repository. Input can be an Arxiv ID or an author's name.
parameters
:
-
name
:
query
type
:
string
required
:
true
label
:
en_US
:
Query string
zh_Hans
:
查询字符串
human_description
:
en_US
:
The Arxiv ID or author's name used for searching.
zh_Hans
:
用于搜索的Arxiv ID或作者姓名。
llm_description
:
The Arxiv ID or author's name used for searching.
form
:
llm
api/core/tools/provider/builtin/gaode/tools/gaode_weather.py
View file @
c8757ef0
...
@@ -54,4 +54,4 @@ class GaodeRepositoriesTool(BuiltinTool):
...
@@ -54,4 +54,4 @@ class GaodeRepositoriesTool(BuiltinTool):
s
.
close
()
s
.
close
()
return
self
.
create_text_message
(
f
'No weather information for {city} was found.'
)
return
self
.
create_text_message
(
f
'No weather information for {city} was found.'
)
except
Exception
as
e
:
except
Exception
as
e
:
return
self
.
create_text_message
(
"G
ithub
API Key and Api Version is invalid. {}"
.
format
(
e
))
return
self
.
create_text_message
(
"G
aode
API Key and Api Version is invalid. {}"
.
format
(
e
))
api/core/tools/tool/api_tool.py
View file @
c8757ef0
...
@@ -67,9 +67,9 @@ class ApiTool(Tool):
...
@@ -67,9 +67,9 @@ class ApiTool(Tool):
if
'api_key_header_prefix'
in
credentials
:
if
'api_key_header_prefix'
in
credentials
:
api_key_header_prefix
=
credentials
[
'api_key_header_prefix'
]
api_key_header_prefix
=
credentials
[
'api_key_header_prefix'
]
if
api_key_header_prefix
==
'basic'
:
if
api_key_header_prefix
==
'basic'
and
credentials
[
'api_key_value'
]
:
credentials
[
'api_key_value'
]
=
f
'Basic {credentials["api_key_value"]}'
credentials
[
'api_key_value'
]
=
f
'Basic {credentials["api_key_value"]}'
elif
api_key_header_prefix
==
'bearer'
:
elif
api_key_header_prefix
==
'bearer'
and
credentials
[
'api_key_value'
]
:
credentials
[
'api_key_value'
]
=
f
'Bearer {credentials["api_key_value"]}'
credentials
[
'api_key_value'
]
=
f
'Bearer {credentials["api_key_value"]}'
elif
api_key_header_prefix
==
'custom'
:
elif
api_key_header_prefix
==
'custom'
:
pass
pass
...
@@ -184,21 +184,7 @@ class ApiTool(Tool):
...
@@ -184,21 +184,7 @@ class ApiTool(Tool):
for
name
,
property
in
properties
.
items
():
for
name
,
property
in
properties
.
items
():
if
name
in
parameters
:
if
name
in
parameters
:
# convert type
# convert type
try
:
body
[
name
]
=
self
.
_convert_body_property_type
(
property
,
parameters
[
name
])
value
=
parameters
[
name
]
if
property
[
'type'
]
==
'integer'
:
value
=
int
(
value
)
elif
property
[
'type'
]
==
'number'
:
# check if it is a float
if
'.'
in
value
:
value
=
float
(
value
)
else
:
value
=
int
(
value
)
elif
property
[
'type'
]
==
'boolean'
:
value
=
bool
(
value
)
body
[
name
]
=
value
except
ValueError
as
e
:
body
[
name
]
=
parameters
[
name
]
elif
name
in
required
:
elif
name
in
required
:
raise
ToolProviderCredentialValidationError
(
raise
ToolProviderCredentialValidationError
(
f
"Missing required parameter {name} in operation {self.api_bundle.operation_id}"
f
"Missing required parameter {name} in operation {self.api_bundle.operation_id}"
...
@@ -228,10 +214,6 @@ class ApiTool(Tool):
...
@@ -228,10 +214,6 @@ class ApiTool(Tool):
elif
method
==
'put'
:
elif
method
==
'put'
:
response
=
ssrf_proxy
.
put
(
url
,
params
=
params
,
headers
=
headers
,
cookies
=
cookies
,
data
=
body
,
timeout
=
10
,
follow_redirects
=
True
)
response
=
ssrf_proxy
.
put
(
url
,
params
=
params
,
headers
=
headers
,
cookies
=
cookies
,
data
=
body
,
timeout
=
10
,
follow_redirects
=
True
)
elif
method
==
'delete'
:
elif
method
==
'delete'
:
"""
request body data is unsupported for DELETE method in standard http protocol
however, OpenAPI 3.0 supports request body data for DELETE method, so we support it here by using requests
"""
response
=
ssrf_proxy
.
delete
(
url
,
params
=
params
,
headers
=
headers
,
cookies
=
cookies
,
data
=
body
,
timeout
=
10
,
allow_redirects
=
True
)
response
=
ssrf_proxy
.
delete
(
url
,
params
=
params
,
headers
=
headers
,
cookies
=
cookies
,
data
=
body
,
timeout
=
10
,
allow_redirects
=
True
)
elif
method
==
'patch'
:
elif
method
==
'patch'
:
response
=
ssrf_proxy
.
patch
(
url
,
params
=
params
,
headers
=
headers
,
cookies
=
cookies
,
data
=
body
,
timeout
=
10
,
follow_redirects
=
True
)
response
=
ssrf_proxy
.
patch
(
url
,
params
=
params
,
headers
=
headers
,
cookies
=
cookies
,
data
=
body
,
timeout
=
10
,
follow_redirects
=
True
)
...
@@ -243,6 +225,66 @@ class ApiTool(Tool):
...
@@ -243,6 +225,66 @@ class ApiTool(Tool):
raise
ValueError
(
f
'Invalid http method {method}'
)
raise
ValueError
(
f
'Invalid http method {method}'
)
return
response
return
response
def
_convert_body_property_any_of
(
self
,
property
:
dict
[
str
,
Any
],
value
:
Any
,
any_of
:
list
[
dict
[
str
,
Any
]],
max_recursive
=
10
)
->
Any
:
if
max_recursive
<=
0
:
raise
Exception
(
"Max recursion depth reached"
)
for
option
in
any_of
or
[]:
try
:
if
'type'
in
option
:
# Attempt to convert the value based on the type.
if
option
[
'type'
]
==
'integer'
or
option
[
'type'
]
==
'int'
:
return
int
(
value
)
elif
option
[
'type'
]
==
'number'
:
if
'.'
in
str
(
value
):
return
float
(
value
)
else
:
return
int
(
value
)
elif
option
[
'type'
]
==
'string'
:
return
str
(
value
)
elif
option
[
'type'
]
==
'boolean'
:
if
str
(
value
)
.
lower
()
in
[
'true'
,
'1'
]:
return
True
elif
str
(
value
)
.
lower
()
in
[
'false'
,
'0'
]:
return
False
else
:
continue
# Not a boolean, try next option
elif
option
[
'type'
]
==
'null'
and
not
value
:
return
None
else
:
continue
# Unsupported type, try next option
elif
'anyOf'
in
option
and
isinstance
(
option
[
'anyOf'
],
list
):
# Recursive call to handle nested anyOf
return
self
.
_convert_body_property_any_of
(
property
,
value
,
option
[
'anyOf'
],
max_recursive
-
1
)
except
ValueError
:
continue
# Conversion failed, try next option
# If no option succeeded, you might want to return the value as is or raise an error
return
value
# or raise ValueError(f"Cannot convert value '{value}' to any specified type in anyOf")
def
_convert_body_property_type
(
self
,
property
:
dict
[
str
,
Any
],
value
:
Any
)
->
Any
:
try
:
if
'type'
in
property
:
if
property
[
'type'
]
==
'integer'
or
property
[
'type'
]
==
'int'
:
return
int
(
value
)
elif
property
[
'type'
]
==
'number'
:
# check if it is a float
if
'.'
in
value
:
return
float
(
value
)
else
:
return
int
(
value
)
elif
property
[
'type'
]
==
'string'
:
return
str
(
value
)
elif
property
[
'type'
]
==
'boolean'
:
return
bool
(
value
)
elif
property
[
'type'
]
==
'null'
:
if
value
is
None
:
return
None
else
:
raise
ValueError
(
f
"Invalid type {property['type']} for property {property}"
)
elif
'anyOf'
in
property
and
isinstance
(
property
[
'anyOf'
],
list
):
return
self
.
_convert_body_property_any_of
(
property
,
value
,
property
[
'anyOf'
])
except
ValueError
as
e
:
return
value
def
_invoke
(
self
,
user_id
:
str
,
tool_parameters
:
dict
[
str
,
Any
])
->
ToolInvokeMessage
|
list
[
ToolInvokeMessage
]:
def
_invoke
(
self
,
user_id
:
str
,
tool_parameters
:
dict
[
str
,
Any
])
->
ToolInvokeMessage
|
list
[
ToolInvokeMessage
]:
"""
"""
...
...
api/core/tools/utils/parser.py
View file @
c8757ef0
...
@@ -146,7 +146,8 @@ class ApiBasedToolSchemaParser:
...
@@ -146,7 +146,8 @@ class ApiBasedToolSchemaParser:
bundles
.
append
(
ApiBasedToolBundle
(
bundles
.
append
(
ApiBasedToolBundle
(
server_url
=
server_url
+
interface
[
'path'
],
server_url
=
server_url
+
interface
[
'path'
],
method
=
interface
[
'method'
],
method
=
interface
[
'method'
],
summary
=
interface
[
'operation'
][
'summary'
]
if
'summary'
in
interface
[
'operation'
]
else
None
,
summary
=
interface
[
'operation'
][
'description'
]
if
'description'
in
interface
[
'operation'
]
else
interface
[
'operation'
][
'summary'
]
if
'summary'
in
interface
[
'operation'
]
else
None
,
operation_id
=
interface
[
'operation'
][
'operationId'
],
operation_id
=
interface
[
'operation'
][
'operationId'
],
parameters
=
parameters
,
parameters
=
parameters
,
author
=
''
,
author
=
''
,
...
@@ -249,12 +250,10 @@ class ApiBasedToolSchemaParser:
...
@@ -249,12 +250,10 @@ class ApiBasedToolSchemaParser:
if
'operationId'
not
in
operation
:
if
'operationId'
not
in
operation
:
raise
ToolApiSchemaError
(
f
'No operationId found in operation {method} {path}.'
)
raise
ToolApiSchemaError
(
f
'No operationId found in operation {method} {path}.'
)
if
'summary'
not
in
operation
or
len
(
operation
[
'summary'
])
==
0
:
if
(
'summary'
not
in
operation
or
len
(
operation
[
'summary'
])
==
0
)
and
\
warning
[
'missing_summary'
]
=
f
'No summary found in operation {method} {path}.'
(
'description'
not
in
operation
or
len
(
operation
[
'description'
])
==
0
):
warning
[
'missing_summary'
]
=
f
'No summary or description found in operation {method} {path}.'
if
'description'
not
in
operation
or
len
(
operation
[
'description'
])
==
0
:
warning
[
'missing_description'
]
=
f
'No description found in operation {method} {path}.'
openapi
[
'paths'
][
path
][
method
]
=
{
openapi
[
'paths'
][
path
][
method
]
=
{
'operationId'
:
operation
[
'operationId'
],
'operationId'
:
operation
[
'operationId'
],
'summary'
:
operation
.
get
(
'summary'
,
''
),
'summary'
:
operation
.
get
(
'summary'
,
''
),
...
...
api/models/dataset.py
View file @
c8757ef0
...
@@ -116,6 +116,10 @@ class Dataset(db.Model):
...
@@ -116,6 +116,10 @@ class Dataset(db.Model):
}
}
return
self
.
retrieval_model
if
self
.
retrieval_model
else
default_retrieval_model
return
self
.
retrieval_model
if
self
.
retrieval_model
else
default_retrieval_model
@
staticmethod
def
gen_collection_name_by_id
(
dataset_id
:
str
)
->
str
:
normalized_dataset_id
=
dataset_id
.
replace
(
"-"
,
"_"
)
return
f
'Vector_index_{normalized_dataset_id}_Node'
class
DatasetProcessRule
(
db
.
Model
):
class
DatasetProcessRule
(
db
.
Model
):
__tablename__
=
'dataset_process_rules'
__tablename__
=
'dataset_process_rules'
...
...
api/requirements.txt
View file @
c8757ef0
...
@@ -66,4 +66,5 @@ yfinance~=0.2.35
...
@@ -66,4 +66,5 @@ yfinance~=0.2.35
pydub~=0.25.1
pydub~=0.25.1
gmpy2~=2.1.5
gmpy2~=2.1.5
numexpr~=2.9.0
numexpr~=2.9.0
duckduckgo-search==4.4.3
duckduckgo-search==4.4.3
\ No newline at end of file
arxiv==2.1.0
\ No newline at end of file
api/services/dataset_service.py
View file @
c8757ef0
...
@@ -1255,7 +1255,7 @@ class DatasetCollectionBindingService:
...
@@ -1255,7 +1255,7 @@ class DatasetCollectionBindingService:
dataset_collection_binding
=
DatasetCollectionBinding
(
dataset_collection_binding
=
DatasetCollectionBinding
(
provider_name
=
provider_name
,
provider_name
=
provider_name
,
model_name
=
model_name
,
model_name
=
model_name
,
collection_name
=
"Vector_index_"
+
str
(
uuid
.
uuid4
())
.
replace
(
"-"
,
"_"
)
+
'_Node'
,
collection_name
=
Dataset
.
gen_collection_name_by_id
(
str
(
uuid
.
uuid4
()))
,
type
=
collection_type
type
=
collection_type
)
)
db
.
session
.
add
(
dataset_collection_binding
)
db
.
session
.
add
(
dataset_collection_binding
)
...
...
api/services/tools_manage_service.py
View file @
c8757ef0
...
@@ -209,8 +209,8 @@ class ToolManageService:
...
@@ -209,8 +209,8 @@ class ToolManageService:
# extra info like description will be set here
# extra info like description will be set here
tool_bundles
,
schema_type
=
ToolManageService
.
convert_schema_to_tool_bundles
(
schema
,
extra_info
)
tool_bundles
,
schema_type
=
ToolManageService
.
convert_schema_to_tool_bundles
(
schema
,
extra_info
)
if
len
(
tool_bundles
)
>
10
:
if
len
(
tool_bundles
)
>
10
0
:
raise
ValueError
(
'the number of apis should be less than 10'
)
raise
ValueError
(
'the number of apis should be less than 10
0
'
)
# create db provider
# create db provider
db_provider
=
ApiToolProvider
(
db_provider
=
ApiToolProvider
(
...
...
web/app/components/app/configuration/config/agent/agent-tools/index.tsx
View file @
c8757ef0
...
@@ -153,18 +153,18 @@ const AgentTools: FC = () => {
...
@@ -153,18 +153,18 @@ const AgentTools: FC = () => {
)
)
:
(
:
(
<
div
className=
'hidden group-hover:flex items-center'
>
<
div
className=
'hidden group-hover:flex items-center'
>
{
item
.
provider_type
===
CollectionType
.
builtIn
&&
(
{
/* {item.provider_type === CollectionType.builtIn && ( */
}
<
TooltipPlus
<
TooltipPlus
popupContent=
{
t
(
'tools.setBuiltInTools.infoAndSetting'
)
}
popupContent=
{
t
(
'tools.setBuiltInTools.infoAndSetting'
)
}
>
>
<
div
className=
'mr-1 p-1 rounded-md hover:bg-black/5 cursor-pointer'
onClick=
{
()
=>
{
<
div
className=
'mr-1 p-1 rounded-md hover:bg-black/5 cursor-pointer'
onClick=
{
()
=>
{
setCurrentTool
(
item
)
setCurrentTool
(
item
)
setIsShowSettingTool
(
true
)
setIsShowSettingTool
(
true
)
}
}
>
}
}
>
<
InfoCircle
className=
'w-4 h-4 text-gray-500'
/>
<
InfoCircle
className=
'w-4 h-4 text-gray-500'
/>
</
div
>
</
div
>
</
TooltipPlus
>
</
TooltipPlus
>
)
}
{
/* )} */
}
<
div
className=
'p-1 rounded-md hover:bg-black/5 cursor-pointer'
onClick=
{
()
=>
{
<
div
className=
'p-1 rounded-md hover:bg-black/5 cursor-pointer'
onClick=
{
()
=>
{
const
newModelConfig
=
produce
(
modelConfig
,
(
draft
)
=>
{
const
newModelConfig
=
produce
(
modelConfig
,
(
draft
)
=>
{
...
@@ -209,6 +209,7 @@ const AgentTools: FC = () => {
...
@@ -209,6 +209,7 @@ const AgentTools: FC = () => {
toolName=
{
currentTool
?.
tool_name
as
string
}
toolName=
{
currentTool
?.
tool_name
as
string
}
setting=
{
currentTool
?.
tool_parameters
as
any
}
setting=
{
currentTool
?.
tool_parameters
as
any
}
collection=
{
currentTool
?.
collection
as
Collection
}
collection=
{
currentTool
?.
collection
as
Collection
}
isBuiltIn=
{
currentTool
?.
collection
?.
type
===
CollectionType
.
builtIn
}
onSave=
{
handleToolSettingChange
}
onSave=
{
handleToolSettingChange
}
onHide=
{
()
=>
setIsShowSettingTool
(
false
)
}
onHide=
{
()
=>
setIsShowSettingTool
(
false
)
}
/>)
/>)
...
...
web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx
View file @
c8757ef0
...
@@ -8,14 +8,17 @@ import Drawer from '@/app/components/base/drawer-plus'
...
@@ -8,14 +8,17 @@ import Drawer from '@/app/components/base/drawer-plus'
import
Form
from
'@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import
Form
from
'@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import
{
addDefaultValue
,
toolParametersToFormSchemas
}
from
'@/app/components/tools/utils/to-form-schema'
import
{
addDefaultValue
,
toolParametersToFormSchemas
}
from
'@/app/components/tools/utils/to-form-schema'
import
type
{
Collection
,
Tool
}
from
'@/app/components/tools/types'
import
type
{
Collection
,
Tool
}
from
'@/app/components/tools/types'
import
{
fetchBuiltInToolList
}
from
'@/service/tools'
import
{
fetchBuiltInToolList
,
fetchCustomToolList
}
from
'@/service/tools'
import
I18n
from
'@/context/i18n'
import
I18n
from
'@/context/i18n'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
Loading
from
'@/app/components/base/loading'
import
Loading
from
'@/app/components/base/loading'
import
{
DiagonalDividingLine
}
from
'@/app/components/base/icons/src/public/common'
import
{
DiagonalDividingLine
}
from
'@/app/components/base/icons/src/public/common'
import
{
getLanguage
}
from
'@/i18n/language'
import
{
getLanguage
}
from
'@/i18n/language'
import
AppIcon
from
'@/app/components/base/app-icon'
type
Props
=
{
type
Props
=
{
collection
:
Collection
collection
:
Collection
isBuiltIn
?:
boolean
toolName
:
string
toolName
:
string
setting
?:
Record
<
string
,
any
>
setting
?:
Record
<
string
,
any
>
readonly
?:
boolean
readonly
?:
boolean
...
@@ -25,6 +28,7 @@ type Props = {
...
@@ -25,6 +28,7 @@ type Props = {
const
SettingBuiltInTool
:
FC
<
Props
>
=
({
const
SettingBuiltInTool
:
FC
<
Props
>
=
({
collection
,
collection
,
isBuiltIn
=
true
,
toolName
,
toolName
,
setting
=
{},
setting
=
{},
readonly
,
readonly
,
...
@@ -52,7 +56,7 @@ const SettingBuiltInTool: FC<Props> = ({
...
@@ -52,7 +56,7 @@ const SettingBuiltInTool: FC<Props> = ({
(
async
()
=>
{
(
async
()
=>
{
setIsLoading
(
true
)
setIsLoading
(
true
)
try
{
try
{
const
list
=
await
fetchBuiltIn
ToolList
(
collection
.
name
)
const
list
=
isBuiltIn
?
await
fetchBuiltInToolList
(
collection
.
name
)
:
await
fetchCustom
ToolList
(
collection
.
name
)
setTools
(
list
)
setTools
(
list
)
const
currTool
=
list
.
find
(
tool
=>
tool
.
name
===
toolName
)
const
currTool
=
list
.
find
(
tool
=>
tool
.
name
===
toolName
)
if
(
currTool
)
{
if
(
currTool
)
{
...
@@ -135,12 +139,24 @@ const SettingBuiltInTool: FC<Props> = ({
...
@@ -135,12 +139,24 @@ const SettingBuiltInTool: FC<Props> = ({
onHide=
{
onHide
}
onHide=
{
onHide
}
title=
{
(
title=
{
(
<
div
className=
'flex'
>
<
div
className=
'flex'
>
<
div
{
collection
.
icon
===
'string'
className=
'w-6 h-6 bg-cover bg-center rounded-md'
?
(
style=
{
{
<
div
backgroundImage
:
`url(${collection.icon})`
,
className=
'w-6 h-6 bg-cover bg-center rounded-md'
}
}
style=
{
{
></
div
>
backgroundImage
:
`url(${collection.icon})`
,
}
}
></
div
>
)
:
(
<
AppIcon
className=
'rounded-md'
size=
'tiny'
icon=
{
(
collection
.
icon
as
any
)?.
content
}
background=
{
(
collection
.
icon
as
any
)?.
background
}
/>
)
}
<
div
className=
'ml-2 leading-6 text-base font-semibold text-gray-900'
>
{
currTool
?.
label
[
language
]
}
</
div
>
<
div
className=
'ml-2 leading-6 text-base font-semibold text-gray-900'
>
{
currTool
?.
label
[
language
]
}
</
div
>
{
(
hasSetting
&&
!
readonly
)
&&
(<>
{
(
hasSetting
&&
!
readonly
)
&&
(<>
<
DiagonalDividingLine
className=
'mx-4'
/>
<
DiagonalDividingLine
className=
'mx-4'
/>
...
...
web/app/components/explore/app-list/index.tsx
View file @
c8757ef0
...
@@ -26,7 +26,8 @@ const Apps: FC = () => {
...
@@ -26,7 +26,8 @@ const Apps: FC = () => {
const
{
isCurrentWorkspaceManager
}
=
useAppContext
()
const
{
isCurrentWorkspaceManager
}
=
useAppContext
()
const
router
=
useRouter
()
const
router
=
useRouter
()
const
{
hasEditPermission
}
=
useContext
(
ExploreContext
)
const
{
hasEditPermission
}
=
useContext
(
ExploreContext
)
const
allCategoriesEn
=
t
(
'explore.apps.allCategories'
)
const
allCategoriesEn
=
t
(
'explore.apps.allCategories'
,
{
lng
:
'en'
})
const
[
currCategory
,
setCurrCategory
]
=
useTabSearchParams
({
const
[
currCategory
,
setCurrCategory
]
=
useTabSearchParams
({
defaultTab
:
allCategoriesEn
,
defaultTab
:
allCategoriesEn
,
})
})
...
@@ -47,11 +48,10 @@ const Apps: FC = () => {
...
@@ -47,11 +48,10 @@ const Apps: FC = () => {
},
},
)
)
const
currList
=
(()
=>
{
const
currList
if
(
currCategory
===
allCategoriesEn
)
=
currCategory
===
allCategoriesEn
return
allList
?
allList
return
allList
.
filter
(
item
=>
item
.
category
===
currCategory
)
:
allList
.
filter
(
item
=>
item
.
category
===
currCategory
)
})()
const
[
currApp
,
setCurrApp
]
=
React
.
useState
<
App
|
null
>
(
null
)
const
[
currApp
,
setCurrApp
]
=
React
.
useState
<
App
|
null
>
(
null
)
const
[
isShowCreateModal
,
setIsShowCreateModal
]
=
React
.
useState
(
false
)
const
[
isShowCreateModal
,
setIsShowCreateModal
]
=
React
.
useState
(
false
)
...
@@ -112,6 +112,7 @@ const Apps: FC = () => {
...
@@ -112,6 +112,7 @@ const Apps: FC = () => {
list=
{
categories
}
list=
{
categories
}
value=
{
currCategory
}
value=
{
currCategory
}
onChange=
{
setCurrCategory
}
onChange=
{
setCurrCategory
}
allCategoriesEn=
{
allCategoriesEn
}
/>
/>
<
div
className=
"relative flex flex-1 mt-6 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow"
>
<
div
className=
"relative flex flex-1 mt-6 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow"
>
<
nav
<
nav
...
...
web/app/components/explore/category.tsx
View file @
c8757ef0
...
@@ -12,7 +12,11 @@ export type ICategoryProps = {
...
@@ -12,7 +12,11 @@ export type ICategoryProps = {
className
?:
string
className
?:
string
list
:
AppCategory
[]
list
:
AppCategory
[]
value
:
string
value
:
string
onChange
:
(
value
:
AppCategory
|
''
)
=>
void
onChange
:
(
value
:
AppCategory
|
string
)
=>
void
/**
* default value for searchparam 'category' in en
*/
allCategoriesEn
:
string
}
}
const
Category
:
FC
<
ICategoryProps
>
=
({
const
Category
:
FC
<
ICategoryProps
>
=
({
...
@@ -20,17 +24,24 @@ const Category: FC<ICategoryProps> = ({
...
@@ -20,17 +24,24 @@ const Category: FC<ICategoryProps> = ({
list
,
list
,
value
,
value
,
onChange
,
onChange
,
allCategoriesEn
,
})
=>
{
})
=>
{
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
const
isAllCategories
=
!
list
.
includes
(
value
)
const
itemClassName
=
(
isSelected
:
boolean
)
=>
cn
(
isSelected
?
'bg-white text-primary-600 border-gray-200 font-semibold shadow-[0px_1px_2px_rgba(16,24,40,0.05)]'
:
'border-transparent font-medium'
,
'flex items-center h-7 px-3 border cursor-pointer rounded-lg'
,
)
const
itemClassName
=
(
isSelected
:
boolean
)
=>
cn
(
isSelected
?
'bg-white text-primary-600 border-gray-200 font-semibold'
:
'border-transparent font-medium'
,
'flex items-center h-7 px-3 border cursor-pointer rounded-lg'
)
const
itemStyle
=
(
isSelected
:
boolean
)
=>
isSelected
?
{
boxShadow
:
'0px 1px 2px rgba(16, 24, 40, 0.05)'
}
:
{}
return
(
return
(
<
div
className=
{
cn
(
className
,
'flex space-x-1 text-[13px] flex-wrap'
)
}
>
<
div
className=
{
cn
(
className
,
'flex space-x-1 text-[13px] flex-wrap'
)
}
>
<
div
<
div
className=
{
itemClassName
(
value
===
''
)
}
className=
{
itemClassName
(
isAllCategories
)
}
style=
{
itemStyle
(
value
===
''
)
}
onClick=
{
()
=>
onChange
(
allCategoriesEn
)
}
onClick=
{
()
=>
onChange
(
''
)
}
>
>
{
t
(
'explore.apps.allCategories'
)
}
{
t
(
'explore.apps.allCategories'
)
}
</
div
>
</
div
>
...
@@ -38,7 +49,6 @@ const Category: FC<ICategoryProps> = ({
...
@@ -38,7 +49,6 @@ const Category: FC<ICategoryProps> = ({
<
div
<
div
key=
{
name
}
key=
{
name
}
className=
{
itemClassName
(
name
===
value
)
}
className=
{
itemClassName
(
name
===
value
)
}
style=
{
itemStyle
(
name
===
value
)
}
onClick=
{
()
=>
onChange
(
name
)
}
onClick=
{
()
=>
onChange
(
name
)
}
>
>
{
categoryI18n
[
name
]
?
t
(
`explore.category.${name}`
)
:
name
}
{
categoryI18n
[
name
]
?
t
(
`explore.category.${name}`
)
:
name
}
...
@@ -47,4 +57,5 @@ const Category: FC<ICategoryProps> = ({
...
@@ -47,4 +57,5 @@ const Category: FC<ICategoryProps> = ({
</
div
>
</
div
>
)
)
}
}
export
default
React
.
memo
(
Category
)
export
default
React
.
memo
(
Category
)
web/app/components/share/chat/welcome/massive-component.tsx
View file @
c8757ef0
...
@@ -23,9 +23,9 @@ export const AppInfo: FC<{ siteInfo: SiteInfo }> = ({ siteInfo }) => {
...
@@ -23,9 +23,9 @@ export const AppInfo: FC<{ siteInfo: SiteInfo }> = ({ siteInfo }) => {
export
const
PromptTemplate
:
FC
<
{
html
:
string
}
>
=
({
html
})
=>
{
export
const
PromptTemplate
:
FC
<
{
html
:
string
}
>
=
({
html
})
=>
{
return
(
return
(
<
div
<
div
className=
{
'
box-border text-sm text-gray-700'
}
className=
{
'box-border text-sm text-gray-700'
}
dangerouslySetInnerHTML=
{
{
__html
:
html
}
}
dangerouslySetInnerHTML=
{
{
__html
:
html
}
}
></
div
>
/
>
)
)
}
}
...
...
web/app/components/tools/edit-custom-collection-modal/index.tsx
View file @
c8757ef0
...
@@ -118,6 +118,13 @@ const EditCustomCollectionModal: FC<Props> = ({
...
@@ -118,6 +118,13 @@ const EditCustomCollectionModal: FC<Props> = ({
const
handleSave
=
()
=>
{
const
handleSave
=
()
=>
{
const
postData
=
clone
(
customCollection
)
const
postData
=
clone
(
customCollection
)
delete
postData
.
tools
delete
postData
.
tools
if
(
postData
.
credentials
.
auth_type
===
AuthType
.
none
)
{
delete
postData
.
credentials
.
api_key_header
delete
postData
.
credentials
.
api_key_header_prefix
delete
postData
.
credentials
.
api_key_value
}
if
(
isAdd
)
{
if
(
isAdd
)
{
onAdd
?.(
postData
)
onAdd
?.(
postData
)
return
return
...
...
web/app/components/tools/edit-custom-collection-modal/test-api.tsx
View file @
c8757ef0
...
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
...
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
import
{
useContext
}
from
'use-context-selector'
import
{
useContext
}
from
'use-context-selector'
import
{
Settings01
}
from
'../../base/icons/src/vender/line/general'
import
{
Settings01
}
from
'../../base/icons/src/vender/line/general'
import
ConfigCredentials
from
'./config-credentials'
import
ConfigCredentials
from
'./config-credentials'
import
type
{
Credential
,
CustomCollectionBackend
,
CustomParamSchema
}
from
'@/app/components/tools/types'
import
{
AuthType
,
type
Credential
,
type
CustomCollectionBackend
,
type
CustomParamSchema
}
from
'@/app/components/tools/types'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
Drawer
from
'@/app/components/base/drawer-plus'
import
Drawer
from
'@/app/components/base/drawer-plus'
import
I18n
from
'@/context/i18n'
import
I18n
from
'@/context/i18n'
...
@@ -34,9 +34,16 @@ const TestApi: FC<Props> = ({
...
@@ -34,9 +34,16 @@ const TestApi: FC<Props> = ({
const
{
operation_id
:
toolName
,
parameters
}
=
tool
const
{
operation_id
:
toolName
,
parameters
}
=
tool
const
[
parametersValue
,
setParametersValue
]
=
useState
<
Record
<
string
,
string
>>
({})
const
[
parametersValue
,
setParametersValue
]
=
useState
<
Record
<
string
,
string
>>
({})
const
handleTest
=
async
()
=>
{
const
handleTest
=
async
()
=>
{
// clone test schema
const
credentials
=
JSON
.
parse
(
JSON
.
stringify
(
tempCredential
))
as
Credential
if
(
credentials
.
auth_type
===
AuthType
.
none
)
{
delete
credentials
.
api_key_header_prefix
delete
credentials
.
api_key_header
delete
credentials
.
api_key_value
}
const
data
=
{
const
data
=
{
tool_name
:
toolName
,
tool_name
:
toolName
,
credentials
:
tempCredential
,
credentials
,
schema_type
:
customCollection
.
schema_type
,
schema_type
:
customCollection
.
schema_type
,
schema
:
customCollection
.
schema
,
schema
:
customCollection
.
schema
,
parameters
:
parametersValue
,
parameters
:
parametersValue
,
...
...
web/app/components/tools/index.tsx
View file @
c8757ef0
...
@@ -69,9 +69,22 @@ const Tools: FC<Props> = ({
...
@@ -69,9 +69,22 @@ const Tools: FC<Props> = ({
})()
})()
const
[
query
,
setQuery
]
=
useState
(
''
)
const
[
query
,
setQuery
]
=
useState
(
''
)
const
[
collectionType
,
set
CollectionType
]
=
useTabSearchParams
({
const
[
toolPageCollectionType
,
setToolPage
CollectionType
]
=
useTabSearchParams
({
defaultTab
:
collectionTypeOptions
[
0
].
value
,
defaultTab
:
collectionTypeOptions
[
0
].
value
,
})
})
const
[
appPageCollectionType
,
setAppPageCollectionType
]
=
useState
(
collectionTypeOptions
[
0
].
value
)
const
{
collectionType
,
setCollectionType
}
=
(()
=>
{
if
(
isInToolsPage
)
{
return
{
collectionType
:
toolPageCollectionType
,
setCollectionType
:
setToolPageCollectionType
,
}
}
return
{
collectionType
:
appPageCollectionType
,
setCollectionType
:
setAppPageCollectionType
,
}
})()
const
showCollectionList
=
(()
=>
{
const
showCollectionList
=
(()
=>
{
let
typeFilteredList
:
Collection
[]
=
[]
let
typeFilteredList
:
Collection
[]
=
[]
...
...
web/app/components/tools/tool-list/item.tsx
View file @
c8757ef0
...
@@ -35,9 +35,10 @@ const Item: FC<Props> = ({
...
@@ -35,9 +35,10 @@ const Item: FC<Props> = ({
const
language
=
getLanguage
(
locale
)
const
language
=
getLanguage
(
locale
)
const
isBuiltIn
=
collection
.
type
===
CollectionType
.
builtIn
const
isBuiltIn
=
collection
.
type
===
CollectionType
.
builtIn
const
canShowDetail
=
!
isBuiltIn
||
(
isBuiltIn
&&
isInToolsPage
)
const
canShowDetail
=
isInToolsPage
const
[
showDetail
,
setShowDetail
]
=
useState
(
false
)
const
[
showDetail
,
setShowDetail
]
=
useState
(
false
)
const
addBtn
=
<
Button
className=
'shrink-0 flex items-center h-7 !px-3 !text-xs !font-medium !text-gray-700'
disabled=
{
added
||
!
collection
.
is_team_authorization
}
onClick=
{
()
=>
onAdd
?.(
payload
)
}
>
{
t
(
`common.operation.${added ? 'added' : 'add'}`
)
}
</
Button
>
const
addBtn
=
<
Button
className=
'shrink-0 flex items-center h-7 !px-3 !text-xs !font-medium !text-gray-700'
disabled=
{
added
||
!
collection
.
is_team_authorization
}
onClick=
{
()
=>
onAdd
?.(
payload
)
}
>
{
t
(
`common.operation.${added ? 'added' : 'add'}`
)
}
</
Button
>
return
(
return
(
<>
<>
<
div
<
div
...
@@ -63,7 +64,7 @@ const Item: FC<Props> = ({
...
@@ -63,7 +64,7 @@ const Item: FC<Props> = ({
)
}
)
}
</
div
>
</
div
>
</
div
>
</
div
>
{
showDetail
&&
isBuiltIn
&&
(
{
showDetail
&&
(
<
SettingBuiltInTool
<
SettingBuiltInTool
collection=
{
collection
}
collection=
{
collection
}
toolName=
{
payload
.
name
}
toolName=
{
payload
.
name
}
...
@@ -71,6 +72,7 @@ const Item: FC<Props> = ({
...
@@ -71,6 +72,7 @@ const Item: FC<Props> = ({
onHide=
{
()
=>
{
onHide=
{
()
=>
{
setShowDetail
(
false
)
setShowDetail
(
false
)
}
}
}
}
isBuiltIn=
{
isBuiltIn
}
/>
/>
)
}
)
}
</>
</>
...
...
web/test/mocks/categories.ts
0 → 100644
View file @
c8757ef0
// TODO: maybe use faker.js to randomize the data
export
const
mockApps
=
{
recommended_apps
:
[
{
app
:
{
id
:
'b82da4c0-2887-48cc-a7d6-7edc0bdd6002'
,
name
:
'AI 前端面试官'
,
mode
:
'chat'
,
icon
:
'🤖'
,
icon_background
:
null
,
},
app_id
:
'b82da4c0-2887-48cc-a7d6-7edc0bdd6002'
,
description
:
'一个模拟的前端面试官,通过提问的方式对前端开发的技能水平进行检验。'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'HR'
,
position
:
20
,
is_listed
:
true
,
install_count
:
0
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'1fa25f89-2883-41ac-877e-c372274020a4'
,
name
:
'扁平风插画生成'
,
mode
:
'chat'
,
icon
:
'🖼️'
,
icon_background
:
'#D5F5F6'
,
},
app_id
:
'1fa25f89-2883-41ac-877e-c372274020a4'
,
description
:
'输入相关元素,为你生成扁平插画风格的封面图片'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'绘画'
,
position
:
10
,
is_listed
:
true
,
install_count
:
0
,
installed
:
false
,
editable
:
true
,
is_agent
:
true
,
},
{
app
:
{
id
:
'94b509ad-4225-4924-8b50-5c25c2bd7e3c'
,
name
:
'文章翻译助理 '
,
mode
:
'completion'
,
icon
:
'🤖'
,
icon_background
:
null
,
},
app_id
:
'94b509ad-4225-4924-8b50-5c25c2bd7e3c'
,
description
:
'一个多语言翻译器,提供多种语言翻译能力,输入你需要翻译的文本,选择目标语言即可。提示词来自宝玉。'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'Assistant'
,
position
:
10
,
is_listed
:
true
,
install_count
:
0
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'c8003ab3-9bb7-4693-9249-e603d48e58a6'
,
name
:
'SQL 生成器'
,
mode
:
'completion'
,
icon
:
'🤖'
,
icon_background
:
null
,
},
app_id
:
'c8003ab3-9bb7-4693-9249-e603d48e58a6'
,
description
:
'我将帮助你把自然语言转化成指定的数据库查询 SQL 语句,请在下方输入你需要查询的条件,并选择目标数据库类型。'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'Programming'
,
position
:
12
,
is_listed
:
true
,
install_count
:
3142
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'dad6a1e0-0fe9-47e1-91a9-e16de48f1276'
,
name
:
'代码解释器'
,
mode
:
'chat'
,
icon
:
'eye-in-speech-bubble'
,
icon_background
:
'#FFEAD5'
,
},
app_id
:
'dad6a1e0-0fe9-47e1-91a9-e16de48f1276'
,
description
:
'阐明代码的语法和语义。'
,
copyright
:
'Copyright 2023 Dify'
,
privacy_policy
:
'https://dify.ai'
,
category
:
'Programming'
,
position
:
2
,
is_listed
:
true
,
install_count
:
2344
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'fae3e7ac-8ccc-4d43-8986-7c61d2bdde4f'
,
name
:
'赛博朋克插画生成'
,
mode
:
'chat'
,
icon
:
'🖼️'
,
icon_background
:
'#FFEAD5'
,
},
app_id
:
'fae3e7ac-8ccc-4d43-8986-7c61d2bdde4f'
,
description
:
'输入相关元素,为你生成赛博朋克风格的插画'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'绘画'
,
position
:
10
,
is_listed
:
true
,
install_count
:
0
,
installed
:
false
,
editable
:
true
,
is_agent
:
true
,
},
{
app
:
{
id
:
'4e57bc83-ab95-4f8a-a955-70796b4804a0'
,
name
:
'SEO 文章生成专家'
,
mode
:
'completion'
,
icon
:
'🤖'
,
icon_background
:
'#FFEAD5'
,
},
app_id
:
'4e57bc83-ab95-4f8a-a955-70796b4804a0'
,
description
:
'我是一名SEO专家,可以根据您提供的标题、关键词、相关信息来批量生成SEO文章。'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'Assistant'
,
position
:
10
,
is_listed
:
true
,
install_count
:
0
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'6786ce62-fa85-4ea7-a4d1-5dbe3e3ff59f'
,
name
:
'会议纪要'
,
mode
:
'chat'
,
icon
:
'clipboard'
,
icon_background
:
'#D1E0FF'
,
},
app_id
:
'6786ce62-fa85-4ea7-a4d1-5dbe3e3ff59f'
,
description
:
'帮你重新组织和输出混乱复杂的会议纪要。'
,
copyright
:
'Copyright 2023 Dify'
,
privacy_policy
:
'https://dify.ai'
,
category
:
'Writing'
,
position
:
6
,
is_listed
:
true
,
install_count
:
1542
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'73dd96bb-49b7-4791-acbd-9ef2ef506900'
,
name
:
'美股投资分析助手'
,
mode
:
'chat'
,
icon
:
'🤑'
,
icon_background
:
'#E4FBCC'
,
},
app_id
:
'73dd96bb-49b7-4791-acbd-9ef2ef506900'
,
description
:
'欢迎使用您的个性化美股投资分析助手,在这里我们深入的进行股票分析,为您提供全面的洞察。'
,
copyright
:
'Dify.AI'
,
privacy_policy
:
null
,
category
:
'智能助理'
,
position
:
0
,
is_listed
:
true
,
install_count
:
2
,
installed
:
false
,
editable
:
true
,
is_agent
:
true
,
},
{
app
:
{
id
:
'93ca3c2c-3a47-4658-b230-d5a6cc61ff01'
,
name
:
'SVG Logo 设计'
,
mode
:
'chat'
,
icon
:
'🎨'
,
icon_background
:
'#E4FBCC'
,
},
app_id
:
'93ca3c2c-3a47-4658-b230-d5a6cc61ff01'
,
description
:
'您好,我是您的创意伙伴,将帮助您将想法生动地实现!我可以协助您利用DALL·E 3的能力创造出令人惊叹的设计。'
,
copyright
:
'Dify.AI'
,
privacy_policy
:
null
,
category
:
'智能助理'
,
position
:
4
,
is_listed
:
true
,
install_count
:
6
,
installed
:
false
,
editable
:
true
,
is_agent
:
true
,
},
{
app
:
{
id
:
'59924f26-963f-4b4b-90cf-978bbfcddc49'
,
name
:
'中英文互译'
,
mode
:
'chat'
,
icon
:
'speaking_head_in_silhouette'
,
icon_background
:
'#FBE8FF'
,
},
app_id
:
'59924f26-963f-4b4b-90cf-978bbfcddc49'
,
description
:
'翻译专家:提供中英文互译'
,
copyright
:
'Copyright 2023 Dify'
,
privacy_policy
:
'https://dify.ai'
,
category
:
'Translate'
,
position
:
4
,
is_listed
:
true
,
install_count
:
1662
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'89ad1e65-6711-4c80-b469-a71a434e2dbd'
,
name
:
'个人学习导师'
,
mode
:
'chat'
,
icon
:
'🤖'
,
icon_background
:
'#FFEAD5'
,
},
app_id
:
'89ad1e65-6711-4c80-b469-a71a434e2dbd'
,
description
:
'您的私人学习导师,帮您制定学习计划并辅导'
,
copyright
:
'Copyright 2023 Dify'
,
privacy_policy
:
'https://dify.ai'
,
category
:
'Assistant'
,
position
:
26
,
is_listed
:
true
,
install_count
:
1441
,
installed
:
true
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'ff551444-a3ff-4fd8-b297-f38581c98b4a'
,
name
:
'文献综述写作'
,
mode
:
'completion'
,
icon
:
'female-student'
,
icon_background
:
'#FBE8FF'
,
},
app_id
:
'ff551444-a3ff-4fd8-b297-f38581c98b4a'
,
description
:
'帮你撰写论文文献综述'
,
copyright
:
'Copyright 2023 Dify'
,
privacy_policy
:
'https://dify.ai'
,
category
:
'Writing'
,
position
:
7
,
is_listed
:
true
,
install_count
:
1651
,
installed
:
false
,
editable
:
true
,
is_agent
:
false
,
},
{
app
:
{
id
:
'79227a52-11f1-4cf9-8c49-0bd86f9be813'
,
name
:
'Youtube 频道数据分析'
,
mode
:
'chat'
,
icon
:
'🔢'
,
icon_background
:
'#E4FBCC'
,
},
app_id
:
'79227a52-11f1-4cf9-8c49-0bd86f9be813'
,
description
:
'你好,告诉我您想分析的 YouTube 频道,我将为您整理一份完整的数据分析报告。'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'智能助理'
,
position
:
0
,
is_listed
:
true
,
install_count
:
2
,
installed
:
false
,
editable
:
true
,
is_agent
:
true
,
},
{
app
:
{
id
:
'609f4a7f-36f7-4791-96a7-4ccbe6f8dfbb'
,
name
:
'旅行规划助手'
,
mode
:
'chat'
,
icon
:
'✈️'
,
icon_background
:
'#E4FBCC'
,
},
app_id
:
'609f4a7f-36f7-4791-96a7-4ccbe6f8dfbb'
,
description
:
'欢迎使用您的个性化旅行服务顾问!🌍✈️ 准备好踏上一段充满冒险与放松的旅程了吗?让我们一起深入打造您难忘的旅行体验吧。'
,
copyright
:
null
,
privacy_policy
:
null
,
category
:
'智能助理'
,
position
:
0
,
is_listed
:
true
,
install_count
:
3
,
installed
:
false
,
editable
:
true
,
is_agent
:
true
,
},
],
categories
:
[
'绘画'
,
'HR'
,
'Programming'
,
'Translate'
,
'Assistant'
,
'Writing'
,
'智能助理'
,
],
}
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