diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index 4816f52396..517c7c9e73 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -1,4 +1,5 @@ import io +import requests from flask import send_file from flask_login import current_user # type: ignore @@ -587,6 +588,148 @@ class APOToolBuiltinListApi(Resource): ) +def get_step(start_time, end_time): + time_diff = end_time - start_time + + SECOND = 1000000 # microseconds + MINUTE = 60 * SECOND + HOUR = 60 * MINUTE + + step = SECOND # default step is 1 second + + if time_diff <= 15 * MINUTE: + step = 30 * SECOND + elif time_diff <= 30 * MINUTE: + step = 1 * MINUTE + elif time_diff <= 1 * HOUR: + step = 2 * MINUTE + elif time_diff <= 1.5 * HOUR: + step = 3 * MINUTE + elif time_diff <= 3 * HOUR: + step = 6 * MINUTE + elif time_diff <= 6 * HOUR: + step = 12 * MINUTE + elif time_diff <= 12 * HOUR: + step = 24 * MINUTE + elif time_diff <= 15 * HOUR: + step = 30 * MINUTE + elif time_diff <= 30 * HOUR: + step = 1 * HOUR + else: + step = ((time_diff + 30 * SECOND - 1) // (30 * SECOND)) * SECOND + + return step + +def get_url(type): + match type: + case "metric": + return dify_config.APO_BACKEND_URL + "/api/metric/query" + case "alert": + return dify_config.APO_BACKEND_URL + "/api/alerts/descendant/anormal/delta" + case "topology": + return dify_config.APO_BACKEND_URL + "/api/service/relation" + +class APOToolPreviewApi(Resource): + def post(self): + user = current_user + user_id = user.id + tenant_id = user.current_tenant_id + + parser = reqparse.RequestParser() + parser.add_argument("type", type=str, required=True, nullable=False, location="json") + parser.add_argument("title", type=str, required=True, nullable=False, location="json") + parser.add_argument("startTime", type=int, required=True, nullable=True, location="json") + parser.add_argument("endTime", type=int, required=True, nullable=True, location="json") + parser.add_argument("params", type=dict, required=True, nullable=False, location="json") + + args = parser.parse_args() + + res = {} + + match args["type"]: + case "metric": + url = get_url(args["type"]) + json = { + "metricName": args["title"], + "params": args["params"], + "startTime": args["startTime"], + "endTime": args["endTime"], + "step": get_step(args["startTime"], args["endTime"]), + } + resp = requests.post(url, json=json) + raw = resp.json()['result'] + res = { + 'type': 'metric', + 'display': True, + 'unit': raw['unit'], + 'data': { + "timeseries": raw['timeseries'] + } + } + case "topology": + url = get_url(args["type"]) + params = args["params"] + json = { + "service": params['service'], + "endpoint": params['endpoint'], + "entryService": params['service'], + "entryEndpoint": params['endpoint'], + "startTime": args["startTime"], + "endTime": args["endTime"], + "withTopology": True, + "removeClientCall": True, + } + resp = requests.get(url, params=json) + raw = resp.json() + res = { + 'type': 'topology', + 'display': True, + 'data': raw + } + case "alert": + url = get_url(args["type"]) + params = args["params"] + json = { + "service": params['service'], + "endpoint": params['endpoint'], + "startTime": args["startTime"], + "endTime": args["endTime"], + 'anormalTypes': "app,container,infra,network,error,appInstance", + 'deltaStartTime': args["startTime"], + 'deltaEndTime': args["endTime"], + 'step': get_step(args["startTime"], args["endTime"]), + } + resp = requests.post(url, json=json) + raw = resp.json() + res = { + 'type': 'topology', + 'display': True, + 'data': raw + } + case "log": + url = dify_config.APO_BACKEND_URL + "/api/log/fault/pagelist" + params = args["params"] + json = { + 'service': [params['service']], + 'startTime': args["startTime"], + 'endTime': args["endTime"], + 'pageNum': 1, + 'pageSize': 10 + } + resp = requests.post(url, json=json) + raw = resp.json()['list'][0] + + content_url = dify_config.APO_BACKEND_URL + "/api/log/fault/content" + content = requests.post(content_url, json=raw).json() + res = { + 'type': 'log', + 'display': True, + 'data': content, + } + return jsonable_encoder(res) + + + class ToolApiListApi(Resource): @setup_required @login_required @@ -674,6 +817,7 @@ api.add_resource(ToolWorkflowProviderListToolApi, "/workspaces/current/tool-prov api.add_resource(ToolBuiltinListApi, "/workspaces/current/tools/builtin") api.add_resource(APOToolBuiltinListApi, "/workspaces/current/tools/apo") +api.add_resource(APOToolPreviewApi, "/workspaces/current/tools/apo/preview") api.add_resource(ToolApiListApi, "/workspaces/current/tools/api") api.add_resource(ToolWorkflowListApi, "/workspaces/current/tools/workflow") diff --git a/api/core/tools/builtin_tool/providers/apo_analysis/tools/threshold.yaml b/api/core/tools/builtin_tool/providers/apo_analysis/tools/threshold.yaml index c12ddda9a6..33939fc3b8 100644 --- a/api/core/tools/builtin_tool/providers/apo_analysis/tools/threshold.yaml +++ b/api/core/tools/builtin_tool/providers/apo_analysis/tools/threshold.yaml @@ -8,7 +8,7 @@ identity: description: human: en_US: threshold judgement - zh_Hans: 阈值判定 + zh_Hans: 基于固定阈值检测数据,超过阈值认为异常 pt_BR: threshold judgement llm: threshold judgement parameters: diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/alert.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/alert.yaml index 368ba19581..bf8b57a663 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/alert.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/alert.yaml @@ -13,7 +13,7 @@ description: llm: Query the entry service alert events display: type: alert - unit: alert + title: alert parameters: - name: service type: string diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.py b/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.py index 40d2641ca2..9ee9262358 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.py +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.py @@ -35,7 +35,7 @@ class SelectContainerCPUTool(BuiltinTool): resp = requests.post(dify_config.APO_BACKEND_URL + '/api/metric/query', json=params) list = resp.json()['result'] list = json.dumps({ - 'type': 'cpu', + 'type': 'metric', 'display': True, 'unit': list['unit'], 'data': { diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.yaml index 028b98a7b3..ca7497d289 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/container_cpu.yaml @@ -6,8 +6,8 @@ identity: zh_Hans: 查询容器CPU利用率 pt_BR: Query the CPU utilization of the container display: - type: cpu - unit: precent + type: metric + title: 基础设施情况 - 容器CPU - 容器CPU使用率 - Containerd description: human: en_US: Query the CPU utilization of the container diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.py b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.py index ca0e85c687..ff364841fe 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.py +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.py @@ -35,7 +35,7 @@ class SelectContainerRSSTool(BuiltinTool): resp = requests.post(dify_config.APO_BACKEND_URL + '/api/metric/query', json=params) list = resp.json()['result'] list = json.dumps({ - 'type': 'memory', + 'type': 'metirc', 'display': True, 'unit': list['unit'], 'data': { diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.yaml index 33c2724a2e..2d36f471a5 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rss.yaml @@ -12,8 +12,8 @@ description: pt_BR: Query container resident memory usage llm: Query container resident memory usage display: - type: memory - unit: bytes + type: metric + title: 基础设施情况 - 容器内存 - 容器内存使用率 - Containerd parameters: - name: pod type: string diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.py b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.py index 2911fbd4fd..b750d39115 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.py +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.py @@ -35,7 +35,7 @@ class ContainerRTTTool(BuiltinTool): resp = requests.post(dify_config.APO_BACKEND_URL + '/api/metric/query', json=params) list = resp.json()['result'] list = json.dumps({ - 'type': 'memory', + 'type': 'metric', 'display': True, 'unit': list['unit'], 'data': { diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.yaml index 334102baeb..ff19759338 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/container_rtt.yaml @@ -12,8 +12,8 @@ description: pt_BR: Query container rtt llm: Query container rtt display: - type: network - unit: seconds + type: metric + title: 基础设施情况 - 容器网络 - 与下游服务RTT parameters: - name: pod type: string diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/fault_log.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/fault_log.yaml index 04eaeb0fc0..3ecd09cbc1 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/fault_log.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/fault_log.yaml @@ -13,7 +13,7 @@ description: llm: Query service log data display: type: log - unit: log + title: log parameters: - name: service type: string diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/node_info.py b/api/core/tools/builtin_tool/providers/apo_select/tools/node_info.py deleted file mode 100644 index 32b29aad3b..0000000000 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/node_info.py +++ /dev/null @@ -1,88 +0,0 @@ -import json -from collections.abc import Generator -from typing import Any, Optional - -import requests - -from configs import dify_config -from core.tools.builtin_tool.tool import BuiltinTool -from core.tools.entities.tool_entities import ToolInvokeMessage - - -class FaultLogTool(BuiltinTool): - def _invoke( - self, - user_id: str, - tool_parameters: dict[str, Any], - conversation_id: Optional[str] = None, - app_id: Optional[str] = None, - message_id: Optional[str] = None, - ) -> Generator[ToolInvokeMessage, None, None]: - service = tool_parameters.get("service") - endpoint = tool_parameters.get("endpoint") - entryService = tool_parameters.get("entryService") - entryEndpoint = tool_parameters.get("entryEndpoint") - start_time = tool_parameters.get("startTime") - end_time = tool_parameters.get("endTime") - type = tool_parameters.get("type") - - params = { - "anormalTypes": "app,container,infra,network,error,appInstance", - "endTime": end_time, - "endpoint": endpoint, - "entryEndpoint": entryEndpoint, - "entryService": entryService, - "service": service, - "startTime": start_time, - "step": self.get_step(start_time, end_time), - "type": [type], - "withTopology": True, - } - - url = "http://192.168.1.6:13680/api/nodeinfo" - res = {} - with requests.post(url, json=params, stream=True) as response: - # 逐行读取流式响应 - for line in response.iter_lines(): - if line: # 过滤空行 - # 解码字节为字符串 - decoded_line = line.decode('utf-8') - res = json.loads(decoded_line[5:].strip()) - list = json.dumps({ - 'type': 'nodeinfo', - 'display': True, - 'data': res, - }) - yield self.create_text_message(list) - - def get_step(self, start_time, end_time): - time_diff = end_time - start_time - - SECOND = 1000000 # microseconds - MINUTE = 60 * SECOND - HOUR = 60 * MINUTE - - step = SECOND # default step is 1 second - - if time_diff <= 15 * MINUTE: - step = 30 * SECOND - elif time_diff <= 30 * MINUTE: - step = 1 * MINUTE - elif time_diff <= 1 * HOUR: - step = 2 * MINUTE - elif time_diff <= 1.5 * HOUR: - step = 3 * MINUTE - elif time_diff <= 3 * HOUR: - step = 6 * MINUTE - elif time_diff <= 6 * HOUR: - step = 12 * MINUTE - elif time_diff <= 12 * HOUR: - step = 24 * MINUTE - elif time_diff <= 15 * HOUR: - step = 30 * MINUTE - elif time_diff <= 30 * HOUR: - step = 1 * HOUR - else: - step = ((time_diff + 30 * SECOND - 1) // (30 * SECOND)) * SECOND - - return step \ No newline at end of file diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/node_info.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/node_info.yaml deleted file mode 100644 index d9502b5c10..0000000000 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/node_info.yaml +++ /dev/null @@ -1,108 +0,0 @@ -identity: - name: node_info - author: APO - label: - en_US: node_info - zh_Hans: 获取节点信息 - pt_BR: node_info -description: - human: - en_US: A tool for getting fault log. - zh_Hans: 获取节点信息 - pt_BR: A tool for getting fault log. - llm: A tool for getting fault log. -display: - type: info - unit: info -parameters: - - name: entryService - type: string - required: true - label: - en_US: entryService - zh_Hans: 入口服务名 - pt_BR: entryService - human_description: - en_US: Time format in strftime standard. - zh_Hans: 拓扑入口服务名 - pt_BR: Time format in strftime standard. - llm_description: toplogy service name - form: llm - - name: entryEndpoint - type: string - required: true - label: - en_US: entryEndpoint - zh_Hans: 入口服务端点 - pt_BR: entryEndpoint - human_description: - en_US: Time format in strftime standard. - zh_Hans: 拓扑入口服务端点 - pt_BR: Time format in strftime standard. - llm_description: toplogy endpoint - form: llm - - name: service - type: string - required: true - label: - en_US: service - zh_Hans: 服务名 - pt_BR: service - human_description: - en_US: Time format in strftime standard. - zh_Hans: 查询指定服务的名称 - pt_BR: Time format in strftime standard. - llm_description: Time format in strftime standard. - form: llm - - name: endpoint - type: string - required: true - label: - en_US: endpoint - zh_Hans: 服务端点 - pt_BR: endpoint - human_description: - en_US: Time format in strftime standard. - zh_Hans: 指定服务的端点 - pt_BR: Time format in strftime standard. - llm_description: toplogy endpoint - form: llm - - name: startTime - type: number - required: true - label: - en_US: startTime - zh_Hans: startTime - pt_BR: startTime - human_description: - en_US: Data query start time - zh_Hans: 开始时间 (微秒) - pt_BR: Data query start time - llm_description: Data query start time - form: llm - - name: endTime - type: number - required: true - label: - en_US: endTime - zh_Hans: endTime - pt_BR: endTime - human_description: - en_US: Data query end time - zh_Hans: 结束时间 (微秒) - pt_BR: Data query end time - llm_description: Data query start time - form: llm - - name: type - type: string - required: false - label: - en_US: type - zh_Hans: 查询数据类型(log, slow, error) - pt_BR: type - human_description: - en_US: Time format in strftime standard. - zh_Hans: 指定查询数据类型(log, slow, error)。 - pt_BR: Time format in strftime standard. - llm_description: Time format in strftime standard. - form: llm \ No newline at end of file diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.py b/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.py index bb437db254..35e8d8467b 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.py +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.py @@ -18,13 +18,13 @@ class SelectCPUTool(BuiltinTool): app_id: Optional[str] = None, message_id: Optional[str] = None, ) -> Generator[ToolInvokeMessage, None, None]: - node_name = tool_parameters.get("nodeName") + node = tool_parameters.get("node") start_time = tool_parameters.get("startTime") end_time = tool_parameters.get("endTime") params = { 'metricName': '宿主机监控指标 - Quick CPU / Mem / Disk - CPU Busy', 'params': { - "node": node_name, + "node": node, }, 'startTime': start_time, 'endTime': end_time, @@ -33,7 +33,7 @@ class SelectCPUTool(BuiltinTool): resp = requests.post(dify_config.APO_BACKEND_URL + '/api/metric/query', json=params) list = resp.json()['result'] list = json.dumps({ - 'type': 'cpu', + 'type': 'metric', 'display': True, 'unit': list['unit'], 'data': { diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.yaml index 531797b961..ca60ea924d 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/select_cpu.yaml @@ -12,16 +12,16 @@ description: pt_BR: Query the CPU utilization of the host llm: Query the CPU utilization of the host display: - type: cpu - unit: precent + type: metric + title: 宿主机监控指标 - Quick CPU / Mem / Disk - CPU Busy parameters: - - name: nodeName + - name: node type: string required: true label: - en_US: nodeName - zh_Hans: nodeName - pt_BR: nodeName + en_US: node + zh_Hans: node + pt_BR: node human_description: en_US: Specified host name zh_Hans: 指定的主机名 diff --git a/api/core/tools/builtin_tool/providers/apo_select/tools/topology.yaml b/api/core/tools/builtin_tool/providers/apo_select/tools/topology.yaml index b370819d13..8bb635e018 100644 --- a/api/core/tools/builtin_tool/providers/apo_select/tools/topology.yaml +++ b/api/core/tools/builtin_tool/providers/apo_select/tools/topology.yaml @@ -13,7 +13,7 @@ description: llm: Query the entry service topology display: type: topology - unit: topology + title: topology parameters: - name: service type: string diff --git a/api/core/tools/entities/tool_entities.py b/api/core/tools/entities/tool_entities.py index b72621f871..b4e5312399 100644 --- a/api/core/tools/entities/tool_entities.py +++ b/api/core/tools/entities/tool_entities.py @@ -320,7 +320,7 @@ class ToolDescription(BaseModel): class APODisPlay(BaseModel): type: str = Field(..., description="The type of the apo data display") - unit: str = Field(..., description="The unit of the apo data display") + title: str = Field(..., description="The title of the apo data display") class ToolEntity(BaseModel): identity: ToolIdentity