mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-04-23 14:19:42 +08:00

Signed-off-by: yihong0618 <zouzou0208@gmail.com> Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: xhe <xw897002528@gmail.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: takatost <takatost@gmail.com> Co-authored-by: kurokobo <kuro664@gmail.com> Co-authored-by: Novice Lee <novicelee@NoviPro.local> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: AkaraChen <akarachen@outlook.com> Co-authored-by: Yi <yxiaoisme@gmail.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com> Co-authored-by: twwu <twwu@dify.ai> Co-authored-by: Hiroshi Fujita <fujita-h@users.noreply.github.com> Co-authored-by: AkaraChen <85140972+AkaraChen@users.noreply.github.com> Co-authored-by: NFish <douxc512@gmail.com> Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: Novice <857526207@qq.com> Co-authored-by: Hiroki Nagai <82458324+nagaihiroki-git@users.noreply.github.com> Co-authored-by: Gen Sato <52241300+halogen22@users.noreply.github.com> Co-authored-by: eux <euxuuu@gmail.com> Co-authored-by: huangzhuo1949 <167434202+huangzhuo1949@users.noreply.github.com> Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com> Co-authored-by: lotsik <lotsik@mail.ru> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: nite-knite <nkCoding@gmail.com> Co-authored-by: Jyong <76649700+JohnJyong@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: gakkiyomi <gakkiyomi@aliyun.com> Co-authored-by: CN-P5 <heibai2006@gmail.com> Co-authored-by: CN-P5 <heibai2006@qq.com> Co-authored-by: Chuehnone <1897025+chuehnone@users.noreply.github.com> Co-authored-by: yihong <zouzou0208@gmail.com> Co-authored-by: Kevin9703 <51311316+Kevin9703@users.noreply.github.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Boris Feld <lothiraldan@gmail.com> Co-authored-by: mbo <himabo@gmail.com> Co-authored-by: mabo <mabo@aeyes.ai> Co-authored-by: Warren Chen <warren.chen830@gmail.com> Co-authored-by: JzoNgKVO <27049666+JzoNgKVO@users.noreply.github.com> Co-authored-by: jiandanfeng <chenjh3@wangsu.com> Co-authored-by: zhu-an <70234959+xhdd123321@users.noreply.github.com> Co-authored-by: zhaoqingyu.1075 <zhaoqingyu.1075@bytedance.com> Co-authored-by: 海狸大師 <86974027+yenslife@users.noreply.github.com> Co-authored-by: Xu Song <xusong.vip@gmail.com> Co-authored-by: rayshaw001 <396301947@163.com> Co-authored-by: Ding Jiatong <dingjiatong@gmail.com> Co-authored-by: Bowen Liang <liangbowen@gf.com.cn> Co-authored-by: JasonVV <jasonwangiii@outlook.com> Co-authored-by: le0zh <newlight@qq.com> Co-authored-by: zhuxinliang <zhuxinliang@didiglobal.com> Co-authored-by: k-zaku <zaku99@outlook.jp> Co-authored-by: luckylhb90 <luckylhb90@gmail.com> Co-authored-by: hobo.l <hobo.l@binance.com> Co-authored-by: jiangbo721 <365065261@qq.com> Co-authored-by: 刘江波 <jiangbo721@163.com> Co-authored-by: Shun Miyazawa <34241526+miya@users.noreply.github.com> Co-authored-by: EricPan <30651140+Egfly@users.noreply.github.com> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: sino <sino2322@gmail.com> Co-authored-by: Jhvcc <37662342+Jhvcc@users.noreply.github.com> Co-authored-by: lowell <lowell.hu@zkteco.in> Co-authored-by: Boris Polonsky <BorisPolonsky@users.noreply.github.com> Co-authored-by: Ademílson Tonato <ademilsonft@outlook.com> Co-authored-by: Ademílson Tonato <ademilson.tonato@refurbed.com> Co-authored-by: IWAI, Masaharu <iwaim.sub@gmail.com> Co-authored-by: Yueh-Po Peng (Yabi) <94939112+y10ab1@users.noreply.github.com> Co-authored-by: Jason <ggbbddjm@gmail.com> Co-authored-by: Xin Zhang <sjhpzx@gmail.com> Co-authored-by: yjc980121 <3898524+yjc980121@users.noreply.github.com> Co-authored-by: heyszt <36215648+hieheihei@users.noreply.github.com> Co-authored-by: Abdullah AlOsaimi <osaimiacc@gmail.com> Co-authored-by: Abdullah AlOsaimi <189027247+osaimi@users.noreply.github.com> Co-authored-by: Yingchun Lai <laiyingchun@apache.org> Co-authored-by: Hash Brown <hi@xzd.me> Co-authored-by: zuodongxu <192560071+zuodongxu@users.noreply.github.com> Co-authored-by: Masashi Tomooka <tmokmss@users.noreply.github.com> Co-authored-by: aplio <ryo.091219@gmail.com> Co-authored-by: Obada Khalili <54270856+obadakhalili@users.noreply.github.com> Co-authored-by: Nam Vu <zuzoovn@gmail.com> Co-authored-by: Kei YAMAZAKI <1715090+kei-yamazaki@users.noreply.github.com> Co-authored-by: TechnoHouse <13776377+deephbz@users.noreply.github.com> Co-authored-by: Riddhimaan-Senapati <114703025+Riddhimaan-Senapati@users.noreply.github.com> Co-authored-by: MaFee921 <31881301+2284730142@users.noreply.github.com> Co-authored-by: te-chan <t-nakanome@sakura-is.co.jp> Co-authored-by: HQidea <HQidea@users.noreply.github.com> Co-authored-by: Joshbly <36315710+Joshbly@users.noreply.github.com> Co-authored-by: xhe <xw897002528@gmail.com> Co-authored-by: weiwenyan-dev <154779315+weiwenyan-dev@users.noreply.github.com> Co-authored-by: ex_wenyan.wei <ex_wenyan.wei@tcl.com> Co-authored-by: engchina <12236799+engchina@users.noreply.github.com> Co-authored-by: engchina <atjapan2015@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 呆萌闷油瓶 <253605712@qq.com> Co-authored-by: Kemal <kemalmeler@outlook.com> Co-authored-by: Lazy_Frog <4590648+lazyFrogLOL@users.noreply.github.com> Co-authored-by: Yi Xiao <54782454+YIXIAO0@users.noreply.github.com> Co-authored-by: Steven sun <98230804+Tuyohai@users.noreply.github.com> Co-authored-by: steven <sunzwj@digitalchina.com> Co-authored-by: Kalo Chin <91766386+fdb02983rhy@users.noreply.github.com> Co-authored-by: Katy Tao <34019945+KatyTao@users.noreply.github.com> Co-authored-by: depy <42985524+h4ckdepy@users.noreply.github.com> Co-authored-by: 胡春东 <gycm520@gmail.com> Co-authored-by: Junjie.M <118170653@qq.com> Co-authored-by: MuYu <mr.muzea@gmail.com> Co-authored-by: Naoki Takashima <39912547+takatea@users.noreply.github.com> Co-authored-by: Summer-Gu <37869445+gubinjie@users.noreply.github.com> Co-authored-by: Fei He <droxer.he@gmail.com> Co-authored-by: ybalbert001 <120714773+ybalbert001@users.noreply.github.com> Co-authored-by: Yuanbo Li <ybalbert@amazon.com> Co-authored-by: douxc <7553076+douxc@users.noreply.github.com> Co-authored-by: liuzhenghua <1090179900@qq.com> Co-authored-by: Wu Jiayang <62842862+Wu-Jiayang@users.noreply.github.com> Co-authored-by: Your Name <you@example.com> Co-authored-by: kimjion <45935338+kimjion@users.noreply.github.com> Co-authored-by: AugNSo <song.tiankai@icloud.com> Co-authored-by: llinvokerl <38915183+llinvokerl@users.noreply.github.com> Co-authored-by: liusurong.lsr <liusurong.lsr@alibaba-inc.com> Co-authored-by: Vasu Negi <vasu-negi@users.noreply.github.com> Co-authored-by: Hundredwz <1808096180@qq.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
484 lines
18 KiB
Python
484 lines
18 KiB
Python
import json
|
||
import logging
|
||
from collections.abc import Mapping
|
||
from typing import Any, cast
|
||
|
||
from httpx import get
|
||
|
||
from core.entities.provider_entities import ProviderConfig
|
||
from core.model_runtime.utils.encoders import jsonable_encoder
|
||
from core.tools.__base.tool_runtime import ToolRuntime
|
||
from core.tools.custom_tool.provider import ApiToolProviderController
|
||
from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
|
||
from core.tools.entities.common_entities import I18nObject
|
||
from core.tools.entities.tool_bundle import ApiToolBundle
|
||
from core.tools.entities.tool_entities import (
|
||
ApiProviderAuthType,
|
||
ApiProviderSchemaType,
|
||
)
|
||
from core.tools.tool_label_manager import ToolLabelManager
|
||
from core.tools.tool_manager import ToolManager
|
||
from core.tools.utils.configuration import ProviderConfigEncrypter
|
||
from core.tools.utils.parser import ApiBasedToolSchemaParser
|
||
from extensions.ext_database import db
|
||
from models.tools import ApiToolProvider
|
||
from services.tools.tools_transform_service import ToolTransformService
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class ApiToolManageService:
|
||
@staticmethod
|
||
def parser_api_schema(schema: str) -> Mapping[str, Any]:
|
||
"""
|
||
parse api schema to tool bundle
|
||
"""
|
||
try:
|
||
warnings: dict[str, str] = {}
|
||
try:
|
||
tool_bundles, schema_type = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, warning=warnings)
|
||
except Exception as e:
|
||
raise ValueError(f"invalid schema: {str(e)}")
|
||
|
||
credentials_schema = [
|
||
ProviderConfig(
|
||
name="auth_type",
|
||
type=ProviderConfig.Type.SELECT,
|
||
required=True,
|
||
default="none",
|
||
options=[
|
||
ProviderConfig.Option(value="none", label=I18nObject(en_US="None", zh_Hans="无")),
|
||
ProviderConfig.Option(value="api_key", label=I18nObject(en_US="Api Key", zh_Hans="Api Key")),
|
||
],
|
||
placeholder=I18nObject(en_US="Select auth type", zh_Hans="选择认证方式"),
|
||
),
|
||
ProviderConfig(
|
||
name="api_key_header",
|
||
type=ProviderConfig.Type.TEXT_INPUT,
|
||
required=False,
|
||
placeholder=I18nObject(en_US="Enter api key header", zh_Hans="输入 api key header,如:X-API-KEY"),
|
||
default="api_key",
|
||
help=I18nObject(en_US="HTTP header name for api key", zh_Hans="HTTP 头部字段名,用于传递 api key"),
|
||
),
|
||
ProviderConfig(
|
||
name="api_key_value",
|
||
type=ProviderConfig.Type.TEXT_INPUT,
|
||
required=False,
|
||
placeholder=I18nObject(en_US="Enter api key", zh_Hans="输入 api key"),
|
||
default="",
|
||
),
|
||
]
|
||
|
||
return cast(
|
||
Mapping,
|
||
jsonable_encoder(
|
||
{
|
||
"schema_type": schema_type,
|
||
"parameters_schema": tool_bundles,
|
||
"credentials_schema": credentials_schema,
|
||
"warning": warnings,
|
||
}
|
||
),
|
||
)
|
||
except Exception as e:
|
||
raise ValueError(f"invalid schema: {str(e)}")
|
||
|
||
@staticmethod
|
||
def convert_schema_to_tool_bundles(schema: str, extra_info: dict | None = None) -> tuple[list[ApiToolBundle], str]:
|
||
"""
|
||
convert schema to tool bundles
|
||
|
||
:return: the list of tool bundles, description
|
||
"""
|
||
try:
|
||
return ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, extra_info=extra_info)
|
||
except Exception as e:
|
||
raise ValueError(f"invalid schema: {str(e)}")
|
||
|
||
@staticmethod
|
||
def create_api_tool_provider(
|
||
user_id: str,
|
||
tenant_id: str,
|
||
provider_name: str,
|
||
icon: dict,
|
||
credentials: dict,
|
||
schema_type: str,
|
||
schema: str,
|
||
privacy_policy: str,
|
||
custom_disclaimer: str,
|
||
labels: list[str],
|
||
):
|
||
"""
|
||
create api tool provider
|
||
"""
|
||
if schema_type not in [member.value for member in ApiProviderSchemaType]:
|
||
raise ValueError(f"invalid schema type {schema}")
|
||
|
||
provider_name = provider_name.strip()
|
||
|
||
# check if the provider exists
|
||
provider = (
|
||
db.session.query(ApiToolProvider)
|
||
.filter(
|
||
ApiToolProvider.tenant_id == tenant_id,
|
||
ApiToolProvider.name == provider_name,
|
||
)
|
||
.first()
|
||
)
|
||
|
||
if provider is not None:
|
||
raise ValueError(f"provider {provider_name} already exists")
|
||
|
||
# parse openapi to tool bundle
|
||
extra_info: dict[str, str] = {}
|
||
# extra info like description will be set here
|
||
tool_bundles, schema_type = ApiToolManageService.convert_schema_to_tool_bundles(schema, extra_info)
|
||
|
||
if len(tool_bundles) > 100:
|
||
raise ValueError("the number of apis should be less than 100")
|
||
|
||
# create db provider
|
||
db_provider = ApiToolProvider(
|
||
tenant_id=tenant_id,
|
||
user_id=user_id,
|
||
name=provider_name,
|
||
icon=json.dumps(icon),
|
||
schema=schema,
|
||
description=extra_info.get("description", ""),
|
||
schema_type_str=schema_type,
|
||
tools_str=json.dumps(jsonable_encoder(tool_bundles)),
|
||
credentials_str={},
|
||
privacy_policy=privacy_policy,
|
||
custom_disclaimer=custom_disclaimer,
|
||
)
|
||
|
||
if "auth_type" not in credentials:
|
||
raise ValueError("auth_type is required")
|
||
|
||
# get auth type, none or api key
|
||
auth_type = ApiProviderAuthType.value_of(credentials["auth_type"])
|
||
|
||
# create provider entity
|
||
provider_controller = ApiToolProviderController.from_db(db_provider, auth_type)
|
||
# load tools into provider entity
|
||
provider_controller.load_bundled_tools(tool_bundles)
|
||
|
||
# encrypt credentials
|
||
tool_configuration = ProviderConfigEncrypter(
|
||
tenant_id=tenant_id,
|
||
config=list(provider_controller.get_credentials_schema()),
|
||
provider_type=provider_controller.provider_type.value,
|
||
provider_identity=provider_controller.entity.identity.name,
|
||
)
|
||
|
||
encrypted_credentials = tool_configuration.encrypt(credentials)
|
||
db_provider.credentials_str = json.dumps(encrypted_credentials)
|
||
|
||
db.session.add(db_provider)
|
||
db.session.commit()
|
||
|
||
# update labels
|
||
ToolLabelManager.update_tool_labels(provider_controller, labels)
|
||
|
||
return {"result": "success"}
|
||
|
||
@staticmethod
|
||
def get_api_tool_provider_remote_schema(user_id: str, tenant_id: str, url: str):
|
||
"""
|
||
get api tool provider remote schema
|
||
"""
|
||
headers = {
|
||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko)"
|
||
" Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
|
||
"Accept": "*/*",
|
||
}
|
||
|
||
try:
|
||
response = get(url, headers=headers, timeout=10)
|
||
if response.status_code != 200:
|
||
raise ValueError(f"Got status code {response.status_code}")
|
||
schema = response.text
|
||
|
||
# try to parse schema, avoid SSRF attack
|
||
ApiToolManageService.parser_api_schema(schema)
|
||
except Exception:
|
||
logger.exception("parse api schema error")
|
||
raise ValueError("invalid schema, please check the url you provided")
|
||
|
||
return {"schema": schema}
|
||
|
||
@staticmethod
|
||
def list_api_tool_provider_tools(user_id: str, tenant_id: str, provider_name: str) -> list[ToolApiEntity]:
|
||
"""
|
||
list api tool provider tools
|
||
"""
|
||
provider: ApiToolProvider | None = (
|
||
db.session.query(ApiToolProvider)
|
||
.filter(
|
||
ApiToolProvider.tenant_id == tenant_id,
|
||
ApiToolProvider.name == provider_name,
|
||
)
|
||
.first()
|
||
)
|
||
|
||
if provider is None:
|
||
raise ValueError(f"you have not added provider {provider_name}")
|
||
|
||
controller = ToolTransformService.api_provider_to_controller(db_provider=provider)
|
||
labels = ToolLabelManager.get_tool_labels(controller)
|
||
|
||
return [
|
||
ToolTransformService.convert_tool_entity_to_api_entity(
|
||
tool_bundle,
|
||
tenant_id=tenant_id,
|
||
labels=labels,
|
||
)
|
||
for tool_bundle in provider.tools
|
||
]
|
||
|
||
@staticmethod
|
||
def update_api_tool_provider(
|
||
user_id: str,
|
||
tenant_id: str,
|
||
provider_name: str,
|
||
original_provider: str,
|
||
icon: dict,
|
||
credentials: dict,
|
||
schema_type: str,
|
||
schema: str,
|
||
privacy_policy: str,
|
||
custom_disclaimer: str,
|
||
labels: list[str],
|
||
):
|
||
"""
|
||
update api tool provider
|
||
"""
|
||
if schema_type not in [member.value for member in ApiProviderSchemaType]:
|
||
raise ValueError(f"invalid schema type {schema}")
|
||
|
||
provider_name = provider_name.strip()
|
||
|
||
# check if the provider exists
|
||
provider = (
|
||
db.session.query(ApiToolProvider)
|
||
.filter(
|
||
ApiToolProvider.tenant_id == tenant_id,
|
||
ApiToolProvider.name == original_provider,
|
||
)
|
||
.first()
|
||
)
|
||
|
||
if provider is None:
|
||
raise ValueError(f"api provider {provider_name} does not exists")
|
||
# parse openapi to tool bundle
|
||
extra_info: dict[str, str] = {}
|
||
# extra info like description will be set here
|
||
tool_bundles, schema_type = ApiToolManageService.convert_schema_to_tool_bundles(schema, extra_info)
|
||
|
||
# update db provider
|
||
provider.name = provider_name
|
||
provider.icon = json.dumps(icon)
|
||
provider.schema = schema
|
||
provider.description = extra_info.get("description", "")
|
||
provider.schema_type_str = ApiProviderSchemaType.OPENAPI.value
|
||
provider.tools_str = json.dumps(jsonable_encoder(tool_bundles))
|
||
provider.privacy_policy = privacy_policy
|
||
provider.custom_disclaimer = custom_disclaimer
|
||
|
||
if "auth_type" not in credentials:
|
||
raise ValueError("auth_type is required")
|
||
|
||
# get auth type, none or api key
|
||
auth_type = ApiProviderAuthType.value_of(credentials["auth_type"])
|
||
|
||
# create provider entity
|
||
provider_controller = ApiToolProviderController.from_db(provider, auth_type)
|
||
# load tools into provider entity
|
||
provider_controller.load_bundled_tools(tool_bundles)
|
||
|
||
# get original credentials if exists
|
||
tool_configuration = ProviderConfigEncrypter(
|
||
tenant_id=tenant_id,
|
||
config=list(provider_controller.get_credentials_schema()),
|
||
provider_type=provider_controller.provider_type.value,
|
||
provider_identity=provider_controller.entity.identity.name,
|
||
)
|
||
|
||
original_credentials = tool_configuration.decrypt(provider.credentials)
|
||
masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
|
||
# check if the credential has changed, save the original credential
|
||
for name, value in credentials.items():
|
||
if name in masked_credentials and value == masked_credentials[name]:
|
||
credentials[name] = original_credentials[name]
|
||
|
||
credentials = tool_configuration.encrypt(credentials)
|
||
provider.credentials_str = json.dumps(credentials)
|
||
|
||
db.session.add(provider)
|
||
db.session.commit()
|
||
|
||
# delete cache
|
||
tool_configuration.delete_tool_credentials_cache()
|
||
|
||
# update labels
|
||
ToolLabelManager.update_tool_labels(provider_controller, labels)
|
||
|
||
return {"result": "success"}
|
||
|
||
@staticmethod
|
||
def delete_api_tool_provider(user_id: str, tenant_id: str, provider_name: str):
|
||
"""
|
||
delete tool provider
|
||
"""
|
||
provider = (
|
||
db.session.query(ApiToolProvider)
|
||
.filter(
|
||
ApiToolProvider.tenant_id == tenant_id,
|
||
ApiToolProvider.name == provider_name,
|
||
)
|
||
.first()
|
||
)
|
||
|
||
if provider is None:
|
||
raise ValueError(f"you have not added provider {provider_name}")
|
||
|
||
db.session.delete(provider)
|
||
db.session.commit()
|
||
|
||
return {"result": "success"}
|
||
|
||
@staticmethod
|
||
def get_api_tool_provider(user_id: str, tenant_id: str, provider: str):
|
||
"""
|
||
get api tool provider
|
||
"""
|
||
return ToolManager.user_get_api_provider(provider=provider, tenant_id=tenant_id)
|
||
|
||
@staticmethod
|
||
def test_api_tool_preview(
|
||
tenant_id: str,
|
||
provider_name: str,
|
||
tool_name: str,
|
||
credentials: dict,
|
||
parameters: dict,
|
||
schema_type: str,
|
||
schema: str,
|
||
):
|
||
"""
|
||
test api tool before adding api tool provider
|
||
"""
|
||
if schema_type not in [member.value for member in ApiProviderSchemaType]:
|
||
raise ValueError(f"invalid schema type {schema_type}")
|
||
|
||
try:
|
||
tool_bundles, _ = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema)
|
||
except Exception:
|
||
raise ValueError("invalid schema")
|
||
|
||
# get tool bundle
|
||
tool_bundle = next(filter(lambda tb: tb.operation_id == tool_name, tool_bundles), None)
|
||
if tool_bundle is None:
|
||
raise ValueError(f"invalid tool name {tool_name}")
|
||
|
||
db_provider = (
|
||
db.session.query(ApiToolProvider)
|
||
.filter(
|
||
ApiToolProvider.tenant_id == tenant_id,
|
||
ApiToolProvider.name == provider_name,
|
||
)
|
||
.first()
|
||
)
|
||
|
||
if not db_provider:
|
||
# create a fake db provider
|
||
db_provider = ApiToolProvider(
|
||
tenant_id="",
|
||
user_id="",
|
||
name="",
|
||
icon="",
|
||
schema=schema,
|
||
description="",
|
||
schema_type_str=ApiProviderSchemaType.OPENAPI.value,
|
||
tools_str=json.dumps(jsonable_encoder(tool_bundles)),
|
||
credentials_str=json.dumps(credentials),
|
||
)
|
||
|
||
if "auth_type" not in credentials:
|
||
raise ValueError("auth_type is required")
|
||
|
||
# get auth type, none or api key
|
||
auth_type = ApiProviderAuthType.value_of(credentials["auth_type"])
|
||
|
||
# create provider entity
|
||
provider_controller = ApiToolProviderController.from_db(db_provider, auth_type)
|
||
# load tools into provider entity
|
||
provider_controller.load_bundled_tools(tool_bundles)
|
||
|
||
# decrypt credentials
|
||
if db_provider.id:
|
||
tool_configuration = ProviderConfigEncrypter(
|
||
tenant_id=tenant_id,
|
||
config=list(provider_controller.get_credentials_schema()),
|
||
provider_type=provider_controller.provider_type.value,
|
||
provider_identity=provider_controller.entity.identity.name,
|
||
)
|
||
decrypted_credentials = tool_configuration.decrypt(credentials)
|
||
# check if the credential has changed, save the original credential
|
||
masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
|
||
for name, value in credentials.items():
|
||
if name in masked_credentials and value == masked_credentials[name]:
|
||
credentials[name] = decrypted_credentials[name]
|
||
|
||
try:
|
||
provider_controller.validate_credentials_format(credentials)
|
||
# get tool
|
||
tool = provider_controller.get_tool(tool_name)
|
||
tool = tool.fork_tool_runtime(
|
||
runtime=ToolRuntime(
|
||
credentials=credentials,
|
||
tenant_id=tenant_id,
|
||
)
|
||
)
|
||
result = tool.validate_credentials(credentials, parameters)
|
||
except Exception as e:
|
||
return {"error": str(e)}
|
||
|
||
return {"result": result or "empty response"}
|
||
|
||
@staticmethod
|
||
def list_api_tools(user_id: str, tenant_id: str) -> list[ToolProviderApiEntity]:
|
||
"""
|
||
list api tools
|
||
"""
|
||
# get all api providers
|
||
db_providers: list[ApiToolProvider] = (
|
||
db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all() or []
|
||
)
|
||
|
||
result: list[ToolProviderApiEntity] = []
|
||
|
||
for provider in db_providers:
|
||
# convert provider controller to user provider
|
||
provider_controller = ToolTransformService.api_provider_to_controller(db_provider=provider)
|
||
labels = ToolLabelManager.get_tool_labels(provider_controller)
|
||
user_provider = ToolTransformService.api_provider_to_user_provider(
|
||
provider_controller, db_provider=provider, decrypt_credentials=True
|
||
)
|
||
user_provider.labels = labels
|
||
|
||
# add icon
|
||
ToolTransformService.repack_provider(tenant_id=tenant_id, provider=user_provider)
|
||
|
||
tools = provider_controller.get_tools(tenant_id=tenant_id)
|
||
|
||
for tool in tools or []:
|
||
user_provider.tools.append(
|
||
ToolTransformService.convert_tool_entity_to_api_entity(
|
||
tenant_id=tenant_id, tool=tool, credentials=user_provider.original_credentials, labels=labels
|
||
)
|
||
)
|
||
|
||
result.append(user_provider)
|
||
|
||
return result
|