chore(api/core): apply ruff reformatting (#7624)

This commit is contained in:
Bowen Liang
2024-09-10 17:00:20 +08:00
committed by GitHub
parent 178730266d
commit 2cf1187b32
724 changed files with 21180 additions and 21123 deletions

View File

@@ -1,4 +1,3 @@
from core.tools.entities.common_entities import I18nObject
from core.tools.entities.tool_bundle import ApiToolBundle
from core.tools.entities.tool_entities import (
@@ -18,85 +17,69 @@ class ApiToolProviderController(ToolProviderController):
provider_id: str
@staticmethod
def from_db(db_provider: ApiToolProvider, auth_type: ApiProviderAuthType) -> 'ApiToolProviderController':
def from_db(db_provider: ApiToolProvider, auth_type: ApiProviderAuthType) -> "ApiToolProviderController":
credentials_schema = {
'auth_type': ToolProviderCredentials(
name='auth_type',
"auth_type": ToolProviderCredentials(
name="auth_type",
required=True,
type=ToolProviderCredentials.CredentialsType.SELECT,
options=[
ToolCredentialsOption(value='none', label=I18nObject(en_US='None', zh_Hans='')),
ToolCredentialsOption(value='api_key', label=I18nObject(en_US='api_key', zh_Hans='api_key'))
ToolCredentialsOption(value="none", label=I18nObject(en_US="None", zh_Hans="")),
ToolCredentialsOption(value="api_key", label=I18nObject(en_US="api_key", zh_Hans="api_key")),
],
default='none',
help=I18nObject(
en_US='The auth type of the api provider',
zh_Hans='api provider 的认证类型'
)
default="none",
help=I18nObject(en_US="The auth type of the api provider", zh_Hans="api provider 的认证类型"),
)
}
if auth_type == ApiProviderAuthType.API_KEY:
credentials_schema = {
**credentials_schema,
'api_key_header': ToolProviderCredentials(
name='api_key_header',
"api_key_header": ToolProviderCredentials(
name="api_key_header",
required=False,
default='api_key',
default="api_key",
type=ToolProviderCredentials.CredentialsType.TEXT_INPUT,
help=I18nObject(
en_US='The header name of the api key',
zh_Hans='携带 api key 的 header 名称'
)
help=I18nObject(en_US="The header name of the api key", zh_Hans="携带 api key 的 header 名称"),
),
'api_key_value': ToolProviderCredentials(
name='api_key_value',
"api_key_value": ToolProviderCredentials(
name="api_key_value",
required=True,
type=ToolProviderCredentials.CredentialsType.SECRET_INPUT,
help=I18nObject(
en_US='The api key',
zh_Hans='api key的值'
)
help=I18nObject(en_US="The api key", zh_Hans="api key的值"),
),
'api_key_header_prefix': ToolProviderCredentials(
name='api_key_header_prefix',
"api_key_header_prefix": ToolProviderCredentials(
name="api_key_header_prefix",
required=False,
default='basic',
default="basic",
type=ToolProviderCredentials.CredentialsType.SELECT,
help=I18nObject(
en_US='The prefix of the api key header',
zh_Hans='api key header 的前缀'
),
help=I18nObject(en_US="The prefix of the api key header", zh_Hans="api key header 的前缀"),
options=[
ToolCredentialsOption(value='basic', label=I18nObject(en_US='Basic', zh_Hans='Basic')),
ToolCredentialsOption(value='bearer', label=I18nObject(en_US='Bearer', zh_Hans='Bearer')),
ToolCredentialsOption(value='custom', label=I18nObject(en_US='Custom', zh_Hans='Custom'))
]
)
ToolCredentialsOption(value="basic", label=I18nObject(en_US="Basic", zh_Hans="Basic")),
ToolCredentialsOption(value="bearer", label=I18nObject(en_US="Bearer", zh_Hans="Bearer")),
ToolCredentialsOption(value="custom", label=I18nObject(en_US="Custom", zh_Hans="Custom")),
],
),
}
elif auth_type == ApiProviderAuthType.NONE:
pass
else:
raise ValueError(f'invalid auth type {auth_type}')
raise ValueError(f"invalid auth type {auth_type}")
user_name = db_provider.user.name if db_provider.user_id else ''
user_name = db_provider.user.name if db_provider.user_id else ""
return ApiToolProviderController(**{
'identity': {
'author': user_name,
'name': db_provider.name,
'label': {
'en_US': db_provider.name,
'zh_Hans': db_provider.name
return ApiToolProviderController(
**{
"identity": {
"author": user_name,
"name": db_provider.name,
"label": {"en_US": db_provider.name, "zh_Hans": db_provider.name},
"description": {"en_US": db_provider.description, "zh_Hans": db_provider.description},
"icon": db_provider.icon,
},
'description': {
'en_US': db_provider.description,
'zh_Hans': db_provider.description
},
'icon': db_provider.icon,
},
'credentials_schema': credentials_schema,
'provider_id': db_provider.id or '',
})
"credentials_schema": credentials_schema,
"provider_id": db_provider.id or "",
}
)
@property
def provider_type(self) -> ToolProviderType:
@@ -104,39 +87,35 @@ class ApiToolProviderController(ToolProviderController):
def _parse_tool_bundle(self, tool_bundle: ApiToolBundle) -> ApiTool:
"""
parse tool bundle to tool
parse tool bundle to tool
:param tool_bundle: the tool bundle
:return: the tool
:param tool_bundle: the tool bundle
:return: the tool
"""
return ApiTool(**{
'api_bundle': tool_bundle,
'identity' : {
'author': tool_bundle.author,
'name': tool_bundle.operation_id,
'label': {
'en_US': tool_bundle.operation_id,
'zh_Hans': tool_bundle.operation_id
return ApiTool(
**{
"api_bundle": tool_bundle,
"identity": {
"author": tool_bundle.author,
"name": tool_bundle.operation_id,
"label": {"en_US": tool_bundle.operation_id, "zh_Hans": tool_bundle.operation_id},
"icon": self.identity.icon,
"provider": self.provider_id,
},
'icon': self.identity.icon,
'provider': self.provider_id,
},
'description': {
'human': {
'en_US': tool_bundle.summary or '',
'zh_Hans': tool_bundle.summary or ''
"description": {
"human": {"en_US": tool_bundle.summary or "", "zh_Hans": tool_bundle.summary or ""},
"llm": tool_bundle.summary or "",
},
'llm': tool_bundle.summary or ''
},
'parameters' : tool_bundle.parameters if tool_bundle.parameters else [],
})
"parameters": tool_bundle.parameters if tool_bundle.parameters else [],
}
)
def load_bundled_tools(self, tools: list[ApiToolBundle]) -> list[ApiTool]:
"""
load bundled tools
load bundled tools
:param tools: the bundled tools
:return: the tools
:param tools: the bundled tools
:return: the tools
"""
self.tools = [self._parse_tool_bundle(tool) for tool in tools]
@@ -144,22 +123,23 @@ class ApiToolProviderController(ToolProviderController):
def get_tools(self, user_id: str, tenant_id: str) -> list[ApiTool]:
"""
fetch tools from database
fetch tools from database
:param user_id: the user id
:param tenant_id: the tenant id
:return: the tools
:param user_id: the user id
:param tenant_id: the tenant id
:return: the tools
"""
if self.tools is not None:
return self.tools
tools: list[Tool] = []
# get tenant api providers
db_providers: list[ApiToolProvider] = db.session.query(ApiToolProvider).filter(
ApiToolProvider.tenant_id == tenant_id,
ApiToolProvider.name == self.identity.name
).all()
db_providers: list[ApiToolProvider] = (
db.session.query(ApiToolProvider)
.filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.name == self.identity.name)
.all()
)
if db_providers and len(db_providers) != 0:
for db_provider in db_providers:
@@ -167,16 +147,16 @@ class ApiToolProviderController(ToolProviderController):
assistant_tool = self._parse_tool_bundle(tool)
assistant_tool.is_team_authorization = True
tools.append(assistant_tool)
self.tools = tools
return tools
def get_tool(self, tool_name: str) -> ApiTool:
"""
get tool by name
get tool by name
:param tool_name: the name of the tool
:return: the tool
:param tool_name: the name of the tool
:return: the tool
"""
if self.tools is None:
self.get_tools()
@@ -185,4 +165,4 @@ class ApiToolProviderController(ToolProviderController):
if tool.identity.name == tool_name:
return tool
raise ValueError(f'tool {tool_name} not found')
raise ValueError(f"tool {tool_name} not found")

View File

@@ -11,11 +11,12 @@ from models.tools import PublishedAppTool
logger = logging.getLogger(__name__)
class AppToolProviderEntity(ToolProviderController):
@property
def provider_type(self) -> ToolProviderType:
return ToolProviderType.APP
def _validate_credentials(self, tool_name: str, credentials: dict[str, Any]) -> None:
pass
@@ -23,9 +24,13 @@ class AppToolProviderEntity(ToolProviderController):
pass
def get_tools(self, user_id: str) -> list[Tool]:
db_tools: list[PublishedAppTool] = db.session.query(PublishedAppTool).filter(
PublishedAppTool.user_id == user_id,
).all()
db_tools: list[PublishedAppTool] = (
db.session.query(PublishedAppTool)
.filter(
PublishedAppTool.user_id == user_id,
)
.all()
)
if not db_tools or len(db_tools) == 0:
return []
@@ -34,23 +39,17 @@ class AppToolProviderEntity(ToolProviderController):
for db_tool in db_tools:
tool = {
'identity': {
'author': db_tool.author,
'name': db_tool.tool_name,
'label': {
'en_US': db_tool.tool_name,
'zh_Hans': db_tool.tool_name
},
'icon': ''
"identity": {
"author": db_tool.author,
"name": db_tool.tool_name,
"label": {"en_US": db_tool.tool_name, "zh_Hans": db_tool.tool_name},
"icon": "",
},
'description': {
'human': {
'en_US': db_tool.description_i18n.en_US,
'zh_Hans': db_tool.description_i18n.zh_Hans
},
'llm': db_tool.llm_description
"description": {
"human": {"en_US": db_tool.description_i18n.en_US, "zh_Hans": db_tool.description_i18n.zh_Hans},
"llm": db_tool.llm_description,
},
'parameters': []
"parameters": [],
}
# get app from db
app: App = db_tool.app
@@ -64,52 +63,41 @@ class AppToolProviderEntity(ToolProviderController):
for input_form in user_input_form_list:
# get type
form_type = input_form.keys()[0]
default = input_form[form_type]['default']
required = input_form[form_type]['required']
label = input_form[form_type]['label']
variable_name = input_form[form_type]['variable_name']
options = input_form[form_type].get('options', [])
if form_type == 'paragraph' or form_type == 'text-input':
tool['parameters'].append(ToolParameter(
name=variable_name,
label=I18nObject(
en_US=label,
zh_Hans=label
),
human_description=I18nObject(
en_US=label,
zh_Hans=label
),
llm_description=label,
form=ToolParameter.ToolParameterForm.FORM,
type=ToolParameter.ToolParameterType.STRING,
required=required,
default=default
))
elif form_type == 'select':
tool['parameters'].append(ToolParameter(
name=variable_name,
label=I18nObject(
en_US=label,
zh_Hans=label
),
human_description=I18nObject(
en_US=label,
zh_Hans=label
),
llm_description=label,
form=ToolParameter.ToolParameterForm.FORM,
type=ToolParameter.ToolParameterType.SELECT,
required=required,
default=default,
options=[ToolParameterOption(
value=option,
label=I18nObject(
en_US=option,
zh_Hans=option
)
) for option in options]
))
default = input_form[form_type]["default"]
required = input_form[form_type]["required"]
label = input_form[form_type]["label"]
variable_name = input_form[form_type]["variable_name"]
options = input_form[form_type].get("options", [])
if form_type == "paragraph" or form_type == "text-input":
tool["parameters"].append(
ToolParameter(
name=variable_name,
label=I18nObject(en_US=label, zh_Hans=label),
human_description=I18nObject(en_US=label, zh_Hans=label),
llm_description=label,
form=ToolParameter.ToolParameterForm.FORM,
type=ToolParameter.ToolParameterType.STRING,
required=required,
default=default,
)
)
elif form_type == "select":
tool["parameters"].append(
ToolParameter(
name=variable_name,
label=I18nObject(en_US=label, zh_Hans=label),
human_description=I18nObject(en_US=label, zh_Hans=label),
llm_description=label,
form=ToolParameter.ToolParameterForm.FORM,
type=ToolParameter.ToolParameterType.SELECT,
required=required,
default=default,
options=[
ToolParameterOption(value=option, label=I18nObject(en_US=option, zh_Hans=option))
for option in options
],
)
)
tools.append(Tool(**tool))
return tools
return tools

View File

@@ -10,7 +10,7 @@ class BuiltinToolProviderSort:
@classmethod
def sort(cls, providers: list[UserToolProvider]) -> list[UserToolProvider]:
if not cls._position:
cls._position = get_tool_position_map(os.path.join(os.path.dirname(__file__), '..'))
cls._position = get_tool_position_map(os.path.join(os.path.dirname(__file__), ".."))
def name_func(provider: UserToolProvider) -> str:
return provider.name

View File

@@ -6,6 +6,6 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class AIPPTProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
AIPPTGenerateTool._get_api_token(credentials, user_id='__dify_system__')
AIPPTGenerateTool._get_api_token(credentials, user_id="__dify_system__")
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -20,16 +20,16 @@ class AIPPTGenerateTool(BuiltinTool):
A tool for generating a ppt
"""
_api_base_url = URL('https://co.aippt.cn/api')
_api_base_url = URL("https://co.aippt.cn/api")
_api_token_cache = {}
_api_token_cache_lock:Optional[Lock] = None
_api_token_cache_lock: Optional[Lock] = None
_style_cache = {}
_style_cache_lock:Optional[Lock] = None
_style_cache_lock: Optional[Lock] = None
_task = {}
_task_type_map = {
'auto': 1,
'markdown': 7,
"auto": 1,
"markdown": 7,
}
def __init__(self, **kwargs: Any):
@@ -48,65 +48,55 @@ class AIPPTGenerateTool(BuiltinTool):
Returns:
ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation, which can be a single message or a list of messages.
"""
title = tool_parameters.get('title', '')
title = tool_parameters.get("title", "")
if not title:
return self.create_text_message('Please provide a title for the ppt')
model = tool_parameters.get('model', 'aippt')
return self.create_text_message("Please provide a title for the ppt")
model = tool_parameters.get("model", "aippt")
if not model:
return self.create_text_message('Please provide a model for the ppt')
outline = tool_parameters.get('outline', '')
return self.create_text_message("Please provide a model for the ppt")
outline = tool_parameters.get("outline", "")
# create task
task_id = self._create_task(
type=self._task_type_map['auto' if not outline else 'markdown'],
type=self._task_type_map["auto" if not outline else "markdown"],
title=title,
content=outline,
user_id=user_id
user_id=user_id,
)
# get suit
color = tool_parameters.get('color')
style = tool_parameters.get('style')
color = tool_parameters.get("color")
style = tool_parameters.get("style")
if color == '__default__':
color_id = ''
if color == "__default__":
color_id = ""
else:
color_id = int(color.split('-')[1])
color_id = int(color.split("-")[1])
if style == '__default__':
style_id = ''
if style == "__default__":
style_id = ""
else:
style_id = int(style.split('-')[1])
style_id = int(style.split("-")[1])
suit_id = self._get_suit(style_id=style_id, colour_id=color_id)
# generate outline
if not outline:
self._generate_outline(
task_id=task_id,
model=model,
user_id=user_id
)
self._generate_outline(task_id=task_id, model=model, user_id=user_id)
# generate content
self._generate_content(
task_id=task_id,
model=model,
user_id=user_id
)
self._generate_content(task_id=task_id, model=model, user_id=user_id)
# generate ppt
_, ppt_url = self._generate_ppt(
task_id=task_id,
suit_id=suit_id,
user_id=user_id
)
_, ppt_url = self._generate_ppt(task_id=task_id, suit_id=suit_id, user_id=user_id)
return self.create_text_message('''the ppt has been created successfully,'''
f'''the ppt url is {ppt_url}'''
'''please give the ppt url to user and direct user to download it.''')
return self.create_text_message(
"""the ppt has been created successfully,"""
f"""the ppt url is {ppt_url}"""
"""please give the ppt url to user and direct user to download it."""
)
def _create_task(self, type: int, title: str, content: str, user_id: str) -> str:
"""
@@ -119,129 +109,121 @@ class AIPPTGenerateTool(BuiltinTool):
:return: the task ID
"""
headers = {
'x-channel': '',
'x-api-key': self.runtime.credentials['aippt_access_key'],
'x-token': self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
"x-channel": "",
"x-api-key": self.runtime.credentials["aippt_access_key"],
"x-token": self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
}
response = post(
str(self._api_base_url / 'ai' / 'chat' / 'v2' / 'task'),
str(self._api_base_url / "ai" / "chat" / "v2" / "task"),
headers=headers,
files={
'type': ('', str(type)),
'title': ('', title),
'content': ('', content)
}
files={"type": ("", str(type)), "title": ("", title), "content": ("", content)},
)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to create task: {response.get("msg")}')
return response.get('data', {}).get('id')
return response.get("data", {}).get("id")
def _generate_outline(self, task_id: str, model: str, user_id: str) -> str:
api_url = self._api_base_url / 'ai' / 'chat' / 'outline' if model == 'aippt' else \
self._api_base_url / 'ai' / 'chat' / 'wx' / 'outline'
api_url %= {'task_id': task_id}
api_url = (
self._api_base_url / "ai" / "chat" / "outline"
if model == "aippt"
else self._api_base_url / "ai" / "chat" / "wx" / "outline"
)
api_url %= {"task_id": task_id}
headers = {
'x-channel': '',
'x-api-key': self.runtime.credentials['aippt_access_key'],
'x-token': self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
"x-channel": "",
"x-api-key": self.runtime.credentials["aippt_access_key"],
"x-token": self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
}
response = requests_get(
url=api_url,
headers=headers,
stream=True,
timeout=(10, 60)
)
response = requests_get(url=api_url, headers=headers, stream=True, timeout=(10, 60))
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
outline = ''
for chunk in response.iter_lines(delimiter=b'\n\n'):
raise Exception(f"Failed to connect to aippt: {response.text}")
outline = ""
for chunk in response.iter_lines(delimiter=b"\n\n"):
if not chunk:
continue
event = ''
lines = chunk.decode('utf-8').split('\n')
event = ""
lines = chunk.decode("utf-8").split("\n")
for line in lines:
if line.startswith('event:'):
if line.startswith("event:"):
event = line[6:]
elif line.startswith('data:'):
elif line.startswith("data:"):
data = line[5:]
if event == 'message':
if event == "message":
try:
data = json_loads(data)
outline += data.get('content', '')
outline += data.get("content", "")
except Exception as e:
pass
elif event == 'close':
elif event == "close":
break
elif event == 'error' or event == 'filter':
raise Exception(f'Failed to generate outline: {data}')
elif event == "error" or event == "filter":
raise Exception(f"Failed to generate outline: {data}")
return outline
def _generate_content(self, task_id: str, model: str, user_id: str) -> str:
api_url = self._api_base_url / 'ai' / 'chat' / 'content' if model == 'aippt' else \
self._api_base_url / 'ai' / 'chat' / 'wx' / 'content'
api_url %= {'task_id': task_id}
api_url = (
self._api_base_url / "ai" / "chat" / "content"
if model == "aippt"
else self._api_base_url / "ai" / "chat" / "wx" / "content"
)
api_url %= {"task_id": task_id}
headers = {
'x-channel': '',
'x-api-key': self.runtime.credentials['aippt_access_key'],
'x-token': self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
"x-channel": "",
"x-api-key": self.runtime.credentials["aippt_access_key"],
"x-token": self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
}
response = requests_get(
url=api_url,
headers=headers,
stream=True,
timeout=(10, 60)
)
response = requests_get(url=api_url, headers=headers, stream=True, timeout=(10, 60))
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
if model == 'aippt':
content = ''
for chunk in response.iter_lines(delimiter=b'\n\n'):
raise Exception(f"Failed to connect to aippt: {response.text}")
if model == "aippt":
content = ""
for chunk in response.iter_lines(delimiter=b"\n\n"):
if not chunk:
continue
event = ''
lines = chunk.decode('utf-8').split('\n')
event = ""
lines = chunk.decode("utf-8").split("\n")
for line in lines:
if line.startswith('event:'):
if line.startswith("event:"):
event = line[6:]
elif line.startswith('data:'):
elif line.startswith("data:"):
data = line[5:]
if event == 'message':
if event == "message":
try:
data = json_loads(data)
content += data.get('content', '')
content += data.get("content", "")
except Exception as e:
pass
elif event == 'close':
elif event == "close":
break
elif event == 'error' or event == 'filter':
raise Exception(f'Failed to generate content: {data}')
elif event == "error" or event == "filter":
raise Exception(f"Failed to generate content: {data}")
return content
elif model == 'wenxin':
elif model == "wenxin":
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to generate content: {response.get("msg")}')
return response.get('data', '')
return ''
return response.get("data", "")
return ""
def _generate_ppt(self, task_id: str, suit_id: int, user_id) -> tuple[str, str]:
"""
@@ -252,83 +234,73 @@ class AIPPTGenerateTool(BuiltinTool):
:return: the cover url of the ppt and the ppt url
"""
headers = {
'x-channel': '',
'x-api-key': self.runtime.credentials['aippt_access_key'],
'x-token': self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
"x-channel": "",
"x-api-key": self.runtime.credentials["aippt_access_key"],
"x-token": self._get_api_token(credentials=self.runtime.credentials, user_id=user_id),
}
response = post(
str(self._api_base_url / 'design' / 'v2' / 'save'),
str(self._api_base_url / "design" / "v2" / "save"),
headers=headers,
data={
'task_id': task_id,
'template_id': suit_id
}
data={"task_id": task_id, "template_id": suit_id},
)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to generate ppt: {response.get("msg")}')
id = response.get('data', {}).get('id')
cover_url = response.get('data', {}).get('cover_url')
id = response.get("data", {}).get("id")
cover_url = response.get("data", {}).get("cover_url")
response = post(
str(self._api_base_url / 'download' / 'export' / 'file'),
str(self._api_base_url / "download" / "export" / "file"),
headers=headers,
data={
'id': id,
'format': 'ppt',
'files_to_zip': False,
'edit': True
}
data={"id": id, "format": "ppt", "files_to_zip": False, "edit": True},
)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to generate ppt: {response.get("msg")}')
export_code = response.get('data')
export_code = response.get("data")
if not export_code:
raise Exception('Failed to generate ppt, the export code is empty')
raise Exception("Failed to generate ppt, the export code is empty")
current_iteration = 0
while current_iteration < 50:
# get ppt url
response = post(
str(self._api_base_url / 'download' / 'export' / 'file' / 'result'),
str(self._api_base_url / "download" / "export" / "file" / "result"),
headers=headers,
data={
'task_key': export_code
}
data={"task_key": export_code},
)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to generate ppt: {response.get("msg")}')
if response.get('msg') == '导出中':
if response.get("msg") == "导出中":
current_iteration += 1
sleep(2)
continue
ppt_url = response.get('data', [])
ppt_url = response.get("data", [])
if len(ppt_url) == 0:
raise Exception('Failed to generate ppt, the ppt url is empty')
raise Exception("Failed to generate ppt, the ppt url is empty")
return cover_url, ppt_url[0]
raise Exception('Failed to generate ppt, the export is timeout')
raise Exception("Failed to generate ppt, the export is timeout")
@classmethod
def _get_api_token(cls, credentials: dict[str, str], user_id: str) -> str:
"""
@@ -337,53 +309,43 @@ class AIPPTGenerateTool(BuiltinTool):
:param credentials: the credentials
:return: the API token
"""
access_key = credentials['aippt_access_key']
secret_key = credentials['aippt_secret_key']
access_key = credentials["aippt_access_key"]
secret_key = credentials["aippt_secret_key"]
cache_key = f'{access_key}#@#{user_id}'
cache_key = f"{access_key}#@#{user_id}"
with cls._api_token_cache_lock:
# clear expired tokens
now = time()
for key in list(cls._api_token_cache.keys()):
if cls._api_token_cache[key]['expire'] < now:
if cls._api_token_cache[key]["expire"] < now:
del cls._api_token_cache[key]
if cache_key in cls._api_token_cache:
return cls._api_token_cache[cache_key]['token']
return cls._api_token_cache[cache_key]["token"]
# get token
headers = {
'x-api-key': access_key,
'x-timestamp': str(int(now)),
'x-signature': cls._calculate_sign(access_key, secret_key, int(now))
"x-api-key": access_key,
"x-timestamp": str(int(now)),
"x-signature": cls._calculate_sign(access_key, secret_key, int(now)),
}
param = {
'uid': user_id,
'channel': ''
}
param = {"uid": user_id, "channel": ""}
response = get(
str(cls._api_base_url / 'grant' / 'token'),
params=param,
headers=headers
)
response = get(str(cls._api_base_url / "grant" / "token"), params=param, headers=headers)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to connect to aippt: {response.get("msg")}')
token = response.get('data', {}).get('token')
expire = response.get('data', {}).get('time_expire')
token = response.get("data", {}).get("token")
expire = response.get("data", {}).get("time_expire")
with cls._api_token_cache_lock:
cls._api_token_cache[cache_key] = {
'token': token,
'expire': now + expire
}
cls._api_token_cache[cache_key] = {"token": token, "expire": now + expire}
return token
@@ -391,11 +353,9 @@ class AIPPTGenerateTool(BuiltinTool):
def _calculate_sign(cls, access_key: str, secret_key: str, timestamp: int) -> str:
return b64encode(
hmac_new(
key=secret_key.encode('utf-8'),
msg=f'GET@/api/grant/token/@{timestamp}'.encode(),
digestmod=sha1
key=secret_key.encode("utf-8"), msg=f"GET@/api/grant/token/@{timestamp}".encode(), digestmod=sha1
).digest()
).decode('utf-8')
).decode("utf-8")
@classmethod
def _get_styles(cls, credentials: dict[str, str], user_id: str) -> tuple[list[dict], list[dict]]:
@@ -408,47 +368,46 @@ class AIPPTGenerateTool(BuiltinTool):
# clear expired styles
now = time()
for key in list(cls._style_cache.keys()):
if cls._style_cache[key]['expire'] < now:
if cls._style_cache[key]["expire"] < now:
del cls._style_cache[key]
key = f'{credentials["aippt_access_key"]}#@#{user_id}'
if key in cls._style_cache:
return cls._style_cache[key]['colors'], cls._style_cache[key]['styles']
return cls._style_cache[key]["colors"], cls._style_cache[key]["styles"]
headers = {
'x-channel': '',
'x-api-key': credentials['aippt_access_key'],
'x-token': cls._get_api_token(credentials=credentials, user_id=user_id)
"x-channel": "",
"x-api-key": credentials["aippt_access_key"],
"x-token": cls._get_api_token(credentials=credentials, user_id=user_id),
}
response = get(
str(cls._api_base_url / 'template_component' / 'suit' / 'select'),
headers=headers
)
response = get(str(cls._api_base_url / "template_component" / "suit" / "select"), headers=headers)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to connect to aippt: {response.get("msg")}')
colors = [{
'id': f'id-{item.get("id")}',
'name': item.get('name'),
'en_name': item.get('en_name', item.get('name')),
} for item in response.get('data', {}).get('colour') or []]
styles = [{
'id': f'id-{item.get("id")}',
'name': item.get('title'),
} for item in response.get('data', {}).get('suit_style') or []]
colors = [
{
"id": f'id-{item.get("id")}',
"name": item.get("name"),
"en_name": item.get("en_name", item.get("name")),
}
for item in response.get("data", {}).get("colour") or []
]
styles = [
{
"id": f'id-{item.get("id")}',
"name": item.get("title"),
}
for item in response.get("data", {}).get("suit_style") or []
]
with cls._style_cache_lock:
cls._style_cache[key] = {
'colors': colors,
'styles': styles,
'expire': now + 60 * 60
}
cls._style_cache[key] = {"colors": colors, "styles": styles, "expire": now + 60 * 60}
return colors, styles
@@ -459,44 +418,39 @@ class AIPPTGenerateTool(BuiltinTool):
:param credentials: the credentials
:return: Tuple[list[dict[id, color]], list[dict[id, style]]
"""
if not self.runtime.credentials.get('aippt_access_key') or not self.runtime.credentials.get('aippt_secret_key'):
raise Exception('Please provide aippt credentials')
if not self.runtime.credentials.get("aippt_access_key") or not self.runtime.credentials.get("aippt_secret_key"):
raise Exception("Please provide aippt credentials")
return self._get_styles(credentials=self.runtime.credentials, user_id=user_id)
def _get_suit(self, style_id: int, colour_id: int) -> int:
"""
Get suit
"""
headers = {
'x-channel': '',
'x-api-key': self.runtime.credentials['aippt_access_key'],
'x-token': self._get_api_token(credentials=self.runtime.credentials, user_id='__dify_system__')
"x-channel": "",
"x-api-key": self.runtime.credentials["aippt_access_key"],
"x-token": self._get_api_token(credentials=self.runtime.credentials, user_id="__dify_system__"),
}
response = get(
str(self._api_base_url / 'template_component' / 'suit' / 'search'),
str(self._api_base_url / "template_component" / "suit" / "search"),
headers=headers,
params={
'style_id': style_id,
'colour_id': colour_id,
'page': 1,
'page_size': 1
}
params={"style_id": style_id, "colour_id": colour_id, "page": 1, "page_size": 1},
)
if response.status_code != 200:
raise Exception(f'Failed to connect to aippt: {response.text}')
raise Exception(f"Failed to connect to aippt: {response.text}")
response = response.json()
if response.get('code') != 0:
if response.get("code") != 0:
raise Exception(f'Failed to connect to aippt: {response.get("msg")}')
if len(response.get('data', {}).get('list') or []) > 0:
return response.get('data', {}).get('list')[0].get('id')
raise Exception('Failed to get suit, the suit does not exist, please check the style and color')
if len(response.get("data", {}).get("list") or []) > 0:
return response.get("data", {}).get("list")[0].get("id")
raise Exception("Failed to get suit, the suit does not exist, please check the style and color")
def get_runtime_parameters(self) -> list[ToolParameter]:
"""
Get runtime parameters
@@ -504,43 +458,40 @@ class AIPPTGenerateTool(BuiltinTool):
Override this method to add runtime parameters to the tool.
"""
try:
colors, styles = self.get_styles(user_id='__dify_system__')
colors, styles = self.get_styles(user_id="__dify_system__")
except Exception as e:
colors, styles = [
{'id': '-1', 'name': '__default__', 'en_name': '__default__'}
], [
{'id': '-1', 'name': '__default__', 'en_name': '__default__'}
]
colors, styles = (
[{"id": "-1", "name": "__default__", "en_name": "__default__"}],
[{"id": "-1", "name": "__default__", "en_name": "__default__"}],
)
return [
ToolParameter(
name='color',
label=I18nObject(zh_Hans='颜色', en_US='Color'),
human_description=I18nObject(zh_Hans='颜色', en_US='Color'),
name="color",
label=I18nObject(zh_Hans="颜色", en_US="Color"),
human_description=I18nObject(zh_Hans="颜色", en_US="Color"),
type=ToolParameter.ToolParameterType.SELECT,
form=ToolParameter.ToolParameterForm.FORM,
required=False,
default=colors[0]['id'],
default=colors[0]["id"],
options=[
ToolParameterOption(
value=color['id'],
label=I18nObject(zh_Hans=color['name'], en_US=color['en_name'])
) for color in colors
]
value=color["id"], label=I18nObject(zh_Hans=color["name"], en_US=color["en_name"])
)
for color in colors
],
),
ToolParameter(
name='style',
label=I18nObject(zh_Hans='风格', en_US='Style'),
human_description=I18nObject(zh_Hans='风格', en_US='Style'),
name="style",
label=I18nObject(zh_Hans="风格", en_US="Style"),
human_description=I18nObject(zh_Hans="风格", en_US="Style"),
type=ToolParameter.ToolParameterType.SELECT,
form=ToolParameter.ToolParameterForm.FORM,
required=False,
default=styles[0]['id'],
default=styles[0]["id"],
options=[
ToolParameterOption(
value=style['id'],
label=I18nObject(zh_Hans=style['name'], en_US=style['name'])
) for style in styles
]
ToolParameterOption(value=style["id"], label=I18nObject(zh_Hans=style["name"], en_US=style["name"]))
for style in styles
],
),
]
]

View File

@@ -13,7 +13,7 @@ class AlphaVantageProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"code": "AAPL", # Apple Inc.
},

View File

@@ -9,17 +9,16 @@ ALPHAVANTAGE_API_URL = "https://www.alphavantage.co/query"
class QueryStockTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
stock_code = tool_parameters.get('code', '')
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
stock_code = tool_parameters.get("code", "")
if not stock_code:
return self.create_text_message('Please tell me your stock code')
return self.create_text_message("Please tell me your stock code")
if 'api_key' not in self.runtime.credentials or not self.runtime.credentials.get('api_key'):
if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
return self.create_text_message("Alpha Vantage API key is required.")
params = {
@@ -27,7 +26,7 @@ class QueryStockTool(BuiltinTool):
"symbol": stock_code,
"outputsize": "compact",
"datatype": "json",
"apikey": self.runtime.credentials['api_key']
"apikey": self.runtime.credentials["api_key"],
}
response = requests.get(url=ALPHAVANTAGE_API_URL, params=params)
response.raise_for_status()
@@ -35,15 +34,15 @@ class QueryStockTool(BuiltinTool):
return self.create_json_message(result)
def _handle_response(self, response: dict[str, Any]) -> dict[str, Any]:
result = response.get('Time Series (Daily)', {})
result = response.get("Time Series (Daily)", {})
if not result:
return {}
stock_result = {}
for k, v in result.items():
stock_result[k] = {}
stock_result[k]['open'] = v.get('1. open')
stock_result[k]['high'] = v.get('2. high')
stock_result[k]['low'] = v.get('3. low')
stock_result[k]['close'] = v.get('4. close')
stock_result[k]['volume'] = v.get('5. volume')
stock_result[k]["open"] = v.get("1. open")
stock_result[k]["high"] = v.get("2. high")
stock_result[k]["low"] = v.get("3. low")
stock_result[k]["close"] = v.get("4. close")
stock_result[k]["volume"] = v.get("5. volume")
return stock_result

View File

@@ -11,11 +11,10 @@ class ArxivProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"query": "John Doe",
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -8,6 +8,8 @@ from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool
logger = logging.getLogger(__name__)
class ArxivAPIWrapper(BaseModel):
"""Wrapper around ArxivAPI.
@@ -86,11 +88,13 @@ class ArxivAPIWrapper(BaseModel):
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.
@@ -102,13 +106,13 @@ class ArxivSearchTool(BuiltinTool):
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', '')
query = tool_parameters.get("query", "")
if not query:
return self.create_text_message('Please input 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))

View File

@@ -11,15 +11,14 @@ class SageMakerProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"sagemaker_endpoint" : "",
"sagemaker_endpoint": "",
"query": "misaka mikoto",
"candidate_texts" : "hello$$$hello world",
"topk" : 5,
"aws_region" : ""
"candidate_texts": "hello$$$hello world",
"topk": 5,
"aws_region": "",
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -12,6 +12,7 @@ from core.tools.tool.builtin_tool import BuiltinTool
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class GuardrailParameters(BaseModel):
guardrail_id: str = Field(..., description="The identifier of the guardrail")
guardrail_version: str = Field(..., description="The version of the guardrail")
@@ -19,35 +20,35 @@ class GuardrailParameters(BaseModel):
text: str = Field(..., description="The text to apply the guardrail to")
aws_region: str = Field(..., description="AWS region for the Bedrock client")
class ApplyGuardrailTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
Invoke the ApplyGuardrail tool
"""
try:
# Validate and parse input parameters
params = GuardrailParameters(**tool_parameters)
# Initialize AWS client
bedrock_client = boto3.client('bedrock-runtime', region_name=params.aws_region)
bedrock_client = boto3.client("bedrock-runtime", region_name=params.aws_region)
# Apply guardrail
response = bedrock_client.apply_guardrail(
guardrailIdentifier=params.guardrail_id,
guardrailVersion=params.guardrail_version,
source=params.source,
content=[{"text": {"text": params.text}}]
content=[{"text": {"text": params.text}}],
)
logger.info(f"Raw response from AWS: {json.dumps(response, indent=2)}")
# Check for empty response
if not response:
return self.create_text_message(text="Received empty response from AWS Bedrock.")
# Process the result
action = response.get("action", "No action specified")
outputs = response.get("outputs", [])
@@ -58,9 +59,11 @@ class ApplyGuardrailTool(BuiltinTool):
formatted_assessments = []
for assessment in assessments:
for policy_type, policy_data in assessment.items():
if isinstance(policy_data, dict) and 'topics' in policy_data:
for topic in policy_data['topics']:
formatted_assessments.append(f"Policy: {policy_type}, Topic: {topic['name']}, Type: {topic['type']}, Action: {topic['action']}")
if isinstance(policy_data, dict) and "topics" in policy_data:
for topic in policy_data["topics"]:
formatted_assessments.append(
f"Policy: {policy_type}, Topic: {topic['name']}, Type: {topic['type']}, Action: {topic['action']}"
)
else:
formatted_assessments.append(f"Policy: {policy_type}, Data: {policy_data}")
@@ -68,19 +71,19 @@ class ApplyGuardrailTool(BuiltinTool):
result += f"Output: {output}\n "
if formatted_assessments:
result += "Assessments:\n " + "\n ".join(formatted_assessments) + "\n "
# result += f"Full response: {json.dumps(response, indent=2, ensure_ascii=False)}"
# result += f"Full response: {json.dumps(response, indent=2, ensure_ascii=False)}"
return self.create_text_message(text=result)
except BotoCoreError as e:
error_message = f'AWS service error: {str(e)}'
error_message = f"AWS service error: {str(e)}"
logger.error(error_message, exc_info=True)
return self.create_text_message(text=error_message)
except json.JSONDecodeError as e:
error_message = f'JSON parsing error: {str(e)}'
error_message = f"JSON parsing error: {str(e)}"
logger.error(error_message, exc_info=True)
return self.create_text_message(text=error_message)
except Exception as e:
error_message = f'An unexpected error occurred: {str(e)}'
error_message = f"An unexpected error occurred: {str(e)}"
logger.error(error_message, exc_info=True)
return self.create_text_message(text=error_message)
return self.create_text_message(text=error_message)

View File

@@ -11,78 +11,81 @@ class LambdaTranslateUtilsTool(BuiltinTool):
lambda_client: Any = None
def _invoke_lambda(self, text_content, src_lang, dest_lang, model_id, dictionary_name, request_type, lambda_name):
msg = {
"src_content":text_content,
"src_lang": src_lang,
"dest_lang":dest_lang,
msg = {
"src_content": text_content,
"src_lang": src_lang,
"dest_lang": dest_lang,
"dictionary_id": dictionary_name,
"request_type" : request_type,
"model_id" : model_id
"request_type": request_type,
"model_id": model_id,
}
invoke_response = self.lambda_client.invoke(FunctionName=lambda_name,
InvocationType='RequestResponse',
Payload=json.dumps(msg))
response_body = invoke_response['Payload']
invoke_response = self.lambda_client.invoke(
FunctionName=lambda_name, InvocationType="RequestResponse", Payload=json.dumps(msg)
)
response_body = invoke_response["Payload"]
response_str = response_body.read().decode("unicode_escape")
return response_str
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
line = 0
try:
if not self.lambda_client:
aws_region = tool_parameters.get('aws_region')
aws_region = tool_parameters.get("aws_region")
if aws_region:
self.lambda_client = boto3.client("lambda", region_name=aws_region)
else:
self.lambda_client = boto3.client("lambda")
line = 1
text_content = tool_parameters.get('text_content', '')
text_content = tool_parameters.get("text_content", "")
if not text_content:
return self.create_text_message('Please input text_content')
return self.create_text_message("Please input text_content")
line = 2
src_lang = tool_parameters.get('src_lang', '')
src_lang = tool_parameters.get("src_lang", "")
if not src_lang:
return self.create_text_message('Please input src_lang')
return self.create_text_message("Please input src_lang")
line = 3
dest_lang = tool_parameters.get('dest_lang', '')
dest_lang = tool_parameters.get("dest_lang", "")
if not dest_lang:
return self.create_text_message('Please input dest_lang')
return self.create_text_message("Please input dest_lang")
line = 4
lambda_name = tool_parameters.get('lambda_name', '')
lambda_name = tool_parameters.get("lambda_name", "")
if not lambda_name:
return self.create_text_message('Please input lambda_name')
return self.create_text_message("Please input lambda_name")
line = 5
request_type = tool_parameters.get('request_type', '')
request_type = tool_parameters.get("request_type", "")
if not request_type:
return self.create_text_message('Please input request_type')
return self.create_text_message("Please input request_type")
line = 6
model_id = tool_parameters.get('model_id', '')
model_id = tool_parameters.get("model_id", "")
if not model_id:
return self.create_text_message('Please input model_id')
return self.create_text_message("Please input model_id")
line = 7
dictionary_name = tool_parameters.get('dictionary_name', '')
dictionary_name = tool_parameters.get("dictionary_name", "")
if not dictionary_name:
return self.create_text_message('Please input dictionary_name')
result = self._invoke_lambda(text_content, src_lang, dest_lang, model_id, dictionary_name, request_type, lambda_name)
return self.create_text_message("Please input dictionary_name")
result = self._invoke_lambda(
text_content, src_lang, dest_lang, model_id, dictionary_name, request_type, lambda_name
)
return self.create_text_message(text=result)
except Exception as e:
return self.create_text_message(f'Exception {str(e)}, line : {line}')
return self.create_text_message(f"Exception {str(e)}, line : {line}")

View File

@@ -18,54 +18,53 @@ class LambdaYamlToJsonTool(BuiltinTool):
lambda_client: Any = None
def _invoke_lambda(self, lambda_name: str, yaml_content: str) -> str:
msg = {
"body": yaml_content
}
msg = {"body": yaml_content}
logger.info(json.dumps(msg))
invoke_response = self.lambda_client.invoke(FunctionName=lambda_name,
InvocationType='RequestResponse',
Payload=json.dumps(msg))
response_body = invoke_response['Payload']
invoke_response = self.lambda_client.invoke(
FunctionName=lambda_name, InvocationType="RequestResponse", Payload=json.dumps(msg)
)
response_body = invoke_response["Payload"]
response_str = response_body.read().decode("utf-8")
resp_json = json.loads(response_str)
logger.info(resp_json)
if resp_json['statusCode'] != 200:
if resp_json["statusCode"] != 200:
raise Exception(f"Invalid status code: {response_str}")
return resp_json['body']
return resp_json["body"]
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
try:
if not self.lambda_client:
aws_region = tool_parameters.get('aws_region') # todo: move aws_region out, and update client region
aws_region = tool_parameters.get("aws_region") # todo: move aws_region out, and update client region
if aws_region:
self.lambda_client = boto3.client("lambda", region_name=aws_region)
else:
self.lambda_client = boto3.client("lambda")
yaml_content = tool_parameters.get('yaml_content', '')
yaml_content = tool_parameters.get("yaml_content", "")
if not yaml_content:
return self.create_text_message('Please input yaml_content')
return self.create_text_message("Please input yaml_content")
lambda_name = tool_parameters.get('lambda_name', '')
lambda_name = tool_parameters.get("lambda_name", "")
if not lambda_name:
return self.create_text_message('Please input lambda_name')
logger.debug(f'{json.dumps(tool_parameters, indent=2, ensure_ascii=False)}')
return self.create_text_message("Please input lambda_name")
logger.debug(f"{json.dumps(tool_parameters, indent=2, ensure_ascii=False)}")
result = self._invoke_lambda(lambda_name, yaml_content)
logger.debug(result)
return self.create_text_message(result)
except Exception as e:
return self.create_text_message(f'Exception: {str(e)}')
return self.create_text_message(f"Exception: {str(e)}")
console_handler.flush()
console_handler.flush()

View File

@@ -9,37 +9,33 @@ from core.tools.tool.builtin_tool import BuiltinTool
class SageMakerReRankTool(BuiltinTool):
sagemaker_client: Any = None
sagemaker_endpoint:str = None
topk:int = None
sagemaker_endpoint: str = None
topk: int = None
def _sagemaker_rerank(self, query_input: str, docs: list[str], rerank_endpoint:str):
inputs = [query_input]*len(docs)
def _sagemaker_rerank(self, query_input: str, docs: list[str], rerank_endpoint: str):
inputs = [query_input] * len(docs)
response_model = self.sagemaker_client.invoke_endpoint(
EndpointName=rerank_endpoint,
Body=json.dumps(
{
"inputs": inputs,
"docs": docs
}
),
Body=json.dumps({"inputs": inputs, "docs": docs}),
ContentType="application/json",
)
json_str = response_model['Body'].read().decode('utf8')
json_str = response_model["Body"].read().decode("utf8")
json_obj = json.loads(json_str)
scores = json_obj['scores']
scores = json_obj["scores"]
return scores if isinstance(scores, list) else [scores]
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
line = 0
try:
if not self.sagemaker_client:
aws_region = tool_parameters.get('aws_region')
aws_region = tool_parameters.get("aws_region")
if aws_region:
self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region)
else:
@@ -47,25 +43,25 @@ class SageMakerReRankTool(BuiltinTool):
line = 1
if not self.sagemaker_endpoint:
self.sagemaker_endpoint = tool_parameters.get('sagemaker_endpoint')
self.sagemaker_endpoint = tool_parameters.get("sagemaker_endpoint")
line = 2
if not self.topk:
self.topk = tool_parameters.get('topk', 5)
self.topk = tool_parameters.get("topk", 5)
line = 3
query = tool_parameters.get('query', '')
query = tool_parameters.get("query", "")
if not query:
return self.create_text_message('Please input query')
return self.create_text_message("Please input query")
line = 4
candidate_texts = tool_parameters.get('candidate_texts')
candidate_texts = tool_parameters.get("candidate_texts")
if not candidate_texts:
return self.create_text_message('Please input candidate_texts')
return self.create_text_message("Please input candidate_texts")
line = 5
candidate_docs = json.loads(candidate_texts)
docs = [ item.get('content') for item in candidate_docs ]
docs = [item.get("content") for item in candidate_docs]
line = 6
scores = self._sagemaker_rerank(query_input=query, docs=docs, rerank_endpoint=self.sagemaker_endpoint)
@@ -75,10 +71,10 @@ class SageMakerReRankTool(BuiltinTool):
candidate_docs[idx]["score"] = scores[idx]
line = 8
sorted_candidate_docs = sorted(candidate_docs, key=lambda x: x['score'], reverse=True)
sorted_candidate_docs = sorted(candidate_docs, key=lambda x: x["score"], reverse=True)
line = 9
return [ self.create_json_message(res) for res in sorted_candidate_docs[:self.topk] ]
return [self.create_json_message(res) for res in sorted_candidate_docs[: self.topk]]
except Exception as e:
return self.create_text_message(f'Exception {str(e)}, line : {line}')
return self.create_text_message(f"Exception {str(e)}, line : {line}")

View File

@@ -14,82 +14,88 @@ class TTSModelType(Enum):
CloneVoice_CrossLingual = "CloneVoice_CrossLingual"
InstructVoice = "InstructVoice"
class SageMakerTTSTool(BuiltinTool):
sagemaker_client: Any = None
sagemaker_endpoint:str = None
s3_client : Any = None
comprehend_client : Any = None
sagemaker_endpoint: str = None
s3_client: Any = None
comprehend_client: Any = None
def _detect_lang_code(self, content:str, map_dict:dict=None):
map_dict = {
"zh" : "<|zh|>",
"en" : "<|en|>",
"ja" : "<|jp|>",
"zh-TW" : "<|yue|>",
"ko" : "<|ko|>"
}
def _detect_lang_code(self, content: str, map_dict: dict = None):
map_dict = {"zh": "<|zh|>", "en": "<|en|>", "ja": "<|jp|>", "zh-TW": "<|yue|>", "ko": "<|ko|>"}
response = self.comprehend_client.detect_dominant_language(Text=content)
language_code = response['Languages'][0]['LanguageCode']
return map_dict.get(language_code, '<|zh|>')
language_code = response["Languages"][0]["LanguageCode"]
return map_dict.get(language_code, "<|zh|>")
def _build_tts_payload(self, model_type:str, content_text:str, model_role:str, prompt_text:str, prompt_audio:str, instruct_text:str):
def _build_tts_payload(
self,
model_type: str,
content_text: str,
model_role: str,
prompt_text: str,
prompt_audio: str,
instruct_text: str,
):
if model_type == TTSModelType.PresetVoice.value and model_role:
return { "tts_text" : content_text, "role" : model_role }
return {"tts_text": content_text, "role": model_role}
if model_type == TTSModelType.CloneVoice.value and prompt_text and prompt_audio:
return { "tts_text" : content_text, "prompt_text": prompt_text, "prompt_audio" : prompt_audio }
if model_type == TTSModelType.CloneVoice_CrossLingual.value and prompt_audio:
return {"tts_text": content_text, "prompt_text": prompt_text, "prompt_audio": prompt_audio}
if model_type == TTSModelType.CloneVoice_CrossLingual.value and prompt_audio:
lang_tag = self._detect_lang_code(content_text)
return { "tts_text" : f"{content_text}", "prompt_audio" : prompt_audio, "lang_tag" : lang_tag }
if model_type == TTSModelType.InstructVoice.value and instruct_text and model_role:
return { "tts_text" : content_text, "role" : model_role, "instruct_text" : instruct_text }
return {"tts_text": f"{content_text}", "prompt_audio": prompt_audio, "lang_tag": lang_tag}
if model_type == TTSModelType.InstructVoice.value and instruct_text and model_role:
return {"tts_text": content_text, "role": model_role, "instruct_text": instruct_text}
raise RuntimeError(f"Invalid params for {model_type}")
def _invoke_sagemaker(self, payload:dict, endpoint:str):
def _invoke_sagemaker(self, payload: dict, endpoint: str):
response_model = self.sagemaker_client.invoke_endpoint(
EndpointName=endpoint,
Body=json.dumps(payload),
ContentType="application/json",
)
json_str = response_model['Body'].read().decode('utf8')
json_str = response_model["Body"].read().decode("utf8")
json_obj = json.loads(json_str)
return json_obj
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
try:
if not self.sagemaker_client:
aws_region = tool_parameters.get('aws_region')
aws_region = tool_parameters.get("aws_region")
if aws_region:
self.sagemaker_client = boto3.client("sagemaker-runtime", region_name=aws_region)
self.s3_client = boto3.client("s3", region_name=aws_region)
self.comprehend_client = boto3.client('comprehend', region_name=aws_region)
self.comprehend_client = boto3.client("comprehend", region_name=aws_region)
else:
self.sagemaker_client = boto3.client("sagemaker-runtime")
self.s3_client = boto3.client("s3")
self.comprehend_client = boto3.client('comprehend')
self.comprehend_client = boto3.client("comprehend")
if not self.sagemaker_endpoint:
self.sagemaker_endpoint = tool_parameters.get('sagemaker_endpoint')
self.sagemaker_endpoint = tool_parameters.get("sagemaker_endpoint")
tts_text = tool_parameters.get('tts_text')
tts_infer_type = tool_parameters.get('tts_infer_type')
tts_text = tool_parameters.get("tts_text")
tts_infer_type = tool_parameters.get("tts_infer_type")
voice = tool_parameters.get('voice')
mock_voice_audio = tool_parameters.get('mock_voice_audio')
mock_voice_text = tool_parameters.get('mock_voice_text')
voice_instruct_prompt = tool_parameters.get('voice_instruct_prompt')
payload = self._build_tts_payload(tts_infer_type, tts_text, voice, mock_voice_text, mock_voice_audio, voice_instruct_prompt)
voice = tool_parameters.get("voice")
mock_voice_audio = tool_parameters.get("mock_voice_audio")
mock_voice_text = tool_parameters.get("mock_voice_text")
voice_instruct_prompt = tool_parameters.get("voice_instruct_prompt")
payload = self._build_tts_payload(
tts_infer_type, tts_text, voice, mock_voice_text, mock_voice_audio, voice_instruct_prompt
)
result = self._invoke_sagemaker(payload, self.sagemaker_endpoint)
return self.create_text_message(text=result['s3_presign_url'])
return self.create_text_message(text=result["s3_presign_url"])
except Exception as e:
return self.create_text_message(f'Exception {str(e)}')
return self.create_text_message(f"Exception {str(e)}")

View File

@@ -13,12 +13,8 @@ class AzureDALLEProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
tool_parameters={
"prompt": "cute girl, blue eyes, white hair, anime style",
"size": "square",
"n": 1
},
user_id="",
tool_parameters={"prompt": "cute girl, blue eyes, white hair, anime style", "size": "square", "n": 1},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -9,47 +9,48 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DallE3Tool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
client = AzureOpenAI(
api_version=self.runtime.credentials['azure_openai_api_version'],
azure_endpoint=self.runtime.credentials['azure_openai_base_url'],
api_key=self.runtime.credentials['azure_openai_api_key'],
api_version=self.runtime.credentials["azure_openai_api_version"],
azure_endpoint=self.runtime.credentials["azure_openai_base_url"],
api_key=self.runtime.credentials["azure_openai_api_key"],
)
SIZE_MAPPING = {
'square': '1024x1024',
'vertical': '1024x1792',
'horizontal': '1792x1024',
"square": "1024x1024",
"vertical": "1024x1792",
"horizontal": "1792x1024",
}
# prompt
prompt = tool_parameters.get('prompt', '')
prompt = tool_parameters.get("prompt", "")
if not prompt:
return self.create_text_message('Please input prompt')
return self.create_text_message("Please input prompt")
# get size
size = SIZE_MAPPING[tool_parameters.get('size', 'square')]
size = SIZE_MAPPING[tool_parameters.get("size", "square")]
# get n
n = tool_parameters.get('n', 1)
n = tool_parameters.get("n", 1)
# get quality
quality = tool_parameters.get('quality', 'standard')
if quality not in ['standard', 'hd']:
return self.create_text_message('Invalid quality')
quality = tool_parameters.get("quality", "standard")
if quality not in ["standard", "hd"]:
return self.create_text_message("Invalid quality")
# get style
style = tool_parameters.get('style', 'vivid')
if style not in ['natural', 'vivid']:
return self.create_text_message('Invalid style')
style = tool_parameters.get("style", "vivid")
if style not in ["natural", "vivid"]:
return self.create_text_message("Invalid style")
# set extra body
seed_id = tool_parameters.get('seed_id', self._generate_random_id(8))
extra_body = {'seed': seed_id}
seed_id = tool_parameters.get("seed_id", self._generate_random_id(8))
extra_body = {"seed": seed_id}
# call openapi dalle3
model = self.runtime.credentials['azure_openai_api_model_name']
model = self.runtime.credentials["azure_openai_api_model_name"]
response = client.images.generate(
prompt=prompt,
model=model,
@@ -58,21 +59,25 @@ class DallE3Tool(BuiltinTool):
extra_body=extra_body,
style=style,
quality=quality,
response_format='b64_json'
response_format="b64_json",
)
result = []
for image in response.data:
result.append(self.create_blob_message(blob=b64decode(image.b64_json),
meta={'mime_type': 'image/png'},
save_as=self.VARIABLE_KEY.IMAGE.value))
result.append(self.create_text_message(f'\nGenerate image source to Seed ID: {seed_id}'))
result.append(
self.create_blob_message(
blob=b64decode(image.b64_json),
meta={"mime_type": "image/png"},
save_as=self.VARIABLE_KEY.IMAGE.value,
)
)
result.append(self.create_text_message(f"\nGenerate image source to Seed ID: {seed_id}"))
return result
@staticmethod
def _generate_random_id(length=8):
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
random_id = ''.join(random.choices(characters, k=length))
characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
random_id = "".join(random.choices(characters, k=length))
return random_id

View File

@@ -8,142 +8,135 @@ from core.tools.tool.builtin_tool import BuiltinTool
class BingSearchTool(BuiltinTool):
url: str = 'https://api.bing.microsoft.com/v7.0/search'
url: str = "https://api.bing.microsoft.com/v7.0/search"
def _invoke_bing(self,
user_id: str,
server_url: str,
subscription_key: str, query: str, limit: int,
result_type: str, market: str, lang: str,
filters: list[str]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke_bing(
self,
user_id: str,
server_url: str,
subscription_key: str,
query: str,
limit: int,
result_type: str,
market: str,
lang: str,
filters: list[str],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke bing search
invoke bing search
"""
market_code = f'{lang}-{market}'
accept_language = f'{lang},{market_code};q=0.9'
headers = {
'Ocp-Apim-Subscription-Key': subscription_key,
'Accept-Language': accept_language
}
market_code = f"{lang}-{market}"
accept_language = f"{lang},{market_code};q=0.9"
headers = {"Ocp-Apim-Subscription-Key": subscription_key, "Accept-Language": accept_language}
query = quote(query)
server_url = f'{server_url}?q={query}&mkt={market_code}&count={limit}&responseFilter={",".join(filters)}'
response = get(server_url, headers=headers)
if response.status_code != 200:
raise Exception(f'Error {response.status_code}: {response.text}')
response = response.json()
search_results = response['webPages']['value'][:limit] if 'webPages' in response else []
related_searches = response['relatedSearches']['value'] if 'relatedSearches' in response else []
entities = response['entities']['value'] if 'entities' in response else []
news = response['news']['value'] if 'news' in response else []
computation = response['computation']['value'] if 'computation' in response else None
raise Exception(f"Error {response.status_code}: {response.text}")
if result_type == 'link':
response = response.json()
search_results = response["webPages"]["value"][:limit] if "webPages" in response else []
related_searches = response["relatedSearches"]["value"] if "relatedSearches" in response else []
entities = response["entities"]["value"] if "entities" in response else []
news = response["news"]["value"] if "news" in response else []
computation = response["computation"]["value"] if "computation" in response else None
if result_type == "link":
results = []
if search_results:
for result in search_results:
url = f': {result["url"]}' if "url" in result else ""
results.append(self.create_text_message(
text=f'{result["name"]}{url}'
))
results.append(self.create_text_message(text=f'{result["name"]}{url}'))
if entities:
for entity in entities:
url = f': {entity["url"]}' if "url" in entity else ""
results.append(self.create_text_message(
text=f'{entity.get("name", "")}{url}'
))
results.append(self.create_text_message(text=f'{entity.get("name", "")}{url}'))
if news:
for news_item in news:
url = f': {news_item["url"]}' if "url" in news_item else ""
results.append(self.create_text_message(
text=f'{news_item.get("name", "")}{url}'
))
results.append(self.create_text_message(text=f'{news_item.get("name", "")}{url}'))
if related_searches:
for related in related_searches:
url = f': {related["displayText"]}' if "displayText" in related else ""
results.append(self.create_text_message(
text=f'{related.get("displayText", "")}{url}'
))
results.append(self.create_text_message(text=f'{related.get("displayText", "")}{url}'))
return results
else:
# construct text
text = ''
text = ""
if search_results:
for i, result in enumerate(search_results):
text += f'{i+1}: {result.get("name", "")} - {result.get("snippet", "")}\n'
if computation and 'expression' in computation and 'value' in computation:
text += '\nComputation:\n'
if computation and "expression" in computation and "value" in computation:
text += "\nComputation:\n"
text += f'{computation["expression"]} = {computation["value"]}\n'
if entities:
text += '\nEntities:\n'
text += "\nEntities:\n"
for entity in entities:
url = f'- {entity["url"]}' if "url" in entity else ""
text += f'{entity.get("name", "")}{url}\n'
if news:
text += '\nNews:\n'
text += "\nNews:\n"
for news_item in news:
url = f'- {news_item["url"]}' if "url" in news_item else ""
text += f'{news_item.get("name", "")}{url}\n'
if related_searches:
text += '\n\nRelated Searches:\n'
text += "\n\nRelated Searches:\n"
for related in related_searches:
url = f'- {related["webSearchUrl"]}' if "webSearchUrl" in related else ""
text += f'{related.get("displayText", "")}{url}\n'
return self.create_text_message(text=self.summary(user_id=user_id, content=text))
def validate_credentials(self, credentials: dict[str, Any], tool_parameters: dict[str, Any]) -> None:
key = credentials.get('subscription_key')
key = credentials.get("subscription_key")
if not key:
raise Exception('subscription_key is required')
server_url = credentials.get('server_url')
raise Exception("subscription_key is required")
server_url = credentials.get("server_url")
if not server_url:
server_url = self.url
query = tool_parameters.get('query')
query = tool_parameters.get("query")
if not query:
raise Exception('query is required')
limit = min(tool_parameters.get('limit', 5), 10)
result_type = tool_parameters.get('result_type', 'text') or 'text'
raise Exception("query is required")
market = tool_parameters.get('market', 'US')
lang = tool_parameters.get('language', 'en')
limit = min(tool_parameters.get("limit", 5), 10)
result_type = tool_parameters.get("result_type", "text") or "text"
market = tool_parameters.get("market", "US")
lang = tool_parameters.get("language", "en")
filter = []
if credentials.get('allow_entities', False):
filter.append('Entities')
if credentials.get("allow_entities", False):
filter.append("Entities")
if credentials.get('allow_computation', False):
filter.append('Computation')
if credentials.get("allow_computation", False):
filter.append("Computation")
if credentials.get('allow_news', False):
filter.append('News')
if credentials.get("allow_news", False):
filter.append("News")
if credentials.get('allow_related_searches', False):
filter.append('RelatedSearches')
if credentials.get("allow_related_searches", False):
filter.append("RelatedSearches")
if credentials.get('allow_web_pages', False):
filter.append('WebPages')
if credentials.get("allow_web_pages", False):
filter.append("WebPages")
if not filter:
raise Exception('At least one filter is required')
raise Exception("At least one filter is required")
self._invoke_bing(
user_id='test',
user_id="test",
server_url=server_url,
subscription_key=key,
query=query,
@@ -151,50 +144,51 @@ class BingSearchTool(BuiltinTool):
result_type=result_type,
market=market,
lang=lang,
filters=filter
filters=filter,
)
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
key = self.runtime.credentials.get('subscription_key', None)
key = self.runtime.credentials.get("subscription_key", None)
if not key:
raise Exception('subscription_key is required')
server_url = self.runtime.credentials.get('server_url', None)
raise Exception("subscription_key is required")
server_url = self.runtime.credentials.get("server_url", None)
if not server_url:
server_url = self.url
query = tool_parameters.get('query')
query = tool_parameters.get("query")
if not query:
raise Exception('query is required')
limit = min(tool_parameters.get('limit', 5), 10)
result_type = tool_parameters.get('result_type', 'text') or 'text'
market = tool_parameters.get('market', 'US')
lang = tool_parameters.get('language', 'en')
raise Exception("query is required")
limit = min(tool_parameters.get("limit", 5), 10)
result_type = tool_parameters.get("result_type", "text") or "text"
market = tool_parameters.get("market", "US")
lang = tool_parameters.get("language", "en")
filter = []
if tool_parameters.get('enable_computation', False):
filter.append('Computation')
if tool_parameters.get('enable_entities', False):
filter.append('Entities')
if tool_parameters.get('enable_news', False):
filter.append('News')
if tool_parameters.get('enable_related_search', False):
filter.append('RelatedSearches')
if tool_parameters.get('enable_webpages', False):
filter.append('WebPages')
if tool_parameters.get("enable_computation", False):
filter.append("Computation")
if tool_parameters.get("enable_entities", False):
filter.append("Entities")
if tool_parameters.get("enable_news", False):
filter.append("News")
if tool_parameters.get("enable_related_search", False):
filter.append("RelatedSearches")
if tool_parameters.get("enable_webpages", False):
filter.append("WebPages")
if not filter:
raise Exception('At least one filter is required')
raise Exception("At least one filter is required")
return self._invoke_bing(
user_id=user_id,
server_url=server_url,
@@ -204,5 +198,5 @@ class BingSearchTool(BuiltinTool):
result_type=result_type,
market=market,
lang=lang,
filters=filter
)
filters=filter,
)

View File

@@ -13,11 +13,10 @@ class BraveProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"query": "Sachin Tendulkar",
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -37,7 +37,7 @@ class BraveSearchWrapper(BaseModel):
for item in web_search_results
]
return json.dumps(final_results)
def _search_request(self, query: str) -> list[dict]:
headers = {
"X-Subscription-Token": self.api_key,
@@ -55,6 +55,7 @@ class BraveSearchWrapper(BaseModel):
return response.json().get("web", {}).get("results", [])
class BraveSearch(BaseModel):
"""Tool that queries the BraveSearch."""
@@ -67,9 +68,7 @@ class BraveSearch(BaseModel):
search_wrapper: BraveSearchWrapper
@classmethod
def from_api_key(
cls, api_key: str, search_kwargs: Optional[dict] = None, **kwargs: Any
) -> "BraveSearch":
def from_api_key(cls, api_key: str, search_kwargs: Optional[dict] = None, **kwargs: Any) -> "BraveSearch":
"""Create a tool from an api key.
Args:
@@ -90,6 +89,7 @@ class BraveSearch(BaseModel):
"""Use the tool."""
return self.search_wrapper.run(query)
class BraveSearchTool(BuiltinTool):
"""
Tool for performing a search using Brave search engine.
@@ -106,12 +106,12 @@ class BraveSearchTool(BuiltinTool):
Returns:
ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation.
"""
query = tool_parameters.get('query', '')
count = tool_parameters.get('count', 3)
api_key = self.runtime.credentials['brave_search_api_key']
query = tool_parameters.get("query", "")
count = tool_parameters.get("count", 3)
api_key = self.runtime.credentials["brave_search_api_key"]
if not query:
return self.create_text_message('Please input query')
return self.create_text_message("Please input query")
tool = BraveSearch.from_api_key(api_key=api_key, search_kwargs={"count": count})
@@ -121,4 +121,3 @@ class BraveSearchTool(BuiltinTool):
return self.create_text_message(f"No results found for '{query}' in Tavily")
else:
return self.create_text_message(text=results)

View File

@@ -7,16 +7,34 @@ from core.tools.provider.builtin.chart.tools.line import LinearChartTool
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
# use a business theme
plt.style.use('seaborn-v0_8-darkgrid')
plt.rcParams['axes.unicode_minus'] = False
plt.style.use("seaborn-v0_8-darkgrid")
plt.rcParams["axes.unicode_minus"] = False
def init_fonts():
fonts = findSystemFonts()
popular_unicode_fonts = [
'Arial Unicode MS', 'DejaVu Sans', 'DejaVu Sans Mono', 'DejaVu Serif', 'FreeMono', 'FreeSans', 'FreeSerif',
'Liberation Mono', 'Liberation Sans', 'Liberation Serif', 'Noto Mono', 'Noto Sans', 'Noto Serif', 'Open Sans',
'Roboto', 'Source Code Pro', 'Source Sans Pro', 'Source Serif Pro', 'Ubuntu', 'Ubuntu Mono'
"Arial Unicode MS",
"DejaVu Sans",
"DejaVu Sans Mono",
"DejaVu Serif",
"FreeMono",
"FreeSans",
"FreeSerif",
"Liberation Mono",
"Liberation Sans",
"Liberation Serif",
"Noto Mono",
"Noto Sans",
"Noto Serif",
"Open Sans",
"Roboto",
"Source Code Pro",
"Source Sans Pro",
"Source Serif Pro",
"Ubuntu",
"Ubuntu Mono",
]
supported_fonts = []
@@ -25,21 +43,23 @@ def init_fonts():
try:
font = TTFont(font_path)
# get family name
family_name = font['name'].getName(1, 3, 1).toUnicode()
family_name = font["name"].getName(1, 3, 1).toUnicode()
if family_name in popular_unicode_fonts:
supported_fonts.append(family_name)
except:
pass
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams["font.family"] = "sans-serif"
# sort by order of popular_unicode_fonts
for font in popular_unicode_fonts:
if font in supported_fonts:
plt.rcParams['font.sans-serif'] = font
plt.rcParams["font.sans-serif"] = font
break
init_fonts()
class ChartProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
@@ -48,11 +68,10 @@ class ChartProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"data": "1,3,5,7,9,2,4,6,8,10",
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -8,12 +8,13 @@ from core.tools.tool.builtin_tool import BuiltinTool
class BarChartTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) \
-> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
data = tool_parameters.get('data', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
data = tool_parameters.get("data", "")
if not data:
return self.create_text_message('Please input data')
data = data.split(';')
return self.create_text_message("Please input data")
data = data.split(";")
# if all data is int, convert to int
if all(i.isdigit() for i in data):
@@ -21,29 +22,27 @@ class BarChartTool(BuiltinTool):
else:
data = [float(i) for i in data]
axis = tool_parameters.get('x_axis') or None
axis = tool_parameters.get("x_axis") or None
if axis:
axis = axis.split(';')
axis = axis.split(";")
if len(axis) != len(data):
axis = None
flg, ax = plt.subplots(figsize=(10, 8))
if axis:
axis = [label[:10] + '...' if len(label) > 10 else label for label in axis]
ax.set_xticklabels(axis, rotation=45, ha='right')
axis = [label[:10] + "..." if len(label) > 10 else label for label in axis]
ax.set_xticklabels(axis, rotation=45, ha="right")
ax.bar(axis, data)
else:
ax.bar(range(len(data)), data)
buf = io.BytesIO()
flg.savefig(buf, format='png')
flg.savefig(buf, format="png")
buf.seek(0)
plt.close(flg)
return [
self.create_text_message('the bar chart is saved as an image.'),
self.create_blob_message(blob=buf.read(),
meta={'mime_type': 'image/png'})
self.create_text_message("the bar chart is saved as an image."),
self.create_blob_message(blob=buf.read(), meta={"mime_type": "image/png"}),
]

View File

@@ -8,18 +8,19 @@ from core.tools.tool.builtin_tool import BuiltinTool
class LinearChartTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
data = tool_parameters.get('data', '')
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
data = tool_parameters.get("data", "")
if not data:
return self.create_text_message('Please input data')
data = data.split(';')
return self.create_text_message("Please input data")
data = data.split(";")
axis = tool_parameters.get('x_axis') or None
axis = tool_parameters.get("x_axis") or None
if axis:
axis = axis.split(';')
axis = axis.split(";")
if len(axis) != len(data):
axis = None
@@ -32,20 +33,18 @@ class LinearChartTool(BuiltinTool):
flg, ax = plt.subplots(figsize=(10, 8))
if axis:
axis = [label[:10] + '...' if len(label) > 10 else label for label in axis]
ax.set_xticklabels(axis, rotation=45, ha='right')
axis = [label[:10] + "..." if len(label) > 10 else label for label in axis]
ax.set_xticklabels(axis, rotation=45, ha="right")
ax.plot(axis, data)
else:
ax.plot(data)
buf = io.BytesIO()
flg.savefig(buf, format='png')
flg.savefig(buf, format="png")
buf.seek(0)
plt.close(flg)
return [
self.create_text_message('the linear chart is saved as an image.'),
self.create_blob_message(blob=buf.read(),
meta={'mime_type': 'image/png'})
self.create_text_message("the linear chart is saved as an image."),
self.create_blob_message(blob=buf.read(), meta={"mime_type": "image/png"}),
]

View File

@@ -8,15 +8,16 @@ from core.tools.tool.builtin_tool import BuiltinTool
class PieChartTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
data = tool_parameters.get('data', '')
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
data = tool_parameters.get("data", "")
if not data:
return self.create_text_message('Please input data')
data = data.split(';')
categories = tool_parameters.get('categories') or None
return self.create_text_message("Please input data")
data = data.split(";")
categories = tool_parameters.get("categories") or None
# if all data is int, convert to int
if all(i.isdigit() for i in data):
@@ -27,7 +28,7 @@ class PieChartTool(BuiltinTool):
flg, ax = plt.subplots()
if categories:
categories = categories.split(';')
categories = categories.split(";")
if len(categories) != len(data):
categories = None
@@ -37,12 +38,11 @@ class PieChartTool(BuiltinTool):
ax.pie(data)
buf = io.BytesIO()
flg.savefig(buf, format='png')
flg.savefig(buf, format="png")
buf.seek(0)
plt.close(flg)
return [
self.create_text_message('the pie chart is saved as an image.'),
self.create_blob_message(blob=buf.read(),
meta={'mime_type': 'image/png'})
]
self.create_text_message("the pie chart is saved as an image."),
self.create_blob_message(blob=buf.read(), meta={"mime_type": "image/png"}),
]

View File

@@ -8,15 +8,15 @@ from core.tools.tool.builtin_tool import BuiltinTool
class SimpleCode(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
"""
invoke simple code
invoke simple code
"""
language = tool_parameters.get('language', CodeLanguage.PYTHON3)
code = tool_parameters.get('code', '')
language = tool_parameters.get("language", CodeLanguage.PYTHON3)
code = tool_parameters.get("code", "")
if language not in [CodeLanguage.PYTHON3, CodeLanguage.JAVASCRIPT]:
raise ValueError(f'Only python3 and javascript are supported, not {language}')
result = CodeExecutor.execute_code(language, '', code)
raise ValueError(f"Only python3 and javascript are supported, not {language}")
return self.create_text_message(result)
result = CodeExecutor.execute_code(language, "", code)
return self.create_text_message(result)

View File

@@ -1,4 +1,5 @@
""" Provide the input parameters type for the cogview provider class """
"""Provide the input parameters type for the cogview provider class"""
from typing import Any
from core.tools.errors import ToolProviderCredentialValidationError
@@ -7,7 +8,8 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class COGVIEWProvider(BuiltinToolProviderController):
""" cogview provider """
"""cogview provider"""
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
CogView3Tool().fork_tool_runtime(
@@ -15,13 +17,12 @@ class COGVIEWProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"prompt": "一个城市在水晶瓶中欢快生活的场景,水彩画风格,展现出微观与珠宝般的美丽。",
"size": "square",
"n": 1
"n": 1,
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e)) from e

View File

@@ -7,43 +7,42 @@ from core.tools.tool.builtin_tool import BuiltinTool
class CogView3Tool(BuiltinTool):
""" CogView3 Tool """
"""CogView3 Tool"""
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
Invoke CogView3 tool
"""
client = ZhipuAI(
base_url=self.runtime.credentials['zhipuai_base_url'],
api_key=self.runtime.credentials['zhipuai_api_key'],
base_url=self.runtime.credentials["zhipuai_base_url"],
api_key=self.runtime.credentials["zhipuai_api_key"],
)
size_mapping = {
'square': '1024x1024',
'vertical': '1024x1792',
'horizontal': '1792x1024',
"square": "1024x1024",
"vertical": "1024x1792",
"horizontal": "1792x1024",
}
# prompt
prompt = tool_parameters.get('prompt', '')
prompt = tool_parameters.get("prompt", "")
if not prompt:
return self.create_text_message('Please input prompt')
return self.create_text_message("Please input prompt")
# get size
size = size_mapping[tool_parameters.get('size', 'square')]
size = size_mapping[tool_parameters.get("size", "square")]
# get n
n = tool_parameters.get('n', 1)
n = tool_parameters.get("n", 1)
# get quality
quality = tool_parameters.get('quality', 'standard')
if quality not in ['standard', 'hd']:
return self.create_text_message('Invalid quality')
quality = tool_parameters.get("quality", "standard")
if quality not in ["standard", "hd"]:
return self.create_text_message("Invalid quality")
# get style
style = tool_parameters.get('style', 'vivid')
if style not in ['natural', 'vivid']:
return self.create_text_message('Invalid style')
style = tool_parameters.get("style", "vivid")
if style not in ["natural", "vivid"]:
return self.create_text_message("Invalid style")
# set extra body
seed_id = tool_parameters.get('seed_id', self._generate_random_id(8))
extra_body = {'seed': seed_id}
seed_id = tool_parameters.get("seed_id", self._generate_random_id(8))
extra_body = {"seed": seed_id}
response = client.images.generations(
prompt=prompt,
model="cogview-3",
@@ -52,18 +51,22 @@ class CogView3Tool(BuiltinTool):
extra_body=extra_body,
style=style,
quality=quality,
response_format='b64_json'
response_format="b64_json",
)
result = []
for image in response.data:
result.append(self.create_image_message(image=image.url))
result.append(self.create_json_message({
"url": image.url,
}))
result.append(
self.create_json_message(
{
"url": image.url,
}
)
)
return result
@staticmethod
def _generate_random_id(length=8):
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
random_id = ''.join(random.choices(characters, k=length))
characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
random_id = "".join(random.choices(characters, k=length))
return random_id

View File

@@ -11,9 +11,9 @@ class CrossRefProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"doi": '10.1007/s00894-022-05373-8',
"doi": "10.1007/s00894-022-05373-8",
},
)
except Exception as e:

View File

@@ -11,15 +11,18 @@ class CrossRefQueryDOITool(BuiltinTool):
"""
Tool for querying the metadata of a publication using its DOI.
"""
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
doi = tool_parameters.get('doi')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
doi = tool_parameters.get("doi")
if not doi:
raise ToolParameterValidationError('doi is required.')
raise ToolParameterValidationError("doi is required.")
# doc: https://github.com/CrossRef/rest-api-doc
url = f"https://api.crossref.org/works/{doi}"
response = requests.get(url)
response.raise_for_status()
response = response.json()
message = response.get('message', {})
message = response.get("message", {})
return self.create_json_message(message)

View File

@@ -12,16 +12,16 @@ def convert_time_str_to_seconds(time_str: str) -> int:
Convert a time string to seconds.
example: 1s -> 1, 1m30s -> 90, 1h30m -> 5400, 1h30m30s -> 5430
"""
time_str = time_str.lower().strip().replace(' ', '')
time_str = time_str.lower().strip().replace(" ", "")
seconds = 0
if 'h' in time_str:
hours, time_str = time_str.split('h')
if "h" in time_str:
hours, time_str = time_str.split("h")
seconds += int(hours) * 3600
if 'm' in time_str:
minutes, time_str = time_str.split('m')
if "m" in time_str:
minutes, time_str = time_str.split("m")
seconds += int(minutes) * 60
if 's' in time_str:
seconds += int(time_str.replace('s', ''))
if "s" in time_str:
seconds += int(time_str.replace("s", ""))
return seconds
@@ -30,6 +30,7 @@ class CrossRefQueryTitleAPI:
Tool for querying the metadata of a publication using its title.
Crossref API doc: https://github.com/CrossRef/rest-api-doc
"""
query_url_template: str = "https://api.crossref.org/works?query.bibliographic={query}&rows={rows}&offset={offset}&sort={sort}&order={order}&mailto={mailto}"
rate_limit: int = 50
rate_interval: float = 1
@@ -38,7 +39,15 @@ class CrossRefQueryTitleAPI:
def __init__(self, mailto: str):
self.mailto = mailto
def _query(self, query: str, rows: int = 5, offset: int = 0, sort: str = 'relevance', order: str = 'desc', fuzzy_query: bool = False) -> list[dict]:
def _query(
self,
query: str,
rows: int = 5,
offset: int = 0,
sort: str = "relevance",
order: str = "desc",
fuzzy_query: bool = False,
) -> list[dict]:
"""
Query the metadata of a publication using its title.
:param query: the title of the publication
@@ -47,33 +56,37 @@ class CrossRefQueryTitleAPI:
:param order: the sort order
:param fuzzy_query: whether to return all items that match the query
"""
url = self.query_url_template.format(query=query, rows=rows, offset=offset, sort=sort, order=order, mailto=self.mailto)
url = self.query_url_template.format(
query=query, rows=rows, offset=offset, sort=sort, order=order, mailto=self.mailto
)
response = requests.get(url)
response.raise_for_status()
rate_limit = int(response.headers['x-ratelimit-limit'])
rate_limit = int(response.headers["x-ratelimit-limit"])
# convert time string to seconds
rate_interval = convert_time_str_to_seconds(response.headers['x-ratelimit-interval'])
rate_interval = convert_time_str_to_seconds(response.headers["x-ratelimit-interval"])
self.rate_limit = rate_limit
self.rate_interval = rate_interval
response = response.json()
if response['status'] != 'ok':
if response["status"] != "ok":
return []
message = response['message']
message = response["message"]
if fuzzy_query:
# fuzzy query return all items
return message['items']
return message["items"]
else:
for paper in message['items']:
title = paper['title'][0]
for paper in message["items"]:
title = paper["title"][0]
if title.lower() != query.lower():
continue
return [paper]
return []
def query(self, query: str, rows: int = 5, sort: str = 'relevance', order: str = 'desc', fuzzy_query: bool = False) -> list[dict]:
def query(
self, query: str, rows: int = 5, sort: str = "relevance", order: str = "desc", fuzzy_query: bool = False
) -> list[dict]:
"""
Query the metadata of a publication using its title.
:param query: the title of the publication
@@ -89,7 +102,14 @@ class CrossRefQueryTitleAPI:
results = []
for i in range(query_times):
result = self._query(query, rows=self.rate_limit, offset=i * self.rate_limit, sort=sort, order=order, fuzzy_query=fuzzy_query)
result = self._query(
query,
rows=self.rate_limit,
offset=i * self.rate_limit,
sort=sort,
order=order,
fuzzy_query=fuzzy_query,
)
if fuzzy_query:
results.extend(result)
else:
@@ -107,13 +127,16 @@ class CrossRefQueryTitleTool(BuiltinTool):
"""
Tool for querying the metadata of a publication using its title.
"""
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
query = tool_parameters.get('query')
fuzzy_query = tool_parameters.get('fuzzy_query', False)
rows = tool_parameters.get('rows', 3)
sort = tool_parameters.get('sort', 'relevance')
order = tool_parameters.get('order', 'desc')
mailto = self.runtime.credentials['mailto']
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
query = tool_parameters.get("query")
fuzzy_query = tool_parameters.get("fuzzy_query", False)
rows = tool_parameters.get("rows", 3)
sort = tool_parameters.get("sort", "relevance")
order = tool_parameters.get("order", "desc")
mailto = self.runtime.credentials["mailto"]
result = CrossRefQueryTitleAPI(mailto).query(query, rows, sort, order, fuzzy_query)

View File

@@ -13,13 +13,8 @@ class DALLEProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
tool_parameters={
"prompt": "cute girl, blue eyes, white hair, anime style",
"size": "small",
"n": 1
},
user_id="",
tool_parameters={"prompt": "cute girl, blue eyes, white hair, anime style", "size": "small", "n": 1},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -9,59 +9,58 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DallE2Tool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
openai_organization = self.runtime.credentials.get('openai_organization_id', None)
openai_organization = self.runtime.credentials.get("openai_organization_id", None)
if not openai_organization:
openai_organization = None
openai_base_url = self.runtime.credentials.get('openai_base_url', None)
openai_base_url = self.runtime.credentials.get("openai_base_url", None)
if not openai_base_url:
openai_base_url = None
else:
openai_base_url = str(URL(openai_base_url) / 'v1')
openai_base_url = str(URL(openai_base_url) / "v1")
client = OpenAI(
api_key=self.runtime.credentials['openai_api_key'],
api_key=self.runtime.credentials["openai_api_key"],
base_url=openai_base_url,
organization=openai_organization
organization=openai_organization,
)
SIZE_MAPPING = {
'small': '256x256',
'medium': '512x512',
'large': '1024x1024',
"small": "256x256",
"medium": "512x512",
"large": "1024x1024",
}
# prompt
prompt = tool_parameters.get('prompt', '')
prompt = tool_parameters.get("prompt", "")
if not prompt:
return self.create_text_message('Please input prompt')
return self.create_text_message("Please input prompt")
# get size
size = SIZE_MAPPING[tool_parameters.get('size', 'large')]
size = SIZE_MAPPING[tool_parameters.get("size", "large")]
# get n
n = tool_parameters.get('n', 1)
n = tool_parameters.get("n", 1)
# call openapi dalle2
response = client.images.generate(
prompt=prompt,
model='dall-e-2',
size=size,
n=n,
response_format='b64_json'
)
response = client.images.generate(prompt=prompt, model="dall-e-2", size=size, n=n, response_format="b64_json")
result = []
for image in response.data:
result.append(self.create_blob_message(blob=b64decode(image.b64_json),
meta={ 'mime_type': 'image/png' },
save_as=self.VARIABLE_KEY.IMAGE.value))
result.append(
self.create_blob_message(
blob=b64decode(image.b64_json),
meta={"mime_type": "image/png"},
save_as=self.VARIABLE_KEY.IMAGE.value,
)
)
return result

View File

@@ -10,69 +10,64 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DallE3Tool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
openai_organization = self.runtime.credentials.get('openai_organization_id', None)
openai_organization = self.runtime.credentials.get("openai_organization_id", None)
if not openai_organization:
openai_organization = None
openai_base_url = self.runtime.credentials.get('openai_base_url', None)
openai_base_url = self.runtime.credentials.get("openai_base_url", None)
if not openai_base_url:
openai_base_url = None
else:
openai_base_url = str(URL(openai_base_url) / 'v1')
openai_base_url = str(URL(openai_base_url) / "v1")
client = OpenAI(
api_key=self.runtime.credentials['openai_api_key'],
api_key=self.runtime.credentials["openai_api_key"],
base_url=openai_base_url,
organization=openai_organization
organization=openai_organization,
)
SIZE_MAPPING = {
'square': '1024x1024',
'vertical': '1024x1792',
'horizontal': '1792x1024',
"square": "1024x1024",
"vertical": "1024x1792",
"horizontal": "1792x1024",
}
# prompt
prompt = tool_parameters.get('prompt', '')
prompt = tool_parameters.get("prompt", "")
if not prompt:
return self.create_text_message('Please input prompt')
return self.create_text_message("Please input prompt")
# get size
size = SIZE_MAPPING[tool_parameters.get('size', 'square')]
size = SIZE_MAPPING[tool_parameters.get("size", "square")]
# get n
n = tool_parameters.get('n', 1)
n = tool_parameters.get("n", 1)
# get quality
quality = tool_parameters.get('quality', 'standard')
if quality not in ['standard', 'hd']:
return self.create_text_message('Invalid quality')
quality = tool_parameters.get("quality", "standard")
if quality not in ["standard", "hd"]:
return self.create_text_message("Invalid quality")
# get style
style = tool_parameters.get('style', 'vivid')
if style not in ['natural', 'vivid']:
return self.create_text_message('Invalid style')
style = tool_parameters.get("style", "vivid")
if style not in ["natural", "vivid"]:
return self.create_text_message("Invalid style")
# call openapi dalle3
response = client.images.generate(
prompt=prompt,
model='dall-e-3',
size=size,
n=n,
style=style,
quality=quality,
response_format='b64_json'
prompt=prompt, model="dall-e-3", size=size, n=n, style=style, quality=quality, response_format="b64_json"
)
result = []
for image in response.data:
mime_type, blob_image = DallE3Tool._decode_image(image.b64_json)
blob_message = self.create_blob_message(blob=blob_image,
meta={'mime_type': mime_type},
save_as=self.VARIABLE_KEY.IMAGE.value)
blob_message = self.create_blob_message(
blob=blob_image, meta={"mime_type": mime_type}, save_as=self.VARIABLE_KEY.IMAGE.value
)
result.append(blob_message)
return result
@@ -86,7 +81,7 @@ class DallE3Tool(BuiltinTool):
:return: A tuple containing the MIME type and the decoded image bytes
"""
if DallE3Tool._is_plain_base64(base64_image):
return 'image/png', base64.b64decode(base64_image)
return "image/png", base64.b64decode(base64_image)
else:
return DallE3Tool._extract_mime_and_data(base64_image)
@@ -98,7 +93,7 @@ class DallE3Tool(BuiltinTool):
:param encoded_str: Base64 encoded image string
:return: True if the string is plain base64, False otherwise
"""
return not encoded_str.startswith('data:image')
return not encoded_str.startswith("data:image")
@staticmethod
def _extract_mime_and_data(encoded_str: str) -> tuple[str, bytes]:
@@ -108,13 +103,13 @@ class DallE3Tool(BuiltinTool):
:param encoded_str: Base64 encoded image string with MIME type prefix
:return: A tuple containing the MIME type and the decoded image bytes
"""
mime_type = encoded_str.split(';')[0].split(':')[1]
image_data_base64 = encoded_str.split(',')[1]
mime_type = encoded_str.split(";")[0].split(":")[1]
image_data_base64 = encoded_str.split(",")[1]
decoded_data = base64.b64decode(image_data_base64)
return mime_type, decoded_data
@staticmethod
def _generate_random_id(length=8):
characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
random_id = ''.join(random.choices(characters, k=length))
characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
random_id = "".join(random.choices(characters, k=length))
return random_id

View File

@@ -11,7 +11,7 @@ class DevDocsProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"doc": "python~3.12",
"topic": "library/code",
@@ -19,4 +19,3 @@ class DevDocsProvider(BuiltinToolProviderController):
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -13,7 +13,9 @@ class SearchDevDocsInput(BaseModel):
class SearchDevDocsTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
Invokes the DevDocs search tool with the given user ID and tool parameters.
@@ -24,13 +26,13 @@ class SearchDevDocsTool(BuiltinTool):
Returns:
ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation, which can be a single message or a list of messages.
"""
doc = tool_parameters.get('doc', '')
topic = tool_parameters.get('topic', '')
doc = tool_parameters.get("doc", "")
topic = tool_parameters.get("topic", "")
if not doc:
return self.create_text_message('Please provide the documentation name.')
return self.create_text_message("Please provide the documentation name.")
if not topic:
return self.create_text_message('Please provide the topic path.')
return self.create_text_message("Please provide the topic path.")
url = f"https://documents.devdocs.io/{doc}/{topic}.html"
response = requests.get(url)
@@ -39,4 +41,6 @@ class SearchDevDocsTool(BuiltinTool):
content = response.text
return self.create_text_message(self.summary(user_id=user_id, content=content))
else:
return self.create_text_message(f"Failed to retrieve the documentation. Status code: {response.status_code}")
return self.create_text_message(
f"Failed to retrieve the documentation. Status code: {response.status_code}"
)

View File

@@ -7,15 +7,12 @@ class DIDProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
# Example validation using the D-ID talks tool
TalksTool().fork_tool_runtime(
runtime={"credentials": credentials}
).invoke(
user_id='',
TalksTool().fork_tool_runtime(runtime={"credentials": credentials}).invoke(
user_id="",
tool_parameters={
"source_url": "https://www.d-id.com/wp-content/uploads/2023/11/Hero-image-1.png",
"text_input": "Hello, welcome to use D-ID tool in Dify",
}
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -12,14 +12,14 @@ logger = logging.getLogger(__name__)
class DIDApp:
def __init__(self, api_key: str | None = None, base_url: str | None = None):
self.api_key = api_key
self.base_url = base_url or 'https://api.d-id.com'
self.base_url = base_url or "https://api.d-id.com"
if not self.api_key:
raise ValueError('API key is required')
raise ValueError("API key is required")
def _prepare_headers(self, idempotency_key: str | None = None):
headers = {'Content-Type': 'application/json', 'Authorization': f'Basic {self.api_key}'}
headers = {"Content-Type": "application/json", "Authorization": f"Basic {self.api_key}"}
if idempotency_key:
headers['Idempotency-Key'] = idempotency_key
headers["Idempotency-Key"] = idempotency_key
return headers
def _request(
@@ -44,44 +44,44 @@ class DIDApp:
return None
def talks(self, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs):
endpoint = f'{self.base_url}/talks'
endpoint = f"{self.base_url}/talks"
headers = self._prepare_headers(idempotency_key)
data = kwargs['params']
logger.debug(f'Send request to {endpoint=} body={data}')
response = self._request('POST', endpoint, data, headers)
data = kwargs["params"]
logger.debug(f"Send request to {endpoint=} body={data}")
response = self._request("POST", endpoint, data, headers)
if response is None:
raise HTTPError('Failed to initiate D-ID talks after multiple retries')
id: str = response['id']
raise HTTPError("Failed to initiate D-ID talks after multiple retries")
id: str = response["id"]
if wait:
return self._monitor_job_status(id=id, target='talks', poll_interval=poll_interval)
return self._monitor_job_status(id=id, target="talks", poll_interval=poll_interval)
return id
def animations(self, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs):
endpoint = f'{self.base_url}/animations'
endpoint = f"{self.base_url}/animations"
headers = self._prepare_headers(idempotency_key)
data = kwargs['params']
logger.debug(f'Send request to {endpoint=} body={data}')
response = self._request('POST', endpoint, data, headers)
data = kwargs["params"]
logger.debug(f"Send request to {endpoint=} body={data}")
response = self._request("POST", endpoint, data, headers)
if response is None:
raise HTTPError('Failed to initiate D-ID talks after multiple retries')
id: str = response['id']
raise HTTPError("Failed to initiate D-ID talks after multiple retries")
id: str = response["id"]
if wait:
return self._monitor_job_status(target='animations', id=id, poll_interval=poll_interval)
return self._monitor_job_status(target="animations", id=id, poll_interval=poll_interval)
return id
def check_did_status(self, target: str, id: str):
endpoint = f'{self.base_url}/{target}/{id}'
endpoint = f"{self.base_url}/{target}/{id}"
headers = self._prepare_headers()
response = self._request('GET', endpoint, headers=headers)
response = self._request("GET", endpoint, headers=headers)
if response is None:
raise HTTPError(f'Failed to check status for talks {id} after multiple retries')
raise HTTPError(f"Failed to check status for talks {id} after multiple retries")
return response
def _monitor_job_status(self, target: str, id: str, poll_interval: int):
while True:
status = self.check_did_status(target=target, id=id)
if status['status'] == 'done':
if status["status"] == "done":
return status
elif status['status'] == 'error' or status['status'] == 'rejected':
elif status["status"] == "error" or status["status"] == "rejected":
raise HTTPError(f'Talks {id} failed: {status["status"]} {status.get("error",{}).get("description")}')
time.sleep(poll_interval)

View File

@@ -10,33 +10,33 @@ class AnimationsTool(BuiltinTool):
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
app = DIDApp(api_key=self.runtime.credentials['did_api_key'], base_url=self.runtime.credentials['base_url'])
app = DIDApp(api_key=self.runtime.credentials["did_api_key"], base_url=self.runtime.credentials["base_url"])
driver_expressions_str = tool_parameters.get('driver_expressions')
driver_expressions_str = tool_parameters.get("driver_expressions")
driver_expressions = json.loads(driver_expressions_str) if driver_expressions_str else None
config = {
'stitch': tool_parameters.get('stitch', True),
'mute': tool_parameters.get('mute'),
'result_format': tool_parameters.get('result_format') or 'mp4',
"stitch": tool_parameters.get("stitch", True),
"mute": tool_parameters.get("mute"),
"result_format": tool_parameters.get("result_format") or "mp4",
}
config = {k: v for k, v in config.items() if v is not None and v != ''}
config = {k: v for k, v in config.items() if v is not None and v != ""}
options = {
'source_url': tool_parameters['source_url'],
'driver_url': tool_parameters.get('driver_url'),
'config': config,
"source_url": tool_parameters["source_url"],
"driver_url": tool_parameters.get("driver_url"),
"config": config,
}
options = {k: v for k, v in options.items() if v is not None and v != ''}
options = {k: v for k, v in options.items() if v is not None and v != ""}
if not options.get('source_url'):
raise ValueError('Source URL is required')
if not options.get("source_url"):
raise ValueError("Source URL is required")
if config.get('logo_url'):
if not config.get('logo_x'):
raise ValueError('Logo X position is required when logo URL is provided')
if not config.get('logo_y'):
raise ValueError('Logo Y position is required when logo URL is provided')
if config.get("logo_url"):
if not config.get("logo_x"):
raise ValueError("Logo X position is required when logo URL is provided")
if not config.get("logo_y"):
raise ValueError("Logo Y position is required when logo URL is provided")
animations_result = app.animations(params=options, wait=True)
@@ -44,6 +44,6 @@ class AnimationsTool(BuiltinTool):
animations_result = json.dumps(animations_result, ensure_ascii=False, indent=4)
if not animations_result:
return self.create_text_message('D-ID animations request failed.')
return self.create_text_message("D-ID animations request failed.")
return self.create_text_message(animations_result)

View File

@@ -10,49 +10,49 @@ class TalksTool(BuiltinTool):
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
app = DIDApp(api_key=self.runtime.credentials['did_api_key'], base_url=self.runtime.credentials['base_url'])
app = DIDApp(api_key=self.runtime.credentials["did_api_key"], base_url=self.runtime.credentials["base_url"])
driver_expressions_str = tool_parameters.get('driver_expressions')
driver_expressions_str = tool_parameters.get("driver_expressions")
driver_expressions = json.loads(driver_expressions_str) if driver_expressions_str else None
script = {
'type': tool_parameters.get('script_type') or 'text',
'input': tool_parameters.get('text_input'),
'audio_url': tool_parameters.get('audio_url'),
'reduce_noise': tool_parameters.get('audio_reduce_noise', False),
"type": tool_parameters.get("script_type") or "text",
"input": tool_parameters.get("text_input"),
"audio_url": tool_parameters.get("audio_url"),
"reduce_noise": tool_parameters.get("audio_reduce_noise", False),
}
script = {k: v for k, v in script.items() if v is not None and v != ''}
script = {k: v for k, v in script.items() if v is not None and v != ""}
config = {
'stitch': tool_parameters.get('stitch', True),
'sharpen': tool_parameters.get('sharpen'),
'fluent': tool_parameters.get('fluent'),
'result_format': tool_parameters.get('result_format') or 'mp4',
'pad_audio': tool_parameters.get('pad_audio'),
'driver_expressions': driver_expressions,
"stitch": tool_parameters.get("stitch", True),
"sharpen": tool_parameters.get("sharpen"),
"fluent": tool_parameters.get("fluent"),
"result_format": tool_parameters.get("result_format") or "mp4",
"pad_audio": tool_parameters.get("pad_audio"),
"driver_expressions": driver_expressions,
}
config = {k: v for k, v in config.items() if v is not None and v != ''}
config = {k: v for k, v in config.items() if v is not None and v != ""}
options = {
'source_url': tool_parameters['source_url'],
'driver_url': tool_parameters.get('driver_url'),
'script': script,
'config': config,
"source_url": tool_parameters["source_url"],
"driver_url": tool_parameters.get("driver_url"),
"script": script,
"config": config,
}
options = {k: v for k, v in options.items() if v is not None and v != ''}
options = {k: v for k, v in options.items() if v is not None and v != ""}
if not options.get('source_url'):
raise ValueError('Source URL is required')
if not options.get("source_url"):
raise ValueError("Source URL is required")
if script.get('type') == 'audio':
script.pop('input', None)
if not script.get('audio_url'):
raise ValueError('Audio URL is required for audio script type')
if script.get("type") == "audio":
script.pop("input", None)
if not script.get("audio_url"):
raise ValueError("Audio URL is required for audio script type")
if script.get('type') == 'text':
script.pop('audio_url', None)
script.pop('reduce_noise', None)
if not script.get('input'):
raise ValueError('Text input is required for text script type')
if script.get("type") == "text":
script.pop("audio_url", None)
script.pop("reduce_noise", None)
if not script.get("input"):
raise ValueError("Text input is required for text script type")
talks_result = app.talks(params=options, wait=True)
@@ -60,6 +60,6 @@ class TalksTool(BuiltinTool):
talks_result = json.dumps(talks_result, ensure_ascii=False, indent=4)
if not talks_result:
return self.create_text_message('D-ID talks request failed.')
return self.create_text_message("D-ID talks request failed.")
return self.create_text_message(talks_result)

View File

@@ -13,38 +13,43 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DingTalkGroupBotTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
Dingtalk custom group robot API docs:
https://open.dingtalk.com/document/orgapp/custom-robot-access
invoke tools
Dingtalk custom group robot API docs:
https://open.dingtalk.com/document/orgapp/custom-robot-access
"""
content = tool_parameters.get('content')
content = tool_parameters.get("content")
if not content:
return self.create_text_message('Invalid parameter content')
return self.create_text_message("Invalid parameter content")
access_token = tool_parameters.get('access_token')
access_token = tool_parameters.get("access_token")
if not access_token:
return self.create_text_message('Invalid parameter access_token. '
'Regarding information about security details,'
'please refer to the DingTalk docs:'
'https://open.dingtalk.com/document/robots/customize-robot-security-settings')
return self.create_text_message(
"Invalid parameter access_token. "
"Regarding information about security details,"
"please refer to the DingTalk docs:"
"https://open.dingtalk.com/document/robots/customize-robot-security-settings"
)
sign_secret = tool_parameters.get('sign_secret')
sign_secret = tool_parameters.get("sign_secret")
if not sign_secret:
return self.create_text_message('Invalid parameter sign_secret. '
'Regarding information about security details,'
'please refer to the DingTalk docs:'
'https://open.dingtalk.com/document/robots/customize-robot-security-settings')
return self.create_text_message(
"Invalid parameter sign_secret. "
"Regarding information about security details,"
"please refer to the DingTalk docs:"
"https://open.dingtalk.com/document/robots/customize-robot-security-settings"
)
msgtype = 'text'
api_url = 'https://oapi.dingtalk.com/robot/send'
msgtype = "text"
api_url = "https://oapi.dingtalk.com/robot/send"
headers = {
'Content-Type': 'application/json',
"Content-Type": "application/json",
}
params = {
'access_token': access_token,
"access_token": access_token,
}
self._apply_security_mechanism(params, sign_secret)
@@ -53,7 +58,7 @@ class DingTalkGroupBotTool(BuiltinTool):
"msgtype": msgtype,
"text": {
"content": content,
}
},
}
try:
@@ -62,7 +67,8 @@ class DingTalkGroupBotTool(BuiltinTool):
return self.create_text_message("Text message sent successfully")
else:
return self.create_text_message(
f"Failed to send the text message, status code: {res.status_code}, response: {res.text}")
f"Failed to send the text message, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to send message to group chat bot. {}".format(e))
@@ -70,14 +76,14 @@ class DingTalkGroupBotTool(BuiltinTool):
def _apply_security_mechanism(params: dict[str, Any], sign_secret: str):
try:
timestamp = str(round(time.time() * 1000))
secret_enc = sign_secret.encode('utf-8')
string_to_sign = f'{timestamp}\n{sign_secret}'
string_to_sign_enc = string_to_sign.encode('utf-8')
secret_enc = sign_secret.encode("utf-8")
string_to_sign = f"{timestamp}\n{sign_secret}"
string_to_sign_enc = string_to_sign.encode("utf-8")
hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
params['timestamp'] = timestamp
params['sign'] = sign
params["timestamp"] = timestamp
params["sign"] = sign
except Exception:
msg = "Failed to apply security mechanism to the request."
logging.exception(msg)

View File

@@ -11,11 +11,10 @@ class DuckDuckGoProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
user_id="",
tool_parameters={
"query": "John Doe",
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -13,8 +13,8 @@ class DuckDuckGoAITool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
query_dict = {
"keywords": tool_parameters.get('query'),
"model": tool_parameters.get('model'),
"keywords": tool_parameters.get("query"),
"model": tool_parameters.get("model"),
}
response = DDGS().chat(**query_dict)
return self.create_text_message(text=response)

View File

@@ -14,18 +14,17 @@ class DuckDuckGoImageSearchTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]:
query_dict = {
"keywords": tool_parameters.get('query'),
"timelimit": tool_parameters.get('timelimit'),
"size": tool_parameters.get('size'),
"max_results": tool_parameters.get('max_results'),
"keywords": tool_parameters.get("query"),
"timelimit": tool_parameters.get("timelimit"),
"size": tool_parameters.get("size"),
"max_results": tool_parameters.get("max_results"),
}
response = DDGS().images(**query_dict)
result = []
for res in response:
res['transfer_method'] = FileTransferMethod.REMOTE_URL
msg = ToolInvokeMessage(type=ToolInvokeMessage.MessageType.IMAGE_LINK,
message=res.get('image'),
save_as='',
meta=res)
res["transfer_method"] = FileTransferMethod.REMOTE_URL
msg = ToolInvokeMessage(
type=ToolInvokeMessage.MessageType.IMAGE_LINK, message=res.get("image"), save_as="", meta=res
)
result.append(msg)
return result

View File

@@ -21,10 +21,11 @@ class DuckDuckGoSearchTool(BuiltinTool):
"""
Tool for performing a search using DuckDuckGo search engine.
"""
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
query = tool_parameters.get('query')
max_results = tool_parameters.get('max_results', 5)
require_summary = tool_parameters.get('require_summary', False)
query = tool_parameters.get("query")
max_results = tool_parameters.get("max_results", 5)
require_summary = tool_parameters.get("require_summary", False)
response = DDGS().text(query, max_results=max_results)
if require_summary:
results = "\n".join([res.get("body") for res in response])
@@ -34,7 +35,11 @@ class DuckDuckGoSearchTool(BuiltinTool):
def summary_results(self, user_id: str, content: str, query: str) -> str:
prompt = SUMMARY_PROMPT.format(query=query, content=content)
summary = self.invoke_model(user_id=user_id, prompt_messages=[
SystemPromptMessage(content=prompt),
], stop=[])
summary = self.invoke_model(
user_id=user_id,
prompt_messages=[
SystemPromptMessage(content=prompt),
],
stop=[],
)
return summary.message.content

View File

@@ -13,8 +13,8 @@ class DuckDuckGoTranslateTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
query_dict = {
"keywords": tool_parameters.get('query'),
"to": tool_parameters.get('translate_to'),
"keywords": tool_parameters.get("query"),
"to": tool_parameters.get("translate_to"),
}
response = DDGS().translate(**query_dict)[0].get('translated', 'Unable to translate!')
response = DDGS().translate(**query_dict)[0].get("translated", "Unable to translate!")
return self.create_text_message(text=response)

View File

@@ -8,35 +8,35 @@ from core.tools.utils.uuid_utils import is_valid_uuid
class FeishuGroupBotTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
API document: https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
invoke tools
API document: https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot
"""
url = "https://open.feishu.cn/open-apis/bot/v2/hook"
content = tool_parameters.get('content', '')
content = tool_parameters.get("content", "")
if not content:
return self.create_text_message('Invalid parameter content')
return self.create_text_message("Invalid parameter content")
hook_key = tool_parameters.get('hook_key', '')
hook_key = tool_parameters.get("hook_key", "")
if not is_valid_uuid(hook_key):
return self.create_text_message(
f'Invalid parameter hook_key ${hook_key}, not a valid UUID')
return self.create_text_message(f"Invalid parameter hook_key ${hook_key}, not a valid UUID")
msg_type = 'text'
api_url = f'{url}/{hook_key}'
msg_type = "text"
api_url = f"{url}/{hook_key}"
headers = {
'Content-Type': 'application/json',
"Content-Type": "application/json",
}
params = {}
payload = {
"msg_type": msg_type,
"content": {
"text": content,
}
},
}
try:
@@ -45,6 +45,7 @@ class FeishuGroupBotTool(BuiltinTool):
return self.create_text_message("Text message sent successfully")
else:
return self.create_text_message(
f"Failed to send the text message, status code: {res.status_code}, response: {res.text}")
f"Failed to send the text message, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to send message to group chat bot. {}".format(e))
return self.create_text_message("Failed to send message to group chat bot. {}".format(e))

View File

@@ -5,4 +5,4 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class FeishuBaseProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
GetTenantAccessTokenTool()
pass
pass

View File

@@ -8,45 +8,49 @@ from core.tools.tool.builtin_tool import BuiltinTool
class AddBaseRecordTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
table_id = tool_parameters.get('table_id', '')
table_id = tool_parameters.get("table_id", "")
if not table_id:
return self.create_text_message('Invalid parameter table_id')
return self.create_text_message("Invalid parameter table_id")
fields = tool_parameters.get('fields', '')
fields = tool_parameters.get("fields", "")
if not fields:
return self.create_text_message('Invalid parameter fields')
return self.create_text_message("Invalid parameter fields")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {}
payload = {
"fields": json.loads(fields)
}
payload = {"fields": json.loads(fields)}
try:
res = httpx.post(url.format(app_token=app_token, table_id=table_id), headers=headers, params=params,
json=payload, timeout=30)
res = httpx.post(
url.format(app_token=app_token, table_id=table_id),
headers=headers,
params=params,
json=payload,
timeout=30,
)
res_json = res.json()
if res.is_success:
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to add base record, status code: {res.status_code}, response: {res.text}")
f"Failed to add base record, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to add base record. {}".format(e))

View File

@@ -8,28 +8,25 @@ from core.tools.tool.builtin_tool import BuiltinTool
class CreateBaseTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
name = tool_parameters.get('name', '')
folder_token = tool_parameters.get('folder_token', '')
name = tool_parameters.get("name", "")
folder_token = tool_parameters.get("folder_token", "")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {}
payload = {
"name": name,
"folder_token": folder_token
}
payload = {"name": name, "folder_token": folder_token}
try:
res = httpx.post(url, headers=headers, params=params, json=payload, timeout=30)
@@ -38,6 +35,7 @@ class CreateBaseTool(BuiltinTool):
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to create base, status code: {res.status_code}, response: {res.text}")
f"Failed to create base, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to create base. {}".format(e))

View File

@@ -8,37 +8,32 @@ from core.tools.tool.builtin_tool import BuiltinTool
class CreateBaseTableTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
name = tool_parameters.get('name', '')
name = tool_parameters.get("name", "")
fields = tool_parameters.get('fields', '')
fields = tool_parameters.get("fields", "")
if not fields:
return self.create_text_message('Invalid parameter fields')
return self.create_text_message("Invalid parameter fields")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {}
payload = {
"table": {
"name": name,
"fields": json.loads(fields)
}
}
payload = {"table": {"name": name, "fields": json.loads(fields)}}
try:
res = httpx.post(url.format(app_token=app_token), headers=headers, params=params, json=payload, timeout=30)
@@ -47,6 +42,7 @@ class CreateBaseTableTool(BuiltinTool):
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to create base table, status code: {res.status_code}, response: {res.text}")
f"Failed to create base table, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to create base table. {}".format(e))

View File

@@ -8,45 +8,49 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DeleteBaseRecordsTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/batch_delete"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
table_id = tool_parameters.get('table_id', '')
table_id = tool_parameters.get("table_id", "")
if not table_id:
return self.create_text_message('Invalid parameter table_id')
return self.create_text_message("Invalid parameter table_id")
record_ids = tool_parameters.get('record_ids', '')
record_ids = tool_parameters.get("record_ids", "")
if not record_ids:
return self.create_text_message('Invalid parameter record_ids')
return self.create_text_message("Invalid parameter record_ids")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {}
payload = {
"records": json.loads(record_ids)
}
payload = {"records": json.loads(record_ids)}
try:
res = httpx.post(url.format(app_token=app_token, table_id=table_id), headers=headers, params=params,
json=payload, timeout=30)
res = httpx.post(
url.format(app_token=app_token, table_id=table_id),
headers=headers,
params=params,
json=payload,
timeout=30,
)
res_json = res.json()
if res.is_success:
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to delete base records, status code: {res.status_code}, response: {res.text}")
f"Failed to delete base records, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to delete base records. {}".format(e))

View File

@@ -8,32 +8,30 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DeleteBaseTablesTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/batch_delete"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
table_ids = tool_parameters.get('table_ids', '')
table_ids = tool_parameters.get("table_ids", "")
if not table_ids:
return self.create_text_message('Invalid parameter table_ids')
return self.create_text_message("Invalid parameter table_ids")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {}
payload = {
"table_ids": json.loads(table_ids)
}
payload = {"table_ids": json.loads(table_ids)}
try:
res = httpx.post(url.format(app_token=app_token), headers=headers, params=params, json=payload, timeout=30)
@@ -42,6 +40,7 @@ class DeleteBaseTablesTool(BuiltinTool):
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to delete base tables, status code: {res.status_code}, response: {res.text}")
f"Failed to delete base tables, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to delete base tables. {}".format(e))

View File

@@ -8,22 +8,22 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GetBaseInfoTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
try:
@@ -33,6 +33,7 @@ class GetBaseInfoTool(BuiltinTool):
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to get base info, status code: {res.status_code}, response: {res.text}")
f"Failed to get base info, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to get base info. {}".format(e))

View File

@@ -8,27 +8,24 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GetTenantAccessTokenTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
app_id = tool_parameters.get('app_id', '')
app_id = tool_parameters.get("app_id", "")
if not app_id:
return self.create_text_message('Invalid parameter app_id')
return self.create_text_message("Invalid parameter app_id")
app_secret = tool_parameters.get('app_secret', '')
app_secret = tool_parameters.get("app_secret", "")
if not app_secret:
return self.create_text_message('Invalid parameter app_secret')
return self.create_text_message("Invalid parameter app_secret")
headers = {
'Content-Type': 'application/json',
"Content-Type": "application/json",
}
params = {}
payload = {
"app_id": app_id,
"app_secret": app_secret
}
payload = {"app_id": app_id, "app_secret": app_secret}
"""
{
@@ -45,6 +42,7 @@ class GetTenantAccessTokenTool(BuiltinTool):
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to get tenant access token, status code: {res.status_code}, response: {res.text}")
f"Failed to get tenant access token, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to get tenant access token. {}".format(e))

View File

@@ -8,31 +8,31 @@ from core.tools.tool.builtin_tool import BuiltinTool
class ListBaseRecordsTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/search"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
table_id = tool_parameters.get('table_id', '')
table_id = tool_parameters.get("table_id", "")
if not table_id:
return self.create_text_message('Invalid parameter table_id')
return self.create_text_message("Invalid parameter table_id")
page_token = tool_parameters.get('page_token', '')
page_size = tool_parameters.get('page_size', '')
sort_condition = tool_parameters.get('sort_condition', '')
filter_condition = tool_parameters.get('filter_condition', '')
page_token = tool_parameters.get("page_token", "")
page_size = tool_parameters.get("page_size", "")
sort_condition = tool_parameters.get("sort_condition", "")
filter_condition = tool_parameters.get("filter_condition", "")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {
@@ -40,22 +40,26 @@ class ListBaseRecordsTool(BuiltinTool):
"page_size": page_size,
}
payload = {
"automatic_fields": True
}
payload = {"automatic_fields": True}
if sort_condition:
payload["sort"] = json.loads(sort_condition)
if filter_condition:
payload["filter"] = json.loads(filter_condition)
try:
res = httpx.post(url.format(app_token=app_token, table_id=table_id), headers=headers, params=params,
json=payload, timeout=30)
res = httpx.post(
url.format(app_token=app_token, table_id=table_id),
headers=headers,
params=params,
json=payload,
timeout=30,
)
res_json = res.json()
if res.is_success:
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to list base records, status code: {res.status_code}, response: {res.text}")
f"Failed to list base records, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to list base records. {}".format(e))

View File

@@ -8,25 +8,25 @@ from core.tools.tool.builtin_tool import BuiltinTool
class ListBaseTablesTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
page_token = tool_parameters.get('page_token', '')
page_size = tool_parameters.get('page_size', '')
page_token = tool_parameters.get("page_token", "")
page_size = tool_parameters.get("page_size", "")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {
@@ -41,6 +41,7 @@ class ListBaseTablesTool(BuiltinTool):
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to list base tables, status code: {res.status_code}, response: {res.text}")
f"Failed to list base tables, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to list base tables. {}".format(e))

View File

@@ -8,40 +8,42 @@ from core.tools.tool.builtin_tool import BuiltinTool
class ReadBaseRecordTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/{record_id}"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
table_id = tool_parameters.get('table_id', '')
table_id = tool_parameters.get("table_id", "")
if not table_id:
return self.create_text_message('Invalid parameter table_id')
return self.create_text_message("Invalid parameter table_id")
record_id = tool_parameters.get('record_id', '')
record_id = tool_parameters.get("record_id", "")
if not record_id:
return self.create_text_message('Invalid parameter record_id')
return self.create_text_message("Invalid parameter record_id")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
try:
res = httpx.get(url.format(app_token=app_token, table_id=table_id, record_id=record_id), headers=headers,
timeout=30)
res = httpx.get(
url.format(app_token=app_token, table_id=table_id, record_id=record_id), headers=headers, timeout=30
)
res_json = res.json()
if res.is_success:
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to read base record, status code: {res.status_code}, response: {res.text}")
f"Failed to read base record, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to read base record. {}".format(e))

View File

@@ -8,49 +8,53 @@ from core.tools.tool.builtin_tool import BuiltinTool
class UpdateBaseRecordTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
url = "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/{record_id}"
access_token = tool_parameters.get('Authorization', '')
access_token = tool_parameters.get("Authorization", "")
if not access_token:
return self.create_text_message('Invalid parameter access_token')
return self.create_text_message("Invalid parameter access_token")
app_token = tool_parameters.get('app_token', '')
app_token = tool_parameters.get("app_token", "")
if not app_token:
return self.create_text_message('Invalid parameter app_token')
return self.create_text_message("Invalid parameter app_token")
table_id = tool_parameters.get('table_id', '')
table_id = tool_parameters.get("table_id", "")
if not table_id:
return self.create_text_message('Invalid parameter table_id')
return self.create_text_message("Invalid parameter table_id")
record_id = tool_parameters.get('record_id', '')
record_id = tool_parameters.get("record_id", "")
if not record_id:
return self.create_text_message('Invalid parameter record_id')
return self.create_text_message("Invalid parameter record_id")
fields = tool_parameters.get('fields', '')
fields = tool_parameters.get("fields", "")
if not fields:
return self.create_text_message('Invalid parameter fields')
return self.create_text_message("Invalid parameter fields")
headers = {
'Content-Type': 'application/json',
'Authorization': f"Bearer {access_token}",
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}",
}
params = {}
payload = {
"fields": json.loads(fields)
}
payload = {"fields": json.loads(fields)}
try:
res = httpx.put(url.format(app_token=app_token, table_id=table_id, record_id=record_id), headers=headers,
params=params, json=payload, timeout=30)
res = httpx.put(
url.format(app_token=app_token, table_id=table_id, record_id=record_id),
headers=headers,
params=params,
json=payload,
timeout=30,
)
res_json = res.json()
if res.is_success:
return self.create_text_message(text=json.dumps(res_json))
else:
return self.create_text_message(
f"Failed to update base record, status code: {res.status_code}, response: {res.text}")
f"Failed to update base record, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to update base record. {}".format(e))

View File

@@ -5,11 +5,11 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class FeishuDocumentProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
app_id = credentials.get('app_id')
app_secret = credentials.get('app_secret')
app_id = credentials.get("app_id")
app_secret = credentials.get("app_secret")
if not app_id or not app_secret:
raise ToolProviderCredentialValidationError("app_id and app_secret is required")
try:
assert FeishuRequest(app_id, app_secret).tenant_access_token is not None
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -7,13 +7,13 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class CreateDocumentTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app_id = self.runtime.credentials.get('app_id')
app_secret = self.runtime.credentials.get('app_secret')
app_id = self.runtime.credentials.get("app_id")
app_secret = self.runtime.credentials.get("app_secret")
client = FeishuRequest(app_id, app_secret)
title = tool_parameters.get('title')
content = tool_parameters.get('content')
folder_token = tool_parameters.get('folder_token')
title = tool_parameters.get("title")
content = tool_parameters.get("content")
folder_token = tool_parameters.get("folder_token")
res = client.create_document(title, content, folder_token)
return self.create_json_message(res)

View File

@@ -7,11 +7,11 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class GetDocumentRawContentTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app_id = self.runtime.credentials.get('app_id')
app_secret = self.runtime.credentials.get('app_secret')
app_id = self.runtime.credentials.get("app_id")
app_secret = self.runtime.credentials.get("app_secret")
client = FeishuRequest(app_id, app_secret)
document_id = tool_parameters.get('document_id')
document_id = tool_parameters.get("document_id")
res = client.get_document_raw_content(document_id)
return self.create_json_message(res)
return self.create_json_message(res)

View File

@@ -7,13 +7,13 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class ListDocumentBlockTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app_id = self.runtime.credentials.get('app_id')
app_secret = self.runtime.credentials.get('app_secret')
app_id = self.runtime.credentials.get("app_id")
app_secret = self.runtime.credentials.get("app_secret")
client = FeishuRequest(app_id, app_secret)
document_id = tool_parameters.get('document_id')
page_size = tool_parameters.get('page_size', 500)
page_token = tool_parameters.get('page_token', '')
document_id = tool_parameters.get("document_id")
page_size = tool_parameters.get("page_size", 500)
page_token = tool_parameters.get("page_token", "")
res = client.list_document_block(document_id, page_token, page_size)
return self.create_json_message(res)

View File

@@ -7,13 +7,13 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class CreateDocumentTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app_id = self.runtime.credentials.get('app_id')
app_secret = self.runtime.credentials.get('app_secret')
app_id = self.runtime.credentials.get("app_id")
app_secret = self.runtime.credentials.get("app_secret")
client = FeishuRequest(app_id, app_secret)
document_id = tool_parameters.get('document_id')
content = tool_parameters.get('content')
position = tool_parameters.get('position')
document_id = tool_parameters.get("document_id")
content = tool_parameters.get("content")
position = tool_parameters.get("position")
res = client.write_document(document_id, content, position)
return self.create_json_message(res)

View File

@@ -5,11 +5,11 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class FeishuMessageProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
app_id = credentials.get('app_id')
app_secret = credentials.get('app_secret')
app_id = credentials.get("app_id")
app_secret = credentials.get("app_secret")
if not app_id or not app_secret:
raise ToolProviderCredentialValidationError("app_id and app_secret is required")
try:
assert FeishuRequest(app_id, app_secret).tenant_access_token is not None
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -7,14 +7,14 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class SendBotMessageTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app_id = self.runtime.credentials.get('app_id')
app_secret = self.runtime.credentials.get('app_secret')
app_id = self.runtime.credentials.get("app_id")
app_secret = self.runtime.credentials.get("app_secret")
client = FeishuRequest(app_id, app_secret)
receive_id_type = tool_parameters.get('receive_id_type')
receive_id = tool_parameters.get('receive_id')
msg_type = tool_parameters.get('msg_type')
content = tool_parameters.get('content')
receive_id_type = tool_parameters.get("receive_id_type")
receive_id = tool_parameters.get("receive_id")
msg_type = tool_parameters.get("msg_type")
content = tool_parameters.get("content")
res = client.send_bot_message(receive_id_type, receive_id, msg_type, content)
return self.create_json_message(res)

View File

@@ -6,14 +6,14 @@ from core.tools.utils.feishu_api_utils import FeishuRequest
class SendWebhookMessageTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) ->ToolInvokeMessage:
app_id = self.runtime.credentials.get('app_id')
app_secret = self.runtime.credentials.get('app_secret')
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app_id = self.runtime.credentials.get("app_id")
app_secret = self.runtime.credentials.get("app_secret")
client = FeishuRequest(app_id, app_secret)
webhook = tool_parameters.get('webhook')
msg_type = tool_parameters.get('msg_type')
content = tool_parameters.get('content')
webhook = tool_parameters.get("webhook")
msg_type = tool_parameters.get("msg_type")
content = tool_parameters.get("content")
res = client.send_webhook_message(webhook, msg_type, content)
return self.create_json_message(res)

View File

@@ -7,15 +7,8 @@ class FirecrawlProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
# Example validation using the ScrapeTool, only scraping title for minimize content
ScrapeTool().fork_tool_runtime(
runtime={"credentials": credentials}
).invoke(
user_id='',
tool_parameters={
"url": "https://google.com",
"onlyIncludeTags": 'title'
}
ScrapeTool().fork_tool_runtime(runtime={"credentials": credentials}).invoke(
user_id="", tool_parameters={"url": "https://google.com", "onlyIncludeTags": "title"}
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -13,27 +13,24 @@ logger = logging.getLogger(__name__)
class FirecrawlApp:
def __init__(self, api_key: str | None = None, base_url: str | None = None):
self.api_key = api_key
self.base_url = base_url or 'https://api.firecrawl.dev'
self.base_url = base_url or "https://api.firecrawl.dev"
if not self.api_key:
raise ValueError("API key is required")
def _prepare_headers(self, idempotency_key: str | None = None):
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.api_key}'
}
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"}
if idempotency_key:
headers['Idempotency-Key'] = idempotency_key
headers["Idempotency-Key"] = idempotency_key
return headers
def _request(
self,
method: str,
url: str,
data: Mapping[str, Any] | None = None,
headers: Mapping[str, str] | None = None,
retries: int = 3,
backoff_factor: float = 0.3,
self,
method: str,
url: str,
data: Mapping[str, Any] | None = None,
headers: Mapping[str, str] | None = None,
retries: int = 3,
backoff_factor: float = 0.3,
) -> Mapping[str, Any] | None:
if not headers:
headers = self._prepare_headers()
@@ -44,54 +41,54 @@ class FirecrawlApp:
return response.json()
except requests.exceptions.RequestException as e:
if i < retries - 1:
time.sleep(backoff_factor * (2 ** i))
time.sleep(backoff_factor * (2**i))
else:
raise
return None
def scrape_url(self, url: str, **kwargs):
endpoint = f'{self.base_url}/v0/scrape'
data = {'url': url, **kwargs}
endpoint = f"{self.base_url}/v0/scrape"
data = {"url": url, **kwargs}
logger.debug(f"Sent request to {endpoint=} body={data}")
response = self._request('POST', endpoint, data)
response = self._request("POST", endpoint, data)
if response is None:
raise HTTPError("Failed to scrape URL after multiple retries")
return response
def search(self, query: str, **kwargs):
endpoint = f'{self.base_url}/v0/search'
data = {'query': query, **kwargs}
endpoint = f"{self.base_url}/v0/search"
data = {"query": query, **kwargs}
logger.debug(f"Sent request to {endpoint=} body={data}")
response = self._request('POST', endpoint, data)
response = self._request("POST", endpoint, data)
if response is None:
raise HTTPError("Failed to perform search after multiple retries")
return response
def crawl_url(
self, url: str, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs
self, url: str, wait: bool = True, poll_interval: int = 5, idempotency_key: str | None = None, **kwargs
):
endpoint = f'{self.base_url}/v0/crawl'
endpoint = f"{self.base_url}/v0/crawl"
headers = self._prepare_headers(idempotency_key)
data = {'url': url, **kwargs}
data = {"url": url, **kwargs}
logger.debug(f"Sent request to {endpoint=} body={data}")
response = self._request('POST', endpoint, data, headers)
response = self._request("POST", endpoint, data, headers)
if response is None:
raise HTTPError("Failed to initiate crawl after multiple retries")
job_id: str = response['jobId']
job_id: str = response["jobId"]
if wait:
return self._monitor_job_status(job_id=job_id, poll_interval=poll_interval)
return response
def check_crawl_status(self, job_id: str):
endpoint = f'{self.base_url}/v0/crawl/status/{job_id}'
response = self._request('GET', endpoint)
endpoint = f"{self.base_url}/v0/crawl/status/{job_id}"
response = self._request("GET", endpoint)
if response is None:
raise HTTPError(f"Failed to check status for job {job_id} after multiple retries")
return response
def cancel_crawl_job(self, job_id: str):
endpoint = f'{self.base_url}/v0/crawl/cancel/{job_id}'
response = self._request('DELETE', endpoint)
endpoint = f"{self.base_url}/v0/crawl/cancel/{job_id}"
response = self._request("DELETE", endpoint)
if response is None:
raise HTTPError(f"Failed to cancel job {job_id} after multiple retries")
return response
@@ -99,9 +96,9 @@ class FirecrawlApp:
def _monitor_job_status(self, job_id: str, poll_interval: int):
while True:
status = self.check_crawl_status(job_id)
if status['status'] == 'completed':
if status["status"] == "completed":
return status
elif status['status'] == 'failed':
elif status["status"] == "failed":
raise HTTPError(f'Job {job_id} failed: {status["error"]}')
time.sleep(poll_interval)
@@ -109,7 +106,7 @@ class FirecrawlApp:
def get_array_params(tool_parameters: dict[str, Any], key):
param = tool_parameters.get(key)
if param:
return param.split(',')
return param.split(",")
def get_json_params(tool_parameters: dict[str, Any], key):

View File

@@ -11,38 +11,36 @@ class CrawlTool(BuiltinTool):
the crawlerOptions and pageOptions comes from doc here:
https://docs.firecrawl.dev/api-reference/endpoint/crawl
"""
app = FirecrawlApp(api_key=self.runtime.credentials['firecrawl_api_key'],
base_url=self.runtime.credentials['base_url'])
app = FirecrawlApp(
api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
)
crawlerOptions = {}
pageOptions = {}
wait_for_results = tool_parameters.get('wait_for_results', True)
wait_for_results = tool_parameters.get("wait_for_results", True)
crawlerOptions['excludes'] = get_array_params(tool_parameters, 'excludes')
crawlerOptions['includes'] = get_array_params(tool_parameters, 'includes')
crawlerOptions['returnOnlyUrls'] = tool_parameters.get('returnOnlyUrls', False)
crawlerOptions['maxDepth'] = tool_parameters.get('maxDepth')
crawlerOptions['mode'] = tool_parameters.get('mode')
crawlerOptions['ignoreSitemap'] = tool_parameters.get('ignoreSitemap', False)
crawlerOptions['limit'] = tool_parameters.get('limit', 5)
crawlerOptions['allowBackwardCrawling'] = tool_parameters.get('allowBackwardCrawling', False)
crawlerOptions['allowExternalContentLinks'] = tool_parameters.get('allowExternalContentLinks', False)
crawlerOptions["excludes"] = get_array_params(tool_parameters, "excludes")
crawlerOptions["includes"] = get_array_params(tool_parameters, "includes")
crawlerOptions["returnOnlyUrls"] = tool_parameters.get("returnOnlyUrls", False)
crawlerOptions["maxDepth"] = tool_parameters.get("maxDepth")
crawlerOptions["mode"] = tool_parameters.get("mode")
crawlerOptions["ignoreSitemap"] = tool_parameters.get("ignoreSitemap", False)
crawlerOptions["limit"] = tool_parameters.get("limit", 5)
crawlerOptions["allowBackwardCrawling"] = tool_parameters.get("allowBackwardCrawling", False)
crawlerOptions["allowExternalContentLinks"] = tool_parameters.get("allowExternalContentLinks", False)
pageOptions['headers'] = get_json_params(tool_parameters, 'headers')
pageOptions['includeHtml'] = tool_parameters.get('includeHtml', False)
pageOptions['includeRawHtml'] = tool_parameters.get('includeRawHtml', False)
pageOptions['onlyIncludeTags'] = get_array_params(tool_parameters, 'onlyIncludeTags')
pageOptions['removeTags'] = get_array_params(tool_parameters, 'removeTags')
pageOptions['onlyMainContent'] = tool_parameters.get('onlyMainContent', False)
pageOptions['replaceAllPathsWithAbsolutePaths'] = tool_parameters.get('replaceAllPathsWithAbsolutePaths', False)
pageOptions['screenshot'] = tool_parameters.get('screenshot', False)
pageOptions['waitFor'] = tool_parameters.get('waitFor', 0)
pageOptions["headers"] = get_json_params(tool_parameters, "headers")
pageOptions["includeHtml"] = tool_parameters.get("includeHtml", False)
pageOptions["includeRawHtml"] = tool_parameters.get("includeRawHtml", False)
pageOptions["onlyIncludeTags"] = get_array_params(tool_parameters, "onlyIncludeTags")
pageOptions["removeTags"] = get_array_params(tool_parameters, "removeTags")
pageOptions["onlyMainContent"] = tool_parameters.get("onlyMainContent", False)
pageOptions["replaceAllPathsWithAbsolutePaths"] = tool_parameters.get("replaceAllPathsWithAbsolutePaths", False)
pageOptions["screenshot"] = tool_parameters.get("screenshot", False)
pageOptions["waitFor"] = tool_parameters.get("waitFor", 0)
crawl_result = app.crawl_url(
url=tool_parameters['url'],
wait=wait_for_results,
crawlerOptions=crawlerOptions,
pageOptions=pageOptions
url=tool_parameters["url"], wait=wait_for_results, crawlerOptions=crawlerOptions, pageOptions=pageOptions
)
return self.create_json_message(crawl_result)

View File

@@ -7,14 +7,15 @@ from core.tools.tool.builtin_tool import BuiltinTool
class CrawlJobTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
app = FirecrawlApp(api_key=self.runtime.credentials['firecrawl_api_key'],
base_url=self.runtime.credentials['base_url'])
operation = tool_parameters.get('operation', 'get')
if operation == 'get':
result = app.check_crawl_status(job_id=tool_parameters['job_id'])
elif operation == 'cancel':
result = app.cancel_crawl_job(job_id=tool_parameters['job_id'])
app = FirecrawlApp(
api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
)
operation = tool_parameters.get("operation", "get")
if operation == "get":
result = app.check_crawl_status(job_id=tool_parameters["job_id"])
elif operation == "cancel":
result = app.cancel_crawl_job(job_id=tool_parameters["job_id"])
else:
raise ValueError(f'Invalid operation: {operation}')
raise ValueError(f"Invalid operation: {operation}")
return self.create_json_message(result)

View File

@@ -6,34 +6,34 @@ from core.tools.tool.builtin_tool import BuiltinTool
class ScrapeTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage:
"""
the pageOptions and extractorOptions comes from doc here:
https://docs.firecrawl.dev/api-reference/endpoint/scrape
"""
app = FirecrawlApp(api_key=self.runtime.credentials['firecrawl_api_key'],
base_url=self.runtime.credentials['base_url'])
app = FirecrawlApp(
api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
)
pageOptions = {}
extractorOptions = {}
pageOptions['headers'] = get_json_params(tool_parameters, 'headers')
pageOptions['includeHtml'] = tool_parameters.get('includeHtml', False)
pageOptions['includeRawHtml'] = tool_parameters.get('includeRawHtml', False)
pageOptions['onlyIncludeTags'] = get_array_params(tool_parameters, 'onlyIncludeTags')
pageOptions['removeTags'] = get_array_params(tool_parameters, 'removeTags')
pageOptions['onlyMainContent'] = tool_parameters.get('onlyMainContent', False)
pageOptions['replaceAllPathsWithAbsolutePaths'] = tool_parameters.get('replaceAllPathsWithAbsolutePaths', False)
pageOptions['screenshot'] = tool_parameters.get('screenshot', False)
pageOptions['waitFor'] = tool_parameters.get('waitFor', 0)
pageOptions["headers"] = get_json_params(tool_parameters, "headers")
pageOptions["includeHtml"] = tool_parameters.get("includeHtml", False)
pageOptions["includeRawHtml"] = tool_parameters.get("includeRawHtml", False)
pageOptions["onlyIncludeTags"] = get_array_params(tool_parameters, "onlyIncludeTags")
pageOptions["removeTags"] = get_array_params(tool_parameters, "removeTags")
pageOptions["onlyMainContent"] = tool_parameters.get("onlyMainContent", False)
pageOptions["replaceAllPathsWithAbsolutePaths"] = tool_parameters.get("replaceAllPathsWithAbsolutePaths", False)
pageOptions["screenshot"] = tool_parameters.get("screenshot", False)
pageOptions["waitFor"] = tool_parameters.get("waitFor", 0)
extractorOptions['mode'] = tool_parameters.get('mode', '')
extractorOptions['extractionPrompt'] = tool_parameters.get('extractionPrompt', '')
extractorOptions['extractionSchema'] = get_json_params(tool_parameters, 'extractionSchema')
extractorOptions["mode"] = tool_parameters.get("mode", "")
extractorOptions["extractionPrompt"] = tool_parameters.get("extractionPrompt", "")
extractorOptions["extractionSchema"] = get_json_params(tool_parameters, "extractionSchema")
crawl_result = app.scrape_url(url=tool_parameters['url'],
pageOptions=pageOptions,
extractorOptions=extractorOptions)
crawl_result = app.scrape_url(
url=tool_parameters["url"], pageOptions=pageOptions, extractorOptions=extractorOptions
)
return self.create_json_message(crawl_result)

View File

@@ -11,18 +11,17 @@ class SearchTool(BuiltinTool):
the pageOptions and searchOptions comes from doc here:
https://docs.firecrawl.dev/api-reference/endpoint/search
"""
app = FirecrawlApp(api_key=self.runtime.credentials['firecrawl_api_key'],
base_url=self.runtime.credentials['base_url'])
app = FirecrawlApp(
api_key=self.runtime.credentials["firecrawl_api_key"], base_url=self.runtime.credentials["base_url"]
)
pageOptions = {}
pageOptions['onlyMainContent'] = tool_parameters.get('onlyMainContent', False)
pageOptions['fetchPageContent'] = tool_parameters.get('fetchPageContent', True)
pageOptions['includeHtml'] = tool_parameters.get('includeHtml', False)
pageOptions['includeRawHtml'] = tool_parameters.get('includeRawHtml', False)
searchOptions = {'limit': tool_parameters.get('limit')}
pageOptions["onlyMainContent"] = tool_parameters.get("onlyMainContent", False)
pageOptions["fetchPageContent"] = tool_parameters.get("fetchPageContent", True)
pageOptions["includeHtml"] = tool_parameters.get("includeHtml", False)
pageOptions["includeRawHtml"] = tool_parameters.get("includeRawHtml", False)
searchOptions = {"limit": tool_parameters.get("limit")}
search_result = app.search(
query=tool_parameters['keyword'],
pageOptions=pageOptions,
searchOptions=searchOptions
query=tool_parameters["keyword"], pageOptions=pageOptions, searchOptions=searchOptions
)
return self.create_json_message(search_result)

View File

@@ -9,17 +9,19 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class GaodeProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
if 'api_key' not in credentials or not credentials.get('api_key'):
if "api_key" not in credentials or not credentials.get("api_key"):
raise ToolProviderCredentialValidationError("Gaode API key is required.")
try:
response = requests.get(url="https://restapi.amap.com/v3/geocode/geo?address={address}&key={apikey}"
"".format(address=urllib.parse.quote('广东省广州市天河区广州塔'),
apikey=credentials.get('api_key')))
if response.status_code == 200 and (response.json()).get('info') == 'OK':
response = requests.get(
url="https://restapi.amap.com/v3/geocode/geo?address={address}&key={apikey}" "".format(
address=urllib.parse.quote("广东省广州市天河区广州塔"), apikey=credentials.get("api_key")
)
)
if response.status_code == 200 and (response.json()).get("info") == "OK":
pass
else:
raise ToolProviderCredentialValidationError((response.json()).get('info'))
raise ToolProviderCredentialValidationError((response.json()).get("info"))
except Exception as e:
raise ToolProviderCredentialValidationError("Gaode API Key is invalid. {}".format(e))
except Exception as e:

View File

@@ -8,50 +8,57 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GaodeRepositoriesTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
city = tool_parameters.get('city', '')
city = tool_parameters.get("city", "")
if not city:
return self.create_text_message('Please tell me your city')
return self.create_text_message("Please tell me your city")
if 'api_key' not in self.runtime.credentials or not self.runtime.credentials.get('api_key'):
if "api_key" not in self.runtime.credentials or not self.runtime.credentials.get("api_key"):
return self.create_text_message("Gaode API key is required.")
try:
s = requests.session()
api_domain = 'https://restapi.amap.com/v3'
city_response = s.request(method='GET', headers={"Content-Type": "application/json; charset=utf-8"},
url="{url}/config/district?keywords={keywords}"
"&subdistrict=0&extensions=base&key={apikey}"
"".format(url=api_domain, keywords=city,
apikey=self.runtime.credentials.get('api_key')))
api_domain = "https://restapi.amap.com/v3"
city_response = s.request(
method="GET",
headers={"Content-Type": "application/json; charset=utf-8"},
url="{url}/config/district?keywords={keywords}" "&subdistrict=0&extensions=base&key={apikey}" "".format(
url=api_domain, keywords=city, apikey=self.runtime.credentials.get("api_key")
),
)
City_data = city_response.json()
if city_response.status_code == 200 and City_data.get('info') == 'OK':
if len(City_data.get('districts')) > 0:
CityCode = City_data['districts'][0]['adcode']
weatherInfo_response = s.request(method='GET',
url="{url}/weather/weatherInfo?city={citycode}&extensions=all&key={apikey}&output=json"
"".format(url=api_domain, citycode=CityCode,
apikey=self.runtime.credentials.get('api_key')))
if city_response.status_code == 200 and City_data.get("info") == "OK":
if len(City_data.get("districts")) > 0:
CityCode = City_data["districts"][0]["adcode"]
weatherInfo_response = s.request(
method="GET",
url="{url}/weather/weatherInfo?city={citycode}&extensions=all&key={apikey}&output=json"
"".format(url=api_domain, citycode=CityCode, apikey=self.runtime.credentials.get("api_key")),
)
weatherInfo_data = weatherInfo_response.json()
if weatherInfo_response.status_code == 200 and weatherInfo_data.get('info') == 'OK':
if weatherInfo_response.status_code == 200 and weatherInfo_data.get("info") == "OK":
contents = []
if len(weatherInfo_data.get('forecasts')) > 0:
for item in weatherInfo_data['forecasts'][0]['casts']:
if len(weatherInfo_data.get("forecasts")) > 0:
for item in weatherInfo_data["forecasts"][0]["casts"]:
content = {}
content['date'] = item.get('date')
content['week'] = item.get('week')
content['dayweather'] = item.get('dayweather')
content['daytemp_float'] = item.get('daytemp_float')
content['daywind'] = item.get('daywind')
content['nightweather'] = item.get('nightweather')
content['nighttemp_float'] = item.get('nighttemp_float')
content["date"] = item.get("date")
content["week"] = item.get("week")
content["dayweather"] = item.get("dayweather")
content["daytemp_float"] = item.get("daytemp_float")
content["daywind"] = item.get("daywind")
content["nightweather"] = item.get("nightweather")
content["nighttemp_float"] = item.get("nighttemp_float")
contents.append(content)
s.close()
return self.create_text_message(self.summary(user_id=user_id, content=json.dumps(contents, ensure_ascii=False)))
return self.create_text_message(
self.summary(user_id=user_id, content=json.dumps(contents, ensure_ascii=False))
)
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:
return self.create_text_message("Gaode API Key and Api Version is invalid. {}".format(e))

View File

@@ -7,16 +7,13 @@ class GetImgAIProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
# Example validation using the text2image tool
Text2ImageTool().fork_tool_runtime(
runtime={"credentials": credentials}
).invoke(
user_id='',
Text2ImageTool().fork_tool_runtime(runtime={"credentials": credentials}).invoke(
user_id="",
tool_parameters={
"prompt": "A fire egg",
"response_format": "url",
"style": "photorealism",
}
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -8,18 +8,16 @@ from requests.exceptions import HTTPError
logger = logging.getLogger(__name__)
class GetImgAIApp:
def __init__(self, api_key: str | None = None, base_url: str | None = None):
self.api_key = api_key
self.base_url = base_url or 'https://api.getimg.ai/v1'
self.base_url = base_url or "https://api.getimg.ai/v1"
if not self.api_key:
raise ValueError("API key is required")
def _prepare_headers(self):
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.api_key}'
}
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"}
return headers
def _request(
@@ -38,22 +36,20 @@ class GetImgAIApp:
return response.json()
except requests.exceptions.RequestException as e:
if i < retries - 1 and isinstance(e, HTTPError) and e.response.status_code >= 500:
time.sleep(backoff_factor * (2 ** i))
time.sleep(backoff_factor * (2**i))
else:
raise
return None
def text2image(
self, mode: str, **kwargs
):
data = kwargs['params']
if not data.get('prompt'):
def text2image(self, mode: str, **kwargs):
data = kwargs["params"]
if not data.get("prompt"):
raise ValueError("Prompt is required")
endpoint = f'{self.base_url}/{mode}/text-to-image'
endpoint = f"{self.base_url}/{mode}/text-to-image"
headers = self._prepare_headers()
logger.debug(f"Send request to {endpoint=} body={data}")
response = self._request('POST', endpoint, data, headers)
response = self._request("POST", endpoint, data, headers)
if response is None:
raise HTTPError("Failed to initiate getimg.ai after multiple retries")
return response

View File

@@ -7,28 +7,28 @@ from core.tools.tool.builtin_tool import BuiltinTool
class Text2ImageTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
app = GetImgAIApp(api_key=self.runtime.credentials['getimg_api_key'], base_url=self.runtime.credentials['base_url'])
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
app = GetImgAIApp(
api_key=self.runtime.credentials["getimg_api_key"], base_url=self.runtime.credentials["base_url"]
)
options = {
'style': tool_parameters.get('style'),
'prompt': tool_parameters.get('prompt'),
'aspect_ratio': tool_parameters.get('aspect_ratio'),
'output_format': tool_parameters.get('output_format', 'jpeg'),
'response_format': tool_parameters.get('response_format', 'url'),
'width': tool_parameters.get('width'),
'height': tool_parameters.get('height'),
'steps': tool_parameters.get('steps'),
'negative_prompt': tool_parameters.get('negative_prompt'),
'prompt_2': tool_parameters.get('prompt_2'),
"style": tool_parameters.get("style"),
"prompt": tool_parameters.get("prompt"),
"aspect_ratio": tool_parameters.get("aspect_ratio"),
"output_format": tool_parameters.get("output_format", "jpeg"),
"response_format": tool_parameters.get("response_format", "url"),
"width": tool_parameters.get("width"),
"height": tool_parameters.get("height"),
"steps": tool_parameters.get("steps"),
"negative_prompt": tool_parameters.get("negative_prompt"),
"prompt_2": tool_parameters.get("prompt_2"),
}
options = {k: v for k, v in options.items() if v}
text2image_result = app.text2image(
mode=tool_parameters.get('mode', 'essential-v2'),
params=options,
wait=True
)
text2image_result = app.text2image(mode=tool_parameters.get("mode", "essential-v2"), params=options, wait=True)
if not isinstance(text2image_result, str):
text2image_result = json.dumps(text2image_result, ensure_ascii=False, indent=4)

View File

@@ -7,25 +7,25 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class GithubProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
if 'access_tokens' not in credentials or not credentials.get('access_tokens'):
if "access_tokens" not in credentials or not credentials.get("access_tokens"):
raise ToolProviderCredentialValidationError("Github API Access Tokens is required.")
if 'api_version' not in credentials or not credentials.get('api_version'):
api_version = '2022-11-28'
if "api_version" not in credentials or not credentials.get("api_version"):
api_version = "2022-11-28"
else:
api_version = credentials.get('api_version')
api_version = credentials.get("api_version")
try:
headers = {
"Content-Type": "application/vnd.github+json",
"Authorization": f"Bearer {credentials.get('access_tokens')}",
"X-GitHub-Api-Version": api_version
"X-GitHub-Api-Version": api_version,
}
response = requests.get(
url="https://api.github.com/search/users?q={account}".format(account='charli117'),
headers=headers)
url="https://api.github.com/search/users?q={account}".format(account="charli117"), headers=headers
)
if response.status_code != 200:
raise ToolProviderCredentialValidationError((response.json()).get('message'))
raise ToolProviderCredentialValidationError((response.json()).get("message"))
except Exception as e:
raise ToolProviderCredentialValidationError("Github API Key and Api Version is invalid. {}".format(e))
except Exception as e:

View File

@@ -10,53 +10,61 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GithubRepositoriesTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
top_n = tool_parameters.get('top_n', 5)
query = tool_parameters.get('query', '')
top_n = tool_parameters.get("top_n", 5)
query = tool_parameters.get("query", "")
if not query:
return self.create_text_message('Please input symbol')
return self.create_text_message("Please input symbol")
if 'access_tokens' not in self.runtime.credentials or not self.runtime.credentials.get('access_tokens'):
if "access_tokens" not in self.runtime.credentials or not self.runtime.credentials.get("access_tokens"):
return self.create_text_message("Github API Access Tokens is required.")
if 'api_version' not in self.runtime.credentials or not self.runtime.credentials.get('api_version'):
api_version = '2022-11-28'
if "api_version" not in self.runtime.credentials or not self.runtime.credentials.get("api_version"):
api_version = "2022-11-28"
else:
api_version = self.runtime.credentials.get('api_version')
api_version = self.runtime.credentials.get("api_version")
try:
headers = {
"Content-Type": "application/vnd.github+json",
"Authorization": f"Bearer {self.runtime.credentials.get('access_tokens')}",
"X-GitHub-Api-Version": api_version
"X-GitHub-Api-Version": api_version,
}
s = requests.session()
api_domain = 'https://api.github.com'
response = s.request(method='GET', headers=headers,
url=f"{api_domain}/search/repositories?"
f"q={quote(query)}&sort=stars&per_page={top_n}&order=desc")
api_domain = "https://api.github.com"
response = s.request(
method="GET",
headers=headers,
url=f"{api_domain}/search/repositories?" f"q={quote(query)}&sort=stars&per_page={top_n}&order=desc",
)
response_data = response.json()
if response.status_code == 200 and isinstance(response_data.get('items'), list):
if response.status_code == 200 and isinstance(response_data.get("items"), list):
contents = []
if len(response_data.get('items')) > 0:
for item in response_data.get('items'):
if len(response_data.get("items")) > 0:
for item in response_data.get("items"):
content = {}
updated_at_object = datetime.strptime(item['updated_at'], "%Y-%m-%dT%H:%M:%SZ")
content['owner'] = item['owner']['login']
content['name'] = item['name']
content['description'] = item['description'][:100] + '...' if len(item['description']) > 100 else item['description']
content['url'] = item['html_url']
content['star'] = item['watchers']
content['forks'] = item['forks']
content['updated'] = updated_at_object.strftime("%Y-%m-%d")
updated_at_object = datetime.strptime(item["updated_at"], "%Y-%m-%dT%H:%M:%SZ")
content["owner"] = item["owner"]["login"]
content["name"] = item["name"]
content["description"] = (
item["description"][:100] + "..." if len(item["description"]) > 100 else item["description"]
)
content["url"] = item["html_url"]
content["star"] = item["watchers"]
content["forks"] = item["forks"]
content["updated"] = updated_at_object.strftime("%Y-%m-%d")
contents.append(content)
s.close()
return self.create_text_message(self.summary(user_id=user_id, content=json.dumps(contents, ensure_ascii=False)))
return self.create_text_message(
self.summary(user_id=user_id, content=json.dumps(contents, ensure_ascii=False))
)
else:
return self.create_text_message(f'No items related to {query} were found.')
return self.create_text_message(f"No items related to {query} were found.")
else:
return self.create_text_message((response.json()).get('message'))
return self.create_text_message((response.json()).get("message"))
except Exception as e:
return self.create_text_message("Github API Key and Api Version is invalid. {}".format(e))

View File

@@ -9,13 +9,13 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class GitlabProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
if 'access_tokens' not in credentials or not credentials.get('access_tokens'):
if "access_tokens" not in credentials or not credentials.get("access_tokens"):
raise ToolProviderCredentialValidationError("Gitlab Access Tokens is required.")
if 'site_url' not in credentials or not credentials.get('site_url'):
site_url = 'https://gitlab.com'
if "site_url" not in credentials or not credentials.get("site_url"):
site_url = "https://gitlab.com"
else:
site_url = credentials.get('site_url')
site_url = credentials.get("site_url")
try:
headers = {
@@ -23,12 +23,10 @@ class GitlabProvider(BuiltinToolProviderController):
"Authorization": f"Bearer {credentials.get('access_tokens')}",
}
response = requests.get(
url= f"{site_url}/api/v4/user",
headers=headers)
response = requests.get(url=f"{site_url}/api/v4/user", headers=headers)
if response.status_code != 200:
raise ToolProviderCredentialValidationError((response.json()).get('message'))
raise ToolProviderCredentialValidationError((response.json()).get("message"))
except Exception as e:
raise ToolProviderCredentialValidationError("Gitlab Access Tokens is invalid. {}".format(e))
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -9,39 +9,47 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GitlabCommitsTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
project = tool_parameters.get('project', '')
employee = tool_parameters.get('employee', '')
start_time = tool_parameters.get('start_time', '')
end_time = tool_parameters.get('end_time', '')
change_type = tool_parameters.get('change_type', 'all')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
project = tool_parameters.get("project", "")
employee = tool_parameters.get("employee", "")
start_time = tool_parameters.get("start_time", "")
end_time = tool_parameters.get("end_time", "")
change_type = tool_parameters.get("change_type", "all")
if not project:
return self.create_text_message('Project is required')
return self.create_text_message("Project is required")
if not start_time:
start_time = (datetime.utcnow() - timedelta(days=1)).isoformat()
if not end_time:
end_time = datetime.utcnow().isoformat()
access_token = self.runtime.credentials.get('access_tokens')
site_url = self.runtime.credentials.get('site_url')
access_token = self.runtime.credentials.get("access_tokens")
site_url = self.runtime.credentials.get("site_url")
if 'access_tokens' not in self.runtime.credentials or not self.runtime.credentials.get('access_tokens'):
if "access_tokens" not in self.runtime.credentials or not self.runtime.credentials.get("access_tokens"):
return self.create_text_message("Gitlab API Access Tokens is required.")
if 'site_url' not in self.runtime.credentials or not self.runtime.credentials.get('site_url'):
site_url = 'https://gitlab.com'
if "site_url" not in self.runtime.credentials or not self.runtime.credentials.get("site_url"):
site_url = "https://gitlab.com"
# Get commit content
result = self.fetch(user_id, site_url, access_token, project, employee, start_time, end_time, change_type)
return [self.create_json_message(item) for item in result]
def fetch(self,user_id: str, site_url: str, access_token: str, project: str, employee: str = None, start_time: str = '', end_time: str = '', change_type: str = '') -> list[dict[str, Any]]:
def fetch(
self,
user_id: str,
site_url: str,
access_token: str,
project: str,
employee: str = None,
start_time: str = "",
end_time: str = "",
change_type: str = "",
) -> list[dict[str, Any]]:
domain = site_url
headers = {"PRIVATE-TOKEN": access_token}
results = []
@@ -53,59 +61,66 @@ class GitlabCommitsTool(BuiltinTool):
response.raise_for_status()
projects = response.json()
filtered_projects = [p for p in projects if project == "*" or p['name'] == project]
filtered_projects = [p for p in projects if project == "*" or p["name"] == project]
for project in filtered_projects:
project_id = project['id']
project_name = project['name']
project_id = project["id"]
project_name = project["name"]
print(f"Project: {project_name}")
# Get all of project commits
commits_url = f"{domain}/api/v4/projects/{project_id}/repository/commits"
params = {
'since': start_time,
'until': end_time
}
params = {"since": start_time, "until": end_time}
if employee:
params['author'] = employee
params["author"] = employee
commits_response = requests.get(commits_url, headers=headers, params=params)
commits_response.raise_for_status()
commits = commits_response.json()
for commit in commits:
commit_sha = commit['id']
author_name = commit['author_name']
commit_sha = commit["id"]
author_name = commit["author_name"]
diff_url = f"{domain}/api/v4/projects/{project_id}/repository/commits/{commit_sha}/diff"
diff_response = requests.get(diff_url, headers=headers)
diff_response.raise_for_status()
diffs = diff_response.json()
for diff in diffs:
# Calculate code lines of changed
added_lines = diff['diff'].count('\n+')
removed_lines = diff['diff'].count('\n-')
added_lines = diff["diff"].count("\n+")
removed_lines = diff["diff"].count("\n-")
total_changes = added_lines + removed_lines
if change_type == "new":
if added_lines > 1:
final_code = ''.join([line[1:] for line in diff['diff'].split('\n') if line.startswith('+') and not line.startswith('+++')])
results.append({
"commit_sha": commit_sha,
"author_name": author_name,
"diff": final_code
})
final_code = "".join(
[
line[1:]
for line in diff["diff"].split("\n")
if line.startswith("+") and not line.startswith("+++")
]
)
results.append(
{"commit_sha": commit_sha, "author_name": author_name, "diff": final_code}
)
else:
if total_changes > 1:
final_code = ''.join([line[1:] for line in diff['diff'].split('\n') if (line.startswith('+') or line.startswith('-')) and not line.startswith('+++') and not line.startswith('---')])
final_code = "".join(
[
line[1:]
for line in diff["diff"].split("\n")
if (line.startswith("+") or line.startswith("-"))
and not line.startswith("+++")
and not line.startswith("---")
]
)
final_code_escaped = json.dumps(final_code)[1:-1] # Escape the final code
results.append({
"commit_sha": commit_sha,
"author_name": author_name,
"diff": final_code_escaped
})
results.append(
{"commit_sha": commit_sha, "author_name": author_name, "diff": final_code_escaped}
)
except requests.RequestException as e:
print(f"Error fetching data from GitLab: {e}")
return results
return results

View File

@@ -7,32 +7,29 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GitlabFilesTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
project = tool_parameters.get('project', '')
branch = tool_parameters.get('branch', '')
path = tool_parameters.get('path', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
project = tool_parameters.get("project", "")
branch = tool_parameters.get("branch", "")
path = tool_parameters.get("path", "")
if not project:
return self.create_text_message('Project is required')
return self.create_text_message("Project is required")
if not branch:
return self.create_text_message('Branch is required')
return self.create_text_message("Branch is required")
if not path:
return self.create_text_message('Path is required')
return self.create_text_message("Path is required")
access_token = self.runtime.credentials.get('access_tokens')
site_url = self.runtime.credentials.get('site_url')
access_token = self.runtime.credentials.get("access_tokens")
site_url = self.runtime.credentials.get("site_url")
if 'access_tokens' not in self.runtime.credentials or not self.runtime.credentials.get('access_tokens'):
if "access_tokens" not in self.runtime.credentials or not self.runtime.credentials.get("access_tokens"):
return self.create_text_message("Gitlab API Access Tokens is required.")
if 'site_url' not in self.runtime.credentials or not self.runtime.credentials.get('site_url'):
site_url = 'https://gitlab.com'
if "site_url" not in self.runtime.credentials or not self.runtime.credentials.get("site_url"):
site_url = "https://gitlab.com"
# Get project ID from project name
project_id = self.get_project_id(site_url, access_token, project)
if not project_id:
@@ -42,9 +39,9 @@ class GitlabFilesTool(BuiltinTool):
result = self.fetch(user_id, project_id, site_url, access_token, branch, path)
return [self.create_json_message(item) for item in result]
def extract_project_name_and_path(self, path: str) -> tuple[str, str]:
parts = path.split('/', 1)
parts = path.split("/", 1)
if len(parts) < 2:
return None, None
return parts[0], parts[1]
@@ -57,13 +54,15 @@ class GitlabFilesTool(BuiltinTool):
response.raise_for_status()
projects = response.json()
for project in projects:
if project['name'] == project_name:
return project['id']
if project["name"] == project_name:
return project["id"]
except requests.RequestException as e:
print(f"Error fetching project ID from GitLab: {e}")
return None
def fetch(self,user_id: str, project_id: str, site_url: str, access_token: str, branch: str, path: str = None) -> list[dict[str, Any]]:
def fetch(
self, user_id: str, project_id: str, site_url: str, access_token: str, branch: str, path: str = None
) -> list[dict[str, Any]]:
domain = site_url
headers = {"PRIVATE-TOKEN": access_token}
results = []
@@ -76,20 +75,16 @@ class GitlabFilesTool(BuiltinTool):
items = response.json()
for item in items:
item_path = item['path']
if item['type'] == 'tree': # It's a directory
item_path = item["path"]
if item["type"] == "tree": # It's a directory
results.extend(self.fetch(project_id, site_url, access_token, branch, item_path))
else: # It's a file
file_url = f"{domain}/api/v4/projects/{project_id}/repository/files/{item_path}/raw?ref={branch}"
file_response = requests.get(file_url, headers=headers)
file_response.raise_for_status()
file_content = file_response.text
results.append({
"path": item_path,
"branch": branch,
"content": file_content
})
results.append({"path": item_path, "branch": branch, "content": file_content})
except requests.RequestException as e:
print(f"Error fetching data from GitLab: {e}")
return results
return results

View File

@@ -13,12 +13,8 @@ class GoogleProvider(BuiltinToolProviderController):
"credentials": credentials,
}
).invoke(
user_id='',
tool_parameters={
"query": "test",
"result_type": "link"
},
user_id="",
tool_parameters={"query": "test", "result_type": "link"},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -9,7 +9,6 @@ SERP_API_URL = "https://serpapi.com/search"
class GoogleSearchTool(BuiltinTool):
def _parse_response(self, response: dict) -> dict:
result = {}
if "knowledge_graph" in response:
@@ -17,25 +16,23 @@ class GoogleSearchTool(BuiltinTool):
result["description"] = response["knowledge_graph"].get("description", "")
if "organic_results" in response:
result["organic_results"] = [
{
"title": item.get("title", ""),
"link": item.get("link", ""),
"snippet": item.get("snippet", "")
}
{"title": item.get("title", ""), "link": item.get("link", ""), "snippet": item.get("snippet", "")}
for item in response["organic_results"]
]
return result
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
params = {
"api_key": self.runtime.credentials['serpapi_api_key'],
"q": tool_parameters['query'],
"api_key": self.runtime.credentials["serpapi_api_key"],
"q": tool_parameters["query"],
"engine": "google",
"google_domain": "google.com",
"gl": "us",
"hl": "en"
"hl": "en",
}
response = requests.get(url=SERP_API_URL, params=params)
response.raise_for_status()

View File

@@ -8,10 +8,6 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class JsonExtractProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
GoogleTranslate().invoke(user_id='',
tool_parameters={
"content": "这是一段测试文本",
"dest": "en"
})
GoogleTranslate().invoke(user_id="", tool_parameters={"content": "这是一段测试文本", "dest": "en"})
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -7,46 +7,40 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GoogleTranslate(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
content = tool_parameters.get('content', '')
content = tool_parameters.get("content", "")
if not content:
return self.create_text_message('Invalid parameter content')
return self.create_text_message("Invalid parameter content")
dest = tool_parameters.get('dest', '')
dest = tool_parameters.get("dest", "")
if not dest:
return self.create_text_message('Invalid parameter destination language')
return self.create_text_message("Invalid parameter destination language")
try:
result = self._translate(content, dest)
return self.create_text_message(str(result))
except Exception:
return self.create_text_message('Translation service error, please check the network')
return self.create_text_message("Translation service error, please check the network")
def _translate(self, content: str, dest: str) -> str:
try:
url = "https://translate.googleapis.com/translate_a/single"
params = {
"client": "gtx",
"sl": "auto",
"tl": dest,
"dt": "t",
"q": content
}
params = {"client": "gtx", "sl": "auto", "tl": dest, "dt": "t", "q": content}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
response_json = requests.get(
url, params=params, headers=headers).json()
response_json = requests.get(url, params=params, headers=headers).json()
result = response_json[0]
translated_text = ''.join([item[0] for item in result if item[0]])
translated_text = "".join([item[0] for item in result if item[0]])
return str(translated_text)
except Exception as e:
return str(e)

View File

@@ -5,4 +5,4 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class HapProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
pass
pass

View File

@@ -8,41 +8,40 @@ from core.tools.tool.builtin_tool import BuiltinTool
class AddWorksheetRecordTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
sign = tool_parameters.get('sign', '')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
worksheet_id = tool_parameters.get('worksheet_id', '')
return self.create_text_message("Invalid parameter Sign")
worksheet_id = tool_parameters.get("worksheet_id", "")
if not worksheet_id:
return self.create_text_message('Invalid parameter Worksheet ID')
record_data = tool_parameters.get('record_data', '')
return self.create_text_message("Invalid parameter Worksheet ID")
record_data = tool_parameters.get("record_data", "")
if not record_data:
return self.create_text_message('Invalid parameter Record Row Data')
host = tool_parameters.get('host', '')
return self.create_text_message("Invalid parameter Record Row Data")
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not host.startswith(("http://", "https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url = f"{host}/v2/open/worksheet/addRow"
headers = {'Content-Type': 'application/json'}
headers = {"Content-Type": "application/json"}
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
try:
payload['controls'] = json.loads(record_data)
payload["controls"] = json.loads(record_data)
res = httpx.post(url, headers=headers, json=payload, timeout=60)
res.raise_for_status()
res_json = res.json()
if res_json.get('error_code') != 1:
if res_json.get("error_code") != 1:
return self.create_text_message(f"Failed to add the new record. {res_json['error_msg']}")
return self.create_text_message(f"New record added successfully. The record ID is {res_json['data']}.")
except httpx.RequestError as e:

View File

@@ -7,43 +7,42 @@ from core.tools.tool.builtin_tool import BuiltinTool
class DeleteWorksheetRecordTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
sign = tool_parameters.get('sign', '')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
worksheet_id = tool_parameters.get('worksheet_id', '')
return self.create_text_message("Invalid parameter Sign")
worksheet_id = tool_parameters.get("worksheet_id", "")
if not worksheet_id:
return self.create_text_message('Invalid parameter Worksheet ID')
row_id = tool_parameters.get('row_id', '')
return self.create_text_message("Invalid parameter Worksheet ID")
row_id = tool_parameters.get("row_id", "")
if not row_id:
return self.create_text_message('Invalid parameter Record Row ID')
host = tool_parameters.get('host', '')
return self.create_text_message("Invalid parameter Record Row ID")
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not host.startswith(("http://", "https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url = f"{host}/v2/open/worksheet/deleteRow"
headers = {'Content-Type': 'application/json'}
headers = {"Content-Type": "application/json"}
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "rowId": row_id}
try:
res = httpx.post(url, headers=headers, json=payload, timeout=30)
res.raise_for_status()
res_json = res.json()
if res_json.get('error_code') != 1:
if res_json.get("error_code") != 1:
return self.create_text_message(f"Failed to delete the record. {res_json['error_msg']}")
return self.create_text_message("Successfully deleted the record.")
except httpx.RequestError as e:
return self.create_text_message(f"Failed to delete the record, request error: {e}")
except Exception as e:
return self.create_text_message(f"Failed to delete the record, unexpected error: {e}")
return self.create_text_message(f"Failed to delete the record, unexpected error: {e}")

View File

@@ -8,43 +8,42 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GetWorksheetFieldsTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
sign = tool_parameters.get('sign', '')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
worksheet_id = tool_parameters.get('worksheet_id', '')
return self.create_text_message("Invalid parameter Sign")
worksheet_id = tool_parameters.get("worksheet_id", "")
if not worksheet_id:
return self.create_text_message('Invalid parameter Worksheet ID')
host = tool_parameters.get('host', '')
return self.create_text_message("Invalid parameter Worksheet ID")
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not host.startswith(("http://", "https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url = f"{host}/v2/open/worksheet/getWorksheetInfo"
headers = {'Content-Type': 'application/json'}
headers = {"Content-Type": "application/json"}
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
try:
res = httpx.post(url, headers=headers, json=payload, timeout=60)
res.raise_for_status()
res_json = res.json()
if res_json.get('error_code') != 1:
if res_json.get("error_code") != 1:
return self.create_text_message(f"Failed to get the worksheet information. {res_json['error_msg']}")
fields_json, fields_table = self.get_controls(res_json['data']['controls'])
result_type = tool_parameters.get('result_type', 'table')
fields_json, fields_table = self.get_controls(res_json["data"]["controls"])
result_type = tool_parameters.get("result_type", "table")
return self.create_text_message(
text=json.dumps(fields_json, ensure_ascii=False) if result_type == 'json' else fields_table
text=json.dumps(fields_json, ensure_ascii=False) if result_type == "json" else fields_table
)
except httpx.RequestError as e:
return self.create_text_message(f"Failed to get the worksheet information, request error: {e}")
@@ -88,61 +87,65 @@ class GetWorksheetFieldsTool(BuiltinTool):
50: "Text",
51: "Query Record",
}
return field_type_map.get(field_type_id, '')
return field_type_map.get(field_type_id, "")
def get_controls(self, controls: list) -> dict:
fields = []
fields_list = ['|fieldId|fieldName|fieldType|fieldTypeId|description|options|','|'+'---|'*6]
fields_list = ["|fieldId|fieldName|fieldType|fieldTypeId|description|options|", "|" + "---|" * 6]
for control in controls:
if control['type'] in self._get_ignore_types():
if control["type"] in self._get_ignore_types():
continue
field_type_id = control['type']
field_type = self.get_field_type_by_id(control['type'])
field_type_id = control["type"]
field_type = self.get_field_type_by_id(control["type"])
if field_type_id == 30:
source_type = control['sourceControl']['type']
source_type = control["sourceControl"]["type"]
if source_type in self._get_ignore_types():
continue
else:
field_type_id = source_type
field_type = self.get_field_type_by_id(source_type)
field = {
'id': control['controlId'],
'name': control['controlName'],
'type': field_type,
'typeId': field_type_id,
'description': control['remark'].replace('\n', ' ').replace('\t', ' '),
'options': self._extract_options(control),
"id": control["controlId"],
"name": control["controlName"],
"type": field_type,
"typeId": field_type_id,
"description": control["remark"].replace("\n", " ").replace("\t", " "),
"options": self._extract_options(control),
}
fields.append(field)
fields_list.append(f"|{field['id']}|{field['name']}|{field['type']}|{field['typeId']}|{field['description']}|{field['options'] if field['options'] else ''}|")
fields_list.append(
f"|{field['id']}|{field['name']}|{field['type']}|{field['typeId']}|{field['description']}|{field['options'] if field['options'] else ''}|"
)
fields.append({
'id': 'ctime',
'name': 'Created Time',
'type': self.get_field_type_by_id(16),
'typeId': 16,
'description': '',
'options': []
})
fields.append(
{
"id": "ctime",
"name": "Created Time",
"type": self.get_field_type_by_id(16),
"typeId": 16,
"description": "",
"options": [],
}
)
fields_list.append("|ctime|Created Time|Date|16|||")
return fields, '\n'.join(fields_list)
return fields, "\n".join(fields_list)
def _extract_options(self, control: dict) -> list:
options = []
if control['type'] in [9, 10, 11]:
options.extend([{"key": opt['key'], "value": opt['value']} for opt in control.get('options', [])])
elif control['type'] in [28, 36]:
itemnames = control['advancedSetting'].get('itemnames')
if itemnames and itemnames.startswith('[{'):
if control["type"] in [9, 10, 11]:
options.extend([{"key": opt["key"], "value": opt["value"]} for opt in control.get("options", [])])
elif control["type"] in [28, 36]:
itemnames = control["advancedSetting"].get("itemnames")
if itemnames and itemnames.startswith("[{"):
try:
options = json.loads(itemnames)
except json.JSONDecodeError:
pass
elif control['type'] == 30:
source_type = control['sourceControl']['type']
elif control["type"] == 30:
source_type = control["sourceControl"]["type"]
if source_type not in self._get_ignore_types():
options.extend([{"key": opt['key'], "value": opt['value']} for opt in control.get('options', [])])
options.extend([{"key": opt["key"], "value": opt["value"]} for opt in control.get("options", [])])
return options
def _get_ignore_types(self):
return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}
return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}

View File

@@ -8,64 +8,66 @@ from core.tools.tool.builtin_tool import BuiltinTool
class GetWorksheetPivotDataTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
sign = tool_parameters.get('sign', '')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
worksheet_id = tool_parameters.get('worksheet_id', '')
return self.create_text_message("Invalid parameter Sign")
worksheet_id = tool_parameters.get("worksheet_id", "")
if not worksheet_id:
return self.create_text_message('Invalid parameter Worksheet ID')
x_column_fields = tool_parameters.get('x_column_fields', '')
if not x_column_fields or not x_column_fields.startswith('['):
return self.create_text_message('Invalid parameter Column Fields')
y_row_fields = tool_parameters.get('y_row_fields', '')
if y_row_fields and not y_row_fields.strip().startswith('['):
return self.create_text_message('Invalid parameter Row Fields')
return self.create_text_message("Invalid parameter Worksheet ID")
x_column_fields = tool_parameters.get("x_column_fields", "")
if not x_column_fields or not x_column_fields.startswith("["):
return self.create_text_message("Invalid parameter Column Fields")
y_row_fields = tool_parameters.get("y_row_fields", "")
if y_row_fields and not y_row_fields.strip().startswith("["):
return self.create_text_message("Invalid parameter Row Fields")
elif not y_row_fields:
y_row_fields = '[]'
value_fields = tool_parameters.get('value_fields', '')
if not value_fields or not value_fields.strip().startswith('['):
return self.create_text_message('Invalid parameter Value Fields')
host = tool_parameters.get('host', '')
y_row_fields = "[]"
value_fields = tool_parameters.get("value_fields", "")
if not value_fields or not value_fields.strip().startswith("["):
return self.create_text_message("Invalid parameter Value Fields")
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not host.startswith(("http://", "https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url = f"{host}/report/getPivotData"
headers = {'Content-Type': 'application/json'}
headers = {"Content-Type": "application/json"}
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "options": {"showTotal": True}}
try:
x_column_fields = json.loads(x_column_fields)
payload['columns'] = x_column_fields
payload["columns"] = x_column_fields
y_row_fields = json.loads(y_row_fields)
if y_row_fields: payload['rows'] = y_row_fields
if y_row_fields:
payload["rows"] = y_row_fields
value_fields = json.loads(value_fields)
payload['values'] = value_fields
sort_fields = tool_parameters.get('sort_fields', '')
if not sort_fields: sort_fields = '[]'
payload["values"] = value_fields
sort_fields = tool_parameters.get("sort_fields", "")
if not sort_fields:
sort_fields = "[]"
sort_fields = json.loads(sort_fields)
if sort_fields: payload['options']['sort'] = sort_fields
if sort_fields:
payload["options"]["sort"] = sort_fields
res = httpx.post(url, headers=headers, json=payload, timeout=60)
res.raise_for_status()
res_json = res.json()
if res_json.get('status') != 1:
if res_json.get("status") != 1:
return self.create_text_message(f"Failed to get the worksheet pivot data. {res_json['msg']}")
pivot_json = self.generate_pivot_json(res_json['data'])
pivot_table = self.generate_pivot_table(res_json['data'])
result_type = tool_parameters.get('result_type', '')
text = pivot_table if result_type == 'table' else json.dumps(pivot_json, ensure_ascii=False)
pivot_json = self.generate_pivot_json(res_json["data"])
pivot_table = self.generate_pivot_table(res_json["data"])
result_type = tool_parameters.get("result_type", "")
text = pivot_table if result_type == "table" else json.dumps(pivot_json, ensure_ascii=False)
return self.create_text_message(text)
except httpx.RequestError as e:
return self.create_text_message(f"Failed to get the worksheet pivot data, request error: {e}")
@@ -75,27 +77,31 @@ class GetWorksheetPivotDataTool(BuiltinTool):
return self.create_text_message(f"Failed to get the worksheet pivot data, unexpected error: {e}")
def generate_pivot_table(self, data: dict[str, Any]) -> str:
columns = data['metadata']['columns']
rows = data['metadata']['rows']
values = data['metadata']['values']
columns = data["metadata"]["columns"]
rows = data["metadata"]["rows"]
values = data["metadata"]["values"]
rows_data = data['data']
rows_data = data["data"]
header = ([row['displayName'] for row in rows] if rows else []) + [column['displayName'] for column in columns] + [value['displayName'] for value in values]
line = (['---'] * len(rows) if rows else []) + ['---'] * len(columns) + ['--:'] * len(values)
header = (
([row["displayName"] for row in rows] if rows else [])
+ [column["displayName"] for column in columns]
+ [value["displayName"] for value in values]
)
line = (["---"] * len(rows) if rows else []) + ["---"] * len(columns) + ["--:"] * len(values)
table = [header, line]
for row in rows_data:
row_data = [self.replace_pipe(row['rows'][r['controlId']]) for r in rows] if rows else []
row_data.extend([self.replace_pipe(row['columns'][column['controlId']]) for column in columns])
row_data.extend([self.replace_pipe(str(row['values'][value['controlId']])) for value in values])
row_data = [self.replace_pipe(row["rows"][r["controlId"]]) for r in rows] if rows else []
row_data.extend([self.replace_pipe(row["columns"][column["controlId"]]) for column in columns])
row_data.extend([self.replace_pipe(str(row["values"][value["controlId"]])) for value in values])
table.append(row_data)
return '\n'.join([('|'+'|'.join(row) +'|') for row in table])
return "\n".join([("|" + "|".join(row) + "|") for row in table])
def replace_pipe(self, text: str) -> str:
return text.replace('|', '').replace('\n', ' ')
return text.replace("|", "").replace("\n", " ")
def generate_pivot_json(self, data: dict[str, Any]) -> dict:
fields = {
"x-axis": [
@@ -103,13 +109,14 @@ class GetWorksheetPivotDataTool(BuiltinTool):
for column in data["metadata"]["columns"]
],
"y-axis": [
{"fieldId": row["controlId"], "fieldName": row["displayName"]}
for row in data["metadata"]["rows"]
] if data["metadata"]["rows"] else [],
{"fieldId": row["controlId"], "fieldName": row["displayName"]} for row in data["metadata"]["rows"]
]
if data["metadata"]["rows"]
else [],
"values": [
{"fieldId": value["controlId"], "fieldName": value["displayName"]}
for value in data["metadata"]["values"]
]
],
}
# fields = ([
# {"fieldId": row["controlId"], "fieldName": row["displayName"]}
@@ -127,4 +134,4 @@ class GetWorksheetPivotDataTool(BuiltinTool):
row_data.update(row["columns"])
row_data.update(row["values"])
rows.append(row_data)
return {"fields": fields, "rows": rows, "summary": data["metadata"]["totalRow"]}
return {"fields": fields, "rows": rows, "summary": data["metadata"]["totalRow"]}

View File

@@ -9,152 +9,173 @@ from core.tools.tool.builtin_tool import BuiltinTool
class ListWorksheetRecordsTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get('sign', '')
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
return self.create_text_message("Invalid parameter Sign")
worksheet_id = tool_parameters.get('worksheet_id', '')
worksheet_id = tool_parameters.get("worksheet_id", "")
if not worksheet_id:
return self.create_text_message('Invalid parameter Worksheet ID')
return self.create_text_message("Invalid parameter Worksheet ID")
host = tool_parameters.get('host', '')
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not (host.startswith("http://") or host.startswith("https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url_fields = f"{host}/v2/open/worksheet/getWorksheetInfo"
headers = {'Content-Type': 'application/json'}
headers = {"Content-Type": "application/json"}
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id}
field_ids = tool_parameters.get('field_ids', '')
field_ids = tool_parameters.get("field_ids", "")
try:
res = httpx.post(url_fields, headers=headers, json=payload, timeout=30)
res_json = res.json()
if res.is_success:
if res_json['error_code'] != 1:
return self.create_text_message("Failed to get the worksheet information. {}".format(res_json['error_msg']))
if res_json["error_code"] != 1:
return self.create_text_message(
"Failed to get the worksheet information. {}".format(res_json["error_msg"])
)
else:
worksheet_name = res_json['data']['name']
fields, schema, table_header = self.get_schema(res_json['data']['controls'], field_ids)
worksheet_name = res_json["data"]["name"]
fields, schema, table_header = self.get_schema(res_json["data"]["controls"], field_ids)
else:
return self.create_text_message(
f"Failed to get the worksheet information, status code: {res.status_code}, response: {res.text}")
f"Failed to get the worksheet information, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to get the worksheet information, something went wrong: {}".format(e))
return self.create_text_message(
"Failed to get the worksheet information, something went wrong: {}".format(e)
)
if field_ids:
payload['controls'] = [v.strip() for v in field_ids.split(',')] if field_ids else []
filters = tool_parameters.get('filters', '')
payload["controls"] = [v.strip() for v in field_ids.split(",")] if field_ids else []
filters = tool_parameters.get("filters", "")
if filters:
payload['filters'] = json.loads(filters)
sort_id = tool_parameters.get('sort_id', '')
sort_is_asc = tool_parameters.get('sort_is_asc', False)
payload["filters"] = json.loads(filters)
sort_id = tool_parameters.get("sort_id", "")
sort_is_asc = tool_parameters.get("sort_is_asc", False)
if sort_id:
payload['sortId'] = sort_id
payload['isAsc'] = sort_is_asc
limit = tool_parameters.get('limit', 50)
payload['pageSize'] = limit
page_index = tool_parameters.get('page_index', 1)
payload['pageIndex'] = page_index
payload['useControlId'] = True
payload['listType'] = 1
payload["sortId"] = sort_id
payload["isAsc"] = sort_is_asc
limit = tool_parameters.get("limit", 50)
payload["pageSize"] = limit
page_index = tool_parameters.get("page_index", 1)
payload["pageIndex"] = page_index
payload["useControlId"] = True
payload["listType"] = 1
url = f"{host}/v2/open/worksheet/getFilterRows"
try:
res = httpx.post(url, headers=headers, json=payload, timeout=90)
res_json = res.json()
if res.is_success:
if res_json['error_code'] != 1:
return self.create_text_message("Failed to get the records. {}".format(res_json['error_msg']))
if res_json["error_code"] != 1:
return self.create_text_message("Failed to get the records. {}".format(res_json["error_msg"]))
else:
result = {
"fields": fields,
"rows": [],
"total": res_json.get("data", {}).get("total"),
"payload": {key: payload[key] for key in ['worksheetId', 'controls', 'filters', 'sortId', 'isAsc', 'pageSize', 'pageIndex'] if key in payload}
"payload": {
key: payload[key]
for key in [
"worksheetId",
"controls",
"filters",
"sortId",
"isAsc",
"pageSize",
"pageIndex",
]
if key in payload
},
}
rows = res_json.get("data", {}).get("rows", [])
result_type = tool_parameters.get('result_type', '')
if not result_type: result_type = 'table'
if result_type == 'json':
result_type = tool_parameters.get("result_type", "")
if not result_type:
result_type = "table"
if result_type == "json":
for row in rows:
result['rows'].append(self.get_row_field_value(row, schema))
result["rows"].append(self.get_row_field_value(row, schema))
return self.create_text_message(json.dumps(result, ensure_ascii=False))
else:
result_text = f"Found {result['total']} rows in worksheet \"{worksheet_name}\"."
if result['total'] > 0:
if result["total"] > 0:
result_text += f" The following are {result['total'] if result['total'] < limit else limit} pieces of data presented in a table format:\n\n{table_header}"
for row in rows:
result_values = []
for f in fields:
result_values.append(self.handle_value_type(row[f['fieldId']], schema[f['fieldId']]))
result_text += '\n|'+'|'.join(result_values)+'|'
result_values.append(
self.handle_value_type(row[f["fieldId"]], schema[f["fieldId"]])
)
result_text += "\n|" + "|".join(result_values) + "|"
return self.create_text_message(result_text)
else:
return self.create_text_message(
f"Failed to get the records, status code: {res.status_code}, response: {res.text}")
f"Failed to get the records, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to get the records, something went wrong: {}".format(e))
def get_row_field_value(self, row: dict, schema: dict):
row_value = {"rowid": row["rowid"]}
for field in schema:
row_value[field] = self.handle_value_type(row[field], schema[field])
return row_value
def get_schema(self, controls: list, fieldids: str):
allow_fields = {v.strip() for v in fieldids.split(',')} if fieldids else set()
def get_schema(self, controls: list, fieldids: str):
allow_fields = {v.strip() for v in fieldids.split(",")} if fieldids else set()
fields = []
schema = {}
field_names = []
for control in controls:
control_type_id = self.get_real_type_id(control)
if (control_type_id in self._get_ignore_types()) or (allow_fields and not control['controlId'] in allow_fields):
if (control_type_id in self._get_ignore_types()) or (
allow_fields and not control["controlId"] in allow_fields
):
continue
else:
fields.append({'fieldId': control['controlId'], 'fieldName': control['controlName']})
schema[control['controlId']] = {'typeId': control_type_id, 'options': self.set_option(control)}
field_names.append(control['controlName'])
if (not allow_fields or ('ctime' in allow_fields)):
fields.append({'fieldId': 'ctime', 'fieldName': 'Created Time'})
schema['ctime'] = {'typeId': 16, 'options': {}}
fields.append({"fieldId": control["controlId"], "fieldName": control["controlName"]})
schema[control["controlId"]] = {"typeId": control_type_id, "options": self.set_option(control)}
field_names.append(control["controlName"])
if not allow_fields or ("ctime" in allow_fields):
fields.append({"fieldId": "ctime", "fieldName": "Created Time"})
schema["ctime"] = {"typeId": 16, "options": {}}
field_names.append("Created Time")
fields.append({'fieldId':'rowid', 'fieldName': 'Record Row ID'})
schema['rowid'] = {'typeId': 2, 'options': {}}
fields.append({"fieldId": "rowid", "fieldName": "Record Row ID"})
schema["rowid"] = {"typeId": 2, "options": {}}
field_names.append("Record Row ID")
return fields, schema, '|'+'|'.join(field_names)+'|\n|'+'---|'*len(field_names)
return fields, schema, "|" + "|".join(field_names) + "|\n|" + "---|" * len(field_names)
def get_real_type_id(self, control: dict) -> int:
return control['sourceControlType'] if control['type'] == 30 else control['type']
return control["sourceControlType"] if control["type"] == 30 else control["type"]
def set_option(self, control: dict) -> dict:
options = {}
if control.get('options'):
options = {option['key']: option['value'] for option in control['options']}
elif control.get('advancedSetting', {}).get('itemnames'):
if control.get("options"):
options = {option["key"]: option["value"] for option in control["options"]}
elif control.get("advancedSetting", {}).get("itemnames"):
try:
itemnames = json.loads(control['advancedSetting']['itemnames'])
options = {item['key']: item['value'] for item in itemnames}
itemnames = json.loads(control["advancedSetting"]["itemnames"])
options = {item["key"]: item["value"] for item in itemnames}
except json.JSONDecodeError:
pass
return options
def _get_ignore_types(self):
return {14, 21, 22, 34, 42, 43, 45, 47, 49, 10010}
def handle_value_type(self, value, field):
type_id = field.get("typeId")
if type_id == 10:
@@ -167,33 +188,33 @@ class ListWorksheetRecordsTool(BuiltinTool):
value = self.parse_cascade_or_associated(field, value)
elif type_id == 40:
value = self.parse_location(value)
return self.rich_text_to_plain_text(value) if value else ''
return self.rich_text_to_plain_text(value) if value else ""
def process_value(self, value):
if isinstance(value, str):
if value.startswith("[{\"accountId\""):
if value.startswith('[{"accountId"'):
value = json.loads(value)
value = ', '.join([item['fullname'] for item in value])
elif value.startswith("[{\"departmentId\""):
value = ", ".join([item["fullname"] for item in value])
elif value.startswith('[{"departmentId"'):
value = json.loads(value)
value = ''.join([item['departmentName'] for item in value])
elif value.startswith("[{\"organizeId\""):
value = "".join([item["departmentName"] for item in value])
elif value.startswith('[{"organizeId"'):
value = json.loads(value)
value = ''.join([item['organizeName'] for item in value])
elif value.startswith("[{\"file_id\""):
value = ''
elif value == '[]':
value = ''
elif hasattr(value, 'accountId'):
value = value['fullname']
value = "".join([item["organizeName"] for item in value])
elif value.startswith('[{"file_id"'):
value = ""
elif value == "[]":
value = ""
elif hasattr(value, "accountId"):
value = value["fullname"]
return value
def parse_cascade_or_associated(self, field, value):
if (field['typeId'] == 35 and value.startswith('[')) or (field['typeId'] == 29 and value.startswith('[{')):
if (field["typeId"] == 35 and value.startswith("[")) or (field["typeId"] == 29 and value.startswith("[{")):
value = json.loads(value)
value = value[0]['name'] if len(value) > 0 else ''
value = value[0]["name"] if len(value) > 0 else ""
else:
value = ''
value = ""
return value
def parse_location(self, value):
@@ -205,5 +226,5 @@ class ListWorksheetRecordsTool(BuiltinTool):
return value
def rich_text_to_plain_text(self, rich_text):
text = re.sub(r'<[^>]+>', '', rich_text) if '<' in rich_text else rich_text
return text.replace("|", "").replace("\n", " ")
text = re.sub(r"<[^>]+>", "", rich_text) if "<" in rich_text else rich_text
return text.replace("|", "").replace("\n", " ")

View File

@@ -8,75 +8,76 @@ from core.tools.tool.builtin_tool import BuiltinTool
class ListWorksheetsTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
sign = tool_parameters.get('sign', '')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
host = tool_parameters.get('host', '')
return self.create_text_message("Invalid parameter Sign")
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not (host.startswith("http://") or host.startswith("https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url = f"{host}/v1/open/app/get"
result_type = tool_parameters.get('result_type', '')
result_type = tool_parameters.get("result_type", "")
if not result_type:
result_type = 'table'
result_type = "table"
headers = { 'Content-Type': 'application/json' }
params = { "appKey": appkey, "sign": sign, }
headers = {"Content-Type": "application/json"}
params = {
"appKey": appkey,
"sign": sign,
}
try:
res = httpx.get(url, headers=headers, params=params, timeout=30)
res_json = res.json()
if res.is_success:
if res_json['error_code'] != 1:
return self.create_text_message("Failed to access the application. {}".format(res_json['error_msg']))
if res_json["error_code"] != 1:
return self.create_text_message(
"Failed to access the application. {}".format(res_json["error_msg"])
)
else:
if result_type == 'json':
if result_type == "json":
worksheets = []
for section in res_json['data']['sections']:
for section in res_json["data"]["sections"]:
worksheets.extend(self._extract_worksheets(section, result_type))
return self.create_text_message(text=json.dumps(worksheets, ensure_ascii=False))
else:
worksheets = '|worksheetId|worksheetName|description|\n|---|---|---|'
for section in res_json['data']['sections']:
worksheets = "|worksheetId|worksheetName|description|\n|---|---|---|"
for section in res_json["data"]["sections"]:
worksheets += self._extract_worksheets(section, result_type)
return self.create_text_message(worksheets)
else:
return self.create_text_message(
f"Failed to list worksheets, status code: {res.status_code}, response: {res.text}")
f"Failed to list worksheets, status code: {res.status_code}, response: {res.text}"
)
except Exception as e:
return self.create_text_message("Failed to list worksheets, something went wrong: {}".format(e))
def _extract_worksheets(self, section, type):
items = []
tables = ''
for item in section.get('items', []):
if item.get('type') == 0 and (not 'notes' in item or item.get('notes') != 'NO'):
if type == 'json':
filtered_item = {
'id': item['id'],
'name': item['name'],
'notes': item.get('notes', '')
}
tables = ""
for item in section.get("items", []):
if item.get("type") == 0 and (not "notes" in item or item.get("notes") != "NO"):
if type == "json":
filtered_item = {"id": item["id"], "name": item["name"], "notes": item.get("notes", "")}
items.append(filtered_item)
else:
tables += f"\n|{item['id']}|{item['name']}|{item.get('notes', '')}|"
for child_section in section.get('childSections', []):
if type == 'json':
items.extend(self._extract_worksheets(child_section, 'json'))
for child_section in section.get("childSections", []):
if type == "json":
items.extend(self._extract_worksheets(child_section, "json"))
else:
tables += self._extract_worksheets(child_section, 'table')
return items if type == 'json' else tables
tables += self._extract_worksheets(child_section, "table")
return items if type == "json" else tables

View File

@@ -8,44 +8,43 @@ from core.tools.tool.builtin_tool import BuiltinTool
class UpdateWorksheetRecordTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get('appkey', '')
def _invoke(
self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
appkey = tool_parameters.get("appkey", "")
if not appkey:
return self.create_text_message('Invalid parameter App Key')
sign = tool_parameters.get('sign', '')
return self.create_text_message("Invalid parameter App Key")
sign = tool_parameters.get("sign", "")
if not sign:
return self.create_text_message('Invalid parameter Sign')
worksheet_id = tool_parameters.get('worksheet_id', '')
return self.create_text_message("Invalid parameter Sign")
worksheet_id = tool_parameters.get("worksheet_id", "")
if not worksheet_id:
return self.create_text_message('Invalid parameter Worksheet ID')
row_id = tool_parameters.get('row_id', '')
return self.create_text_message("Invalid parameter Worksheet ID")
row_id = tool_parameters.get("row_id", "")
if not row_id:
return self.create_text_message('Invalid parameter Record Row ID')
record_data = tool_parameters.get('record_data', '')
return self.create_text_message("Invalid parameter Record Row ID")
record_data = tool_parameters.get("record_data", "")
if not record_data:
return self.create_text_message('Invalid parameter Record Row Data')
host = tool_parameters.get('host', '')
return self.create_text_message("Invalid parameter Record Row Data")
host = tool_parameters.get("host", "")
if not host:
host = 'https://api.mingdao.com'
host = "https://api.mingdao.com"
elif not host.startswith(("http://", "https://")):
return self.create_text_message('Invalid parameter Host Address')
return self.create_text_message("Invalid parameter Host Address")
else:
host = f"{host[:-1] if host.endswith('/') else host}/api"
url = f"{host}/v2/open/worksheet/editRow"
headers = {'Content-Type': 'application/json'}
headers = {"Content-Type": "application/json"}
payload = {"appKey": appkey, "sign": sign, "worksheetId": worksheet_id, "rowId": row_id}
try:
payload['controls'] = json.loads(record_data)
payload["controls"] = json.loads(record_data)
res = httpx.post(url, headers=headers, json=payload, timeout=60)
res.raise_for_status()
res_json = res.json()
if res_json.get('error_code') != 1:
if res_json.get("error_code") != 1:
return self.create_text_message(f"Failed to update the record. {res_json['error_msg']}")
return self.create_text_message("Record updated successfully.")
except httpx.RequestError as e:

View File

@@ -10,27 +10,29 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class GoogleProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
if credentials['api_key'] is None:
credentials['api_key'] = ''
if credentials["api_key"] is None:
credentials["api_key"] = ""
else:
result = JinaReaderTool().fork_tool_runtime(
runtime={
"credentials": credentials,
}
).invoke(
user_id='',
tool_parameters={
"url": "https://example.com",
},
)[0]
result = (
JinaReaderTool()
.fork_tool_runtime(
runtime={
"credentials": credentials,
}
)
.invoke(
user_id="",
tool_parameters={
"url": "https://example.com",
},
)[0]
)
message = json.loads(result.message)
if message['code'] != 200:
raise ToolProviderCredentialValidationError(message['message'])
if message["code"] != 200:
raise ToolProviderCredentialValidationError(message["message"])
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
def _get_tool_labels(self) -> list[ToolLabelEnum]:
return [
ToolLabelEnum.SEARCH, ToolLabelEnum.PRODUCTIVITY
]
return [ToolLabelEnum.SEARCH, ToolLabelEnum.PRODUCTIVITY]

View File

@@ -9,26 +9,25 @@ from core.tools.tool.builtin_tool import BuiltinTool
class JinaReaderTool(BuiltinTool):
_jina_reader_endpoint = 'https://r.jina.ai/'
_jina_reader_endpoint = "https://r.jina.ai/"
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
invoke tools
"""
url = tool_parameters['url']
url = tool_parameters["url"]
headers = {
'Accept': 'application/json'
}
headers = {"Accept": "application/json"}
if 'api_key' in self.runtime.credentials and self.runtime.credentials.get('api_key'):
headers['Authorization'] = "Bearer " + self.runtime.credentials.get('api_key')
if "api_key" in self.runtime.credentials and self.runtime.credentials.get("api_key"):
headers["Authorization"] = "Bearer " + self.runtime.credentials.get("api_key")
request_params = tool_parameters.get('request_params')
if request_params is not None and request_params != '':
request_params = tool_parameters.get("request_params")
if request_params is not None and request_params != "":
try:
request_params = json.loads(request_params)
if not isinstance(request_params, dict):
@@ -36,40 +35,40 @@ class JinaReaderTool(BuiltinTool):
except (json.JSONDecodeError, ValueError) as e:
raise ValueError(f"Invalid request_params: {e}")
target_selector = tool_parameters.get('target_selector')
if target_selector is not None and target_selector != '':
headers['X-Target-Selector'] = target_selector
target_selector = tool_parameters.get("target_selector")
if target_selector is not None and target_selector != "":
headers["X-Target-Selector"] = target_selector
wait_for_selector = tool_parameters.get('wait_for_selector')
if wait_for_selector is not None and wait_for_selector != '':
headers['X-Wait-For-Selector'] = wait_for_selector
wait_for_selector = tool_parameters.get("wait_for_selector")
if wait_for_selector is not None and wait_for_selector != "":
headers["X-Wait-For-Selector"] = wait_for_selector
if tool_parameters.get('image_caption', False):
headers['X-With-Generated-Alt'] = 'true'
if tool_parameters.get("image_caption", False):
headers["X-With-Generated-Alt"] = "true"
if tool_parameters.get('gather_all_links_at_the_end', False):
headers['X-With-Links-Summary'] = 'true'
if tool_parameters.get("gather_all_links_at_the_end", False):
headers["X-With-Links-Summary"] = "true"
if tool_parameters.get('gather_all_images_at_the_end', False):
headers['X-With-Images-Summary'] = 'true'
if tool_parameters.get("gather_all_images_at_the_end", False):
headers["X-With-Images-Summary"] = "true"
proxy_server = tool_parameters.get('proxy_server')
if proxy_server is not None and proxy_server != '':
headers['X-Proxy-Url'] = proxy_server
proxy_server = tool_parameters.get("proxy_server")
if proxy_server is not None and proxy_server != "":
headers["X-Proxy-Url"] = proxy_server
if tool_parameters.get('no_cache', False):
headers['X-No-Cache'] = 'true'
if tool_parameters.get("no_cache", False):
headers["X-No-Cache"] = "true"
max_retries = tool_parameters.get('max_retries', 3)
max_retries = tool_parameters.get("max_retries", 3)
response = ssrf_proxy.get(
str(URL(self._jina_reader_endpoint + url)),
headers=headers,
params=request_params,
timeout=(10, 60),
max_retries=max_retries
max_retries=max_retries,
)
if tool_parameters.get('summary', False):
if tool_parameters.get("summary", False):
return self.create_text_message(self.summary(user_id, response.text))
return self.create_text_message(response.text)

View File

@@ -8,44 +8,39 @@ from core.tools.tool.builtin_tool import BuiltinTool
class JinaSearchTool(BuiltinTool):
_jina_search_endpoint = 'https://s.jina.ai/'
_jina_search_endpoint = "https://s.jina.ai/"
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
query = tool_parameters['query']
query = tool_parameters["query"]
headers = {
'Accept': 'application/json'
}
headers = {"Accept": "application/json"}
if 'api_key' in self.runtime.credentials and self.runtime.credentials.get('api_key'):
headers['Authorization'] = "Bearer " + self.runtime.credentials.get('api_key')
if "api_key" in self.runtime.credentials and self.runtime.credentials.get("api_key"):
headers["Authorization"] = "Bearer " + self.runtime.credentials.get("api_key")
if tool_parameters.get('image_caption', False):
headers['X-With-Generated-Alt'] = 'true'
if tool_parameters.get("image_caption", False):
headers["X-With-Generated-Alt"] = "true"
if tool_parameters.get('gather_all_links_at_the_end', False):
headers['X-With-Links-Summary'] = 'true'
if tool_parameters.get("gather_all_links_at_the_end", False):
headers["X-With-Links-Summary"] = "true"
if tool_parameters.get('gather_all_images_at_the_end', False):
headers['X-With-Images-Summary'] = 'true'
if tool_parameters.get("gather_all_images_at_the_end", False):
headers["X-With-Images-Summary"] = "true"
proxy_server = tool_parameters.get('proxy_server')
if proxy_server is not None and proxy_server != '':
headers['X-Proxy-Url'] = proxy_server
proxy_server = tool_parameters.get("proxy_server")
if proxy_server is not None and proxy_server != "":
headers["X-Proxy-Url"] = proxy_server
if tool_parameters.get('no_cache', False):
headers['X-No-Cache'] = 'true'
if tool_parameters.get("no_cache", False):
headers["X-No-Cache"] = "true"
max_retries = tool_parameters.get('max_retries', 3)
max_retries = tool_parameters.get("max_retries", 3)
response = ssrf_proxy.get(
str(URL(self._jina_search_endpoint + query)),
headers=headers,
timeout=(10, 60),
max_retries=max_retries
str(URL(self._jina_search_endpoint + query)), headers=headers, timeout=(10, 60), max_retries=max_retries
)
return self.create_text_message(response.text)

View File

@@ -6,33 +6,29 @@ from core.tools.tool.builtin_tool import BuiltinTool
class JinaTokenizerTool(BuiltinTool):
_jina_tokenizer_endpoint = 'https://tokenize.jina.ai/'
_jina_tokenizer_endpoint = "https://tokenize.jina.ai/"
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> ToolInvokeMessage:
content = tool_parameters['content']
body = {
"content": content
}
content = tool_parameters["content"]
body = {"content": content}
headers = {
'Content-Type': 'application/json'
}
headers = {"Content-Type": "application/json"}
if 'api_key' in self.runtime.credentials and self.runtime.credentials.get('api_key'):
headers['Authorization'] = "Bearer " + self.runtime.credentials.get('api_key')
if "api_key" in self.runtime.credentials and self.runtime.credentials.get("api_key"):
headers["Authorization"] = "Bearer " + self.runtime.credentials.get("api_key")
if tool_parameters.get('return_chunks', False):
body['return_chunks'] = True
if tool_parameters.get('return_tokens', False):
body['return_tokens'] = True
if tokenizer := tool_parameters.get('tokenizer'):
body['tokenizer'] = tokenizer
if tool_parameters.get("return_chunks", False):
body["return_chunks"] = True
if tool_parameters.get("return_tokens", False):
body["return_tokens"] = True
if tokenizer := tool_parameters.get("tokenizer"):
body["tokenizer"] = tokenizer
response = ssrf_proxy.post(
self._jina_tokenizer_endpoint,

View File

@@ -8,10 +8,9 @@ from core.tools.provider.builtin_tool_provider import BuiltinToolProviderControl
class JsonExtractProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict[str, Any]) -> None:
try:
JSONParseTool().invoke(user_id='',
tool_parameters={
'content': '{"name": "John", "age": 30, "city": "New York"}',
'json_filter': '$.name'
})
JSONParseTool().invoke(
user_id="",
tool_parameters={"content": '{"name": "John", "age": 30, "city": "New York"}', "json_filter": "$.name"},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
raise ToolProviderCredentialValidationError(str(e))

View File

@@ -8,34 +8,35 @@ from core.tools.tool.builtin_tool import BuiltinTool
class JSONDeleteTool(BuiltinTool):
def _invoke(self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
Invoke the JSON delete tool
"""
# Get content
content = tool_parameters.get('content', '')
content = tool_parameters.get("content", "")
if not content:
return self.create_text_message('Invalid parameter content')
return self.create_text_message("Invalid parameter content")
# Get query
query = tool_parameters.get('query', '')
query = tool_parameters.get("query", "")
if not query:
return self.create_text_message('Invalid parameter query')
return self.create_text_message("Invalid parameter query")
ensure_ascii = tool_parameters.get('ensure_ascii', True)
ensure_ascii = tool_parameters.get("ensure_ascii", True)
try:
result = self._delete(content, query, ensure_ascii)
return self.create_text_message(str(result))
except Exception as e:
return self.create_text_message(f'Failed to delete JSON content: {str(e)}')
return self.create_text_message(f"Failed to delete JSON content: {str(e)}")
def _delete(self, origin_json: str, query: str, ensure_ascii: bool) -> str:
try:
input_data = json.loads(origin_json)
expr = parse('$.' + query.lstrip('$.')) # Ensure query path starts with $
expr = parse("$." + query.lstrip("$.")) # Ensure query path starts with $
matches = expr.find(input_data)

Some files were not shown because too many files have changed in this diff Show More