From f91d6b0657086fcaade5d501632c35fe8468b509 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 18 Jun 2024 19:23:41 +0800 Subject: [PATCH 001/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后台接口定义 --- .../validation/ValidationMessages.properties | 2 + .../ValidationMessages_en.properties | 3 + .../ValidationMessages_en_US.properties | 3 + .../ValidationMessages_zh.properties | 2 + .../ValidationMessages_zh_CN.properties | 2 + .../job/analysis/api/web/WebAIResource.java | 155 ++++++++++++++++++ .../model/web/req/AIAnalyzeErrorReq.java | 59 +++++++ .../model/web/req/AICheckScriptReq.java | 59 +++++++ .../model/web/req/AIGeneralChatReq.java | 47 ++++++ .../job/analysis/model/web/resp/AIAnswer.java | 65 ++++++++ .../analysis/model/web/resp/AIChatRecord.java | 50 ++++++ .../analysis/model/web/resp/UserInput.java | 52 ++++++ .../api/web/impl/WebAIResourceImpl.java | 137 ++++++++++++++++ 13 files changed, 636 insertions(+) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/UserInput.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties index fb9347513d..6161d3ccb8 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties @@ -108,3 +108,5 @@ validation.constraints.queryAgentInfoHostIds_tooMany.message=单次查询主机 validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行对象数量范围必须在 {min}-{max} 之间 +## AI对话 +validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties index 057449613f..96f54b62ba 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties @@ -106,3 +106,6 @@ validation.constraints.InvalidWhiteIpScope.message=ScopeList cannot be empty ## Others validation.constraints.queryAgentInfoHostIds_tooMany.message=HostIds size can not be over 5000 validation.constraints.queryExecuteObjects_outOfRange.message=Query execute objects size must be between {min} and {max} + +## AI Chat +validation.constraints.AIGeneralChatContent_empty.message=Chat message cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties index 057449613f..96f54b62ba 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties @@ -106,3 +106,6 @@ validation.constraints.InvalidWhiteIpScope.message=ScopeList cannot be empty ## Others validation.constraints.queryAgentInfoHostIds_tooMany.message=HostIds size can not be over 5000 validation.constraints.queryExecuteObjects_outOfRange.message=Query execute objects size must be between {min} and {max} + +## AI Chat +validation.constraints.AIGeneralChatContent_empty.message=Chat message cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties index fb9347513d..6161d3ccb8 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties @@ -108,3 +108,5 @@ validation.constraints.queryAgentInfoHostIds_tooMany.message=单次查询主机 validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行对象数量范围必须在 {min}-{max} 之间 +## AI对话 +validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties index fb9347513d..6161d3ccb8 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties @@ -108,3 +108,5 @@ validation.constraints.queryAgentInfoHostIds_tooMany.message=单次查询主机 validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行对象数量范围必须在 {min}-{max} 之间 +## AI对话 +validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java new file mode 100644 index 0000000000..6b5b21558d --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -0,0 +1,155 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.api.web; + +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; +import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; +import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.common.annotation.WebAPI; +import com.tencent.bk.job.common.model.Response; +import com.tencent.bk.job.common.model.dto.AppResourceScope; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import springfox.documentation.annotations.ApiIgnore; + +import java.util.List; +import java.util.Map; + +@Api(tags = {"job-analysis:web:AI"}) +@RequestMapping("/web/ai/scope/{scopeType}/{scopeId}") +@RestController +@WebAPI +public interface WebAIResource { + + @ApiOperation(value = "获取AI相关的配置参数,取值:analyzeErrorLogMaxLength表示分析报错信息时支持的最大日志长度,单位为字符", + produces = "application/json") + @GetMapping("/config") + Response> getAIConfig( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId + ); + + @ApiOperation(value = "获取最近的AI对话记录历史(按产生时间倒序排列)", produces = "application/json") + @GetMapping("/latestChatHistoryList") + Response> getLatestChatHistoryList( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId, + @ApiParam(value = "start", name = "对话记录起始位置,不传默认为0") + @RequestParam(value = "start", defaultValue = "0") + Integer start, + @ApiParam(value = "length", name = "需要获取的对话记录条数,最大200条,不传默认20条") + @RequestParam(value = "length", defaultValue = "20") + Integer length + ); + + @ApiOperation(value = "通用聊天接口", produces = "application/json") + @PostMapping("/general/chat") + Response generalChat( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId, + @ApiParam(value = "AI通用聊天参数", required = true) + @RequestBody AIGeneralChatReq req + ); + + @ApiOperation(value = "检查脚本", produces = "application/json") + @PostMapping("/checkScript") + Response checkScript( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId, + @ApiParam(value = "AI检查脚本参数", required = true) + @RequestBody AICheckScriptReq req + ); + + @ApiOperation(value = "分析报错信息", produces = "application/json") + @PostMapping("/analyzeError") + Response analyzeError( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId, + @ApiParam(value = "AI分析报错信息参数", required = true) + @RequestBody AIAnalyzeErrorReq req + ); +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java new file mode 100644 index 0000000000..548b5a836b --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req; + +import com.tencent.bk.job.common.validation.CheckEnum; +import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("AI分析报错信息请求体") +@Data +public class AIAnalyzeErrorReq { + + /** + * 脚本类型 + */ + @ApiModelProperty(value = "脚本类型,1:shell,2:bat,3:perl,4:python,5:powershell,6:SQL") + @NotNull(message = "{validation.constraints.ScriptType_empty.message}") + @CheckEnum(enumClass = ScriptTypeEnum.class, enumMethod = "isValid", + message = "{validation.constraints.ScriptType_illegal.message}") + private Integer type; + + /** + * 脚本内容 + */ + @ApiModelProperty(value = "脚本内容,BASE64编码") + @NotEmpty(message = "{validation.constraints.ScriptContent_empty.message}") + private String content; +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java new file mode 100644 index 0000000000..588121d0f9 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req; + +import com.tencent.bk.job.common.validation.CheckEnum; +import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("AI检查脚本请求体") +@Data +public class AICheckScriptReq { + + /** + * 脚本类型 + */ + @ApiModelProperty(value = "脚本类型,1:shell,2:bat,3:perl,4:python,5:powershell,6:SQL") + @NotNull(message = "{validation.constraints.ScriptType_empty.message}") + @CheckEnum(enumClass = ScriptTypeEnum.class, enumMethod = "isValid", + message = "{validation.constraints.ScriptType_illegal.message}") + private Integer type; + + /** + * 脚本内容 + */ + @ApiModelProperty(value = "脚本内容,BASE64编码") + @NotEmpty(message = "{validation.constraints.ScriptContent_empty.message}") + private String content; +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java new file mode 100644 index 0000000000..dd4b1a921d --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("AI通用对话请求体") +@Data +public class AIGeneralChatReq { + + /** + * 用户输入内容 + */ + @ApiModelProperty(value = "用户输入内容") + @NotEmpty(message = "{validation.constraints.AIGeneralChatContent_empty.message}") + private String content; +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java new file mode 100644 index 0000000000..d3b5405d6d --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.resp; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.tencent.bk.job.common.util.json.LongTimestampSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("AI回答内容") +@Data +public class AIAnswer { + + /** + * 错误码 + */ + @ApiModelProperty(value = "错误码") + private Integer errorCode; + + /** + * 错误信息 + */ + @ApiModelProperty(value = "错误信息") + private String errorMessage; + + /** + * 内容 + */ + @ApiModelProperty(value = "内容") + private String content; + + /** + * 回答时间 + */ + @ApiModelProperty("回答时间") + @JsonSerialize(using = LongTimestampSerializer.class) + private Long time; +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java new file mode 100644 index 0000000000..c4ebb99143 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("一条AI对话记录") +@Data +public class AIChatRecord { + + /** + * 用户输入 + */ + @ApiModelProperty(value = "用户输入") + private UserInput userInput; + + /** + * AI回答 + */ + @ApiModelProperty("AI回答") + private AIAnswer aiAnswer; +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/UserInput.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/UserInput.java new file mode 100644 index 0000000000..6fc870e7da --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/UserInput.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.resp; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.tencent.bk.job.common.util.json.LongTimestampSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("用户提问") +@Data +public class UserInput { + /** + * 内容 + */ + @ApiModelProperty(value = "内容") + private String content; + + /** + * 提问时间 + */ + @ApiModelProperty("提问时间") + @JsonSerialize(using = LongTimestampSerializer.class) + private Long time; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java new file mode 100644 index 0000000000..80b28368a6 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -0,0 +1,137 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.api.web.impl; + +import com.tencent.bk.job.analysis.api.web.WebAIResource; +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; +import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; +import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.model.web.resp.UserInput; +import com.tencent.bk.job.common.model.Response; +import com.tencent.bk.job.common.model.dto.AppResourceScope; +import com.tencent.bk.job.common.util.ThreadUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController("jobAnalysisWebAIResource") +@Slf4j +public class WebAIResourceImpl implements WebAIResource { + @Override + public Response> getAIConfig(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId) { + Map map = new HashMap<>(); + map.put("analyzeErrorLogMaxLength", 5 * 1024 * 1024L); + return Response.buildSuccessResp(map); + } + + @Override + public Response> getLatestChatHistoryList(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + Integer start, + Integer length) { + List aiChatRecordList = new ArrayList<>(); + AIChatRecord record = new AIChatRecord(); + UserInput userInput = new UserInput(); + userInput.setContent("你是谁"); + userInput.setTime(System.currentTimeMillis()); + record.setUserInput(userInput); + ThreadUtils.sleep(1000); + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("我是AI小鲸"); + aiAnswer.setErrorCode(0); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + record.setAiAnswer(aiAnswer); + aiChatRecordList.add(record); + + AIChatRecord record2 = new AIChatRecord(); + UserInput userInput2 = new UserInput(); + userInput2.setContent("Hello"); + userInput2.setTime(System.currentTimeMillis()); + record2.setUserInput(userInput2); + ThreadUtils.sleep(1000); + AIAnswer aiAnswer2 = new AIAnswer(); + aiAnswer2.setContent("World"); + aiAnswer2.setErrorCode(0); + aiAnswer2.setErrorMessage(null); + aiAnswer2.setTime(System.currentTimeMillis()); + record2.setAiAnswer(aiAnswer2); + aiChatRecordList.add(record2); + return Response.buildSuccessResp(aiChatRecordList); + } + + @Override + public Response generalChat(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIGeneralChatReq req) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("我是AI小鲸"); + aiAnswer.setErrorCode(0); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + return Response.buildSuccessResp(aiAnswer); + } + + @Override + public Response checkScript(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AICheckScriptReq req) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("没什么问题"); + aiAnswer.setErrorCode(0); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + return Response.buildSuccessResp(aiAnswer); + } + + @Override + public Response analyzeError(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIAnalyzeErrorReq req) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("让我想想..."); + aiAnswer.setErrorCode(0); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + return Response.buildSuccessResp(aiAnswer); + } +} From 77dfcdeb18af0acc5177ac1cd070e0467bf9ff37 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 18 Jun 2024 20:31:49 +0800 Subject: [PATCH 002/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后台接口定义 --- .../validation/ValidationMessages.properties | 1 + .../ValidationMessages_en.properties | 1 + .../ValidationMessages_en_US.properties | 1 + .../ValidationMessages_zh.properties | 1 + .../ValidationMessages_zh_CN.properties | 1 + .../model/web/req/AIAnalyzeErrorReq.java | 40 +++++++++++-------- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties index 6161d3ccb8..d14f71a1ca 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties @@ -110,3 +110,4 @@ validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行 ## AI对话 validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 +validation.constraints.AIAnalyzeErrorContent_empty.message=报错信息内容不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties index 96f54b62ba..4c6eaec803 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties @@ -109,3 +109,4 @@ validation.constraints.queryExecuteObjects_outOfRange.message=Query execute obje ## AI Chat validation.constraints.AIGeneralChatContent_empty.message=Chat message cannot be empty +validation.constraints.AIAnalyzeErrorContent_empty.message=Error content cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties index 96f54b62ba..4c6eaec803 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties @@ -109,3 +109,4 @@ validation.constraints.queryExecuteObjects_outOfRange.message=Query execute obje ## AI Chat validation.constraints.AIGeneralChatContent_empty.message=Chat message cannot be empty +validation.constraints.AIAnalyzeErrorContent_empty.message=Error content cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties index 6161d3ccb8..d14f71a1ca 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties @@ -110,3 +110,4 @@ validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行 ## AI对话 validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 +validation.constraints.AIAnalyzeErrorContent_empty.message=报错信息内容不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties index 6161d3ccb8..d14f71a1ca 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties @@ -110,3 +110,4 @@ validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行 ## AI对话 validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 +validation.constraints.AIAnalyzeErrorContent_empty.message=报错信息内容不能为空 diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java index 548b5a836b..63444206dd 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java @@ -24,8 +24,6 @@ package com.tencent.bk.job.analysis.model.web.req; -import com.tencent.bk.job.common.validation.CheckEnum; -import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -33,7 +31,6 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; @AllArgsConstructor @NoArgsConstructor @@ -41,19 +38,28 @@ @Data public class AIAnalyzeErrorReq { - /** - * 脚本类型 - */ - @ApiModelProperty(value = "脚本类型,1:shell,2:bat,3:perl,4:python,5:powershell,6:SQL") - @NotNull(message = "{validation.constraints.ScriptType_empty.message}") - @CheckEnum(enumClass = ScriptTypeEnum.class, enumMethod = "isValid", - message = "{validation.constraints.ScriptType_illegal.message}") - private Integer type; - - /** - * 脚本内容 - */ - @ApiModelProperty(value = "脚本内容,BASE64编码") - @NotEmpty(message = "{validation.constraints.ScriptContent_empty.message}") + @ApiModelProperty(value = "任务ID") + private Long taskInstanceId; + + @ApiModelProperty(value = "步骤ID") + private Long stepInstanceId; + + @ApiModelProperty(value = "执行次数") + private Integer executeCount; + + @ApiModelProperty(value = "滚动批次,非滚动步骤不需要传入") + private Integer batch; + + @ApiModelProperty(value = "执行对象类型") + private Integer executeObjectType; + + @ApiModelProperty(value = "执行对象资源 ID") + private Long executeObjectResourceId; + + @ApiModelProperty(value = "文件任务上传下载标识,0-上传,1-下载") + private Integer mode; + + @ApiModelProperty(value = "报错信息内容") + @NotEmpty(message = "{validation.constraints.AIAnalyzeErrorContent_empty.message}") private String content; } From 2c56101484a0dd39f711e9aee39f0935114fd468 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 18 Jun 2024 22:06:18 +0800 Subject: [PATCH 003/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后台数据表结构设计 --- ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql new file mode 100644 index 0000000000..3ed3dbaa8d --- /dev/null +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -0,0 +1,48 @@ +SET NAMES utf8mb4; +USE job_analysis; + +-- ------------------------ +-- 创建AI提示符模板数据表 +-- ------------------------ +CREATE TABLE IF NOT EXISTS `ai_prompt_template` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `code` varchar(255) NOT NULL COMMENT '模板代码,用于唯一标识模板', + `locale` varchar(16) NOT NULL DEFAULT 'zh_CN' COMMENT '语言', + `name` varchar(255) NOT NULL COMMENT '模板名称', + `content` TEXT NOT NULL COMMENT '模板内容', + `description` TEXT COMMENT '对模板的描述', + `creator` varchar(128) NOT NULL COMMENT '创建者', + `last_modify_user` varchar(128) NULL DEFAULT NULL COMMENT '更新者', + `create_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '创建时间', + `last_modify_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '更新时间', + `row_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `row_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `idx_code_locale` (`code`,`locale`) USING BTREE +) ENGINE = InnoDB +CHARACTER SET = utf8mb4; + +-- ------------------------ +-- 创建AI对话记录表 +-- ------------------------ +CREATE TABLE IF NOT EXISTS `ai_chat_history` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `username` varchar(64) NOT NULL COMMENT '用户名', + `user_input` TEXT NOT NULL COMMENT '用户输入内容', + `prompt_template_id` int(10) NULL DEFAULT NULL COMMENT '使用的提示符模板ID', + `ai_input` TEXT NOT NULL COMMENT '提交给AI的输入内容', + `ai_answer` TEXT NOT NULL COMMENT 'AI回答的内容', + `error_code` varchar(128) NOT NULL COMMENT 'AI回答失败时的错误码', + `error_message` varchar(512) NOT NULL COMMENT 'AI回答失败时的错误信息', + `start_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '开始时间', + `answer_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT 'AI回答完成时间', + `total_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '总耗时', + `is_deleted` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否已删除:0表示未删除,1表示已删除', + `row_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `row_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_username_startTime` (`username`,`start_time`) USING BTREE, + INDEX `idx_start_time` (`start_time`) USING BTREE +) ENGINE = InnoDB +CHARACTER SET = utf8mb4; + From 13eff15dd16b3dc142a0d50cb3a9d3869e352e96 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 20 Jun 2024 20:07:32 +0800 Subject: [PATCH 004/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现AIDev接口调用SDK --- src/backend/commons/ai-dev-sdk/build.gradle | 41 ++++ .../bk/job/common/aidev/IBkAIDevClient.java | 31 +++ .../aidev/config/AIDevAutoConfiguration.java | 49 +++++ .../aidev/config/BkAIDevProperties.java | 41 ++++ .../config/CustomPaasLoginProperties.java | 43 +++++ .../aidev/exception/BkAIDevException.java | 49 +++++ .../job/common/aidev/impl/BkAIDevClient.java | 176 ++++++++++++++++++ .../aidev/model/common/AIDevMessage.java | 43 +++++ .../common/aidev/model/req/AIDevInput.java | 41 ++++ .../job/common/aidev/model/req/AIDevReq.java | 38 ++++ .../aidev/model/req/AIDevReqConfig.java | 46 +++++ .../common/aidev/model/req/AIDevReqData.java | 38 ++++ .../common/aidev/model/resp/AIDevChoice.java | 43 +++++ .../common/aidev/model/resp/AIDevData.java | 38 ++++ .../common/aidev/model/resp/AIDevResp.java | 41 ++++ .../common/aidev/model/resp/AIDevResult.java | 44 +++++ .../main/resources/META-INF/spring.factories | 2 + .../i18n/exception/message.properties | 3 + .../i18n/exception/message_en.properties | 3 + .../i18n/exception/message_en_US.properties | 3 + .../i18n/exception/message_zh.properties | 3 + .../i18n/exception/message_zh_CN.properties | 3 + .../bk/job/common/constant/ErrorCode.java | 4 + .../job/common/metrics/CommonMetricNames.java | 9 + .../job/common/util/http/BaseHttpHelper.java | 2 +- .../esb/config/BkApiGatewayProperties.java | 2 + .../common/esb/model/BkApiAuthorization.java | 30 ++- .../bk/job/common/esb/sdk/BkApiClient.java | 53 ++++++ .../paas/login/StandardLoginClient.java | 2 +- src/backend/settings.gradle | 1 + 30 files changed, 916 insertions(+), 6 deletions(-) create mode 100644 src/backend/commons/ai-dev-sdk/build.gradle create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/BkAIDevProperties.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkAIDevException.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevInput.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReq.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqConfig.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqData.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevChoice.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevData.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResp.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResult.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/resources/META-INF/spring.factories diff --git a/src/backend/commons/ai-dev-sdk/build.gradle b/src/backend/commons/ai-dev-sdk/build.gradle new file mode 100644 index 0000000000..907fb546e8 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/build.gradle @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +dependencies { + api project(':commons:common') + api project(':commons:esb-sdk') + api project(':commons:common-i18n') + implementation 'com.fasterxml.jackson.core:jackson-core' + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation 'com.fasterxml.jackson.core:jackson-annotations' + implementation 'org.apache.commons:commons-lang3' + implementation "net.sf.dozer:dozer" + implementation 'io.micrometer:micrometer-registry-prometheus' + implementation 'org.apache.commons:commons-collections4' + implementation 'org.apache.httpcomponents:httpclient' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.junit.jupiter:junit-jupiter' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java new file mode 100644 index 0000000000..c588067e46 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev; + +public interface IBkAIDevClient { + + String getHunYuanAnswer(String token, String userInput); + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java new file mode 100644 index 0000000000..21ce38b786 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.config; + +import com.tencent.bk.job.common.esb.config.AppProperties; +import com.tencent.bk.job.common.esb.config.BkApiGatewayProperties; +import com.tencent.bk.job.common.aidev.impl.BkAIDevClient; +import io.micrometer.core.instrument.MeterRegistry; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties({BkAIDevProperties.class, CustomPaasLoginProperties.class}) +public class AIDevAutoConfiguration { + + @Bean + public BkAIDevClient bkAIDevClient(MeterRegistry meterRegistry, + AppProperties appProperties, + CustomPaasLoginProperties customPaasLoginProperties, + BkApiGatewayProperties bkApiGatewayProperties) { + return new BkAIDevClient(meterRegistry, appProperties, customPaasLoginProperties, bkApiGatewayProperties); + } + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/BkAIDevProperties.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/BkAIDevProperties.java new file mode 100644 index 0000000000..fb5acf4b05 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/BkAIDevProperties.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * AI开发中心配置 + */ +@ConfigurationProperties(prefix = "bk-ai-dev") +@Getter +@Setter +public class BkAIDevProperties { + + private boolean enabled = true; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java new file mode 100644 index 0000000000..d7789a7253 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.config; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "paas.login.custom") +@Getter +@Setter +@ToString +public class CustomPaasLoginProperties { + + /** + * 是否使用第三方登录系统 + */ + private boolean enabled = false; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkAIDevException.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkAIDevException.java new file mode 100644 index 0000000000..e4f1264657 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkAIDevException.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.exception; + +import com.tencent.bk.job.common.exception.InternalException; +import lombok.Getter; +import lombok.ToString; + +/** + * 调用AIDev接口异常 + */ +@Getter +@ToString +public class BkAIDevException extends InternalException { + + public BkAIDevException(Throwable cause, Integer errorCode, Object[] errorParams) { + super(cause, errorCode, errorParams); + } + + public BkAIDevException(String message, Integer errorCode) { + super(message, errorCode); + } + + public BkAIDevException(String message, Throwable cause, Integer errorCode) { + super(message, cause, errorCode); + } +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java new file mode 100644 index 0000000000..c3d037815d --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java @@ -0,0 +1,176 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.impl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.tencent.bk.job.common.aidev.IBkAIDevClient; +import com.tencent.bk.job.common.aidev.config.CustomPaasLoginProperties; +import com.tencent.bk.job.common.aidev.exception.BkAIDevException; +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; +import com.tencent.bk.job.common.aidev.model.req.AIDevInput; +import com.tencent.bk.job.common.aidev.model.req.AIDevReq; +import com.tencent.bk.job.common.aidev.model.req.AIDevReqConfig; +import com.tencent.bk.job.common.aidev.model.req.AIDevReqData; +import com.tencent.bk.job.common.aidev.model.resp.AIDevChoice; +import com.tencent.bk.job.common.aidev.model.resp.AIDevData; +import com.tencent.bk.job.common.aidev.model.resp.AIDevResp; +import com.tencent.bk.job.common.aidev.model.resp.AIDevResult; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.constant.HttpMethodEnum; +import com.tencent.bk.job.common.esb.config.AppProperties; +import com.tencent.bk.job.common.esb.config.BkApiGatewayProperties; +import com.tencent.bk.job.common.esb.model.BkApiAuthorization; +import com.tencent.bk.job.common.esb.model.OpenApiRequestInfo; +import com.tencent.bk.job.common.esb.sdk.BkApiClient; +import com.tencent.bk.job.common.metrics.CommonMetricNames; +import com.tencent.bk.job.common.util.http.HttpHelperFactory; +import com.tencent.bk.job.common.util.http.HttpMetricUtil; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("SameParameterValue") +public class BkAIDevClient extends BkApiClient implements IBkAIDevClient { + + private static final String URI_GET_HUN_YUAN_ANSWER = + "/aidev/intelligence/raw_service/model-self_host-hunyuan-ChatCompletion/execute/"; + + private final AppProperties appProperties; + private final CustomPaasLoginProperties customPaasLoginProperties; + + public BkAIDevClient(MeterRegistry meterRegistry, + AppProperties appProperties, + CustomPaasLoginProperties customPaasLoginProperties, + BkApiGatewayProperties bkApiGatewayProperties) { + super( + meterRegistry, + CommonMetricNames.BK_NOTICE_API, + getBkAIDevUrlSafely(bkApiGatewayProperties), + HttpHelperFactory.getDefaultHttpHelper() + ); + this.appProperties = appProperties; + this.customPaasLoginProperties = customPaasLoginProperties; + } + + private static String getBkAIDevUrlSafely(BkApiGatewayProperties bkApiGatewayProperties) { + if (bkApiGatewayProperties == null || bkApiGatewayProperties.getBkAIDev() == null) { + return null; + } + return bkApiGatewayProperties.getBkAIDev().getUrl(); + } + + @Override + public String getHunYuanAnswer(String token, String userInput) { + AIDevReq req = buildAIDevReq(userInput); + BkApiAuthorization authorization = buildAuthorization(token); + AIDevResp> resp = requestBkAIDevApi( + HttpMethodEnum.POST, + URI_GET_HUN_YUAN_ANSWER, + req, + authorization, + new TypeReference>>() { + }, + true + ); + List aiDevDataList = resp.getData(); + AIDevResult aiDevResult = aiDevDataList.get(0).getResult(); + List aiDevChoiceList = aiDevResult.getChoices(); + AIDevMessage aiDevMessage = aiDevChoiceList.get(0).getMessage(); + return aiDevMessage.getContent(); + } + + private AIDevReq buildAIDevReq(String userInput) { + AIDevReq req = new AIDevReq(); + req.setConfig(AIDevReqConfig.hunyuanConfig()); + AIDevReqData data = new AIDevReqData(); + List inputs = new ArrayList<>(); + AIDevInput input = new AIDevInput(); + List messages = new ArrayList<>(); + AIDevMessage message = new AIDevMessage(); + message.setRole(AIDevMessage.ROLE_USER); + message.setContent(userInput); + messages.add(message); + input.setMessages(messages); + inputs.add(input); + data.setInputs(inputs); + req.setData(data); + return req; + } + + private BkApiAuthorization buildAuthorization(String token) { + if (customPaasLoginProperties.isEnabled()) { + return BkApiAuthorization.bkTicketUserAuthorization( + appProperties.getCode(), + appProperties.getSecret(), + token + ); + } else { + return BkApiAuthorization.bkTokenUserAuthorization( + appProperties.getCode(), + appProperties.getSecret(), + token + ); + } + } + + /** + * 通过ApiGateway请求AIDev API的统一入口,监控数据埋点位置 + * + * @param method Http方法 + * @param uri 请求地址 + * @param reqBody 请求体内容 + * @param typeReference 指定了返回值类型的TypeReference对象 + * @param 泛型:返回值类型 + * @return 返回值类型实例 + */ + private R requestBkAIDevApi(HttpMethodEnum method, + String uri, + AIDevReq reqBody, + BkApiAuthorization authorization, + TypeReference typeReference, + Boolean idempotent) { + try { + HttpMetricUtil.setHttpMetricName(CommonMetricNames.BK_AI_DEV_API_HTTP); + HttpMetricUtil.addTagForCurrentMetric(Tag.of("api_name", uri)); + OpenApiRequestInfo requestInfo = OpenApiRequestInfo + .builder() + .method(method) + .uri(uri) + .body(reqBody) + .authorization(authorization) + .setIdempotent(idempotent) + .build(); + return requestApiAndWrapResponse(requestInfo, typeReference, + HttpHelperFactory.getLongRetryableHttpHelper()); + } catch (Exception e) { + throw new BkAIDevException(e, ErrorCode.BK_AI_DEV_API_DATA_ERROR, null); + } finally { + HttpMetricUtil.clearHttpMetric(); + } + } + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java new file mode 100644 index 0000000000..05ab9220fb --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.common; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@NoArgsConstructor +@Data +public class AIDevMessage { + + public static String ROLE_USER = "user"; + public static String ROLE_ASSISTANT = "assistant"; + + private String role; + + private String content; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevInput.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevInput.java new file mode 100644 index 0000000000..3d42f253d9 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevInput.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.req; + +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +@NoArgsConstructor +@Data +public class AIDevInput { + + private List messages; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReq.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReq.java new file mode 100644 index 0000000000..507d0c2f9f --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReq.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.req; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class AIDevReq { + + private AIDevReqData data; + + private AIDevReqConfig config; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqConfig.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqConfig.java new file mode 100644 index 0000000000..5d256b1fa2 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqConfig.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class AIDevReqConfig { + + public static String ACTION_CREATE = "create"; + public static String MODEL_HUN_YUAN = "hunyuan"; + + private String action; + + private String model; + + public static AIDevReqConfig hunyuanConfig() { + return new AIDevReqConfig(ACTION_CREATE, MODEL_HUN_YUAN); + } +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqData.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqData.java new file mode 100644 index 0000000000..7bd3853ecb --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/req/AIDevReqData.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.req; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@NoArgsConstructor +@Data +public class AIDevReqData { + + private List inputs; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevChoice.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevChoice.java new file mode 100644 index 0000000000..39e4f00469 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevChoice.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.resp; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@NoArgsConstructor +@Data +public class AIDevChoice { + + @JsonProperty("finish_reason") + private String finishReason; + + private AIDevMessage message; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevData.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevData.java new file mode 100644 index 0000000000..b087c48959 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevData.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.resp; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@NoArgsConstructor +@Data +public class AIDevData { + + private AIDevResult result; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResp.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResp.java new file mode 100644 index 0000000000..e4c260a2cd --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResp.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.resp; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class AIDevResp { + + private String code; + + private String name; + + private Boolean result; + + private T data; +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResult.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResult.java new file mode 100644 index 0000000000..ef3c10acd3 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/resp/AIDevResult.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.model.resp; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +@NoArgsConstructor +@Data +public class AIDevResult { + + private String id; + private String model; + private String version; + private Long created; + private List choices; + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/resources/META-INF/spring.factories b/src/backend/commons/ai-dev-sdk/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000..c52dab5923 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.tencent.bk.job.common.aidev.config.AIDevAutoConfiguration diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties index a7e5b8a0d3..3877f15edb 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties @@ -81,6 +81,9 @@ 1217001=消息通知中心API不存在:{0} 1217002=消息通知中心接口数据异常 +#AIDev异常 +1218001=AIDev平台接口数据异常 + ##业务错误-通用 1241001=请求参数缺失 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties index 7481df32e8..76f0482b6b 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties @@ -81,6 +81,9 @@ 1217001=BK-Notice API not found:{0} 1217002=BK-Notice API data unexpected +#AIDev异常 +1218001=BK-AIDev API data unexpected + ## Business error - common 1241001=Missing request parameters 1241002=Invalid request parameters diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties index 7481df32e8..76f0482b6b 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties @@ -81,6 +81,9 @@ 1217001=BK-Notice API not found:{0} 1217002=BK-Notice API data unexpected +#AIDev异常 +1218001=BK-AIDev API data unexpected + ## Business error - common 1241001=Missing request parameters 1241002=Invalid request parameters diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties index a7e5b8a0d3..3877f15edb 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties @@ -81,6 +81,9 @@ 1217001=消息通知中心API不存在:{0} 1217002=消息通知中心接口数据异常 +#AIDev异常 +1218001=AIDev平台接口数据异常 + ##业务错误-通用 1241001=请求参数缺失 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties index 931e85f554..3c5026bbba 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties @@ -81,6 +81,9 @@ 1217001=消息通知中心API不存在:{0} 1217002=消息通知中心接口数据异常 +#AIDev异常 +1218001=AIDev平台接口数据异常 + ##业务错误-通用 1241001=请求参数缺失 diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java index 7850f1aaa6..2b62b4b24b 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java @@ -88,6 +88,10 @@ public class ErrorCode { // 消息通知中心接口数据异常 public static final int BK_NOTICE_API_DATA_ERROR = 1217002; + // AIDev平台异常 + // AIDev接口数据异常 + public static final int BK_AI_DEV_API_DATA_ERROR = 1218001; + // ======== 系统错误-权限错误 ==================// // 用户({0})权限不足,请前往权限中心确认并申请补充后重试 public static final int PERMISSION_DENIED = 1238001; diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/metrics/CommonMetricNames.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/metrics/CommonMetricNames.java index 0fba47495d..b1671adb40 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/metrics/CommonMetricNames.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/metrics/CommonMetricNames.java @@ -85,6 +85,15 @@ public class CommonMetricNames { */ public static final String BK_NOTICE_API = "job.client.bknotice.api"; + /** + * 仅统计调用AIDev后台 API的HTTP请求过程 + */ + public static final String BK_AI_DEV_API_HTTP = "job.client.bkAIDev.api.http"; + /** + * 统计调用AIDev后台 API整个过程,含反序列化 + */ + public static final String BK_AI_DEV_API = "job.client.bkAIDev.api"; + /** * 被调用ESB API diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/util/http/BaseHttpHelper.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/util/http/BaseHttpHelper.java index dc898edb3f..1627804907 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/util/http/BaseHttpHelper.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/util/http/BaseHttpHelper.java @@ -195,7 +195,7 @@ private HttpResponse execute(HttpRequestBase httpClientRequest, if (httpStatusCode >= HttpStatus.SC_BAD_REQUEST) { String reasonPhrase = httpResponse.getStatusLine().getReasonPhrase(); log.warn( - "Request fail, method: {}, url={}, httpStatusCode={}, errorReason={}, body={},", + "Request fail, method: {}, url={}, httpStatusCode={}, errorReason={}, body={}", httpClientRequest.getMethod(), httpClientRequest.getURI().getPath(), httpStatusCode, diff --git a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java index d0664d2907..9834eafbb0 100644 --- a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java +++ b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java @@ -39,6 +39,8 @@ public class BkApiGatewayProperties { private ApiGwConfig bkNotice; + private ApiGwConfig bkAIDev; + private ApiGwConfig cmdb; @Getter diff --git a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/model/BkApiAuthorization.java b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/model/BkApiAuthorization.java index 8e94ce405c..b4b69b20e9 100644 --- a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/model/BkApiAuthorization.java +++ b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/model/BkApiAuthorization.java @@ -59,12 +59,21 @@ public class BkApiAuthorization { private String accessToken; /** - * 用户登录态 token,用于认证用户;登录蓝鲸,对应 Cookies 中 bk_token 字段的值;提供 bk_token 时,不需要再提供 bk_username + * 用户登录态 token,用于认证用户;登录蓝鲸,对应 Cookies 中 bk_token 字段的值; + * 与bk_ticket互斥,提供 bk_token 时,不需要再提供 bk_ticket/bk_username */ @SkipLogFields @JsonProperty("bk_token") private String bkToken; + /** + * 某些环境下的用户登录态 token,用于认证用户;登录蓝鲸,对应 Cookies 中 bk_ticket 字段的值; + * 与bk_token互斥,提供 bk_ticket 时,不需要再提供 bk_token/bk_username + */ + @SkipLogFields + @JsonProperty("bk_ticket") + private String bkTicket; + /** * 当前用户用户名;仅用于应用免用户认证的场景中,用于指定当前用户 */ @@ -101,12 +110,12 @@ public static BkApiAuthorization appAuthorization(String appCode, String appSecr } /** - * 用户认证 - 根据 token + * 用户认证 - 根据 bk_token * - * @param bkToken 用户登录 token + * @param bkToken 用户登录 bk_token * @return 认证信息 */ - public static BkApiAuthorization userAuthorization(String appCode, String appSecret, String bkToken) { + public static BkApiAuthorization bkTokenUserAuthorization(String appCode, String appSecret, String bkToken) { BkApiAuthorization authorization = new BkApiAuthorization(); authorization.setAppCode(appCode); authorization.setAppSecret(appSecret); @@ -114,5 +123,18 @@ public static BkApiAuthorization userAuthorization(String appCode, String appSec return authorization; } + /** + * 用户认证 - 根据 bk_ticket + * + * @param bkTicket 用户登录 bk_ticket + * @return 认证信息 + */ + public static BkApiAuthorization bkTicketUserAuthorization(String appCode, String appSecret, String bkTicket) { + BkApiAuthorization authorization = new BkApiAuthorization(); + authorization.setAppCode(appCode); + authorization.setAppSecret(appSecret); + authorization.setBkTicket(bkTicket); + return authorization; + } } diff --git a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/BkApiClient.java b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/BkApiClient.java index f9d90810f3..afc332d1bc 100644 --- a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/BkApiClient.java +++ b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/sdk/BkApiClient.java @@ -171,6 +171,59 @@ public EsbResp doRequest(OpenApiRequestInfo requestInfo, } } + public R requestApiAndWrapResponse(OpenApiRequestInfo requestInfo, + TypeReference typeReference, + HttpHelper httpHelper) { + if (log.isInfoEnabled()) { + log.info("[AbstractBkApiClient] Request|method={}|uri={}|reqStr={}", + requestInfo.getMethod().name(), requestInfo.getUri(), + requestInfo.getBody() != null ? JsonUtils.toJsonWithoutSkippedFields(requestInfo.getBody()) : null); + } + String uri = requestInfo.getUri(); + String respStr = null; + String status = EsbMetricTags.VALUE_STATUS_OK; + HttpMethodEnum httpMethod = requestInfo.getMethod(); + long start = System.currentTimeMillis(); + String bkApiRequestId = null; + boolean success = true; + try { + HttpResponse response = requestApi(httpHelper, requestInfo); + bkApiRequestId = extractBkApiRequestId(response); + respStr = response.getEntity(); + if (StringUtils.isBlank(respStr)) { + String errorMsg = "[AbstractBkApiClient] " + httpMethod.name() + " " + + uri + ", error: " + "Response is blank"; + log.warn( + "[AbstractBkApiClient] fail: Response is blank| requestId={}|method={}|uri={}", + bkApiRequestId, + httpMethod.name(), + uri + ); + status = EsbMetricTags.VALUE_STATUS_ERROR; + throw new InternalException(errorMsg, ErrorCode.API_ERROR); + } + return jsonMapper.fromJson(respStr, typeReference); + } catch (Throwable e) { + success = false; + String errorMsg = "Fail to request api|method=" + httpMethod.name() + + "|uri=" + uri; + log.error(errorMsg, e); + status = EsbMetricTags.VALUE_STATUS_ERROR; + throw new InternalException("Fail to request bk api", e, ErrorCode.API_ERROR); + } finally { + long cost = System.currentTimeMillis() - start; + if (meterRegistry != null) { + meterRegistry.timer(metricName, buildMetricTags(uri, status)) + .record(cost, TimeUnit.MILLISECONDS); + } + if (log.isInfoEnabled()) { + log.info("[AbstractBkApiClient] Response|requestId={}|method={}|uri={}|success={}" + + "|costTime={}|resp={}", + bkApiRequestId, httpMethod.name(), requestInfo.getUri(), success, cost, respStr); + } + } + } + private EsbResp requestApiAndWrapResponse(OpenApiRequestInfo requestInfo, BkApiContext apiContext, TypeReference> typeReference, diff --git a/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/StandardLoginClient.java b/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/StandardLoginClient.java index ef6a41f21a..eda0287a18 100644 --- a/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/StandardLoginClient.java +++ b/src/backend/commons/paas-sdk/src/main/java/com/tencent/bk/job/common/paas/login/StandardLoginClient.java @@ -84,7 +84,7 @@ public BkUserDTO getUserInfoByToken(String bkToken) { .method(HttpMethodEnum.GET) .uri(API_GET_USER_INFO) .addQueryParam("bk_token", bkToken) - .authorization(BkApiAuthorization.userAuthorization( + .authorization(BkApiAuthorization.bkTokenUserAuthorization( appProperties.getCode(), appProperties.getSecret(), bkToken)) .build(), new TypeReference>() { diff --git a/src/backend/settings.gradle b/src/backend/settings.gradle index c52347ce3e..e0bef2a337 100644 --- a/src/backend/settings.gradle +++ b/src/backend/settings.gradle @@ -31,6 +31,7 @@ include 'commons:cmdb-sdk' include 'commons:paas-sdk' include 'commons:gse-sdk' include 'commons:notice-sdk' +include 'commons:ai-dev-sdk' include 'commons:common-iam' include 'commons:common-jwt' include 'commons:common-utils' From 7d8a5b5ba4ab8729fb13d9ee9a4748d96c09071c Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 20 Jun 2024 21:41:47 +0800 Subject: [PATCH 005/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接口初步对接 --- .../job/analysis/api/web/WebAIResource.java | 7 +++ .../job/analysis/model/web/resp/AIAnswer.java | 9 ++++ .../service-job-analysis/build.gradle | 1 + .../api/web/impl/WebAIResourceImpl.java | 20 ++++++-- .../bk/job/analysis/service/ai/AIService.java | 31 ++++++++++++ .../service/ai/impl/AIServiceImpl.java | 49 +++++++++++++++++++ 6 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 6b5b21558d..0cfd040a59 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -35,6 +35,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -102,6 +103,12 @@ Response generalChat( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, + @ApiParam("bk_ticket,从Cookie获取") + @CookieValue(value = "bk_ticket") + String bkTicket, + @ApiParam("bk_token,从Cookie获取") + @CookieValue(value = "bk_token") + String bkToken, @ApiIgnore @RequestAttribute(value = "appResourceScope") AppResourceScope appResourceScope, diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java index d3b5405d6d..b8c217bb21 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java @@ -31,6 +31,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.checkerframework.checker.units.qual.A; @AllArgsConstructor @NoArgsConstructor @@ -62,4 +63,12 @@ public class AIAnswer { @ApiModelProperty("回答时间") @JsonSerialize(using = LongTimestampSerializer.class) private Long time; + + public static AIAnswer successAnswer(String content) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setErrorCode(0); + aiAnswer.setTime(System.currentTimeMillis()); + aiAnswer.setContent(content); + return aiAnswer; + } } diff --git a/src/backend/job-analysis/service-job-analysis/build.gradle b/src/backend/job-analysis/service-job-analysis/build.gradle index 04c4b14bbc..6bf212e044 100644 --- a/src/backend/job-analysis/service-job-analysis/build.gradle +++ b/src/backend/job-analysis/service-job-analysis/build.gradle @@ -37,6 +37,7 @@ dependencies { api project(":commons:paas-sdk") api project(":commons:esb-sdk") api project(":commons:gse-sdk") + api project(":commons:ai-dev-sdk") implementation "org.springframework.boot:spring-boot-starter-web" implementation "org.springframework.boot:spring-boot-starter-jdbc" implementation "org.springframework.boot:spring-boot-starter-jooq" diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 80b28368a6..7f1c445096 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -31,10 +31,14 @@ import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.UserInput; +import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; import com.tencent.bk.job.common.util.ThreadUtils; +import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; @@ -45,6 +49,14 @@ @RestController("jobAnalysisWebAIResource") @Slf4j public class WebAIResourceImpl implements WebAIResource { + + private final AIService aiService; + + @Autowired + public WebAIResourceImpl(AIService aiService) { + this.aiService = aiService; + } + @Override public Response> getAIConfig(String username, AppResourceScope appResourceScope, @@ -95,15 +107,13 @@ public Response> getLatestChatHistoryList(String username, @Override public Response generalChat(String username, + String bkTicket, + String bkToken, AppResourceScope appResourceScope, String scopeType, String scopeId, AIGeneralChatReq req) { - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("我是AI小鲸"); - aiAnswer.setErrorCode(0); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); + AIAnswer aiAnswer = aiService.getAIAnswer(bkTicket, req.getContent()); return Response.buildSuccessResp(aiAnswer); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java new file mode 100644 index 0000000000..1df57d2652 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; + +public interface AIService { + AIAnswer getAIAnswer(String token, String userInput); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java new file mode 100644 index 0000000000..c447b20623 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.AIService; +import com.tencent.bk.job.common.aidev.IBkAIDevClient; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class AIServiceImpl implements AIService { + + private final IBkAIDevClient bkAIDevClient; + + @Autowired + public AIServiceImpl(IBkAIDevClient bkAIDevClient) { + this.bkAIDevClient = bkAIDevClient; + } + + @Override + public AIAnswer getAIAnswer(String token, String userInput) { + return AIAnswer.successAnswer(bkAIDevClient.getHunYuanAnswer(token, userInput)); + } +} From ccca18316e62c71f547683e0f675873ab7a3db37 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 24 Jun 2024 10:37:01 +0800 Subject: [PATCH 006/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对接AI接口,实现通用聊天与聊天历史记录接口 --- .../config/CustomPaasLoginProperties.java | 5 + .../bk/job/common/mysql/dao}/BaseDAOImpl.java | 22 ++- .../common/mysql/dao}/RecordDTOConverter.java | 2 +- .../common/mysql}/util/JooqDataTypeUtil.java | 9 +- .../job/analysis/api/web/WebAIResource.java | 7 - .../job/analysis/model/web/resp/AIAnswer.java | 4 +- .../service-job-analysis/build.gradle | 1 + .../api/web/impl/WebAIResourceImpl.java | 38 +++-- .../bk/job/analysis/dao/AIChatHistoryDAO.java | 49 ++++++ .../job/analysis/dao/AIPromptTemplateDAO.java | 31 ++++ .../dao/impl/AIChatHistoryDAOImpl.java | 150 ++++++++++++++++++ .../dao/impl/AIPromptTemplateDAOImpl.java | 79 +++++++++ .../analysis/model/dto/AIChatHistoryDTO.java | 128 +++++++++++++++ .../model/dto/AIPromptTemplateDTO.java | 93 +++++++++++ .../service/ai/AIChatHistoryService.java | 50 ++++++ .../bk/job/analysis/service/ai/AIService.java | 11 +- .../job/analysis/service/ai/ChatService.java | 52 ++++++ .../ai/impl/AIChatHistoryServiceImpl.java | 56 +++++++ .../service/ai/impl/AIServiceImpl.java | 8 +- .../service/ai/impl/ChatServiceImpl.java | 85 ++++++++++ .../service/login/LoginTokenService.java | 29 ++++ .../login/impl/LoginTokenServiceImpl.java | 99 ++++++++++++ .../impl/FileSourceBatchTaskDAOImpl.java | 1 + .../filesource/impl/FileSourceDAOImpl.java | 1 + .../impl/FileSourceTaskDAOImpl.java | 1 + .../dao/filesource/impl/FileTaskDAOImpl.java | 1 + .../manage/common/util/DbRecordMapper.java | 1 + .../impl/CustomScriptTemplateDAOImpl.java | 2 +- .../impl/DangerousRuleDAOImpl.java | 2 +- .../job/manage/dao/impl/AccountDAOImpl.java | 2 +- .../dao/impl/ApplicationHostDAOImpl.java | 2 +- .../job/manage/dao/impl/HostTopoDAOImpl.java | 2 +- .../impl/ScriptCitedTaskTemplateDAOImpl.java | 2 +- .../dao/impl/ScriptRelateTaskPlanDAOImpl.java | 2 +- .../plan/impl/TaskPlanFileInfoDAOImpl.java | 2 +- .../plan/impl/TaskPlanFileStepDAOImpl.java | 2 +- .../plan/impl/TaskPlanScriptStepDAOImpl.java | 2 +- .../impl/TaskTemplateFileInfoDAOImpl.java | 2 +- .../impl/TaskTemplateFileStepDAOImpl.java | 2 +- .../impl/TaskTemplateScriptStepDAOImpl.java | 2 +- .../impl/TaskTemplateVariableDAOImpl.java | 2 +- .../whiteip/impl/WhiteIPRecordDAOImpl.java | 2 +- ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 4 +- 43 files changed, 1001 insertions(+), 46 deletions(-) rename src/backend/{job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl => commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao}/BaseDAOImpl.java (79%) rename src/backend/{job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl => commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao}/RecordDTOConverter.java (96%) rename src/backend/{job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common => commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql}/util/JooqDataTypeUtil.java (93%) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIPromptTemplateDAO.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/LoginTokenService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/impl/LoginTokenServiceImpl.java diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java index d7789a7253..a3ab258b7d 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/CustomPaasLoginProperties.java @@ -40,4 +40,9 @@ public class CustomPaasLoginProperties { */ private boolean enabled = false; + /** + * 固定Token值,不提供则从Cookie中获取 + */ + private String token; + } diff --git a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/BaseDAOImpl.java b/src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao/BaseDAOImpl.java similarity index 79% rename from src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/BaseDAOImpl.java rename to src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao/BaseDAOImpl.java index 0b05caf595..d70a6117eb 100644 --- a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/BaseDAOImpl.java +++ b/src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao/BaseDAOImpl.java @@ -22,12 +22,13 @@ * IN THE SOFTWARE. */ -package com.tencent.bk.job.file_gateway.dao.filesource.impl; +package com.tencent.bk.job.common.mysql.dao; import lombok.extern.slf4j.Slf4j; import org.jooq.Record; import org.jooq.Result; import org.jooq.ResultQuery; +import org.jooq.SelectConditionStep; import org.jooq.SelectLimitStep; import org.jooq.conf.ParamType; import org.slf4j.helpers.MessageFormatter; @@ -73,4 +74,23 @@ public List listPage(SelectLimi return records.map(converter::convert); } } + + public DTOClazz fetchOne(SelectConditionStep query, + RecordDTOConverter converter) { + String sql = ""; + try { + sql = query.getSQL(ParamType.INLINED); + log.debug("SQL={}", sql); + RecordClazz record = query.fetchOne(); + return record == null ? null : converter.convert(record); + } catch (Exception e) { + String msg = MessageFormatter.format( + "error SQL={}", + sql + ).getMessage(); + log.error(msg, e); + throw e; + } + } + } diff --git a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/RecordDTOConverter.java b/src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao/RecordDTOConverter.java similarity index 96% rename from src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/RecordDTOConverter.java rename to src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao/RecordDTOConverter.java index 1c1f171b98..1be75a7147 100644 --- a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/RecordDTOConverter.java +++ b/src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/dao/RecordDTOConverter.java @@ -22,7 +22,7 @@ * IN THE SOFTWARE. */ -package com.tencent.bk.job.file_gateway.dao.filesource.impl; +package com.tencent.bk.job.common.mysql.dao; public interface RecordDTOConverter { DTOClazz convert(RecordClazz record); diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/JooqDataTypeUtil.java b/src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/util/JooqDataTypeUtil.java similarity index 93% rename from src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/JooqDataTypeUtil.java rename to src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/util/JooqDataTypeUtil.java index f821eb3edf..8e7d20b64c 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/JooqDataTypeUtil.java +++ b/src/backend/commons/common-mysql/src/main/java/com/tencent/bk/job/common/mysql/util/JooqDataTypeUtil.java @@ -22,7 +22,7 @@ * IN THE SOFTWARE. */ -package com.tencent.bk.job.manage.common.util; +package com.tencent.bk.job.common.mysql.util; import org.jooq.types.UByte; import org.jooq.types.UInteger; @@ -36,6 +36,13 @@ public static ULong buildULong(Long value) { return ULong.valueOf(value); } + public static Long buildLong(ULong value) { + if (value == null) { + return null; + } + return value.longValue(); + } + public static UInteger buildUInteger(Integer value) { if (value == null) { return null; diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 0cfd040a59..6b5b21558d 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -35,7 +35,6 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -103,12 +102,6 @@ Response generalChat( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, - @ApiParam("bk_ticket,从Cookie获取") - @CookieValue(value = "bk_ticket") - String bkTicket, - @ApiParam("bk_token,从Cookie获取") - @CookieValue(value = "bk_token") - String bkToken, @ApiIgnore @RequestAttribute(value = "appResourceScope") AppResourceScope appResourceScope, diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java index b8c217bb21..f6ed0bc275 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java @@ -43,7 +43,7 @@ public class AIAnswer { * 错误码 */ @ApiModelProperty(value = "错误码") - private Integer errorCode; + private String errorCode; /** * 错误信息 @@ -66,7 +66,7 @@ public class AIAnswer { public static AIAnswer successAnswer(String content) { AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setErrorCode(0); + aiAnswer.setErrorCode("0"); aiAnswer.setTime(System.currentTimeMillis()); aiAnswer.setContent(content); return aiAnswer; diff --git a/src/backend/job-analysis/service-job-analysis/build.gradle b/src/backend/job-analysis/service-job-analysis/build.gradle index 6bf212e044..38bf0992bc 100644 --- a/src/backend/job-analysis/service-job-analysis/build.gradle +++ b/src/backend/job-analysis/service-job-analysis/build.gradle @@ -33,6 +33,7 @@ dependencies { api project(":commons:common-iam") api project(":commons:common-security") api project(":commons:common-redis") + api project(":commons:common-mysql") api project(":commons:cmdb-sdk") api project(":commons:paas-sdk") api project(":commons:esb-sdk") diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 7f1c445096..642aef4a4e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -25,36 +25,36 @@ package com.tencent.bk.job.analysis.api.web.impl; import com.tencent.bk.job.analysis.api.web.WebAIResource; +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.UserInput; -import com.tencent.bk.job.analysis.service.ai.AIService; +import com.tencent.bk.job.analysis.service.ai.ChatService; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; import com.tencent.bk.job.common.util.ThreadUtils; -import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @RestController("jobAnalysisWebAIResource") @Slf4j public class WebAIResourceImpl implements WebAIResource { - private final AIService aiService; + private final ChatService chatService; @Autowired - public WebAIResourceImpl(AIService aiService) { - this.aiService = aiService; + public WebAIResourceImpl(ChatService chatService) { + this.chatService = chatService; } @Override @@ -74,6 +74,18 @@ public Response> getLatestChatHistoryList(String username, String scopeId, Integer start, Integer length) { + List chatRecordList = chatService.getLatestChatHistoryList(username, start, length); + return Response.buildSuccessResp( + chatRecordList.stream().map(AIChatHistoryDTO::toAIChatRecord).collect(Collectors.toList()) + ); + } + + public List mockLatestChatHistoryList(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + Integer start, + Integer length) { List aiChatRecordList = new ArrayList<>(); AIChatRecord record = new AIChatRecord(); UserInput userInput = new UserInput(); @@ -83,7 +95,7 @@ public Response> getLatestChatHistoryList(String username, ThreadUtils.sleep(1000); AIAnswer aiAnswer = new AIAnswer(); aiAnswer.setContent("我是AI小鲸"); - aiAnswer.setErrorCode(0); + aiAnswer.setErrorCode("0"); aiAnswer.setErrorMessage(null); aiAnswer.setTime(System.currentTimeMillis()); record.setAiAnswer(aiAnswer); @@ -97,23 +109,21 @@ public Response> getLatestChatHistoryList(String username, ThreadUtils.sleep(1000); AIAnswer aiAnswer2 = new AIAnswer(); aiAnswer2.setContent("World"); - aiAnswer2.setErrorCode(0); + aiAnswer2.setErrorCode("0"); aiAnswer2.setErrorMessage(null); aiAnswer2.setTime(System.currentTimeMillis()); record2.setAiAnswer(aiAnswer2); aiChatRecordList.add(record2); - return Response.buildSuccessResp(aiChatRecordList); + return aiChatRecordList; } @Override public Response generalChat(String username, - String bkTicket, - String bkToken, AppResourceScope appResourceScope, String scopeType, String scopeId, AIGeneralChatReq req) { - AIAnswer aiAnswer = aiService.getAIAnswer(bkTicket, req.getContent()); + AIAnswer aiAnswer = chatService.chatWithAI(username, req.getContent()); return Response.buildSuccessResp(aiAnswer); } @@ -125,7 +135,7 @@ public Response checkScript(String username, AICheckScriptReq req) { AIAnswer aiAnswer = new AIAnswer(); aiAnswer.setContent("没什么问题"); - aiAnswer.setErrorCode(0); + aiAnswer.setErrorCode("0"); aiAnswer.setErrorMessage(null); aiAnswer.setTime(System.currentTimeMillis()); return Response.buildSuccessResp(aiAnswer); @@ -139,7 +149,7 @@ public Response analyzeError(String username, AIAnalyzeErrorReq req) { AIAnswer aiAnswer = new AIAnswer(); aiAnswer.setContent("让我想想..."); - aiAnswer.setErrorCode(0); + aiAnswer.setErrorCode("0"); aiAnswer.setErrorMessage(null); aiAnswer.setTime(System.currentTimeMillis()); return Response.buildSuccessResp(aiAnswer); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java new file mode 100644 index 0000000000..bb09b47c2c --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.dao; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; + +import java.util.List; + +public interface AIChatHistoryDAO { + /** + * 插入聊天记录 + * + * @param aiChatHistoryDTO AI聊天记录 + * @return 插入记录的id + */ + Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO); + + /** + * 获取最近的聊天记录列表 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近的聊天记录列表 + */ + List getLatestChatHistoryList(String username, Integer start, Integer length); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIPromptTemplateDAO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIPromptTemplateDAO.java new file mode 100644 index 0000000000..ffe088b061 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIPromptTemplateDAO.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.dao; + +import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; + +public interface AIPromptTemplateDAO { + AIPromptTemplateDTO getAIPromptTemplate(String code, String locale); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java new file mode 100644 index 0000000000..2be7381d0b --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -0,0 +1,150 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.dao.impl; + +import com.tencent.bk.job.analysis.dao.AIChatHistoryDAO; +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.tables.AiChatHistory; +import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.jooq.Condition; +import org.jooq.DSLContext; +import org.jooq.Record; +import org.jooq.conf.ParamType; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * 仿照AnalysisTaskDAOImpl实现一个AIChatHistoryDAOImpl + */ +@Slf4j +@Repository +public class AIChatHistoryDAOImpl extends BaseDAOImpl implements AIChatHistoryDAO { + + private static final AiChatHistory defaultTable = AiChatHistory.AI_CHAT_HISTORY; + + private final DSLContext dslContext; + + public AIChatHistoryDAOImpl(@Qualifier("job-analysis-dsl-context") DSLContext dslContext) { + this.dslContext = dslContext; + } + + /** + * 插入AI对话历史记录 + * + * @param aiChatHistoryDTO AI对话历史记录 + * @return 记录ID + */ + @SuppressWarnings("DataFlowIssue") + @Override + public Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { + val query = dslContext.insertInto(defaultTable, + defaultTable.USERNAME, + defaultTable.USER_INPUT, + defaultTable.PROMPT_TEMPLATE_ID, + defaultTable.AI_INPUT, + defaultTable.AI_ANSWER, + defaultTable.ERROR_CODE, + defaultTable.ERROR_MESSAGE, + defaultTable.START_TIME, + defaultTable.ANSWER_TIME, + defaultTable.TOTAL_TIME, + defaultTable.IS_DELETED + ) + .values( + aiChatHistoryDTO.getUsername(), + aiChatHistoryDTO.getUserInput(), + aiChatHistoryDTO.getPromptTemplateId(), + aiChatHistoryDTO.getAiInput(), + aiChatHistoryDTO.getAiAnswer(), + aiChatHistoryDTO.getErrorCode(), + aiChatHistoryDTO.getErrorMessage(), + JooqDataTypeUtil.buildULong(aiChatHistoryDTO.getStartTime()), + JooqDataTypeUtil.buildULong(aiChatHistoryDTO.getAnswerTime()), + JooqDataTypeUtil.buildULong(aiChatHistoryDTO.getTotalTime()), + JooqDataTypeUtil.buildUByte(aiChatHistoryDTO.getIsDeleted() ? 1 : 0) + ) + .returning(defaultTable.ID); + val sql = query.getSQL(ParamType.INLINED); + try { + return query.fetchOne().getId(); + } catch (Exception e) { + log.error(sql); + throw e; + } + } + + @Override + public List getLatestChatHistoryList(String username, Integer start, Integer length) { + Collection conditions = new ArrayList<>(); + conditions.add(defaultTable.USERNAME.eq(username)); + conditions.add(defaultTable.IS_DELETED.eq(JooqDataTypeUtil.buildUByte(0))); + return listByConditions(conditions, start, length); + } + + private List listByConditions(Collection conditions, + Integer start, + Integer length) { + val query = dslContext.select( + defaultTable.ID, + defaultTable.USERNAME, + defaultTable.USER_INPUT, + defaultTable.PROMPT_TEMPLATE_ID, + defaultTable.AI_INPUT, + defaultTable.AI_ANSWER, + defaultTable.ERROR_CODE, + defaultTable.ERROR_MESSAGE, + defaultTable.START_TIME, + defaultTable.ANSWER_TIME, + defaultTable.TOTAL_TIME + ) + .from(defaultTable) + .where(conditions) + .orderBy(defaultTable.START_TIME.desc()); + return listPage(query, start, length, this::convertRecordToDto); + } + + private AIChatHistoryDTO convertRecordToDto(Record record) { + AIChatHistoryDTO aiChatHistoryDTO = new AIChatHistoryDTO(); + aiChatHistoryDTO.setId(record.get(defaultTable.ID)); + aiChatHistoryDTO.setUsername(record.get(defaultTable.USERNAME)); + aiChatHistoryDTO.setUserInput(record.get(defaultTable.USER_INPUT)); + aiChatHistoryDTO.setPromptTemplateId(record.get(defaultTable.PROMPT_TEMPLATE_ID)); + aiChatHistoryDTO.setAiInput(record.get(defaultTable.AI_INPUT)); + aiChatHistoryDTO.setAiAnswer(record.get(defaultTable.AI_ANSWER)); + aiChatHistoryDTO.setErrorCode(record.get(defaultTable.ERROR_CODE)); + aiChatHistoryDTO.setErrorMessage(record.get(defaultTable.ERROR_MESSAGE)); + aiChatHistoryDTO.setStartTime(JooqDataTypeUtil.buildLong(record.get(defaultTable.START_TIME))); + aiChatHistoryDTO.setAnswerTime(JooqDataTypeUtil.buildLong(record.get(defaultTable.ANSWER_TIME))); + aiChatHistoryDTO.setTotalTime(JooqDataTypeUtil.buildLong(record.get(defaultTable.TOTAL_TIME))); + return aiChatHistoryDTO; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java new file mode 100644 index 0000000000..e1d37d5a97 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java @@ -0,0 +1,79 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.dao.impl; + +import com.tencent.bk.job.analysis.dao.AIPromptTemplateDAO; +import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; +import com.tencent.bk.job.analysis.model.tables.AiPromptTemplate; +import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.jooq.Condition; +import org.jooq.DSLContext; +import org.jooq.Record; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Collection; + +@Slf4j +@Repository +public class AIPromptTemplateDAOImpl extends BaseDAOImpl implements AIPromptTemplateDAO { + private static final AiPromptTemplate defaultTable = AiPromptTemplate.AI_PROMPT_TEMPLATE; + + private final DSLContext dslContext; + + public AIPromptTemplateDAOImpl(@Qualifier("job-analysis-dsl-context") DSLContext dslContext) { + this.dslContext = dslContext; + } + + @Override + public AIPromptTemplateDTO getAIPromptTemplate(String code, String locale) { + Collection conditions = new ArrayList<>(); + conditions.add(defaultTable.CODE.eq(code)); + conditions.add(defaultTable.LOCALE.eq(locale)); + val query = dslContext.select( + defaultTable.ID, + defaultTable.CODE, + defaultTable.LOCALE, + defaultTable.NAME, + defaultTable.CONTENT + ) + .from(defaultTable) + .where(conditions); + return fetchOne(query, this::convertRecordToDto); + } + + private AIPromptTemplateDTO convertRecordToDto(Record record) { + AIPromptTemplateDTO aiPromptTemplateDTO = new AIPromptTemplateDTO(); + aiPromptTemplateDTO.setId(record.get(defaultTable.ID)); + aiPromptTemplateDTO.setCode(record.get(defaultTable.CODE)); + aiPromptTemplateDTO.setLocale(record.get(defaultTable.LOCALE)); + aiPromptTemplateDTO.setName(record.get(defaultTable.NAME)); + aiPromptTemplateDTO.setContent(record.get(defaultTable.CONTENT)); + return aiPromptTemplateDTO; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java new file mode 100644 index 0000000000..aa4b284d0b --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java @@ -0,0 +1,128 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.model.web.resp.UserInput; +import com.tencent.bk.job.common.util.json.LongTimestampSerializer; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * AI对话历史记录 + */ +@Data +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class AIChatHistoryDTO { + /** + * id + */ + private Long id; + + /** + * 用户名 + */ + private String username; + + /** + * 用户输入内容 + */ + private String userInput; + + /** + * 使用的提示符模板ID + */ + private Integer promptTemplateId; + + /** + * 提交给AI的输入内容 + */ + private String aiInput; + + /** + * AI回答的内容 + */ + private String aiAnswer; + + /** + * AI回答失败时的错误码 + */ + private String errorCode; + + /** + * AI回答失败时的错误信息 + */ + private String errorMessage; + + /** + * 开始时间 + */ + @JsonSerialize(using = LongTimestampSerializer.class) + private Long startTime; + + /** + * AI回答完成时间 + */ + @JsonSerialize(using = LongTimestampSerializer.class) + private Long answerTime; + + /** + * 总耗时 + */ + @JsonSerialize(using = LongTimestampSerializer.class) + private Long totalTime; + + /** + * 是否已删除:0表示未删除,1表示已删除 + */ + private Boolean isDeleted; + + public void updateTotalTime() { + if (startTime != null && answerTime != null) { + totalTime = answerTime - startTime; + } + } + + public AIChatRecord toAIChatRecord() { + AIChatRecord aiChatRecord = new AIChatRecord(); + UserInput userInput = new UserInput(); + userInput.setContent(this.userInput); + userInput.setTime(startTime); + aiChatRecord.setUserInput(userInput); + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent(this.aiAnswer); + aiAnswer.setTime(answerTime); + aiAnswer.setErrorCode(errorCode); + aiAnswer.setErrorMessage(errorMessage); + aiChatRecord.setAiAnswer(aiAnswer); + return aiChatRecord; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java new file mode 100644 index 0000000000..b664e52fc3 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.tencent.bk.job.common.util.json.LongTimestampSerializer; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * AI提示符模板 + */ +@Data +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class AIPromptTemplateDTO { + /** + * id + */ + private Integer id; + + /** + * 模板代码,用于唯一标识模板 + */ + private String code; + + /** + * 语言 + */ + private String locale; + + /** + * 模板名称 + */ + private String name; + + /** + * 模板内容 + */ + private String content; + + /** + * 对模板的描述 + */ + private String description; + + /** + * 创建者 + */ + private String creator; + + /** + * 更新者 + */ + private String lastModifyUser; + + /** + * 创建时间 + */ + @JsonSerialize(using = LongTimestampSerializer.class) + private Long createTime; + + /** + * 更新时间 + */ + @JsonSerialize(using = LongTimestampSerializer.class) + private Long lastModifyTime; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java new file mode 100644 index 0000000000..e0780f0e8b --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; + +import java.util.List; + +public interface AIChatHistoryService { + /** + * 插入聊天记录 + * + * @param aiChatHistoryDTO AI聊天记录 + * @return 插入记录的id + */ + Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO); + + + /** + * 获取最近的聊天记录列表 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近的聊天记录列表 + */ + List getLatestChatHistoryList(String username, Integer start, Integer length); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java index 1df57d2652..45b9c9ad87 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java @@ -27,5 +27,14 @@ import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; public interface AIService { - AIAnswer getAIAnswer(String token, String userInput); + + /** + * 根据用户输入获取AI回答 + * 注意:默认使用当前线程上下文中的请求Cookie中的bk_ticket/bk_token调用大模型接口, + * 非HTTP请求处理线程中调用需要额外实现登录态传递逻辑 + * + * @param userInput 用户输入 + * @return AI回答结果 + */ + AIAnswer getAIAnswer(String userInput); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java new file mode 100644 index 0000000000..f042ac7405 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; + +import java.util.List; + +public interface ChatService { + + /** + * 与AI聊天并处理聊天记录保存等逻辑 + * + * @param username 用户名 + * @param userInput 用户输入 + * @return AI回答结果 + */ + AIAnswer chatWithAI(String username, String userInput); + + /** + * 获取最近的聊天记录列表 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近的聊天记录列表 + */ + List getLatestChatHistoryList(String username, Integer start, Integer length); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java new file mode 100644 index 0000000000..1fdc14b4b5 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.dao.AIChatHistoryDAO; +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class AIChatHistoryServiceImpl implements AIChatHistoryService { + + private final AIChatHistoryDAO aiChatHistoryDAO; + + @Autowired + public AIChatHistoryServiceImpl(AIChatHistoryDAO aiChatHistoryDAO) { + this.aiChatHistoryDAO = aiChatHistoryDAO; + } + + @Override + public Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { + return aiChatHistoryDAO.insertAIChatHistory(aiChatHistoryDTO); + } + + @Override + public List getLatestChatHistoryList(String username, Integer start, Integer length) { + return aiChatHistoryDAO.getLatestChatHistoryList(username, start, length); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java index c447b20623..4f66a92aae 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java @@ -26,6 +26,7 @@ import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.service.ai.AIService; +import com.tencent.bk.job.analysis.service.login.LoginTokenService; import com.tencent.bk.job.common.aidev.IBkAIDevClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -35,15 +36,18 @@ @Service public class AIServiceImpl implements AIService { + private final LoginTokenService loginTokenService; private final IBkAIDevClient bkAIDevClient; @Autowired - public AIServiceImpl(IBkAIDevClient bkAIDevClient) { + public AIServiceImpl(LoginTokenService loginTokenService, IBkAIDevClient bkAIDevClient) { + this.loginTokenService = loginTokenService; this.bkAIDevClient = bkAIDevClient; } @Override - public AIAnswer getAIAnswer(String token, String userInput) { + public AIAnswer getAIAnswer(String userInput) { + String token = loginTokenService.getToken(); return AIAnswer.successAnswer(bkAIDevClient.getHunYuanAnswer(token, userInput)); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java new file mode 100644 index 0000000000..8e3784af4b --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -0,0 +1,85 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import com.tencent.bk.job.analysis.service.ai.AIService; +import com.tencent.bk.job.analysis.service.ai.ChatService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@Service +public class ChatServiceImpl implements ChatService { + + private final AIService aiService; + private final AIChatHistoryService aiChatHistoryService; + + @Autowired + public ChatServiceImpl(AIService aiService, AIChatHistoryService aiChatHistoryService) { + this.aiService = aiService; + this.aiChatHistoryService = aiChatHistoryService; + } + + @Override + public AIAnswer chatWithAI(String username, String userInput) { + // 1.调用AI服务获取回答 + Long startTime = System.currentTimeMillis(); + AIAnswer aiAnswer = aiService.getAIAnswer(userInput); + // 2.保存聊天记录 + AIChatHistoryDTO aiChatHistoryDTO = buildAIChatHistoryDTO(username, startTime, userInput, aiAnswer); + aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + return aiAnswer; + } + + @Override + public List getLatestChatHistoryList(String username, Integer start, Integer length) { + return aiChatHistoryService.getLatestChatHistoryList(username, start, length); + } + + private AIChatHistoryDTO buildAIChatHistoryDTO(String username, + Long startTime, + String userInput, + AIAnswer aiAnswer) { + AIChatHistoryDTO aiChatHistoryDTO = new AIChatHistoryDTO(); + aiChatHistoryDTO.setUsername(username); + aiChatHistoryDTO.setUserInput(userInput); + aiChatHistoryDTO.setPromptTemplateId(null); + aiChatHistoryDTO.setAiInput(userInput); + aiChatHistoryDTO.setAiAnswer(aiAnswer.getContent()); + aiChatHistoryDTO.setErrorCode(String.valueOf(aiAnswer.getErrorCode())); + aiChatHistoryDTO.setErrorMessage(aiAnswer.getErrorMessage()); + aiChatHistoryDTO.setStartTime(startTime); + aiChatHistoryDTO.setAnswerTime(System.currentTimeMillis()); + aiChatHistoryDTO.updateTotalTime(); + aiChatHistoryDTO.setIsDeleted(false); + return aiChatHistoryDTO; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/LoginTokenService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/LoginTokenService.java new file mode 100644 index 0000000000..915723cf59 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/LoginTokenService.java @@ -0,0 +1,29 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.login; + +public interface LoginTokenService { + String getToken(); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/impl/LoginTokenServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/impl/LoginTokenServiceImpl.java new file mode 100644 index 0000000000..14114c8e32 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/login/impl/LoginTokenServiceImpl.java @@ -0,0 +1,99 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.login.impl; + +import com.tencent.bk.job.analysis.service.login.LoginTokenService; +import com.tencent.bk.job.common.aidev.config.CustomPaasLoginProperties; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.InternalException; +import com.tencent.bk.job.common.util.JobContextUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +@Slf4j +@Service +public class LoginTokenServiceImpl implements LoginTokenService { + private final CustomPaasLoginProperties customPaasLoginProperties; + + @Autowired + public LoginTokenServiceImpl(CustomPaasLoginProperties customPaasLoginProperties) { + this.customPaasLoginProperties = customPaasLoginProperties; + } + + @Override + public String getToken() { + String token = customPaasLoginProperties.getToken(); + if (StringUtils.isNotBlank(token)) { + return token; + } + if (!customPaasLoginProperties.isEnabled()) { + return getBkToken(); + } + return getBkTicket(); + } + + private String getBkToken() { + return getCookieValue("bk_token"); + } + + private String getBkTicket() { + return getCookieValue("bk_ticket"); + } + + /** + * 从请求中获取cookie + * + * @param cookieName cookie名称 + * @return cookie值 + */ + private String getCookieValue(String cookieName) { + HttpServletRequest request = JobContextUtil.getRequest(); + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + String message = "Cookies is null, please check request in thread context"; + throw new InternalException(message, ErrorCode.INTERNAL_ERROR); + } + for (Cookie cookie : cookies) { + if (cookieName.equals(cookie.getName())) { + return cookie.getValue(); + } + } + if (log.isDebugEnabled()) { + StringBuilder cookiesSb = new StringBuilder(); + for (Cookie cookie : cookies) { + cookiesSb.append(cookie.toString()); + cookiesSb.append(","); + } + log.debug("Cookies={}", cookiesSb); + } + String message = "Cannot find cookie by name: " + cookieName; + throw new InternalException(message, ErrorCode.INTERNAL_ERROR); + } +} diff --git a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceBatchTaskDAOImpl.java b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceBatchTaskDAOImpl.java index 80b5695adf..2bc272e36e 100644 --- a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceBatchTaskDAOImpl.java +++ b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceBatchTaskDAOImpl.java @@ -26,6 +26,7 @@ import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.InternalException; +import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; import com.tencent.bk.job.common.util.JobUUID; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.file_gateway.dao.filesource.FileSourceBatchTaskDAO; diff --git a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceDAOImpl.java b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceDAOImpl.java index 4d265f5b79..84909df763 100644 --- a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceDAOImpl.java +++ b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceDAOImpl.java @@ -25,6 +25,7 @@ package com.tencent.bk.job.file_gateway.dao.filesource.impl; import com.fasterxml.jackson.core.type.TypeReference; +import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.file_gateway.dao.filesource.FileSourceDAO; import com.tencent.bk.job.file_gateway.dao.filesource.FileSourceTypeDAO; diff --git a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceTaskDAOImpl.java b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceTaskDAOImpl.java index 33d82ac79b..96f6b2059e 100644 --- a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceTaskDAOImpl.java +++ b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileSourceTaskDAOImpl.java @@ -26,6 +26,7 @@ import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.InternalException; +import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; import com.tencent.bk.job.common.util.JobUUID; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.file_gateway.dao.filesource.FileSourceTaskDAO; diff --git a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileTaskDAOImpl.java b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileTaskDAOImpl.java index f7f139506d..23872ad1d2 100644 --- a/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileTaskDAOImpl.java +++ b/src/backend/job-file-gateway/service-job-file-gateway/src/main/java/com/tencent/bk/job/file_gateway/dao/filesource/impl/FileTaskDAOImpl.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.file_gateway.dao.filesource.impl; +import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; import com.tencent.bk.job.file_gateway.consts.TaskStatusEnum; import com.tencent.bk.job.file_gateway.dao.filesource.FileTaskDAO; import com.tencent.bk.job.file_gateway.model.dto.FileTaskDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/DbRecordMapper.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/DbRecordMapper.java index 6c42b3e327..5ddb90169c 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/DbRecordMapper.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/common/util/DbRecordMapper.java @@ -28,6 +28,7 @@ import com.tencent.bk.job.common.constant.DuplicateHandlerEnum; import com.tencent.bk.job.common.constant.NotExistPathHandlerEnum; import com.tencent.bk.job.common.model.dto.UserRoleInfoDTO; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import com.tencent.bk.job.manage.api.common.constants.task.TaskApprovalTypeEnum; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/customsetting/impl/CustomScriptTemplateDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/customsetting/impl/CustomScriptTemplateDAOImpl.java index aa37ec3032..9abf129792 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/customsetting/impl/CustomScriptTemplateDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/customsetting/impl/CustomScriptTemplateDAOImpl.java @@ -24,7 +24,7 @@ package com.tencent.bk.job.manage.dao.customsetting.impl; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.customsetting.CustomScriptTemplateDAO; import com.tencent.bk.job.manage.model.dto.customsetting.ScriptTemplateDTO; import com.tencent.bk.job.manage.model.tables.UserCustomScriptTemplate; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/globalsetting/impl/DangerousRuleDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/globalsetting/impl/DangerousRuleDAOImpl.java index 6b0674268e..ce48f55362 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/globalsetting/impl/DangerousRuleDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/globalsetting/impl/DangerousRuleDAOImpl.java @@ -26,7 +26,7 @@ import com.tencent.bk.job.manage.api.common.constants.EnableStatusEnum; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.globalsetting.DangerousRuleDAO; import com.tencent.bk.job.manage.model.dto.globalsetting.DangerousRuleDTO; import com.tencent.bk.job.manage.model.query.DangerousRuleQuery; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/AccountDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/AccountDAOImpl.java index e66e5d3b67..d34f183791 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/AccountDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/AccountDAOImpl.java @@ -30,7 +30,7 @@ import com.tencent.bk.job.common.model.PageData; import com.tencent.bk.job.common.util.date.DateUtils; import com.tencent.bk.job.manage.api.common.constants.account.AccountTypeEnum; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.AccountDAO; import com.tencent.bk.job.manage.model.dto.AccountDTO; import com.tencent.bk.job.manage.model.dto.AccountDisplayDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ApplicationHostDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ApplicationHostDAOImpl.java index 99af02fabe..4703f72df0 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ApplicationHostDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ApplicationHostDAOImpl.java @@ -40,7 +40,7 @@ import com.tencent.bk.job.common.util.StringUtil; import com.tencent.bk.job.common.util.TagUtils; import com.tencent.bk.job.manage.common.TopologyHelper; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.ApplicationDAO; import com.tencent.bk.job.manage.dao.ApplicationHostDAO; import com.tencent.bk.job.manage.dao.HostTopoDAO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/HostTopoDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/HostTopoDAOImpl.java index 46b90d3eb3..52c022158e 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/HostTopoDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/HostTopoDAOImpl.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.manage.dao.impl; import com.tencent.bk.job.common.util.CollectionUtil; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.HostTopoDAO; import com.tencent.bk.job.manage.model.dto.HostTopoDTO; import com.tencent.bk.job.manage.model.tables.HostTopo; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptCitedTaskTemplateDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptCitedTaskTemplateDAOImpl.java index 58e3c26079..75f8a03b7a 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptCitedTaskTemplateDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptCitedTaskTemplateDAOImpl.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.manage.dao.impl; import com.tencent.bk.job.manage.api.common.constants.JobResourceStatusEnum; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.ScriptCitedTaskTemplateDAO; import com.tencent.bk.job.manage.model.dto.script.ScriptCitedTaskTemplateDTO; import com.tencent.bk.job.manage.model.tables.ScriptVersion; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptRelateTaskPlanDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptRelateTaskPlanDAOImpl.java index 90a1910b37..945bb9e8ec 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptRelateTaskPlanDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/impl/ScriptRelateTaskPlanDAOImpl.java @@ -26,7 +26,7 @@ import com.tencent.bk.job.manage.api.common.constants.JobResourceStatusEnum; import com.tencent.bk.job.manage.api.common.constants.task.TaskPlanTypeEnum; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.ScriptRelateTaskPlanDAO; import com.tencent.bk.job.manage.model.dto.ScriptRelatedTaskPlanDTO; import com.tencent.bk.job.manage.model.tables.ScriptVersion; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileInfoDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileInfoDAOImpl.java index cdcee5749b..0b3626aff3 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileInfoDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileInfoDAOImpl.java @@ -28,7 +28,7 @@ import com.tencent.bk.job.common.exception.InternalException; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.manage.common.util.DbRecordMapper; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.TaskFileInfoDAO; import com.tencent.bk.job.manage.model.dto.task.TaskFileInfoDTO; import com.tencent.bk.job.manage.model.dto.task.TaskTargetDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileStepDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileStepDAOImpl.java index 44f0dd46c4..742e33c4cc 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileStepDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanFileStepDAOImpl.java @@ -28,7 +28,7 @@ import com.tencent.bk.job.common.exception.InternalException; import com.tencent.bk.job.manage.api.common.constants.task.TaskFileTypeEnum; import com.tencent.bk.job.manage.common.util.DbRecordMapper; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.TaskFileStepDAO; import com.tencent.bk.job.manage.model.dto.task.TaskFileStepDTO; import com.tencent.bk.job.manage.model.dto.task.TaskTargetDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanScriptStepDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanScriptStepDAOImpl.java index 5ac061fdd2..f88cc28476 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanScriptStepDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/plan/impl/TaskPlanScriptStepDAOImpl.java @@ -246,7 +246,7 @@ public long insertScriptStep(TaskScriptStepDTO scriptStep) { scriptStep.getContent(), UByte.valueOf(scriptStep.getLanguage().getValue()), sensitiveParamCryptoService.encryptParamIfNeeded(scriptStep.getSecureParam(), - scriptStep.getScriptParam()), + scriptStep.getScriptParam()), ULong.valueOf(scriptStep.getTimeout()), ULong.valueOf(scriptStep.getAccount()), scriptStep.getExecuteTarget().toJsonString(), diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileInfoDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileInfoDAOImpl.java index 6f3d988084..4986b7f545 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileInfoDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileInfoDAOImpl.java @@ -28,7 +28,7 @@ import com.tencent.bk.job.common.exception.InternalException; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.manage.common.util.DbRecordMapper; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.TaskFileInfoDAO; import com.tencent.bk.job.manage.model.dto.task.TaskFileInfoDTO; import com.tencent.bk.job.manage.model.dto.task.TaskTargetDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileStepDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileStepDAOImpl.java index d6d94e04cf..0697e1f380 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileStepDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateFileStepDAOImpl.java @@ -27,7 +27,7 @@ import com.tencent.bk.job.common.constant.NotExistPathHandlerEnum; import com.tencent.bk.job.manage.api.common.constants.task.TaskFileTypeEnum; import com.tencent.bk.job.manage.common.util.DbRecordMapper; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.TaskFileStepDAO; import com.tencent.bk.job.manage.model.dto.task.TaskFileStepDTO; import com.tencent.bk.job.manage.model.dto.task.TaskTargetDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateScriptStepDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateScriptStepDAOImpl.java index f3ef9e02f9..16a051d8e4 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateScriptStepDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateScriptStepDAOImpl.java @@ -25,9 +25,9 @@ package com.tencent.bk.job.manage.dao.template.impl; import com.tencent.bk.job.common.crypto.scenario.SensitiveParamCryptoService; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import com.tencent.bk.job.manage.api.common.constants.task.TaskScriptSourceEnum; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.template.TaskTemplateScriptStepDAO; import com.tencent.bk.job.manage.model.dto.TemplateStepScriptStatusInfo; import com.tencent.bk.job.manage.model.dto.task.TaskScriptStepDTO; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateVariableDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateVariableDAOImpl.java index 5f91133ac6..4d509ce0d0 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateVariableDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateVariableDAOImpl.java @@ -28,7 +28,7 @@ import com.tencent.bk.job.common.constant.TaskVariableTypeEnum; import com.tencent.bk.job.common.crypto.scenario.CipherVariableCryptoService; import com.tencent.bk.job.common.exception.InternalException; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.TaskVariableDAO; import com.tencent.bk.job.manage.model.dto.task.TaskVariableDTO; import com.tencent.bk.job.manage.model.tables.TaskTemplateVariable; diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/whiteip/impl/WhiteIPRecordDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/whiteip/impl/WhiteIPRecordDAOImpl.java index 7c19037b20..a16575dacd 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/whiteip/impl/WhiteIPRecordDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/whiteip/impl/WhiteIPRecordDAOImpl.java @@ -31,7 +31,7 @@ import com.tencent.bk.job.common.model.dto.HostDTO; import com.tencent.bk.job.common.mysql.JobTransactional; import com.tencent.bk.job.common.util.CustomCollectionUtils; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.ApplicationDAO; import com.tencent.bk.job.manage.dao.whiteip.ActionScopeDAO; import com.tencent.bk.job.manage.dao.whiteip.WhiteIPActionScopeDAO; diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index 3ed3dbaa8d..d7e58b4dbd 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -32,8 +32,8 @@ CREATE TABLE IF NOT EXISTS `ai_chat_history` ( `prompt_template_id` int(10) NULL DEFAULT NULL COMMENT '使用的提示符模板ID', `ai_input` TEXT NOT NULL COMMENT '提交给AI的输入内容', `ai_answer` TEXT NOT NULL COMMENT 'AI回答的内容', - `error_code` varchar(128) NOT NULL COMMENT 'AI回答失败时的错误码', - `error_message` varchar(512) NOT NULL COMMENT 'AI回答失败时的错误信息', + `error_code` varchar(128) NULL DEFAULT NULL COMMENT 'AI回答失败时的错误码', + `error_message` varchar(512) NULL DEFAULT NULL COMMENT 'AI回答失败时的错误信息', `start_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '开始时间', `answer_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT 'AI回答完成时间', `total_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '总耗时', From ab0115e494875019ef70e2d9a2d4117a7fdbf50a Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 24 Jun 2024 11:57:24 +0800 Subject: [PATCH 007/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 去除聊天记录mock数据 --- .../api/web/impl/WebAIResourceImpl.java | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 642aef4a4e..17c31f80d8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -31,16 +31,13 @@ import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; -import com.tencent.bk.job.analysis.model.web.resp.UserInput; import com.tencent.bk.job.analysis.service.ai.ChatService; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; -import com.tencent.bk.job.common.util.ThreadUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -80,43 +77,6 @@ public Response> getLatestChatHistoryList(String username, ); } - public List mockLatestChatHistoryList(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - Integer start, - Integer length) { - List aiChatRecordList = new ArrayList<>(); - AIChatRecord record = new AIChatRecord(); - UserInput userInput = new UserInput(); - userInput.setContent("你是谁"); - userInput.setTime(System.currentTimeMillis()); - record.setUserInput(userInput); - ThreadUtils.sleep(1000); - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("我是AI小鲸"); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); - record.setAiAnswer(aiAnswer); - aiChatRecordList.add(record); - - AIChatRecord record2 = new AIChatRecord(); - UserInput userInput2 = new UserInput(); - userInput2.setContent("Hello"); - userInput2.setTime(System.currentTimeMillis()); - record2.setUserInput(userInput2); - ThreadUtils.sleep(1000); - AIAnswer aiAnswer2 = new AIAnswer(); - aiAnswer2.setContent("World"); - aiAnswer2.setErrorCode("0"); - aiAnswer2.setErrorMessage(null); - aiAnswer2.setTime(System.currentTimeMillis()); - record2.setAiAnswer(aiAnswer2); - aiChatRecordList.add(record2); - return aiChatRecordList; - } - @Override public Response generalChat(String username, AppResourceScope appResourceScope, From ed9e2671a17ff0e0a7433c5dbc80ed40ca9dcb05 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 24 Jun 2024 17:22:59 +0800 Subject: [PATCH 008/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通用聊天接口支持多轮对话 --- .../bk/job/common/aidev/IBkAIDevClient.java | 6 +++- .../job/common/aidev/impl/BkAIDevClient.java | 14 +++++--- .../bk/job/analysis/service/ai/AIService.java | 8 +++-- .../service/ai/impl/AIServiceImpl.java | 35 +++++++++++++++++-- .../service/ai/impl/ChatServiceImpl.java | 10 ++++-- 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java index c588067e46..1d534f83ed 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkAIDevClient.java @@ -24,8 +24,12 @@ package com.tencent.bk.job.common.aidev; +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; + +import java.util.List; + public interface IBkAIDevClient { - String getHunYuanAnswer(String token, String userInput); + String getHunYuanAnswer(String token, List messageHistoryList, String userInput); } diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java index c3d037815d..9ae22c59f5 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java @@ -49,6 +49,7 @@ import com.tencent.bk.job.common.util.http.HttpMetricUtil; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; +import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; @@ -84,8 +85,8 @@ private static String getBkAIDevUrlSafely(BkApiGatewayProperties bkApiGatewayPro } @Override - public String getHunYuanAnswer(String token, String userInput) { - AIDevReq req = buildAIDevReq(userInput); + public String getHunYuanAnswer(String token, List messageHistoryList, String userInput) { + AIDevReq req = buildAIDevReq(messageHistoryList, userInput); BkApiAuthorization authorization = buildAuthorization(token); AIDevResp> resp = requestBkAIDevApi( HttpMethodEnum.POST, @@ -103,13 +104,18 @@ public String getHunYuanAnswer(String token, String userInput) { return aiDevMessage.getContent(); } - private AIDevReq buildAIDevReq(String userInput) { + private AIDevReq buildAIDevReq(List messageHistoryList, String userInput) { AIDevReq req = new AIDevReq(); req.setConfig(AIDevReqConfig.hunyuanConfig()); AIDevReqData data = new AIDevReqData(); List inputs = new ArrayList<>(); AIDevInput input = new AIDevInput(); - List messages = new ArrayList<>(); + List messages; + if (CollectionUtils.isEmpty(messageHistoryList)) { + messages = new ArrayList<>(); + } else { + messages = new ArrayList<>(messageHistoryList); + } AIDevMessage message = new AIDevMessage(); message.setRole(AIDevMessage.ROLE_USER); message.setContent(userInput); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java index 45b9c9ad87..4315a4206e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java @@ -24,8 +24,11 @@ package com.tencent.bk.job.analysis.service.ai; +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import java.util.List; + public interface AIService { /** @@ -33,8 +36,9 @@ public interface AIService { * 注意:默认使用当前线程上下文中的请求Cookie中的bk_ticket/bk_token调用大模型接口, * 非HTTP请求处理线程中调用需要额外实现登录态传递逻辑 * - * @param userInput 用户输入 + * @param chatHistoryDTOList 历史聊天记录 + * @param userInput 用户输入 * @return AI回答结果 */ - AIAnswer getAIAnswer(String userInput); + AIAnswer getAIAnswer(List chatHistoryDTOList, String userInput); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java index 4f66a92aae..3779417b53 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java @@ -24,14 +24,21 @@ package com.tencent.bk.job.analysis.service.ai.impl; +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.login.LoginTokenService; import com.tencent.bk.job.common.aidev.IBkAIDevClient; +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + @Slf4j @Service public class AIServiceImpl implements AIService { @@ -46,8 +53,32 @@ public AIServiceImpl(LoginTokenService loginTokenService, IBkAIDevClient bkAIDev } @Override - public AIAnswer getAIAnswer(String userInput) { + public AIAnswer getAIAnswer(List chatHistoryDTOList, String userInput) { String token = loginTokenService.getToken(); - return AIAnswer.successAnswer(bkAIDevClient.getHunYuanAnswer(token, userInput)); + return AIAnswer.successAnswer( + bkAIDevClient.getHunYuanAnswer( + token, + buildMessageHistoryList(chatHistoryDTOList), + userInput + ) + ); + } + + private List buildMessageHistoryList(List chatHistoryDTOList) { + if (CollectionUtils.isEmpty(chatHistoryDTOList)) { + return Collections.emptyList(); + } + List messageHistoryList = new ArrayList<>(); + for (AIChatHistoryDTO chatHistoryDTO : chatHistoryDTOList) { + AIDevMessage aiDevMessage = new AIDevMessage(); + aiDevMessage.setRole(AIDevMessage.ROLE_USER); + aiDevMessage.setContent(chatHistoryDTO.getAiInput()); + messageHistoryList.add(aiDevMessage); + aiDevMessage = new AIDevMessage(); + aiDevMessage.setRole(AIDevMessage.ROLE_ASSISTANT); + aiDevMessage.setContent(chatHistoryDTO.getAiAnswer()); + messageHistoryList.add(aiDevMessage); + } + return messageHistoryList; } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 8e3784af4b..5a1ec8d9f7 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -33,6 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Comparator; import java.util.List; @Slf4j @@ -50,10 +51,13 @@ public ChatServiceImpl(AIService aiService, AIChatHistoryService aiChatHistorySe @Override public AIAnswer chatWithAI(String username, String userInput) { - // 1.调用AI服务获取回答 Long startTime = System.currentTimeMillis(); - AIAnswer aiAnswer = aiService.getAIAnswer(userInput); - // 2.保存聊天记录 + // 1.获取最近的聊天记录 + List chatHistoryDTOList = getLatestChatHistoryList(username, 0, 5); + chatHistoryDTOList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); + // 2.调用AI服务获取回答 + AIAnswer aiAnswer = aiService.getAIAnswer(chatHistoryDTOList, userInput); + // 3.保存聊天记录 AIChatHistoryDTO aiChatHistoryDTO = buildAIChatHistoryDTO(username, startTime, userInput, aiAnswer); aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); return aiAnswer; From 6f45f6bafc627f1085ce2b41e3ebacfeb17e66a7 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 24 Jun 2024 21:14:39 +0800 Subject: [PATCH 009/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持检查脚本 --- .../consts/PromptTemplateCodeEnum.java | 41 ++++++++++ .../api/web/impl/WebAIResourceImpl.java | 11 ++- .../dao/impl/AIPromptTemplateDAOImpl.java | 6 +- .../model/dto/AIPromptTemplateDTO.java | 7 +- .../service/ai/AIChatHistoryService.java | 16 ++++ .../service/ai/AICheckScriptService.java | 40 ++++++++++ .../analysis/service/ai/AIPromptService.java | 39 +++++++++ .../bk/job/analysis/service/ai/AIService.java | 10 +++ .../ai/impl/AIChatHistoryServiceImpl.java | 22 ++++++ .../ai/impl/AICheckScriptServiceImpl.java | 72 +++++++++++++++++ .../service/ai/impl/AIPromptServiceImpl.java | 79 +++++++++++++++++++ .../service/ai/impl/AIServiceImpl.java | 5 ++ .../service/ai/impl/ChatServiceImpl.java | 26 ++---- ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 3 +- 14 files changed, 348 insertions(+), 29 deletions(-) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java new file mode 100644 index 0000000000..d303e15d25 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.consts; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum PromptTemplateCodeEnum { + /** + * 检查脚本 + */ + CHECK_SCRIPT, + /** + * 分析任务报错 + */ + ANALYZE_TASK_ERROR; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 17c31f80d8..aae2f533dd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -31,6 +31,7 @@ import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; import com.tencent.bk.job.analysis.service.ai.ChatService; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; @@ -48,10 +49,12 @@ public class WebAIResourceImpl implements WebAIResource { private final ChatService chatService; + private final AICheckScriptService aiCheckScriptService; @Autowired - public WebAIResourceImpl(ChatService chatService) { + public WebAIResourceImpl(ChatService chatService, AICheckScriptService aiCheckScriptService) { this.chatService = chatService; + this.aiCheckScriptService = aiCheckScriptService; } @Override @@ -93,11 +96,7 @@ public Response checkScript(String username, String scopeType, String scopeId, AICheckScriptReq req) { - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("没什么问题"); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); + AIAnswer aiAnswer = aiCheckScriptService.check(username, req.getType(), req.getContent()); return Response.buildSuccessResp(aiAnswer); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java index e1d37d5a97..e401367a64 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIPromptTemplateDAOImpl.java @@ -60,7 +60,8 @@ public AIPromptTemplateDTO getAIPromptTemplate(String code, String locale) { defaultTable.CODE, defaultTable.LOCALE, defaultTable.NAME, - defaultTable.CONTENT + defaultTable.RAW_PROMPT, + defaultTable.TEMPLATE ) .from(defaultTable) .where(conditions); @@ -73,7 +74,8 @@ private AIPromptTemplateDTO convertRecordToDto(Record record) { aiPromptTemplateDTO.setCode(record.get(defaultTable.CODE)); aiPromptTemplateDTO.setLocale(record.get(defaultTable.LOCALE)); aiPromptTemplateDTO.setName(record.get(defaultTable.NAME)); - aiPromptTemplateDTO.setContent(record.get(defaultTable.CONTENT)); + aiPromptTemplateDTO.setRawPrompt(record.get(defaultTable.RAW_PROMPT)); + aiPromptTemplateDTO.setTemplate(record.get(defaultTable.TEMPLATE)); return aiPromptTemplateDTO; } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java index b664e52fc3..1db99ee351 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptTemplateDTO.java @@ -59,10 +59,15 @@ public class AIPromptTemplateDTO { */ private String name; + /** + * 不含上下文的简单提示符 + */ + private String rawPrompt; + /** * 模板内容 */ - private String content; + private String template; /** * 对模板的描述 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java index e0780f0e8b..14a93a7437 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -25,10 +25,26 @@ package com.tencent.bk.job.analysis.service.ai; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import java.util.List; public interface AIChatHistoryService { + /** + * 构建AI聊天记录 + * + * @param username 用户名 + * @param startTime 开始时间 + * @param userInput 用户输入 + * @param aiAnswer AI回答 + * @return AI聊天记录 + */ + AIChatHistoryDTO buildAIChatHistoryDTO(String username, + Long startTime, + String userInput, + String aiInput, + AIAnswer aiAnswer); + /** * 插入聊天记录 * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java new file mode 100644 index 0000000000..7804fc1c3b --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; + +public interface AICheckScriptService { + + /** + * 检查脚本 + * + * @param username 用户名 + * @param type 脚本类型 + * @param scriptContent 脚本内容 + * @return AI回答 + */ + AIAnswer check(String username, Integer type, String scriptContent); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java new file mode 100644 index 0000000000..191df65aec --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import org.apache.commons.lang3.tuple.Pair; + +public interface AIPromptService { + + /** + * 获取检查脚本的AI提示符 + * + * @param type 脚本类型 + * @param scriptContent 脚本内容 + * @return <原始提示符,通过模板渲染的AI提示符> + */ + Pair getCheckScriptPrompt(Integer type, String scriptContent); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java index 4315a4206e..4176e4e826 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java @@ -41,4 +41,14 @@ public interface AIService { * @return AI回答结果 */ AIAnswer getAIAnswer(List chatHistoryDTOList, String userInput); + + /** + * 根据用户输入获取AI回答(不传入历史聊天记录) + * 注意:默认使用当前线程上下文中的请求Cookie中的bk_ticket/bk_token调用大模型接口, + * 非HTTP请求处理线程中调用需要额外实现登录态传递逻辑 + * + * @param userInput 用户输入 + * @return AI回答结果 + */ + AIAnswer getAIAnswer(String userInput); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index 1fdc14b4b5..d0574770be 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -26,6 +26,7 @@ import com.tencent.bk.job.analysis.dao.AIChatHistoryDAO; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -44,6 +45,27 @@ public AIChatHistoryServiceImpl(AIChatHistoryDAO aiChatHistoryDAO) { this.aiChatHistoryDAO = aiChatHistoryDAO; } + @Override + public AIChatHistoryDTO buildAIChatHistoryDTO(String username, + Long startTime, + String userInput, + String aiInput, + AIAnswer aiAnswer) { + AIChatHistoryDTO aiChatHistoryDTO = new AIChatHistoryDTO(); + aiChatHistoryDTO.setUsername(username); + aiChatHistoryDTO.setUserInput(userInput); + aiChatHistoryDTO.setPromptTemplateId(null); + aiChatHistoryDTO.setAiInput(aiInput); + aiChatHistoryDTO.setAiAnswer(aiAnswer.getContent()); + aiChatHistoryDTO.setErrorCode(String.valueOf(aiAnswer.getErrorCode())); + aiChatHistoryDTO.setErrorMessage(aiAnswer.getErrorMessage()); + aiChatHistoryDTO.setStartTime(startTime); + aiChatHistoryDTO.setAnswerTime(System.currentTimeMillis()); + aiChatHistoryDTO.updateTotalTime(); + aiChatHistoryDTO.setIsDeleted(false); + return aiChatHistoryDTO; + } + @Override public Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { return aiChatHistoryDAO.insertAIChatHistory(aiChatHistoryDTO); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java new file mode 100644 index 0000000000..1675e4dd67 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; +import com.tencent.bk.job.analysis.service.ai.AIPromptService; +import com.tencent.bk.job.analysis.service.ai.AIService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class AICheckScriptServiceImpl implements AICheckScriptService { + + private final AIPromptService aiPromptService; + private final AIService aiService; + private final AIChatHistoryService aiChatHistoryService; + + @Autowired + public AICheckScriptServiceImpl(AIPromptService aiPromptService, + AIService aiService, + AIChatHistoryService aiChatHistoryService) { + this.aiPromptService = aiPromptService; + this.aiService = aiService; + this.aiChatHistoryService = aiChatHistoryService; + } + + @Override + public AIAnswer check(String username, Integer type, String scriptContent) { + long startTime = System.currentTimeMillis(); + Pair pair = aiPromptService.getCheckScriptPrompt(type, scriptContent); + String rawPrompt = pair.getLeft(); + String renderedPrompt = pair.getRight(); + AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); + AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( + username, + startTime, + rawPrompt, + renderedPrompt, + aiAnswer + ); + aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + return aiAnswer; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java new file mode 100644 index 0000000000..bec4a1bdc7 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java @@ -0,0 +1,79 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.consts.PromptTemplateCodeEnum; +import com.tencent.bk.job.analysis.dao.AIPromptTemplateDAO; +import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; +import com.tencent.bk.job.analysis.service.ai.AIPromptService; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.InternalException; +import com.tencent.bk.job.common.util.JobContextUtil; +import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class AIPromptServiceImpl implements AIPromptService { + + private final AIPromptTemplateDAO aiPromptTemplateDAO; + + @Autowired + public AIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + this.aiPromptTemplateDAO = aiPromptTemplateDAO; + } + + @Override + public Pair getCheckScriptPrompt(Integer type, String scriptContent) { + String userLang = JobContextUtil.getUserLang(); + AIPromptTemplateDTO promptTemplate = aiPromptTemplateDAO.getAIPromptTemplate( + PromptTemplateCodeEnum.CHECK_SCRIPT.name(), + userLang + ); + if (promptTemplate == null) { + String message = "Cannot find prompt template for (code=CHECK_SCRIPT, userLang=" + userLang + "), " + + "please check template config in DB"; + throw new InternalException(message, ErrorCode.INTERNAL_ERROR); + } + log.info( + "Use prompt template [{}(id={})], userLang={}", + promptTemplate.getName(), + promptTemplate.getId(), + userLang + ); + String renderedPrompt = renderPrompt(promptTemplate.getTemplate(), type, scriptContent); + return Pair.of(promptTemplate.getRawPrompt(), renderedPrompt); + } + + private String renderPrompt(String promptTemplateContent, Integer type, String scriptContent) { + String scriptTypeName = ScriptTypeEnum.getName(type); + return promptTemplateContent + .replace("{script_type}", scriptTypeName) + .replace("{script_content}", scriptContent); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java index 3779417b53..0a07b6d072 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java @@ -64,6 +64,11 @@ public AIAnswer getAIAnswer(List chatHistoryDTOList, String us ); } + @Override + public AIAnswer getAIAnswer(String userInput) { + return getAIAnswer(null, userInput); + } + private List buildMessageHistoryList(List chatHistoryDTOList) { if (CollectionUtils.isEmpty(chatHistoryDTOList)) { return Collections.emptyList(); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 5a1ec8d9f7..2e2d697d20 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -58,7 +58,13 @@ public AIAnswer chatWithAI(String username, String userInput) { // 2.调用AI服务获取回答 AIAnswer aiAnswer = aiService.getAIAnswer(chatHistoryDTOList, userInput); // 3.保存聊天记录 - AIChatHistoryDTO aiChatHistoryDTO = buildAIChatHistoryDTO(username, startTime, userInput, aiAnswer); + AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( + username, + startTime, + userInput, + userInput, + aiAnswer + ); aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); return aiAnswer; } @@ -68,22 +74,4 @@ public List getLatestChatHistoryList(String username, Integer return aiChatHistoryService.getLatestChatHistoryList(username, start, length); } - private AIChatHistoryDTO buildAIChatHistoryDTO(String username, - Long startTime, - String userInput, - AIAnswer aiAnswer) { - AIChatHistoryDTO aiChatHistoryDTO = new AIChatHistoryDTO(); - aiChatHistoryDTO.setUsername(username); - aiChatHistoryDTO.setUserInput(userInput); - aiChatHistoryDTO.setPromptTemplateId(null); - aiChatHistoryDTO.setAiInput(userInput); - aiChatHistoryDTO.setAiAnswer(aiAnswer.getContent()); - aiChatHistoryDTO.setErrorCode(String.valueOf(aiAnswer.getErrorCode())); - aiChatHistoryDTO.setErrorMessage(aiAnswer.getErrorMessage()); - aiChatHistoryDTO.setStartTime(startTime); - aiChatHistoryDTO.setAnswerTime(System.currentTimeMillis()); - aiChatHistoryDTO.updateTotalTime(); - aiChatHistoryDTO.setIsDeleted(false); - return aiChatHistoryDTO; - } } diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index d7e58b4dbd..927039ba09 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -9,7 +9,8 @@ CREATE TABLE IF NOT EXISTS `ai_prompt_template` ( `code` varchar(255) NOT NULL COMMENT '模板代码,用于唯一标识模板', `locale` varchar(16) NOT NULL DEFAULT 'zh_CN' COMMENT '语言', `name` varchar(255) NOT NULL COMMENT '模板名称', - `content` TEXT NOT NULL COMMENT '模板内容', + `raw_prompt` varchar(255) NULL DEFAULT NULL COMMENT '不含上下文的简单提示符', + `template` TEXT NOT NULL COMMENT '模板内容', `description` TEXT COMMENT '对模板的描述', `creator` varchar(128) NOT NULL COMMENT '创建者', `last_modify_user` varchar(128) NULL DEFAULT NULL COMMENT '更新者', From b859eb1a632cd27118979609d215c32770e3ec44 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 27 Jun 2024 15:21:58 +0800 Subject: [PATCH 010/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持分析简单报错信息 --- .../consts/PromptTemplateCodeEnum.java | 4 +- .../api/web/impl/WebAIResourceImpl.java | 14 ++-- .../service/ai/AIAnalyzeErrorService.java | 39 ++++++++++ .../analysis/service/ai/AIPromptService.java | 8 +++ .../ai/impl/AIAnalyzeErrorServiceImpl.java | 72 +++++++++++++++++++ .../service/ai/impl/AIPromptServiceImpl.java | 31 +++++++- 6 files changed, 158 insertions(+), 10 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java index d303e15d25..6c38f14374 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java @@ -35,7 +35,7 @@ public enum PromptTemplateCodeEnum { */ CHECK_SCRIPT, /** - * 分析任务报错 + * 分析报错信息 */ - ANALYZE_TASK_ERROR; + ANALYZE_ERROR; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index aae2f533dd..778716be95 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -31,6 +31,7 @@ import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; import com.tencent.bk.job.analysis.service.ai.ChatService; import com.tencent.bk.job.common.model.Response; @@ -50,11 +51,15 @@ public class WebAIResourceImpl implements WebAIResource { private final ChatService chatService; private final AICheckScriptService aiCheckScriptService; + private final AIAnalyzeErrorService aiAnalyzeErrorService; @Autowired - public WebAIResourceImpl(ChatService chatService, AICheckScriptService aiCheckScriptService) { + public WebAIResourceImpl(ChatService chatService, + AICheckScriptService aiCheckScriptService, + AIAnalyzeErrorService aiAnalyzeErrorService) { this.chatService = chatService; this.aiCheckScriptService = aiCheckScriptService; + this.aiAnalyzeErrorService = aiAnalyzeErrorService; } @Override @@ -106,11 +111,8 @@ public Response analyzeError(String username, String scopeType, String scopeId, AIAnalyzeErrorReq req) { - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("让我想想..."); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); + + AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, req.getContent()); return Response.buildSuccessResp(aiAnswer); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java new file mode 100644 index 0000000000..3f8699ca68 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; + +public interface AIAnalyzeErrorService { + + /** + * AI分析报错信息 + * + * @param username 用户名 + * @param errorContent 错误内容 + * @return AI回答 + */ + AIAnswer analyze(String username, String errorContent); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java index 191df65aec..f7ee45a50a 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java @@ -36,4 +36,12 @@ public interface AIPromptService { * @return <原始提示符,通过模板渲染的AI提示符> */ Pair getCheckScriptPrompt(Integer type, String scriptContent); + + /** + * 获取分析报错信息的AI提示符 + * + * @param errorContent 报错内容 + * @return <原始提示符,通过模板渲染的AI提示符> + */ + Pair getAnalyzeErrorPrompt(String errorContent); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java new file mode 100644 index 0000000000..a5db55dcb9 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import com.tencent.bk.job.analysis.service.ai.AIPromptService; +import com.tencent.bk.job.analysis.service.ai.AIService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class AIAnalyzeErrorServiceImpl implements AIAnalyzeErrorService { + + private final AIPromptService aiPromptService; + private final AIService aiService; + private final AIChatHistoryService aiChatHistoryService; + + @Autowired + public AIAnalyzeErrorServiceImpl(AIPromptService aiPromptService, + AIService aiService, + AIChatHistoryService aiChatHistoryService) { + this.aiPromptService = aiPromptService; + this.aiService = aiService; + this.aiChatHistoryService = aiChatHistoryService; + } + + @Override + public AIAnswer analyze(String username, String errorContent) { + long startTime = System.currentTimeMillis(); + Pair pair = aiPromptService.getAnalyzeErrorPrompt(errorContent); + String rawPrompt = pair.getLeft(); + String renderedPrompt = pair.getRight(); + AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); + AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( + username, + startTime, + rawPrompt, + renderedPrompt, + aiAnswer + ); + aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + return aiAnswer; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java index bec4a1bdc7..ea097ffa57 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java @@ -66,14 +66,41 @@ public Pair getCheckScriptPrompt(Integer type, String scriptCont promptTemplate.getId(), userLang ); - String renderedPrompt = renderPrompt(promptTemplate.getTemplate(), type, scriptContent); + String renderedPrompt = renderCheckScriptPrompt(promptTemplate.getTemplate(), type, scriptContent); return Pair.of(promptTemplate.getRawPrompt(), renderedPrompt); } - private String renderPrompt(String promptTemplateContent, Integer type, String scriptContent) { + @Override + public Pair getAnalyzeErrorPrompt(String errorContent) { + String userLang = JobContextUtil.getUserLang(); + AIPromptTemplateDTO promptTemplate = aiPromptTemplateDAO.getAIPromptTemplate( + PromptTemplateCodeEnum.ANALYZE_ERROR.name(), + userLang + ); + if (promptTemplate == null) { + String message = "Cannot find prompt template for (code=ANALYZE_ERROR, userLang=" + userLang + "), " + + "please check template config in DB"; + throw new InternalException(message, ErrorCode.INTERNAL_ERROR); + } + log.info( + "Use prompt template [{}(id={})], userLang={}", + promptTemplate.getName(), + promptTemplate.getId(), + userLang + ); + String renderedPrompt = renderAnalyzeErrorPrompt(promptTemplate.getTemplate(), errorContent); + return Pair.of(promptTemplate.getRawPrompt(), renderedPrompt); + } + + private String renderCheckScriptPrompt(String promptTemplateContent, Integer type, String scriptContent) { String scriptTypeName = ScriptTypeEnum.getName(type); return promptTemplateContent .replace("{script_type}", scriptTypeName) .replace("{script_content}", scriptContent); } + + private String renderAnalyzeErrorPrompt(String promptTemplateContent, String errorContent) { + return promptTemplateContent + .replace("{error_content}", errorContent); + } } From 2ea237c24c875cf6d160e922e7ade2e63116aca5 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 17 Jul 2024 17:23:09 +0800 Subject: [PATCH 011/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加清空聊天记录接口定义 --- .../job/analysis/api/web/WebAIResource.java | 19 ++++++++ .../model/web/resp/ClearChatHistoryResp.java | 44 +++++++++++++++++++ .../api/web/impl/WebAIResourceImpl.java | 10 +++++ 3 files changed, 73 insertions(+) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/ClearChatHistoryResp.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 6b5b21558d..eef0cc4407 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -29,12 +29,14 @@ import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.common.annotation.WebAPI; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -152,4 +154,21 @@ Response analyzeError( @ApiParam(value = "AI分析报错信息参数", required = true) @RequestBody AIAnalyzeErrorReq req ); + + @ApiOperation(value = "清空聊天记录", produces = "application/json") + @DeleteMapping("/clearChatHistory") + Response clearChatHistory( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId + ); } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/ClearChatHistoryResp.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/ClearChatHistoryResp.java new file mode 100644 index 0000000000..e000cff55c --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/ClearChatHistoryResp.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("清空聊天记录响应数据") +@Data +public class ClearChatHistoryResp { + + /** + * 被清空的聊天记录数量 + */ + @ApiModelProperty(value = "被清空的聊天记录数量") + private Integer count; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 778716be95..4816e49796 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -31,6 +31,7 @@ import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; import com.tencent.bk.job.analysis.service.ai.ChatService; @@ -115,4 +116,13 @@ public Response analyzeError(String username, AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, req.getContent()); return Response.buildSuccessResp(aiAnswer); } + + @Override + public Response clearChatHistory(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId) { + return Response.buildSuccessResp(new ClearChatHistoryResp(0)); + } + } From ca14bd171f7582211b06d72b85649b1d9d873cb9 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 17 Jul 2024 17:27:39 +0800 Subject: [PATCH 012/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加接口Mock数据 --- .../api/web/impl/MockWebAIResourceImpl.java | 148 ++++++++++++++++++ .../api/web/impl/WebAIResourceImpl.java | 2 +- 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java new file mode 100644 index 0000000000..bff1a8fef3 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java @@ -0,0 +1,148 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.api.web.impl; + +import com.tencent.bk.job.analysis.api.web.WebAIResource; +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; +import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; +import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; +import com.tencent.bk.job.analysis.model.web.resp.UserInput; +import com.tencent.bk.job.common.model.Response; +import com.tencent.bk.job.common.model.dto.AppResourceScope; +import com.tencent.bk.job.common.util.ThreadUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RestController("jobAnalysisMockWebAIResourceImpl") +@Slf4j +public class MockWebAIResourceImpl implements WebAIResource { + + @Override + public Response> getAIConfig(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId) { + Map map = new HashMap<>(); + map.put("analyzeErrorLogMaxLength", 5 * 1024 * 1024L); + return Response.buildSuccessResp(map); + } + + @Override + public Response> getLatestChatHistoryList(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + Integer start, + Integer length) { + List aiChatRecordList = new ArrayList<>(); + AIChatRecord record = new AIChatRecord(); + UserInput userInput = new UserInput(); + userInput.setContent("你是谁"); + userInput.setTime(System.currentTimeMillis()); + record.setUserInput(userInput); + ThreadUtils.sleep(1000); + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("我是AI小鲸"); + aiAnswer.setErrorCode("0"); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + record.setAiAnswer(aiAnswer); + aiChatRecordList.add(record); + + AIChatRecord record2 = new AIChatRecord(); + UserInput userInput2 = new UserInput(); + userInput2.setContent("Hello"); + userInput2.setTime(System.currentTimeMillis()); + record2.setUserInput(userInput2); + ThreadUtils.sleep(1000); + AIAnswer aiAnswer2 = new AIAnswer(); + aiAnswer2.setContent("World"); + aiAnswer2.setErrorCode("0"); + aiAnswer2.setErrorMessage(null); + aiAnswer2.setTime(System.currentTimeMillis()); + record2.setAiAnswer(aiAnswer2); + aiChatRecordList.add(record2); + return Response.buildSuccessResp(aiChatRecordList); + } + + @Override + public Response generalChat(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIGeneralChatReq req) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("我是AI小鲸"); + aiAnswer.setErrorCode("0"); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + return Response.buildSuccessResp(aiAnswer); + } + + @Override + public Response checkScript(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AICheckScriptReq req) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("没什么问题"); + aiAnswer.setErrorCode("0"); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + return Response.buildSuccessResp(aiAnswer); + } + + @Override + public Response analyzeError(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIAnalyzeErrorReq req) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setContent("让我想想..."); + aiAnswer.setErrorCode("0"); + aiAnswer.setErrorMessage(null); + aiAnswer.setTime(System.currentTimeMillis()); + return Response.buildSuccessResp(aiAnswer); + } + + @Override + public Response clearChatHistory(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId) { + return Response.buildSuccessResp(new ClearChatHistoryResp(0)); + } + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 4816e49796..e7ac69155b 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -46,7 +46,7 @@ import java.util.Map; import java.util.stream.Collectors; -@RestController("jobAnalysisWebAIResource") +//@RestController("jobAnalysisWebAIResource") @Slf4j public class WebAIResourceImpl implements WebAIResource { From 4310e9008712ef5f2fdb94324fc298ef0e9279da Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 17 Jul 2024 17:37:44 +0800 Subject: [PATCH 013/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加容器化Chart配置 --- .../kubernetes/charts/bk-job/templates/configmap-common.yaml | 2 ++ support-files/kubernetes/charts/bk-job/values.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml index 1aec6eace1..f4204bd483 100644 --- a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml +++ b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml @@ -41,6 +41,8 @@ data: url: {{ .Values.bkGseApiGatewayUrl }} bkNotice: url: {{ .Values.bkNoticeApiGatewayUrl }} + bkAIDev: + url: {{ .Values.bkAIDevApiGatewayUrl }} cmdb: url: {{ .Values.bkCmdbApiGatewayUrl }} gse: diff --git a/support-files/kubernetes/charts/bk-job/values.yaml b/support-files/kubernetes/charts/bk-job/values.yaml index 7ddd98ba52..d1f9f4159b 100644 --- a/support-files/kubernetes/charts/bk-job/values.yaml +++ b/support-files/kubernetes/charts/bk-job/values.yaml @@ -429,6 +429,8 @@ bkGseApiGatewayUrl: "http://bkapi.example.com" bkNoticeApiGatewayUrl: "http://bkapi.example.com/api/bk-notice" # 蓝鲸 CMDB API Gateway url bkCmdbApiGatewayUrl: "http://bkapi.example.com/api/cmdb" +# 蓝鲸 AIDev API Gateway url +bkAIDevApiGatewayUrl: "http://bkapi.example.com/api/aidev" # 文档中心 url bkDocsCenterUrl: "https://bk.tencent.com/docs" # 问题反馈 url From 614daeb00a37f233762deba45f0832e7139b2c05 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 17 Jul 2024 17:47:37 +0800 Subject: [PATCH 014/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 解决多余的@Autowire问题 --- .../bk/job/analysis/api/web/impl/WebAIResourceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index e7ac69155b..417781d987 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -38,8 +38,6 @@ import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.List; @@ -54,7 +52,7 @@ public class WebAIResourceImpl implements WebAIResource { private final AICheckScriptService aiCheckScriptService; private final AIAnalyzeErrorService aiAnalyzeErrorService; - @Autowired + // @Autowired public WebAIResourceImpl(ChatService chatService, AICheckScriptService aiCheckScriptService, AIAnalyzeErrorService aiAnalyzeErrorService) { From 1bf33d5d6599850c1ae457c3e6b6d6004dcc5503 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 17 Jul 2024 18:58:58 +0800 Subject: [PATCH 015/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复rebase后的编译问题 --- .../bk/job/manage/dao/template/impl/TaskTemplateDAOImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateDAOImpl.java b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateDAOImpl.java index c98d73c672..bd0e4d81ae 100644 --- a/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateDAOImpl.java +++ b/src/backend/job-manage/service-job-manage/src/main/java/com/tencent/bk/job/manage/dao/template/impl/TaskTemplateDAOImpl.java @@ -26,9 +26,9 @@ import com.tencent.bk.job.common.model.BaseSearchCondition; import com.tencent.bk.job.common.model.PageData; +import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; import com.tencent.bk.job.common.util.TagUtils; import com.tencent.bk.job.manage.common.util.DbRecordMapper; -import com.tencent.bk.job.manage.common.util.JooqDataTypeUtil; import com.tencent.bk.job.manage.dao.template.TaskTemplateDAO; import com.tencent.bk.job.manage.model.dto.task.TaskTemplateInfoDTO; import com.tencent.bk.job.manage.model.query.TaskTemplateQuery; From cbdd255d33be4d3f0cca0fba530514184144aac1 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 24 Jul 2024 16:51:24 +0800 Subject: [PATCH 016/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现脚本检查功能 --- .../job/common/aidev/impl/BkAIDevClient.java | 27 +++++- .../i18n/exception/message.properties | 3 + .../i18n/exception/message_en.properties | 3 + .../i18n/exception/message_en_US.properties | 3 + .../i18n/exception/message_zh.properties | 3 + .../i18n/exception/message_zh_CN.properties | 3 + .../bk/job/common/constant/ErrorCode.java | 4 + .../esb/config/BkApiGatewayProperties.java | 10 ++ .../consts/PromptTemplateCodeEnum.java | 8 +- .../api/web/impl/WebAIResourceImpl.java | 11 ++- .../job/analysis/model/dto/AIPromptDTO.java | 54 +++++++++++ .../service/ai/AIAnalyzeErrorService.java | 8 +- ...e.java => CheckScriptAIPromptService.java} | 16 +--- .../FileTransferTaskErrorAIPromptService.java | 40 ++++++++ ...ScriptExecuteTaskErrorAIPromptService.java | 41 ++++++++ .../ai/context/TaskContextService.java | 40 ++++++++ .../context/impl/TaskContextServiceImpl.java | 94 +++++++++++++++++++ .../ai/context/model/FileTaskContext.java | 31 ++++++ .../ai/context/model/ScriptTaskContext.java | 45 +++++++++ .../service/ai/context/model/TaskContext.java | 58 ++++++++++++ .../ai/impl/AIAnalyzeErrorServiceImpl.java | 63 ++++++++----- .../service/ai/impl/AIBasePromptService.java | 65 +++++++++++++ .../service/ai/impl/AIBaseService.java | 65 +++++++++++++ .../ai/impl/AICheckScriptServiceImpl.java | 34 ++----- ...va => CheckScriptAIPromptServiceImpl.java} | 39 ++------ ...eTransferTaskErrorAIPromptServiceImpl.java | 60 ++++++++++++ ...ptExecuteTaskErrorAIPromptServiceImpl.java | 65 +++++++++++++ .../inner/ServiceStepInstanceResource.java | 56 +++++++++++ .../inner/ServiceScriptStepInstanceDTO.java | 52 ++++++++++ .../model/inner/ServiceStepInstanceDTO.java | 52 ++++++++++ .../ServiceStepInstanceResourceImpl.java | 67 +++++++++++++ .../bk/job/execute/model/StepInstanceDTO.java | 21 ++++- ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 9 ++ 33 files changed, 1042 insertions(+), 108 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptDTO.java rename src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/{AIPromptService.java => CheckScriptAIPromptService.java} (77%) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java rename src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/{AIPromptServiceImpl.java => CheckScriptAIPromptServiceImpl.java} (68%) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceScriptStepInstanceDTO.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java create mode 100644 src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResourceImpl.java diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java index 9ae22c59f5..b0c3e6f19e 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java @@ -50,6 +50,7 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; @@ -61,6 +62,7 @@ public class BkAIDevClient extends BkApiClient implements IBkAIDevClient { "/aidev/intelligence/raw_service/model-self_host-hunyuan-ChatCompletion/execute/"; private final AppProperties appProperties; + private final BkApiGatewayProperties.ApiGwConfig bkAIDevConfig; private final CustomPaasLoginProperties customPaasLoginProperties; public BkAIDevClient(MeterRegistry meterRegistry, @@ -74,6 +76,7 @@ public BkAIDevClient(MeterRegistry meterRegistry, HttpHelperFactory.getDefaultHttpHelper() ); this.appProperties = appProperties; + this.bkAIDevConfig = bkApiGatewayProperties.getBkAIDev(); this.customPaasLoginProperties = customPaasLoginProperties; } @@ -130,19 +133,35 @@ private AIDevReq buildAIDevReq(List messageHistoryList, String use private BkApiAuthorization buildAuthorization(String token) { if (customPaasLoginProperties.isEnabled()) { return BkApiAuthorization.bkTicketUserAuthorization( - appProperties.getCode(), - appProperties.getSecret(), + getAppCode(), + getAppSecret(), token ); } else { return BkApiAuthorization.bkTokenUserAuthorization( - appProperties.getCode(), - appProperties.getSecret(), + getAppCode(), + getAppSecret(), token ); } } + private String getAppCode() { + String appCode = bkAIDevConfig.getAppCode(); + if (StringUtils.isNotBlank(appCode)) { + return appCode; + } + return appProperties.getCode(); + } + + private String getAppSecret() { + String appCode = bkAIDevConfig.getAppCode(); + if (StringUtils.isNotBlank(appCode)) { + return appCode; + } + return appProperties.getSecret(); + } + /** * 通过ApiGateway请求AIDev API的统一入口,监控数据埋点位置 * diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties index 3877f15edb..9967f27d76 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties @@ -230,6 +230,9 @@ ## 业务错误-升级迁移 1263001=迁移失败,任务: {0}, 详情: {1} +## 业务错误-统计分析服务(job-analysis) +1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 + ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 1247403=无该应用访问权限 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties index 76f0482b6b..d3d9649ae0 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties @@ -230,6 +230,9 @@ ## Business error (migration) 1263001=Migration fail,task: {0}, detail: {1} +## Business error (job-analysis) +1264001=Analyze task error only support Script or File step + ## Business error - User/Login 1247001=User does not exist or is not logged in 1247403=No access permission for this application diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties index 76f0482b6b..d3d9649ae0 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties @@ -230,6 +230,9 @@ ## Business error (migration) 1263001=Migration fail,task: {0}, detail: {1} +## Business error (job-analysis) +1264001=Analyze task error only support Script or File step + ## Business error - User/Login 1247001=User does not exist or is not logged in 1247403=No access permission for this application diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties index 3877f15edb..9967f27d76 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties @@ -230,6 +230,9 @@ ## 业务错误-升级迁移 1263001=迁移失败,任务: {0}, 详情: {1} +## 业务错误-统计分析服务(job-analysis) +1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 + ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 1247403=无该应用访问权限 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties index 3c5026bbba..fb1a7ddb3a 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties @@ -231,6 +231,9 @@ ## 业务错误-升级迁移 1263001=迁移失败,任务: {0}, 详情: {1} +## 统计分析服务job-analysis错误码 +1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 + ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 1247403=无该应用访问权限 diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java index 2b62b4b24b..cff732422d 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java @@ -456,4 +456,8 @@ public class ErrorCode { // 迁移失败,任务: {0}, 详情: {1} public static final int MIGRATION_FAIL = 1263001; + // 统计分析服务job-analysis错误码 start + // AI分析任务报错信息仅支持脚本或文件任务步骤 + public static final int AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP = 1264001; + // 统计分析服务job-analysis错误码 end } diff --git a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java index 9834eafbb0..37559bcf44 100644 --- a/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java +++ b/src/backend/commons/esb-sdk/src/main/java/com/tencent/bk/job/common/esb/config/BkApiGatewayProperties.java @@ -51,5 +51,15 @@ public static class ApiGwConfig { * 蓝鲸Api Gateway url */ private String url; + + /** + * 蓝鲸Api Gateway appCode,若配置了则优先使用,覆盖app.code配置项 + */ + private String appCode; + + /** + * 蓝鲸Api Gateway appSecret,若配置了则优先使用,覆盖app.secret配置项 + */ + private String appSecret; } } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java index 6c38f14374..31cc9c1b18 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/PromptTemplateCodeEnum.java @@ -35,7 +35,11 @@ public enum PromptTemplateCodeEnum { */ CHECK_SCRIPT, /** - * 分析报错信息 + * 分析脚本执行任务报错信息 */ - ANALYZE_ERROR; + ANALYZE_SCRIPT_EXECUTE_TASK_ERROR, + /** + * 分析文件分发任务报错信息 + */ + ANALYZE_FILE_TRANSFER_TASK_ERROR } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 417781d987..3d1a749a77 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -38,13 +38,17 @@ import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -//@RestController("jobAnalysisWebAIResource") +@Primary +@RestController("jobAnalysisWebAIResource") @Slf4j public class WebAIResourceImpl implements WebAIResource { @@ -52,7 +56,7 @@ public class WebAIResourceImpl implements WebAIResource { private final AICheckScriptService aiCheckScriptService; private final AIAnalyzeErrorService aiAnalyzeErrorService; - // @Autowired + @Autowired public WebAIResourceImpl(ChatService chatService, AICheckScriptService aiCheckScriptService, AIAnalyzeErrorService aiAnalyzeErrorService) { @@ -110,8 +114,7 @@ public Response analyzeError(String username, String scopeType, String scopeId, AIAnalyzeErrorReq req) { - - AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, req.getContent()); + AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, appResourceScope.getAppId(), req); return Response.buildSuccessResp(aiAnswer); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptDTO.java new file mode 100644 index 0000000000..712f8b8ebf --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIPromptDTO.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.model.web.resp.UserInput; +import com.tencent.bk.job.common.util.json.LongTimestampSerializer; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * AI对话历史记录 + */ +@Data +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class AIPromptDTO { + + /** + * 原始提示符 + */ + private String rawPrompt; + /** + * 通过模板渲染的AI提示符 + */ + private String renderedPrompt; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java index 3f8699ca68..bbbabdd431 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.service.ai; +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; public interface AIAnalyzeErrorService { @@ -31,9 +32,10 @@ public interface AIAnalyzeErrorService { /** * AI分析报错信息 * - * @param username 用户名 - * @param errorContent 错误内容 + * @param username 用户名 + * @param appId Job业务ID + * @param req 请求内容 * @return AI回答 */ - AIAnswer analyze(String username, String errorContent); + AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java similarity index 77% rename from src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java rename to src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java index f7ee45a50a..cfd16c80e1 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIPromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/CheckScriptAIPromptService.java @@ -24,24 +24,16 @@ package com.tencent.bk.job.analysis.service.ai; -import org.apache.commons.lang3.tuple.Pair; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; -public interface AIPromptService { +public interface CheckScriptAIPromptService { /** * 获取检查脚本的AI提示符 * * @param type 脚本类型 * @param scriptContent 脚本内容 - * @return <原始提示符,通过模板渲染的AI提示符> + * @return AI提示符 */ - Pair getCheckScriptPrompt(Integer type, String scriptContent); - - /** - * 获取分析报错信息的AI提示符 - * - * @param errorContent 报错内容 - * @return <原始提示符,通过模板渲染的AI提示符> - */ - Pair getAnalyzeErrorPrompt(String errorContent); + AIPromptDTO getPrompt(Integer type, String scriptContent); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java new file mode 100644 index 0000000000..52e2533ee3 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; + +public interface FileTransferTaskErrorAIPromptService { + + /** + * 获取分析文件分发任务报错信息的AI提示符 + * + * @param context 文件分发任务上下文 + * @param errorContent 报错内容 + * @return AI提示符 + */ + AIPromptDTO getPrompt(FileTaskContext context, String errorContent); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java new file mode 100644 index 0000000000..21b2846a25 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ScriptExecuteTaskErrorAIPromptService.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai; + +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +import com.tencent.bk.job.analysis.service.ai.context.model.ScriptTaskContext; + +public interface ScriptExecuteTaskErrorAIPromptService { + + /** + * 获取分析脚本执行任务报错信息的AI提示符 + * + * @param context 脚本任务上下文 + * @param errorContent 报错内容 + * @return AI提示符 + */ + AIPromptDTO getPrompt(ScriptTaskContext context, String errorContent); + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java new file mode 100644 index 0000000000..325b52fece --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context; + +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; + +public interface TaskContextService { + + /** + * 获取脚本任务上下文 + * + * @param username 用户名 + * @param appId Job业务ID + * @param stepInstanceId 步骤实例ID + * @return 脚本任务上下文 + */ + TaskContext getTaskContext(String username, Long appId, Long stepInstanceId); +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java new file mode 100644 index 0000000000..0a49363085 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java @@ -0,0 +1,94 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.impl; + +import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; +import com.tencent.bk.job.analysis.service.ai.context.model.ScriptTaskContext; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; +import com.tencent.bk.job.analysis.service.ai.context.TaskContextService; +import com.tencent.bk.job.common.exception.ServiceException; +import com.tencent.bk.job.common.iam.exception.PermissionDeniedException; +import com.tencent.bk.job.common.iam.model.AuthResult; +import com.tencent.bk.job.common.model.InternalResponse; +import com.tencent.bk.job.common.model.error.ErrorType; +import com.tencent.bk.job.execute.api.inner.ServiceStepInstanceResource; +import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; +import com.tencent.bk.job.execute.model.inner.ServiceScriptStepInstanceDTO; +import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * 任务上下文服务 + */ +@Service +public class TaskContextServiceImpl implements TaskContextService { + + private final ServiceStepInstanceResource serviceStepInstanceResource; + + @Autowired + public TaskContextServiceImpl(ServiceStepInstanceResource serviceStepInstanceResource) { + this.serviceStepInstanceResource = serviceStepInstanceResource; + } + + @Override + public TaskContext getTaskContext(String username, Long appId, Long stepInstanceId) { + InternalResponse resp = serviceStepInstanceResource.getStepInstance( + username, + appId, + stepInstanceId + ); + if (resp.isSuccess()) { + ServiceStepInstanceDTO stepInstance = resp.getData(); + return buildTaskContext(stepInstance); + } + if (resp.getAuthResult() != null && !resp.getAuthResult().isPass()) { + throw new PermissionDeniedException(AuthResult.fromAuthResultDTO(resp.getAuthResult())); + } + throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); + } + + private TaskContext buildTaskContext(ServiceStepInstanceDTO stepInstance) { + ServiceScriptStepInstanceDTO scriptStepInstance = stepInstance.getScriptStepInstance(); + ScriptTaskContext scriptTaskContext = null; + FileTaskContext fileTaskContext = null; + StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(stepInstance.getExecuteType()); + if (stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SCRIPT + || stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SQL) { + scriptTaskContext = new ScriptTaskContext( + scriptStepInstance.getScriptType(), + scriptStepInstance.getScriptContent(), + scriptStepInstance.getScriptParam() + ); + } else if (stepExecuteTypeEnum == StepExecuteTypeEnum.SEND_FILE) { + fileTaskContext = new FileTaskContext(); + } + return new TaskContext( + stepInstance.getExecuteType(), + scriptTaskContext, + fileTaskContext + ); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java new file mode 100644 index 0000000000..df2b815273 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.Data; + +@Data +public class FileTaskContext { +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java new file mode 100644 index 0000000000..f006617ed1 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ScriptTaskContext.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@AllArgsConstructor +@Data +public class ScriptTaskContext { + /** + * 脚本类型 + */ + private Integer scriptType; + /** + * 脚本内容 + */ + private String scriptContent; + /** + * 脚本参数 + */ + private String scriptParams; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java new file mode 100644 index 0000000000..9a9078fa06 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Data; + +@AllArgsConstructor +@Data +public class TaskContext { + + /** + * 步骤类型 + */ + private Integer executeType; + /** + * 脚本任务上下文 + */ + private ScriptTaskContext scriptTaskContext; + /** + * 文件任务上下文 + */ + private FileTaskContext fileTaskContext; + + public boolean isScriptTask() { + StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(executeType); + return stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SCRIPT + || stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SQL; + } + + public boolean isFileTask() { + StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(executeType); + return stepExecuteTypeEnum == StepExecuteTypeEnum.SEND_FILE; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java index a5db55dcb9..ed0d05d06d 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -24,49 +24,64 @@ package com.tencent.bk.job.analysis.service.ai.impl; -import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; -import com.tencent.bk.job.analysis.service.ai.AIPromptService; import com.tencent.bk.job.analysis.service.ai.AIService; +import com.tencent.bk.job.analysis.service.ai.FileTransferTaskErrorAIPromptService; +import com.tencent.bk.job.analysis.service.ai.ScriptExecuteTaskErrorAIPromptService; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; +import com.tencent.bk.job.analysis.service.ai.context.TaskContextService; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.InvalidParamException; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service -public class AIAnalyzeErrorServiceImpl implements AIAnalyzeErrorService { +public class AIAnalyzeErrorServiceImpl extends AIBaseService implements AIAnalyzeErrorService { - private final AIPromptService aiPromptService; - private final AIService aiService; - private final AIChatHistoryService aiChatHistoryService; + private final TaskContextService taskContextService; + private final ScriptExecuteTaskErrorAIPromptService scriptExecuteTaskErrorAIPromptService; + private final FileTransferTaskErrorAIPromptService fileTransferTaskErrorAIPromptService; @Autowired - public AIAnalyzeErrorServiceImpl(AIPromptService aiPromptService, + public AIAnalyzeErrorServiceImpl(TaskContextService taskContextService, + ScriptExecuteTaskErrorAIPromptService scriptExecuteTaskErrorAIPromptService, + FileTransferTaskErrorAIPromptService fileTransferTaskErrorAIPromptService, AIService aiService, AIChatHistoryService aiChatHistoryService) { - this.aiPromptService = aiPromptService; - this.aiService = aiService; - this.aiChatHistoryService = aiChatHistoryService; + super(aiService, aiChatHistoryService); + this.taskContextService = taskContextService; + this.scriptExecuteTaskErrorAIPromptService = scriptExecuteTaskErrorAIPromptService; + this.fileTransferTaskErrorAIPromptService = fileTransferTaskErrorAIPromptService; } @Override - public AIAnswer analyze(String username, String errorContent) { - long startTime = System.currentTimeMillis(); - Pair pair = aiPromptService.getAnalyzeErrorPrompt(errorContent); - String rawPrompt = pair.getLeft(); - String renderedPrompt = pair.getRight(); - AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); - AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( + public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { + TaskContext taskContext = taskContextService.getTaskContext( username, - startTime, - rawPrompt, - renderedPrompt, - aiAnswer + appId, + req.getStepInstanceId() ); - aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); - return aiAnswer; + String errorContent = req.getContent(); + AIPromptDTO aiPromptDTO; + if (taskContext.isScriptTask()) { + aiPromptDTO = scriptExecuteTaskErrorAIPromptService.getPrompt( + taskContext.getScriptTaskContext(), + errorContent + ); + } else if (taskContext.isFileTask()) { + aiPromptDTO = fileTransferTaskErrorAIPromptService.getPrompt( + taskContext.getFileTaskContext(), + errorContent + ); + } else { + throw new InvalidParamException(ErrorCode.AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP); + } + return getAIAnswer(username, aiPromptDTO); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java new file mode 100644 index 0000000000..25cc8fa8c4 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBasePromptService.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.dao.AIPromptTemplateDAO; +import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.InternalException; +import com.tencent.bk.job.common.util.JobContextUtil; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class AIBasePromptService { + + private final AIPromptTemplateDAO aiPromptTemplateDAO; + + public AIBasePromptService(AIPromptTemplateDAO aiPromptTemplateDAO) { + this.aiPromptTemplateDAO = aiPromptTemplateDAO; + } + + protected AIPromptTemplateDTO getPromptTemplate(String templateCode) { + String userLang = JobContextUtil.getUserLang(); + AIPromptTemplateDTO promptTemplate = aiPromptTemplateDAO.getAIPromptTemplate( + templateCode, + userLang + ); + if (promptTemplate == null) { + String message = "Cannot find prompt template for (" + + "code=" + templateCode + + ", userLang=" + userLang + + "), please check template config in DB"; + throw new InternalException(message, ErrorCode.INTERNAL_ERROR); + } + log.info( + "Use prompt template [{}(id={})], userLang={}", + promptTemplate.getName(), + promptTemplate.getId(), + userLang + ); + return promptTemplate; + } + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java new file mode 100644 index 0000000000..20ced181ee --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import com.tencent.bk.job.analysis.service.ai.AIService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class AIBaseService { + + private final AIService aiService; + private final AIChatHistoryService aiChatHistoryService; + + @Autowired + public AIBaseService(AIService aiService, + AIChatHistoryService aiChatHistoryService) { + this.aiService = aiService; + this.aiChatHistoryService = aiChatHistoryService; + } + + public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { + long startTime = System.currentTimeMillis(); + String rawPrompt = aiPromptDTO.getRawPrompt(); + String renderedPrompt = aiPromptDTO.getRenderedPrompt(); + AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); + AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( + username, + startTime, + rawPrompt, + renderedPrompt, + aiAnswer + ); + aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + return aiAnswer; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java index 1675e4dd67..b67c9aff91 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java @@ -24,49 +24,33 @@ package com.tencent.bk.job.analysis.service.ai.impl; -import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; -import com.tencent.bk.job.analysis.service.ai.AIPromptService; import com.tencent.bk.job.analysis.service.ai.AIService; +import com.tencent.bk.job.analysis.service.ai.CheckScriptAIPromptService; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service -public class AICheckScriptServiceImpl implements AICheckScriptService { +public class AICheckScriptServiceImpl extends AIBaseService implements AICheckScriptService { - private final AIPromptService aiPromptService; - private final AIService aiService; - private final AIChatHistoryService aiChatHistoryService; + private final CheckScriptAIPromptService checkScriptAIPromptService; @Autowired - public AICheckScriptServiceImpl(AIPromptService aiPromptService, + public AICheckScriptServiceImpl(CheckScriptAIPromptService checkScriptAIPromptService, AIService aiService, AIChatHistoryService aiChatHistoryService) { - this.aiPromptService = aiPromptService; - this.aiService = aiService; - this.aiChatHistoryService = aiChatHistoryService; + super(aiService, aiChatHistoryService); + this.checkScriptAIPromptService = checkScriptAIPromptService; } @Override public AIAnswer check(String username, Integer type, String scriptContent) { - long startTime = System.currentTimeMillis(); - Pair pair = aiPromptService.getCheckScriptPrompt(type, scriptContent); - String rawPrompt = pair.getLeft(); - String renderedPrompt = pair.getRight(); - AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); - AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( - username, - startTime, - rawPrompt, - renderedPrompt, - aiAnswer - ); - aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); - return aiAnswer; + AIPromptDTO aiPromptDTO = checkScriptAIPromptService.getPrompt(type, scriptContent); + return getAIAnswer(username, aiPromptDTO); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java similarity index 68% rename from src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java rename to src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java index ea097ffa57..9314c10960 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java @@ -26,30 +26,30 @@ import com.tencent.bk.job.analysis.consts.PromptTemplateCodeEnum; import com.tencent.bk.job.analysis.dao.AIPromptTemplateDAO; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; -import com.tencent.bk.job.analysis.service.ai.AIPromptService; +import com.tencent.bk.job.analysis.service.ai.CheckScriptAIPromptService; import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.InternalException; import com.tencent.bk.job.common.util.JobContextUtil; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Slf4j @Service -public class AIPromptServiceImpl implements AIPromptService { +public class CheckScriptAIPromptServiceImpl implements CheckScriptAIPromptService { private final AIPromptTemplateDAO aiPromptTemplateDAO; @Autowired - public AIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + public CheckScriptAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { this.aiPromptTemplateDAO = aiPromptTemplateDAO; } @Override - public Pair getCheckScriptPrompt(Integer type, String scriptContent) { + public AIPromptDTO getPrompt(Integer type, String scriptContent) { String userLang = JobContextUtil.getUserLang(); AIPromptTemplateDTO promptTemplate = aiPromptTemplateDAO.getAIPromptTemplate( PromptTemplateCodeEnum.CHECK_SCRIPT.name(), @@ -67,29 +67,7 @@ public Pair getCheckScriptPrompt(Integer type, String scriptCont userLang ); String renderedPrompt = renderCheckScriptPrompt(promptTemplate.getTemplate(), type, scriptContent); - return Pair.of(promptTemplate.getRawPrompt(), renderedPrompt); - } - - @Override - public Pair getAnalyzeErrorPrompt(String errorContent) { - String userLang = JobContextUtil.getUserLang(); - AIPromptTemplateDTO promptTemplate = aiPromptTemplateDAO.getAIPromptTemplate( - PromptTemplateCodeEnum.ANALYZE_ERROR.name(), - userLang - ); - if (promptTemplate == null) { - String message = "Cannot find prompt template for (code=ANALYZE_ERROR, userLang=" + userLang + "), " + - "please check template config in DB"; - throw new InternalException(message, ErrorCode.INTERNAL_ERROR); - } - log.info( - "Use prompt template [{}(id={})], userLang={}", - promptTemplate.getName(), - promptTemplate.getId(), - userLang - ); - String renderedPrompt = renderAnalyzeErrorPrompt(promptTemplate.getTemplate(), errorContent); - return Pair.of(promptTemplate.getRawPrompt(), renderedPrompt); + return new AIPromptDTO(promptTemplate.getRawPrompt(), renderedPrompt); } private String renderCheckScriptPrompt(String promptTemplateContent, Integer type, String scriptContent) { @@ -98,9 +76,4 @@ private String renderCheckScriptPrompt(String promptTemplateContent, Integer typ .replace("{script_type}", scriptTypeName) .replace("{script_content}", scriptContent); } - - private String renderAnalyzeErrorPrompt(String promptTemplateContent, String errorContent) { - return promptTemplateContent - .replace("{error_content}", errorContent); - } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java new file mode 100644 index 0000000000..33ffe00915 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java @@ -0,0 +1,60 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.consts.PromptTemplateCodeEnum; +import com.tencent.bk.job.analysis.dao.AIPromptTemplateDAO; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; +import com.tencent.bk.job.analysis.service.ai.FileTransferTaskErrorAIPromptService; +import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class FileTransferTaskErrorAIPromptServiceImpl extends AIBasePromptService + implements FileTransferTaskErrorAIPromptService { + + @Autowired + public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + super(aiPromptTemplateDAO); + } + + @Override + public AIPromptDTO getPrompt(FileTaskContext context, String errorContent) { + String templateCode = PromptTemplateCodeEnum.ANALYZE_FILE_TRANSFER_TASK_ERROR.name(); + AIPromptTemplateDTO promptTemplate = getPromptTemplate(templateCode); + String renderedPrompt = renderPrompt(promptTemplate.getTemplate(), context, errorContent); + return new AIPromptDTO(promptTemplate.getRawPrompt(), renderedPrompt); + } + + private String renderPrompt(String promptTemplateContent, FileTaskContext context, String errorContent) { + // TODO:补充文件任务报错分析模板渲染相关内容 + return promptTemplateContent + .replace("{error_content}", errorContent); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java new file mode 100644 index 0000000000..e8c1728309 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.consts.PromptTemplateCodeEnum; +import com.tencent.bk.job.analysis.dao.AIPromptTemplateDAO; +import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; +import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; +import com.tencent.bk.job.analysis.service.ai.ScriptExecuteTaskErrorAIPromptService; +import com.tencent.bk.job.analysis.service.ai.context.model.ScriptTaskContext; +import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class ScriptExecuteTaskErrorAIPromptServiceImpl extends AIBasePromptService + implements ScriptExecuteTaskErrorAIPromptService { + + @Autowired + public ScriptExecuteTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + super(aiPromptTemplateDAO); + } + + @Override + public AIPromptDTO getPrompt(ScriptTaskContext context, String errorContent) { + String templateCode = PromptTemplateCodeEnum.ANALYZE_SCRIPT_EXECUTE_TASK_ERROR.name(); + AIPromptTemplateDTO promptTemplate = getPromptTemplate(templateCode); + String renderedPrompt = renderPrompt(promptTemplate.getTemplate(), context, errorContent); + return new AIPromptDTO(promptTemplate.getRawPrompt(), renderedPrompt); + } + + private String renderPrompt(String promptTemplateContent, + ScriptTaskContext context, + String errorContent) { + return promptTemplateContent + .replace("{script_type}}", ScriptTypeEnum.getName(context.getScriptType())) + .replace("{script_content}", context.getScriptContent()) + .replace("{script_params}", context.getScriptParams()) + .replace("{error_content}", errorContent); + } +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java new file mode 100644 index 0000000000..9b638dabd6 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.api.inner; + +import com.tencent.bk.job.common.annotation.InternalAPI; +import com.tencent.bk.job.common.model.InternalResponse; +import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; +import com.tentent.bk.job.common.api.feign.annotation.SmartFeignClient; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiParam; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * 步骤实例API-服务内部调用 + */ +@Api(tags = {"StepInstance"}) +@SmartFeignClient(value = "job-execute", contextId = "stepInstanceResource") +@InternalAPI +@RequestMapping("/service/stepInstance") +public interface ServiceStepInstanceResource { + @GetMapping("/appIds/{appId}/stepInstanceIds/{stepInstanceId}") + InternalResponse getStepInstance( + @RequestHeader("username") + String username, + @ApiParam(value = "作业平台业务ID", required = true) + @PathVariable(value = "appId") + Long appId, + @ApiParam(value = "步骤实例ID", name = "stepInstanceId", required = true) + @PathVariable("stepInstanceId") + Long stepInstanceId); +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceScriptStepInstanceDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceScriptStepInstanceDTO.java new file mode 100644 index 0000000000..d59763b97c --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceScriptStepInstanceDTO.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +@ApiModel("脚本步骤实例") +@Data +public class ServiceScriptStepInstanceDTO { + /** + * 步骤实例ID + */ + private Long stepInstanceId; + + /** + * 脚本类型:1(shell脚本)、2(bat脚本)、3(perl脚本)、4(python脚本)、5(powershell脚本)、6(SQL脚本) + */ + private Integer scriptType; + + /** + * 脚本内容 + */ + private String scriptContent; + + /** + * 脚本参数 + */ + private String scriptParam; +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java new file mode 100644 index 0000000000..1824d647a1 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@ApiModel("步骤实例") +@Data +public class ServiceStepInstanceDTO { + /** + * 步骤实例ID + */ + @ApiModelProperty("步骤实例ID") + private Long id; + + /** + * 步骤类型 + */ + @ApiModelProperty("步骤类型") + private Integer executeType; + + /** + * 步骤类型 + */ + @ApiModelProperty("脚本任务步骤") + private ServiceScriptStepInstanceDTO scriptStepInstance; + +} diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResourceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResourceImpl.java new file mode 100644 index 0000000000..1b9fb10d5b --- /dev/null +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResourceImpl.java @@ -0,0 +1,67 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.api.inner; + +import com.tencent.bk.job.common.exception.NotFoundException; +import com.tencent.bk.job.common.iam.exception.PermissionDeniedException; +import com.tencent.bk.job.common.iam.model.AuthResult; +import com.tencent.bk.job.common.model.InternalResponse; +import com.tencent.bk.job.common.model.iam.AuthResultDTO; +import com.tencent.bk.job.execute.model.StepInstanceDTO; +import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; +import com.tencent.bk.job.execute.service.StepInstanceService; +import com.tencent.bk.job.execute.service.TaskInstanceAccessProcessor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +public class ServiceStepInstanceResourceImpl implements ServiceStepInstanceResource { + + private final TaskInstanceAccessProcessor taskInstanceAccessProcessor; + private final StepInstanceService stepInstanceService; + + @Autowired + public ServiceStepInstanceResourceImpl(TaskInstanceAccessProcessor taskInstanceAccessProcessor, + StepInstanceService stepInstanceService) { + this.taskInstanceAccessProcessor = taskInstanceAccessProcessor; + this.stepInstanceService = stepInstanceService; + } + + @Override + public InternalResponse getStepInstance(String username, Long appId, Long stepInstanceId) { + try { + StepInstanceDTO stepInstance = stepInstanceService.getStepInstanceDetail(appId, stepInstanceId); + taskInstanceAccessProcessor.processBeforeAccess(username, appId, stepInstance.getTaskInstanceId()); + return InternalResponse.buildSuccessResp(stepInstance.toServiceStepInstanceDTO()); + } catch (NotFoundException e) { + return InternalResponse.buildCommonFailResp(e); + } catch (PermissionDeniedException e) { + AuthResultDTO authResult = AuthResult.toAuthResultDTO(e.getAuthResult()); + return InternalResponse.buildAuthFailResp(authResult); + } + } +} diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java index a8cbf6476f..69b2ae5b97 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java @@ -27,6 +27,9 @@ import com.tencent.bk.job.common.constant.DuplicateHandlerEnum; import com.tencent.bk.job.common.model.dto.Container; import com.tencent.bk.job.common.model.dto.HostDTO; +import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; +import com.tencent.bk.job.execute.model.inner.ServiceScriptStepInstanceDTO; +import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import lombok.Getter; import lombok.Setter; @@ -294,7 +297,8 @@ public void buildStepFinalExecuteObjects(boolean isSupportExecuteObjectFeature) /** * 遍历步骤实例包含的所有执行目标(执行目标+文件分发源执行目标) - * @param consumer + * + * @param consumer 消费者 */ public void forEachExecuteObjects(Consumer consumer) { if (targetExecuteObjects != null) { @@ -309,4 +313,19 @@ public void forEachExecuteObjects(Consumer consumer) { } } } + + public ServiceStepInstanceDTO toServiceStepInstanceDTO() { + ServiceStepInstanceDTO serviceStepInstanceDTO = new ServiceStepInstanceDTO(); + serviceStepInstanceDTO.setId(id); + serviceStepInstanceDTO.setExecuteType(executeType.getValue()); + if (executeType == StepExecuteTypeEnum.EXECUTE_SCRIPT || executeType == StepExecuteTypeEnum.EXECUTE_SQL) { + ServiceScriptStepInstanceDTO scriptStepInstance = new ServiceScriptStepInstanceDTO(); + scriptStepInstance.setStepInstanceId(id); + scriptStepInstance.setScriptType(scriptType.getValue()); + scriptStepInstance.setScriptContent(scriptContent); + scriptStepInstance.setScriptParam(scriptParam); + serviceStepInstanceDTO.setScriptStepInstance(scriptStepInstance); + } + return serviceStepInstanceDTO; + } } diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index 927039ba09..d43d9f4df9 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -47,3 +47,12 @@ CREATE TABLE IF NOT EXISTS `ai_chat_history` ( ) ENGINE = InnoDB CHARACTER SET = utf8mb4; +-- ------------------------ +-- 插入初始化数据 +-- ------------------------ +REPLACE INTO job_analysis.ai_prompt_template +(id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) +VALUES(1, 'CHECK_SCRIPT', 'zh_CN', '检查脚本是否有语法问题', '帮我检查一下我写的脚本有没有语法问题。', '已知脚本内容如下:\\n``````{script_type}\\n{script_content}\\n``````\\n帮我检查一下我写的脚本有没有语法问题。\\n\\n回答问题时请遵守以下要求:\\n1. 指出问题时请给出对应的脚本行号,请注意首行shebang与空白的行也需要计算,没有问题的行不需要体现在回答中。', '检查脚本是否有语法问题', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +REPLACE INTO job_analysis.ai_prompt_template +(id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) +VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\\n作业平台的依赖系统介绍:\\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\\n\\n以下介绍作业平台脚本执行功能的原理:\\n【功能一:脚本批量执行】\\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\\n\\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({bk_helper_link})进行人工咨询。\\n\\n当前任务执行的脚本内容为:\\n``````{script_type}\\n{script_content}\\n``````\\n脚本参数为:\\n{script_params}\\n\\n报错信息如下:\\n{error_content}\\n\\n请回答用户的提问:\\n任务报错内容解析', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); From ff215881d917d51539aefe13cff62d1c5f958a45 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 24 Jul 2024 18:02:58 +0800 Subject: [PATCH 017/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现聊天记录删除功能 --- .../job/common/aidev/impl/BkAIDevClient.java | 6 +- .../api/web/impl/MockWebAIResourceImpl.java | 148 ------------------ .../api/web/impl/WebAIResourceImpl.java | 9 +- .../bk/job/analysis/dao/AIChatHistoryDAO.java | 9 ++ .../dao/impl/AIChatHistoryDAOImpl.java | 13 ++ .../service/ai/AIChatHistoryService.java | 8 + .../ai/impl/AIChatHistoryServiceImpl.java | 21 ++- .../inner/ServiceStepInstanceResource.java | 4 +- 8 files changed, 58 insertions(+), 160 deletions(-) delete mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java index b0c3e6f19e..795984bd4c 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java @@ -155,9 +155,9 @@ private String getAppCode() { } private String getAppSecret() { - String appCode = bkAIDevConfig.getAppCode(); - if (StringUtils.isNotBlank(appCode)) { - return appCode; + String appSecret = bkAIDevConfig.getAppSecret(); + if (StringUtils.isNotBlank(appSecret)) { + return appSecret; } return appProperties.getSecret(); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java deleted file mode 100644 index bff1a8fef3..0000000000 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/MockWebAIResourceImpl.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. - * - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. - * - * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. - * - * License for BK-JOB蓝鲸智云作业平台: - * -------------------------------------------------------------------- - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and - * to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of - * the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -package com.tencent.bk.job.analysis.api.web.impl; - -import com.tencent.bk.job.analysis.api.web.WebAIResource; -import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; -import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; -import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; -import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; -import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; -import com.tencent.bk.job.analysis.model.web.resp.UserInput; -import com.tencent.bk.job.common.model.Response; -import com.tencent.bk.job.common.model.dto.AppResourceScope; -import com.tencent.bk.job.common.util.ThreadUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.RestController; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RestController("jobAnalysisMockWebAIResourceImpl") -@Slf4j -public class MockWebAIResourceImpl implements WebAIResource { - - @Override - public Response> getAIConfig(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId) { - Map map = new HashMap<>(); - map.put("analyzeErrorLogMaxLength", 5 * 1024 * 1024L); - return Response.buildSuccessResp(map); - } - - @Override - public Response> getLatestChatHistoryList(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - Integer start, - Integer length) { - List aiChatRecordList = new ArrayList<>(); - AIChatRecord record = new AIChatRecord(); - UserInput userInput = new UserInput(); - userInput.setContent("你是谁"); - userInput.setTime(System.currentTimeMillis()); - record.setUserInput(userInput); - ThreadUtils.sleep(1000); - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("我是AI小鲸"); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); - record.setAiAnswer(aiAnswer); - aiChatRecordList.add(record); - - AIChatRecord record2 = new AIChatRecord(); - UserInput userInput2 = new UserInput(); - userInput2.setContent("Hello"); - userInput2.setTime(System.currentTimeMillis()); - record2.setUserInput(userInput2); - ThreadUtils.sleep(1000); - AIAnswer aiAnswer2 = new AIAnswer(); - aiAnswer2.setContent("World"); - aiAnswer2.setErrorCode("0"); - aiAnswer2.setErrorMessage(null); - aiAnswer2.setTime(System.currentTimeMillis()); - record2.setAiAnswer(aiAnswer2); - aiChatRecordList.add(record2); - return Response.buildSuccessResp(aiChatRecordList); - } - - @Override - public Response generalChat(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIGeneralChatReq req) { - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("我是AI小鲸"); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); - return Response.buildSuccessResp(aiAnswer); - } - - @Override - public Response checkScript(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AICheckScriptReq req) { - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("没什么问题"); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); - return Response.buildSuccessResp(aiAnswer); - } - - @Override - public Response analyzeError(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIAnalyzeErrorReq req) { - AIAnswer aiAnswer = new AIAnswer(); - aiAnswer.setContent("让我想想..."); - aiAnswer.setErrorCode("0"); - aiAnswer.setErrorMessage(null); - aiAnswer.setTime(System.currentTimeMillis()); - return Response.buildSuccessResp(aiAnswer); - } - - @Override - public Response clearChatHistory(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId) { - return Response.buildSuccessResp(new ClearChatHistoryResp(0)); - } - -} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 3d1a749a77..db1f2606c9 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -33,6 +33,7 @@ import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; import com.tencent.bk.job.analysis.service.ai.ChatService; import com.tencent.bk.job.common.model.Response; @@ -55,14 +56,17 @@ public class WebAIResourceImpl implements WebAIResource { private final ChatService chatService; private final AICheckScriptService aiCheckScriptService; private final AIAnalyzeErrorService aiAnalyzeErrorService; + private final AIChatHistoryService aiChatHistoryService; @Autowired public WebAIResourceImpl(ChatService chatService, AICheckScriptService aiCheckScriptService, - AIAnalyzeErrorService aiAnalyzeErrorService) { + AIAnalyzeErrorService aiAnalyzeErrorService, + AIChatHistoryService aiChatHistoryService) { this.chatService = chatService; this.aiCheckScriptService = aiCheckScriptService; this.aiAnalyzeErrorService = aiAnalyzeErrorService; + this.aiChatHistoryService = aiChatHistoryService; } @Override @@ -123,7 +127,8 @@ public Response clearChatHistory(String username, AppResourceScope appResourceScope, String scopeType, String scopeId) { - return Response.buildSuccessResp(new ClearChatHistoryResp(0)); + int deletedTotalCount = aiChatHistoryService.softDeleteChatHistory(username); + return Response.buildSuccessResp(new ClearChatHistoryResp(deletedTotalCount)); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java index bb09b47c2c..f47380dfec 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java @@ -46,4 +46,13 @@ public interface AIChatHistoryDAO { * @return 最近的聊天记录列表 */ List getLatestChatHistoryList(String username, Integer start, Integer length); + + /** + * 软删除聊天记录(优先删除创建时间较早的) + * + * @param username 用户名 + * @param limit 最大删除数量 + * @return 删除的记录条数 + */ + int softDeleteChatHistory(String username, Integer limit); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index 2be7381d0b..a05e655046 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -110,6 +110,19 @@ public List getLatestChatHistoryList(String username, Integer return listByConditions(conditions, start, length); } + @Override + public int softDeleteChatHistory(String username, Integer limit) { + Collection conditions = new ArrayList<>(); + conditions.add(defaultTable.USERNAME.eq(username)); + conditions.add(defaultTable.IS_DELETED.eq(JooqDataTypeUtil.buildUByte(0))); + return dslContext.update(defaultTable) + .set(defaultTable.IS_DELETED, JooqDataTypeUtil.buildUByte(1)) + .where(conditions) + .orderBy(defaultTable.START_TIME) + .limit(limit) + .execute(); + } + private List listByConditions(Collection conditions, Integer start, Integer length) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java index 14a93a7437..2599f442b6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -63,4 +63,12 @@ AIChatHistoryDTO buildAIChatHistoryDTO(String username, * @return 最近的聊天记录列表 */ List getLatestChatHistoryList(String username, Integer start, Integer length); + + /** + * 软删除聊天记录 + * + * @param username 用户名 + * @return 删除的聊天记录数量 + */ + int softDeleteChatHistory(String username); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index d0574770be..0d7c9374ab 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -47,10 +47,10 @@ public AIChatHistoryServiceImpl(AIChatHistoryDAO aiChatHistoryDAO) { @Override public AIChatHistoryDTO buildAIChatHistoryDTO(String username, - Long startTime, - String userInput, - String aiInput, - AIAnswer aiAnswer) { + Long startTime, + String userInput, + String aiInput, + AIAnswer aiAnswer) { AIChatHistoryDTO aiChatHistoryDTO = new AIChatHistoryDTO(); aiChatHistoryDTO.setUsername(username); aiChatHistoryDTO.setUserInput(userInput); @@ -75,4 +75,17 @@ public Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { public List getLatestChatHistoryList(String username, Integer start, Integer length) { return aiChatHistoryDAO.getLatestChatHistoryList(username, start, length); } + + @Override + public int softDeleteChatHistory(String username) { + int batchSize = 10000; + int deletedCount = 0; + int deletedTotalCount = 0; + do { + deletedCount = aiChatHistoryDAO.softDeleteChatHistory(username, batchSize); + deletedTotalCount += deletedCount; + } while (deletedCount == batchSize); + log.info("{} chat history of user {} soft deleted", deletedTotalCount, username); + return deletedTotalCount; + } } diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java index 9b638dabd6..378463ef1d 100644 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepInstanceResource.java @@ -33,7 +33,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; /** * 步骤实例API-服务内部调用 @@ -41,9 +40,8 @@ @Api(tags = {"StepInstance"}) @SmartFeignClient(value = "job-execute", contextId = "stepInstanceResource") @InternalAPI -@RequestMapping("/service/stepInstance") public interface ServiceStepInstanceResource { - @GetMapping("/appIds/{appId}/stepInstanceIds/{stepInstanceId}") + @GetMapping("/service/stepInstance/appIds/{appId}/stepInstanceIds/{stepInstanceId}") InternalResponse getStepInstance( @RequestHeader("username") String username, From 5f6fc4acb5b1447c069f43c1ed114029e8a9be72 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Fri, 26 Jul 2024 14:47:12 +0800 Subject: [PATCH 018/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化模板占位符实现,防止与业务内容文本冲突 --- .../service/ai/impl/AITemplateVarService.java | 55 +++++++++++++++++++ .../impl/CheckScriptAIPromptServiceImpl.java | 9 ++- ...eTransferTaskErrorAIPromptServiceImpl.java | 11 +++- ...ptExecuteTaskErrorAIPromptServiceImpl.java | 17 ++++-- ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 4 +- 5 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java new file mode 100644 index 0000000000..ed205d0376 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import org.springframework.stereotype.Service; + +/** + * AI模板变量服务,统一提供模板变量占位符 + */ +@Service +public class AITemplateVarService { + + public String getTemplateVarPlaceHolder(String varName) { + String TEMPLATE_VAR_PREFIX = "{BK_JOB_AI_TEMPLATE_VAR"; + return TEMPLATE_VAR_PREFIX + "{" + varName + "}}"; + } + + public String getScriptTypePlaceHolder() { + return getTemplateVarPlaceHolder("script_type"); + } + + public String getScriptParamsPlaceHolder() { + return getTemplateVarPlaceHolder("script_params"); + } + + public String getScriptContentPlaceHolder() { + return getTemplateVarPlaceHolder("script_content"); + } + + public String getErrorContentPlaceHolder() { + return getTemplateVarPlaceHolder("error_content"); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java index 9314c10960..8dd042080f 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/CheckScriptAIPromptServiceImpl.java @@ -42,10 +42,13 @@ public class CheckScriptAIPromptServiceImpl implements CheckScriptAIPromptService { private final AIPromptTemplateDAO aiPromptTemplateDAO; + private final AITemplateVarService aiTemplateVarService; @Autowired - public CheckScriptAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + public CheckScriptAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO, + AITemplateVarService aiTemplateVarService) { this.aiPromptTemplateDAO = aiPromptTemplateDAO; + this.aiTemplateVarService = aiTemplateVarService; } @Override @@ -73,7 +76,7 @@ public AIPromptDTO getPrompt(Integer type, String scriptContent) { private String renderCheckScriptPrompt(String promptTemplateContent, Integer type, String scriptContent) { String scriptTypeName = ScriptTypeEnum.getName(type); return promptTemplateContent - .replace("{script_type}", scriptTypeName) - .replace("{script_content}", scriptContent); + .replace(aiTemplateVarService.getScriptTypePlaceHolder(), scriptTypeName) + .replace(aiTemplateVarService.getScriptContentPlaceHolder(), scriptContent); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java index 33ffe00915..9a6c525b12 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java @@ -34,14 +34,21 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +/** + * 文件分发任务报错分析AI提示符服务 + */ @Slf4j @Service public class FileTransferTaskErrorAIPromptServiceImpl extends AIBasePromptService implements FileTransferTaskErrorAIPromptService { + private final AITemplateVarService aiTemplateVarService; + @Autowired - public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO, + AITemplateVarService aiTemplateVarService) { super(aiPromptTemplateDAO); + this.aiTemplateVarService = aiTemplateVarService; } @Override @@ -55,6 +62,6 @@ public AIPromptDTO getPrompt(FileTaskContext context, String errorContent) { private String renderPrompt(String promptTemplateContent, FileTaskContext context, String errorContent) { // TODO:补充文件任务报错分析模板渲染相关内容 return promptTemplateContent - .replace("{error_content}", errorContent); + .replace(aiTemplateVarService.getErrorContentPlaceHolder(), errorContent); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java index e8c1728309..7baad0b751 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java @@ -35,14 +35,21 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +/** + * 脚本任务报错检查AI提示符服务 + */ @Slf4j @Service public class ScriptExecuteTaskErrorAIPromptServiceImpl extends AIBasePromptService implements ScriptExecuteTaskErrorAIPromptService { + private final AITemplateVarService aiTemplateVarService; + @Autowired - public ScriptExecuteTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO) { + public ScriptExecuteTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO, + AITemplateVarService aiTemplateVarService) { super(aiPromptTemplateDAO); + this.aiTemplateVarService = aiTemplateVarService; } @Override @@ -57,9 +64,9 @@ private String renderPrompt(String promptTemplateContent, ScriptTaskContext context, String errorContent) { return promptTemplateContent - .replace("{script_type}}", ScriptTypeEnum.getName(context.getScriptType())) - .replace("{script_content}", context.getScriptContent()) - .replace("{script_params}", context.getScriptParams()) - .replace("{error_content}", errorContent); + .replace(aiTemplateVarService.getScriptTypePlaceHolder(), ScriptTypeEnum.getName(context.getScriptType())) + .replace(aiTemplateVarService.getScriptParamsPlaceHolder(), context.getScriptParams()) + .replace(aiTemplateVarService.getErrorContentPlaceHolder(), errorContent) + .replace(aiTemplateVarService.getScriptContentPlaceHolder(), context.getScriptContent()); } } diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index d43d9f4df9..9435fd38d3 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -52,7 +52,7 @@ CHARACTER SET = utf8mb4; -- ------------------------ REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) -VALUES(1, 'CHECK_SCRIPT', 'zh_CN', '检查脚本是否有语法问题', '帮我检查一下我写的脚本有没有语法问题。', '已知脚本内容如下:\\n``````{script_type}\\n{script_content}\\n``````\\n帮我检查一下我写的脚本有没有语法问题。\\n\\n回答问题时请遵守以下要求:\\n1. 指出问题时请给出对应的脚本行号,请注意首行shebang与空白的行也需要计算,没有问题的行不需要体现在回答中。', '检查脚本是否有语法问题', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +VALUES(1, 'CHECK_SCRIPT', 'zh_CN', '检查脚本是否有语法问题', '帮我检查一下我写的脚本有没有语法问题。', '已知脚本内容如下:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n帮我检查一下我写的脚本有没有语法问题。\n\n回答问题时请遵守以下要求:\n1. 指出问题时请给出对应的脚本行号,请注意首行shebang与空白的行也需要计算,没有问题的行不需要体现在回答中。', '检查脚本是否有语法问题', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) -VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\\n作业平台的依赖系统介绍:\\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\\n\\n以下介绍作业平台脚本执行功能的原理:\\n【功能一:脚本批量执行】\\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\\n\\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({bk_helper_link})进行人工咨询。\\n\\n当前任务执行的脚本内容为:\\n``````{script_type}\\n{script_content}\\n``````\\n脚本参数为:\\n{script_params}\\n\\n报错信息如下:\\n{error_content}\\n\\n请回答用户的提问:\\n任务报错内容解析', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能一:脚本批量执行】\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前任务执行的脚本内容为:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n脚本参数为:\n{BK_JOB_AI_TEMPLATE_VAR{script_params}}\n\n报错信息如下:\n{BK_JOB_AI_TEMPLATE_VAR{error_content}}\n\n请回答用户的提问:\n任务报错内容解析', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); From ad915b1914280ade48e28313125a367880f83ced Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 29 Jul 2024 21:46:30 +0800 Subject: [PATCH 019/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持文件分发任务报错信息分析 --- .../service-job-analysis/build.gradle | 1 + .../FileTransferTaskErrorAIPromptService.java | 5 +- .../ai/context/TaskContextService.java | 12 +- .../constants/FileTaskErrorSourceEnum.java | 43 +++++ .../context/impl/FileTaskContextService.java | 138 +++++++++++++++ .../context/impl/FileTaskFailLogAnalyzer.java | 79 +++++++++ .../context/impl/TaskContextServiceImpl.java | 48 +++--- .../context/model/ContainerDescription.java | 42 +++++ .../context/model/DownloadFileErrorInfo.java | 78 +++++++++ .../ai/context/model/FileTaskContext.java | 162 ++++++++++++++++++ .../model/FileTaskErrorSourceResult.java | 55 ++++++ .../ai/context/model/HostDescription.java | 42 +++++ .../ai/context/model/TaskContextQuery.java | 97 +++++++++++ .../ai/context/model/UploadFileErrorInfo.java | 65 +++++++ .../ai/impl/AIAnalyzeErrorServiceImpl.java | 15 +- .../service/ai/impl/AITemplateVarService.java | 12 ++ ...eTransferTaskErrorAIPromptServiceImpl.java | 11 +- .../api/inner/ServiceStepLogResource.java | 54 ++++++ .../common/constants/FileDistStatusEnum.java | 2 +- .../common/constants/RunStatusEnum.java | 2 +- .../model/inner/ServiceExecuteObject.java | 76 ++++++++ .../model/inner/ServiceExecuteTargetDTO.java | 52 ++++++ .../model/inner/ServiceFileDetailDTO.java | 40 +++++ .../model/inner/ServiceFileSourceDTO.java | 59 +++++++ .../inner/ServiceFileStepInstanceDTO.java | 45 +++++ .../model/inner/ServiceStepInstanceDTO.java | 27 ++- .../execute/model/web/vo/ExecuteObjectVO.java | 4 + ...atchGetJobInstanceIpLogV3ResourceImpl.java | 7 +- ...bInstanceExecuteObjectLogResourceImpl.java | 7 +- .../consts/ExecuteObjectTaskStatusEnum.java | 2 +- .../execute/engine/model/ExecuteObject.java | 11 ++ .../job/execute/model/ExecuteTargetDTO.java | 14 ++ .../bk/job/execute/model/FileDetailDTO.java | 7 + .../bk/job/execute/model/FileSourceDTO.java | 17 ++ .../bk/job/execute/model/StepInstanceDTO.java | 16 ++ .../service/impl/LogExportServiceImpl.java | 7 +- .../execute/service/impl/LogServiceImpl.java | 7 +- .../bk/job/logsvr/util/LogFieldUtil.java | 46 +++++ .../bk/job/logsvr/util/LogFieldUtilTest.java | 60 +++++++ ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 3 + 40 files changed, 1391 insertions(+), 79 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ContainerDescription.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/DownloadFileErrorInfo.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskErrorSourceResult.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/HostDescription.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContextQuery.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/UploadFileErrorInfo.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteObject.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteTargetDTO.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileDetailDTO.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileSourceDTO.java create mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileStepInstanceDTO.java create mode 100644 src/backend/job-logsvr/api-job-logsvr/src/main/java/com/tencent/bk/job/logsvr/util/LogFieldUtil.java create mode 100644 src/backend/job-logsvr/boot-job-logsvr/src/test/java/com/tencent/bk/job/logsvr/util/LogFieldUtilTest.java diff --git a/src/backend/job-analysis/service-job-analysis/build.gradle b/src/backend/job-analysis/service-job-analysis/build.gradle index 38bf0992bc..d233d71bd5 100644 --- a/src/backend/job-analysis/service-job-analysis/build.gradle +++ b/src/backend/job-analysis/service-job-analysis/build.gradle @@ -28,6 +28,7 @@ dependencies { api project(":job-analysis:api-job-analysis") api project(":job-analysis:model-job-analysis") api project(":job-crontab:api-job-crontab") + api project(":job-logsvr:api-job-logsvr") api project(":commons:common") api project(":commons:common-web") api project(":commons:common-iam") diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java index 52e2533ee3..5a0941e13c 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/FileTransferTaskErrorAIPromptService.java @@ -32,9 +32,8 @@ public interface FileTransferTaskErrorAIPromptService { /** * 获取分析文件分发任务报错信息的AI提示符 * - * @param context 文件分发任务上下文 - * @param errorContent 报错内容 + * @param context 文件分发任务上下文 * @return AI提示符 */ - AIPromptDTO getPrompt(FileTaskContext context, String errorContent); + AIPromptDTO getPrompt(FileTaskContext context); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java index 325b52fece..f7e5fb5581 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/TaskContextService.java @@ -25,16 +25,16 @@ package com.tencent.bk.job.analysis.service.ai.context; import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContextQuery; public interface TaskContextService { /** - * 获取脚本任务上下文 + * 获取任务上下文 * - * @param username 用户名 - * @param appId Job业务ID - * @param stepInstanceId 步骤实例ID - * @return 脚本任务上下文 + * @param username 用户名 + * @param contextQuery 任务上下文查询条件 + * @return 任务上下文 */ - TaskContext getTaskContext(String username, Long appId, Long stepInstanceId); + TaskContext getTaskContext(String username, TaskContextQuery contextQuery); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java new file mode 100644 index 0000000000..4e430b49a7 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.constants; + +/** + * 文件任务失败原因来源枚举 + */ +public enum FileTaskErrorSourceEnum { + SOURCE_FILE_UPLOAD_ERROR("源文件上传出错导致的任务失败"), + DOWNLOAD_ERROR("目标执行对象下载文件出错导致的任务失败"), + UPLOAD_AND_DOWNLOAD_ERROR("源文件上传与目标执行对象下载文件均出错导致的任务失败"); + + /** + * 任务失败原因描述 + */ + private final String description; + + FileTaskErrorSourceEnum(String description) { + this.description = description; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java new file mode 100644 index 0000000000..eca567c07f --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java @@ -0,0 +1,138 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.impl; + +import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; +import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskErrorSourceResult; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContextQuery; +import com.tencent.bk.job.common.exception.ServiceException; +import com.tencent.bk.job.common.gse.constants.FileDistModeEnum; +import com.tencent.bk.job.common.model.InternalResponse; +import com.tencent.bk.job.common.model.error.ErrorType; +import com.tencent.bk.job.execute.common.constants.FileDistStatusEnum; +import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; +import com.tencent.bk.job.logsvr.api.ServiceLogResource; +import com.tencent.bk.job.logsvr.consts.FileTaskModeEnum; +import com.tencent.bk.job.logsvr.model.service.ServiceExecuteObjectLogDTO; +import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; +import com.tencent.bk.job.logsvr.util.LogFieldUtil; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 文件任务上下文服务 + */ +@Service +public class FileTaskContextService { + + private final ServiceLogResource serviceLogResource; + private final FileTaskFailLogAnalyzer fileTaskFailLogAnalyzer; + + @Autowired + public FileTaskContextService(ServiceLogResource serviceLogResource, + FileTaskFailLogAnalyzer fileTaskFailLogAnalyzer) { + this.serviceLogResource = serviceLogResource; + this.fileTaskFailLogAnalyzer = fileTaskFailLogAnalyzer; + } + + public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskContextQuery contextQuery) { + String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); + // 上传日志 + InternalResponse resp = serviceLogResource.getFileLogByExecuteObjectId( + jobCreateDate, + contextQuery.getStepInstanceId(), + contextQuery.getExecuteCount(), + null, + FileDistModeEnum.UPLOAD.getValue(), + contextQuery.getBatch() + ); + if (!resp.isSuccess()) { + throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); + } + ServiceExecuteObjectLogDTO uploadExecuteObjectLog = resp.getData(); + // 下载日志 + resp = serviceLogResource.getFileLogByExecuteObjectId( + jobCreateDate, + contextQuery.getStepInstanceId(), + contextQuery.getExecuteCount(), + contextQuery.getExecuteObjectId(), + FileDistModeEnum.DOWNLOAD.getValue(), + contextQuery.getBatch() + ); + if (!resp.isSuccess()) { + throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); + } + ServiceExecuteObjectLogDTO downloadExecuteObjectLog = resp.getData(); + return buildContextForFileTask(stepInstance, uploadExecuteObjectLog, downloadExecuteObjectLog); + } + + private TaskContext buildContextForFileTask(ServiceStepInstanceDTO stepInstance, + ServiceExecuteObjectLogDTO uploadExecuteObjectLog, + ServiceExecuteObjectLogDTO downloadExecuteObjectLog) { + List fileTaskLogs = new ArrayList<>(); + List uploadFileTaskLogs = uploadExecuteObjectLog.getFileTaskLogs(); + if (CollectionUtils.isNotEmpty(uploadFileTaskLogs)) { + fileTaskLogs.addAll(uploadFileTaskLogs); + } + List downloadFileTaskLogs = downloadExecuteObjectLog.getFileTaskLogs(); + if (CollectionUtils.isNotEmpty(downloadFileTaskLogs)) { + fileTaskLogs.addAll(downloadFileTaskLogs); + } + List uploadFailLogs = new ArrayList<>(); + List downloadFailLogs = new ArrayList<>(); + for (ServiceFileTaskLogDTO fileTaskLog : fileTaskLogs) { + if (isUploadFailLog(fileTaskLog)) { + uploadFailLogs.add(fileTaskLog); + } else if (isDownloadFailLog(fileTaskLog)) { + downloadFailLogs.add(fileTaskLog); + } + } + // 对上传失败和下载失败的日志进行分析,确定导致任务失败的主要原因 + FileTaskErrorSourceResult result = fileTaskFailLogAnalyzer.analyze(uploadFailLogs, downloadFailLogs); + FileTaskContext fileTaskContext = new FileTaskContext(stepInstance.getFileStepInstance(), result); + return new TaskContext(stepInstance.getExecuteType(), null, fileTaskContext); + } + + private boolean isUploadFailLog(ServiceFileTaskLogDTO fileTaskLog) { + Integer mode = fileTaskLog.getMode(); + assert (mode != null); + Integer status = fileTaskLog.getStatus(); + return FileTaskModeEnum.UPLOAD.getValue().equals(mode) + && FileDistStatusEnum.FAILED.getValue().equals(status); + } + + private boolean isDownloadFailLog(ServiceFileTaskLogDTO fileTaskLog) { + Integer mode = fileTaskLog.getMode(); + assert (mode != null); + Integer status = fileTaskLog.getStatus(); + return FileTaskModeEnum.DOWNLOAD.getValue().equals(mode) + && FileDistStatusEnum.FAILED.getValue().equals(status); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java new file mode 100644 index 0000000000..ab3ee3f887 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java @@ -0,0 +1,79 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.impl; + +import com.tencent.bk.job.analysis.service.ai.context.constants.FileTaskErrorSourceEnum; +import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskErrorSourceResult; +import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 根据上传与下载失败的日志信息分析导致出错的源头 + */ +@Service +public class FileTaskFailLogAnalyzer { + + public FileTaskErrorSourceResult analyze(List uploadFailLogs, + List downloadFailLogs) { + if (uploadFailLogs.isEmpty() && downloadFailLogs.isEmpty()) { + return new FileTaskErrorSourceResult(); + } + // 上传失败日志为空:说明是下载出错 + if (uploadFailLogs.isEmpty()) { + return new FileTaskErrorSourceResult(FileTaskErrorSourceEnum.DOWNLOAD_ERROR, uploadFailLogs, + downloadFailLogs); + } + // 下载失败日志为空:说明是源文件上传出错 + if (downloadFailLogs.isEmpty()) { + return new FileTaskErrorSourceResult(FileTaskErrorSourceEnum.SOURCE_FILE_UPLOAD_ERROR, uploadFailLogs, + downloadFailLogs); + } + // 上传失败日志与下载失败日志均不为空 + Set downloadFailSrcFilePathSet = downloadFailLogs.stream().map(fileTaskLog -> + fileTaskLog.getSrcExecuteObjectId() + ":" + fileTaskLog.getSrcFile() + ).collect(Collectors.toSet()); + Set uploadFailFilePathSet = uploadFailLogs.stream().map(fileTaskLog -> + fileTaskLog.getSrcExecuteObjectId() + ":" + fileTaskLog.getSrcFile() + ).collect(Collectors.toSet()); + downloadFailSrcFilePathSet.removeAll(uploadFailFilePathSet); + if (downloadFailSrcFilePathSet.isEmpty()) { + // 下载失败日志关联的上传文件任务均失败:说明根本原因是源文件上传出错 + return new FileTaskErrorSourceResult(FileTaskErrorSourceEnum.SOURCE_FILE_UPLOAD_ERROR, uploadFailLogs, + downloadFailLogs); + } else { + // 上传失败日志与下载失败日志均不为空,且下载失败日志并不全是上传失败导致:说明根本原因是上传下载同时出错 + // 筛选出由于下载方出错导致的下载失败日志 + List realDownloadFailLogs = downloadFailLogs.stream().filter(fileTaskLog -> + downloadFailSrcFilePathSet.contains(fileTaskLog.getSrcExecuteObjectId() + ":" + fileTaskLog.getSrcFile()) + ).collect(Collectors.toList()); + return new FileTaskErrorSourceResult(FileTaskErrorSourceEnum.UPLOAD_AND_DOWNLOAD_ERROR, uploadFailLogs, + realDownloadFailLogs); + } + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java index 0a49363085..459bfe640a 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java @@ -24,10 +24,13 @@ package com.tencent.bk.job.analysis.service.ai.context.impl; +import com.tencent.bk.job.analysis.service.ai.context.TaskContextService; import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; import com.tencent.bk.job.analysis.service.ai.context.model.ScriptTaskContext; import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; -import com.tencent.bk.job.analysis.service.ai.context.TaskContextService; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContextQuery; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.InvalidParamException; import com.tencent.bk.job.common.exception.ServiceException; import com.tencent.bk.job.common.iam.exception.PermissionDeniedException; import com.tencent.bk.job.common.iam.model.AuthResult; @@ -47,22 +50,31 @@ public class TaskContextServiceImpl implements TaskContextService { private final ServiceStepInstanceResource serviceStepInstanceResource; + private final FileTaskContextService fileTaskContextService; @Autowired - public TaskContextServiceImpl(ServiceStepInstanceResource serviceStepInstanceResource) { + public TaskContextServiceImpl(ServiceStepInstanceResource serviceStepInstanceResource, + FileTaskContextService fileTaskContextService) { this.serviceStepInstanceResource = serviceStepInstanceResource; + this.fileTaskContextService = fileTaskContextService; } @Override - public TaskContext getTaskContext(String username, Long appId, Long stepInstanceId) { + public TaskContext getTaskContext(String username, TaskContextQuery contextQuery) { InternalResponse resp = serviceStepInstanceResource.getStepInstance( username, - appId, - stepInstanceId + contextQuery.getAppId(), + contextQuery.getStepInstanceId() ); if (resp.isSuccess()) { ServiceStepInstanceDTO stepInstance = resp.getData(); - return buildTaskContext(stepInstance); + if (stepInstance.isScriptStep()) { + return buildContextForScriptTask(stepInstance); + } else if (stepInstance.isFileStep()) { + return fileTaskContextService.getTaskContext(stepInstance, contextQuery); + } else { + throw new InvalidParamException(ErrorCode.AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP); + } } if (resp.getAuthResult() != null && !resp.getAuthResult().isPass()) { throw new PermissionDeniedException(AuthResult.fromAuthResultDTO(resp.getAuthResult())); @@ -70,25 +82,13 @@ public TaskContext getTaskContext(String username, Long appId, Long stepInstance throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); } - private TaskContext buildTaskContext(ServiceStepInstanceDTO stepInstance) { + private TaskContext buildContextForScriptTask(ServiceStepInstanceDTO stepInstance) { ServiceScriptStepInstanceDTO scriptStepInstance = stepInstance.getScriptStepInstance(); - ScriptTaskContext scriptTaskContext = null; - FileTaskContext fileTaskContext = null; - StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(stepInstance.getExecuteType()); - if (stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SCRIPT - || stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SQL) { - scriptTaskContext = new ScriptTaskContext( - scriptStepInstance.getScriptType(), - scriptStepInstance.getScriptContent(), - scriptStepInstance.getScriptParam() - ); - } else if (stepExecuteTypeEnum == StepExecuteTypeEnum.SEND_FILE) { - fileTaskContext = new FileTaskContext(); - } - return new TaskContext( - stepInstance.getExecuteType(), - scriptTaskContext, - fileTaskContext + ScriptTaskContext scriptTaskContext = new ScriptTaskContext( + scriptStepInstance.getScriptType(), + scriptStepInstance.getScriptContent(), + scriptStepInstance.getScriptParam() ); + return new TaskContext(stepInstance.getExecuteType(), scriptTaskContext, null); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ContainerDescription.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ContainerDescription.java new file mode 100644 index 0000000000..5ab9f58e25 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/ContainerDescription.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 容器描述信息,提交给AI让其进行归纳总结,字段名必须清晰 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class ContainerDescription { + /** + * 容器UID + */ + private String uid; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/DownloadFileErrorInfo.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/DownloadFileErrorInfo.java new file mode 100644 index 0000000000..3842537fb9 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/DownloadFileErrorInfo.java @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 单条下载文件错误信息,提交给AI让其进行归纳总结,字段名必须清晰 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class DownloadFileErrorInfo { + + /** + * 源执行对象类型 + */ + private String sourceExecuteObjectType; + + /** + * 源主机描述信息 + */ + private HostDescription sourceHostDescription; + + /** + * 源容器描述信息 + */ + private ContainerDescription sourceContainerDescription; + + /** + * 目标执行对象类型 + */ + private String targetExecuteObjectType; + + /** + * 目标主机描述信息 + */ + private HostDescription targetHostDescription; + + /** + * 目标容器描述信息 + */ + private ContainerDescription targetContainerDescription; + + /** + * 目标文件路径 + */ + private String targetFilePath; + + /** + * 目标文件下载报错信息 + */ + private String errorLog; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java index df2b815273..b88e493c9d 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java @@ -24,8 +24,170 @@ package com.tencent.bk.job.analysis.service.ai.context.model; +import com.tencent.bk.job.common.constant.ExecuteObjectTypeEnum; +import com.tencent.bk.job.common.util.json.JsonUtils; +import com.tencent.bk.job.execute.model.inner.ServiceExecuteObject; +import com.tencent.bk.job.execute.model.inner.ServiceExecuteTargetDTO; +import com.tencent.bk.job.execute.model.inner.ServiceFileSourceDTO; +import com.tencent.bk.job.execute.model.inner.ServiceFileStepInstanceDTO; +import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; +import lombok.AllArgsConstructor; import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@AllArgsConstructor @Data public class FileTaskContext { + /** + * 文件分发任务实例 + */ + ServiceFileStepInstanceDTO fileStepInstance; + /** + * 文件分发任务错误根源分析结果 + */ + FileTaskErrorSourceResult errorSourceResult; + + Map executeObjectMap; + + public FileTaskContext(ServiceFileStepInstanceDTO fileStepInstance, FileTaskErrorSourceResult errorSourceResult) { + this.fileStepInstance = fileStepInstance; + this.errorSourceResult = errorSourceResult; + } + + private void buildExecuteObjectMapIfNeeded() { + if (executeObjectMap != null) { + return; + } + executeObjectMap = new HashMap<>(); + List fileSourceList = fileStepInstance.getFileSourceList(); + if (CollectionUtils.isNotEmpty(fileSourceList)) { + for (ServiceFileSourceDTO fileSource : fileSourceList) { + ServiceExecuteTargetDTO servers = fileSource.getServers(); + if (servers == null) { + continue; + } + List executeObjects = servers.getExecuteObjects(); + if (CollectionUtils.isEmpty(executeObjects)) { + continue; + } + for (ServiceExecuteObject executeObject : executeObjects) { + executeObjectMap.put(executeObject.getId(), executeObject); + } + } + } + ServiceExecuteTargetDTO targetExecuteObjects = fileStepInstance.getTargetExecuteObjects(); + if (targetExecuteObjects == null) { + return; + } + List executeObjects = targetExecuteObjects.getExecuteObjects(); + if (CollectionUtils.isEmpty(executeObjects)) { + return; + } + for (ServiceExecuteObject executeObject : executeObjects) { + executeObjectMap.put(executeObject.getId(), executeObject); + } + } + + public String getFileTaskErrorSource() { + return errorSourceResult.getErrorSource().name(); + } + + @SuppressWarnings("DuplicatedCode") + public String getUploadFileErrorData() { + buildExecuteObjectMapIfNeeded(); + List uploadFileErrorInfoList = new ArrayList<>(); + List uploadFailLogs = errorSourceResult.getUploadFailLogs(); + if (CollectionUtils.isEmpty(uploadFailLogs)) { + return JsonUtils.toJson(uploadFileErrorInfoList); + } + for (ServiceFileTaskLogDTO uploadFailLog : uploadFailLogs) { + UploadFileErrorInfo uploadFileErrorInfo = new UploadFileErrorInfo(); + String srcExecuteObjectId = uploadFailLog.getSrcExecuteObjectId(); + ServiceExecuteObject executeObject = executeObjectMap.get(srcExecuteObjectId); + ExecuteObjectTypeEnum executeObjectType = ExecuteObjectTypeEnum.valOf(executeObject.getType()); + uploadFileErrorInfo.setSourceExecuteObjectType(executeObjectType.name()); + uploadFileErrorInfo.setSourceFilePath(uploadFailLog.getDisplaySrcFile()); + if (executeObjectType == ExecuteObjectTypeEnum.HOST) { + HostDescription sourceHostDescription = new HostDescription(); + sourceHostDescription.setCloudIp(executeObject.getHost().toCloudIp()); + uploadFileErrorInfo.setSourceHostDescription(sourceHostDescription); + } else if (executeObjectType == ExecuteObjectTypeEnum.CONTAINER) { + ContainerDescription sourceContainerDescription = new ContainerDescription(); + sourceContainerDescription.setUid(executeObject.getContainer().getContainerId()); + uploadFileErrorInfo.setSourceContainerDescription(sourceContainerDescription); + } + // 填充错误日志信息 + String lastLineLog = getLastLineLog(uploadFailLog.getContent()); + uploadFileErrorInfo.setErrorLog(lastLineLog); + + uploadFileErrorInfoList.add(uploadFileErrorInfo); + } + return JsonUtils.toJson(uploadFileErrorInfoList); + } + + @SuppressWarnings("DuplicatedCode") + public String getDownloadFileErrorData() { + buildExecuteObjectMapIfNeeded(); + List downloadFileErrorInfoList = new ArrayList<>(); + List downloadFailLogs = errorSourceResult.getDownloadFailLogs(); + if (CollectionUtils.isEmpty(downloadFailLogs)) { + return JsonUtils.toJson(downloadFileErrorInfoList); + } + for (ServiceFileTaskLogDTO downloadFailLog : downloadFailLogs) { + DownloadFileErrorInfo downloadFileErrorInfo = new DownloadFileErrorInfo(); + // 填充下载文件的源执行对象信息 + String srcExecuteObjectId = downloadFailLog.getSrcExecuteObjectId(); + ServiceExecuteObject srcExecuteObject = executeObjectMap.get(srcExecuteObjectId); + ExecuteObjectTypeEnum srcExecuteObjectType = ExecuteObjectTypeEnum.valOf(srcExecuteObject.getType()); + downloadFileErrorInfo.setSourceExecuteObjectType(srcExecuteObjectType.name()); + if (srcExecuteObjectType == ExecuteObjectTypeEnum.HOST) { + HostDescription sourceHostDescription = new HostDescription(); + sourceHostDescription.setCloudIp(srcExecuteObject.getHost().toCloudIp()); + downloadFileErrorInfo.setSourceHostDescription(sourceHostDescription); + } else if (srcExecuteObjectType == ExecuteObjectTypeEnum.CONTAINER) { + ContainerDescription sourceContainerDescription = new ContainerDescription(); + sourceContainerDescription.setUid(srcExecuteObject.getContainer().getContainerId()); + downloadFileErrorInfo.setSourceContainerDescription(sourceContainerDescription); + } + + // 填充下载文件的目标执行对象信息 + String destExecuteObjectId = downloadFailLog.getDestExecuteObjectId(); + ServiceExecuteObject targetExecuteObject = executeObjectMap.get(destExecuteObjectId); + ExecuteObjectTypeEnum targetExecuteObjectType = ExecuteObjectTypeEnum.valOf(targetExecuteObject.getType()); + downloadFileErrorInfo.setTargetExecuteObjectType(targetExecuteObjectType.name()); + downloadFileErrorInfo.setTargetFilePath(downloadFailLog.getDestFile()); + if (targetExecuteObjectType == ExecuteObjectTypeEnum.HOST) { + HostDescription targetHostDescription = new HostDescription(); + targetHostDescription.setCloudIp(targetExecuteObject.getHost().toCloudIp()); + downloadFileErrorInfo.setTargetHostDescription(targetHostDescription); + } else if (targetExecuteObjectType == ExecuteObjectTypeEnum.CONTAINER) { + ContainerDescription targetContainerDescription = new ContainerDescription(); + targetContainerDescription.setUid(targetExecuteObject.getContainer().getContainerId()); + downloadFileErrorInfo.setTargetContainerDescription(targetContainerDescription); + } + // 填充错误日志信息 + downloadFileErrorInfo.setErrorLog(getLastLineLog(downloadFailLog.getContent())); + downloadFileErrorInfoList.add(downloadFileErrorInfo); + } + return JsonUtils.toJson(downloadFileErrorInfoList); + } + + /** + * 获取日志的最后一行 + * + * @param log 原始日志 + * @return 最后一行日志内容 + */ + private String getLastLineLog(String log) { + if (log != null) { + String[] lines = log.split("\n"); + return lines[lines.length - 1]; + } + return null; + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskErrorSourceResult.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskErrorSourceResult.java new file mode 100644 index 0000000000..0e50ef2edb --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskErrorSourceResult.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import com.tencent.bk.job.analysis.service.ai.context.constants.FileTaskErrorSourceEnum; +import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 文件分发任务错误根源分析结果 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class FileTaskErrorSourceResult { + /** + * 错误根源 + */ + FileTaskErrorSourceEnum errorSource; + + /** + * 上传失败的日志 + */ + List uploadFailLogs; + /** + * 下载失败的日志 + */ + List downloadFailLogs; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/HostDescription.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/HostDescription.java new file mode 100644 index 0000000000..0aec77decf --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/HostDescription.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 主机描述,提交给AI让其进行归纳总结,字段名必须清晰 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class HostDescription { + /** + * 云区域IP + */ + private String cloudIp; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContextQuery.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContextQuery.java new file mode 100644 index 0000000000..5d04e1a1c3 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContextQuery.java @@ -0,0 +1,97 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; +import com.tencent.bk.job.execute.model.web.vo.ExecuteObjectVO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 任务上下文查询条件 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class TaskContextQuery { + + /** + * 作业平台业务ID + */ + private Long appId; + + /** + * 任务ID + */ + private Long taskInstanceId; + + /** + * 步骤ID + */ + private Long stepInstanceId; + + /** + * 执行次数 + */ + private Integer executeCount; + + /** + * 滚动批次,非滚动步骤不需要传入 + */ + private Integer batch; + + /** + * 执行对象类型 + */ + private Integer executeObjectType; + + /** + * 执行对象资源 ID + */ + private Long executeObjectResourceId; + + /** + * 文件任务上传下载标识,0-上传,1-下载 + */ + private Integer mode; + + public static TaskContextQuery fromAIAnalyzeErrorReq(Long appId, AIAnalyzeErrorReq req) { + TaskContextQuery contextQuery = new TaskContextQuery(); + contextQuery.setAppId(appId); + contextQuery.setTaskInstanceId(req.getTaskInstanceId()); + contextQuery.setStepInstanceId(req.getStepInstanceId()); + contextQuery.setExecuteCount(req.getExecuteCount()); + contextQuery.setBatch(req.getBatch()); + contextQuery.setExecuteObjectType(req.getExecuteObjectType()); + contextQuery.setExecuteObjectResourceId(req.getExecuteObjectResourceId()); + contextQuery.setMode(req.getMode()); + return contextQuery; + } + + public String getExecuteObjectId() { + return ExecuteObjectVO.buildExecuteObjectId(executeObjectType, executeObjectResourceId); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/UploadFileErrorInfo.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/UploadFileErrorInfo.java new file mode 100644 index 0000000000..41f171acbb --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/UploadFileErrorInfo.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; +import com.tencent.bk.job.execute.model.web.vo.ExecuteObjectVO; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 单条上传文件错误信息,提交给AI让其进行归纳总结,字段名必须清晰 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +public class UploadFileErrorInfo { + + /** + * 源执行对象类型 + */ + private String sourceExecuteObjectType; + + /** + * 源主机描述信息 + */ + private HostDescription sourceHostDescription; + + /** + * 源容器描述信息 + */ + private ContainerDescription sourceContainerDescription; + + /** + * 源文件路径 + */ + private String sourceFilePath; + + /** + * 源文件上传报错信息 + */ + private String errorLog; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java index ed0d05d06d..f3c62a31b3 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -32,8 +32,9 @@ import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.FileTransferTaskErrorAIPromptService; import com.tencent.bk.job.analysis.service.ai.ScriptExecuteTaskErrorAIPromptService; -import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; import com.tencent.bk.job.analysis.service.ai.context.TaskContextService; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContext; +import com.tencent.bk.job.analysis.service.ai.context.model.TaskContextQuery; import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.InvalidParamException; import lombok.extern.slf4j.Slf4j; @@ -62,11 +63,8 @@ public AIAnalyzeErrorServiceImpl(TaskContextService taskContextService, @Override public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { - TaskContext taskContext = taskContextService.getTaskContext( - username, - appId, - req.getStepInstanceId() - ); + TaskContextQuery contextQuery = TaskContextQuery.fromAIAnalyzeErrorReq(appId, req); + TaskContext taskContext = taskContextService.getTaskContext(username, contextQuery); String errorContent = req.getContent(); AIPromptDTO aiPromptDTO; if (taskContext.isScriptTask()) { @@ -75,10 +73,7 @@ public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { errorContent ); } else if (taskContext.isFileTask()) { - aiPromptDTO = fileTransferTaskErrorAIPromptService.getPrompt( - taskContext.getFileTaskContext(), - errorContent - ); + aiPromptDTO = fileTransferTaskErrorAIPromptService.getPrompt(taskContext.getFileTaskContext()); } else { throw new InvalidParamException(ErrorCode.AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java index ed205d0376..f04e11e50e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java @@ -52,4 +52,16 @@ public String getScriptContentPlaceHolder() { public String getErrorContentPlaceHolder() { return getTemplateVarPlaceHolder("error_content"); } + + public String getFileTaskErrorSourcePlaceHolder() { + return getTemplateVarPlaceHolder("file_task_error_source"); + } + + public String getUploadFileErrorDataPlaceHolder() { + return getTemplateVarPlaceHolder("upload_file_error_data"); + } + + public String getDownloadFileErrorDataPlaceHolder() { + return getTemplateVarPlaceHolder("download_file_error_data"); + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java index 9a6c525b12..e846ff0473 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java @@ -52,16 +52,17 @@ public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemp } @Override - public AIPromptDTO getPrompt(FileTaskContext context, String errorContent) { + public AIPromptDTO getPrompt(FileTaskContext context) { String templateCode = PromptTemplateCodeEnum.ANALYZE_FILE_TRANSFER_TASK_ERROR.name(); AIPromptTemplateDTO promptTemplate = getPromptTemplate(templateCode); - String renderedPrompt = renderPrompt(promptTemplate.getTemplate(), context, errorContent); + String renderedPrompt = renderPrompt(promptTemplate.getTemplate(), context); return new AIPromptDTO(promptTemplate.getRawPrompt(), renderedPrompt); } - private String renderPrompt(String promptTemplateContent, FileTaskContext context, String errorContent) { - // TODO:补充文件任务报错分析模板渲染相关内容 + private String renderPrompt(String promptTemplateContent, FileTaskContext context) { return promptTemplateContent - .replace(aiTemplateVarService.getErrorContentPlaceHolder(), errorContent); + .replace(aiTemplateVarService.getFileTaskErrorSourcePlaceHolder(), context.getFileTaskErrorSource()) + .replace(aiTemplateVarService.getUploadFileErrorDataPlaceHolder(), context.getUploadFileErrorData()) + .replace(aiTemplateVarService.getDownloadFileErrorDataPlaceHolder(), context.getDownloadFileErrorData()); } } diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java new file mode 100644 index 0000000000..c1d4394e1b --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.api.inner; + +import com.tencent.bk.job.common.annotation.InternalAPI; +import com.tencent.bk.job.common.model.InternalResponse; +import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; +import com.tentent.bk.job.common.api.feign.annotation.SmartFeignClient; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiParam; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; + +/** + * 步骤实例API-服务内部调用 + */ +@Api(tags = {"StepInstance"}) +@SmartFeignClient(value = "job-execute", contextId = "stepInstanceResource") +@InternalAPI +public interface ServiceStepLogResource { + @GetMapping("/service/stepInstance/appIds/{appId}/stepInstanceIds/{stepInstanceId}") + InternalResponse getStepInstance( + @RequestHeader("username") + String username, + @ApiParam(value = "作业平台业务ID", required = true) + @PathVariable(value = "appId") + Long appId, + @ApiParam(value = "步骤实例ID", name = "stepInstanceId", required = true) + @PathVariable("stepInstanceId") + Long stepInstanceId); +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/FileDistStatusEnum.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/FileDistStatusEnum.java index c40030dce9..5d9502911d 100644 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/FileDistStatusEnum.java +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/FileDistStatusEnum.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.execute.common.constants; /** - * 文件分发状态 + * 单个文件分发对(源执行对象,源文件路径,目标执行对象,目标文件路径)的文件分发状态,体现在详情页文件分发日志中 */ public enum FileDistStatusEnum { PULLING(0, "Pulling from third file source"), diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/RunStatusEnum.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/RunStatusEnum.java index 954aa2ee5e..b1634cb39a 100644 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/RunStatusEnum.java +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/common/constants/RunStatusEnum.java @@ -28,7 +28,7 @@ import java.util.Set; /** - * 作业执行状态 + * 任务与步骤的执行状态 */ public enum RunStatusEnum { /** diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteObject.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteObject.java new file mode 100644 index 0000000000..8bb4f0cb12 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteObject.java @@ -0,0 +1,76 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.tencent.bk.job.common.annotation.PersistenceObject; +import com.tencent.bk.job.common.constant.ExecuteObjectTypeEnum; +import com.tencent.bk.job.common.model.dto.Container; +import com.tencent.bk.job.common.model.dto.HostDTO; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +/** + * 作业执行对象通用模型 + */ +@Setter +@Getter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +@ToString +@PersistenceObject +@Slf4j +public class ServiceExecuteObject { + /** + * 执行对象 ID + */ + private String id; + + /** + * 执行对象类型 + * + * @see ExecuteObjectTypeEnum + */ + private int type; + + /** + * 执行对象资源 ID(主机 ID/容器 ID) + */ + private Long resourceId; + + /** + * 容器 + */ + private Container container; + + /** + * 主机 + */ + private HostDTO host; + +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteTargetDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteTargetDTO.java new file mode 100644 index 0000000000..28958b3011 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceExecuteTargetDTO.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.tencent.bk.job.common.annotation.PersistenceObject; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +/** + * 任务执行目标 DTO + */ +@Data +@PersistenceObject +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@Slf4j +public class ServiceExecuteTargetDTO { + /** + * 如果执行目标是通过全局变量-主机列表定义的,variable 表示变量 name + */ + private String variable; + + /** + * 执行对象列表(所有主机+容器) + */ + private List executeObjects; + +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileDetailDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileDetailDTO.java new file mode 100644 index 0000000000..42c111f873 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileDetailDTO.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@Data +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ServiceFileDetailDTO { + /** + * 文件路径 + */ + private String filePath; +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileSourceDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileSourceDTO.java new file mode 100644 index 0000000000..d32fa60755 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileSourceDTO.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.bk.job.common.annotation.PersistenceObject; +import lombok.Data; + +import java.util.List; + +/** + * 文件源 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@PersistenceObject +public class ServiceFileSourceDTO { + + /** + * 文件类型 + */ + @JsonProperty("fileType") + private Integer fileType; + /** + * 文件列表 + */ + @JsonProperty("files") + private List files; + /** + * 文件源服务器 + */ + @JsonProperty("servers") + private ServiceExecuteTargetDTO servers; + + +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileStepInstanceDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileStepInstanceDTO.java new file mode 100644 index 0000000000..0d0ec3be54 --- /dev/null +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceFileStepInstanceDTO.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.execute.model.inner; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.util.List; + +@ApiModel("脚本步骤实例") +@Data +public class ServiceFileStepInstanceDTO { + + /** + * 文件传输的源文件 + */ + private List fileSourceList; + + /** + * 执行目标 + */ + protected ServiceExecuteTargetDTO targetExecuteObjects; +} diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java index 1824d647a1..d2227c87d1 100644 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.execute.model.inner; +import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -31,22 +32,30 @@ @ApiModel("步骤实例") @Data public class ServiceStepInstanceDTO { - /** - * 步骤实例ID - */ + @ApiModelProperty("步骤实例ID") private Long id; - /** - * 步骤类型 - */ @ApiModelProperty("步骤类型") private Integer executeType; - /** - * 步骤类型 - */ + @ApiModelProperty("创建时间") + private Long createTime; + @ApiModelProperty("脚本任务步骤") private ServiceScriptStepInstanceDTO scriptStepInstance; + @ApiModelProperty("文件任务步骤") + private ServiceFileStepInstanceDTO fileStepInstance; + + public boolean isScriptStep() { + StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(executeType); + return stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SCRIPT + || stepExecuteTypeEnum == StepExecuteTypeEnum.EXECUTE_SQL; + } + + public boolean isFileStep() { + StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(executeType); + return stepExecuteTypeEnum == StepExecuteTypeEnum.SEND_FILE; + } } diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/web/vo/ExecuteObjectVO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/web/vo/ExecuteObjectVO.java index de60682719..1761c5a3a5 100644 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/web/vo/ExecuteObjectVO.java +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/web/vo/ExecuteObjectVO.java @@ -70,4 +70,8 @@ public class ExecuteObjectVO { public static ExecuteObjectTypeEnum fromExecuteObjectTypeValue(int type) { return ExecuteObjectTypeEnum.valOf(type); } + + public static String buildExecuteObjectId(Integer executeObjectType, Long executeObjectResoruceId) { + return executeObjectType + ":" + executeObjectResoruceId; + } } diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBatchGetJobInstanceIpLogV3ResourceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBatchGetJobInstanceIpLogV3ResourceImpl.java index 85dcdff9a7..8a2258e8f7 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBatchGetJobInstanceIpLogV3ResourceImpl.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBatchGetJobInstanceIpLogV3ResourceImpl.java @@ -37,7 +37,6 @@ import com.tencent.bk.job.common.metrics.CommonMetricNames; import com.tencent.bk.job.common.model.ValidateResult; import com.tencent.bk.job.common.model.dto.HostDTO; -import com.tencent.bk.job.common.util.date.DateUtils; import com.tencent.bk.job.common.util.ip.IpUtils; import com.tencent.bk.job.execute.model.AtomicFileTaskLog; import com.tencent.bk.job.execute.model.ExecuteObjectCompositeKey; @@ -54,13 +53,12 @@ import com.tencent.bk.job.execute.service.TaskInstanceAccessProcessor; import com.tencent.bk.job.execute.util.ExecuteObjectCompositeKeyUtils; import com.tencent.bk.job.logsvr.consts.LogTypeEnum; +import com.tencent.bk.job.logsvr.util.LogFieldUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.RestController; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; import java.util.List; import java.util.stream.Collectors; @@ -139,8 +137,7 @@ private void buildScriptLogs(EsbIpLogsV3DTO ipLogs, List hostKeys) { ipLogs.setLogType(LogTypeEnum.SCRIPT.getValue()); - String jobCreateDate = DateUtils.formatUnixTimestamp(stepInstance.getCreateTime(), ChronoUnit.MILLIS, - "yyyy_MM_dd", ZoneId.of("UTC")); + String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); List hostLogContentList = logService.batchGetScriptExecuteObjectLogContent( jobCreateDate, stepInstance, stepInstance.getExecuteCount(), null, hostKeys); diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBkCIPluginBatchGetJobInstanceExecuteObjectLogResourceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBkCIPluginBatchGetJobInstanceExecuteObjectLogResourceImpl.java index 8a6176f709..dbce443862 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBkCIPluginBatchGetJobInstanceExecuteObjectLogResourceImpl.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/api/esb/v3/EsbBkCIPluginBatchGetJobInstanceExecuteObjectLogResourceImpl.java @@ -32,7 +32,6 @@ import com.tencent.bk.job.common.exception.NotFoundException; import com.tencent.bk.job.common.iam.constant.ActionId; import com.tencent.bk.job.common.metrics.CommonMetricNames; -import com.tencent.bk.job.common.util.date.DateUtils; import com.tencent.bk.job.execute.model.AtomicFileTaskLog; import com.tencent.bk.job.execute.model.ExecuteObjectCompositeKey; import com.tencent.bk.job.execute.model.FileExecuteObjectLogContent; @@ -48,13 +47,12 @@ import com.tencent.bk.job.execute.service.TaskInstanceAccessProcessor; import com.tencent.bk.job.execute.util.ExecuteObjectCompositeKeyUtils; import com.tencent.bk.job.logsvr.consts.LogTypeEnum; +import com.tencent.bk.job.logsvr.util.LogFieldUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; import java.util.List; import java.util.stream.Collectors; @@ -118,8 +116,7 @@ private void buildScriptLogs(EsbExecuteObjectLogsDTO executeObjectLogs, List executeObjectCompositeKeys) { executeObjectLogs.setLogType(LogTypeEnum.SCRIPT.getValue()); - String jobCreateDate = DateUtils.formatUnixTimestamp(stepInstance.getCreateTime(), ChronoUnit.MILLIS, - "yyyy_MM_dd", ZoneId.of("UTC")); + String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); List executeObjectLogContentList = logService.batchGetScriptExecuteObjectLogContent(jobCreateDate, stepInstance, stepInstance.getExecuteCount(), null, executeObjectCompositeKeys); diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/consts/ExecuteObjectTaskStatusEnum.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/consts/ExecuteObjectTaskStatusEnum.java index 0dff3cd1cb..33c0458191 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/consts/ExecuteObjectTaskStatusEnum.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/consts/ExecuteObjectTaskStatusEnum.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.execute.engine.consts; /** - * 执行对象任务状态 + * 单个执行对象的任务状态,体现在任务详情页左侧状态栏 */ public enum ExecuteObjectTaskStatusEnum { /** diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/model/ExecuteObject.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/model/ExecuteObject.java index 5a394acf11..86ccd46fd0 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/model/ExecuteObject.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/engine/model/ExecuteObject.java @@ -38,6 +38,7 @@ import com.tencent.bk.job.common.model.dto.HostDTO; import com.tencent.bk.job.common.model.openapi.v4.OpenApiExecuteObjectDTO; import com.tencent.bk.job.execute.model.ExecuteObjectCompositeKey; +import com.tencent.bk.job.execute.model.inner.ServiceExecuteObject; import com.tencent.bk.job.execute.model.web.vo.ExecuteObjectVO; import lombok.Getter; import lombok.NoArgsConstructor; @@ -269,4 +270,14 @@ public OpenApiExecuteObjectDTO toOpenApiExecuteObjectDTO() { public boolean isSupportExecuteObjectFeature() { return id != null; } + + public ServiceExecuteObject toServiceExecuteObject() { + ServiceExecuteObject serviceExecuteObject = new ServiceExecuteObject(); + serviceExecuteObject.setId(id); + serviceExecuteObject.setType(type.getValue()); + serviceExecuteObject.setResourceId(resourceId); + serviceExecuteObject.setHost(host); + serviceExecuteObject.setContainer(container); + return serviceExecuteObject; + } } diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/ExecuteTargetDTO.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/ExecuteTargetDTO.java index 53777a2aa3..44fdaa33ac 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/ExecuteTargetDTO.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/ExecuteTargetDTO.java @@ -42,6 +42,7 @@ import com.tencent.bk.job.common.model.vo.TaskTargetVO; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.execute.engine.model.ExecuteObject; +import com.tencent.bk.job.execute.model.inner.ServiceExecuteTargetDTO; import com.tencent.bk.job.execute.util.label.selector.LabelSelectorParse; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -617,4 +618,17 @@ public boolean hasHostExecuteObject() { return CollectionUtils.isNotEmpty(staticIpList) || CollectionUtils.isNotEmpty(dynamicServerGroups) || CollectionUtils.isNotEmpty(topoNodes); } + + public ServiceExecuteTargetDTO toServiceExecuteTargetDTO() { + ServiceExecuteTargetDTO serviceExecuteTargetDTO = new ServiceExecuteTargetDTO(); + serviceExecuteTargetDTO.setVariable(variable); + if (executeObjects != null) { + serviceExecuteTargetDTO.setExecuteObjects( + executeObjects.stream() + .map(ExecuteObject::toServiceExecuteObject) + .collect(Collectors.toList()) + ); + } + return serviceExecuteTargetDTO; + } } diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileDetailDTO.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileDetailDTO.java index c0dff5abc9..dc9fe2e6d6 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileDetailDTO.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileDetailDTO.java @@ -25,6 +25,7 @@ package com.tencent.bk.job.execute.model; import com.fasterxml.jackson.annotation.JsonInclude; +import com.tencent.bk.job.execute.model.inner.ServiceFileDetailDTO; import lombok.Data; import lombok.NoArgsConstructor; @@ -92,4 +93,10 @@ public FileDetailDTO clone() { fileDetailDTO.setResolvedFilePath(resolvedFilePath); return fileDetailDTO; } + + public ServiceFileDetailDTO toServiceFileDetailDTO() { + ServiceFileDetailDTO serviceFileDetailDTO = new ServiceFileDetailDTO(); + serviceFileDetailDTO.setFilePath(filePath); + return serviceFileDetailDTO; + } } diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileSourceDTO.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileSourceDTO.java index 71604ccb97..dd08eb8a98 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileSourceDTO.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/FileSourceDTO.java @@ -27,10 +27,13 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.tencent.bk.job.common.annotation.PersistenceObject; +import com.tencent.bk.job.execute.model.inner.ServiceFileSourceDTO; import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * 文件源 @@ -106,4 +109,18 @@ public FileSourceDTO clone() { } return cloneFileSourceDTO; } + + public ServiceFileSourceDTO toServiceFileSourceDTO() { + ServiceFileSourceDTO serviceFileSourceDTO = new ServiceFileSourceDTO(); + serviceFileSourceDTO.setFileType(fileType); + if (CollectionUtils.isNotEmpty(files)) { + serviceFileSourceDTO.setFiles( + files.stream() + .map(FileDetailDTO::toServiceFileDetailDTO) + .collect(Collectors.toList()) + ); + } + serviceFileSourceDTO.setServers(servers.toServiceExecuteTargetDTO()); + return serviceFileSourceDTO; + } } diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java index 69b2ae5b97..ead4f43383 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java @@ -28,6 +28,7 @@ import com.tencent.bk.job.common.model.dto.Container; import com.tencent.bk.job.common.model.dto.HostDTO; import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; +import com.tencent.bk.job.execute.model.inner.ServiceFileStepInstanceDTO; import com.tencent.bk.job.execute.model.inner.ServiceScriptStepInstanceDTO; import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; @@ -42,6 +43,7 @@ import java.util.List; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * 步骤实例 @@ -318,6 +320,7 @@ public ServiceStepInstanceDTO toServiceStepInstanceDTO() { ServiceStepInstanceDTO serviceStepInstanceDTO = new ServiceStepInstanceDTO(); serviceStepInstanceDTO.setId(id); serviceStepInstanceDTO.setExecuteType(executeType.getValue()); + serviceStepInstanceDTO.setCreateTime(createTime); if (executeType == StepExecuteTypeEnum.EXECUTE_SCRIPT || executeType == StepExecuteTypeEnum.EXECUTE_SQL) { ServiceScriptStepInstanceDTO scriptStepInstance = new ServiceScriptStepInstanceDTO(); scriptStepInstance.setStepInstanceId(id); @@ -325,6 +328,19 @@ public ServiceStepInstanceDTO toServiceStepInstanceDTO() { scriptStepInstance.setScriptContent(scriptContent); scriptStepInstance.setScriptParam(scriptParam); serviceStepInstanceDTO.setScriptStepInstance(scriptStepInstance); + } else if (executeType == StepExecuteTypeEnum.SEND_FILE) { + ServiceFileStepInstanceDTO fileStepInstance = new ServiceFileStepInstanceDTO(); + if (fileSourceList != null) { + fileStepInstance.setFileSourceList( + fileSourceList.stream() + .map(FileSourceDTO::toServiceFileSourceDTO) + .collect(Collectors.toList()) + ); + } + if (targetExecuteObjects != null) { + fileStepInstance.setTargetExecuteObjects(targetExecuteObjects.toServiceExecuteTargetDTO()); + } + serviceStepInstanceDTO.setFileStepInstance(fileStepInstance); } return serviceStepInstanceDTO; } diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogExportServiceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogExportServiceImpl.java index 295a6c7106..0729e98146 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogExportServiceImpl.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogExportServiceImpl.java @@ -31,7 +31,6 @@ import com.tencent.bk.job.common.redis.util.LockUtils; import com.tencent.bk.job.common.util.CollectionUtil; import com.tencent.bk.job.common.util.JobContextUtil; -import com.tencent.bk.job.common.util.date.DateUtils; import com.tencent.bk.job.common.util.file.ZipUtil; import com.tencent.bk.job.common.util.json.JsonUtils; import com.tencent.bk.job.execute.config.LogExportConfig; @@ -46,6 +45,7 @@ import com.tencent.bk.job.execute.service.LogService; import com.tencent.bk.job.execute.service.ScriptExecuteObjectTaskService; import com.tencent.bk.job.execute.service.StepInstanceService; +import com.tencent.bk.job.logsvr.util.LogFieldUtil; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; @@ -59,8 +59,6 @@ import java.io.File; import java.io.PrintWriter; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -279,8 +277,7 @@ private boolean getLogContentAndWriteToFile(StepInstanceBaseDTO stepInstance, LogExportJobInfoDTO exportJobInfo) { Collection querys = buildLogBatchQuery(stepInstance.getId(), executeObjectTasks); - String jobCreateDate = DateUtils.formatUnixTimestamp(stepInstance.getCreateTime(), ChronoUnit.MILLIS, - "yyyy_MM_dd", ZoneId.of("UTC")); + String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); try (PrintWriter out = new PrintWriter(logFile, "UTF-8")) { for (LogBatchQuery query : querys) { for (List executeObjects : query.getExecuteObjectBatches()) { diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogServiceImpl.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogServiceImpl.java index b47ed5bfdb..2ed4408abd 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogServiceImpl.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/service/impl/LogServiceImpl.java @@ -54,6 +54,7 @@ import com.tencent.bk.job.logsvr.model.service.ServiceFileLogQueryRequest; import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; import com.tencent.bk.job.logsvr.model.service.ServiceScriptLogQueryRequest; +import com.tencent.bk.job.logsvr.util.LogFieldUtil; import com.tencent.bk.job.manage.api.common.constants.task.TaskFileTypeEnum; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; @@ -150,8 +151,7 @@ public void batchWriteScriptLog(long jobCreateTime, if (CollectionUtils.isEmpty(scriptLogs)) { return; } - String jobCreateDate = DateUtils.formatUnixTimestamp(jobCreateTime, ChronoUnit.MILLIS, - "yyyy_MM_dd", ZoneId.of("UTC")); + String jobCreateDate = LogFieldUtil.buildJobCreateDate(jobCreateTime); ServiceBatchSaveLogRequest request = new ServiceBatchSaveLogRequest(); request.setJobCreateDate(jobCreateDate); request.setLogType(LogTypeEnum.SCRIPT.getValue()); @@ -227,8 +227,7 @@ public ScriptExecuteObjectLogContent getScriptExecuteObjectLogContent( } private String buildTaskCreateDateStr(StepInstanceBaseDTO stepInstance) { - return DateUtils.formatUnixTimestamp(stepInstance.getCreateTime(), ChronoUnit.MILLIS, - "yyyy_MM_dd", ZoneId.of("UTC")); + return LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); } private ScriptExecuteObjectLogContent convertToScriptExecuteObjectLogContent( diff --git a/src/backend/job-logsvr/api-job-logsvr/src/main/java/com/tencent/bk/job/logsvr/util/LogFieldUtil.java b/src/backend/job-logsvr/api-job-logsvr/src/main/java/com/tencent/bk/job/logsvr/util/LogFieldUtil.java new file mode 100644 index 0000000000..4b5c973f93 --- /dev/null +++ b/src/backend/job-logsvr/api-job-logsvr/src/main/java/com/tencent/bk/job/logsvr/util/LogFieldUtil.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.logsvr.util; + +import com.tencent.bk.job.common.util.date.DateUtils; + +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; + +/** + * 日志字段工具类 + */ +public class LogFieldUtil { + + /** + * 根据任务创建时间获取创建日期,格式为:yyyy_MM_dd + * + * @param createTime 任务创建时间 + * @return 创建日期 + */ + public static String buildJobCreateDate(Long createTime) { + return DateUtils.formatUnixTimestamp(createTime, ChronoUnit.MILLIS, "yyyy_MM_dd", ZoneId.of("UTC")); + } +} diff --git a/src/backend/job-logsvr/boot-job-logsvr/src/test/java/com/tencent/bk/job/logsvr/util/LogFieldUtilTest.java b/src/backend/job-logsvr/boot-job-logsvr/src/test/java/com/tencent/bk/job/logsvr/util/LogFieldUtilTest.java new file mode 100644 index 0000000000..e14a3cd665 --- /dev/null +++ b/src/backend/job-logsvr/boot-job-logsvr/src/test/java/com/tencent/bk/job/logsvr/util/LogFieldUtilTest.java @@ -0,0 +1,60 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.logsvr.util; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 日志字段工具类测试用例 + */ +public class LogFieldUtilTest { + + @Test + public void testBuildJobCreateDate() { + String timeStr = "2024-07-26T00:00:00+00:00"; + LocalDateTime time = LocalDateTime.parse(timeStr, ISO_OFFSET_DATE_TIME); + long timeMills = time.toInstant(ZoneOffset.UTC).getEpochSecond() * 1000L; + System.out.println(timeStr + ": " + timeMills); + assertThat(LogFieldUtil.buildJobCreateDate(timeMills)).isEqualTo("2024_07_26"); + + timeStr = "2024-07-26T12:00:00+00:00"; + time = LocalDateTime.parse(timeStr, ISO_OFFSET_DATE_TIME); + timeMills = time.toInstant(ZoneOffset.UTC).getEpochSecond() * 1000L; + System.out.println(timeStr + ": " + timeMills); + assertThat(LogFieldUtil.buildJobCreateDate(timeMills)).isEqualTo("2024_07_26"); + + timeStr = "2024-07-26T23:59:59+00:00"; + time = LocalDateTime.parse(timeStr, ISO_OFFSET_DATE_TIME); + timeMills = time.toInstant(ZoneOffset.UTC).getEpochSecond() * 1000L; + System.out.println(timeStr + ": " + timeMills); + assertThat(LogFieldUtil.buildJobCreateDate(timeMills)).isEqualTo("2024_07_26"); + } +} diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index 9435fd38d3..bdb0519ecd 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -56,3 +56,6 @@ VALUES(1, 'CHECK_SCRIPT', 'zh_CN', '检查脚本是否有语法问题', '帮我 REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能一:脚本批量执行】\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前任务执行的脚本内容为:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n脚本参数为:\n{BK_JOB_AI_TEMPLATE_VAR{script_params}}\n\n报错信息如下:\n{BK_JOB_AI_TEMPLATE_VAR{error_content}}\n\n请回答用户的提问:\n任务报错内容解析', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +REPLACE INTO job_analysis.ai_prompt_template +(id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) +VALUES(3, 'ANALYZE_FILE_TRANSFER_TASK_ERROR', 'zh_CN', '分析文件分发任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能:文件批量传输】\n用户在Web页面填写任务名称、超时时长、上传限速、下载限速、源文件、目标路径、传输模式、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务状态与传输进度日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与进度日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前已经结合任务上下文信息进行了预分析,得到以下结果:\n(1)当前任务失败的主要原因为:\n{BK_JOB_AI_TEMPLATE_VAR{error_source}}\n\n(2)源文件上传失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{upload_file_error_data}}\n\n(3)目标机器下载失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{download_file_error_data}}\n\n\n请回答用户的提问:\n请你结合预分析结果数据,对任务报错内容进一步分析,并给出总结性的原因与处理建议', '系统内置的AI命令提示模板:分析文件分发任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); From 6cd21df8a8a75e101fa979da7e52df78a9a5b07e Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 29 Jul 2024 22:28:25 +0800 Subject: [PATCH 020/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持文件分发任务报错信息分析 --- .../api/inner/ServiceStepLogResource.java | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java deleted file mode 100644 index c1d4394e1b..0000000000 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/api/inner/ServiceStepLogResource.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. - * - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. - * - * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. - * - * License for BK-JOB蓝鲸智云作业平台: - * -------------------------------------------------------------------- - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and - * to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of - * the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -package com.tencent.bk.job.execute.api.inner; - -import com.tencent.bk.job.common.annotation.InternalAPI; -import com.tencent.bk.job.common.model.InternalResponse; -import com.tencent.bk.job.execute.model.inner.ServiceStepInstanceDTO; -import com.tentent.bk.job.common.api.feign.annotation.SmartFeignClient; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiParam; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestHeader; - -/** - * 步骤实例API-服务内部调用 - */ -@Api(tags = {"StepInstance"}) -@SmartFeignClient(value = "job-execute", contextId = "stepInstanceResource") -@InternalAPI -public interface ServiceStepLogResource { - @GetMapping("/service/stepInstance/appIds/{appId}/stepInstanceIds/{stepInstanceId}") - InternalResponse getStepInstance( - @RequestHeader("username") - String username, - @ApiParam(value = "作业平台业务ID", required = true) - @PathVariable(value = "appId") - Long appId, - @ApiParam(value = "步骤实例ID", name = "stepInstanceId", required = true) - @PathVariable("stepInstanceId") - Long stepInstanceId); -} From f355dad57e880c1550b7f3334aefcd1136a6766a Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 30 Jul 2024 09:42:47 +0800 Subject: [PATCH 021/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持文件分发任务报错信息分析 --- .../analysis/api/web/impl/WebAIResourceImpl.java | 13 ++++++++++++- .../job/analysis/dao/impl/AIChatHistoryDAOImpl.java | 2 +- .../ai/context/impl/FileTaskContextService.java | 10 +++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index db1f2606c9..7991e7274e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -43,6 +43,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.web.bind.annotation.RestController; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -87,9 +88,19 @@ public Response> getLatestChatHistoryList(String username, Integer start, Integer length) { List chatRecordList = chatService.getLatestChatHistoryList(username, start, length); - return Response.buildSuccessResp( + List aiChatRecordList = new ArrayList<>(); + aiChatRecordList.add(getStartRecord()); + aiChatRecordList.addAll( chatRecordList.stream().map(AIChatHistoryDTO::toAIChatRecord).collect(Collectors.toList()) ); + return Response.buildSuccessResp(aiChatRecordList); + } + + private AIChatRecord getStartRecord() { + AIChatRecord startRecord = new AIChatRecord(); + startRecord.setUserInput(null); + startRecord.setAiAnswer(new AIAnswer("0", null, "Hello", 0L)); + return startRecord; } @Override diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index a05e655046..860db334e3 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -141,7 +141,7 @@ private List listByConditions(Collection conditions ) .from(defaultTable) .where(conditions) - .orderBy(defaultTable.START_TIME.desc()); + .orderBy(defaultTable.START_TIME); return listPage(query, start, length, this::convertRecordToDto); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java index eca567c07f..2098e429fd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java @@ -52,20 +52,20 @@ @Service public class FileTaskContextService { - private final ServiceLogResource serviceLogResource; + private final ServiceLogResource logResource; private final FileTaskFailLogAnalyzer fileTaskFailLogAnalyzer; @Autowired - public FileTaskContextService(ServiceLogResource serviceLogResource, + public FileTaskContextService(ServiceLogResource logResource, FileTaskFailLogAnalyzer fileTaskFailLogAnalyzer) { - this.serviceLogResource = serviceLogResource; + this.logResource = logResource; this.fileTaskFailLogAnalyzer = fileTaskFailLogAnalyzer; } public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskContextQuery contextQuery) { String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); // 上传日志 - InternalResponse resp = serviceLogResource.getFileLogByExecuteObjectId( + InternalResponse resp = logResource.getFileLogByExecuteObjectId( jobCreateDate, contextQuery.getStepInstanceId(), contextQuery.getExecuteCount(), @@ -78,7 +78,7 @@ public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskConte } ServiceExecuteObjectLogDTO uploadExecuteObjectLog = resp.getData(); // 下载日志 - resp = serviceLogResource.getFileLogByExecuteObjectId( + resp = logResource.getFileLogByExecuteObjectId( jobCreateDate, contextQuery.getStepInstanceId(), contextQuery.getExecuteCount(), From d8906d1334b997bf282faf1727bd341d2f59c01d Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 30 Jul 2024 10:15:29 +0800 Subject: [PATCH 022/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持文件分发任务报错信息分析 --- .../com/tencent/bk/job/analysis/JobAnalysisBootApplication.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java b/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java index f7a6f34f3c..e9a5f3d274 100644 --- a/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java +++ b/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java @@ -40,6 +40,7 @@ basePackages = { "com.tencent.bk.job.manage.api", "com.tencent.bk.job.execute.api", + "com.tencent.bk.job.logsvr.api", "com.tencent.bk.job.crontab.api" } ) From e68c8c2991501404f22700188685443f13e03bc0 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 31 Jul 2024 14:41:11 +0800 Subject: [PATCH 023/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持文件分发任务报错信息分析 --- .../context/impl/FileTaskContextService.java | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java index 2098e429fd..ead3bd70ae 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java @@ -37,6 +37,7 @@ import com.tencent.bk.job.logsvr.api.ServiceLogResource; import com.tencent.bk.job.logsvr.consts.FileTaskModeEnum; import com.tencent.bk.job.logsvr.model.service.ServiceExecuteObjectLogDTO; +import com.tencent.bk.job.logsvr.model.service.ServiceFileLogQueryRequest; import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; import com.tencent.bk.job.logsvr.util.LogFieldUtil; import org.apache.commons.collections.CollectionUtils; @@ -65,20 +66,28 @@ public FileTaskContextService(ServiceLogResource logResource, public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskContextQuery contextQuery) { String jobCreateDate = LogFieldUtil.buildJobCreateDate(stepInstance.getCreateTime()); // 上传日志 - InternalResponse resp = logResource.getFileLogByExecuteObjectId( + ServiceFileLogQueryRequest request = new ServiceFileLogQueryRequest(); + request.setJobCreateDate(jobCreateDate); + request.setStepInstanceId(contextQuery.getStepInstanceId()); + request.setExecuteCount(contextQuery.getExecuteCount()); + request.setMode(FileDistModeEnum.UPLOAD.getValue()); + request.setBatch(contextQuery.getBatch()); + InternalResponse> uploadLogResp = logResource.listFileExecuteObjectLogs( jobCreateDate, contextQuery.getStepInstanceId(), contextQuery.getExecuteCount(), - null, - FileDistModeEnum.UPLOAD.getValue(), - contextQuery.getBatch() + request ); - if (!resp.isSuccess()) { - throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); + if (!uploadLogResp.isSuccess()) { + throw new ServiceException( + uploadLogResp.getErrorMsg(), + ErrorType.valOf(uploadLogResp.getErrorType()), + uploadLogResp.getCode() + ); } - ServiceExecuteObjectLogDTO uploadExecuteObjectLog = resp.getData(); + List uploadExecuteObjectLogList = uploadLogResp.getData(); // 下载日志 - resp = logResource.getFileLogByExecuteObjectId( + InternalResponse downloadLogResp = logResource.getFileLogByExecuteObjectId( jobCreateDate, contextQuery.getStepInstanceId(), contextQuery.getExecuteCount(), @@ -86,20 +95,26 @@ public TaskContext getTaskContext(ServiceStepInstanceDTO stepInstance, TaskConte FileDistModeEnum.DOWNLOAD.getValue(), contextQuery.getBatch() ); - if (!resp.isSuccess()) { - throw new ServiceException(resp.getErrorMsg(), ErrorType.valOf(resp.getErrorType()), resp.getCode()); + if (!downloadLogResp.isSuccess()) { + throw new ServiceException( + downloadLogResp.getErrorMsg(), + ErrorType.valOf(downloadLogResp.getErrorType()), + downloadLogResp.getCode() + ); } - ServiceExecuteObjectLogDTO downloadExecuteObjectLog = resp.getData(); - return buildContextForFileTask(stepInstance, uploadExecuteObjectLog, downloadExecuteObjectLog); + ServiceExecuteObjectLogDTO downloadExecuteObjectLog = downloadLogResp.getData(); + return buildContextForFileTask(stepInstance, uploadExecuteObjectLogList, downloadExecuteObjectLog); } private TaskContext buildContextForFileTask(ServiceStepInstanceDTO stepInstance, - ServiceExecuteObjectLogDTO uploadExecuteObjectLog, + List uploadExecuteObjectLogList, ServiceExecuteObjectLogDTO downloadExecuteObjectLog) { List fileTaskLogs = new ArrayList<>(); - List uploadFileTaskLogs = uploadExecuteObjectLog.getFileTaskLogs(); - if (CollectionUtils.isNotEmpty(uploadFileTaskLogs)) { - fileTaskLogs.addAll(uploadFileTaskLogs); + if (CollectionUtils.isNotEmpty(uploadExecuteObjectLogList)) { + uploadExecuteObjectLogList.forEach(uploadExecuteObjectLog -> { + List uploadFileTaskLogs = uploadExecuteObjectLog.getFileTaskLogs(); + fileTaskLogs.addAll(uploadFileTaskLogs); + }); } List downloadFileTaskLogs = downloadExecuteObjectLog.getFileTaskLogs(); if (CollectionUtils.isNotEmpty(downloadFileTaskLogs)) { From 268f971fe4c07de0ee60dcab2d4dcb951f11ebc3 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 31 Jul 2024 22:58:37 +0800 Subject: [PATCH 024/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持文件分发任务报错信息分析; 2.支持配置BK助手链接; 3.支持成功任务默认提示。 --- .../com/tencent/bk/job/common/config/BkConfig.java | 4 ++++ .../analysis/api/web/impl/WebAIResourceImpl.java | 5 +++-- .../context/constants/FileTaskErrorSourceEnum.java | 1 + .../ai/context/impl/FileTaskContextService.java | 2 +- .../ai/context/impl/FileTaskFailLogAnalyzer.java | 11 +++++++++-- .../ai/context/impl/TaskContextServiceImpl.java | 2 +- .../service/ai/context/model/TaskContext.java | 10 ++++++++++ .../service/ai/impl/AIAnalyzeErrorServiceImpl.java | 3 +++ .../analysis/service/ai/impl/AIBaseService.java | 14 ++++++++++++++ .../service/ai/impl/AITemplateVarService.java | 4 ++++ .../FileTransferTaskErrorAIPromptServiceImpl.java | 7 ++++++- .../ScriptExecuteTaskErrorAIPromptServiceImpl.java | 7 ++++++- .../model/inner/ServiceStepInstanceDTO.java | 3 +++ .../bk/job/execute/model/StepInstanceDTO.java | 1 + .../charts/bk-job/templates/configmap-common.yaml | 1 + support-files/kubernetes/charts/bk-job/values.yaml | 2 ++ ...08_job_analysis_20240618-1000_V3.10.0_mysql.sql | 2 +- 17 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/config/BkConfig.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/config/BkConfig.java index ed4d34545e..de28aa5207 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/config/BkConfig.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/config/BkConfig.java @@ -19,4 +19,8 @@ public class BkConfig { * 蓝鲸根域名。指蓝鲸产品公共 cookies 写入的目录,同时也是各个系统的公共域名部分。 */ private String bkDomain; + /** + * BK助手链接。 + */ + private String bkHelperLink = ""; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 7991e7274e..7b0b1f1668 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -32,6 +32,7 @@ import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; +import com.tencent.bk.job.analysis.model.web.resp.UserInput; import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; @@ -98,8 +99,8 @@ public Response> getLatestChatHistoryList(String username, private AIChatRecord getStartRecord() { AIChatRecord startRecord = new AIChatRecord(); - startRecord.setUserInput(null); - startRecord.setAiAnswer(new AIAnswer("0", null, "Hello", 0L)); + startRecord.setUserInput(new UserInput("", 0L)); + startRecord.setAiAnswer(new AIAnswer("0", null, "Hello, I`m BlueKing AI Assistant", 0L)); return startRecord; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java index 4e430b49a7..5d70d4b2b6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java @@ -28,6 +28,7 @@ * 文件任务失败原因来源枚举 */ public enum FileTaskErrorSourceEnum { + NO_ERROR("任务成功并未失败"), SOURCE_FILE_UPLOAD_ERROR("源文件上传出错导致的任务失败"), DOWNLOAD_ERROR("目标执行对象下载文件出错导致的任务失败"), UPLOAD_AND_DOWNLOAD_ERROR("源文件上传与目标执行对象下载文件均出错导致的任务失败"); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java index ead3bd70ae..51dd4055cf 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskContextService.java @@ -132,7 +132,7 @@ private TaskContext buildContextForFileTask(ServiceStepInstanceDTO stepInstance, // 对上传失败和下载失败的日志进行分析,确定导致任务失败的主要原因 FileTaskErrorSourceResult result = fileTaskFailLogAnalyzer.analyze(uploadFailLogs, downloadFailLogs); FileTaskContext fileTaskContext = new FileTaskContext(stepInstance.getFileStepInstance(), result); - return new TaskContext(stepInstance.getExecuteType(), null, fileTaskContext); + return new TaskContext(stepInstance.getExecuteType(), stepInstance.getStatus(), null, fileTaskContext); } private boolean isUploadFailLog(ServiceFileTaskLogDTO fileTaskLog) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java index ab3ee3f887..1b4bf8a53f 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java @@ -34,15 +34,22 @@ import java.util.stream.Collectors; /** - * 根据上传与下载失败的日志信息分析导致出错的源头 + * 文件分发任务失败日志分析器 */ @Service public class FileTaskFailLogAnalyzer { + /** + * 根据上传与下载失败的日志信息分析导致出错的源头 + * + * @param uploadFailLogs 上传失败日志 + * @param downloadFailLogs 下载失败日志 + * @return 分析结果 + */ public FileTaskErrorSourceResult analyze(List uploadFailLogs, List downloadFailLogs) { if (uploadFailLogs.isEmpty() && downloadFailLogs.isEmpty()) { - return new FileTaskErrorSourceResult(); + return new FileTaskErrorSourceResult(FileTaskErrorSourceEnum.NO_ERROR, uploadFailLogs, downloadFailLogs); } // 上传失败日志为空:说明是下载出错 if (uploadFailLogs.isEmpty()) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java index 459bfe640a..365db17406 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/TaskContextServiceImpl.java @@ -89,6 +89,6 @@ private TaskContext buildContextForScriptTask(ServiceStepInstanceDTO stepInstanc scriptStepInstance.getScriptContent(), scriptStepInstance.getScriptParam() ); - return new TaskContext(stepInstance.getExecuteType(), scriptTaskContext, null); + return new TaskContext(stepInstance.getExecuteType(), stepInstance.getStatus(), scriptTaskContext, null); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java index 9a9078fa06..bdc2742291 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java @@ -24,7 +24,9 @@ package com.tencent.bk.job.analysis.service.ai.context.model; +import com.tencent.bk.job.execute.common.constants.RunStatusEnum; import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; @@ -36,6 +38,10 @@ public class TaskContext { * 步骤类型 */ private Integer executeType; + /** + * 步骤状态 + */ + private Integer status; /** * 脚本任务上下文 */ @@ -55,4 +61,8 @@ public boolean isFileTask() { StepExecuteTypeEnum stepExecuteTypeEnum = StepExecuteTypeEnum.valOf(executeType); return stepExecuteTypeEnum == StepExecuteTypeEnum.SEND_FILE; } + + public boolean isSuccess() { + return RunStatusEnum.SUCCESS.getValue().equals(status); + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java index f3c62a31b3..7b24c71452 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -65,6 +65,9 @@ public AIAnalyzeErrorServiceImpl(TaskContextService taskContextService, public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { TaskContextQuery contextQuery = TaskContextQuery.fromAIAnalyzeErrorReq(appId, req); TaskContext taskContext = taskContextService.getTaskContext(username, contextQuery); + if (taskContext.isSuccess()) { + return getSimpleAIAnswer(username, "Task is success, do not need to analyze"); + } String errorContent = req.getContent(); AIPromptDTO aiPromptDTO; if (taskContext.isScriptTask()) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java index 20ced181ee..9c810d4b16 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -62,4 +62,18 @@ public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); return aiAnswer; } + + public AIAnswer getSimpleAIAnswer(String username, String content) { + long startTime = System.currentTimeMillis(); + AIAnswer aiAnswer = new AIAnswer("0", "", content, System.currentTimeMillis()); + AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( + username, + startTime, + content, + content, + aiAnswer + ); + aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + return aiAnswer; + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java index f04e11e50e..90d9f3ae8c 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AITemplateVarService.java @@ -64,4 +64,8 @@ public String getUploadFileErrorDataPlaceHolder() { public String getDownloadFileErrorDataPlaceHolder() { return getTemplateVarPlaceHolder("download_file_error_data"); } + + public String getBkHelperLinkPlaceHolder() { + return getTemplateVarPlaceHolder("bk_helper_link"); + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java index e846ff0473..32dd7204d0 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java @@ -30,6 +30,7 @@ import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; import com.tencent.bk.job.analysis.service.ai.FileTransferTaskErrorAIPromptService; import com.tencent.bk.job.analysis.service.ai.context.model.FileTaskContext; +import com.tencent.bk.job.common.config.BkConfig; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -43,12 +44,15 @@ public class FileTransferTaskErrorAIPromptServiceImpl extends AIBasePromptServic implements FileTransferTaskErrorAIPromptService { private final AITemplateVarService aiTemplateVarService; + private final BkConfig bkConfig; @Autowired public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO, - AITemplateVarService aiTemplateVarService) { + AITemplateVarService aiTemplateVarService, + BkConfig bkConfig) { super(aiPromptTemplateDAO); this.aiTemplateVarService = aiTemplateVarService; + this.bkConfig = bkConfig; } @Override @@ -61,6 +65,7 @@ public AIPromptDTO getPrompt(FileTaskContext context) { private String renderPrompt(String promptTemplateContent, FileTaskContext context) { return promptTemplateContent + .replace(aiTemplateVarService.getBkHelperLinkPlaceHolder(), bkConfig.getBkHelperLink()) .replace(aiTemplateVarService.getFileTaskErrorSourcePlaceHolder(), context.getFileTaskErrorSource()) .replace(aiTemplateVarService.getUploadFileErrorDataPlaceHolder(), context.getUploadFileErrorData()) .replace(aiTemplateVarService.getDownloadFileErrorDataPlaceHolder(), context.getDownloadFileErrorData()); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java index 7baad0b751..1fa5c85892 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ScriptExecuteTaskErrorAIPromptServiceImpl.java @@ -30,6 +30,7 @@ import com.tencent.bk.job.analysis.model.dto.AIPromptTemplateDTO; import com.tencent.bk.job.analysis.service.ai.ScriptExecuteTaskErrorAIPromptService; import com.tencent.bk.job.analysis.service.ai.context.model.ScriptTaskContext; +import com.tencent.bk.job.common.config.BkConfig; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -44,12 +45,15 @@ public class ScriptExecuteTaskErrorAIPromptServiceImpl extends AIBasePromptServi implements ScriptExecuteTaskErrorAIPromptService { private final AITemplateVarService aiTemplateVarService; + private final BkConfig bkConfig; @Autowired public ScriptExecuteTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO, - AITemplateVarService aiTemplateVarService) { + AITemplateVarService aiTemplateVarService, + BkConfig bkConfig) { super(aiPromptTemplateDAO); this.aiTemplateVarService = aiTemplateVarService; + this.bkConfig = bkConfig; } @Override @@ -64,6 +68,7 @@ private String renderPrompt(String promptTemplateContent, ScriptTaskContext context, String errorContent) { return promptTemplateContent + .replace(aiTemplateVarService.getBkHelperLinkPlaceHolder(), bkConfig.getBkHelperLink()) .replace(aiTemplateVarService.getScriptTypePlaceHolder(), ScriptTypeEnum.getName(context.getScriptType())) .replace(aiTemplateVarService.getScriptParamsPlaceHolder(), context.getScriptParams()) .replace(aiTemplateVarService.getErrorContentPlaceHolder(), errorContent) diff --git a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java index d2227c87d1..1590e4339c 100644 --- a/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java +++ b/src/backend/job-execute/api-job-execute/src/main/java/com/tencent/bk/job/execute/model/inner/ServiceStepInstanceDTO.java @@ -39,6 +39,9 @@ public class ServiceStepInstanceDTO { @ApiModelProperty("步骤类型") private Integer executeType; + @ApiModelProperty("步骤状态") + private Integer status; + @ApiModelProperty("创建时间") private Long createTime; diff --git a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java index ead4f43383..fcb1fa2d97 100644 --- a/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java +++ b/src/backend/job-execute/service-job-execute/src/main/java/com/tencent/bk/job/execute/model/StepInstanceDTO.java @@ -320,6 +320,7 @@ public ServiceStepInstanceDTO toServiceStepInstanceDTO() { ServiceStepInstanceDTO serviceStepInstanceDTO = new ServiceStepInstanceDTO(); serviceStepInstanceDTO.setId(id); serviceStepInstanceDTO.setExecuteType(executeType.getValue()); + serviceStepInstanceDTO.setStatus(status.getValue()); serviceStepInstanceDTO.setCreateTime(createTime); if (executeType == StepExecuteTypeEnum.EXECUTE_SCRIPT || executeType == StepExecuteTypeEnum.EXECUTE_SQL) { ServiceScriptStepInstanceDTO scriptStepInstance = new ServiceScriptStepInstanceDTO(); diff --git a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml index f4204bd483..ccc8325873 100644 --- a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml +++ b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml @@ -29,6 +29,7 @@ data: traceIdRatioBased: {{ .Values.job.trace.report.ratio }} bk: bkDomain: {{ .Values.global.bkDomain }} + bkHelperLink: {{ .Values.global.bkHelperLink }} app: code: {{ .Values.appCode }} secret: {{ .Values.appSecret }} diff --git a/support-files/kubernetes/charts/bk-job/values.yaml b/support-files/kubernetes/charts/bk-job/values.yaml index d1f9f4159b..5626c509fb 100644 --- a/support-files/kubernetes/charts/bk-job/values.yaml +++ b/support-files/kubernetes/charts/bk-job/values.yaml @@ -18,6 +18,8 @@ global: storageClass: "" # 蓝鲸根域名。指蓝鲸产品公共 cookies 写入的目录,同时也是各个系统的公共域名部分 bkDomain: "example.com" + # BK助手链接,用于AI对话回答中的跳转链接 + bkHelperLink: "" deploy: ## 部署方式。支持标准(standard) 和 轻量化部署(lite)方式 mode: standard diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index bdb0519ecd..a956768b17 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -58,4 +58,4 @@ REPLACE INTO job_analysis.ai_prompt_template VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能一:脚本批量执行】\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前任务执行的脚本内容为:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n脚本参数为:\n{BK_JOB_AI_TEMPLATE_VAR{script_params}}\n\n报错信息如下:\n{BK_JOB_AI_TEMPLATE_VAR{error_content}}\n\n请回答用户的提问:\n任务报错内容解析', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) -VALUES(3, 'ANALYZE_FILE_TRANSFER_TASK_ERROR', 'zh_CN', '分析文件分发任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能:文件批量传输】\n用户在Web页面填写任务名称、超时时长、上传限速、下载限速、源文件、目标路径、传输模式、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务状态与传输进度日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与进度日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前已经结合任务上下文信息进行了预分析,得到以下结果:\n(1)当前任务失败的主要原因为:\n{BK_JOB_AI_TEMPLATE_VAR{error_source}}\n\n(2)源文件上传失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{upload_file_error_data}}\n\n(3)目标机器下载失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{download_file_error_data}}\n\n\n请回答用户的提问:\n请你结合预分析结果数据,对任务报错内容进一步分析,并给出总结性的原因与处理建议', '系统内置的AI命令提示模板:分析文件分发任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +VALUES(3, 'ANALYZE_FILE_TRANSFER_TASK_ERROR', 'zh_CN', '分析文件分发任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能:文件批量传输】\n用户在Web页面填写任务名称、超时时长、上传限速、下载限速、源文件、目标路径、传输模式、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务状态与传输进度日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与进度日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前已经结合任务上下文信息进行了预分析,得到以下结果:\n(1)当前任务失败的主要原因为:\n{BK_JOB_AI_TEMPLATE_VAR{file_task_error_source}}\n\n(2)源文件上传失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{upload_file_error_data}}\n\n(3)目标机器下载失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{download_file_error_data}}\n\n\n请回答用户的提问:\n请你结合预分析结果数据,对任务报错内容进一步分析,并给出总结性的原因与处理建议', '系统内置的AI命令提示模板:分析文件分发任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); From f0a71ed20c59f0e6af44fcedfc09964ec79dcf56 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 1 Aug 2024 17:44:38 +0800 Subject: [PATCH 025/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持非失败状态任务直接答复; 2.支持任务失败原因描述的国际化处理。 --- .../dao/impl/AIChatHistoryDAOImpl.java | 2 +- .../constants/FileTaskErrorSourceEnum.java | 31 ++++++++-- .../context/impl/FileTaskFailLogAnalyzer.java | 8 ++- .../ai/context/model/FileTaskContext.java | 4 +- .../service/ai/context/model/TaskContext.java | 5 +- .../ai/impl/AIAnalyzeErrorServiceImpl.java | 22 +++++-- .../service/ai/impl/AIBaseService.java | 32 +++++++++- .../ai/impl/AIChatHistoryServiceImpl.java | 24 +++++++- .../service/ai/impl/AIMessageI18nService.java | 59 +++++++++++++++++++ ...eTransferTaskErrorAIPromptServiceImpl.java | 10 +++- .../main/resources/i18n/message.properties | 7 +++ .../main/resources/i18n/message_en.properties | 7 +++ .../resources/i18n/message_en_US.properties | 7 +++ .../main/resources/i18n/message_zh.properties | 7 +++ .../resources/i18n/message_zh_CN.properties | 7 +++ 15 files changed, 208 insertions(+), 24 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index 860db334e3..a05e655046 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -141,7 +141,7 @@ private List listByConditions(Collection conditions ) .from(defaultTable) .where(conditions) - .orderBy(defaultTable.START_TIME); + .orderBy(defaultTable.START_TIME.desc()); return listPage(query, start, length, this::convertRecordToDto); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java index 5d70d4b2b6..32d3afb3bb 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/constants/FileTaskErrorSourceEnum.java @@ -24,21 +24,42 @@ package com.tencent.bk.job.analysis.service.ai.context.constants; +import lombok.Getter; + /** * 文件任务失败原因来源枚举 */ +@Getter public enum FileTaskErrorSourceEnum { - NO_ERROR("任务成功并未失败"), - SOURCE_FILE_UPLOAD_ERROR("源文件上传出错导致的任务失败"), - DOWNLOAD_ERROR("目标执行对象下载文件出错导致的任务失败"), - UPLOAD_AND_DOWNLOAD_ERROR("源文件上传与目标执行对象下载文件均出错导致的任务失败"); + NO_ERROR( + "job.analysis.ai.fileTaskErrorSource.noError", + "任务成功并未失败" + ), + SOURCE_FILE_UPLOAD_ERROR( + "job.analysis.ai.fileTaskErrorSource.sourceFileUploadError", + "源文件上传出错导致的任务失败" + ), + DOWNLOAD_ERROR( + "job.analysis.ai.fileTaskErrorSource.downloadError", + "目标执行对象下载文件出错导致的任务失败" + ), + UPLOAD_AND_DOWNLOAD_ERROR( + "job.analysis.ai.fileTaskErrorSource.uploadAndDownloadError", + "源文件上传与目标执行对象下载文件均出错导致的任务失败" + ); + + /** + * 任务失败原因描述国际化key + */ + private final String i18nKey; /** * 任务失败原因描述 */ private final String description; - FileTaskErrorSourceEnum(String description) { + FileTaskErrorSourceEnum(String i18nKey, String description) { + this.i18nKey = i18nKey; this.description = description; } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java index 1b4bf8a53f..13b174cd3f 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/impl/FileTaskFailLogAnalyzer.java @@ -29,6 +29,7 @@ import com.tencent.bk.job.logsvr.model.service.ServiceFileTaskLogDTO; import org.springframework.stereotype.Service; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -71,8 +72,11 @@ public FileTaskErrorSourceResult analyze(List uploadFailL downloadFailSrcFilePathSet.removeAll(uploadFailFilePathSet); if (downloadFailSrcFilePathSet.isEmpty()) { // 下载失败日志关联的上传文件任务均失败:说明根本原因是源文件上传出错 - return new FileTaskErrorSourceResult(FileTaskErrorSourceEnum.SOURCE_FILE_UPLOAD_ERROR, uploadFailLogs, - downloadFailLogs); + return new FileTaskErrorSourceResult( + FileTaskErrorSourceEnum.SOURCE_FILE_UPLOAD_ERROR, + uploadFailLogs, + Collections.emptyList() + ); } else { // 上传失败日志与下载失败日志均不为空,且下载失败日志并不全是上传失败导致:说明根本原因是上传下载同时出错 // 筛选出由于下载方出错导致的下载失败日志 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java index b88e493c9d..89dd09602e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/FileTaskContext.java @@ -93,8 +93,8 @@ private void buildExecuteObjectMapIfNeeded() { } } - public String getFileTaskErrorSource() { - return errorSourceResult.getErrorSource().name(); + public String getFileTaskErrorSourceI18nKey() { + return errorSourceResult.getErrorSource().getI18nKey(); } @SuppressWarnings("DuplicatedCode") diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java index bdc2742291..52c476ef3a 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/TaskContext.java @@ -26,7 +26,6 @@ import com.tencent.bk.job.execute.common.constants.RunStatusEnum; import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; -import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; @@ -62,7 +61,7 @@ public boolean isFileTask() { return stepExecuteTypeEnum == StepExecuteTypeEnum.SEND_FILE; } - public boolean isSuccess() { - return RunStatusEnum.SUCCESS.getValue().equals(status); + public boolean isTaskFail() { + return RunStatusEnum.FAIL.getValue().equals(status); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java index 7b24c71452..cb12163fbe 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -48,26 +48,34 @@ public class AIAnalyzeErrorServiceImpl extends AIBaseService implements AIAnalyz private final TaskContextService taskContextService; private final ScriptExecuteTaskErrorAIPromptService scriptExecuteTaskErrorAIPromptService; private final FileTransferTaskErrorAIPromptService fileTransferTaskErrorAIPromptService; + private final AIMessageI18nService aiMessageI18nService; @Autowired public AIAnalyzeErrorServiceImpl(TaskContextService taskContextService, ScriptExecuteTaskErrorAIPromptService scriptExecuteTaskErrorAIPromptService, FileTransferTaskErrorAIPromptService fileTransferTaskErrorAIPromptService, AIService aiService, - AIChatHistoryService aiChatHistoryService) { + AIChatHistoryService aiChatHistoryService, + AIMessageI18nService aiMessageI18nService) { super(aiService, aiChatHistoryService); this.taskContextService = taskContextService; this.scriptExecuteTaskErrorAIPromptService = scriptExecuteTaskErrorAIPromptService; this.fileTransferTaskErrorAIPromptService = fileTransferTaskErrorAIPromptService; + this.aiMessageI18nService = aiMessageI18nService; } + /** + * 通过AI分析任务报错信息,并记录对话历史 + * + * @param username 用户名 + * @param appId Job业务ID + * @param req 请求内容 + * @return AI回答 + */ @Override public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { TaskContextQuery contextQuery = TaskContextQuery.fromAIAnalyzeErrorReq(appId, req); TaskContext taskContext = taskContextService.getTaskContext(username, contextQuery); - if (taskContext.isSuccess()) { - return getSimpleAIAnswer(username, "Task is success, do not need to analyze"); - } String errorContent = req.getContent(); AIPromptDTO aiPromptDTO; if (taskContext.isScriptTask()) { @@ -75,8 +83,14 @@ public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { taskContext.getScriptTaskContext(), errorContent ); + if (!taskContext.isTaskFail()) { + return getDirectlyAIAnswer(username, aiPromptDTO, aiMessageI18nService.getNotFailTaskAIAnswerMessage()); + } } else if (taskContext.isFileTask()) { aiPromptDTO = fileTransferTaskErrorAIPromptService.getPrompt(taskContext.getFileTaskContext()); + if (!taskContext.isTaskFail()) { + return getDirectlyAIAnswer(username, aiPromptDTO, aiMessageI18nService.getNotFailTaskAIAnswerMessage()); + } } else { throw new InvalidParamException(ErrorCode.AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java index 9c810d4b16..3315cfc2d4 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -47,6 +47,13 @@ public AIBaseService(AIService aiService, this.aiChatHistoryService = aiChatHistoryService; } + /** + * 使用AI提示符调用AI接口生成AI回答 + * + * @param username 用户名 + * @param aiPromptDTO AI提示符 + * @return AI回答 + */ public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { long startTime = System.currentTimeMillis(); String rawPrompt = aiPromptDTO.getRawPrompt(); @@ -63,17 +70,36 @@ public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { return aiAnswer; } - public AIAnswer getSimpleAIAnswer(String username, String content) { + /** + * 使用指定内容直接生成AI回答 + * + * @param username 用户名 + * @param aiPromptDTO AI提示符 + * @param content 指定内容 + * @return AI回答 + */ + public AIAnswer getDirectlyAIAnswer(String username, AIPromptDTO aiPromptDTO, String content) { long startTime = System.currentTimeMillis(); + String rawPrompt = aiPromptDTO.getRawPrompt(); AIAnswer aiAnswer = new AIAnswer("0", "", content, System.currentTimeMillis()); AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( username, startTime, - content, - content, + rawPrompt, + buildAIDirectlyAnswerInput(content), aiAnswer ); aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); return aiAnswer; } + + /** + * 构建直接回答的AI输入(无需调用AI生成回答) + * + * @param content 回答内容 + * @return AI输入 + */ + private String buildAIDirectlyAnswerInput(String content) { + return "Answer This Directly:" + content; + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index 0d7c9374ab..8ee7aec7b6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -32,8 +32,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Comparator; import java.util.List; +/** + * AI聊天记录管理服务 + */ @Slf4j @Service public class AIChatHistoryServiceImpl implements AIChatHistoryService { @@ -71,15 +75,31 @@ public Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { return aiChatHistoryDAO.insertAIChatHistory(aiChatHistoryDTO); } + /** + * 从DB获取最近的聊天记录列表,按起始时间升序排列 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近的聊天记录列表 + */ @Override public List getLatestChatHistoryList(String username, Integer start, Integer length) { - return aiChatHistoryDAO.getLatestChatHistoryList(username, start, length); + List aiChatHistoryList = aiChatHistoryDAO.getLatestChatHistoryList(username, start, length); + aiChatHistoryList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); + return aiChatHistoryList; } + /** + * 从DB中分批软删除聊天记录(优先删除创建时间较早的) + * + * @param username 用户名 + * @return 删除的记录条数 + */ @Override public int softDeleteChatHistory(String username) { int batchSize = 10000; - int deletedCount = 0; + int deletedCount; int deletedTotalCount = 0; do { deletedCount = aiChatHistoryDAO.softDeleteChatHistory(username, batchSize); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java new file mode 100644 index 0000000000..4a13238783 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.common.i18n.service.MessageI18nService; +import org.springframework.stereotype.Service; + +/** + * AI消息国际化服务 + */ +@Service +public class AIMessageI18nService { + private final MessageI18nService messageI18nService; + + public AIMessageI18nService(MessageI18nService messageI18nService) { + this.messageI18nService = messageI18nService; + } + + /** + * 获取非失败状态任务的AI分析结果信息 + * + * @return 国际化的AI分析结果信息 + */ + public String getNotFailTaskAIAnswerMessage() { + return messageI18nService.getI18n("job.analysis.ai.notFailTaskAnswerMessage"); + } + + /** + * 根据国际化Key获取国际化信息 + * + * @param i18nKey 国际化Key + * @return 国际化信息 + */ + public String getI18nMessage(String i18nKey) { + return messageI18nService.getI18n(i18nKey); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java index 32dd7204d0..9c90c8e6a8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/FileTransferTaskErrorAIPromptServiceImpl.java @@ -45,14 +45,17 @@ public class FileTransferTaskErrorAIPromptServiceImpl extends AIBasePromptServic private final AITemplateVarService aiTemplateVarService; private final BkConfig bkConfig; + private final AIMessageI18nService aiMessageI18nService; @Autowired public FileTransferTaskErrorAIPromptServiceImpl(AIPromptTemplateDAO aiPromptTemplateDAO, AITemplateVarService aiTemplateVarService, - BkConfig bkConfig) { + BkConfig bkConfig, + AIMessageI18nService aiMessageI18nService) { super(aiPromptTemplateDAO); this.aiTemplateVarService = aiTemplateVarService; this.bkConfig = bkConfig; + this.aiMessageI18nService = aiMessageI18nService; } @Override @@ -66,7 +69,10 @@ public AIPromptDTO getPrompt(FileTaskContext context) { private String renderPrompt(String promptTemplateContent, FileTaskContext context) { return promptTemplateContent .replace(aiTemplateVarService.getBkHelperLinkPlaceHolder(), bkConfig.getBkHelperLink()) - .replace(aiTemplateVarService.getFileTaskErrorSourcePlaceHolder(), context.getFileTaskErrorSource()) + .replace( + aiTemplateVarService.getFileTaskErrorSourcePlaceHolder(), + aiMessageI18nService.getI18nMessage(context.getFileTaskErrorSourceI18nKey()) + ) .replace(aiTemplateVarService.getUploadFileErrorDataPlaceHolder(), context.getUploadFileErrorData()) .replace(aiTemplateVarService.getDownloadFileErrorDataPlaceHolder(), context.getDownloadFileErrorData()); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties index a85c77f966..9885178a5e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties @@ -30,3 +30,10 @@ job.analysis.analysistask.result.BadTplPlanInfo.typeName.TaskPlan=作业执行 job.analysis.analysistask.result.BadTplPlanInfo.typeName.Template=作业模板 job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=步骤 job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=执行目标异常 + +# AI分析部分 +job.analysis.ai.notFailTaskAnswerMessage=只有状态为“执行失败”的任务才需要进行分析,当前任务无需分析 +job.analysis.ai.fileTaskErrorSource.noError=任务成功并未失败 +job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=源文件上传出错导致的任务失败 +job.analysis.ai.fileTaskErrorSource.downloadError=目标执行对象下载文件出错导致的任务失败 +job.analysis.ai.fileTaskErrorSource.uploadAndDownloadError=源文件上传与目标执行对象下载文件均出错导致的任务失败 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties index 86bdee6fa3..528e26eae6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties @@ -30,3 +30,10 @@ job.analysis.analysistask.result.BadTplPlanInfo.typeName.TaskPlan=TaskPlan job.analysis.analysistask.result.BadTplPlanInfo.typeName.Template=Template job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=Steps job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=Abnormal hosts + +# AI分析部分 +job.analysis.ai.notFailTaskAnswerMessage=Task is not in fail status, do not need to analyze +job.analysis.ai.fileTaskErrorSource.noError=No error +job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=Source execute object upload file error +job.analysis.ai.fileTaskErrorSource.downloadError=Target execute object download file error +job.analysis.ai.fileTaskErrorSource.uploadAndDownloadError=Source execute object upload file error and Target execute object download file error diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties index 86bdee6fa3..528e26eae6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties @@ -30,3 +30,10 @@ job.analysis.analysistask.result.BadTplPlanInfo.typeName.TaskPlan=TaskPlan job.analysis.analysistask.result.BadTplPlanInfo.typeName.Template=Template job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=Steps job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=Abnormal hosts + +# AI分析部分 +job.analysis.ai.notFailTaskAnswerMessage=Task is not in fail status, do not need to analyze +job.analysis.ai.fileTaskErrorSource.noError=No error +job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=Source execute object upload file error +job.analysis.ai.fileTaskErrorSource.downloadError=Target execute object download file error +job.analysis.ai.fileTaskErrorSource.uploadAndDownloadError=Source execute object upload file error and Target execute object download file error diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties index a85c77f966..9885178a5e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties @@ -30,3 +30,10 @@ job.analysis.analysistask.result.BadTplPlanInfo.typeName.TaskPlan=作业执行 job.analysis.analysistask.result.BadTplPlanInfo.typeName.Template=作业模板 job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=步骤 job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=执行目标异常 + +# AI分析部分 +job.analysis.ai.notFailTaskAnswerMessage=只有状态为“执行失败”的任务才需要进行分析,当前任务无需分析 +job.analysis.ai.fileTaskErrorSource.noError=任务成功并未失败 +job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=源文件上传出错导致的任务失败 +job.analysis.ai.fileTaskErrorSource.downloadError=目标执行对象下载文件出错导致的任务失败 +job.analysis.ai.fileTaskErrorSource.uploadAndDownloadError=源文件上传与目标执行对象下载文件均出错导致的任务失败 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties index a85c77f966..9885178a5e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties @@ -30,3 +30,10 @@ job.analysis.analysistask.result.BadTplPlanInfo.typeName.TaskPlan=作业执行 job.analysis.analysistask.result.BadTplPlanInfo.typeName.Template=作业模板 job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=步骤 job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=执行目标异常 + +# AI分析部分 +job.analysis.ai.notFailTaskAnswerMessage=只有状态为“执行失败”的任务才需要进行分析,当前任务无需分析 +job.analysis.ai.fileTaskErrorSource.noError=任务成功并未失败 +job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=源文件上传出错导致的任务失败 +job.analysis.ai.fileTaskErrorSource.downloadError=目标执行对象下载文件出错导致的任务失败 +job.analysis.ai.fileTaskErrorSource.uploadAndDownloadError=源文件上传与目标执行对象下载文件均出错导致的任务失败 From 31331427d79db1dc8de5291464c018e53fdc2621 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 1 Aug 2024 17:56:02 +0800 Subject: [PATCH 026/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持AI问候语国际化。 --- .../api/web/impl/WebAIResourceImpl.java | 27 ++++++++++++++----- .../service/ai/impl/AIMessageI18nService.java | 4 +++ .../main/resources/i18n/message.properties | 1 + .../main/resources/i18n/message_en.properties | 1 + .../resources/i18n/message_en_US.properties | 1 + .../main/resources/i18n/message_zh.properties | 1 + .../resources/i18n/message_zh_CN.properties | 1 + 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 7b0b1f1668..e568aaac8d 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -37,6 +37,7 @@ import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; import com.tencent.bk.job.analysis.service.ai.ChatService; +import com.tencent.bk.job.analysis.service.ai.impl.AIMessageI18nService; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; import lombok.extern.slf4j.Slf4j; @@ -59,16 +60,19 @@ public class WebAIResourceImpl implements WebAIResource { private final AICheckScriptService aiCheckScriptService; private final AIAnalyzeErrorService aiAnalyzeErrorService; private final AIChatHistoryService aiChatHistoryService; + private final AIMessageI18nService aiMessageI18nService; @Autowired public WebAIResourceImpl(ChatService chatService, AICheckScriptService aiCheckScriptService, AIAnalyzeErrorService aiAnalyzeErrorService, - AIChatHistoryService aiChatHistoryService) { + AIChatHistoryService aiChatHistoryService, + AIMessageI18nService aiMessageI18nService) { this.chatService = chatService; this.aiCheckScriptService = aiCheckScriptService; this.aiAnalyzeErrorService = aiAnalyzeErrorService; this.aiChatHistoryService = aiChatHistoryService; + this.aiMessageI18nService = aiMessageI18nService; } @Override @@ -90,18 +94,27 @@ public Response> getLatestChatHistoryList(String username, Integer length) { List chatRecordList = chatService.getLatestChatHistoryList(username, start, length); List aiChatRecordList = new ArrayList<>(); - aiChatRecordList.add(getStartRecord()); + aiChatRecordList.add(getGreetingRecord()); aiChatRecordList.addAll( chatRecordList.stream().map(AIChatHistoryDTO::toAIChatRecord).collect(Collectors.toList()) ); return Response.buildSuccessResp(aiChatRecordList); } - private AIChatRecord getStartRecord() { - AIChatRecord startRecord = new AIChatRecord(); - startRecord.setUserInput(new UserInput("", 0L)); - startRecord.setAiAnswer(new AIAnswer("0", null, "Hello, I`m BlueKing AI Assistant", 0L)); - return startRecord; + private AIChatRecord getGreetingRecord() { + AIChatRecord greetingRecord = new AIChatRecord(); + greetingRecord.setUserInput(new UserInput("", 0L)); + greetingRecord.setAiAnswer(getGreetingAIAnswer()); + return greetingRecord; + } + + private AIAnswer getGreetingAIAnswer() { + return new AIAnswer( + "0", + null, + aiMessageI18nService.getAIGreetingMessage(), + 0L + ); } @Override diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java index 4a13238783..76096752da 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIMessageI18nService.java @@ -38,6 +38,10 @@ public AIMessageI18nService(MessageI18nService messageI18nService) { this.messageI18nService = messageI18nService; } + public String getAIGreetingMessage() { + return messageI18nService.getI18n("job.analysis.ai.greetingMessage"); + } + /** * 获取非失败状态任务的AI分析结果信息 * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties index 9885178a5e..09820a11fd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message.properties @@ -32,6 +32,7 @@ job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=步 job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=执行目标异常 # AI分析部分 +job.analysis.ai.greetingMessage=你好,我是AI小鲸 job.analysis.ai.notFailTaskAnswerMessage=只有状态为“执行失败”的任务才需要进行分析,当前任务无需分析 job.analysis.ai.fileTaskErrorSource.noError=任务成功并未失败 job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=源文件上传出错导致的任务失败 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties index 528e26eae6..5fb4356cbd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en.properties @@ -32,6 +32,7 @@ job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=Step job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=Abnormal hosts # AI分析部分 +job.analysis.ai.greetingMessage=Hello, I`m BlueKing AI Assistant job.analysis.ai.notFailTaskAnswerMessage=Task is not in fail status, do not need to analyze job.analysis.ai.fileTaskErrorSource.noError=No error job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=Source execute object upload file error diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties index 528e26eae6..5fb4356cbd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_en_US.properties @@ -32,6 +32,7 @@ job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=Step job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=Abnormal hosts # AI分析部分 +job.analysis.ai.greetingMessage=Hello, I`m BlueKing AI Assistant job.analysis.ai.notFailTaskAnswerMessage=Task is not in fail status, do not need to analyze job.analysis.ai.fileTaskErrorSource.noError=No error job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=Source execute object upload file error diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties index 9885178a5e..09820a11fd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh.properties @@ -32,6 +32,7 @@ job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=步 job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=执行目标异常 # AI分析部分 +job.analysis.ai.greetingMessage=你好,我是AI小鲸 job.analysis.ai.notFailTaskAnswerMessage=只有状态为“执行失败”的任务才需要进行分析,当前任务无需分析 job.analysis.ai.fileTaskErrorSource.noError=任务成功并未失败 job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=源文件上传出错导致的任务失败 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties index 9885178a5e..09820a11fd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties +++ b/src/backend/job-analysis/service-job-analysis/src/main/resources/i18n/message_zh_CN.properties @@ -32,6 +32,7 @@ job.analysis.analysistask.result.AbnormalTargetPlanInfo.stepName.scope.Step=步 job.analysis.analysistask.result.AbnormalTargetPlanInfo.description.AbnormalTarget=执行目标异常 # AI分析部分 +job.analysis.ai.greetingMessage=你好,我是AI小鲸 job.analysis.ai.notFailTaskAnswerMessage=只有状态为“执行失败”的任务才需要进行分析,当前任务无需分析 job.analysis.ai.fileTaskErrorSource.noError=任务成功并未失败 job.analysis.ai.fileTaskErrorSource.sourceFileUploadError=源文件上传出错导致的任务失败 From f503e640deeff3aa68e1b778512f0540db4e4a14 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 1 Aug 2024 20:13:21 +0800 Subject: [PATCH 027/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持配置AI相关参数。 --- .../analysis/JobAnalysisBootApplication.java | 3 + .../api/web/impl/WebAIResourceImpl.java | 11 ++-- .../bk/job/analysis/config/AIProperties.java | 58 +++++++++++++++++++ .../service/ai/impl/AIConfigService.java | 53 +++++++++++++++++ .../kubernetes/charts/bk-job/VALUES_LOG.md | 14 +++++ .../bk-job/templates/configmap-common.yaml | 7 +++ .../kubernetes/charts/bk-job/values.yaml | 9 +++ 7 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java diff --git a/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java b/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java index e9a5f3d274..a9bbb63fa5 100644 --- a/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java +++ b/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java @@ -24,10 +24,12 @@ package com.tencent.bk.job.analysis; +import com.tencent.bk.job.analysis.config.AIProperties; import com.tencent.bk.job.common.service.boot.JobBootApplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.scheduling.annotation.EnableScheduling; @@ -45,6 +47,7 @@ } ) @EnableScheduling +@EnableConfigurationProperties(AIProperties.class) public class JobAnalysisBootApplication { public static void main(String[] args) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index e568aaac8d..a06924ae12 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -37,6 +37,7 @@ import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; import com.tencent.bk.job.analysis.service.ai.ChatService; +import com.tencent.bk.job.analysis.service.ai.impl.AIConfigService; import com.tencent.bk.job.analysis.service.ai.impl.AIMessageI18nService; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; @@ -46,7 +47,6 @@ import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -56,6 +56,7 @@ @Slf4j public class WebAIResourceImpl implements WebAIResource { + private final AIConfigService aiConfigService; private final ChatService chatService; private final AICheckScriptService aiCheckScriptService; private final AIAnalyzeErrorService aiAnalyzeErrorService; @@ -63,11 +64,13 @@ public class WebAIResourceImpl implements WebAIResource { private final AIMessageI18nService aiMessageI18nService; @Autowired - public WebAIResourceImpl(ChatService chatService, + public WebAIResourceImpl(AIConfigService aiConfigService, + ChatService chatService, AICheckScriptService aiCheckScriptService, AIAnalyzeErrorService aiAnalyzeErrorService, AIChatHistoryService aiChatHistoryService, AIMessageI18nService aiMessageI18nService) { + this.aiConfigService = aiConfigService; this.chatService = chatService; this.aiCheckScriptService = aiCheckScriptService; this.aiAnalyzeErrorService = aiAnalyzeErrorService; @@ -80,9 +83,7 @@ public Response> getAIConfig(String username, AppResourceScope appResourceScope, String scopeType, String scopeId) { - Map map = new HashMap<>(); - map.put("analyzeErrorLogMaxLength", 5 * 1024 * 1024L); - return Response.buildSuccessResp(map); + return Response.buildSuccessResp(aiConfigService.getAIConfig()); } @Override diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java new file mode 100644 index 0000000000..724d4f4316 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.config; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * AI相关配置 + */ +@Getter +@Setter +@ToString +@ConfigurationProperties(prefix = "ai") +public class AIProperties { + + private Boolean enabled = false; + + /** + * 错误日志分析相关配置 + */ + private AnalyzeErrorLogConfig analyzeErrorLog; + + @Getter + @Setter + @ToString + public static class AnalyzeErrorLogConfig { + /** + * 支持分析的错误日志最大长度 + */ + private String logMaxLength = "5MB"; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java new file mode 100644 index 0000000000..b3422a3bac --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.config.AIProperties; +import com.tencent.bk.job.common.util.file.FileSizeUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class AIConfigService { + private final AIProperties aiProperties; + + @Autowired + public AIConfigService(AIProperties aiProperties) { + this.aiProperties = aiProperties; + } + + public Map getAIConfig() { + Map map = new HashMap<>(); + String logMaxLengthStr = aiProperties.getAnalyzeErrorLog().getLogMaxLength(); + long logMaxLengthBytes = FileSizeUtil.parseFileSizeBytes(logMaxLengthStr); + map.put("enabled", aiProperties.getEnabled()); + map.put("analyzeErrorLogMaxLength", logMaxLengthBytes); + return map; + } + +} diff --git a/support-files/kubernetes/charts/bk-job/VALUES_LOG.md b/support-files/kubernetes/charts/bk-job/VALUES_LOG.md index ebed01d58e..34ee7724c7 100644 --- a/support-files/kubernetes/charts/bk-job/VALUES_LOG.md +++ b/support-files/kubernetes/charts/bk-job/VALUES_LOG.md @@ -1,5 +1,19 @@ # chart values 更新日志 +## 0.7.0 +1. 增加AI相关配置 + +```yaml +# AI相关配置 +ai: + # 是否开启AI功能,默认不开启 + enabled: false + # AI分析错误日志功能相关配置 + analyzeErrorLog: + # 支持分析的错误日志最大长度,单位支持B、KB、MB、GB、TB、PB,默认5MB + logMaxLength: "5MB" +``` + ## 0.6.5 1. 增加正在执行中的作业总量的配额限制 diff --git a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml index ccc8325873..c8621c75f2 100644 --- a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml +++ b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml @@ -176,3 +176,10 @@ data: enabled: {{ .Values.jvmDiagnosticFile.clearByLastModifyTime.enabled }} # JVM诊断文件保留的小时数,默认168小时(7天) keep-hours: {{ .Values.jvmDiagnosticFile.clearByLastModifyTime.keepHours }} + ai: + # 是否开启AI功能,默认开启 + enabled: {{ .Values.ai.enabled }} + # AI分析错误日志功能相关配置 + analyze-error-log: + # 支持分析的错误日志最大长度 + log-max-length: {{ .Values.ai.analyzeErrorLog.logMaxLength }} diff --git a/support-files/kubernetes/charts/bk-job/values.yaml b/support-files/kubernetes/charts/bk-job/values.yaml index 5626c509fb..369b6f44a6 100644 --- a/support-files/kubernetes/charts/bk-job/values.yaml +++ b/support-files/kubernetes/charts/bk-job/values.yaml @@ -715,6 +715,15 @@ log: # 服务后台日志可使用的最大磁盘空间(超出后将清理最旧的日志文件,但每类日志文件至少保留一个),单位支持B、KB、MB、GB、TB、PB,默认40GB maxVolume: 40GB +# AI相关配置 +ai: + # 是否开启AI功能,默认不开启 + enabled: false + # AI分析错误日志功能相关配置 + analyzeErrorLog: + # 支持分析的错误日志最大长度,单位支持B、KB、MB、GB、TB、PB,默认5MB + logMaxLength: "5MB" + ## 蓝鲸日志采集配置 bkLogConfig: # 是否开启蓝鲸日志采集 From c0cd1946f5c64e1069bcecc0fa211b010a39c007 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 1 Aug 2024 20:31:29 +0800 Subject: [PATCH 028/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.优化AI配置。 --- .../analysis/JobAnalysisBootApplication.java | 3 -- .../api/web/impl/WebAIResourceImpl.java | 2 -- .../bk/job/analysis/config/AIConfig.java | 36 +++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIConfig.java diff --git a/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java b/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java index a9bbb63fa5..e9a5f3d274 100644 --- a/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java +++ b/src/backend/job-analysis/boot-job-analysis/src/main/java/com/tencent/bk/job/analysis/JobAnalysisBootApplication.java @@ -24,12 +24,10 @@ package com.tencent.bk.job.analysis; -import com.tencent.bk.job.analysis.config.AIProperties; import com.tencent.bk.job.common.service.boot.JobBootApplication; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration; import org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.scheduling.annotation.EnableScheduling; @@ -47,7 +45,6 @@ } ) @EnableScheduling -@EnableConfigurationProperties(AIProperties.class) public class JobAnalysisBootApplication { public static void main(String[] args) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index a06924ae12..71866511ee 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -43,7 +43,6 @@ import com.tencent.bk.job.common.model.dto.AppResourceScope; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Primary; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; @@ -51,7 +50,6 @@ import java.util.Map; import java.util.stream.Collectors; -@Primary @RestController("jobAnalysisWebAIResource") @Slf4j public class WebAIResourceImpl implements WebAIResource { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIConfig.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIConfig.java new file mode 100644 index 0000000000..2ba267f2be --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIConfig.java @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Slf4j +@Configuration(value = "jobAnalysisAIConfig") +@EnableConfigurationProperties(AIProperties.class) +public class AIConfig { + +} From 15d3e54bda20442c819cbdfeb17d23c88a163be5 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Fri, 2 Aug 2024 11:12:39 +0800 Subject: [PATCH 029/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持基础参数校验。 --- .../i18n/validation/ValidationMessages.properties | 12 ++++++++++-- .../i18n/validation/ValidationMessages_en.properties | 12 ++++++++++-- .../validation/ValidationMessages_en_US.properties | 12 ++++++++++-- .../i18n/validation/ValidationMessages_zh.properties | 12 ++++++++++-- .../validation/ValidationMessages_zh_CN.properties | 12 ++++++++++-- .../bk/job/analysis/api/web/WebAIResource.java | 8 ++++++++ .../analysis/model/web/req/AIAnalyzeErrorReq.java | 9 ++++++++- .../job/analysis/model/web/req/AIGeneralChatReq.java | 2 +- 8 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties index d14f71a1ca..faf6711d98 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties @@ -109,5 +109,13 @@ validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行 ## AI对话 -validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 -validation.constraints.AIAnalyzeErrorContent_empty.message=报错信息内容不能为空 +validation.constraints.AIGeneralChat_contentEmpty.message=聊天输入内容不能为空 +validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不能为空 +validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 +validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 +validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 +validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 +validation.constraints.AIAnalyzeError_executeCountEmpty.message=执行次数不能为空 +validation.constraints.AIAnalyzeError_batchEmpty.message=滚动批次不能为空 +validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=执行对象类型不能为空 +validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=执行对象资源ID不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties index 4c6eaec803..7e3fc32614 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties @@ -108,5 +108,13 @@ validation.constraints.queryAgentInfoHostIds_tooMany.message=HostIds size can no validation.constraints.queryExecuteObjects_outOfRange.message=Query execute objects size must be between {min} and {max} ## AI Chat -validation.constraints.AIGeneralChatContent_empty.message=Chat message cannot be empty -validation.constraints.AIAnalyzeErrorContent_empty.message=Error content cannot be empty +validation.constraints.AIGeneralChat_contentEmpty.message=Chat message cannot be empty +validation.constraints.AIAnalyzeError_contentEmpty.message=Error content cannot be empty +validation.constraints.AIInvalidHistoryStart.message=Start must be equal or greater than 0 +validation.constraints.AIInvalidHistoryLength.message=Length must be between {min} and {max} +validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=TaskInstanceId cannot be empty +validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=StepInstanceId cannot be empty +validation.constraints.AIAnalyzeError_executeCountEmpty.message=ExecuteCount cannot be empty +validation.constraints.AIAnalyzeError_batchEmpty.message=Batch cannot be empty +validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=ExecuteObjectType cannot be empty +validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=ExecuteObjectResourceId cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties index 4c6eaec803..7e3fc32614 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties @@ -108,5 +108,13 @@ validation.constraints.queryAgentInfoHostIds_tooMany.message=HostIds size can no validation.constraints.queryExecuteObjects_outOfRange.message=Query execute objects size must be between {min} and {max} ## AI Chat -validation.constraints.AIGeneralChatContent_empty.message=Chat message cannot be empty -validation.constraints.AIAnalyzeErrorContent_empty.message=Error content cannot be empty +validation.constraints.AIGeneralChat_contentEmpty.message=Chat message cannot be empty +validation.constraints.AIAnalyzeError_contentEmpty.message=Error content cannot be empty +validation.constraints.AIInvalidHistoryStart.message=Start must be equal or greater than 0 +validation.constraints.AIInvalidHistoryLength.message=Length must be between {min} and {max} +validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=TaskInstanceId cannot be empty +validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=StepInstanceId cannot be empty +validation.constraints.AIAnalyzeError_executeCountEmpty.message=ExecuteCount cannot be empty +validation.constraints.AIAnalyzeError_batchEmpty.message=Batch cannot be empty +validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=ExecuteObjectType cannot be empty +validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=ExecuteObjectResourceId cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties index d14f71a1ca..faf6711d98 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties @@ -109,5 +109,13 @@ validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行 ## AI对话 -validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 -validation.constraints.AIAnalyzeErrorContent_empty.message=报错信息内容不能为空 +validation.constraints.AIGeneralChat_contentEmpty.message=聊天输入内容不能为空 +validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不能为空 +validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 +validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 +validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 +validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 +validation.constraints.AIAnalyzeError_executeCountEmpty.message=执行次数不能为空 +validation.constraints.AIAnalyzeError_batchEmpty.message=滚动批次不能为空 +validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=执行对象类型不能为空 +validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=执行对象资源ID不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties index d14f71a1ca..faf6711d98 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties @@ -109,5 +109,13 @@ validation.constraints.queryExecuteObjects_outOfRange.message=单次查询执行 ## AI对话 -validation.constraints.AIGeneralChatContent_empty.message=聊天输入内容不能为空 -validation.constraints.AIAnalyzeErrorContent_empty.message=报错信息内容不能为空 +validation.constraints.AIGeneralChat_contentEmpty.message=聊天输入内容不能为空 +validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不能为空 +validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 +validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 +validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 +validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 +validation.constraints.AIAnalyzeError_executeCountEmpty.message=执行次数不能为空 +validation.constraints.AIAnalyzeError_batchEmpty.message=滚动批次不能为空 +validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=执行对象类型不能为空 +validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=执行对象资源ID不能为空 diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index eef0cc4407..fc8f727452 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -36,6 +36,8 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; +import org.hibernate.validator.constraints.Range; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -48,6 +50,7 @@ import org.springframework.web.bind.annotation.RestController; import springfox.documentation.annotations.ApiIgnore; +import javax.validation.constraints.Min; import java.util.List; import java.util.Map; @@ -92,9 +95,11 @@ Response> getLatestChatHistoryList( String scopeId, @ApiParam(value = "start", name = "对话记录起始位置,不传默认为0") @RequestParam(value = "start", defaultValue = "0") + @Min(value = 0L, message = "{validation.constraints.AIInvalidHistoryStart.message}") Integer start, @ApiParam(value = "length", name = "需要获取的对话记录条数,最大200条,不传默认20条") @RequestParam(value = "length", defaultValue = "20") + @Range(min = 0L, max = 200L, message = "{validation.constraints.AIInvalidHistoryLength.message}") Integer length ); @@ -114,6 +119,7 @@ Response generalChat( @PathVariable(value = "scopeId") String scopeId, @ApiParam(value = "AI通用聊天参数", required = true) + @Validated @RequestBody AIGeneralChatReq req ); @@ -133,6 +139,7 @@ Response checkScript( @PathVariable(value = "scopeId") String scopeId, @ApiParam(value = "AI检查脚本参数", required = true) + @Validated @RequestBody AICheckScriptReq req ); @@ -152,6 +159,7 @@ Response analyzeError( @PathVariable(value = "scopeId") String scopeId, @ApiParam(value = "AI分析报错信息参数", required = true) + @Validated @RequestBody AIAnalyzeErrorReq req ); diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java index 63444206dd..d87521f4bd 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java @@ -31,6 +31,7 @@ import lombok.NoArgsConstructor; import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; @AllArgsConstructor @NoArgsConstructor @@ -39,27 +40,33 @@ public class AIAnalyzeErrorReq { @ApiModelProperty(value = "任务ID") + @NotNull(message = "{validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message}") private Long taskInstanceId; @ApiModelProperty(value = "步骤ID") + @NotNull(message = "{validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message}") private Long stepInstanceId; @ApiModelProperty(value = "执行次数") + @NotNull(message = "{validation.constraints.AIAnalyzeError_executeCountEmpty.message}") private Integer executeCount; @ApiModelProperty(value = "滚动批次,非滚动步骤不需要传入") + @NotNull(message = "{validation.constraints.AIAnalyzeError_batchEmpty.message}") private Integer batch; @ApiModelProperty(value = "执行对象类型") + @NotNull(message = "{validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message}") private Integer executeObjectType; @ApiModelProperty(value = "执行对象资源 ID") + @NotNull(message = "{validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message}") private Long executeObjectResourceId; @ApiModelProperty(value = "文件任务上传下载标识,0-上传,1-下载") private Integer mode; @ApiModelProperty(value = "报错信息内容") - @NotEmpty(message = "{validation.constraints.AIAnalyzeErrorContent_empty.message}") + @NotEmpty(message = "{validation.constraints.AIAnalyzeError_contentEmpty.message}") private String content; } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java index dd4b1a921d..8c935ce19d 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java @@ -42,6 +42,6 @@ public class AIGeneralChatReq { * 用户输入内容 */ @ApiModelProperty(value = "用户输入内容") - @NotEmpty(message = "{validation.constraints.AIGeneralChatContent_empty.message}") + @NotEmpty(message = "{validation.constraints.AIGeneralChat_contentEmpty.message}") private String content; } From c8ce171b226debe78d6f8ef6e2c96e69fac0df64 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Fri, 2 Aug 2024 20:57:25 +0800 Subject: [PATCH 030/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持日志内容等参数校验。 --- .../i18n/exception/message.properties | 1 + .../i18n/exception/message_en.properties | 1 + .../i18n/exception/message_en_US.properties | 1 + .../i18n/exception/message_zh.properties | 1 + .../i18n/exception/message_zh_CN.properties | 1 + .../validation/ValidationMessages.properties | 4 ++ .../ValidationMessages_en.properties | 4 ++ .../ValidationMessages_en_US.properties | 4 ++ .../ValidationMessages_zh.properties | 4 ++ .../ValidationMessages_zh_CN.properties | 4 ++ .../bk/job/common/constant/ErrorCode.java | 2 + .../job/analysis/api/web/WebAIResource.java | 7 +-- .../model/web/req/AIAnalyzeErrorReq.java | 8 +-- .../model/web/req/AICheckScriptReq.java | 3 ++ .../model/web/req/AIGeneralChatReq.java | 3 ++ .../model/web/req/validation/MaxLength.java | 52 +++++++++++++++++++ .../req/validation/MaxLengthValidator.java | 46 ++++++++++++++++ .../api/web/impl/WebAIResourceImpl.java | 29 ++++++++++- .../bk/job/analysis/config/AIProperties.java | 6 ++- .../service/ai/impl/AIConfigService.java | 3 +- 20 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLength.java create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLengthValidator.java diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties index 9967f27d76..50074d1f4a 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties @@ -232,6 +232,7 @@ ## 业务错误-统计分析服务(job-analysis) 1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 +1264002=AI分析任务报错信息内容超过最大值:{0} ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties index d3d9649ae0..1d380d4e65 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties @@ -232,6 +232,7 @@ ## Business error (job-analysis) 1264001=Analyze task error only support Script or File step +1264002=AI analyze task error content exceed max length: {0} ## Business error - User/Login 1247001=User does not exist or is not logged in diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties index d3d9649ae0..1d380d4e65 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties @@ -232,6 +232,7 @@ ## Business error (job-analysis) 1264001=Analyze task error only support Script or File step +1264002=AI analyze task error content exceed max length: {0} ## Business error - User/Login 1247001=User does not exist or is not logged in diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties index 9967f27d76..50074d1f4a 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties @@ -232,6 +232,7 @@ ## 业务错误-统计分析服务(job-analysis) 1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 +1264002=AI分析任务报错信息内容超过最大值:{0} ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties index fb1a7ddb3a..334554b8f3 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties @@ -233,6 +233,7 @@ ## 统计分析服务job-analysis错误码 1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 +1264002=AI分析任务报错信息内容超过最大值:{0} ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties index faf6711d98..f24353cd8e 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties @@ -114,8 +114,12 @@ validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不 validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 +validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=步骤执行类型不能为空 validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 validation.constraints.AIAnalyzeError_executeCountEmpty.message=执行次数不能为空 validation.constraints.AIAnalyzeError_batchEmpty.message=滚动批次不能为空 validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=执行对象类型不能为空 validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=执行对象资源ID不能为空 +validation.constraints.AICheckScript_contentExceedMaxLength.message=检查的脚本内容长度不可超过5MB +validation.constraints.AIGeneralChat_contentExceedMaxLength.message=聊天输入内容长度不可超过5MB +validation.constraints.ExceedMaxLength.message=字段超出最大长度 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties index 7e3fc32614..fd810aff7d 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties @@ -113,8 +113,12 @@ validation.constraints.AIAnalyzeError_contentEmpty.message=Error content cannot validation.constraints.AIInvalidHistoryStart.message=Start must be equal or greater than 0 validation.constraints.AIInvalidHistoryLength.message=Length must be between {min} and {max} validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=TaskInstanceId cannot be empty +validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=StepExecuteType cannot be empty validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=StepInstanceId cannot be empty validation.constraints.AIAnalyzeError_executeCountEmpty.message=ExecuteCount cannot be empty validation.constraints.AIAnalyzeError_batchEmpty.message=Batch cannot be empty validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=ExecuteObjectType cannot be empty validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=ExecuteObjectResourceId cannot be empty +validation.constraints.AICheckScript_contentExceedMaxLength.message=Script content cannot exceed 5MB +validation.constraints.AIGeneralChat_contentExceedMaxLength.message=Chat content cannot exceed 5MB +validation.constraints.ExceedMaxLength.message=String field exceed max length diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties index 7e3fc32614..fd810aff7d 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties @@ -113,8 +113,12 @@ validation.constraints.AIAnalyzeError_contentEmpty.message=Error content cannot validation.constraints.AIInvalidHistoryStart.message=Start must be equal or greater than 0 validation.constraints.AIInvalidHistoryLength.message=Length must be between {min} and {max} validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=TaskInstanceId cannot be empty +validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=StepExecuteType cannot be empty validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=StepInstanceId cannot be empty validation.constraints.AIAnalyzeError_executeCountEmpty.message=ExecuteCount cannot be empty validation.constraints.AIAnalyzeError_batchEmpty.message=Batch cannot be empty validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=ExecuteObjectType cannot be empty validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=ExecuteObjectResourceId cannot be empty +validation.constraints.AICheckScript_contentExceedMaxLength.message=Script content cannot exceed 5MB +validation.constraints.AIGeneralChat_contentExceedMaxLength.message=Chat content cannot exceed 5MB +validation.constraints.ExceedMaxLength.message=String field exceed max length diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties index faf6711d98..f24353cd8e 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties @@ -114,8 +114,12 @@ validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不 validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 +validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=步骤执行类型不能为空 validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 validation.constraints.AIAnalyzeError_executeCountEmpty.message=执行次数不能为空 validation.constraints.AIAnalyzeError_batchEmpty.message=滚动批次不能为空 validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=执行对象类型不能为空 validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=执行对象资源ID不能为空 +validation.constraints.AICheckScript_contentExceedMaxLength.message=检查的脚本内容长度不可超过5MB +validation.constraints.AIGeneralChat_contentExceedMaxLength.message=聊天输入内容长度不可超过5MB +validation.constraints.ExceedMaxLength.message=字段超出最大长度 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties index faf6711d98..f24353cd8e 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties @@ -114,8 +114,12 @@ validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不 validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 +validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=步骤执行类型不能为空 validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 validation.constraints.AIAnalyzeError_executeCountEmpty.message=执行次数不能为空 validation.constraints.AIAnalyzeError_batchEmpty.message=滚动批次不能为空 validation.constraints.AIAnalyzeError_executeObjectTypeEmpty.message=执行对象类型不能为空 validation.constraints.AIAnalyzeError_executeObjectResourceIdEmpty.message=执行对象资源ID不能为空 +validation.constraints.AICheckScript_contentExceedMaxLength.message=检查的脚本内容长度不可超过5MB +validation.constraints.AIGeneralChat_contentExceedMaxLength.message=聊天输入内容长度不可超过5MB +validation.constraints.ExceedMaxLength.message=字段超出最大长度 diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java index cff732422d..fc583cf9da 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java @@ -459,5 +459,7 @@ public class ErrorCode { // 统计分析服务job-analysis错误码 start // AI分析任务报错信息仅支持脚本或文件任务步骤 public static final int AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP = 1264001; + // AI分析任务报错信息内容超过最大值:{0} + public static final int AI_ANALYZE_ERROR_CONTENT_EXCEED_MAX_LENGTH = 1264002; // 统计分析服务job-analysis错误码 end } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index fc8f727452..0a89e5c697 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -54,6 +54,7 @@ import java.util.List; import java.util.Map; +@Validated @Api(tags = {"job-analysis:web:AI"}) @RequestMapping("/web/ai/scope/{scopeType}/{scopeId}") @RestController @@ -93,13 +94,13 @@ Response> getLatestChatHistoryList( @ApiParam(value = "资源范围ID", required = true) @PathVariable(value = "scopeId") String scopeId, - @ApiParam(value = "start", name = "对话记录起始位置,不传默认为0") + @ApiParam(value = "对话记录起始位置,不传默认为0") @RequestParam(value = "start", defaultValue = "0") @Min(value = 0L, message = "{validation.constraints.AIInvalidHistoryStart.message}") Integer start, - @ApiParam(value = "length", name = "需要获取的对话记录条数,最大200条,不传默认20条") + @ApiParam(value = "需要获取的对话记录条数,最大200条,不传默认20条") @RequestParam(value = "length", defaultValue = "20") - @Range(min = 0L, max = 200L, message = "{validation.constraints.AIInvalidHistoryLength.message}") + @Range(max = 200L, message = "{validation.constraints.AIInvalidHistoryLength.message}") Integer length ); diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java index d87521f4bd..7c19bef30c 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIAnalyzeErrorReq.java @@ -30,7 +30,6 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; @AllArgsConstructor @@ -43,6 +42,10 @@ public class AIAnalyzeErrorReq { @NotNull(message = "{validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message}") private Long taskInstanceId; + @ApiModelProperty("步骤执行类型:1-脚本,2-文件") +// @NotNull(message = "{validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message}") + private Integer stepExecuteType; + @ApiModelProperty(value = "步骤ID") @NotNull(message = "{validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message}") private Long stepInstanceId; @@ -66,7 +69,6 @@ public class AIAnalyzeErrorReq { @ApiModelProperty(value = "文件任务上传下载标识,0-上传,1-下载") private Integer mode; - @ApiModelProperty(value = "报错信息内容") - @NotEmpty(message = "{validation.constraints.AIAnalyzeError_contentEmpty.message}") + @ApiModelProperty(value = "脚本任务报错信息内容") private String content; } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java index 588121d0f9..7dd0ee4fa4 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AICheckScriptReq.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.model.web.req; +import com.tencent.bk.job.analysis.model.web.req.validation.MaxLength; import com.tencent.bk.job.common.validation.CheckEnum; import com.tencent.bk.job.manage.api.common.constants.script.ScriptTypeEnum; import io.swagger.annotations.ApiModel; @@ -55,5 +56,7 @@ public class AICheckScriptReq { */ @ApiModelProperty(value = "脚本内容,BASE64编码") @NotEmpty(message = "{validation.constraints.ScriptContent_empty.message}") + @MaxLength(value = 5 * 1024L * 1024L, + message = "{validation.constraints.AICheckScript_contentExceedMaxLength.message}") private String content; } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java index 8c935ce19d..1014f73303 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/AIGeneralChatReq.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.model.web.req; +import com.tencent.bk.job.analysis.model.web.req.validation.MaxLength; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -43,5 +44,7 @@ public class AIGeneralChatReq { */ @ApiModelProperty(value = "用户输入内容") @NotEmpty(message = "{validation.constraints.AIGeneralChat_contentEmpty.message}") + @MaxLength(value = 5 * 1024L * 1024L, + message = "{validation.constraints.AIGeneralChat_contentExceedMaxLength.message}") private String content; } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLength.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLength.java new file mode 100644 index 0000000000..d8ff1d8c6f --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLength.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req.validation; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = MaxLengthValidator.class) +@Documented +public @interface MaxLength { + long value(); + + String message() default "{validation.constraints.ExceedMaxLength.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLengthValidator.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLengthValidator.java new file mode 100644 index 0000000000..67088ba4b9 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/validation/MaxLengthValidator.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req.validation; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class MaxLengthValidator implements ConstraintValidator { + private Long maxLength; + + @Override + public void initialize(MaxLength maxLengthAnnotation) { + this.maxLength = maxLengthAnnotation.value(); + } + + @Override + public boolean isValid(String content, + ConstraintValidatorContext constraintValidatorContext) { + if (content == null) { + return true; + } + return content.length() <= maxLength; + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 71866511ee..02db570371 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -25,6 +25,7 @@ package com.tencent.bk.job.analysis.api.web.impl; import com.tencent.bk.job.analysis.api.web.WebAIResource; +import com.tencent.bk.job.analysis.config.AIProperties; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; @@ -39,8 +40,12 @@ import com.tencent.bk.job.analysis.service.ai.ChatService; import com.tencent.bk.job.analysis.service.ai.impl.AIConfigService; import com.tencent.bk.job.analysis.service.ai.impl.AIMessageI18nService; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.ServiceException; import com.tencent.bk.job.common.model.Response; import com.tencent.bk.job.common.model.dto.AppResourceScope; +import com.tencent.bk.job.common.model.error.ErrorType; +import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; @@ -60,6 +65,7 @@ public class WebAIResourceImpl implements WebAIResource { private final AIAnalyzeErrorService aiAnalyzeErrorService; private final AIChatHistoryService aiChatHistoryService; private final AIMessageI18nService aiMessageI18nService; + private final AIProperties aiProperties; @Autowired public WebAIResourceImpl(AIConfigService aiConfigService, @@ -67,13 +73,15 @@ public WebAIResourceImpl(AIConfigService aiConfigService, AICheckScriptService aiCheckScriptService, AIAnalyzeErrorService aiAnalyzeErrorService, AIChatHistoryService aiChatHistoryService, - AIMessageI18nService aiMessageI18nService) { + AIMessageI18nService aiMessageI18nService, + AIProperties aiProperties) { this.aiConfigService = aiConfigService; this.chatService = chatService; this.aiCheckScriptService = aiCheckScriptService; this.aiAnalyzeErrorService = aiAnalyzeErrorService; this.aiChatHistoryService = aiChatHistoryService; this.aiMessageI18nService = aiMessageI18nService; + this.aiProperties = aiProperties; } @Override @@ -142,10 +150,29 @@ public Response analyzeError(String username, String scopeType, String scopeId, AIAnalyzeErrorReq req) { + checkScriptLogContentLength(req); AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, appResourceScope.getAppId(), req); return Response.buildSuccessResp(aiAnswer); } + /** + * 结合动态配置的限制值检查脚本日志内容长度是否超限 + * + * @param req 请求体 + */ + private void checkScriptLogContentLength(AIAnalyzeErrorReq req) { + if (StepExecuteTypeEnum.EXECUTE_SCRIPT.getValue().equals(req.getStepExecuteType())) { + Long logMaxLengthBytes = aiProperties.getAnalyzeErrorLog().getLogMaxLengthBytes(); + if (req.getContent().length() > logMaxLengthBytes) { + throw new ServiceException( + ErrorType.INVALID_PARAM, + ErrorCode.AI_ANALYZE_ERROR_CONTENT_EXCEED_MAX_LENGTH, + new Object[]{aiProperties.getAnalyzeErrorLog().getLogMaxLength()} + ); + } + } + } + @Override public Response clearChatHistory(String username, AppResourceScope appResourceScope, diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java index 724d4f4316..cf11c28ab9 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java @@ -24,7 +24,7 @@ package com.tencent.bk.job.analysis.config; -import lombok.Data; +import com.tencent.bk.job.common.util.file.FileSizeUtil; import lombok.Getter; import lombok.Setter; import lombok.ToString; @@ -54,5 +54,9 @@ public static class AnalyzeErrorLogConfig { * 支持分析的错误日志最大长度 */ private String logMaxLength = "5MB"; + + public Long getLogMaxLengthBytes() { + return FileSizeUtil.parseFileSizeBytes(logMaxLength); + } } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java index b3422a3bac..18563a9ebd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIConfigService.java @@ -43,8 +43,7 @@ public AIConfigService(AIProperties aiProperties) { public Map getAIConfig() { Map map = new HashMap<>(); - String logMaxLengthStr = aiProperties.getAnalyzeErrorLog().getLogMaxLength(); - long logMaxLengthBytes = FileSizeUtil.parseFileSizeBytes(logMaxLengthStr); + Long logMaxLengthBytes = aiProperties.getAnalyzeErrorLog().getLogMaxLengthBytes(); map.put("enabled", aiProperties.getEnabled()); map.put("analyzeErrorLogMaxLength", logMaxLengthBytes); return map; From 2e0c380e468cf665a127226625e9807a427b869a Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 5 Aug 2024 15:14:34 +0800 Subject: [PATCH 031/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.完善接口文档。 --- .../com/tencent/bk/job/analysis/api/web/WebAIResource.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 0a89e5c697..1e712fba61 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -61,7 +61,9 @@ @WebAPI public interface WebAIResource { - @ApiOperation(value = "获取AI相关的配置参数,取值:analyzeErrorLogMaxLength表示分析报错信息时支持的最大日志长度,单位为字符", + @ApiOperation(value = "获取AI相关的配置参数,取值:\n" + + "enabled:表示是否启用AI功能;\n" + + "analyzeErrorLogMaxLength:表示分析报错信息时支持的最大日志长度,单位为字符;", produces = "application/json") @GetMapping("/config") Response> getAIConfig( From 5f03ae571a70d1ef74eeaca768ded7619a76cb75 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 5 Aug 2024 19:49:26 +0800 Subject: [PATCH 032/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.修复HttpHelperFactory度量指标初始化。 --- .../job/common/aidev/impl/BkAIDevClient.java | 2 +- .../job/common/service/config/HttpConfig.java | 49 ------------------- .../config/JobCommonAutoConfiguration.java | 17 +++++++ 3 files changed, 18 insertions(+), 50 deletions(-) delete mode 100644 src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/HttpConfig.java diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java index 795984bd4c..b7a1b2ec7d 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkAIDevClient.java @@ -71,7 +71,7 @@ public BkAIDevClient(MeterRegistry meterRegistry, BkApiGatewayProperties bkApiGatewayProperties) { super( meterRegistry, - CommonMetricNames.BK_NOTICE_API, + CommonMetricNames.BK_AI_DEV_API, getBkAIDevUrlSafely(bkApiGatewayProperties), HttpHelperFactory.getDefaultHttpHelper() ); diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/HttpConfig.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/HttpConfig.java deleted file mode 100644 index a25d5bd57e..0000000000 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/HttpConfig.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. - * - * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. - * - * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. - * - * License for BK-JOB蓝鲸智云作业平台: - * -------------------------------------------------------------------- - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and - * to permit persons to whom the Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial portions of - * the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -package com.tencent.bk.job.common.service.config; - -import com.tencent.bk.job.common.util.http.HttpHelperFactory; -import io.micrometer.core.instrument.MeterRegistry; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Slf4j -@Configuration -public class HttpConfig { - - @Bean - public HttpConfigSetter httpConfigSetter(@Autowired MeterRegistry meterRegistry) { - HttpHelperFactory.setMeterRegistry(meterRegistry); - log.info("meterRegistry for HttpHelperFactory init"); - return new HttpConfigSetter(); - } - - static class HttpConfigSetter { - HttpConfigSetter() { - } - } -} diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/JobCommonAutoConfiguration.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/JobCommonAutoConfiguration.java index 5d0d58f2ac..0fa47c1b8d 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/JobCommonAutoConfiguration.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/service/config/JobCommonAutoConfiguration.java @@ -26,11 +26,16 @@ import com.tencent.bk.job.common.config.BkConfig; import com.tencent.bk.job.common.util.ApplicationContextRegister; +import com.tencent.bk.job.common.util.http.HttpHelperFactory; +import io.micrometer.core.instrument.MeterRegistry; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; +@Slf4j @Configuration(proxyBeanMethods = false) @Import({JobCommonConfig.class, BkConfig.class}) public class JobCommonAutoConfiguration { @@ -39,4 +44,16 @@ public class JobCommonAutoConfiguration { public ApplicationContextRegister applicationContextRegister() { return new ApplicationContextRegister(); } + + @Bean + HttpConfigSetter httpConfigSetter(@Autowired MeterRegistry meterRegistry) { + HttpHelperFactory.setMeterRegistry(meterRegistry); + log.info("meterRegistry for HttpHelperFactory init"); + return new HttpConfigSetter(); + } + + static class HttpConfigSetter { + HttpConfigSetter() { + } + } } From 2f7dc3c7aea763a3ab8b5e4ccfc5b6d23266e3fd Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 6 Aug 2024 15:47:34 +0800 Subject: [PATCH 033/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.对话记录添加状态字段。 --- .../job/analysis/consts/AIChatStatusEnum.java | 59 +++++++++++++++++++ .../analysis/model/web/resp/AIChatRecord.java | 13 ++-- .../api/web/impl/WebAIResourceImpl.java | 2 + .../analysis/model/dto/AIChatHistoryDTO.java | 3 + 4 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java new file mode 100644 index 0000000000..f1e93ac038 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.consts; + +/** + * AI对话状态 + */ +public enum AIChatStatusEnum { + /** + * 正在回答 + */ + REPLYING(1), + /** + * 已完成 + */ + FINISHED(2); + + private final int status; + + AIChatStatusEnum(int status) { + this.status = status; + } + + public static AIChatStatusEnum getAIChatStatus(int status) { + for (AIChatStatusEnum aiChatStatusEnum : AIChatStatusEnum.values()) { + if (aiChatStatusEnum.getStatus() == status) { + return aiChatStatusEnum; + } + } + throw new RuntimeException("Unknown AIChat status " + status); + } + + public int getStatus() { + return status; + } + +} diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java index c4ebb99143..c0a22733b5 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.model.web.resp; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; @@ -36,15 +37,15 @@ @Data public class AIChatRecord { - /** - * 用户输入 - */ @ApiModelProperty(value = "用户输入") private UserInput userInput; - /** - * AI回答 - */ @ApiModelProperty("AI回答") private AIAnswer aiAnswer; + + /** + * 对话状态,取值源于{@link AIChatStatusEnum}. + */ + @ApiModelProperty("对话状态:1-正在回答,2-已完成") + private Integer status; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 02db570371..c222d31bd0 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -26,6 +26,7 @@ import com.tencent.bk.job.analysis.api.web.WebAIResource; import com.tencent.bk.job.analysis.config.AIProperties; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; @@ -112,6 +113,7 @@ private AIChatRecord getGreetingRecord() { AIChatRecord greetingRecord = new AIChatRecord(); greetingRecord.setUserInput(new UserInput("", 0L)); greetingRecord.setAiAnswer(getGreetingAIAnswer()); + greetingRecord.setStatus(AIChatStatusEnum.FINISHED.getStatus()); return greetingRecord; } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java index aa4b284d0b..d1ea97f83c 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java @@ -25,6 +25,7 @@ package com.tencent.bk.job.analysis.model.dto; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.UserInput; @@ -123,6 +124,8 @@ public AIChatRecord toAIChatRecord() { aiAnswer.setErrorCode(errorCode); aiAnswer.setErrorMessage(errorMessage); aiChatRecord.setAiAnswer(aiAnswer); + // TODO:使用真实数据 + aiChatRecord.setStatus(AIChatStatusEnum.FINISHED.getStatus()); return aiChatRecord; } } From 1958c56f0862a2f6005d72de0660645df5cbfd69 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 12 Aug 2024 15:40:57 +0800 Subject: [PATCH 034/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.对话记录添加状态字段实现。 --- .../bk/job/analysis/dao/AIChatHistoryDAO.java | 18 ++++++++++++++++++ .../dao/impl/AIChatHistoryDAOImpl.java | 19 +++++++++++++++++++ .../analysis/model/dto/AIChatHistoryDTO.java | 8 ++++++-- .../service/ai/AIChatHistoryService.java | 12 ++++++++++++ .../service/ai/impl/AIBaseService.java | 13 ++++++++++--- .../ai/impl/AIChatHistoryServiceImpl.java | 15 +++++++++++++++ .../service/ai/impl/ChatServiceImpl.java | 14 +++++++++----- ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 1 + 8 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java index f47380dfec..d44010c8d3 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java @@ -37,6 +37,24 @@ public interface AIChatHistoryDAO { */ Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO); + /** + * 更新聊天记录状态 + * + * @param historyId AI聊天记录ID + * @param status AI聊天记录状态 + * @param aiAnswer AI回答 + * @param errorCode 错误码 + * @param errorMessage 错误信息 + * @param aiAnswerTime AI回答时间 + * @return 受影响的行数 + */ + int updateChatHistoryStatusAndAIAnswer(Long historyId, + Integer status, + String aiAnswer, + String errorCode, + String errorMessage, + Long aiAnswerTime); + /** * 获取最近的聊天记录列表 * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index a05e655046..039f6cf250 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -102,6 +102,23 @@ public Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { } } + @Override + public int updateChatHistoryStatusAndAIAnswer(Long historyId, + Integer status, + String aiAnswer, + String errorCode, + String errorMessage, + Long aiAnswerTime) { + return dslContext.update(defaultTable) + .set(defaultTable.STATUS, status.byteValue()) + .set(defaultTable.AI_ANSWER, aiAnswer) + .set(defaultTable.ERROR_CODE, errorCode) + .set(defaultTable.ERROR_MESSAGE, errorMessage) + .set(defaultTable.ANSWER_TIME, JooqDataTypeUtil.buildULong(aiAnswerTime)) + .where(defaultTable.ID.eq(historyId)) + .execute(); + } + @Override public List getLatestChatHistoryList(String username, Integer start, Integer length) { Collection conditions = new ArrayList<>(); @@ -132,6 +149,7 @@ private List listByConditions(Collection conditions defaultTable.USER_INPUT, defaultTable.PROMPT_TEMPLATE_ID, defaultTable.AI_INPUT, + defaultTable.STATUS, defaultTable.AI_ANSWER, defaultTable.ERROR_CODE, defaultTable.ERROR_MESSAGE, @@ -152,6 +170,7 @@ private AIChatHistoryDTO convertRecordToDto(Record record) { aiChatHistoryDTO.setUserInput(record.get(defaultTable.USER_INPUT)); aiChatHistoryDTO.setPromptTemplateId(record.get(defaultTable.PROMPT_TEMPLATE_ID)); aiChatHistoryDTO.setAiInput(record.get(defaultTable.AI_INPUT)); + aiChatHistoryDTO.setStatus(JooqDataTypeUtil.getIntegerFromByte(record.get(defaultTable.STATUS))); aiChatHistoryDTO.setAiAnswer(record.get(defaultTable.AI_ANSWER)); aiChatHistoryDTO.setErrorCode(record.get(defaultTable.ERROR_CODE)); aiChatHistoryDTO.setErrorMessage(record.get(defaultTable.ERROR_MESSAGE)); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java index d1ea97f83c..940ec346a0 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java @@ -68,6 +68,11 @@ public class AIChatHistoryDTO { */ private String aiInput; + /** + * AI对话状态,取值源于{@link AIChatStatusEnum}. + */ + private Integer status; + /** * AI回答的内容 */ @@ -124,8 +129,7 @@ public AIChatRecord toAIChatRecord() { aiAnswer.setErrorCode(errorCode); aiAnswer.setErrorMessage(errorMessage); aiChatRecord.setAiAnswer(aiAnswer); - // TODO:使用真实数据 - aiChatRecord.setStatus(AIChatStatusEnum.FINISHED.getStatus()); + aiChatRecord.setStatus(status); return aiChatRecord; } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java index 2599f442b6..6addf933c4 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -36,6 +36,8 @@ public interface AIChatHistoryService { * @param username 用户名 * @param startTime 开始时间 * @param userInput 用户输入 + * @param aiInput AI输入 + * @param status 对话状态 * @param aiAnswer AI回答 * @return AI聊天记录 */ @@ -43,6 +45,7 @@ AIChatHistoryDTO buildAIChatHistoryDTO(String username, Long startTime, String userInput, String aiInput, + Integer status, AIAnswer aiAnswer); /** @@ -53,6 +56,15 @@ AIChatHistoryDTO buildAIChatHistoryDTO(String username, */ Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO); + /** + * 更新聊天记录状态 + * + * @param historyId AI聊天记录ID + * @param aiAnswer AI回答内容 + * @return 受影响的行数 + */ + int finishAIAnswer(Long historyId, AIAnswer aiAnswer); + /** * 获取最近的聊天记录列表 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java index 3315cfc2d4..b99760ac6b 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.service.ai.impl; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; @@ -58,15 +59,20 @@ public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { long startTime = System.currentTimeMillis(); String rawPrompt = aiPromptDTO.getRawPrompt(); String renderedPrompt = aiPromptDTO.getRenderedPrompt(); - AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); + // 1.插入初始聊天记录 AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( username, startTime, rawPrompt, renderedPrompt, - aiAnswer + AIChatStatusEnum.REPLYING.getStatus(), + null ); - aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + // 2.获取AI回答 + AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); + // 3.更新聊天记录状态 + aiChatHistoryService.finishAIAnswer(historyId, aiAnswer); return aiAnswer; } @@ -87,6 +93,7 @@ public AIAnswer getDirectlyAIAnswer(String username, AIPromptDTO aiPromptDTO, St startTime, rawPrompt, buildAIDirectlyAnswerInput(content), + AIChatStatusEnum.FINISHED.getStatus(), aiAnswer ); aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index 8ee7aec7b6..9b47ae6fc7 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.service.ai.impl; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.dao.AIChatHistoryDAO; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; @@ -54,12 +55,14 @@ public AIChatHistoryDTO buildAIChatHistoryDTO(String username, Long startTime, String userInput, String aiInput, + Integer status, AIAnswer aiAnswer) { AIChatHistoryDTO aiChatHistoryDTO = new AIChatHistoryDTO(); aiChatHistoryDTO.setUsername(username); aiChatHistoryDTO.setUserInput(userInput); aiChatHistoryDTO.setPromptTemplateId(null); aiChatHistoryDTO.setAiInput(aiInput); + aiChatHistoryDTO.setStatus(status); aiChatHistoryDTO.setAiAnswer(aiAnswer.getContent()); aiChatHistoryDTO.setErrorCode(String.valueOf(aiAnswer.getErrorCode())); aiChatHistoryDTO.setErrorMessage(aiAnswer.getErrorMessage()); @@ -75,6 +78,18 @@ public Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { return aiChatHistoryDAO.insertAIChatHistory(aiChatHistoryDTO); } + @Override + public int finishAIAnswer(Long historyId, AIAnswer aiAnswer) { + return aiChatHistoryDAO.updateChatHistoryStatusAndAIAnswer( + historyId, + AIChatStatusEnum.FINISHED.getStatus(), + aiAnswer.getContent(), + aiAnswer.getErrorCode(), + aiAnswer.getErrorMessage(), + System.currentTimeMillis() + ); + } + /** * 从DB获取最近的聊天记录列表,按起始时间升序排列 * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 2e2d697d20..3335194ff4 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.service.ai.impl; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; @@ -55,17 +56,20 @@ public AIAnswer chatWithAI(String username, String userInput) { // 1.获取最近的聊天记录 List chatHistoryDTOList = getLatestChatHistoryList(username, 0, 5); chatHistoryDTOList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); - // 2.调用AI服务获取回答 - AIAnswer aiAnswer = aiService.getAIAnswer(chatHistoryDTOList, userInput); - // 3.保存聊天记录 + // 2.保存初始聊天记录 AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( username, startTime, userInput, userInput, - aiAnswer + AIChatStatusEnum.REPLYING.getStatus(), + null ); - aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + // 3.调用AI服务获取回答 + AIAnswer aiAnswer = aiService.getAIAnswer(chatHistoryDTOList, userInput); + // 4.更新聊天记录状态与回答内容 + aiChatHistoryService.finishAIAnswer(historyId, aiAnswer); return aiAnswer; } diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index a956768b17..3646da3e6a 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -32,6 +32,7 @@ CREATE TABLE IF NOT EXISTS `ai_chat_history` ( `user_input` TEXT NOT NULL COMMENT '用户输入内容', `prompt_template_id` int(10) NULL DEFAULT NULL COMMENT '使用的提示符模板ID', `ai_input` TEXT NOT NULL COMMENT '提交给AI的输入内容', + `status` tinyint(4) NULL DEFAULT NULL COMMENT 'AI对话状态:1表示正在回答,2表示已完成', `ai_answer` TEXT NOT NULL COMMENT 'AI回答的内容', `error_code` varchar(128) NULL DEFAULT NULL COMMENT 'AI回答失败时的错误码', `error_message` varchar(512) NULL DEFAULT NULL COMMENT 'AI回答失败时的错误信息', From dc8fc951dce4485194dec2eeecbf799c987bc165 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 12 Aug 2024 16:26:53 +0800 Subject: [PATCH 035/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.对话记录添加状态字段实现。 --- .../bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java | 4 +++- .../service/ai/impl/AIChatHistoryServiceImpl.java | 10 ++++++---- .../0008_job_analysis_20240618-1000_V3.10.0_mysql.sql | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index 039f6cf250..ea4b415e2d 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -71,6 +71,7 @@ public Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { defaultTable.USER_INPUT, defaultTable.PROMPT_TEMPLATE_ID, defaultTable.AI_INPUT, + defaultTable.STATUS, defaultTable.AI_ANSWER, defaultTable.ERROR_CODE, defaultTable.ERROR_MESSAGE, @@ -84,6 +85,7 @@ public Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { aiChatHistoryDTO.getUserInput(), aiChatHistoryDTO.getPromptTemplateId(), aiChatHistoryDTO.getAiInput(), + JooqDataTypeUtil.getByteFromInteger(aiChatHistoryDTO.getStatus()), aiChatHistoryDTO.getAiAnswer(), aiChatHistoryDTO.getErrorCode(), aiChatHistoryDTO.getErrorMessage(), @@ -110,7 +112,7 @@ public int updateChatHistoryStatusAndAIAnswer(Long historyId, String errorMessage, Long aiAnswerTime) { return dslContext.update(defaultTable) - .set(defaultTable.STATUS, status.byteValue()) + .set(defaultTable.STATUS, JooqDataTypeUtil.getByteFromInteger(status)) .set(defaultTable.AI_ANSWER, aiAnswer) .set(defaultTable.ERROR_CODE, errorCode) .set(defaultTable.ERROR_MESSAGE, errorMessage) diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index 9b47ae6fc7..1230e7e77a 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -63,11 +63,13 @@ public AIChatHistoryDTO buildAIChatHistoryDTO(String username, aiChatHistoryDTO.setPromptTemplateId(null); aiChatHistoryDTO.setAiInput(aiInput); aiChatHistoryDTO.setStatus(status); - aiChatHistoryDTO.setAiAnswer(aiAnswer.getContent()); - aiChatHistoryDTO.setErrorCode(String.valueOf(aiAnswer.getErrorCode())); - aiChatHistoryDTO.setErrorMessage(aiAnswer.getErrorMessage()); + if (aiAnswer != null) { + aiChatHistoryDTO.setAiAnswer(aiAnswer.getContent()); + aiChatHistoryDTO.setErrorCode(String.valueOf(aiAnswer.getErrorCode())); + aiChatHistoryDTO.setErrorMessage(aiAnswer.getErrorMessage()); + aiChatHistoryDTO.setAnswerTime(System.currentTimeMillis()); + } aiChatHistoryDTO.setStartTime(startTime); - aiChatHistoryDTO.setAnswerTime(System.currentTimeMillis()); aiChatHistoryDTO.updateTotalTime(); aiChatHistoryDTO.setIsDeleted(false); return aiChatHistoryDTO; diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index 3646da3e6a..46d27171fb 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -33,7 +33,7 @@ CREATE TABLE IF NOT EXISTS `ai_chat_history` ( `prompt_template_id` int(10) NULL DEFAULT NULL COMMENT '使用的提示符模板ID', `ai_input` TEXT NOT NULL COMMENT '提交给AI的输入内容', `status` tinyint(4) NULL DEFAULT NULL COMMENT 'AI对话状态:1表示正在回答,2表示已完成', - `ai_answer` TEXT NOT NULL COMMENT 'AI回答的内容', + `ai_answer` TEXT NULL DEFAULT NULL COMMENT 'AI回答的内容', `error_code` varchar(128) NULL DEFAULT NULL COMMENT 'AI回答失败时的错误码', `error_message` varchar(512) NULL DEFAULT NULL COMMENT 'AI回答失败时的错误信息', `start_time` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '开始时间', From 66861517506c2264b1835be24f589c74e8f10ea3 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 14 Aug 2024 17:29:43 +0800 Subject: [PATCH 036/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.对话类接口响应更新为流式响应定义。 --- .../job/analysis/api/web/WebAIResource.java | 23 ++++--- .../api/web/impl/WebAIResourceImpl.java | 61 +++++++++++++------ 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 1e712fba61..04cd5516de 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -27,7 +27,6 @@ import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.common.annotation.WebAPI; @@ -48,6 +47,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import springfox.documentation.annotations.ApiIgnore; import javax.validation.constraints.Min; @@ -106,9 +106,12 @@ Response> getLatestChatHistoryList( Integer length ); - @ApiOperation(value = "通用聊天接口", produces = "application/json") + @ApiOperation(value = "通用聊天接口(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + + "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + + "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + + "\"errorDetail\": null}", produces = "application/json") @PostMapping("/general/chat") - Response generalChat( + StreamingResponseBody generalChat( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -126,9 +129,12 @@ Response generalChat( @RequestBody AIGeneralChatReq req ); - @ApiOperation(value = "检查脚本", produces = "application/json") + @ApiOperation(value = "检查脚本(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + + "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + + "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + + "\"errorDetail\": null}", produces = "application/json") @PostMapping("/checkScript") - Response checkScript( + StreamingResponseBody checkScript( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -146,9 +152,12 @@ Response checkScript( @RequestBody AICheckScriptReq req ); - @ApiOperation(value = "分析报错信息", produces = "application/json") + @ApiOperation(value = "分析报错信息(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + + "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + + "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + + "\"errorDetail\": null}", produces = "application/json") @PostMapping("/analyzeError") - Response analyzeError( + StreamingResponseBody analyzeError( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index c222d31bd0..089405db69 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -47,10 +47,13 @@ import com.tencent.bk.job.common.model.dto.AppResourceScope; import com.tencent.bk.job.common.model.error.ErrorType; import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; +import com.tencent.bk.sdk.iam.util.JsonUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -127,34 +130,56 @@ private AIAnswer getGreetingAIAnswer() { } @Override - public Response generalChat(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIGeneralChatReq req) { + public StreamingResponseBody generalChat(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIGeneralChatReq req) { AIAnswer aiAnswer = chatService.chatWithAI(username, req.getContent()); - return Response.buildSuccessResp(aiAnswer); + Response response = Response.buildSuccessResp(aiAnswer); + return buildStreamingResponseBody(response); } @Override - public Response checkScript(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AICheckScriptReq req) { + public StreamingResponseBody checkScript(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AICheckScriptReq req) { AIAnswer aiAnswer = aiCheckScriptService.check(username, req.getType(), req.getContent()); - return Response.buildSuccessResp(aiAnswer); + Response response = Response.buildSuccessResp(aiAnswer); + return buildStreamingResponseBody(response); } @Override - public Response analyzeError(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIAnalyzeErrorReq req) { + public StreamingResponseBody analyzeError(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIAnalyzeErrorReq req) { checkScriptLogContentLength(req); AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, appResourceScope.getAppId(), req); - return Response.buildSuccessResp(aiAnswer); + Response response = Response.buildSuccessResp(aiAnswer); + return buildStreamingResponseBody(response); + } + + private StreamingResponseBody buildStreamingResponseBody(Object response) { + return outputStream -> { + try { + outputStream.write(JsonUtil.toJson(response).getBytes()); + outputStream.flush(); + } catch (IOException e) { + // 处理写入数据时的异常 + log.error("Fail to write data", e); + } finally { + // 确保输出流被关闭 + try { + outputStream.close(); + } catch (IOException e) { + log.error("Fail to close output stream", e); + } + } + }; } /** From 2050a981f196d5c943c21fb372cfbc4d924d74bd Mon Sep 17 00:00:00 2001 From: jsonwan Date: Fri, 16 Aug 2024 10:38:19 +0800 Subject: [PATCH 037/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "feature: AI+JOB 一期 #2995" This reverts commit 66861517506c2264b1835be24f589c74e8f10ea3. --- .../job/analysis/api/web/WebAIResource.java | 23 +++---- .../api/web/impl/WebAIResourceImpl.java | 61 ++++++------------- 2 files changed, 25 insertions(+), 59 deletions(-) diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 04cd5516de..1e712fba61 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -27,6 +27,7 @@ import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.common.annotation.WebAPI; @@ -47,7 +48,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import springfox.documentation.annotations.ApiIgnore; import javax.validation.constraints.Min; @@ -106,12 +106,9 @@ Response> getLatestChatHistoryList( Integer length ); - @ApiOperation(value = "通用聊天接口(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + - "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + - "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + - "\"errorDetail\": null}", produces = "application/json") + @ApiOperation(value = "通用聊天接口", produces = "application/json") @PostMapping("/general/chat") - StreamingResponseBody generalChat( + Response generalChat( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -129,12 +126,9 @@ StreamingResponseBody generalChat( @RequestBody AIGeneralChatReq req ); - @ApiOperation(value = "检查脚本(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + - "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + - "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + - "\"errorDetail\": null}", produces = "application/json") + @ApiOperation(value = "检查脚本", produces = "application/json") @PostMapping("/checkScript") - StreamingResponseBody checkScript( + Response checkScript( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -152,12 +146,9 @@ StreamingResponseBody checkScript( @RequestBody AICheckScriptReq req ); - @ApiOperation(value = "分析报错信息(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + - "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + - "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + - "\"errorDetail\": null}", produces = "application/json") + @ApiOperation(value = "分析报错信息", produces = "application/json") @PostMapping("/analyzeError") - StreamingResponseBody analyzeError( + Response analyzeError( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 089405db69..c222d31bd0 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -47,13 +47,10 @@ import com.tencent.bk.job.common.model.dto.AppResourceScope; import com.tencent.bk.job.common.model.error.ErrorType; import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; -import com.tencent.bk.sdk.iam.util.JsonUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -130,56 +127,34 @@ private AIAnswer getGreetingAIAnswer() { } @Override - public StreamingResponseBody generalChat(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIGeneralChatReq req) { + public Response generalChat(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIGeneralChatReq req) { AIAnswer aiAnswer = chatService.chatWithAI(username, req.getContent()); - Response response = Response.buildSuccessResp(aiAnswer); - return buildStreamingResponseBody(response); + return Response.buildSuccessResp(aiAnswer); } @Override - public StreamingResponseBody checkScript(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AICheckScriptReq req) { + public Response checkScript(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AICheckScriptReq req) { AIAnswer aiAnswer = aiCheckScriptService.check(username, req.getType(), req.getContent()); - Response response = Response.buildSuccessResp(aiAnswer); - return buildStreamingResponseBody(response); + return Response.buildSuccessResp(aiAnswer); } @Override - public StreamingResponseBody analyzeError(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIAnalyzeErrorReq req) { + public Response analyzeError(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIAnalyzeErrorReq req) { checkScriptLogContentLength(req); AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, appResourceScope.getAppId(), req); - Response response = Response.buildSuccessResp(aiAnswer); - return buildStreamingResponseBody(response); - } - - private StreamingResponseBody buildStreamingResponseBody(Object response) { - return outputStream -> { - try { - outputStream.write(JsonUtil.toJson(response).getBytes()); - outputStream.flush(); - } catch (IOException e) { - // 处理写入数据时的异常 - log.error("Fail to write data", e); - } finally { - // 确保输出流被关闭 - try { - outputStream.close(); - } catch (IOException e) { - log.error("Fail to close output stream", e); - } - } - }; + return Response.buildSuccessResp(aiAnswer); } /** From b56421fd59a9703e0e3a76e36aeea24d90461f8a Mon Sep 17 00:00:00 2001 From: jsonwan Date: Fri, 16 Aug 2024 10:56:07 +0800 Subject: [PATCH 038/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.增加获取单次对话流式响应数据接口定义。 --- .../validation/ValidationMessages.properties | 1 + .../ValidationMessages_en.properties | 1 + .../ValidationMessages_en_US.properties | 1 + .../ValidationMessages_zh.properties | 1 + .../ValidationMessages_zh_CN.properties | 1 + .../job/analysis/api/web/WebAIResource.java | 52 ++++++++++++++-- .../analysis/model/web/resp/AIChatRecord.java | 3 + .../api/web/impl/WebAIResourceImpl.java | 60 +++++++++++++------ .../analysis/model/dto/AIChatHistoryDTO.java | 1 + .../service/ai/AIAnalyzeErrorService.java | 6 +- .../service/ai/AICheckScriptService.java | 6 +- .../job/analysis/service/ai/ChatService.java | 6 +- .../ai/impl/AIAnalyzeErrorServiceImpl.java | 24 +++++--- .../service/ai/impl/AIBaseService.java | 27 ++++----- .../ai/impl/AICheckScriptServiceImpl.java | 10 ++-- .../service/ai/impl/ChatServiceImpl.java | 16 ++--- 16 files changed, 142 insertions(+), 74 deletions(-) diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties index f24353cd8e..b7d04b2ce7 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages.properties @@ -113,6 +113,7 @@ validation.constraints.AIGeneralChat_contentEmpty.message=聊天输入内容不 validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不能为空 validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 +validation.constraints.AIInvalidRecordId.message=对话记录ID只能为正整数 validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=步骤执行类型不能为空 validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties index fd810aff7d..b4a739c651 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en.properties @@ -112,6 +112,7 @@ validation.constraints.AIGeneralChat_contentEmpty.message=Chat message cannot be validation.constraints.AIAnalyzeError_contentEmpty.message=Error content cannot be empty validation.constraints.AIInvalidHistoryStart.message=Start must be equal or greater than 0 validation.constraints.AIInvalidHistoryLength.message=Length must be between {min} and {max} +validation.constraints.AIInvalidRecordId.message=Record id must be a positive number validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=TaskInstanceId cannot be empty validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=StepExecuteType cannot be empty validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=StepInstanceId cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties index fd810aff7d..b4a739c651 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_en_US.properties @@ -112,6 +112,7 @@ validation.constraints.AIGeneralChat_contentEmpty.message=Chat message cannot be validation.constraints.AIAnalyzeError_contentEmpty.message=Error content cannot be empty validation.constraints.AIInvalidHistoryStart.message=Start must be equal or greater than 0 validation.constraints.AIInvalidHistoryLength.message=Length must be between {min} and {max} +validation.constraints.AIInvalidRecordId.message=Record id must be a positive number validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=TaskInstanceId cannot be empty validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=StepExecuteType cannot be empty validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=StepInstanceId cannot be empty diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties index f24353cd8e..b7d04b2ce7 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh.properties @@ -113,6 +113,7 @@ validation.constraints.AIGeneralChat_contentEmpty.message=聊天输入内容不 validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不能为空 validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 +validation.constraints.AIInvalidRecordId.message=对话记录ID只能为正整数 validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=步骤执行类型不能为空 validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties index f24353cd8e..b7d04b2ce7 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/validation/ValidationMessages_zh_CN.properties @@ -113,6 +113,7 @@ validation.constraints.AIGeneralChat_contentEmpty.message=聊天输入内容不 validation.constraints.AIAnalyzeError_contentEmpty.message=报错信息内容不能为空 validation.constraints.AIInvalidHistoryStart.message=对话历史记录起始位置start必须大于等于0 validation.constraints.AIInvalidHistoryLength.message=对话历史记录数量length必须在 {min}-{max} 之间 +validation.constraints.AIInvalidRecordId.message=对话记录ID只能为正整数 validation.constraints.AIAnalyzeError_taskInstanceIdEmpty.message=任务实例ID不能为空 validation.constraints.AIAnalyzeError_stepExecuteTypeEmpty.message=步骤执行类型不能为空 validation.constraints.AIAnalyzeError_stepInstanceIdEmpty.message=步骤实例ID不能为空 diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 1e712fba61..1dea1060a5 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -27,7 +27,6 @@ import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.common.annotation.WebAPI; @@ -37,17 +36,20 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.hibernate.validator.constraints.Range; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import springfox.documentation.annotations.ApiIgnore; import javax.validation.constraints.Min; @@ -108,7 +110,7 @@ Response> getLatestChatHistoryList( @ApiOperation(value = "通用聊天接口", produces = "application/json") @PostMapping("/general/chat") - Response generalChat( + Response generalChat( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -128,7 +130,7 @@ Response generalChat( @ApiOperation(value = "检查脚本", produces = "application/json") @PostMapping("/checkScript") - Response checkScript( + Response checkScript( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -148,7 +150,7 @@ Response checkScript( @ApiOperation(value = "分析报错信息", produces = "application/json") @PostMapping("/analyzeError") - Response analyzeError( + Response analyzeError( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -166,6 +168,48 @@ Response analyzeError( @RequestBody AIAnalyzeErrorReq req ); + @ApiOperation(value = "获取单次对话流式数据(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0,\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\",\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null,\"errorDetail\": null}", produces = "application/json") + @GetMapping("/chatStream") + ResponseEntity getChatStream( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId, + @ApiParam(value = "对话记录ID") + @RequestParam(value = "recordId") + @Min(value = 1L, message = "{validation.constraints.AIInvalidRecordId.message}") + Long recordId + ); + + @ApiOperation(value = "终止对话", produces = "application/json") + @PutMapping("/terminateChat") + Response terminateChat( + @ApiParam("用户名,网关自动传入") + @RequestHeader("username") + String username, + @ApiIgnore + @RequestAttribute(value = "appResourceScope") + AppResourceScope appResourceScope, + @ApiParam(value = "资源范围类型", required = true) + @PathVariable(value = "scopeType") + String scopeType, + @ApiParam(value = "资源范围ID", required = true) + @PathVariable(value = "scopeId") + String scopeId, + @ApiParam(value = "对话记录ID") + @RequestParam(value = "recordId") + @Min(value = 1L, message = "{validation.constraints.AIInvalidRecordId.message}") + Long recordId + ); + @ApiOperation(value = "清空聊天记录", produces = "application/json") @DeleteMapping("/clearChatHistory") Response clearChatHistory( diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java index c0a22733b5..87f25ed1d1 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIChatRecord.java @@ -37,6 +37,9 @@ @Data public class AIChatRecord { + @ApiModelProperty(value = "ID") + private Long id; + @ApiModelProperty(value = "用户输入") private UserInput userInput; diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index c222d31bd0..1580b79842 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -49,7 +49,9 @@ import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.util.ArrayList; import java.util.List; @@ -127,34 +129,54 @@ private AIAnswer getGreetingAIAnswer() { } @Override - public Response generalChat(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AIGeneralChatReq req) { - AIAnswer aiAnswer = chatService.chatWithAI(username, req.getContent()); - return Response.buildSuccessResp(aiAnswer); + public Response generalChat(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIGeneralChatReq req) { + AIChatRecord aiChatRecord = chatService.chatWithAI(username, req.getContent()); + return Response.buildSuccessResp(aiChatRecord); } @Override - public Response checkScript(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - AICheckScriptReq req) { - AIAnswer aiAnswer = aiCheckScriptService.check(username, req.getType(), req.getContent()); - return Response.buildSuccessResp(aiAnswer); + public Response checkScript(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AICheckScriptReq req) { + AIChatRecord aiChatRecord = aiCheckScriptService.check(username, req.getType(), req.getContent()); + return Response.buildSuccessResp(aiChatRecord); } @Override - public Response analyzeError(String username, + public Response analyzeError(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + AIAnalyzeErrorReq req) { + checkScriptLogContentLength(req); + AIChatRecord aiChatRecord = aiAnalyzeErrorService.analyze(username, appResourceScope.getAppId(), req); + return Response.buildSuccessResp(aiChatRecord); + } + + @Override + public ResponseEntity getChatStream(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + Long recordId) { + // TODO + return ResponseEntity.ok().body(null); + } + + @Override + public Response terminateChat(String username, AppResourceScope appResourceScope, String scopeType, String scopeId, - AIAnalyzeErrorReq req) { - checkScriptLogContentLength(req); - AIAnswer aiAnswer = aiAnalyzeErrorService.analyze(username, appResourceScope.getAppId(), req); - return Response.buildSuccessResp(aiAnswer); + Long recordId) { + // TODO + return Response.buildSuccessResp(true); } /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java index 940ec346a0..02c1749ee0 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java @@ -119,6 +119,7 @@ public void updateTotalTime() { public AIChatRecord toAIChatRecord() { AIChatRecord aiChatRecord = new AIChatRecord(); + aiChatRecord.setId(id); UserInput userInput = new UserInput(); userInput.setContent(this.userInput); userInput.setTime(startTime); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java index bbbabdd431..faae11d762 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIAnalyzeErrorService.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.analysis.service.ai; import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; public interface AIAnalyzeErrorService { @@ -35,7 +35,7 @@ public interface AIAnalyzeErrorService { * @param username 用户名 * @param appId Job业务ID * @param req 请求内容 - * @return AI回答 + * @return AI对话记录 */ - AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req); + AIChatRecord analyze(String username, Long appId, AIAnalyzeErrorReq req); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java index 7804fc1c3b..99524dd176 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AICheckScriptService.java @@ -24,7 +24,7 @@ package com.tencent.bk.job.analysis.service.ai; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; public interface AICheckScriptService { @@ -34,7 +34,7 @@ public interface AICheckScriptService { * @param username 用户名 * @param type 脚本类型 * @param scriptContent 脚本内容 - * @return AI回答 + * @return AI对话记录 */ - AIAnswer check(String username, Integer type, String scriptContent); + AIChatRecord check(String username, Integer type, String scriptContent); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java index f042ac7405..ad8db1c2d9 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -25,7 +25,7 @@ package com.tencent.bk.job.analysis.service.ai; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import java.util.List; @@ -36,9 +36,9 @@ public interface ChatService { * * @param username 用户名 * @param userInput 用户输入 - * @return AI回答结果 + * @return AI对话记录 */ - AIAnswer chatWithAI(String username, String userInput); + AIChatRecord chatWithAI(String username, String userInput); /** * 获取最近的聊天记录列表 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java index cb12163fbe..f983c1e224 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnalyzeErrorServiceImpl.java @@ -26,10 +26,9 @@ import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.service.ai.AIAnalyzeErrorService; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; -import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.FileTransferTaskErrorAIPromptService; import com.tencent.bk.job.analysis.service.ai.ScriptExecuteTaskErrorAIPromptService; import com.tencent.bk.job.analysis.service.ai.context.TaskContextService; @@ -54,10 +53,9 @@ public class AIAnalyzeErrorServiceImpl extends AIBaseService implements AIAnalyz public AIAnalyzeErrorServiceImpl(TaskContextService taskContextService, ScriptExecuteTaskErrorAIPromptService scriptExecuteTaskErrorAIPromptService, FileTransferTaskErrorAIPromptService fileTransferTaskErrorAIPromptService, - AIService aiService, AIChatHistoryService aiChatHistoryService, AIMessageI18nService aiMessageI18nService) { - super(aiService, aiChatHistoryService); + super(aiChatHistoryService); this.taskContextService = taskContextService; this.scriptExecuteTaskErrorAIPromptService = scriptExecuteTaskErrorAIPromptService; this.fileTransferTaskErrorAIPromptService = fileTransferTaskErrorAIPromptService; @@ -70,10 +68,10 @@ public AIAnalyzeErrorServiceImpl(TaskContextService taskContextService, * @param username 用户名 * @param appId Job业务ID * @param req 请求内容 - * @return AI回答 + * @return AI对话记录 */ @Override - public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { + public AIChatRecord analyze(String username, Long appId, AIAnalyzeErrorReq req) { TaskContextQuery contextQuery = TaskContextQuery.fromAIAnalyzeErrorReq(appId, req); TaskContext taskContext = taskContextService.getTaskContext(username, contextQuery); String errorContent = req.getContent(); @@ -84,16 +82,24 @@ public AIAnswer analyze(String username, Long appId, AIAnalyzeErrorReq req) { errorContent ); if (!taskContext.isTaskFail()) { - return getDirectlyAIAnswer(username, aiPromptDTO, aiMessageI18nService.getNotFailTaskAIAnswerMessage()); + return getDirectlyAIChatRecord( + username, + aiPromptDTO, + aiMessageI18nService.getNotFailTaskAIAnswerMessage() + ); } } else if (taskContext.isFileTask()) { aiPromptDTO = fileTransferTaskErrorAIPromptService.getPrompt(taskContext.getFileTaskContext()); if (!taskContext.isTaskFail()) { - return getDirectlyAIAnswer(username, aiPromptDTO, aiMessageI18nService.getNotFailTaskAIAnswerMessage()); + return getDirectlyAIChatRecord( + username, + aiPromptDTO, + aiMessageI18nService.getNotFailTaskAIAnswerMessage() + ); } } else { throw new InvalidParamException(ErrorCode.AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP); } - return getAIAnswer(username, aiPromptDTO); + return getAIChatRecord(username, aiPromptDTO); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java index b99760ac6b..851bcf3000 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -28,8 +28,8 @@ import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; -import com.tencent.bk.job.analysis.service.ai.AIService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -38,13 +38,10 @@ @Service public class AIBaseService { - private final AIService aiService; private final AIChatHistoryService aiChatHistoryService; @Autowired - public AIBaseService(AIService aiService, - AIChatHistoryService aiChatHistoryService) { - this.aiService = aiService; + public AIBaseService(AIChatHistoryService aiChatHistoryService) { this.aiChatHistoryService = aiChatHistoryService; } @@ -53,9 +50,9 @@ public AIBaseService(AIService aiService, * * @param username 用户名 * @param aiPromptDTO AI提示符 - * @return AI回答 + * @return AI对话记录 */ - public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { + public AIChatRecord getAIChatRecord(String username, AIPromptDTO aiPromptDTO) { long startTime = System.currentTimeMillis(); String rawPrompt = aiPromptDTO.getRawPrompt(); String renderedPrompt = aiPromptDTO.getRenderedPrompt(); @@ -69,11 +66,8 @@ public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { null ); Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); - // 2.获取AI回答 - AIAnswer aiAnswer = aiService.getAIAnswer(renderedPrompt); - // 3.更新聊天记录状态 - aiChatHistoryService.finishAIAnswer(historyId, aiAnswer); - return aiAnswer; + aiChatHistoryDTO.setId(historyId); + return aiChatHistoryDTO.toAIChatRecord(); } /** @@ -82,9 +76,9 @@ public AIAnswer getAIAnswer(String username, AIPromptDTO aiPromptDTO) { * @param username 用户名 * @param aiPromptDTO AI提示符 * @param content 指定内容 - * @return AI回答 + * @return AI对话记录 */ - public AIAnswer getDirectlyAIAnswer(String username, AIPromptDTO aiPromptDTO, String content) { + public AIChatRecord getDirectlyAIChatRecord(String username, AIPromptDTO aiPromptDTO, String content) { long startTime = System.currentTimeMillis(); String rawPrompt = aiPromptDTO.getRawPrompt(); AIAnswer aiAnswer = new AIAnswer("0", "", content, System.currentTimeMillis()); @@ -96,8 +90,9 @@ public AIAnswer getDirectlyAIAnswer(String username, AIPromptDTO aiPromptDTO, St AIChatStatusEnum.FINISHED.getStatus(), aiAnswer ); - aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); - return aiAnswer; + Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); + aiChatHistoryDTO.setId(historyId); + return aiChatHistoryDTO.toAIChatRecord(); } /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java index b67c9aff91..c9a4732c33 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AICheckScriptServiceImpl.java @@ -25,10 +25,9 @@ package com.tencent.bk.job.analysis.service.ai.impl; import com.tencent.bk.job.analysis.model.dto.AIPromptDTO; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AICheckScriptService; -import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.CheckScriptAIPromptService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -42,15 +41,14 @@ public class AICheckScriptServiceImpl extends AIBaseService implements AICheckSc @Autowired public AICheckScriptServiceImpl(CheckScriptAIPromptService checkScriptAIPromptService, - AIService aiService, AIChatHistoryService aiChatHistoryService) { - super(aiService, aiChatHistoryService); + super(aiChatHistoryService); this.checkScriptAIPromptService = checkScriptAIPromptService; } @Override - public AIAnswer check(String username, Integer type, String scriptContent) { + public AIChatRecord check(String username, Integer type, String scriptContent) { AIPromptDTO aiPromptDTO = checkScriptAIPromptService.getPrompt(type, scriptContent); - return getAIAnswer(username, aiPromptDTO); + return getAIChatRecord(username, aiPromptDTO); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 3335194ff4..2181244ec8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -26,9 +26,8 @@ import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; -import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.ChatService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -41,17 +40,15 @@ @Service public class ChatServiceImpl implements ChatService { - private final AIService aiService; private final AIChatHistoryService aiChatHistoryService; @Autowired - public ChatServiceImpl(AIService aiService, AIChatHistoryService aiChatHistoryService) { - this.aiService = aiService; + public ChatServiceImpl(AIChatHistoryService aiChatHistoryService) { this.aiChatHistoryService = aiChatHistoryService; } @Override - public AIAnswer chatWithAI(String username, String userInput) { + public AIChatRecord chatWithAI(String username, String userInput) { Long startTime = System.currentTimeMillis(); // 1.获取最近的聊天记录 List chatHistoryDTOList = getLatestChatHistoryList(username, 0, 5); @@ -66,11 +63,8 @@ public AIAnswer chatWithAI(String username, String userInput) { null ); Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); - // 3.调用AI服务获取回答 - AIAnswer aiAnswer = aiService.getAIAnswer(chatHistoryDTOList, userInput); - // 4.更新聊天记录状态与回答内容 - aiChatHistoryService.finishAIAnswer(historyId, aiAnswer); - return aiAnswer; + aiChatHistoryDTO.setId(historyId); + return aiChatHistoryDTO.toAIChatRecord(); } @Override From 26b16879e6ddc29fbca94a3c2e3c24ce74966355 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 20 Aug 2024 23:00:10 +0800 Subject: [PATCH 039/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.增加获取单次对话流式响应数据接口实现。 --- src/backend/build.gradle | 5 +- src/backend/commons/ai-dev-sdk/build.gradle | 1 + .../bk/job/common/aidev/IBkOpenAIClient.java | 49 +++++ .../aidev/config/AIDevAutoConfiguration.java | 11 +- .../aidev/exception/BkOpenAIException.java | 41 +++++ .../aidev/impl/BkChatCompletionModel.java | 41 +++++ .../job/common/aidev/impl/BkOpenAIClient.java | 172 ++++++++++++++++++ .../aidev/model/common/AIDevMessage.java | 7 + .../i18n/exception/message.properties | 2 + .../i18n/exception/message_en.properties | 2 + .../i18n/exception/message_en_US.properties | 2 + .../i18n/exception/message_zh.properties | 2 + .../i18n/exception/message_zh_CN.properties | 2 + .../bk/job/common/constant/ErrorCode.java | 4 + .../job/analysis/consts/AIChatStatusEnum.java | 10 +- .../job/analysis/model/web/resp/AIAnswer.java | 8 + .../api/web/impl/WebAIResourceImpl.java | 5 +- .../config/ExecutorConfiguration.java | 15 ++ .../bk/job/analysis/config/WebMvcConfig.java | 63 +++++++ .../bk/job/analysis/dao/AIChatHistoryDAO.java | 28 +++ .../dao/impl/AIChatHistoryDAOImpl.java | 47 +++++ .../service/ai/AIChatHistoryService.java | 27 +++ .../bk/job/analysis/service/ai/AIService.java | 14 ++ .../job/analysis/service/ai/ChatService.java | 10 + .../ai/context/model/MessagePartEvent.java | 52 ++++++ .../service/ai/impl/AIBaseService.java | 2 +- .../ai/impl/AIChatHistoryServiceImpl.java | 29 +++ .../service/ai/impl/AIServiceImpl.java | 22 ++- .../service/ai/impl/ChatServiceImpl.java | 98 +++++++++- 29 files changed, 758 insertions(+), 13 deletions(-) create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkOpenAIClient.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkOpenAIException.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkChatCompletionModel.java create mode 100644 src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkOpenAIClient.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/WebMvcConfig.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java diff --git a/src/backend/build.gradle b/src/backend/build.gradle index e70d0ce344..a3acbfc4d7 100644 --- a/src/backend/build.gradle +++ b/src/backend/build.gradle @@ -132,7 +132,7 @@ ext { set('cronUtilsVersion', "9.1.6") set('otelJdbcVersion', "1.24.0-alpha") set('commonsValidatorVersion', "1.6") - set('okHttpVersion', "4.9.1") + set('okHttpVersion', "4.12.0") set('jcommanderVersion', "1.71") set('kubernetesJavaClientVersion', "11.0.4") set('springCloudKubernetesVersion', "2.0.6") @@ -147,6 +147,7 @@ ext { set('bkAuditJavaSdkVersion', "1.0.8") set('mockitoVersion', "4.0.0") set('embeddedRedisVersion', "0.6") + set('openai4jVersion', "0.18.0") } group "com.tencent.bk.job" @@ -316,6 +317,7 @@ subprojects { dependency "io.opentelemetry.instrumentation:opentelemetry-jdbc:$otelJdbcVersion" dependency "com.squareup.okhttp3:logging-interceptor:$okHttpVersion" dependency "com.squareup.okhttp3:okhttp:$okHttpVersion" + dependency "com.squareup.okhttp3:okhttp-sse:$okHttpVersion" dependencySet(group: "org.jooq", version: "$jooqVersion") { entry "jooq" entry "jooq-codegen" @@ -340,6 +342,7 @@ subprojects { dependency "com.tencent.bk.sdk:spring-boot-bk-audit-starter:$bkAuditJavaSdkVersion" dependency "org.mockito:mockito-inline:$mockitoVersion" dependency "com.github.kstyrc:embedded-redis:$embeddedRedisVersion" + dependency "dev.ai4j:openai4j:$openai4jVersion" } } dependencies { diff --git a/src/backend/commons/ai-dev-sdk/build.gradle b/src/backend/commons/ai-dev-sdk/build.gradle index 907fb546e8..cfbb1d24f2 100644 --- a/src/backend/commons/ai-dev-sdk/build.gradle +++ b/src/backend/commons/ai-dev-sdk/build.gradle @@ -34,6 +34,7 @@ dependencies { implementation 'io.micrometer:micrometer-registry-prometheus' implementation 'org.apache.commons:commons-collections4' implementation 'org.apache.httpcomponents:httpclient' + implementation 'dev.ai4j:openai4j' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkOpenAIClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkOpenAIClient.java new file mode 100644 index 0000000000..ecfeabcbc4 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/IBkOpenAIClient.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev; + +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +public interface IBkOpenAIClient { + + /** + * 获取混元流式回答,最终返回完整的回答结果,流式数据由partialRespConsumer接收处理 + * + * @param token 用户身份凭据 + * @param messageHistoryList 历史消息列表 + * @param userInput 用户输入 + * @param partialRespConsumer 分块消息处理器 + * @return 完整的回答结果Future + */ + CompletableFuture getHunYuanAnswerStream(String token, + List messageHistoryList, + String userInput, + Consumer partialRespConsumer); + +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java index 21ce38b786..9c07823dac 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/config/AIDevAutoConfiguration.java @@ -24,9 +24,10 @@ package com.tencent.bk.job.common.aidev.config; +import com.tencent.bk.job.common.aidev.impl.BkAIDevClient; +import com.tencent.bk.job.common.aidev.impl.BkOpenAIClient; import com.tencent.bk.job.common.esb.config.AppProperties; import com.tencent.bk.job.common.esb.config.BkApiGatewayProperties; -import com.tencent.bk.job.common.aidev.impl.BkAIDevClient; import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -46,4 +47,12 @@ public BkAIDevClient bkAIDevClient(MeterRegistry meterRegistry, return new BkAIDevClient(meterRegistry, appProperties, customPaasLoginProperties, bkApiGatewayProperties); } + @Bean + public BkOpenAIClient bkOpenAIClient(MeterRegistry meterRegistry, + AppProperties appProperties, + CustomPaasLoginProperties customPaasLoginProperties, + BkApiGatewayProperties bkApiGatewayProperties) { + return new BkOpenAIClient(meterRegistry, appProperties, customPaasLoginProperties, bkApiGatewayProperties); + } + } diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkOpenAIException.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkOpenAIException.java new file mode 100644 index 0000000000..e9435d9d3b --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/exception/BkOpenAIException.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.exception; + +import com.tencent.bk.job.common.exception.InternalException; +import lombok.Getter; +import lombok.ToString; + +/** + * 调用蓝鲸OpenAI接口异常 + */ +@Getter +@ToString +public class BkOpenAIException extends InternalException { + + public BkOpenAIException(Throwable cause, Integer errorCode) { + super(cause, errorCode); + } +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkChatCompletionModel.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkChatCompletionModel.java new file mode 100644 index 0000000000..ca39c37ba7 --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkChatCompletionModel.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.impl; + +public enum BkChatCompletionModel { + + HUNYUAN("hunyuan"); + + private final String value; + + BkChatCompletionModel(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkOpenAIClient.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkOpenAIClient.java new file mode 100644 index 0000000000..c87cb763bd --- /dev/null +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/impl/BkOpenAIClient.java @@ -0,0 +1,172 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.aidev.impl; + +import com.tencent.bk.job.common.aidev.IBkOpenAIClient; +import com.tencent.bk.job.common.aidev.config.CustomPaasLoginProperties; +import com.tencent.bk.job.common.aidev.exception.BkOpenAIException; +import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.esb.config.AppProperties; +import com.tencent.bk.job.common.esb.config.BkApiGatewayProperties; +import com.tencent.bk.job.common.esb.model.BkApiAuthorization; +import com.tencent.bk.job.common.util.json.JsonUtils; +import dev.ai4j.openai4j.OpenAiClient; +import dev.ai4j.openai4j.chat.ChatCompletionChoice; +import dev.ai4j.openai4j.chat.ChatCompletionRequest; +import dev.ai4j.openai4j.chat.ChatCompletionResponse; +import io.micrometer.core.instrument.MeterRegistry; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import static java.util.Collections.singletonMap; +import static java.util.concurrent.TimeUnit.SECONDS; + +@SuppressWarnings("SameParameterValue") +public class BkOpenAIClient implements IBkOpenAIClient { + + private static final String URI_LLM_V1 = "/appspace/gateway/llm/v1/"; + + private final AppProperties appProperties; + private final BkApiGatewayProperties.ApiGwConfig bkAIDevConfig; + private final CustomPaasLoginProperties customPaasLoginProperties; + private final String bkAIDevUrl; + + public BkOpenAIClient(MeterRegistry meterRegistry, + AppProperties appProperties, + CustomPaasLoginProperties customPaasLoginProperties, + BkApiGatewayProperties bkApiGatewayProperties) { + bkAIDevUrl = getBkAIDevUrlSafely(bkApiGatewayProperties); + this.appProperties = appProperties; + this.bkAIDevConfig = bkApiGatewayProperties.getBkAIDev(); + this.customPaasLoginProperties = customPaasLoginProperties; + } + + private static String getBkAIDevUrlSafely(BkApiGatewayProperties bkApiGatewayProperties) { + if (bkApiGatewayProperties == null || bkApiGatewayProperties.getBkAIDev() == null) { + return null; + } + return bkApiGatewayProperties.getBkAIDev().getUrl(); + } + + private String getLLMV1Url() { + return bkAIDevUrl + URI_LLM_V1; + } + + + private BkApiAuthorization buildAuthorization(String token) { + if (customPaasLoginProperties.isEnabled()) { + return BkApiAuthorization.bkTicketUserAuthorization( + getAppCode(), + getAppSecret(), + token + ); + } else { + return BkApiAuthorization.bkTokenUserAuthorization( + getAppCode(), + getAppSecret(), + token + ); + } + } + + private String getAppCode() { + String appCode = bkAIDevConfig.getAppCode(); + if (StringUtils.isNotBlank(appCode)) { + return appCode; + } + return appProperties.getCode(); + } + + private String getAppSecret() { + String appSecret = bkAIDevConfig.getAppSecret(); + if (StringUtils.isNotBlank(appSecret)) { + return appSecret; + } + return appProperties.getSecret(); + } + + @Override + @SuppressWarnings("unchecked") + public CompletableFuture getHunYuanAnswerStream(String token, + List messageHistoryList, + String userInput, + Consumer partialRespConsumer) { + final OpenAiClient client = OpenAiClient.builder() + .baseUrl(getLLMV1Url()) + .openAiApiKey("empty") + .customHeaders(singletonMap("X-Bkapi-Authorization", JsonUtils.toJson(buildAuthorization(token)))) + .logRequests() + .logResponses() + .build(); + OpenAiClient.OpenAiClientContext context = new OpenAiClient.OpenAiClientContext(); + ChatCompletionRequest request = buildRequest(messageHistoryList, userInput); + ChatCompletionRequest streamRequest = ChatCompletionRequest.builder().from(request).stream(true).build(); + CompletableFuture future = new CompletableFuture<>(); + StringBuilder responseBuilder = new StringBuilder(); + Consumer partialResponseHandler = response -> { + List choices = response.choices(); + if (choices == null || choices.isEmpty()) { + return; + } + String s = choices.get(0).delta().content(); + if (s != null) { + responseBuilder.append(s); + // TODO:限制最大消息内容 + if (partialRespConsumer != null) { + partialRespConsumer.accept(s); + } + } + }; + client.chatCompletion(context, streamRequest) + .onPartialResponse(partialResponseHandler) + .onComplete(() -> future.complete(responseBuilder.toString())) + .onError(future::completeExceptionally) + .execute(); + return future; + } + + private ChatCompletionRequest buildRequest(List messageHistoryList, String userInput) { + ChatCompletionRequest.Builder builder = ChatCompletionRequest.builder() + .model(BkChatCompletionModel.HUNYUAN.toString()); + if (CollectionUtils.isNotEmpty(messageHistoryList)) { + for (AIDevMessage message : messageHistoryList) { + if (message.isUserMessage()) { + builder.addUserMessage(message.getContent()); + } else if (message.isAssistantMessage()) { + builder.addAssistantMessage(message.getContent()); + } + } + } + builder.addUserMessage(userInput); + return builder.build(); + } +} diff --git a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java index 05ab9220fb..e075de2a30 100644 --- a/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java +++ b/src/backend/commons/ai-dev-sdk/src/main/java/com/tencent/bk/job/common/aidev/model/common/AIDevMessage.java @@ -40,4 +40,11 @@ public class AIDevMessage { private String content; + public boolean isUserMessage() { + return role.equals(ROLE_USER); + } + + public boolean isAssistantMessage() { + return role.equals(ROLE_ASSISTANT); + } } diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties index 50074d1f4a..2261239271 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message.properties @@ -83,6 +83,7 @@ #AIDev异常 1218001=AIDev平台接口数据异常 +1218002=蓝鲸OpenAI接口数据异常 ##业务错误-通用 @@ -233,6 +234,7 @@ ## 业务错误-统计分析服务(job-analysis) 1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 1264002=AI分析任务报错信息内容超过最大值:{0} +1264003=AI聊天记录不存在:id={0} ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties index 1d380d4e65..747e35820e 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en.properties @@ -83,6 +83,7 @@ #AIDev异常 1218001=BK-AIDev API data unexpected +1218002=BK-OpenAI API data unexpected ## Business error - common 1241001=Missing request parameters @@ -233,6 +234,7 @@ ## Business error (job-analysis) 1264001=Analyze task error only support Script or File step 1264002=AI analyze task error content exceed max length: {0} +1264003=AI chat history does not exist(id={0}) ## Business error - User/Login 1247001=User does not exist or is not logged in diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties index 1d380d4e65..747e35820e 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_en_US.properties @@ -83,6 +83,7 @@ #AIDev异常 1218001=BK-AIDev API data unexpected +1218002=BK-OpenAI API data unexpected ## Business error - common 1241001=Missing request parameters @@ -233,6 +234,7 @@ ## Business error (job-analysis) 1264001=Analyze task error only support Script or File step 1264002=AI analyze task error content exceed max length: {0} +1264003=AI chat history does not exist(id={0}) ## Business error - User/Login 1247001=User does not exist or is not logged in diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties index 50074d1f4a..2261239271 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh.properties @@ -83,6 +83,7 @@ #AIDev异常 1218001=AIDev平台接口数据异常 +1218002=蓝鲸OpenAI接口数据异常 ##业务错误-通用 @@ -233,6 +234,7 @@ ## 业务错误-统计分析服务(job-analysis) 1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 1264002=AI分析任务报错信息内容超过最大值:{0} +1264003=AI聊天记录不存在:id={0} ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 diff --git a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties index 334554b8f3..8aa9679b08 100644 --- a/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties +++ b/src/backend/commons/common-i18n/src/main/resources/i18n/exception/message_zh_CN.properties @@ -83,6 +83,7 @@ #AIDev异常 1218001=AIDev平台接口数据异常 +1218002=蓝鲸OpenAI接口数据异常 ##业务错误-通用 @@ -234,6 +235,7 @@ ## 统计分析服务job-analysis错误码 1264001=AI分析任务报错信息仅支持脚本或文件任务步骤 1264002=AI分析任务报错信息内容超过最大值:{0} +1264003=AI聊天记录不存在:id={0} ##业务错误-用户服务、登录服务 1247001=用户不存在或者未登录 diff --git a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java index fc583cf9da..1bfb9dfac6 100644 --- a/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java +++ b/src/backend/commons/common/src/main/java/com/tencent/bk/job/common/constant/ErrorCode.java @@ -91,6 +91,8 @@ public class ErrorCode { // AIDev平台异常 // AIDev接口数据异常 public static final int BK_AI_DEV_API_DATA_ERROR = 1218001; + // 蓝鲸OpenAI接口数据异常 + public static final int BK_OPEN_AI_API_DATA_ERROR = 1218002; // ======== 系统错误-权限错误 ==================// // 用户({0})权限不足,请前往权限中心确认并申请补充后重试 @@ -461,5 +463,7 @@ public class ErrorCode { public static final int AI_ANALYZE_ERROR_ONLY_SUPPORT_SCRIPT_OR_FILE_STEP = 1264001; // AI分析任务报错信息内容超过最大值:{0} public static final int AI_ANALYZE_ERROR_CONTENT_EXCEED_MAX_LENGTH = 1264002; + // AI聊天记录不存在:id={0} + public static final int AI_CHAT_HISTORY_NOT_FOUND_BY_ID = 1264003; // 统计分析服务job-analysis错误码 end } diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java index f1e93ac038..35d1919fbf 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java @@ -28,6 +28,10 @@ * AI对话状态 */ public enum AIChatStatusEnum { + /** + * 初始状态 + */ + INIT(0), /** * 正在回答 */ @@ -35,7 +39,11 @@ public enum AIChatStatusEnum { /** * 已完成 */ - FINISHED(2); + FINISHED(2), + /** + * 已取消 + */ + CANCELED(3); private final int status; diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java index f6ed0bc275..83654d8ed6 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/resp/AIAnswer.java @@ -71,4 +71,12 @@ public static AIAnswer successAnswer(String content) { aiAnswer.setContent(content); return aiAnswer; } + + public static AIAnswer failAnswer(String content) { + AIAnswer aiAnswer = new AIAnswer(); + aiAnswer.setErrorCode("1"); + aiAnswer.setTime(System.currentTimeMillis()); + aiAnswer.setContent(content); + return aiAnswer; + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 1580b79842..aeea5253d6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -49,6 +49,7 @@ import com.tencent.bk.job.execute.common.constants.StepExecuteTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @@ -165,8 +166,8 @@ public ResponseEntity getChatStream(String username, String scopeType, String scopeId, Long recordId) { - // TODO - return ResponseEntity.ok().body(null); + StreamingResponseBody streamingResponseBody = chatService.getChatStream(username, recordId); + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(streamingResponseBody); } @Override diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/ExecutorConfiguration.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/ExecutorConfiguration.java index d440f23c0f..85aebe7fbd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/ExecutorConfiguration.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/ExecutorConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.context.annotation.Configuration; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -39,6 +40,20 @@ @Configuration(value = "jobAnalysisExecutorConfig") public class ExecutorConfiguration { + @Bean("analysisAsyncTaskExecutor") + public ThreadPoolExecutor analysisAsyncTaskExecutor(MeterRegistry meterRegistry) { + return new WatchableThreadPoolExecutor( + meterRegistry, + "analysisAsyncTaskExecutor", + 0, + 50, + 180L, + TimeUnit.SECONDS, + new SynchronousQueue<>(), + (r, executor) -> log.warn("AsyncTask runnable rejected!") + ); + } + @Bean("analysisScheduleExecutor") public ThreadPoolExecutor analysisScheduleExecutor(MeterRegistry meterRegistry) { return new WatchableThreadPoolExecutor( diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/WebMvcConfig.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/WebMvcConfig.java new file mode 100644 index 0000000000..04dda79afe --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/WebMvcConfig.java @@ -0,0 +1,63 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.support.TaskExecutorAdapter; +import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 自定义WebMvc配置 + */ +@Slf4j +@Configuration +public class WebMvcConfig { + + @Bean + public WebMvcConfigurer jobWebMvcConfigurer( + @Qualifier("analysisAsyncTaskExecutor") ThreadPoolExecutor threadPoolExecutor + ) { + return new JobWebMvcConfigurer(threadPoolExecutor); + } + + static class JobWebMvcConfigurer implements WebMvcConfigurer { + ThreadPoolExecutor threadPoolExecutor; + + public JobWebMvcConfigurer(ThreadPoolExecutor threadPoolExecutor) { + this.threadPoolExecutor = threadPoolExecutor; + } + + @Override + public void configureAsyncSupport(AsyncSupportConfigurer configurer) { + configurer.setTaskExecutor(new TaskExecutorAdapter(threadPoolExecutor)); + } + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java index d44010c8d3..b7346acc2e 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java @@ -37,6 +37,15 @@ public interface AIChatHistoryDAO { */ Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO); + /** + * 设置聊天记录状态 + * + * @param historyId AI聊天记录ID + * @param status AI聊天记录状态 + * @return 受影响的行数 + */ + int updateChatHistoryStatus(Long historyId, int status); + /** * 更新聊天记录状态 * @@ -65,6 +74,25 @@ int updateChatHistoryStatusAndAIAnswer(Long historyId, */ List getLatestChatHistoryList(String username, Integer start, Integer length); + /** + * 获取最近已完成的聊天记录列表 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近已完成的聊天记录列表 + */ + List getLatestFinishedChatHistoryList(String username, Integer start, Integer length); + + /** + * 获取聊天记录 + * + * @param username 用户名 + * @param id 聊天记录ID + * @return 聊天记录 + */ + AIChatHistoryDTO getChatHistory(String username, Long id); + /** * 软删除聊天记录(优先删除创建时间较早的) * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index ea4b415e2d..d3da4340a1 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.dao.impl; +import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.dao.AIChatHistoryDAO; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.tables.AiChatHistory; @@ -104,6 +105,14 @@ public Long insertAIChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { } } + @Override + public int updateChatHistoryStatus(Long historyId, int status) { + return dslContext.update(defaultTable) + .set(defaultTable.STATUS, JooqDataTypeUtil.getByteFromInteger(status)) + .where(defaultTable.ID.eq(historyId)) + .execute(); + } + @Override public int updateChatHistoryStatusAndAIAnswer(Long historyId, Integer status, @@ -129,6 +138,44 @@ public List getLatestChatHistoryList(String username, Integer return listByConditions(conditions, start, length); } + @Override + public List getLatestFinishedChatHistoryList(String username, Integer start, Integer length) { + Collection conditions = new ArrayList<>(); + conditions.add(defaultTable.USERNAME.eq(username)); + conditions.add(defaultTable.STATUS.eq((byte) AIChatStatusEnum.FINISHED.getStatus())); + conditions.add(defaultTable.IS_DELETED.eq(JooqDataTypeUtil.buildUByte(0))); + return listByConditions(conditions, start, length); + } + + @Override + public AIChatHistoryDTO getChatHistory(String username, Long id) { + Collection conditions = new ArrayList<>(); + conditions.add(defaultTable.USERNAME.eq(username)); + conditions.add(defaultTable.ID.eq(id)); + conditions.add(defaultTable.IS_DELETED.eq(JooqDataTypeUtil.buildUByte(0))); + val query = dslContext.select( + defaultTable.ID, + defaultTable.USERNAME, + defaultTable.USER_INPUT, + defaultTable.PROMPT_TEMPLATE_ID, + defaultTable.AI_INPUT, + defaultTable.STATUS, + defaultTable.AI_ANSWER, + defaultTable.ERROR_CODE, + defaultTable.ERROR_MESSAGE, + defaultTable.START_TIME, + defaultTable.ANSWER_TIME, + defaultTable.TOTAL_TIME + ) + .from(defaultTable) + .where(conditions); + val record = query.fetchOne(); + if (record == null) { + return null; + } + return convertRecordToDto(record); + } + @Override public int softDeleteChatHistory(String username, Integer limit) { Collection conditions = new ArrayList<>(); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java index 6addf933c4..6791383200 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -56,6 +56,14 @@ AIChatHistoryDTO buildAIChatHistoryDTO(String username, */ Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO); + /** + * 更新聊天记录状态为正在回答中 + * + * @param historyId AI聊天记录ID + * @return 受影响的行数 + */ + int setAIAnswerReplying(Long historyId); + /** * 更新聊天记录状态 * @@ -76,6 +84,25 @@ AIChatHistoryDTO buildAIChatHistoryDTO(String username, */ List getLatestChatHistoryList(String username, Integer start, Integer length); + /** + * 获取最近已完成的聊天记录列表 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近的聊天记录列表 + */ + List getLatestFinishedChatHistoryList(String username, Integer start, Integer length); + + /** + * 根据用户名与ID获取聊天记录 + * + * @param username 用户名 + * @param id ID + * @return 聊天记录 + */ + AIChatHistoryDTO getChatHistory(String username, Long id); + /** * 软删除聊天记录 * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java index 4176e4e826..00cfb4c4e8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIService.java @@ -28,6 +28,8 @@ import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; public interface AIService { @@ -51,4 +53,16 @@ public interface AIService { * @return AI回答结果 */ AIAnswer getAIAnswer(String userInput); + + /** + * 获取AI回答流(流式接口) + * + * @param chatHistoryDTOList 历史聊天记录 + * @param userInput 用户输入 + * @param partialRespConsumer AI回答流回调 + * @return AI回答结果Future + */ + CompletableFuture getAIAnswerStream(List chatHistoryDTOList, + String userInput, + Consumer partialRespConsumer); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java index ad8db1c2d9..8471f3b092 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -26,6 +26,7 @@ import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.util.List; @@ -49,4 +50,13 @@ public interface ChatService { * @return 最近的聊天记录列表 */ List getLatestChatHistoryList(String username, Integer start, Integer length); + + /** + * 获取聊天流式数据 + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 流式数据 + */ + StreamingResponseBody getChatStream(String username, Long recordId); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java new file mode 100644 index 0000000000..56f7c52cae --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 含有部分消息的事件 + */ +@AllArgsConstructor +@Data +public class MessagePartEvent { + /** + * 是否为消息结束事件 + */ + private boolean end; + /** + * 消息内容 + */ + private String messagePart; + + public static MessagePartEvent endEvent() { + return new MessagePartEvent(true, null); + } + + public static MessagePartEvent normalEvent(String messagePart) { + return new MessagePartEvent(false, messagePart); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java index 851bcf3000..fcc6ad3df6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIBaseService.java @@ -62,7 +62,7 @@ public AIChatRecord getAIChatRecord(String username, AIPromptDTO aiPromptDTO) { startTime, rawPrompt, renderedPrompt, - AIChatStatusEnum.REPLYING.getStatus(), + AIChatStatusEnum.INIT.getStatus(), null ); Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index 1230e7e77a..adc852ef62 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -80,6 +80,11 @@ public Long insertChatHistory(AIChatHistoryDTO aiChatHistoryDTO) { return aiChatHistoryDAO.insertAIChatHistory(aiChatHistoryDTO); } + @Override + public int setAIAnswerReplying(Long historyId) { + return aiChatHistoryDAO.updateChatHistoryStatus(historyId, AIChatStatusEnum.REPLYING.getStatus()); + } + @Override public int finishAIAnswer(Long historyId, AIAnswer aiAnswer) { return aiChatHistoryDAO.updateChatHistoryStatusAndAIAnswer( @@ -107,6 +112,30 @@ public List getLatestChatHistoryList(String username, Integer return aiChatHistoryList; } + /** + * 从DB获取最近已完成的聊天记录列表,按起始时间升序排列 + * + * @param username 用户名 + * @param start 起始位置 + * @param length 长度 + * @return 最近已完成的聊天记录列表 + */ + @Override + public List getLatestFinishedChatHistoryList(String username, Integer start, Integer length) { + List aiChatHistoryList = aiChatHistoryDAO.getLatestFinishedChatHistoryList( + username, + start, + length + ); + aiChatHistoryList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); + return aiChatHistoryList; + } + + @Override + public AIChatHistoryDTO getChatHistory(String username, Long id) { + return aiChatHistoryDAO.getChatHistory(username, id); + } + /** * 从DB中分批软删除聊天记录(优先删除创建时间较早的) * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java index 0a07b6d072..abfb9103e6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIServiceImpl.java @@ -29,6 +29,7 @@ import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.login.LoginTokenService; import com.tencent.bk.job.common.aidev.IBkAIDevClient; +import com.tencent.bk.job.common.aidev.IBkOpenAIClient; import com.tencent.bk.job.common.aidev.model.common.AIDevMessage; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; @@ -38,6 +39,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; @Slf4j @Service @@ -45,11 +48,28 @@ public class AIServiceImpl implements AIService { private final LoginTokenService loginTokenService; private final IBkAIDevClient bkAIDevClient; + private final IBkOpenAIClient bkOpenAIClient; @Autowired - public AIServiceImpl(LoginTokenService loginTokenService, IBkAIDevClient bkAIDevClient) { + public AIServiceImpl(LoginTokenService loginTokenService, + IBkAIDevClient bkAIDevClient, + IBkOpenAIClient bkOpenAIClient) { this.loginTokenService = loginTokenService; this.bkAIDevClient = bkAIDevClient; + this.bkOpenAIClient = bkOpenAIClient; + } + + @Override + public CompletableFuture getAIAnswerStream(List chatHistoryDTOList, + String userInput, + Consumer partialRespConsumer) { + String token = loginTokenService.getToken(); + return bkOpenAIClient.getHunYuanAnswerStream( + token, + buildMessageHistoryList(chatHistoryDTOList), + userInput, + partialRespConsumer + ); } @Override diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 2181244ec8..9064e6e0ab 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -26,40 +26,54 @@ import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.ChatService; +import com.tencent.bk.job.analysis.service.ai.context.model.MessagePartEvent; +import com.tencent.bk.job.common.constant.ErrorCode; +import com.tencent.bk.job.common.exception.NotFoundException; +import com.tencent.bk.job.common.model.Response; +import com.tencent.bk.job.common.util.json.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; @Slf4j @Service public class ChatServiceImpl implements ChatService { private final AIChatHistoryService aiChatHistoryService; + private final AIService aiService; @Autowired - public ChatServiceImpl(AIChatHistoryService aiChatHistoryService) { + public ChatServiceImpl(AIChatHistoryService aiChatHistoryService, + AIService aiService) { this.aiChatHistoryService = aiChatHistoryService; + this.aiService = aiService; } @Override public AIChatRecord chatWithAI(String username, String userInput) { Long startTime = System.currentTimeMillis(); - // 1.获取最近的聊天记录 - List chatHistoryDTOList = getLatestChatHistoryList(username, 0, 5); - chatHistoryDTOList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); - // 2.保存初始聊天记录 + // 1.保存初始聊天记录 AIChatHistoryDTO aiChatHistoryDTO = aiChatHistoryService.buildAIChatHistoryDTO( username, startTime, userInput, userInput, - AIChatStatusEnum.REPLYING.getStatus(), + AIChatStatusEnum.INIT.getStatus(), null ); Long historyId = aiChatHistoryService.insertChatHistory(aiChatHistoryDTO); @@ -72,4 +86,76 @@ public List getLatestChatHistoryList(String username, Integer return aiChatHistoryService.getLatestChatHistoryList(username, start, length); } + @Override + public StreamingResponseBody getChatStream(String username, Long recordId) { + // 1.获取最近已完成的聊天记录作为上下文 + List chatHistoryDTOList = aiChatHistoryService.getLatestFinishedChatHistoryList( + username, + 0, + 5 + ); + chatHistoryDTOList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); + // 2.查出指定的聊天记录 + AIChatHistoryDTO currentChatHistoryDTO = aiChatHistoryService.getChatHistory(username, recordId); + if (currentChatHistoryDTO == null) { + throw new NotFoundException( + ErrorCode.AI_CHAT_HISTORY_NOT_FOUND_BY_ID, + new String[]{String.valueOf(recordId)} + ); + } + int affectedNum = aiChatHistoryService.setAIAnswerReplying(recordId); + if (affectedNum == 0) { + log.info("AIAnswer is already replying, re-reply, recordId={}", recordId); + } + // 3.获取AI回答放入阻塞队列 + LinkedBlockingQueue messageQueue = new LinkedBlockingQueue<>(10000); + AtomicBoolean isFinished = new AtomicBoolean(false); + StreamingResponseBody streamingResponseBody = outputStream -> { + while (!isFinished.get()) { + try { + MessagePartEvent event = messageQueue.take(); + if (event.isEnd()) { + break; + } + String partMessage = event.getMessagePart(); + Response respBody = Response.buildSuccessResp(AIAnswer.successAnswer(partMessage)); + String message = JsonUtils.toJson(respBody) + "\n"; + outputStream.write(message.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } catch (InterruptedException e) { + log.debug("Interrupted when take message from queue", e); + } catch (IOException e) { + log.error("Write resp to output stream failed", e); + } + } + outputStream.close(); + }; + Consumer partialRespConsumer = + messagePart -> messageQueue.offer(MessagePartEvent.normalEvent(messagePart)); + CompletableFuture future = aiService.getAIAnswerStream( + chatHistoryDTOList, + currentChatHistoryDTO.getAiInput(), + partialRespConsumer + ); + future.whenComplete((content, throwable) -> { + isFinished.set(true); + messageQueue.offer(MessagePartEvent.endEvent()); + // 4.将AI回答写入DB + AIAnswer aiAnswer; + if (throwable == null) { + aiAnswer = AIAnswer.successAnswer(content); + } else { + aiAnswer = AIAnswer.failAnswer(throwable.getMessage()); + } + int affectedRow = aiChatHistoryService.finishAIAnswer(recordId, aiAnswer); + log.info( + "AIAnswer finished, recordId={}, length={}, affectedRow={}", + recordId, + content.length(), + affectedRow + ); + }); + return streamingResponseBody; + } + } From f018c3495127c4775f224dfaf7867cbdf51f5bca Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 21 Aug 2024 16:09:06 +0800 Subject: [PATCH 040/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.调整流式响应数据接口方法为POST。 --- .../job/analysis/api/web/WebAIResource.java | 17 +++++--- .../model/web/req/GenerateChatStreamReq.java | 43 +++++++++++++++++++ .../api/web/impl/WebAIResourceImpl.java | 13 +++--- .../job/analysis/service/ai/ChatService.java | 2 +- .../service/ai/impl/ChatServiceImpl.java | 2 +- 5 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/GenerateChatStreamReq.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 1dea1060a5..0c14e0e545 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -27,6 +27,7 @@ import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; +import com.tencent.bk.job.analysis.model.web.req.GenerateChatStreamReq; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.common.annotation.WebAPI; @@ -168,9 +169,12 @@ Response analyzeError( @RequestBody AIAnalyzeErrorReq req ); - @ApiOperation(value = "获取单次对话流式数据(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0,\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\",\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null,\"errorDetail\": null}", produces = "application/json") - @GetMapping("/chatStream") - ResponseEntity getChatStream( + @ApiOperation(value = "获取单次对话流式数据(流式接口),返回换行符分隔的多条JSON数据,可分块读取,单条JSON数据格式:{\"success\":true,\"code\":0," + + "\"errorMsg\":\"成功\",\"data\":{\"errorCode\":\"0\",\"errorMessage\":null,\"content\":\"hello world\"," + + "\"time\":\"2024-08-14 12:00:00\"},\"requestId\":\"fb991170da868b2a1eb5835bc426e992\",\"authResult\": null," + + "\"errorDetail\": null}", produces = "application/json") + @PostMapping("/chatStream") + ResponseEntity generateChatStream( @ApiParam("用户名,网关自动传入") @RequestHeader("username") String username, @@ -183,10 +187,9 @@ ResponseEntity getChatStream( @ApiParam(value = "资源范围ID", required = true) @PathVariable(value = "scopeId") String scopeId, - @ApiParam(value = "对话记录ID") - @RequestParam(value = "recordId") - @Min(value = 1L, message = "{validation.constraints.AIInvalidRecordId.message}") - Long recordId + @ApiParam(value = "生成流式响应数据请求参数", required = true) + @Validated + @RequestBody GenerateChatStreamReq req ); @ApiOperation(value = "终止对话", produces = "application/json") diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/GenerateChatStreamReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/GenerateChatStreamReq.java new file mode 100644 index 0000000000..e701fc4d66 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/GenerateChatStreamReq.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiParam; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("生成流式响应请求体") +@Data +public class GenerateChatStreamReq { + @ApiParam(value = "对话记录ID") + @Min(value = 1L, message = "{validation.constraints.AIInvalidRecordId.message}") + Long recordId; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index aeea5253d6..c0a9cce71f 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -31,6 +31,7 @@ import com.tencent.bk.job.analysis.model.web.req.AIAnalyzeErrorReq; import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; +import com.tencent.bk.job.analysis.model.web.req.GenerateChatStreamReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; @@ -161,12 +162,12 @@ public Response analyzeError(String username, } @Override - public ResponseEntity getChatStream(String username, - AppResourceScope appResourceScope, - String scopeType, - String scopeId, - Long recordId) { - StreamingResponseBody streamingResponseBody = chatService.getChatStream(username, recordId); + public ResponseEntity generateChatStream(String username, + AppResourceScope appResourceScope, + String scopeType, + String scopeId, + GenerateChatStreamReq req) { + StreamingResponseBody streamingResponseBody = chatService.generateChatStream(username, req.getRecordId()); return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(streamingResponseBody); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java index 8471f3b092..20997114df 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -58,5 +58,5 @@ public interface ChatService { * @param recordId 对话记录ID * @return 流式数据 */ - StreamingResponseBody getChatStream(String username, Long recordId); + StreamingResponseBody generateChatStream(String username, Long recordId); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 9064e6e0ab..03659e44c7 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -87,7 +87,7 @@ public List getLatestChatHistoryList(String username, Integer } @Override - public StreamingResponseBody getChatStream(String username, Long recordId) { + public StreamingResponseBody generateChatStream(String username, Long recordId) { // 1.获取最近已完成的聊天记录作为上下文 List chatHistoryDTOList = aiChatHistoryService.getLatestFinishedChatHistoryList( username, From 9672b6a6e128bb2efea0237c6e908cf0014da00e Mon Sep 17 00:00:00 2001 From: jsonwan Date: Wed, 21 Aug 2024 18:15:00 +0800 Subject: [PATCH 041/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.终止对话单实例逻辑实现; 2.优化流式数据同步相关逻辑。 --- .../job/analysis/consts/AIChatStatusEnum.java | 4 +- .../api/web/impl/WebAIResourceImpl.java | 4 +- .../analysis/model/dto/AIChatHistoryDTO.java | 4 + .../service/ai/AIChatHistoryService.java | 8 ++ .../job/analysis/service/ai/ChatService.java | 9 ++ .../model/AsyncConsumerAndProducerPair.java | 44 ++++++++ .../service/ai/impl/AIAnswerHandler.java | 92 ++++++++++++++++ .../ai/impl/AIAnswerStreamSynchronizer.java | 98 +++++++++++++++++ .../ai/impl/AIChatHistoryServiceImpl.java | 5 + .../service/ai/impl/ChatServiceImpl.java | 103 ++++++++---------- 10 files changed, 308 insertions(+), 63 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerHandler.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java index 35d1919fbf..bc1146bc54 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java @@ -41,9 +41,9 @@ public enum AIChatStatusEnum { */ FINISHED(2), /** - * 已取消 + * 已终止 */ - CANCELED(3); + TERMINATED(3); private final int status; diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index c0a9cce71f..0940bed032 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -177,8 +177,8 @@ public Response terminateChat(String username, String scopeType, String scopeId, Long recordId) { - // TODO - return Response.buildSuccessResp(true); + boolean result = chatService.terminateChat(username, recordId); + return Response.buildSuccessResp(result); } /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java index 02c1749ee0..0ffe0e7405 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/dto/AIChatHistoryDTO.java @@ -133,4 +133,8 @@ public AIChatRecord toAIChatRecord() { aiChatRecord.setStatus(status); return aiChatRecord; } + + public boolean isInitOrReplying() { + return status == AIChatStatusEnum.INIT.getStatus() || status == AIChatStatusEnum.REPLYING.getStatus(); + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java index 6791383200..068f8960f2 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/AIChatHistoryService.java @@ -73,6 +73,14 @@ AIChatHistoryDTO buildAIChatHistoryDTO(String username, */ int finishAIAnswer(Long historyId, AIAnswer aiAnswer); + /** + * 终止聊天 + * + * @param historyId AI聊天记录ID + * @return 受影响的行数 + */ + int terminateAIAnswer(Long historyId); + /** * 获取最近的聊天记录列表 diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java index 20997114df..99ab42f3f8 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -59,4 +59,13 @@ public interface ChatService { * @return 流式数据 */ StreamingResponseBody generateChatStream(String username, Long recordId); + + /** + * 终止聊天 + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 是否终止成功 + */ + boolean terminateChat(String username, Long recordId); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java new file mode 100644 index 0000000000..141aabf8c2 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/AsyncConsumerAndProducerPair.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.context.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import java.util.function.Consumer; + +@AllArgsConstructor +@Data +public class AsyncConsumerAndProducerPair { + /** + * 消费者 + */ + Consumer consumer; + /** + * 生产者 + */ + StreamingResponseBody producer; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerHandler.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerHandler.java new file mode 100644 index 0000000000..1ed5b00310 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerHandler.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.helpers.MessageFormatter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.concurrent.CancellationException; + +/** + * AI回答处理器 + */ +@Slf4j +@Service +public class AIAnswerHandler { + + private final AIChatHistoryService aiChatHistoryService; + + @Autowired + public AIAnswerHandler(AIChatHistoryService aiChatHistoryService) { + this.aiChatHistoryService = aiChatHistoryService; + } + + /** + * 处理完整的AI回答 + * + * @param recordId 对话记录ID + * @param content AI回答内容 + * @param throwable AI回答过程产生的异常 + */ + public void handleAIAnswer(Long recordId, String content, Throwable throwable) { + AIAnswer aiAnswer; + if (throwable == null) { + // 1.对话正常完成 + aiAnswer = AIAnswer.successAnswer(content); + int affectedRow = aiChatHistoryService.finishAIAnswer(recordId, aiAnswer); + log.info( + "AIAnswer finished, recordId={}, length={}, affectedRow={}", + recordId, + content.length(), + affectedRow + ); + } else if (throwable instanceof CancellationException) { + // 2.对话被主动终止 + int affectedRow = aiChatHistoryService.terminateAIAnswer(recordId); + log.info( + "AIAnswer terminated, recordId={}, affectedRow={}", + recordId, + affectedRow + ); + } else { + // 3.对话异常 + aiAnswer = AIAnswer.failAnswer(content); + int affectedRow = aiChatHistoryService.finishAIAnswer(recordId, aiAnswer); + String message = MessageFormatter.format( + "AIAnswer finished(fail), recordId={}, length={}, affectedRow={}", + new Object[]{ + recordId, + content.length(), + affectedRow + } + ).getMessage(); + log.warn(message, throwable); + } + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java new file mode 100644 index 0000000000..0e6dec5c36 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java @@ -0,0 +1,98 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.service.ai.impl; + +import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; +import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndProducerPair; +import com.tencent.bk.job.analysis.service.ai.context.model.MessagePartEvent; +import com.tencent.bk.job.common.model.Response; +import com.tencent.bk.job.common.util.json.JsonUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +/** + * AI回答流式响应同步器 + * 使用阻塞队列构建异步消费者与生产者组合,消费者读取到数据后立即提供给生产者进行输出 + */ +@Slf4j +public class AIAnswerStreamSynchronizer { + + private final AtomicBoolean isFinished = new AtomicBoolean(false); + private final LinkedBlockingQueue messageQueue; + + /** + * 构造函数 + * + * @param capacity 可缓存于内存的消息事件量 + */ + public AIAnswerStreamSynchronizer(int capacity) { + messageQueue = new LinkedBlockingQueue<>(capacity); + } + + /** + * 构建异步消费者与生产者组合 + * + * @return 异步消费者与生产者组合 + */ + public AsyncConsumerAndProducerPair buildAsyncConsumerAndProducerPair() { + StreamingResponseBody streamingResponseBody = outputStream -> { + while (!isFinished.get()) { + try { + MessagePartEvent event = messageQueue.take(); + if (event.isEnd()) { + break; + } + String partMessage = event.getMessagePart(); + Response respBody = Response.buildSuccessResp(AIAnswer.successAnswer(partMessage)); + String message = JsonUtils.toJson(respBody) + "\n"; + outputStream.write(message.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } catch (InterruptedException e) { + log.debug("Interrupted when take message from queue", e); + } catch (IOException e) { + log.error("Write resp to output stream failed", e); + } + } + outputStream.close(); + }; + Consumer partialRespConsumer = + messagePart -> messageQueue.offer(MessagePartEvent.normalEvent(messagePart)); + return new AsyncConsumerAndProducerPair(partialRespConsumer, streamingResponseBody); + } + + /** + * 触发结束事件,消费者读取完数据后,触发生产者停止生产并清理 + */ + public void triggerEndEvent() { + isFinished.set(true); + messageQueue.offer(MessagePartEvent.endEvent()); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index adc852ef62..4b7810adb2 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -97,6 +97,11 @@ public int finishAIAnswer(Long historyId, AIAnswer aiAnswer) { ); } + @Override + public int terminateAIAnswer(Long historyId) { + return aiChatHistoryDAO.updateChatHistoryStatus(historyId, AIChatStatusEnum.TERMINATED.getStatus()); + } + /** * 从DB获取最近的聊天记录列表,按起始时间升序排列 * diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 03659e44c7..079ca51488 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -26,41 +26,38 @@ import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; -import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.ChatService; -import com.tencent.bk.job.analysis.service.ai.context.model.MessagePartEvent; +import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndProducerPair; import com.tencent.bk.job.common.constant.ErrorCode; import com.tencent.bk.job.common.exception.NotFoundException; -import com.tencent.bk.job.common.model.Response; -import com.tencent.bk.job.common.util.json.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; -import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; +import java.util.concurrent.ConcurrentHashMap; @Slf4j @Service public class ChatServiceImpl implements ChatService { private final AIChatHistoryService aiChatHistoryService; + private final AIAnswerHandler aiAnswerHandler; private final AIService aiService; + private final ConcurrentHashMap> futureMap = new ConcurrentHashMap<>(); @Autowired public ChatServiceImpl(AIChatHistoryService aiChatHistoryService, + AIAnswerHandler aiAnswerHandler, AIService aiService) { this.aiChatHistoryService = aiChatHistoryService; + this.aiAnswerHandler = aiAnswerHandler; this.aiService = aiService; } @@ -96,66 +93,54 @@ public StreamingResponseBody generateChatStream(String username, Long recordId) ); chatHistoryDTOList.sort(Comparator.comparing(AIChatHistoryDTO::getStartTime)); // 2.查出指定的聊天记录 - AIChatHistoryDTO currentChatHistoryDTO = aiChatHistoryService.getChatHistory(username, recordId); - if (currentChatHistoryDTO == null) { - throw new NotFoundException( - ErrorCode.AI_CHAT_HISTORY_NOT_FOUND_BY_ID, - new String[]{String.valueOf(recordId)} - ); - } + AIChatHistoryDTO currentChatHistoryDTO = getChatHistory(username, recordId); int affectedNum = aiChatHistoryService.setAIAnswerReplying(recordId); if (affectedNum == 0) { log.info("AIAnswer is already replying, re-reply, recordId={}", recordId); } - // 3.获取AI回答放入阻塞队列 - LinkedBlockingQueue messageQueue = new LinkedBlockingQueue<>(10000); - AtomicBoolean isFinished = new AtomicBoolean(false); - StreamingResponseBody streamingResponseBody = outputStream -> { - while (!isFinished.get()) { - try { - MessagePartEvent event = messageQueue.take(); - if (event.isEnd()) { - break; - } - String partMessage = event.getMessagePart(); - Response respBody = Response.buildSuccessResp(AIAnswer.successAnswer(partMessage)); - String message = JsonUtils.toJson(respBody) + "\n"; - outputStream.write(message.getBytes(StandardCharsets.UTF_8)); - outputStream.flush(); - } catch (InterruptedException e) { - log.debug("Interrupted when take message from queue", e); - } catch (IOException e) { - log.error("Write resp to output stream failed", e); - } - } - outputStream.close(); - }; - Consumer partialRespConsumer = - messagePart -> messageQueue.offer(MessagePartEvent.normalEvent(messagePart)); + // 3.将AI回答流数据与接口输出流进行同步对接 + int inMemoryMessageMaxNum = 10000; + AIAnswerStreamSynchronizer aiAnswerStreamSynchronizer = new AIAnswerStreamSynchronizer(inMemoryMessageMaxNum); + AsyncConsumerAndProducerPair consumerAndProducerPair = + aiAnswerStreamSynchronizer.buildAsyncConsumerAndProducerPair(); CompletableFuture future = aiService.getAIAnswerStream( chatHistoryDTOList, currentChatHistoryDTO.getAiInput(), - partialRespConsumer + consumerAndProducerPair.getConsumer() ); future.whenComplete((content, throwable) -> { - isFinished.set(true); - messageQueue.offer(MessagePartEvent.endEvent()); - // 4.将AI回答写入DB - AIAnswer aiAnswer; - if (throwable == null) { - aiAnswer = AIAnswer.successAnswer(content); - } else { - aiAnswer = AIAnswer.failAnswer(throwable.getMessage()); - } - int affectedRow = aiChatHistoryService.finishAIAnswer(recordId, aiAnswer); - log.info( - "AIAnswer finished, recordId={}, length={}, affectedRow={}", - recordId, - content.length(), - affectedRow - ); + aiAnswerStreamSynchronizer.triggerEndEvent(); + // 4.处理AI回复内容 + aiAnswerHandler.handleAIAnswer(recordId, content, throwable); + futureMap.remove(recordId); }); - return streamingResponseBody; + futureMap.put(recordId, future); + return consumerAndProducerPair.getProducer(); + } + + private AIChatHistoryDTO getChatHistory(String username, Long recordId) { + AIChatHistoryDTO chatHistoryDTO = aiChatHistoryService.getChatHistory(username, recordId); + if (chatHistoryDTO == null) { + throw new NotFoundException( + ErrorCode.AI_CHAT_HISTORY_NOT_FOUND_BY_ID, + new String[]{String.valueOf(recordId)} + ); + } + return chatHistoryDTO; } + public boolean terminateChat(String username, Long recordId) { + AIChatHistoryDTO chatHistoryDTO = getChatHistory(username, recordId); + if (!chatHistoryDTO.isInitOrReplying()) { + log.info("Cannot terminate chat, chat is already finished or canceled, recordId={}", recordId); + return false; + } + CompletableFuture future = futureMap.get(recordId); + if (future == null) { + log.info("Cannot find future for recordId={}, chat maybe finished or canceled just now", recordId); + return false; + } + future.cancel(true); + return true; + } } From e2174c2145299326ee291dc1307959a94bdfeada Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 22 Aug 2024 22:45:22 +0800 Subject: [PATCH 042/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.终止对话多实例逻辑实现。 --- .../api/web/impl/WebAIResourceImpl.java | 2 +- .../config/JobFunctionConfiguration.java | 52 ++++++++++++ .../analysis/consts/AIChatOperationEnum.java | 54 ++++++++++++ .../AIChatOperationEventListener.java | 75 +++++++++++++++++ .../listener/event/AIChatOperationEvent.java | 84 +++++++++++++++++++ .../mq/AIChatOperationEventDispatcher.java | 55 ++++++++++++ .../job/analysis/service/ai/ChatService.java | 11 ++- .../service/ai/impl/ChatServiceImpl.java | 26 ++++-- .../templates/job-analysis/configmap.yaml | 34 ++++++++ 9 files changed, 384 insertions(+), 9 deletions(-) create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/JobFunctionConfiguration.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatOperationEnum.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/AIChatOperationEventListener.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/event/AIChatOperationEvent.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/mq/AIChatOperationEventDispatcher.java diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 0940bed032..5dced17fcc 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -177,7 +177,7 @@ public Response terminateChat(String username, String scopeType, String scopeId, Long recordId) { - boolean result = chatService.terminateChat(username, recordId); + boolean result = chatService.triggerTerminateChat(username, recordId); return Response.buildSuccessResp(result); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/JobFunctionConfiguration.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/JobFunctionConfiguration.java new file mode 100644 index 0000000000..28d6e9963d --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/JobFunctionConfiguration.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.config; + +import com.tencent.bk.job.analysis.listener.AIChatOperationEventListener; +import com.tencent.bk.job.analysis.listener.event.AIChatOperationEvent; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.function.Consumer; + +/** + * spring cloud function 定义 + *

+ * 注意:方法名与配置文件中的spring.cloud.stream.function.definition对应,修改需要注意!!! + */ +@Configuration(value = "jobAnalysisFunctionConfig") +@Slf4j +public class JobFunctionConfiguration { + @Bean + public Consumer handleAIChatOperationEvent( + @Autowired AIChatOperationEventListener aiChatOperationEventListener + ) { + log.info("Init handleAIChatOperationEvent consumer"); + return aiChatOperationEventListener::handleEvent; + } + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatOperationEnum.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatOperationEnum.java new file mode 100644 index 0000000000..cf7e32cc5b --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatOperationEnum.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.consts; + +import lombok.Getter; + +/** + * AI对话相关操作 + */ +@Getter +public enum AIChatOperationEnum { + /** + * 终止对话 + */ + TERMINATE_CHAT(1); + + private final int value; + + AIChatOperationEnum(int val) { + this.value = val; + } + + public static AIChatOperationEnum valueOf(int value) { + for (AIChatOperationEnum aiChatOperationEnum : values()) { + if (aiChatOperationEnum.getValue() == value) { + return aiChatOperationEnum; + } + } + throw new IllegalArgumentException("No AIChatOperationEnum constant: " + value); + } + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/AIChatOperationEventListener.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/AIChatOperationEventListener.java new file mode 100644 index 0000000000..37ff05f9f4 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/AIChatOperationEventListener.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.listener; + +import com.tencent.bk.job.analysis.consts.AIChatOperationEnum; +import com.tencent.bk.job.analysis.listener.event.AIChatOperationEvent; +import com.tencent.bk.job.analysis.service.ai.ChatService; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.helpers.MessageFormatter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * AI对话事件处理 + */ +@Component("aiChatOperationEvent") +@Slf4j +public class AIChatOperationEventListener { + + private final ChatService chatService; + + @Autowired + public AIChatOperationEventListener(ChatService chatService) { + this.chatService = chatService; + } + + /** + * 处理AI对话操作事件 + * + * @param event AI对话操作事件 + */ + public void handleEvent(AIChatOperationEvent event) { + log.info("Handle aiChatOperation event, event: {}, duration: {}ms", event, event.duration()); + String username = event.getUsername(); + long recordId = event.getRecordId(); + AIChatOperationEnum action = AIChatOperationEnum.valueOf(event.getAction()); + try { + if (action == AIChatOperationEnum.TERMINATE_CHAT) { + chatService.terminateChat(username, recordId); + } else { + log.error("Invalid event action: {}", action); + } + } catch (Throwable e) { + String errorMsg = MessageFormatter.format( + "Handle aiChatOperation event error, username={}, recordId={}", + username, + recordId + ).getMessage(); + log.error(errorMsg, e); + } + } + +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/event/AIChatOperationEvent.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/event/AIChatOperationEvent.java new file mode 100644 index 0000000000..ec0565cf89 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/listener/event/AIChatOperationEvent.java @@ -0,0 +1,84 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.listener.event; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.tencent.bk.job.analysis.consts.AIChatOperationEnum; +import com.tencent.bk.job.common.event.Event; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.StringJoiner; + +/** + * AI对话操作事件 + */ +@Getter +@Setter +@NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) +public class AIChatOperationEvent extends Event { + /** + * AI对话相关操作 + * + * @see AIChatOperationEnum + */ + private int action; + /** + * 操作用户 + */ + private String username; + /** + * AI对话记录ID + */ + private Long recordId; + + /** + * 构造终止AI对话事件 + * + * @param recordId 对话记录ID + * @return 终止AI对话事件 + */ + public static AIChatOperationEvent terminateChat(String username, long recordId) { + AIChatOperationEvent event = new AIChatOperationEvent(); + event.setAction(AIChatOperationEnum.TERMINATE_CHAT.getValue()); + event.setUsername(username); + event.setRecordId(recordId); + event.setTime(LocalDateTime.now()); + return event; + } + + @Override + public String toString() { + return new StringJoiner(", ", AIChatOperationEvent.class.getSimpleName() + "[", "]") + .add("action=" + action) + .add("username=" + username) + .add("recordId=" + recordId) + .add("time=" + time) + .toString(); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/mq/AIChatOperationEventDispatcher.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/mq/AIChatOperationEventDispatcher.java new file mode 100644 index 0000000000..7ab7e6ba38 --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/mq/AIChatOperationEventDispatcher.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.mq; + +import com.tencent.bk.job.analysis.listener.event.AIChatOperationEvent; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.stream.function.StreamBridge; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class AIChatOperationEventDispatcher { + + private final StreamBridge streamBridge; + + @Autowired + public AIChatOperationEventDispatcher(StreamBridge streamBridge) { + this.streamBridge = streamBridge; + } + + /** + * 广播AI对话操作事件 + * + * @param event AI对话操作事件 + */ + public void broadCastAIChatOperationEvent(AIChatOperationEvent event) { + log.info("Begin to broadcast aiChatOperation event: {}", event); + String aiChatOperationFanout = "aiChatOperationFanout-out-0"; + streamBridge.send(aiChatOperationFanout, event); + log.info("Broadcast aiChatOperation event successfully, event: {}", event); + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java index 99ab42f3f8..0f3150e903 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/ChatService.java @@ -61,11 +61,20 @@ public interface ChatService { StreamingResponseBody generateChatStream(String username, Long recordId); /** - * 终止聊天 + * 终止当前实例中的对话 * * @param username 用户名 * @param recordId 对话记录ID * @return 是否终止成功 */ boolean terminateChat(String username, Long recordId); + + /** + * 触发终止对话 + * + * @param username 用户名 + * @param recordId 对话记录ID + * @return 是否终止成功 + */ + boolean triggerTerminateChat(String username, Long recordId); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 079ca51488..0a1d5c5cf5 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -25,8 +25,10 @@ package com.tencent.bk.job.analysis.service.ai.impl; import com.tencent.bk.job.analysis.consts.AIChatStatusEnum; +import com.tencent.bk.job.analysis.listener.event.AIChatOperationEvent; import com.tencent.bk.job.analysis.model.dto.AIChatHistoryDTO; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; +import com.tencent.bk.job.analysis.mq.AIChatOperationEventDispatcher; import com.tencent.bk.job.analysis.service.ai.AIChatHistoryService; import com.tencent.bk.job.analysis.service.ai.AIService; import com.tencent.bk.job.analysis.service.ai.ChatService; @@ -50,15 +52,17 @@ public class ChatServiceImpl implements ChatService { private final AIChatHistoryService aiChatHistoryService; private final AIAnswerHandler aiAnswerHandler; private final AIService aiService; + private final AIChatOperationEventDispatcher aiChatOperationEventDispatcher; private final ConcurrentHashMap> futureMap = new ConcurrentHashMap<>(); @Autowired public ChatServiceImpl(AIChatHistoryService aiChatHistoryService, AIAnswerHandler aiAnswerHandler, - AIService aiService) { + AIService aiService, AIChatOperationEventDispatcher aiChatOperationEventDispatcher) { this.aiChatHistoryService = aiChatHistoryService; this.aiAnswerHandler = aiAnswerHandler; this.aiService = aiService; + this.aiChatOperationEventDispatcher = aiChatOperationEventDispatcher; } @Override @@ -130,17 +134,25 @@ private AIChatHistoryDTO getChatHistory(String username, Long recordId) { } public boolean terminateChat(String username, Long recordId) { - AIChatHistoryDTO chatHistoryDTO = getChatHistory(username, recordId); - if (!chatHistoryDTO.isInitOrReplying()) { - log.info("Cannot terminate chat, chat is already finished or canceled, recordId={}", recordId); - return false; - } CompletableFuture future = futureMap.get(recordId); if (future == null) { log.info("Cannot find future for recordId={}, chat maybe finished or canceled just now", recordId); return false; } - future.cancel(true); + boolean result = future.cancel(true); + log.info("Terminate chat, username={}, recordId={}, result={}", username, recordId, result); + return result; + } + + public boolean triggerTerminateChat(String username, Long recordId) { + AIChatHistoryDTO chatHistoryDTO = getChatHistory(username, recordId); + if (!chatHistoryDTO.isInitOrReplying()) { + log.info("Cannot terminate chat, chat is already finished or canceled, recordId={}", recordId); + return false; + } + aiChatOperationEventDispatcher.broadCastAIChatOperationEvent( + AIChatOperationEvent.terminateChat(username, recordId) + ); return true; } } diff --git a/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml b/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml index 57bdfb6529..e777d7884d 100644 --- a/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml +++ b/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml @@ -15,6 +15,8 @@ data: spring: cloud: stream: + function: + definition: handleAIChatOperationEvent defaultBinder: jobCommon binders: jobCommon: @@ -31,6 +33,38 @@ data: password: ${rabbitmq-password} {{- end }} virtual-host: {{ include "job.rabbitmq.vhost" . }} + jobAnalysis: + type: rabbit + environment: + spring: + rabbitmq: + host: {{ include "job.rabbitmq.host" . }} + port: {{ include "job.rabbitmq.port" . }} + username: {{ include "job.rabbitmq.username" . }} + {{ if .Values.externalRabbitMQ.existingPasswordSecret }} + password: {{ .Values.externalRabbitMQ.existingPasswordKey | default "rabbitmq-password" | printf "${%s}" }} + {{- else -}} + password: ${rabbitmq-password} + {{- end }} + virtual-host: {{ include "job.rabbitmq.vhost" . }} + bindings: + handleAIChatOperationEvent-in-0: + destination: analysis.aiChatOperation.fanout + binder: jobAnalysis + consumer: + concurrency: 3 + aiChatOperationFanout-out-0: + destination: analysis.aiChatOperation.fanout + binder: jobAnalysis + rabbit: + bindings: + handleAIChatOperationEvent-in-0: + consumer: + maxConcurrency: 3 + exchangeType: fanout + aiChatOperationFanout-out-0: + producer: + exchangeType: fanout datasource: job-analysis: driver-class-name: {{ include "job.jdbcMysqlDriverClass" . }} From e3ca9c640dfb51748809fb64b95bcd5226b96c50 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Tue, 3 Sep 2024 21:36:26 +0800 Subject: [PATCH 043/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持自动清理AI聊天记录。 --- .../redis/util/DistributedUniqueTask.java | 80 ++++++++ .../bk/job/analysis/config/AIProperties.java | 21 +- .../bk/job/analysis/dao/AIChatHistoryDAO.java | 43 ++++ .../dao/impl/AIChatHistoryDAOImpl.java | 67 +++++++ .../bk/job/analysis/task/ScheduledTasks.java | 18 +- .../task/ai/AIChatHistoryCleanTask.java | 185 ++++++++++++++++++ .../kubernetes/charts/bk-job/VALUES_LOG.md | 23 ++- .../bk-job/templates/configmap-common.yaml | 7 - .../templates/job-analysis/configmap.yaml | 2 + .../kubernetes/charts/bk-job/values.yaml | 22 ++- 10 files changed, 443 insertions(+), 25 deletions(-) create mode 100644 src/backend/commons/common-redis/src/main/java/com/tencent/bk/job/common/redis/util/DistributedUniqueTask.java create mode 100644 src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java diff --git a/src/backend/commons/common-redis/src/main/java/com/tencent/bk/job/common/redis/util/DistributedUniqueTask.java b/src/backend/commons/common-redis/src/main/java/com/tencent/bk/job/common/redis/util/DistributedUniqueTask.java new file mode 100644 index 0000000000..f1860a7d98 --- /dev/null +++ b/src/backend/commons/common-redis/src/main/java/com/tencent/bk/job/common/redis/util/DistributedUniqueTask.java @@ -0,0 +1,80 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.common.redis.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.concurrent.Callable; + +/** + * 分布式唯一任务:在多个实例同时触发的情况下,只会有一个实例执行,唯一性通过同一个Redis Key保证 + */ +@Slf4j +public class DistributedUniqueTask { + + private final RedisTemplate redisTemplate; + private final String name; + private final String redisKey; + private final String redisValue; + private final Callable task; + + public DistributedUniqueTask(RedisTemplate redisTemplate, + String name, + String redisKey, + String redisValue, + Callable task) { + this.redisTemplate = redisTemplate; + this.name = name; + this.redisKey = redisKey; + this.redisValue = redisValue; + this.task = task; + } + + public V execute() throws Exception { + HeartBeatRedisLockConfig config = HeartBeatRedisLockConfig.getDefault(); + config.setHeartBeatThreadName(name + "-RedisKeyHeartBeatThread"); + HeartBeatRedisLock lock = new HeartBeatRedisLock( + redisTemplate, + redisKey, + redisValue, + config + ); + LockResult lockResult = lock.lock(); + if (!lockResult.isLockGotten()) { + log.info( + "lock {} gotten by another instance: {}, return", + redisKey, + lockResult.getLockValue() + ); + return null; + } + try { + return task.call(); + } finally { + lockResult.tryToRelease(); + } + } +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java index cf11c28ab9..8ab550a1b6 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/config/AIProperties.java @@ -44,7 +44,12 @@ public class AIProperties { /** * 错误日志分析相关配置 */ - private AnalyzeErrorLogConfig analyzeErrorLog; + private AnalyzeErrorLogConfig analyzeErrorLog = new AnalyzeErrorLogConfig(); + + /** + * 聊天记录相关配置 + */ + private ChatHistoryConfig chatHistory = new ChatHistoryConfig(); @Getter @Setter @@ -59,4 +64,18 @@ public Long getLogMaxLengthBytes() { return FileSizeUtil.parseFileSizeBytes(logMaxLength); } } + + @Getter + @Setter + @ToString + public static class ChatHistoryConfig { + /** + * 最大保留天数 + */ + private Integer maxKeepDays = 31; + /** + * 单个用户最大保留的聊天记录数量 + */ + private Integer maxHistoryPerUser = 1000; + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java index b7346acc2e..232a5e745c 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/AIChatHistoryDAO.java @@ -101,4 +101,47 @@ int updateChatHistoryStatusAndAIAnswer(Long historyId, * @return 删除的记录条数 */ int softDeleteChatHistory(String username, Integer limit); + + /** + * 获取指定时间(包含)之前的记录最大id + * + * @param maxStartTime 最大开始时间 + * @return 记录最大id + */ + Long getMaxIdBeforeOrEqualStartTime(long maxStartTime); + + /** + * 硬删除id小于指定id的聊天记录(按id从小到大的顺序删除) + * + * @param maxId 最大id + * @param limit 最大删除数量 + * @return 删除的记录条数 + */ + int deleteChatHistory(long maxId, int limit); + + /** + * 硬删除某个用户的id小于指定id的聊天记录(按id从小到大的顺序删除) + * + * @param username 用户名 + * @param maxId 最大id + * @param limit 最大删除数量 + * @return 删除的记录条数 + */ + int deleteChatHistory(String username, long maxId, int limit); + + /** + * 获取所有有聊天记录(未删除)的用户 + * + * @return 所有有聊天记录(未删除)的用户 + */ + List listAllUserOfChatHistory(); + + /** + * 获取offset后第一条记录的Id + * + * @param username 用户名 + * @param offset 偏移量 + * @return 第一条记录的Id + */ + Long getFirstIdAfterOffset(String username, int offset); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java index d3da4340a1..e85f322156 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/dao/impl/AIChatHistoryDAOImpl.java @@ -30,12 +30,14 @@ import com.tencent.bk.job.analysis.model.tables.AiChatHistory; import com.tencent.bk.job.common.mysql.dao.BaseDAOImpl; import com.tencent.bk.job.common.mysql.util.JooqDataTypeUtil; +import io.micrometer.core.instrument.util.StringUtils; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.jooq.Condition; import org.jooq.DSLContext; import org.jooq.Record; import org.jooq.conf.ParamType; +import org.jooq.impl.DSL; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Repository; @@ -189,6 +191,71 @@ public int softDeleteChatHistory(String username, Integer limit) { .execute(); } + @Override + public Long getMaxIdBeforeOrEqualStartTime(long maxStartTime) { + Long existStartTime = dslContext.select(defaultTable.START_TIME) + .from(defaultTable) + .where(defaultTable.START_TIME.lessOrEqual(JooqDataTypeUtil.buildULong(maxStartTime))) + .orderBy(defaultTable.START_TIME.desc()) + .limit(1) + .fetchOneInto(Long.class); + if (existStartTime == null) { + return null; + } + Long maxId = dslContext.select(DSL.max(defaultTable.ID)) + .from(defaultTable) + .where(defaultTable.START_TIME.equal(JooqDataTypeUtil.buildULong(existStartTime))) + .fetchOneInto(Long.class); + if (log.isDebugEnabled()) { + log.debug("existStartTime={},maxId={}", existStartTime, maxId); + } + return maxId; + } + + @Override + public int deleteChatHistory(long maxId, int limit) { + return deleteChatHistory(null, maxId, limit); + } + + @Override + public int deleteChatHistory(String username, long maxId, int limit) { + List conditions = new ArrayList<>(); + if (StringUtils.isNotBlank(username)) { + conditions.add(defaultTable.USERNAME.eq(username)); + } + conditions.add(defaultTable.ID.lessOrEqual(maxId)); + return dslContext.deleteFrom(defaultTable) + .where(conditions) + .orderBy(defaultTable.ID) + .limit(limit) + .execute(); + } + + @Override + public List listAllUserOfChatHistory() { + List conditions = new ArrayList<>(); + conditions.add(defaultTable.IS_DELETED.eq(JooqDataTypeUtil.buildUByte(0))); + return dslContext.selectDistinct(defaultTable.USERNAME) + .from(defaultTable) + .where(conditions) + .fetch() + .into(String.class); + } + + @Override + public Long getFirstIdAfterOffset(String username, int offset) { + List conditions = new ArrayList<>(); + conditions.add(defaultTable.USERNAME.eq(username)); + conditions.add(defaultTable.IS_DELETED.eq(JooqDataTypeUtil.buildUByte(0))); + return dslContext.select(defaultTable.ID) + .from(defaultTable) + .where(conditions) + .orderBy(defaultTable.ID.desc()) + .offset(offset) + .limit(1) + .fetchOneInto(Long.class); + } + private List listByConditions(Collection conditions, Integer start, Integer length) { diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ScheduledTasks.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ScheduledTasks.java index fdbdd1b7a6..8925df2ec3 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ScheduledTasks.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ScheduledTasks.java @@ -24,6 +24,7 @@ package com.tencent.bk.job.analysis.task; +import com.tencent.bk.job.analysis.task.ai.AIChatHistoryCleanTask; import com.tencent.bk.job.analysis.task.analysis.AnalysisTaskScheduler; import com.tencent.bk.job.analysis.task.statistics.StatisticsTaskScheduler; import org.slf4j.Logger; @@ -40,12 +41,15 @@ public class ScheduledTasks { private static final Logger logger = LoggerFactory.getLogger(ScheduledTasks.class); private final AnalysisTaskScheduler analysisTaskScheduler; private final StatisticsTaskScheduler statisticsTaskScheduler; + private final AIChatHistoryCleanTask aiChatHistoryCleanTask; @Autowired public ScheduledTasks(AnalysisTaskScheduler analysisTaskScheduler, - StatisticsTaskScheduler statisticsTaskScheduler) { + StatisticsTaskScheduler statisticsTaskScheduler, + AIChatHistoryCleanTask aiChatHistoryCleanTask) { this.analysisTaskScheduler = analysisTaskScheduler; this.statisticsTaskScheduler = statisticsTaskScheduler; + this.aiChatHistoryCleanTask = aiChatHistoryCleanTask; } /** @@ -77,4 +81,16 @@ public void analysisTaskSchedulerTask() { logger.error("analysisTaskSchedulerTask fail", e); } } + + /** + * 定时清理用户产生的AI聊天记录,1h一次 + */ + @Scheduled(cron = "0 20 * * * *") + public void aiChatHistoryCleanTask() { + try { + aiChatHistoryCleanTask.execute(); + } catch (Exception e) { + logger.error("aiChatHistoryCleanTask fail", e); + } + } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java new file mode 100644 index 0000000000..f6d162d82e --- /dev/null +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/task/ai/AIChatHistoryCleanTask.java @@ -0,0 +1,185 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.task.ai; + + +import com.tencent.bk.job.analysis.config.AIProperties; +import com.tencent.bk.job.analysis.dao.AIChatHistoryDAO; +import com.tencent.bk.job.common.redis.util.DistributedUniqueTask; +import com.tencent.bk.job.common.util.ThreadUtils; +import com.tencent.bk.job.common.util.TimeUtil; +import com.tencent.bk.job.common.util.ip.IpUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.StopWatch; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +/** + * 清理AI对话记录的任务 + */ +@Slf4j +@Component +public class AIChatHistoryCleanTask { + + private static final String machineIp = IpUtils.getFirstMachineIP(); + private static final String REDIS_KEY_AI_CHAT_HISTORY_CLEAN_TASK_RUNNING_MACHINE = + "aiChatHistoryCleanTask-running-machine"; + private static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; + private final RedisTemplate redisTemplate; + private final AIProperties aiProperties; + private final AIChatHistoryDAO aiChatHistoryDAO; + + @Autowired + public AIChatHistoryCleanTask(RedisTemplate redisTemplate, + AIProperties aiProperties, + AIChatHistoryDAO aiChatHistoryDAO) { + this.redisTemplate = redisTemplate; + this.aiProperties = aiProperties; + this.aiChatHistoryDAO = aiChatHistoryDAO; + } + + public void execute() { + log.info("AIChatHistoryCleanTask start"); + StopWatch watch = new StopWatch(); + Boolean successExecuted = false; + try { + // 分布式唯一性保证 + successExecuted = new DistributedUniqueTask<>( + redisTemplate, + AIChatHistoryCleanTask.class.getSimpleName(), + REDIS_KEY_AI_CHAT_HISTORY_CLEAN_TASK_RUNNING_MACHINE, + machineIp, + () -> { + doExecute(watch); + return true; + } + ).execute(); + } catch (Exception e) { + log.error("AIChatHistoryCleanTask failed", e); + } finally { + if (watch.isRunning()) { + watch.stop(); + } + if (successExecuted) { + log.info("AIChatHistoryCleanTask finished, timeConsuming={}", watch.prettyPrint()); + } + } + } + + public void doExecute(StopWatch watch) { + watch.start("cleanChatHistoryByMaxKeepDays"); + Integer maxKeepDays = aiProperties.getChatHistory().getMaxKeepDays(); + cleanChatHistoryByMaxKeepDays(maxKeepDays); + watch.stop(); + watch.start("cleanChatHistoryByMaxHistoryPerUser"); + Integer maxHistoryPerUser = aiProperties.getChatHistory().getMaxHistoryPerUser(); + cleanChatHistoryByMaxHistoryPerUser(maxHistoryPerUser); + watch.stop(); + } + + /** + * 根据最大保留天数清理聊天记录 + * + * @param maxKeepDays 最大保留天数 + */ + private void cleanChatHistoryByMaxKeepDays(Integer maxKeepDays) { + LocalDateTime currentDateTime = LocalDateTime.now(); + LocalDateTime lastKeepDate = currentDateTime.minusDays(maxKeepDays); + LocalDateTime lastKeepDateStartTime = TimeUtil.getDayStartTime(lastKeepDate); + long maxStartTimeMills = lastKeepDateStartTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + log.info( + "Begin to delete ai chat history before {}, maxStartTimeMills={}", + TimeUtil.getTimeStr(lastKeepDateStartTime, TIME_FORMAT), + maxStartTimeMills + ); + Long maxId = aiChatHistoryDAO.getMaxIdBeforeOrEqualStartTime(maxStartTimeMills); + if (maxId == null) { + log.info("No history need to delete"); + return; + } + int batchSize = 10000; + int totalDeletedNum = 0; + int deletedNum; + do { + deletedNum = aiChatHistoryDAO.deleteChatHistory(maxId, batchSize); + totalDeletedNum += deletedNum; + if (deletedNum > 0) { + ThreadUtils.sleep(1000); + } + } while (deletedNum > 0); + log.info( + "Finish to cleanChatHistoryByMaxKeepDays({}), totalDeletedNum={}, maxId={}", + maxKeepDays, + totalDeletedNum, + maxId + ); + } + + /** + * 根据每个用户最大聊天记录数清理聊天记录 + * + * @param maxHistoryPerUser 每个用户最大聊天记录数 + */ + private void cleanChatHistoryByMaxHistoryPerUser(Integer maxHistoryPerUser) { + // 1.查出所有用户 + List userList = aiChatHistoryDAO.listAllUserOfChatHistory(); + // 2.清理每个用户的聊天历史记录 + for (String username : userList) { + Long maxId = aiChatHistoryDAO.getFirstIdAfterOffset(username, maxHistoryPerUser); + if (maxId == null) { + continue; + } + int deletedNum = cleanChatHistoryForUser(username, maxId); + log.info("Finish to cleanChatHistoryForUser({}), maxId={}, deletedNum={}", username, maxId, deletedNum); + } + log.info("cleanChatHistoryByMaxHistoryPerUser finished"); + } + + /** + * 清理某个用户的聊天记录 + * + * @param username 用户名 + * @param maxId 最大记录id(包含) + * @return 删除的记录条数 + */ + private int cleanChatHistoryForUser(String username, Long maxId) { + int batchSize = 10000; + int totalDeletedNum = 0; + int deletedNum; + do { + deletedNum = aiChatHistoryDAO.deleteChatHistory(username, maxId, batchSize); + totalDeletedNum += deletedNum; + if (deletedNum > 0) { + ThreadUtils.sleep(100); + } + } while (deletedNum > 0); + return totalDeletedNum; + } +} diff --git a/support-files/kubernetes/charts/bk-job/VALUES_LOG.md b/support-files/kubernetes/charts/bk-job/VALUES_LOG.md index 34ee7724c7..69b2ed76b9 100644 --- a/support-files/kubernetes/charts/bk-job/VALUES_LOG.md +++ b/support-files/kubernetes/charts/bk-job/VALUES_LOG.md @@ -4,14 +4,21 @@ 1. 增加AI相关配置 ```yaml -# AI相关配置 -ai: - # 是否开启AI功能,默认不开启 - enabled: false - # AI分析错误日志功能相关配置 - analyzeErrorLog: - # 支持分析的错误日志最大长度,单位支持B、KB、MB、GB、TB、PB,默认5MB - logMaxLength: "5MB" +analysisConfig: + # AI相关配置 + ai : + # 是否开启AI功能,默认不开启 + enabled : false + # AI分析错误日志功能相关配置 + analyzeErrorLog : + # 支持分析的错误日志最大长度,单位支持B、KB、MB、GB、TB、PB,默认5MB + logMaxLength : "5MB" + # AI聊天记录相关配置 + chatHistory : + # 最大保留天数,默认31天 + maxKeepDays : 31 + # 单个用户最大保留的聊天记录数量,默认1000条 + maxHistoryPerUser : 1000 ``` ## 0.6.5 diff --git a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml index c8621c75f2..ccc8325873 100644 --- a/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml +++ b/support-files/kubernetes/charts/bk-job/templates/configmap-common.yaml @@ -176,10 +176,3 @@ data: enabled: {{ .Values.jvmDiagnosticFile.clearByLastModifyTime.enabled }} # JVM诊断文件保留的小时数,默认168小时(7天) keep-hours: {{ .Values.jvmDiagnosticFile.clearByLastModifyTime.keepHours }} - ai: - # 是否开启AI功能,默认开启 - enabled: {{ .Values.ai.enabled }} - # AI分析错误日志功能相关配置 - analyze-error-log: - # 支持分析的错误日志最大长度 - log-max-length: {{ .Values.ai.analyzeErrorLog.logMaxLength }} diff --git a/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml b/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml index e777d7884d..0ec9bef709 100644 --- a/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml +++ b/support-files/kubernetes/charts/bk-job/templates/job-analysis/configmap.yaml @@ -99,4 +99,6 @@ data: {{- toYaml .Values.analysisConfig.feign | nindent 6 }} server: port: {{ .Values.analysisConfig.containerPort }} + ai: + {{- toYaml .Values.analysisConfig.ai | nindent 6 }} {{- end }} diff --git a/support-files/kubernetes/charts/bk-job/values.yaml b/support-files/kubernetes/charts/bk-job/values.yaml index 369b6f44a6..63e7a479d1 100644 --- a/support-files/kubernetes/charts/bk-job/values.yaml +++ b/support-files/kubernetes/charts/bk-job/values.yaml @@ -715,14 +715,6 @@ log: # 服务后台日志可使用的最大磁盘空间(超出后将清理最旧的日志文件,但每类日志文件至少保留一个),单位支持B、KB、MB、GB、TB、PB,默认40GB maxVolume: 40GB -# AI相关配置 -ai: - # 是否开启AI功能,默认不开启 - enabled: false - # AI分析错误日志功能相关配置 - analyzeErrorLog: - # 支持分析的错误日志最大长度,单位支持B、KB、MB、GB、TB、PB,默认5MB - logMaxLength: "5MB" ## 蓝鲸日志采集配置 bkLogConfig: @@ -1272,6 +1264,20 @@ analysisConfig: connectTimeout: 5000 # 读取超时,单位:毫秒 readTimeout: 300000 + # AI相关配置 + ai : + # 是否开启AI功能,默认不开启 + enabled : false + # AI分析错误日志功能相关配置 + analyzeErrorLog : + # 支持分析的错误日志最大长度,单位支持B、KB、MB、GB、TB、PB,默认5MB + logMaxLength : "5MB" + # AI聊天记录相关配置 + chatHistory : + # 最大保留天数,默认31天 + maxKeepDays : 31 + # 单个用户最大保留的聊天记录数量,默认1000条 + maxHistoryPerUser : 1000 ## job-file-gateway文件网关服务配置 fileGatewayConfig: From 2a409c343c6a261ccfdc16abebd174b42613ab04 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Thu, 5 Sep 2024 20:55:05 +0800 Subject: [PATCH 044/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.支持国际化; 2.终止生成请求使用Body传参。 --- .../job/analysis/api/web/WebAIResource.java | 8 ++-- .../model/web/req/TerminateChatReq.java | 43 +++++++++++++++++++ .../api/web/impl/WebAIResourceImpl.java | 7 +-- .../ai/context/model/MessagePartEvent.java | 8 +++- .../ai/impl/AIAnswerStreamSynchronizer.java | 9 ++++ ...b_analysis_20240618-1000_V3.10.0_mysql.sql | 15 +++++-- 6 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/TerminateChatReq.java diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java index 0c14e0e545..ab46e82d6d 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/WebAIResource.java @@ -28,6 +28,7 @@ import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.req.GenerateChatStreamReq; +import com.tencent.bk.job.analysis.model.web.req.TerminateChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; import com.tencent.bk.job.common.annotation.WebAPI; @@ -207,10 +208,9 @@ Response terminateChat( @ApiParam(value = "资源范围ID", required = true) @PathVariable(value = "scopeId") String scopeId, - @ApiParam(value = "对话记录ID") - @RequestParam(value = "recordId") - @Min(value = 1L, message = "{validation.constraints.AIInvalidRecordId.message}") - Long recordId + @ApiParam(value = "终止对话请求参数", required = true) + @Validated + @RequestBody TerminateChatReq req ); @ApiOperation(value = "清空聊天记录", produces = "application/json") diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/TerminateChatReq.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/TerminateChatReq.java new file mode 100644 index 0000000000..41815415f2 --- /dev/null +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/model/web/req/TerminateChatReq.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making BK-JOB蓝鲸智云作业平台 available. + * + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-JOB蓝鲸智云作业平台 is licensed under the MIT License. + * + * License for BK-JOB蓝鲸智云作业平台: + * -------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and + * to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +package com.tencent.bk.job.analysis.model.web.req; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiParam; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; + +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("终止对话请求体") +@Data +public class TerminateChatReq { + @ApiParam(value = "对话记录ID") + @Min(value = 1L, message = "{validation.constraints.AIInvalidRecordId.message}") + Long recordId; +} diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java index 5dced17fcc..5a6b6cb0d7 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/api/web/impl/WebAIResourceImpl.java @@ -32,6 +32,7 @@ import com.tencent.bk.job.analysis.model.web.req.AICheckScriptReq; import com.tencent.bk.job.analysis.model.web.req.AIGeneralChatReq; import com.tencent.bk.job.analysis.model.web.req.GenerateChatStreamReq; +import com.tencent.bk.job.analysis.model.web.req.TerminateChatReq; import com.tencent.bk.job.analysis.model.web.resp.AIAnswer; import com.tencent.bk.job.analysis.model.web.resp.AIChatRecord; import com.tencent.bk.job.analysis.model.web.resp.ClearChatHistoryResp; @@ -168,7 +169,7 @@ public ResponseEntity generateChatStream(String username, String scopeId, GenerateChatStreamReq req) { StreamingResponseBody streamingResponseBody = chatService.generateChatStream(username, req.getRecordId()); - return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(streamingResponseBody); + return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).body(streamingResponseBody); } @Override @@ -176,8 +177,8 @@ public Response terminateChat(String username, AppResourceScope appResourceScope, String scopeType, String scopeId, - Long recordId) { - boolean result = chatService.triggerTerminateChat(username, recordId); + TerminateChatReq req) { + boolean result = chatService.triggerTerminateChat(username, req.getRecordId()); return Response.buildSuccessResp(result); } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java index 56f7c52cae..499759d4dd 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/context/model/MessagePartEvent.java @@ -41,12 +41,16 @@ public class MessagePartEvent { * 消息内容 */ private String messagePart; + /** + * 事件产生时间 + */ + private Long timeMills; public static MessagePartEvent endEvent() { - return new MessagePartEvent(true, null); + return new MessagePartEvent(true, null, System.currentTimeMillis()); } public static MessagePartEvent normalEvent(String messagePart) { - return new MessagePartEvent(false, messagePart); + return new MessagePartEvent(false, messagePart, System.currentTimeMillis()); } } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java index 0e6dec5c36..c3eae723f5 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIAnswerStreamSynchronizer.java @@ -28,6 +28,7 @@ import com.tencent.bk.job.analysis.service.ai.context.model.AsyncConsumerAndProducerPair; import com.tencent.bk.job.analysis.service.ai.context.model.MessagePartEvent; import com.tencent.bk.job.common.model.Response; +import com.tencent.bk.job.common.util.TimeUtil; import com.tencent.bk.job.common.util.json.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @@ -75,6 +76,14 @@ public AsyncConsumerAndProducerPair buildAsyncConsumerAndProducerPair() { String message = JsonUtils.toJson(respBody) + "\n"; outputStream.write(message.getBytes(StandardCharsets.UTF_8)); outputStream.flush(); + if (log.isDebugEnabled()) { + log.debug( + "partMessage={}, time={}, delay={}ms", + partMessage, + TimeUtil.formatTime(event.getTimeMills(), "yyyy-MM-dd HH:mm:ss.SSS"), + System.currentTimeMillis() - event.getTimeMills() + ); + } } catch (InterruptedException e) { log.debug("Interrupted when take message from queue", e); } catch (IOException e) { diff --git a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql index 46d27171fb..2539263c5e 100644 --- a/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql +++ b/support-files/sql/job-analysis/0008_job_analysis_20240618-1000_V3.10.0_mysql.sql @@ -53,10 +53,19 @@ CHARACTER SET = utf8mb4; -- ------------------------ REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) -VALUES(1, 'CHECK_SCRIPT', 'zh_CN', '检查脚本是否有语法问题', '帮我检查一下我写的脚本有没有语法问题。', '已知脚本内容如下:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n帮我检查一下我写的脚本有没有语法问题。\n\n回答问题时请遵守以下要求:\n1. 指出问题时请给出对应的脚本行号,请注意首行shebang与空白的行也需要计算,没有问题的行不需要体现在回答中。', '检查脚本是否有语法问题', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +VALUES(1, 'CHECK_SCRIPT', 'zh_CN', '检查脚本是否存在语法问题', '请帮忙检查一下脚本是否有语法或逻辑问题。', '已知脚本内容如下:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n请帮忙检查一下脚本是否有语法或逻辑问题。\n\n回答问题时请遵守以下要求:\n1. 指出问题时请给出对应的脚本行号,请注意首行shebang与空白的行也需要计算,没有问题的行不需要体现在回答中。', '系统内置的AI命令提示模板:检查脚本是否存在语法问题', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) -VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能一:脚本批量执行】\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前任务执行的脚本内容为:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n脚本参数为:\n{BK_JOB_AI_TEMPLATE_VAR{script_params}}\n\n报错信息如下:\n{BK_JOB_AI_TEMPLATE_VAR{error_content}}\n\n请回答用户的提问:\n任务报错内容解析', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +VALUES(2, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'zh_CN', '分析脚本执行任务报错信息', '帮我分析一下这个任务报错的主要原因,并提供一下处理建议', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在被管控的对象机器上(也就是用户的执行对象),GSE Server属于平台自己的服务节点,由平台自己部署和维护;GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,当出现BK-GSE相关的问题时,通过【BK助手】这个企业微信服务号来响应用户的咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能一:脚本批量执行】\n用户在Web页面填写任务名称、脚本来源、脚本内容、脚本参数、超时时长、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务产生的日志并存储到自身系统的DB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前任务执行的脚本内容为:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\n脚本参数为:\n{BK_JOB_AI_TEMPLATE_VAR{script_params}}\n\n报错信息如下:\n{BK_JOB_AI_TEMPLATE_VAR{error_content}}\n\n请回答用户的提问:\n请你结合任务执行的脚本内容,对执行结果的报错信息进行分析;要说明报错的主要含义,如果和脚本内容有关系,则明确指出是哪一段落的问题,并给出修改建议!', '系统内置的AI命令提示模板:分析脚本执行任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); REPLACE INTO job_analysis.ai_prompt_template (id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) -VALUES(3, 'ANALYZE_FILE_TRANSFER_TASK_ERROR', 'zh_CN', '分析文件分发任务报错信息', '任务内容报错解析', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在多台用户(属于业务团队)机器上,每台机器上一个,GSE Server部署在蓝鲸团队自己的机器上,GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,BK-GSE通过【BK助手】这个企业微信服务号来响应用户咨询。\n\n以下介绍作业平台脚本执行功能的原理:\n【功能:文件批量传输】\n用户在Web页面填写任务名称、超时时长、上传限速、下载限速、源文件、目标路径、传输模式、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务状态与传输进度日志并存储到自身系统中的MongoDB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与进度日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前已经结合任务上下文信息进行了预分析,得到以下结果:\n(1)当前任务失败的主要原因为:\n{BK_JOB_AI_TEMPLATE_VAR{file_task_error_source}}\n\n(2)源文件上传失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{upload_file_error_data}}\n\n(3)目标机器下载失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{download_file_error_data}}\n\n\n请回答用户的提问:\n请你结合预分析结果数据,对任务报错内容进一步分析,并给出总结性的原因与处理建议', '系统内置的AI命令提示模板:分析文件分发任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +VALUES(3, 'ANALYZE_FILE_TRANSFER_TASK_ERROR', 'zh_CN', '分析文件分发任务报错信息', '帮我分析一下这个任务报错的主要原因,并提供一下处理建议', '请首先理解以下作业平台领域知识:\n作业平台是一个支持脚本批量执行与文件批量传输的Web平台,底层依赖BK-GSE系统完成脚本执行与文件分发。\n作业平台的依赖系统介绍:\nBK-GSE:由GSE Agent与GSE Server构成,其中GSE Agent分布在被管控的对象机器上(也就是用户的执行对象),GSE Server属于平台自己的服务节点,由平台自己部署和维护;GSE Server对所有的GSE Agent进行管理与控制,可以向Agent下发脚本任务、获取脚本执行日志与结果信息等,还可以调度多个Agent利用组建的BT网络在多个Agent之间传输文件,当出现BK-GSE相关的问题时,通过【BK助手】这个企业微信服务号来响应用户的咨询。\n\n以下介绍作业平台文件分发功能的原理:\n【功能:文件批量传输】\n用户在Web页面填写任务名称、超时时长、上传限速、下载限速、源文件、目标路径、传输模式、执行账号、目标服务器等信息,作业平台将这些信息组装为原子任务信息后,调用BK-GSE提供的接口将任务信息提交给BK-GSE系统,并得到生成的任务ID,随后作业平台每间隔一定时间调用BK-GSE的任务状态查询接口查询任务状态、拉取任务状态与传输进度日志并存储到自身系统的DB中,直到任务完成或者超时。用户可以在任务执行结果页面查看任务执行结果信息、报错信息与进度日志。\n\n你是作业平台的AI助手,需要结合报错信息分析用户的提问并给出回答,回答中可以使用作业平台领域知识中的概念,如果需要BK-GSE进一步排查,请在回答的末尾添加该语句:如有其他问题,可以点击联系[BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}})进行人工咨询。\n\n当前已经结合任务上下文信息进行了预分析,得到以下结果:\n(1)当前任务失败的主要原因为:\n{BK_JOB_AI_TEMPLATE_VAR{error_source}}\n\n(2)源文件上传失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{upload_file_error_data}}\n\n(3)目标机器下载失败的机器与报错信息(JSON格式)为:\n{BK_JOB_AI_TEMPLATE_VAR{download_file_error_data}}\n\n\n请回答用户的提问:\n请你结合预分析结果数据,对任务报错内容进一步分析,并给出总结性的原因与处理建议。', '系统内置的AI命令提示模板:分析文件分发任务报错信息', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +REPLACE INTO job_analysis.ai_prompt_template +(id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) +VALUES(4, 'CHECK_SCRIPT', 'en', 'Check the syntax problems of the script', 'Please check if there are any syntax or logic issues in the script', 'Given the following script content:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\nPlease indicate the line number corresponding to the problem when pointing out an issue. Please note that the first line shebang and blank lines also need to be counted, and lines without issues do not need to be reflected in the answer.', 'System AI template: Check the syntax problems of the script', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +REPLACE INTO job_analysis.ai_prompt_template +(id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) +VALUES(5, 'ANALYZE_SCRIPT_EXECUTE_TASK_ERROR', 'en', 'Analyze error information for script execution tasks', 'Help me analyze the main cause of this task error and provide some processing suggestions.', 'First, please understand the following knowledge related to the JOB platform:\nJOB is a web platform that supports batch script execution and file batch transfer, and relies on the BK-GSE system at the underlying infrastructure.\nIntroduction to the dependent system of the JOB platform:\nBK-GSE: composed of GSE Agent and GSE Server, where GSE Agent is distributed on the controlled object machine (i.e., the user''s execution object), and GSE Server belongs to the platform''s own service node, which is deployed and maintained by the platform itself. GSE Server manages and controls all GSE Agents, can issue script tasks to Agents, obtain script execution logs and result information, etc., and can also schedule multiple Agents to use the BT network to transfer files between multiple Agents. When there are BK-GSE related issues, the WeCom service account [BK-Assistant]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}}) is used to respond to user inquiries.\n\nThe following introduces the principle of the script execution of the JOB platform:\nBatch Script Execution: Users fill in task name, script source, script content, script parameters, timeout duration, execution account, target server and other information on the web page. JOB assembles this information and calls the interface provided by BK-GSE to submit the task to the BK-GSE system, and obtains the generated task ID. Afterwards, the JOB calls the API of BK-GSE at intervals to query the task status, pull the generated logs and store them in its own system DB, until the task is completed or timed out. Users can view task execution result, error information and logs on the task execution result page.\nAs an AI assistant of the JOB platform, you need to be familiar with the functions of the JOB platform and based on the actual error information, provide answers to user''s question. If further troubleshooting by BK-GSE is needed, please add the following statement at the end of the answer: If you have any questions for GSE, click [BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}}) for manual consultation.\n\nThe current task execution script content is:\n```{BK_JOB_AI_TEMPLATE_VAR{script_type}}\n{BK_JOB_AI_TEMPLATE_VAR{script_content}}\n```\nThe script parameters are:\n{BK_JOB_AI_TEMPLATE_VAR{script_params}}\n\nThe error information is as follows:\n{BK_JOB_AI_TEMPLATE_VAR{error_content}}\n\nPlease answer the user''s question:\nPlease analyze the error information of the execution result based on the task execution script content, and explain the main meaning of the error. If it is related to the script content, please specify which paragraph has the problem and provide modification suggestions!', 'System AI template: Analyze error information for script execution tasks', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); +REPLACE INTO job_analysis.ai_prompt_template +(id, code, locale, name, raw_prompt, template, description, creator, last_modify_user, create_time, last_modify_time, row_create_time, row_update_time) +VALUES(6, 'ANALYZE_FILE_TRANSFER_TASK_ERROR', 'en', 'Analyze error information for file distribution tasks', 'Help me analyze the main cause of this task error and provide some processing suggestions.', 'First, please understand the following knowledge related to the JOB platform:\nJOB is a web platform that supports batch script execution and file batch transfer, and relies on the BK-GSE system at the underlying infrastructure.\nIntroduction to the dependent system of the JOB platform:\nBK-GSE: composed of GSE Agent and GSE Server, where GSE Agent is distributed on the controlled object machine (i.e., the user''s execution object), and GSE Server belongs to the platform''s own service node, which is deployed and maintained by the platform itself. GSE Server manages and controls all GSE Agents, can issue script tasks to Agents, obtain script execution logs and result information, etc., and can also schedule multiple Agents to use the BT network to transfer files between multiple Agents. When there are BK-GSE related issues, the WeCom service account [BK-Assistant]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}}) is used to respond to user inquiries.\n\nThe following introduces is the principle of the file transfer of JOB platform:\nBatch File Transfer: Users fill in various parameters on the web interface, such as task name, timeout duration, upload speed limit, download speed limit, source file, target path, transfer mode, execution account, target server, etc. The JOB platform assembles this information and submits it to the BK-GSE system via its API. The BK-GSE system generates a task ID and starts executing the task. The JOB platform periodically queries the task status and pulls the logs and progress information from the BK-GSE system, storing them in its own system DB. Users can view the task execution result, error information, and logs on the task execution result page.\n\nAs an AI assistant of the JOB platform, you need to be familiar with the functions of the JOB platform and based on the actual error information, provide answers to user''s question. If further troubleshooting by BK-GSE is needed, please add the following statement at the end of the answer: If you have any questions for GSE, click [BK助手]({BK_JOB_AI_TEMPLATE_VAR{bk_helper_link}}) for manual consultation.\n\nBased on the pre-analysis of the task context information, the following results have been obtained:\n(1) The main reason for the failure of the current task is:\n{BK_JOB_AI_TEMPLATE_VAR{error_source}}\n\n(2) The machine that failed to upload the source file and the error information (in JSON format) are:\n{BK_JOB_AI_TEMPLATE_VAR{upload_file_error_data}}\n\n(3) The machine that failed to download the target file and the error information (in JSON format) are:\n{BK_JOB_AI_TEMPLATE_VAR{download_file_error_data}}\n\nPlease answer the user''s question:\nBased on the pre-analysis results, please further analyze the error content of the task and provide a summary of the main reasons and processing suggestions.', 'System AI template: Analyze error information for file distribution tasks', 'admin', 'admin', NULL, NULL, '1970-01-01 00:00:00', '1970-01-01 00:00:00'); From 21aa9d3267779be857ef7d39db5465ed2f8e73d2 Mon Sep 17 00:00:00 2001 From: jsonwan Date: Mon, 9 Sep 2024 15:43:03 +0800 Subject: [PATCH 045/115] =?UTF-8?q?feature:=20AI+JOB=20=E4=B8=80=E6=9C=9F?= =?UTF-8?q?=20#2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.终止对话后生成终止对话的AI返回内容; 2.确保流式数据结束后拉取历史记录列表可拉到最新数据。 --- .../job/analysis/consts/AIChatStatusEnum.java | 21 +++++++++++-------- .../ai/impl/AIChatHistoryServiceImpl.java | 9 +++++++- .../service/ai/impl/ChatServiceImpl.java | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java index bc1146bc54..ea12a6ecff 100644 --- a/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java +++ b/src/backend/job-analysis/api-job-analysis/src/main/java/com/tencent/bk/job/analysis/consts/AIChatStatusEnum.java @@ -24,6 +24,8 @@ package com.tencent.bk.job.analysis.consts; +import lombok.Getter; + /** * AI对话状态 */ @@ -31,24 +33,29 @@ public enum AIChatStatusEnum { /** * 初始状态 */ - INIT(0), + INIT(0, "Chat initialized."), /** * 正在回答 */ - REPLYING(1), + REPLYING(1, "Chat is replying."), /** * 已完成 */ - FINISHED(2), + FINISHED(2, "Chat finished."), /** * 已终止 */ - TERMINATED(3); + TERMINATED(3, "Chat terminated."); + @Getter private final int status; - AIChatStatusEnum(int status) { + @Getter + private final String description; + + AIChatStatusEnum(int status, String description) { this.status = status; + this.description = description; } public static AIChatStatusEnum getAIChatStatus(int status) { @@ -60,8 +67,4 @@ public static AIChatStatusEnum getAIChatStatus(int status) { throw new RuntimeException("Unknown AIChat status " + status); } - public int getStatus() { - return status; - } - } diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java index 4b7810adb2..7e91a2b99a 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/AIChatHistoryServiceImpl.java @@ -99,7 +99,14 @@ public int finishAIAnswer(Long historyId, AIAnswer aiAnswer) { @Override public int terminateAIAnswer(Long historyId) { - return aiChatHistoryDAO.updateChatHistoryStatus(historyId, AIChatStatusEnum.TERMINATED.getStatus()); + return aiChatHistoryDAO.updateChatHistoryStatusAndAIAnswer( + historyId, + AIChatStatusEnum.TERMINATED.getStatus(), + AIChatStatusEnum.TERMINATED.getDescription(), + "1", + null, + System.currentTimeMillis() + ); } /** diff --git a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java index 0a1d5c5cf5..ef955bc178 100644 --- a/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java +++ b/src/backend/job-analysis/service-job-analysis/src/main/java/com/tencent/bk/job/analysis/service/ai/impl/ChatServiceImpl.java @@ -113,10 +113,10 @@ public StreamingResponseBody generateChatStream(String username, Long recordId) consumerAndProducerPair.getConsumer() ); future.whenComplete((content, throwable) -> { - aiAnswerStreamSynchronizer.triggerEndEvent(); // 4.处理AI回复内容 aiAnswerHandler.handleAIAnswer(recordId, content, throwable); futureMap.remove(recordId); + aiAnswerStreamSynchronizer.triggerEndEvent(); }); futureMap.put(recordId, future); return consumerAndProducerPair.getProducer(); From ef9918cee24f3ca76dad75e72300fa73fe93885b Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Wed, 10 Jul 2024 17:29:03 +0800 Subject: [PATCH 046/115] =?UTF-8?q?fix:=20=E6=89=93=E5=BC=80F12=E5=90=8E?= =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E6=A0=8F=E4=BC=9A=E6=8A=8A=E5=AF=BC=E8=88=AA?= =?UTF-8?q?=E6=A0=8F=E7=9B=96=E4=BD=8F=E4=B8=94=E5=A4=9A=E6=AC=A1=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E4=BE=A7=E8=BE=B9=E6=A0=8F=E4=BC=9A=E6=97=A0=E9=99=90?= =?UTF-8?q?=E5=BB=B6=E4=BC=B8=20#3111=20#=20Reviewed,=20transaction=20id:?= =?UTF-8?q?=2017769?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/site-frame/index.vue | 22 ++++--------------- src/frontend/src/layout-new.vue | 10 +++++++-- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/frontend/src/components/site-frame/index.vue b/src/frontend/src/components/site-frame/index.vue index db844ba3b7..06eacf1be5 100644 --- a/src/frontend/src/components/site-frame/index.vue +++ b/src/frontend/src/components/site-frame/index.vue @@ -90,13 +90,13 @@ + diff --git a/src/frontend/src/components/ace-editor/index.vue b/src/frontend/src/components/ace-editor/index.vue index 2d96242040..8cc6539e0d 100644 --- a/src/frontend/src/components/ace-editor/index.vue +++ b/src/frontend/src/components/ace-editor/index.vue @@ -64,6 +64,7 @@ class="jb-ace-action" :style="{ height: `${tabHeight}px` }"> + @@ -291,6 +357,7 @@ } .file-download-log { + position: relative; height: 100%; padding-top: 10px; overflow-y: scroll; @@ -327,5 +394,22 @@ animation: file-log-loading-ani 1s linear infinite; } } + + .ai-extend-tool{ + position: absolute; + top: 100px; + left: 100px; + z-index: 1000; + display: none; + width: 32px; + height: 32px; + cursor: pointer; + background: #3D3D3D; + border: 1px solid #4F4F52; + border-radius: 2px; + box-shadow: 0 2px 10px 0 #000;; + align-items: center; + justify-content: center; + } } diff --git a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/file-log/log-item.vue b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/file-log/log-item.vue index 7b232f4d8c..58b7c20e82 100644 --- a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/file-log/log-item.vue +++ b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/file-log/log-item.vue @@ -125,7 +125,7 @@ { parent.$t('history.状态_log') }:{ data.statusDesc } { parent.$t('history.文件源') }: diff --git a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue index 842489a741..a23313002d 100644 --- a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue +++ b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue @@ -27,11 +27,14 @@ diff --git a/src/frontend/src/views/executive-history/step-detail/components/execution-info/index.vue b/src/frontend/src/views/executive-history/step-detail/components/execution-info/index.vue index f1c28ecd4e..a40af20602 100644 --- a/src/frontend/src/views/executive-history/step-detail/components/execution-info/index.vue +++ b/src/frontend/src/views/executive-history/step-detail/components/execution-info/index.vue @@ -38,6 +38,10 @@ :is-file="isFile" :is-task="isTask" />

+ logContentRef.value.getLog(); + let infoBoxParentNode; const handleFullscreen = () => { this.messageInfo(I18n.t('history.按 Esc 即可退出全屏模式')); diff --git a/src/frontend/src/views/executive-history/step-detail/index.vue b/src/frontend/src/views/executive-history/step-detail/index.vue index d9eb938f77..3f8c870f60 100644 --- a/src/frontend/src/views/executive-history/step-detail/index.vue +++ b/src/frontend/src/views/executive-history/step-detail/index.vue @@ -807,11 +807,8 @@ } .container-right { - display: flex; height: 100%; min-width: 800px; - overflow: hidden; - flex-direction: column; flex: 1; } } diff --git a/src/frontend/static/images/ai.png b/src/frontend/static/images/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6f9357e2f5955c21004d36c7d41136efad22b7 GIT binary patch literal 1605 zcmYLJ3pCRU82`^U4I#G(ktK_}Jj%HdV$3vCB&8uT@5lAJk~|kh*2CJE_q@8Oyo!os zG)1+=N*-e@m6^zhyW5iJ&b^)Pch2wo{l4$~zH`2FzH`1yt}a+bc};l$02FbK7&j@y z*8!82=F>?@Vkv=6yJ68lBS&jSS|kK{;DVi<0i;yJ00>AEfUaAlumNrRsSkn-0Pw$k z834!%10X*d0(iWKWJ|1r1%^usOfRml8w!z;l5>-90B0&jgoxfn;!(*U4`SO*B_ zruPW|pusqd-Ej}OsT0nj-tJp$Bpo|Mj=gBFCSFGPD_yFw)7cZ*pv~hRSB`$OGbq;e zIqv8b?hO;6?-=!>MzcV z63KNOcf0z0+qA@Lx-5BuG!Dt`U0E|}Twb2UuXf|Psab7hhAZS~_F6&oPL#PY^$`o- z{4h_wsP7XUrL#Xw39K?Lc(}jqHY{i#4fp&qNbt%x!63#+cz4{(g9C54rkiLlqp`_E z0>}3!&a|tL6c}3&InaeELveIDU@CuJUA!!pdSWLtmm_H=^DA7fkb`gpjNZ_Lse4~lY&rTL;SVNI3!DkJ&< zPY;2o_#EIx=yL32ASs%ZCE6v7pTR=CT-EKqw#@1@L%B58_t2b4b&8Tg4X|m4LRD1s zS9LAWtO@1V@Aio)zi!&oHxO~usS~N~P7f9Ao%-fJsoIQjDO>WWiTzrrnE2&AckjtO zH=Eq}Tc^B>mm)K5Id4<$i{^m3JNYMja3MEe;_FR0j%?Ip(db}PEpb<@Hgjke^qyJk zzmJ#spccbv3=HI@d-a?ttvrW7AI|dQxGLFJBTLl`I*>Z702BVyIN7T7iHdbt60{8# z$8Fg)UB-MVZZbhqIfsdF-Y22yYF6C1DT*&Wih3+~RdO{&Znav#XxJH?mUlO|zqM+R zEd*g+Kkq)nqFUFo;X1`nroRbaLw!?%uI%3VC1e!oSKykXwbtYlaQ$kL7C?9Bk7}yl zdXAkvme)9>umn#mR5HD@x-m{`EN3`MW-f+d!3M#@6*5{o)EG$A{F@KNYV$$9dd&^i zg7*T^w&X_dsk$oNntR^Yz>Cea4Fp_3w^wPd@=QVuyVc*u3@rG(W5J%yRE`bK3A}~; z7<|c$RD@X7?EHK;x=Y(SZDoR!!Qkq>@!QXjabKn-A10oK6kQ7x^?HV7eF$_AOr9@T zrM#O;)62t*CcJEgSO`XfP>RIg!YLg``0tHnQ&G!8Cx@rh51x9D?018_8WRgVm@6G%v0NI({ zMJfZn%!VQEyM(UX+Md8H6&52P+r{Y-4qNrQUy_<={zdU>rjfF-vXxqfZ|Rmp2IgD)Y90GL$ z>x(jXXz=~o7g?d@-7z~mSm1kcARZT9m*v_y4pWV z@5`0P31@r8EIhB!9-rJS!@uXUPX&8_X2MDsZS?HOJ)%ZsKm=i?#r4jRd3x;5c()Mw zIzDV*)GO=aWBjvkM+qr5YveHrqvdfiw#s*ZJ?3`u9#(;gu&=!3UcdF1GGUOV>t|DX z#je;Be!Dr8+eNIKh@{1sC}uE6~GAUnA(MA4*_0P#oA#*E$# zC&XvqB1c}Y@r*w-W6CX)lyx-dAt-Pd)G>daw0Oq-$;B34cJ*98RL8%mZi7agSwG_knbz@;Kdgtt(wDmV9B`Llil$)D^H*Dn3gRlVWe v!_Yc%vglySp%KM5h2p)&L+KHn-zU~|lscoz1D0#o-!Oo4aKSX93FrR-R~Fe& literal 0 HcmV?d00001 diff --git a/src/frontend/static/images/blueking.png b/src/frontend/static/images/blueking.png new file mode 100644 index 0000000000000000000000000000000000000000..f79ad20199a6ebb366da339cfe4c53bd6120314a GIT binary patch literal 8135 zcmV;&A2{HNP)Py9fk{L`RCr#^dO{z|vI_Ev_x1Mu~!T*lJv`{yM7HX&2&9u}&n$m$_?`&>=f&ux9g52BzGzCDp zZlHJhUTPPdP<;yqr1D<4fH!Asjh+eB={tEzAm(|2 zxN_x6?T{fu;^X7v+i11gR$8q#OQllX10b7a*;IyMk^t!39kC07P|ovwDS&*ArcX5KKJJ7XmnAu~>FpzkdBhj~+d$+-=A}`u@tVXi-(rl7I#UGCMn4 zwPwwlgsiNrfsv7sBUCEY695vL0A83FgFvq2IBuKGX3M>F>C)lnpMSnGKR=%f0C0-} z+fsn~(}nUihQxZ(q)B7-di?|d0~v-fhM^xOmf)@}At2mTm) z$82p0Ks~^@N9Ota`0?Y$E?v4jsnKYrsZ^?dcZ$%RJkN35F{jhH>iqfhTl)6xOF#!w z1$PAMod6n$mE!e^ii+Mwqj8=}r5Xhw=1#z9!DnkZj@w>SQ}dU^#Ke;d70_aAZ7!g` zVA9{>wUZ}LCTC}7PtfUf?*Jep^S`l}>~{O2{QUe4y?gh*?E`Eumn-C0ngggmFzH3? zsZ*ztyLRpRXO&9zt3a&eI1W6|L#0xIWy3Y*A;;023dGjk=gysbwP(+s1S%ObDg*<;6!P0;K0OBseq3pAD=4*drwPn^QsxicGy`S;%+#P%Jz$XWCjqN4O(O;{{e zT)lb?cDo(v>8+8RoOIXJgdhmTR;%^Re*OAw$j{Fw@t0ZJB=K)5ptA81f8ESn9}G-F zC^z@NP*zrs7hZU-5#|Ke>eXMNRaz^I9{o5NH^ECw%dq;3H6obN(NULU_XZIEprD2V)E7+Anjb!VIJtlS{@YlV?Giyk3KPQ;J`rEKOm^V0QHT4$j4z$o;;btQyTaQSipOlC7Ibrz(xPQ&kh`>JTb?(NV49M7Soqy%=m13Il1Q>HwJcE6DE z;y7;8nKNhR_3quf+|5*_TP3?!jUWgYP>-lvt=2h_k&%m9BKi>o0S<=)<>eLFx9eZ2(I6%|1{$rV z$$bg)0drYd+3?iV)Sujrzpv$QB>G_kx@*_2xS>Ob{#m2ZObW9zo@%w`S{yw11M>4P zqNJh*)_MnAE;*al5SEa0Phc_(X&0%lo*pZ@PXAS_Sm-0PNJ%iDQ^)ogJY+E9;^Lax zwUG6lPUpJ4d-uNj*kg}X`*Eeh0@N=%Ef&kv$jHbK8HNcf(LunJmX>1UrmZ-B?i%#1 zyPi z3)t58A+uY5QH1q623i&nc;I?9;Nsmtxj`d3%HVlGBw8o|mVtL)7iCZoV6C~0@`4j! zZ+?$w#tg&o;lrAXVWa0L^|xfnl1CRVTxjw0hI`==1omk=3ySVl;r02P{s&Y6lz~U~@{$C>mrhAUUDkgf`-OG5$59y)X=X~2L1pQ%);k>PZV z!pv3ccc9mpdAO%dDk9ZDl@&16OP~@vW%eln^f1#s=V3EdsSez%t+!O+*8acXg(v!9 z)Tpq!9dc+K$L-9^%lplP4?ak4lrl8+VySP8PB)d|C0kQdGsa*r{EcClkQ$$K=4%T+ zM2}Ik(4%vEaT;&b01j7!o@}HK0|DrVrCy@%qY9{9$wQFdWiywec>iDU(I2OxW55LioR_Nh`!)-I#+A8+F1%mUzz3; zr!-(nj_&Q;xiewJh!JPpYvVyD@%;IGe7NEp40&-8TE(y^td?{p4YHB3p-UwCvSD!p zuB_*)Hv<_dk(r7KQ%(+zu58~@{BG(H^zVP?OIoxiAU8L+`^b?aEB!e8UV!>_m2Tg@ zJv1pPX?r+Jy&E?E6aT*Az++>dfzt&PRl5OG7#+zX`?1>oTq8wYUn{S4wPX<&+f|4> zO_;y{73X$iP)ZTz&G}6zsSf8pUtV55Dmgis5-0vreDq4OG;)<@&6=fKzI^#ojYcy& z)SJj{`NP5wpwE5`eS3Bj!K`!d-w4JmFA??!b^s;^dyZuQH4D@`B{gHID1iF>Dtz(D zVyM;XP(ulM-|2LI^2#f(yt!h<3coIt-vkH&eCW`jRs#kMSgTU028DVH>Cc(-7o+{~ z8AwWQ4Rbv}k!2u8`ii=jE)4QqF!xEGxHO{xLj+J}W>8)W*sWDCAO62s^XK_35=n6! zcQ7w6Z}Ni=K1gx0tV2DC6Ti7m67k~V;(n>Asb4b;(>4^Kltr5Iat`{8UjVf(#@i7G z;Ivy%TX_Q<=Y%dg8M^312vB+Ih3(J-;G7QFYD?j?nV`}{L2F0{R9exxYgnLDssLJ^ zBG00roI$PCiCcT$#hUkDL`FtNsG)?sF9LGbs9T8{*Pp?@d8w%cTBlOeWI|qtW;;H=tfVsV|_St3-g>Y_``UA|i6aF-Vj_nlw8H{U+vs zQA?2tF9<06`6!sn{}khEN_(U7p2hfj>GNpUu3ac@ zgma&-udjbQGBWZb1w&;}y_|Sz%mb+1ZeO9*YRUf$SqRXHvvSb)*#=h-E-`)Ap~F3xJ^Ll>-g^*N*9wLgXm;2)bs_6b4`8&q-H$bCN%BgWk{NfK^9|j4Qiz6m9B{qFZ(calMGZ z6q(EO=-$06=FXXk55C-ojNxy9;{v!-ZdZuoTyPzF8?VovgqLQ{1+9k4>-dR@3Hb1X zKjFQPzkxk_2DFAuQO4v+5-97nZ!y-r-(m~}$8ks0YV{C5BJa_mUg0JI%JY0a!!TjV z2m$)c%pCNZ(7@1JXZPd&gqv_V>hbw1%6v(M=LMvsB;oy~i}9yVHY4rv#o*j74$TB7 z>rjRM>jxE5{$>T*pm75|c0(yVx60F∋}cwp-ag2_AYoHxA!l``VU$TP(cu` zu`HV%4xk>9=Xt)IVVLM}89HHR4tk8w0i%+1XvMXkkXZ2@h7TTqx8BM@U0t0B1TpmC zhabYzPmRIyZ%-g?@H}u7>-(nV6k^W{99;idgx}8@kKetq0L8^6;`%)KznPhBu{h^# zy!Fx7;JVL&I=Zz_UX@41{>Av_!xja!j%8WeS*;X#(S`CS?xNUJVwDo(VFUV74!Vt# z0Hv1gbyc|j-7+kCc{~ms&cm8D>qN1qmCaXQdl?%x?LdL{e#G7TummRcj`I#jWznU3 zRX=`-3HND5qxQ*)m7sO|n3!n1@y6?-6fFDKEg1VwgTQFKCWn?5LZoUDf;N~llXSu&rtV!1B^P_JG<}J*;W5uj7`g4Kt`JkapQ!;?m%6g zwDRh(+rhFbq-UffIyx+YB#t#5P^HLUx^xK(mwqa`C5+$#%iEzAY%oOWkd~T+*qA76 z`0gCKj(Zo3THi1z0H#}KQG4YeVjV?LF+9wI0bG0!7&{M>*en1phXptHFNeUppi9Vv zzzeXIUxvnzj>G}eAwcq1UDiriFYQ3A{R*PhHrO~VZfmlkX*(F|nATG2PG-BTi+LWV z9~NW!@Dy+YiyOsdsHmz%ty2q!po3G;LU39zdQf{jKY60K@E58Gg3xr4=Q!>H%d+AQ zt}B#9*(UIlH~^Uma=b1MM3Iee#1!8}I5*f7(a=9n21!mFdt7BVEDGzd z1kEdN>VDQQ!<30i#aborszd#;kMP-BHz|3a`aB`EMFLWrOq@K(7U&HGnY^NH!4b+__V-h%BIDfr>i7 zWRsRp%yyvK3Ni6Qi+kd0d{#`uLPJ@J5n#Wt0grd6!0+bI3Hx!kV^3MQ@QsqiVwtbk z>ph_-?eSdu^*7jl%7V;?rhwJNc!^iARZ8+L)eMjt19VOXIwk|W3(%+};9~U>IF@>8 zOay3rG*Drdz#|5$H9(~WxKILIDhDoCK+L-~d!H`DqyFY`WZSmjkH4LXHf`E~#(1^h zDkAmzU|W88tJV5;R8-WmZ~*m0O1E#{9-Ww&xYB(X%QJgx)_jc}2hT&(;c@6w+Cvo) z=hmB)l%Na}<4KN?kuXL8ol}6$i9j5wEe42(c zU)y$J%bsH>vKXPOzlpVfeG_flHcqtBA+w5#iW$kt$^Mcffg`1&qN0ASTen`zvTPfr zERfqlyWqC$*o%ur)vz%}aEw+QWn9*y(5H3tSX*M`&<;Sy6u?Aaxf_zfF%dXa7$7kQ zsJ4)PlIj`sQurvdmO!U#m1Z}K86YYWIDHe?eL-R@sS|&NGhIXdr7fVTjudK;5eGc= ztNSrQHn2N0Ae69Qcgr0qX62SBLhg=m7*hnDhe;CQ|@QXR!P*UL}&rMPErj!@`0m; zK)w5(2L=XkRTZH9t!0=wV=CI+(;h~nQH-=m#24gzguLo3L}rh3A6wv2ck(m5KeZDE zJ@QB+Kq+Xer z1FQ}S(0`rtKEff@bU}M+DgH2R2p)J~pm>E`xZK=ay!(#=MD~0Uj7r*$E^zg*9b1Mk za>n^xezw_cE2dAM{`+<7)+v`j8l~@KKs`E?NKsl^Iy5CEg>*&8^Dh)uu2}IItWn+Y z*rN}5jf&Wn;2yZVM~uLuyrs-QVr^UuP~#SR89=qFVIuss6B{L|3DURJ#*so`*9C~_ zJ7sZJUqbYi&#`R5blltVUX+v+<(9yi|i*x02XXp|+0Q05+fYWhw_p z>1kwCtjBX>heE5>;k~ba0@Ld?(SH^T1JqyMj&WI~czMorUoeT)<(oEb>NS4+cv?pG z1=EupkpUH@NU?K71l4Z0f2`GNr-s_-AisTK&ihDd*AEX38tf5Oy602u$LRxO30V#AEYE^9qtce_HOaZ;!Ps!Bd7`Ltxj#EDQzZk7xnT_XTVugZ1k3e>dUpZwP!Zvg*1Bjs2<4uBZCI>FW6fJ&bXMk6Iecxx$KXIJ6v7atJ!`THKMpp(s& zm6bD-l9H6}QlmXif!5_cx^(N-tw|$BjQE>YtECSGge;Vq`sN=Saq)%&Pd@bwG#dG1 z3~q3uBoUSu@yP$AklO5ap-62J@M0bJyo+_1#>R-Akv#|U2uKRZbC3&7*qLHr&(9FI z1qJj~S-^JwTSS)b#;hr$F=)^ONJyZg{0+t7aO}#>&3$Rim@)EZv7j3rl|9p&{)a0#-^Uzxl^Mq5FWxFmT|5UZPKihb+6zZ8ZD=7#%5Dc^PEc7?GDC zyG*2S$+hx$1#Sr-&p~cm?&j?}VE08JzYMS|TsP$~1eXO|T?vG1J25b$4sZS8HLv|p zm6erKyLa!tvADRHwln&I>Dl8HaAv~qo9|RA)qNo;>aIX1;67Tu5)Y4f zUOaptI|>x-kjN@Um@IyTMzZj-ibym_mRokK$Rp5`UrM^utwPAcD-E6KYEgt`Yj{qn zKP&5i()Z zwxFoo0>0y9Xj8j2oJWQes-Hfwr|QnBmKuei(LDM{yd(*wb< zcJ11@@#DuY(`vQPhRaf_clz`h{B6~G^m%ABdiUxBwOT7hN(M=!WgST3D~FacOX+Xw z5CJB#l0H&Gz}p+dT4p?jwgfg6x<=sFTBSWs;y}q52?7eQ|BMqye*hficxL=l#6Enn zW&=)Crh-rG166Druo^wA*AHSsrxMKk&6EurH*S1&^5n_#p@l}iryFSLJwD+0+nOW0 zQmP9VF7(XG%F1OJhQ3@AR-r{X68~k(KSN5p9vJiFGf=Ut$Ub6|?ATLdG48Xo1cVF% zF;FS`G6R+VD@9pya>WxkPp$i?-uFVsui{Q;`HrgZW>R^Mzk&x($Y zeka@&Q5ki!*^G_fZpQJmH_`8bq3F@G50c}wq7IbZAF4|_ki=IG0A#;YX^@D`=N9Yi=v83v`g>s;XG{LwGS>;90m;@ zj{Ey|78z)B>r+`=eHrKtmINL#lj?~MJ}c?mC>>B#T#chg598e8L;bN-394%I=FLfC#*Ep*vTVK zqy~;0&cmL4--~(CdpmYULaTd`keG~^7$fu%YQ)8`Fp;>r(|H7xoIG*41X1^CRW1mw zTHGk9LUC~!ZkOIbO?eU2j1xV&cP4`(CXSm}=Edg4MS_E8D zsDgOT$>HQsUSWpWR0B(GHHIq>(*^!Sy`FCOp+%7;_gF} z0rkgHku1I$se*!n0hyVZpSi_9^zzQ#LZx}_lFqz#{rdIav~S-&FIXl?!Aiei1~W>b ztiG4%6TpRqg+u=Tml+naEZe(zWcLJ!) zYOjoxyFpPEtXsD(Y2?U};|&JGf~G`NEe%+(_5?vNo6Y7WJ9qBfIBC)(-%nup%OuIo z1r+sgth{fZd^1x@P<;VrE?&IYvu)e9b96f0$Z!)XcNiMJN0)rfOWZCKi1aPez9T0hHW!v z&XhlQ8Aw-p@|(dddQ;t|K;!bqRN4Cb{)(VaoH$XteEIS*v9Yn^G#btQ?%gPN+Z&)y zu$i1r=Mj_1^zAFJys~xu`t|bnnPniUzp^`|bc>Xt9a7AjqeH_Le}6hw1T`@+QT5lq z{xxCn;K2i8V`GQwbh^hGhPf-B3>E~T(r&l!Fquqy4jed;H+Syb%8H5#-wy})GdRs= zW^;6CC{UGPDw*n6HGJ~PCvl@jjp~(2eJHwM+ls{_ia5&Ca zR#xV2-@g5)*|TT+esskbK!1#EHZxlSP~SFuBP)ME`Z5?JM~>7^n>H=JQ>RWD27{qh zbaeE+YPGtZR;%sIvTO>&Fo_Jqh@Te|1fgCKgbJSL%N!2J1((Zpt*)-_ve|4dxp?v7 zja92wRqfolQ(ia@#y}+#ef`TOiFh!U-H9#m)vHQ_rv$bUpgu<<0~t!WeSd=u1V|{6 z_UEYI5tz-f^1(p&H8O!1>#G|4)eAK7%>h&-x1X@lx2-z{v!wwIq(%Y(<{N!AR}J~< zy= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +consolidate@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" + integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== + dependencies: + bluebird "^3.1.1" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +copy-webpack-plugin@12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz#935e57b8e6183c82f95bd937df658a59f6a2da28" + integrity sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA== + dependencies: + fast-glob "^3.3.2" + glob-parent "^6.0.1" + globby "^14.0.0" + normalize-path "^3.0.0" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" + +core-js-compat@^3.37.1, core-js-compat@^3.38.0: + version "3.38.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" + integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== + dependencies: + browserslist "^4.23.3" + +core-js@3.38.1: + version "3.38.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.38.1.tgz#aa375b79a286a670388a1a363363d53677c0383e" + integrity sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^8.2.0: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + +cron-parser-custom@2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/cron-parser-custom/-/cron-parser-custom-2.13.0.tgz#4df7fef1c0e577df2e445a2fbcf4d2509edb71e3" + integrity sha512-703LGihhQflV3Q1end9XWzAELJ6Ohu2vzxZZgSPPJqthcUms/D/4GkXsP6Nqp4WpOtvB85l5on45m1AZOzsmXA== + dependencies: + is-nan "^1.2.1" + moment-timezone "^0.5.25" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-blank-pseudo@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-6.0.2.tgz#50db072d4fb5b40c2df9ffe5ca5fbb9b19c77fc8" + integrity sha512-J/6m+lsqpKPqWHOifAFtKFeGLOzw3jR92rxQcwRUfA/eTuZzKfKlxOmYDx2+tqOPQAueNvBiY8WhAeHu5qNmTg== + dependencies: + postcss-selector-parser "^6.0.13" + +css-declaration-sorter@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz#6dec1c9523bc4a643e088aab8f09e67a54961024" + integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== + +css-functions-list@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.2.tgz#9a54c6dd8416ed25c1079cd88234e927526c1922" + integrity sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ== + +css-has-pseudo@^6.0.3: + version "6.0.5" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-6.0.5.tgz#372e7293ef9bb901ec0bdce85a6fc1365012fa2c" + integrity sha512-ZTv6RlvJJZKp32jPYnAJVhowDCrRrHUTAxsYSuUPBEDJjzws6neMnzkRblxtgmv1RgcV5dhH2gn7E3wA9Wt6lw== + dependencies: + "@csstools/selector-specificity" "^3.1.1" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" + +css-line-break@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0" + integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w== + dependencies: + utrie "^1.0.2" + +css-loader@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.2.tgz#64671541c6efe06b0e22e750503106bdd86880f8" + integrity sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + +css-minimizer-webpack-plugin@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-7.0.0.tgz#b77a3d2f7c0fd02d3ac250dcc2f79065363f3cd3" + integrity sha512-niy66jxsQHqO+EYbhPuIhqRQ1mNcNVUHrMnkzzir9kFOERJUaQDDRhh7dKDz33kBpkWMF9M8Vx0QlDbc5AHOsw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + cssnano "^7.0.1" + jest-worker "^29.7.0" + postcss "^8.4.38" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" + +css-prefers-color-scheme@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-9.0.1.tgz#30fcb94cc38b639b66fb99e1882ffd97f741feaa" + integrity sha512-iFit06ochwCKPRiWagbTa1OAWCvWWVdEnIFd8BaRrgO8YrrNh4RAWUQTFcYX5tdFZgFl1DJ3iiULchZyEbnF4g== + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.0.1, css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssdb@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-8.1.1.tgz#bb3ea0038a471c61fc89c56d11d168d62fac7829" + integrity sha512-kRbSRgZoxtZNl5snb3nOzBkFOt5AwnephcUTIEFc2DebKG9PN50/cHarlwOooTxYQ/gxsnKs3BxykhNLmfvyLg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssfilter@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" + integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== + +cssnano-preset-default@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-7.0.6.tgz#0220fa7507478369aa2a226bac03e1204cd024c1" + integrity sha512-ZzrgYupYxEvdGGuqL+JKOY70s7+saoNlHSCK/OGn1vB2pQK8KSET8jvenzItcY+kA7NoWvfbb/YhlzuzNKjOhQ== + dependencies: + browserslist "^4.23.3" + css-declaration-sorter "^7.2.0" + cssnano-utils "^5.0.0" + postcss-calc "^10.0.2" + postcss-colormin "^7.0.2" + postcss-convert-values "^7.0.4" + postcss-discard-comments "^7.0.3" + postcss-discard-duplicates "^7.0.1" + postcss-discard-empty "^7.0.0" + postcss-discard-overridden "^7.0.0" + postcss-merge-longhand "^7.0.4" + postcss-merge-rules "^7.0.4" + postcss-minify-font-values "^7.0.0" + postcss-minify-gradients "^7.0.0" + postcss-minify-params "^7.0.2" + postcss-minify-selectors "^7.0.4" + postcss-normalize-charset "^7.0.0" + postcss-normalize-display-values "^7.0.0" + postcss-normalize-positions "^7.0.0" + postcss-normalize-repeat-style "^7.0.0" + postcss-normalize-string "^7.0.0" + postcss-normalize-timing-functions "^7.0.0" + postcss-normalize-unicode "^7.0.2" + postcss-normalize-url "^7.0.0" + postcss-normalize-whitespace "^7.0.0" + postcss-ordered-values "^7.0.1" + postcss-reduce-initial "^7.0.2" + postcss-reduce-transforms "^7.0.0" + postcss-svgo "^7.0.1" + postcss-unique-selectors "^7.0.3" + +cssnano-utils@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-5.0.0.tgz#b53a0343dd5d21012911882db6ae7d2eae0e3687" + integrity sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ== + +cssnano@^7.0.1: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-7.0.6.tgz#63d54fd42bc017f6aaed69e47d9aaef85b7850ec" + integrity sha512-54woqx8SCbp8HwvNZYn68ZFAepuouZW4lTwiMVnBErM3VkO7/Sd4oTOt3Zz3bPx3kxQ36aISppyXj2Md4lg8bw== + dependencies: + cssnano-preset-default "^7.0.6" + lilconfig "^3.1.2" + +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + +csstype@^3.1.0, csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +cuint@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" + integrity sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +dayjs@1.11.13: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decamelize@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9" + integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +diff2html@3.4.48: + version "3.4.48" + resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-3.4.48.tgz#7118f52438922ee5826d7cf16b2d7182f373b43c" + integrity sha512-1lzNSg0G0VPKZPTyi4knzV2nAWTXBy/QaWCKzDto6iEIlcuOJEG0li4bElJfpHNz+pBqPu4AcC1i9ZCo9KMUOg== + dependencies: + diff "5.1.0" + hogan.js "3.0.2" + optionalDependencies: + highlight.js "11.9.0" + +diff@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + +diff@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1, domutils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv@16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +echarts@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.5.1.tgz#8dc9c68d0c548934bedcb5f633db07ed1dd2101c" + integrity sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA== + dependencies: + tslib "2.3.0" + zrender "5.6.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.5.4: + version "1.5.18" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz#5fe62b9d21efbcfa26571066502d94f3ed97e495" + integrity sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +enhanced-resolve@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + integrity sha512-ZaAux1rigq1e2nQrztHn4h2ugvpzZxs64qneNah+8Mh/K0CRqJFJc+UoXnUsq+1yX+DmQFPPdVqboKAJ89e0Iw== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + +enhanced-resolve@^5.17.0, enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +entities@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +envinfo@^7.7.3: + version "7.13.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" + integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== + +errno@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-compat-utils@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" + integrity sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q== + dependencies: + semver "^7.5.4" + +eslint-config-standard@17.1.0, eslint-config-standard@^17.0.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" + integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== + +eslint-config-tencent@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/eslint-config-tencent/-/eslint-config-tencent-1.0.4.tgz#3196811af942202350c57f67ef0b6e81eaf9bcd3" + integrity sha512-h8r5f4iUdF5RyfIhOA+KXVAokltyUs4sGnYrzbY6bSZvUzYS282C2s4z3AYA8DCBfDaKPtUsDBOF+tEy38wTyA== + dependencies: + "@babel/eslint-parser" "^7.14.5" + eslint-plugin-chalk "^1.0.0" + eslint-plugin-import "^2.23.4" + +eslint-import-resolver-custom-alias@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-custom-alias/-/eslint-import-resolver-custom-alias-1.3.2.tgz#e509097e87de7a10b8c205c24644c3eb3fdf03c4" + integrity sha512-wBPcZA2k6/IXaT8FsLMyiyVSG6WVEuaYIAbeKLXeGwr523BmeB9lKAAoLJWSqp3txsnU4gpkgD2x1q6K8k0uDQ== + dependencies: + glob-parent "^6.0.2" + resolve "^1.22.2" + +eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz#b99b211ca4318243f09661fae088f373ad5243c4" + integrity sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ== + dependencies: + debug "^3.2.7" + +eslint-plugin-chalk@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-chalk/-/eslint-plugin-chalk-1.0.0.tgz#5de52a1ce36e787ae9b15ea501a18573ba3d42bd" + integrity sha512-FTi6Wi5dSrkXEVHHojRwyRZZnNFXGnTytuWlrJ3P9HkmMZWTZC3vUaDpyb6jNkumdlrqK0FN8eI3XJG70FwBNA== + dependencies: + chalk "^4.1.2" + +eslint-plugin-es-x@^7.5.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" + integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.11.0" + eslint-compat-utils "^0.5.1" + +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-es@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9" + integrity sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-html@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-8.1.1.tgz#504444f39e3ccb64502ac68dc6c534567aa56492" + integrity sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw== + dependencies: + htmlparser2 "^9.1.0" + +eslint-plugin-import@2.30.0, eslint-plugin-import@^2.23.4, eslint-plugin-import@^2.26.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz#21ceea0fc462657195989dd780e50c92fe95f449" + integrity sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw== + dependencies: + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.9.0" + hasown "^2.0.2" + is-core-module "^2.15.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-n@17.10.2: + version "17.10.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.10.2.tgz#16d8d7d0b1dc076c03513bfea096f8ce1b0bcca8" + integrity sha512-e+s4eAf5NtJaxPhTNu3qMO0Iz40WANS93w9LQgYcvuljgvDmWi/a3rh+OrNyMHeng6aOWGJO0rCg5lH4zi8yTw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + enhanced-resolve "^5.17.0" + eslint-plugin-es-x "^7.5.0" + get-tsconfig "^4.7.0" + globals "^15.8.0" + ignore "^5.2.4" + minimatch "^9.0.5" + semver "^7.5.3" + +eslint-plugin-n@^15.2.4: + version "15.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz#e29221d8f5174f84d18f2eb94765f2eeea033b90" + integrity sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q== + dependencies: + builtins "^5.0.1" + eslint-plugin-es "^4.1.0" + eslint-utils "^3.0.0" + ignore "^5.1.1" + is-core-module "^2.11.0" + minimatch "^3.1.2" + resolve "^1.22.1" + semver "^7.3.8" + +eslint-plugin-node@11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== + +eslint-plugin-promise@^6.0.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz#acd3fd7d55cead7a10f92cf698f36c0aafcd717a" + integrity sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ== + +eslint-plugin-simple-import-sort@12.1.1: + version "12.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz#e64bfdaf91c5b98a298619aa634a9f7aa43b709e" + integrity sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA== + +eslint-plugin-vue@9.28.0: + version "9.28.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz#e4412f0c1024bafd15ffeaa6f76f4c99152e2765" + integrity sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + globals "^13.24.0" + natural-compare "^1.4.0" + nth-check "^2.1.1" + postcss-selector-parser "^6.0.15" + semver "^7.6.3" + vue-eslint-parser "^9.4.3" + xml-name-validator "^4.0.0" + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1, eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-webpack-plugin@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-4.2.0.tgz#41f54b25379908eb9eca8645bc997c90cfdbd34e" + integrity sha512-rsfpFQ01AWQbqtjgPRr2usVRxhWDuG0YDYcG8DJOteD3EFnpeuYuOwk0PQiN7PRBTqS6ElNdtPZPggj8If9WnA== + dependencies: + "@types/eslint" "^8.56.10" + jest-worker "^29.7.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + schema-utils "^4.2.0" + +eslint@8.53.0: + version "8.53.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.53.0.tgz#14f2c8244298fcae1f46945459577413ba2697ce" + integrity sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.3" + "@eslint/js" "8.53.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.3.1, espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.0, esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +express@^4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9, fast-glob@^3.3.1, fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + +fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +figlet@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.7.0.tgz#46903a04603fd19c3e380358418bb2703587a72e" + integrity sha512-gO8l3wvqo0V7wEFLXPbkX83b7MVjRrk1oRLfYlZXol8nEpb/ON9pcKLI4qpBv5YtOTfrINtqb7b40iYY2FTWFg== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-entry-cache@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-7.0.2.tgz#2d61bb70ba89b9548e3035b7c9173fe91deafff0" + integrity sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g== + dependencies: + flat-cache "^3.2.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" + integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== + dependencies: + common-path-prefix "^3.0.0" + pkg-dir "^7.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== + dependencies: + locate-path "^7.1.0" + path-exists "^5.0.0" + +flat-cache@^3.0.4, flat-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.0.0, follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +get-tsconfig@^4.7.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.0.tgz#125dc13a316f61650a12b20c97c11b8fd996fedd" + integrity sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw== + dependencies: + resolve-pkg-maps "^1.0.0" + +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1, glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.1.1, glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0, globals@^13.24.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globals@^15.8.0: + version "15.9.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.9.0.tgz#e9de01771091ffbc37db5714dab484f9f69ff399" + integrity sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA== + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^14.0.0: + version "14.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.2.tgz#06554a54ccfe9264e5a9ff8eded46aa1e306482f" + integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg== + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw== + dependencies: + delegate "^3.1.2" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hash-sum@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" + integrity sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA== + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +highlight.js@11.10.0: + version "11.10.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92" + integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ== + +highlight.js@11.9.0: + version "11.9.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0" + integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw== + +highlight.js@^10.7.2: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +hogan.js@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + integrity sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg== + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.4.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== + +html-loader@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-5.1.0.tgz#85c13e0abc3b5f3aa6e7f664eee6e44d00718d95" + integrity sha512-Jb3xwDbsm0W3qlXrCZwcYqYGnYz55hb6aoKQTlzyZPXsPpi6tHXzAfqalecglMQgNvtEfxrCQPaKT90Irt5XDA== + dependencies: + html-minifier-terser "^7.2.0" + parse5 "^7.1.2" + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-minifier-terser@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz#18752e23a2f0ed4b0f550f217bb41693e975b942" + integrity sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA== + dependencies: + camel-case "^4.1.2" + clean-css "~5.3.2" + commander "^10.0.0" + entities "^4.4.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.15.1" + +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" + integrity sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g== + +html-tags@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +html-webpack-plugin@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" + integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +html2canvas@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543" + integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== + dependencies: + css-line-break "^2.1.0" + text-segmentation "^1.0.3" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +htmlparser2@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + entities "^4.4.0" + +htmlparser2@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" + integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.5.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-nan@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-network-error@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.5.0, jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jiti@^1.20.0: + version "1.21.6" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" + integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== + +js-base64@3.7.7: + version "3.7.7" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" + integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== + +js-cookie@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1" + integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-formatter-js@^2.3.4: + version "2.5.17" + resolved "https://registry.yarnpkg.com/json-formatter-js/-/json-formatter-js-2.5.17.tgz#162c47c18de71de2f23ab9b95c3b7c329cbd7975" + integrity sha512-HJr7mwjPYIysd504YVTcGrSFlXGhQYWDguM+tx5JgGLkoIdh88h8DsfdN/oFt3ouGrEIwaY+dQdxczOLL/VotQ== + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.1, json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2, kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +known-css-properties@^0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.29.0.tgz#e8ba024fb03886f23cb882e806929f32d814158f" + integrity sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ== + +launch-editor@^2.6.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.9.1.tgz#253f173bd441e342d4344b4dae58291abb425047" + integrity sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +linkify-it@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" + integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== + dependencies: + uc.micro "^1.0.1" + +loader-runner@^4.1.0, loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" + integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + +lodash-webpack-plugin@0.11.6: + version "0.11.6" + resolved "https://registry.yarnpkg.com/lodash-webpack-plugin/-/lodash-webpack-plugin-0.11.6.tgz#8204c6b78beb62ce5211217dfe783c21557ecd33" + integrity sha512-nsHN/+IxZK/C425vGC8pAxkKJ8KQH2+NJnhDul14zYNWr6HJcA95w+oRR7Cp0oZpOdMplDZXmjVROp8prPk7ig== + dependencies: + lodash "^4.17.20" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== + +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^4.1.2: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + +markdown-it@^13.0.1: + version "13.0.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +markdown-loader@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/markdown-loader/-/markdown-loader-8.0.0.tgz#c1773c6bcdd366af37aedd662b9679ec18348c95" + integrity sha512-dxrR3WhK/hERbStPFb/yeNdEeWCKa2qUDdXiq3VTruBUWufOtERX04X0K44K4dnlN2i9pjSEzYIQJ3LjH0xkEw== + dependencies: + marked "^4.0.12" + +marked-gfm-heading-id@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/marked-gfm-heading-id/-/marked-gfm-heading-id-3.1.3.tgz#7bcfea85901843baf214b96ccc4ff67581c972a9" + integrity sha512-A0cRU4PCueX/5m8VE4mT8uTQ36l3xMYRojz3Eqnk4BmUFZ0T+9Xhn2KvHcANP4qbhfOeuMrWJCTQbASIBR5xeg== + dependencies: + github-slugger "^2.0.0" + +marked-mangle@1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/marked-mangle/-/marked-mangle-1.1.9.tgz#ee7a4a1e318a47c722c905e6472f346c6f7a08a6" + integrity sha512-eLTXr1xQzba/WZp/trPS0HkR9W02ifasH6IWPrBv++eO2m8POiwV4muQ6Tof2C5Fhdo3z8ggXs6VGw1f931Vsg== + +marked@4.2.12: + version "4.2.12" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5" + integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw== + +marked@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/marked/-/marked-5.1.1.tgz#40b3963bb9da225314f746d5012ba7e34942f636" + integrity sha512-bTmmGdEINWmOMDjnPWDxGPQ4qkDLeYorpYbEtFOXzOruTwUE671q4Guiuchn4N8h/v6NGd7916kXsm3Iz4iUSg== + +marked@^4.0.12: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + +mathml-tag-names@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" + integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== + +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^4.6.0: + version "4.11.1" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.11.1.tgz#9c9c8e65bf8ac72c0db8d0fbbbe29248cf51d56a" + integrity sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ== + dependencies: + "@jsonjoy.com/json-pack" "^1.0.3" + "@jsonjoy.com/util" "^1.3.0" + tree-dump "^1.0.1" + tslib "^2.0.0" + +memory-fs@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^10.1.5: + version "10.1.5" + resolved "https://registry.yarnpkg.com/meow/-/meow-10.1.5.tgz#be52a1d87b5f5698602b0f32875ee5940904aa7f" + integrity sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw== + dependencies: + "@types/minimist" "^1.2.2" + camelcase-keys "^7.0.0" + decamelize "^5.0.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.2" + read-pkg-up "^8.0.0" + redent "^4.0.0" + trim-newlines "^4.0.2" + type-fest "^1.2.2" + yargs-parser "^20.2.9" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +"mime-db@>= 1.43.0 < 2": + version "1.53.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== + +min-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +mini-css-extract-plugin@2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.1.tgz#4d184f12ce90582e983ccef0f6f9db637b4be758" + integrity sha512-+Vyi+GCCOHnrJ2VPS+6aPoXN2k2jgUzDRhTFLjjTBn23qyXJXkjUWQgTL+mXpF5/A8ixLdCc6kWsoeOjKGejKQ== + dependencies: + schema-utils "^4.0.0" + tapable "^2.2.1" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimatch@~3.0.4: + version "3.0.8" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" + integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== + dependencies: + brace-expansion "^1.1.7" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew== + +moment-timezone@^0.5.25: + version "0.5.45" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" + integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== + dependencies: + moment "^2.29.4" + +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +node-xlsx@0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/node-xlsx/-/node-xlsx-0.24.0.tgz#a6a365acb18ad37c66c2b254b6ebe0c22dc9dc6f" + integrity sha512-1olwK48XK9nXZsyH/FCltvGrQYvXXZuxVitxXXv2GIuRm51aBi1+5KwR4rWM4KeO61sFU+00913WLZTD+AcXEg== + dependencies: + xlsx "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" + +nopt@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== + dependencies: + abbrev "1" + +normalize-package-data@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +nth-check@^2.0.1, nth-check@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + +object.groupby@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + +object.values@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1, on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +open@^10.0.3: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + +p-retry@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" + integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== + dependencies: + "@types/retry" "0.12.2" + is-network-error "^1.0.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" + integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-dir@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" + integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== + dependencies: + find-up "^6.3.0" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-advanced-variables@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-advanced-variables/-/postcss-advanced-variables-4.0.0.tgz#502724da72156c7697f0f53ca7cece275ecd8239" + integrity sha512-ooRwPl53XDEfZtc52BimJFwTKFPqeJGmSUuX90ZsN+ZTP9ViTsyLbRcD0gwtEw7su0o6P71ps7gpnOa4eGkx4g== + dependencies: + "@csstools/sass-import-resolve" "^1.0.0" + +postcss-atroot@0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/postcss-atroot/-/postcss-atroot-0.2.4.tgz#0fdf6504e2dbd727bd924547e45c408b99f3d939" + integrity sha512-g9Otw/Z9dAxZKNHpnyWyKRf1uMqO25Q5tDOZa8pqjWXszkOz83xi1f8fHxVgUKCEaW9sn3jyhftiXe+a60cTzg== + +postcss-attribute-case-insensitive@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-6.0.3.tgz#d118023911a768dfccfc0b0147f5ff06d8485806" + integrity sha512-KHkmCILThWBRtg+Jn1owTnHPnFit4OkqS+eKiGEOPIGke54DCeYGJ6r0Fx/HjfE9M9kznApCLcU0DvnPchazMQ== + dependencies: + postcss-selector-parser "^6.0.13" + +postcss-calc@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-10.0.2.tgz#15f01635a27b9d38913a98c4ef2877f5b715b439" + integrity sha512-DT/Wwm6fCKgpYVI7ZEWuPJ4az8hiEHtCUeYjZXqU7Ou4QqYh1Df2yCQ7Ca6N7xqKPFkxN3fhf+u9KSoOCJNAjg== + dependencies: + postcss-selector-parser "^6.1.2" + postcss-value-parser "^4.2.0" + +postcss-clamp@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-clamp/-/postcss-clamp-4.1.0.tgz#7263e95abadd8c2ba1bd911b0b5a5c9c93e02363" + integrity sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-functional-notation@^6.0.8: + version "6.0.14" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-6.0.14.tgz#958d8fc434fafbb15ebc7964053f19d366773078" + integrity sha512-dNUX+UH4dAozZ8uMHZ3CtCNYw8fyFAmqqdcyxMr7PEdM9jLXV19YscoYO0F25KqZYhmtWKQ+4tKrIZQrwzwg7A== + dependencies: + "@csstools/css-color-parser" "^2.0.4" + "@csstools/css-parser-algorithms" "^2.7.1" + "@csstools/css-tokenizer" "^2.4.1" + "@csstools/postcss-progressive-custom-properties" "^3.3.0" + "@csstools/utilities" "^1.0.0" + +postcss-color-hex-alpha@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-9.0.4.tgz#f455902fb222453b2eb9699dfa9fc17a9c056f1e" + integrity sha512-XQZm4q4fNFqVCYMGPiBjcqDhuG7Ey2xrl99AnDJMyr5eDASsAGalndVgHZF8i97VFNy1GQeZc4q2ydagGmhelQ== + dependencies: + "@csstools/utilities" "^1.0.0" + postcss-value-parser "^4.2.0" + +postcss-color-rebeccapurple@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-9.0.3.tgz#63e14d9b9ab196e62e3491606a2b77a9531a6825" + integrity sha512-ruBqzEFDYHrcVq3FnW3XHgwRqVMrtEPLBtD7K2YmsLKVc2jbkxzzNEctJKsPCpDZ+LeMHLKRDoSShVefGc+CkQ== + dependencies: + "@csstools/utilities" "^1.0.0" + postcss-value-parser "^4.2.0" + +postcss-colormin@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-7.0.2.tgz#6f3c53c13158168669f45adc3926f35cb240ef8e" + integrity sha512-YntRXNngcvEvDbEjTdRWGU606eZvB5prmHG4BF0yLmVpamXbpsRJzevyy6MZVyuecgzI2AWAlvFi8DAeCqwpvA== + dependencies: + browserslist "^4.23.3" + caniuse-api "^3.0.0" + colord "^2.9.3" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-7.0.4.tgz#fc13ecedded6365f3c794b502dbcf77d298da12c" + integrity sha512-e2LSXPqEHVW6aoGbjV9RsSSNDO3A0rZLCBxN24zvxF25WknMPpX8Dm9UxxThyEbaytzggRuZxaGXqaOhxQ514Q== + dependencies: + browserslist "^4.23.3" + postcss-value-parser "^4.2.0" + +postcss-custom-media@^10.0.4: + version "10.0.8" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-10.0.8.tgz#0b84916522eb1e8a4b9e3ecd2bce292844cd7323" + integrity sha512-V1KgPcmvlGdxTel4/CyQtBJEFhMVpEmRGFrnVtgfGIHj5PJX9vO36eFBxKBeJn+aCDTed70cc+98Mz3J/uVdGQ== + dependencies: + "@csstools/cascade-layer-name-parser" "^1.0.13" + "@csstools/css-parser-algorithms" "^2.7.1" + "@csstools/css-tokenizer" "^2.4.1" + "@csstools/media-query-list-parser" "^2.1.13" + +postcss-custom-properties@^13.3.6: + version "13.3.12" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-13.3.12.tgz#e21960c7d13aed960b28236412d4da67f75317b0" + integrity sha512-oPn/OVqONB2ZLNqN185LDyaVByELAA/u3l2CS2TS16x2j2XsmV4kd8U49+TMxmUsEU9d8fB/I10E6U7kB0L1BA== + dependencies: + "@csstools/cascade-layer-name-parser" "^1.0.13" + "@csstools/css-parser-algorithms" "^2.7.1" + "@csstools/css-tokenizer" "^2.4.1" + "@csstools/utilities" "^1.0.0" + postcss-value-parser "^4.2.0" + +postcss-custom-selectors@^7.1.8: + version "7.1.12" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-7.1.12.tgz#4d1bac2469003aad3aa3d73481a1b7a45290852b" + integrity sha512-ctIoprBMJwByYMGjXG0F7IT2iMF2hnamQ+aWZETyBM0aAlyaYdVZTeUkk8RB+9h9wP+NdN3f01lfvKl2ZSqC0g== + dependencies: + "@csstools/cascade-layer-name-parser" "^1.0.13" + "@csstools/css-parser-algorithms" "^2.7.1" + "@csstools/css-tokenizer" "^2.4.1" + postcss-selector-parser "^6.1.0" + +postcss-dir-pseudo-class@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-8.0.1.tgz#b93755f52fb90215301b1d3ecb7c5e6416930a1e" + integrity sha512-uULohfWBBVoFiZXgsQA24JV6FdKIidQ+ZqxOouhWwdE+qJlALbkS5ScB43ZTjPK+xUZZhlaO/NjfCt5h4IKUfw== + dependencies: + postcss-selector-parser "^6.0.13" + +postcss-discard-comments@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-7.0.3.tgz#9c414e8ee99d3514ad06a3465ccc20ec1dbce780" + integrity sha512-q6fjd4WU4afNhWOA2WltHgCbkRhZPgQe7cXF74fuVB/ge4QbM9HEaOIzGSiMvM+g/cOsNAUGdf2JDzqA2F8iLA== + dependencies: + postcss-selector-parser "^6.1.2" + +postcss-discard-duplicates@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.1.tgz#f87f2fe47d8f01afb1e98361c1db3ce1e8afd1a3" + integrity sha512-oZA+v8Jkpu1ct/xbbrntHRsfLGuzoP+cpt0nJe5ED2FQF8n8bJtn7Bo28jSmBYwqgqnqkuSXJfSUEE7if4nClQ== + +postcss-discard-empty@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-7.0.0.tgz#218829d1ef0a5d5142dd62f0aa60e00e599d2033" + integrity sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA== + +postcss-discard-overridden@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-7.0.0.tgz#b123ea51e3d4e1d0a254cf71eaff1201926d319c" + integrity sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w== + +postcss-double-position-gradients@^5.0.6: + version "5.0.7" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-5.0.7.tgz#1a4841daf7ac04e94de4672282e8d02d1b3dd274" + integrity sha512-1xEhjV9u1s4l3iP5lRt1zvMjI/ya8492o9l/ivcxHhkO3nOz16moC4JpMxDUGrOs4R3hX+KWT7gKoV842cwRgg== + dependencies: + "@csstools/postcss-progressive-custom-properties" "^3.3.0" + "@csstools/utilities" "^1.0.0" + postcss-value-parser "^4.2.0" + +postcss-extend-rule@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-extend-rule/-/postcss-extend-rule-4.0.0.tgz#4a6a4c7d099c3acc9d896df05367866d13f3984b" + integrity sha512-3gjPWUDNYjkRjtcpoN8ppZRXG8vyAk4mYdkYOETacCkCLVguW5IpCXCO31cDk8SW2/rx0RogWcXm1Zu/EayDVg== + dependencies: + postcss-nesting "^10.1.2" + +postcss-focus-visible@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-9.0.1.tgz#eede1032ce86b3bb2556d93ca5df63c68dfc2559" + integrity sha512-N2VQ5uPz3Z9ZcqI5tmeholn4d+1H14fKXszpjogZIrFbhaq0zNAtq8sAnw6VLiqGbL8YBzsnu7K9bBkTqaRimQ== + dependencies: + postcss-selector-parser "^6.0.13" + +postcss-focus-within@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-8.0.1.tgz#524af4c7eabae35cb1efa220a7903016fcc897fa" + integrity sha512-NFU3xcY/xwNaapVb+1uJ4n23XImoC86JNwkY/uduytSl2s9Ekc2EpzmRR63+ExitnW3Mab3Fba/wRPCT5oDILA== + dependencies: + postcss-selector-parser "^6.0.13" + +postcss-font-variant@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz#efd59b4b7ea8bb06127f2d031bfbb7f24d32fa66" + integrity sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA== + +postcss-gap-properties@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-5.0.1.tgz#887b64655f42370b43f0ab266cc6dbabf504d276" + integrity sha512-k2z9Cnngc24c0KF4MtMuDdToROYqGMMUQGcE6V0odwjHyOHtaDBlLeRBV70y9/vF7KIbShrTRZ70JjsI1BZyWw== + +postcss-html@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-1.7.0.tgz#06c7408f9a1be3b89643c916d5dcd99fb1f069a0" + integrity sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw== + dependencies: + htmlparser2 "^8.0.0" + js-tokens "^9.0.0" + postcss "^8.4.0" + postcss-safe-parser "^6.0.0" + +postcss-image-set-function@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-6.0.3.tgz#84c5e32cc1085198f2cf4a786028dae8a2632bb2" + integrity sha512-i2bXrBYzfbRzFnm+pVuxVePSTCRiNmlfssGI4H0tJQvDue+yywXwUxe68VyzXs7cGtMaH6MCLY6IbCShrSroCw== + dependencies: + "@csstools/utilities" "^1.0.0" + postcss-value-parser "^4.2.0" + +postcss-import-webpack-resolver@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-import-webpack-resolver/-/postcss-import-webpack-resolver-1.0.1.tgz#4be0e9400b7609313e11258a17c3c4d7db186ac9" + integrity sha512-xTvskH2+FnBdpj6Q8JJABpQhDqYY9Y9eQ+Hcoyt4t3T5Gevx1Cja8wevKGUzGtu70rPY5G0RJTXyWXpYixV1ZA== + dependencies: + enhanced-resolve "^3.4.1" + +postcss-import@16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-16.1.0.tgz#258732175518129667fe1e2e2a05b19b5654b96a" + integrity sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-lab-function@^6.0.13: + version "6.0.19" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-6.0.19.tgz#09b04c016bfbacd8576988a73dc19c0fdbeae2c4" + integrity sha512-vwln/mgvFrotJuGV8GFhpAOu9iGf3pvTBr6dLPDmUcqVD5OsQpEFyQMAFTxSxWXGEzBj6ld4pZ/9GDfEpXvo0g== + dependencies: + "@csstools/css-color-parser" "^2.0.4" + "@csstools/css-parser-algorithms" "^2.7.1" + "@csstools/css-tokenizer" "^2.4.1" + "@csstools/postcss-progressive-custom-properties" "^3.3.0" + "@csstools/utilities" "^1.0.0" + +postcss-loader@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-8.1.1.tgz#2822589e7522927344954acb55bbf26e8b195dfe" + integrity sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ== + dependencies: + cosmiconfig "^9.0.0" + jiti "^1.20.0" + semver "^7.5.4" + +postcss-logical@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-7.0.1.tgz#a3121f6510591b195321b16e65fbe13b1cfd3115" + integrity sha512-8GwUQZE0ri0K0HJHkDv87XOLC8DE0msc+HoWLeKdtjDZEwpZ5xuK3QdV6FhmHSQW40LPkg43QzvATRAI3LsRkg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig== + +postcss-merge-longhand@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-7.0.4.tgz#a52d0662b4b29420f3b64a8d5b0ac5133d8db776" + integrity sha512-zer1KoZA54Q8RVHKOY5vMke0cCdNxMP3KBfDerjH/BYHh4nCIh+1Yy0t1pAEQF18ac/4z3OFclO+ZVH8azjR4A== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^7.0.4" + +postcss-merge-rules@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-7.0.4.tgz#648cc864d3121e6ec72c2a4f08df1cc801e60ce8" + integrity sha512-ZsaamiMVu7uBYsIdGtKJ64PkcQt6Pcpep/uO90EpLS3dxJi6OXamIobTYcImyXGoW0Wpugh7DSD3XzxZS9JCPg== + dependencies: + browserslist "^4.23.3" + caniuse-api "^3.0.0" + cssnano-utils "^5.0.0" + postcss-selector-parser "^6.1.2" + +postcss-minify-font-values@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-7.0.0.tgz#d16a75a2548e000779566b3568fc874ee5d0aa17" + integrity sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-7.0.0.tgz#f6d84456e6d49164a55d0e45bb1b1809c6cf0959" + integrity sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg== + dependencies: + colord "^2.9.3" + cssnano-utils "^5.0.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-7.0.2.tgz#264a76e25f202d8b5ca5290569c0e8c3ac599dfe" + integrity sha512-nyqVLu4MFl9df32zTsdcLqCFfE/z2+f8GE1KHPxWOAmegSo6lpV2GNy5XQvrzwbLmiU7d+fYay4cwto1oNdAaQ== + dependencies: + browserslist "^4.23.3" + cssnano-utils "^5.0.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-7.0.4.tgz#2b69c99ec48a1c223fce4840609d9c53340a11f5" + integrity sha512-JG55VADcNb4xFCf75hXkzc1rNeURhlo7ugf6JjiiKRfMsKlDzN9CXHZDyiG6x/zGchpjQS+UAgb1d4nqXqOpmA== + dependencies: + cssesc "^3.0.0" + postcss-selector-parser "^6.1.2" + +postcss-mixins@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/postcss-mixins/-/postcss-mixins-10.0.1.tgz#895ddf323a5a87e70d3b4a85e04ba879b23a8801" + integrity sha512-5+cI9r8L5ChegVsLM9pXa53Ft03Mt9xAq+kvzqfrUHGPCArVGOfUvmQK2CLP3XWWP2dqxDLQI+lIcXG+GTqOBQ== + dependencies: + fast-glob "^3.3.2" + postcss-js "^4.0.1" + postcss-simple-vars "^7.0.1" + sugarss "^4.0.1" + +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + +postcss-modules-local-by-default@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-nested@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== + dependencies: + postcss-selector-parser "^6.1.1" + +postcss-nesting@^10.1.2: + version "10.2.0" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.2.0.tgz#0b12ce0db8edfd2d8ae0aaf86427370b898890be" + integrity sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA== + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +postcss-nesting@^12.1.1: + version "12.1.5" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-12.1.5.tgz#e5e2dc1d63e6166c194da45aa28c04d4024db98f" + integrity sha512-N1NgI1PDCiAGWPTYrwqm8wpjv0bgDmkYHH72pNsqTCv9CObxjxftdYu6AKtGN+pnJa7FQjMm3v4sp8QJbFsYdQ== + dependencies: + "@csstools/selector-resolve-nested" "^1.1.0" + "@csstools/selector-specificity" "^3.1.1" + postcss-selector-parser "^6.1.0" + +postcss-normalize-charset@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-7.0.0.tgz#92244ae73c31bf8f8885d5f16ff69e857ac6c001" + integrity sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ== + +postcss-normalize-display-values@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.0.tgz#01fb50e5e97ef8935363629bea5a6d3b3aac1342" + integrity sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-7.0.0.tgz#4eebd7c9d3dde40c97b8047cad38124fc844c463" + integrity sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.0.tgz#0cb784655d5714d29bd3bda6dee2fb628aa7227b" + integrity sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-7.0.0.tgz#a119d3e63a9614570d8413d572fb9fc8c6a64e8c" + integrity sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.0.tgz#99d0ee8c4b23b7f4355fafb91385833b9b07108b" + integrity sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.2.tgz#095f8d36ea29adfdf494069c1de101112992a713" + integrity sha512-ztisabK5C/+ZWBdYC+Y9JCkp3M9qBv/XFvDtSw0d/XwfT3UaKeW/YTm/MD/QrPNxuecia46vkfEhewjwcYFjkg== + dependencies: + browserslist "^4.23.3" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-7.0.0.tgz#c88cb7cf8952d3ff631e4eba924e7b060ca802f6" + integrity sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.0.tgz#46b025f0bea72139ddee63015619b0c21cebd845" + integrity sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-opacity-percentage@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-2.0.0.tgz#c0a56060cd4586e3f954dbde1efffc2deed53002" + integrity sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ== + +postcss-ordered-values@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-7.0.1.tgz#8b4b5b8070ca7756bd49f07d5edf274b8f6782e0" + integrity sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw== + dependencies: + cssnano-utils "^5.0.0" + postcss-value-parser "^4.2.0" + +postcss-overflow-shorthand@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-5.0.1.tgz#c0a124edad4f7ad88109275a60510e1fb07ab833" + integrity sha512-XzjBYKLd1t6vHsaokMV9URBt2EwC9a7nDhpQpjoPk2HRTSQfokPfyAS/Q7AOrzUu6q+vp/GnrDBGuj/FCaRqrQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-page-break@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-3.0.4.tgz#7fbf741c233621622b68d435babfb70dd8c1ee5f" + integrity sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ== + +postcss-place@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-9.0.1.tgz#c08c46a94e639c1ee3457ac96d50c50a89bd6ac3" + integrity sha512-JfL+paQOgRQRMoYFc2f73pGuG/Aw3tt4vYMR6UA3cWVMxivviPTnMFnFTczUJOA4K2Zga6xgQVE+PcLs64WC8Q== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-preset-env@9.5.4: + version "9.5.4" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-9.5.4.tgz#ea01cc1eb404a9b80f3375a62b50d36d6f27415a" + integrity sha512-o/jOlJjhm4f6rI5q1f+4Og3tz1cjaO50er9ndk7ZdcXHjWOH49kMAhqDC/nQifypQkOAiAmF46dPt3pZM+Cwbg== + dependencies: + "@csstools/postcss-cascade-layers" "^4.0.4" + "@csstools/postcss-color-function" "^3.0.13" + "@csstools/postcss-color-mix-function" "^2.0.13" + "@csstools/postcss-exponential-functions" "^1.0.5" + "@csstools/postcss-font-format-keywords" "^3.0.2" + "@csstools/postcss-gamut-mapping" "^1.0.6" + "@csstools/postcss-gradients-interpolation-method" "^4.0.14" + "@csstools/postcss-hwb-function" "^3.0.12" + "@csstools/postcss-ic-unit" "^3.0.6" + "@csstools/postcss-initial" "^1.0.1" + "@csstools/postcss-is-pseudo-class" "^4.0.6" + "@csstools/postcss-light-dark-function" "^1.0.3" + "@csstools/postcss-logical-float-and-clear" "^2.0.1" + "@csstools/postcss-logical-overflow" "^1.0.1" + "@csstools/postcss-logical-overscroll-behavior" "^1.0.1" + "@csstools/postcss-logical-resize" "^2.0.1" + "@csstools/postcss-logical-viewport-units" "^2.0.7" + "@csstools/postcss-media-minmax" "^1.1.4" + "@csstools/postcss-media-queries-aspect-ratio-number-values" "^2.0.7" + "@csstools/postcss-nested-calc" "^3.0.2" + "@csstools/postcss-normalize-display-values" "^3.0.2" + "@csstools/postcss-oklab-function" "^3.0.13" + "@csstools/postcss-progressive-custom-properties" "^3.2.0" + "@csstools/postcss-relative-color-syntax" "^2.0.13" + "@csstools/postcss-scope-pseudo-class" "^3.0.1" + "@csstools/postcss-stepped-value-functions" "^3.0.6" + "@csstools/postcss-text-decoration-shorthand" "^3.0.5" + "@csstools/postcss-trigonometric-functions" "^3.0.6" + "@csstools/postcss-unset-value" "^3.0.1" + autoprefixer "^10.4.19" + browserslist "^4.22.3" + css-blank-pseudo "^6.0.1" + css-has-pseudo "^6.0.3" + css-prefers-color-scheme "^9.0.1" + cssdb "^8.0.0" + postcss-attribute-case-insensitive "^6.0.3" + postcss-clamp "^4.1.0" + postcss-color-functional-notation "^6.0.8" + postcss-color-hex-alpha "^9.0.4" + postcss-color-rebeccapurple "^9.0.3" + postcss-custom-media "^10.0.4" + postcss-custom-properties "^13.3.6" + postcss-custom-selectors "^7.1.8" + postcss-dir-pseudo-class "^8.0.1" + postcss-double-position-gradients "^5.0.6" + postcss-focus-visible "^9.0.1" + postcss-focus-within "^8.0.1" + postcss-font-variant "^5.0.0" + postcss-gap-properties "^5.0.1" + postcss-image-set-function "^6.0.3" + postcss-lab-function "^6.0.13" + postcss-logical "^7.0.1" + postcss-nesting "^12.1.1" + postcss-opacity-percentage "^2.0.0" + postcss-overflow-shorthand "^5.0.1" + postcss-page-break "^3.0.4" + postcss-place "^9.0.1" + postcss-pseudo-class-any-link "^9.0.1" + postcss-replace-overflow-wrap "^4.0.0" + postcss-selector-not "^7.0.2" + +postcss-property-lookup@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-property-lookup/-/postcss-property-lookup-3.0.0.tgz#ac56c12220023cde055359f4246b326ea50fa361" + integrity sha512-Rkl/LttzMkhWCpAbqB5jZaVin1wVlHClit6bU8Wbd8AN1uBevyEfJGQZIsyqZc+OEEG1dZPnCJnvNuUrCpMB7w== + dependencies: + tcomb "^3.2.21" + +postcss-pseudo-class-any-link@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-9.0.2.tgz#e436a7db1421f8a347fff3f19951a27d4e791987" + integrity sha512-HFSsxIqQ9nA27ahyfH37cRWGk3SYyQLpk0LiWw/UGMV4VKT5YG2ONee4Pz/oFesnK0dn2AjcyequDbIjKJgB0g== + dependencies: + postcss-selector-parser "^6.0.13" + +postcss-reduce-initial@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-7.0.2.tgz#3dc085347a5943e18547d4b0aa5bd4ff5a93b2c5" + integrity sha512-pOnu9zqQww7dEKf62Nuju6JgsW2V0KRNBHxeKohU+JkHd/GAH5uvoObqFLqkeB2n20mr6yrlWDvo5UBU5GnkfA== + dependencies: + browserslist "^4.23.3" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.0.tgz#0386080a14e5faad9f8eda33375b79fe7c4f9677" + integrity sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-replace-overflow-wrap@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz#d2df6bed10b477bf9c52fab28c568b4b29ca4319" + integrity sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw== + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz#3d84dec809f34de020372c41b039956966896686" + integrity sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw== + +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== + +postcss-selector-not@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-7.0.2.tgz#f9184c7770be5dcb4abd7efa3610a15fbd2f0b31" + integrity sha512-/SSxf/90Obye49VZIfc0ls4H0P6i6V1iHv0pzZH8SdgvZOPFkF37ef1r5cyWcMflJSFJ5bfuoluTnFnBBFiuSA== + dependencies: + postcss-selector-parser "^6.0.13" + +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.13, postcss-selector-parser@^6.0.15, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.1.0, postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-simple-vars@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz#836b3097a54dcd13dbd3c36a5dbdd512fad2954c" + integrity sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A== + +postcss-sorting@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/postcss-sorting/-/postcss-sorting-8.0.2.tgz#6393385ece272baf74bee9820fb1b58098e4eeca" + integrity sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q== + +postcss-svgo@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-7.0.1.tgz#2b63571d8e9568384df334bac9917baff4d23f58" + integrity sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^3.3.2" + +postcss-syntax@0.36.2: + version "0.36.2" + resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c" + integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w== + +postcss-unique-selectors@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-7.0.3.tgz#483fc11215b23d517d5d9bbe5833d9915619ca33" + integrity sha512-J+58u5Ic5T1QjP/LDV9g3Cx4CNOgB5vz+kM6+OxHHhFACdcDeKhBXjQmB7fnIZM12YSTvsL0Opwco83DmacW2g== + dependencies: + postcss-selector-parser "^6.1.2" + +postcss-url@10.1.3: + version "10.1.3" + resolved "https://registry.yarnpkg.com/postcss-url/-/postcss-url-10.1.3.tgz#54120cc910309e2475ec05c2cfa8f8a2deafdf1e" + integrity sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw== + dependencies: + make-dir "~3.1.0" + mime "~2.5.2" + minimatch "~3.0.4" + xxhashjs "~0.2.2" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@8.4.44: + version "8.4.44" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.44.tgz#d56834ef6508610ba224bb22b2457b2169ed0480" + integrity sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.1" + source-map-js "^1.2.0" + +postcss@^7.0.36: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.4.0, postcss@^8.4.14, postcss@^8.4.28, postcss@^8.4.32, postcss@^8.4.33, postcss@^8.4.38: + version "8.4.45" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.45.tgz#538d13d89a16ef71edbf75d895284ae06b79e603" + integrity sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.1" + source-map-js "^1.2.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +"prettier@^1.18.2 || ^2.0.0": + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +read-pkg-up@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-8.0.0.tgz#72f595b65e66110f43b052dd9af4de6b10534670" + integrity sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ== + dependencies: + find-up "^5.0.0" + read-pkg "^6.0.0" + type-fest "^1.0.1" + +read-pkg@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-6.0.0.tgz#a67a7d6a1c2b0c3cd6aa2ea521f40c458a4a504c" + integrity sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^3.0.2" + parse-json "^5.2.0" + type-fest "^1.0.1" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +redent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-4.0.0.tgz#0c0ba7caabb24257ab3bb7a4fd95dd1d5c5681f9" + integrity sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag== + dependencies: + indent-string "^5.0.0" + strip-indent "^4.0.0" + +regenerate-unicode-properties@^10.1.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +regexpp@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-package-name@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" + integrity sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve@^1.1.7, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0, schema-utils@^4.0.1, schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA== + +selfsigned@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +semver@^6.0.0, semver@^6.1.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.0.0, semver@^7.3.4, semver@^7.3.6, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +sortablejs@1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290" + integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A== + +source-map-js@^1.0.1, source-map-js@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spark-md5@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" + integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.20" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +speed-measure-webpack-plugin@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.5.0.tgz#caf2c5bee24ab66c1c7c30e8daa7910497f7681a" + integrity sha512-Re0wX5CtM6gW7bZA64ONOfEPEhwbiSF/vz6e2GvadjuaPrQcHTQdRGsD8+BE7iUOysXH8tIenkPCQBEcspXsNg== + dependencies: + chalk "^4.1.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-4.0.0.tgz#b41379433dd06f5eae805e21d631e07ee670d853" + integrity sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA== + dependencies: + min-indent "^1.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5" + integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA== + +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== + +stylehacks@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-7.0.4.tgz#9c21f7374f4bccc0082412b859b3c89d77d3277c" + integrity sha512-i4zfNrGMt9SB4xRK9L83rlsFCgdGANfeDAYacO1pkqcE7cRHPdWHwnKZVz7WY17Veq/FvyYsRAU++Ga+qDFIww== + dependencies: + browserslist "^4.23.3" + postcss-selector-parser "^6.1.2" + +stylelint-config-recommended@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-13.0.0.tgz#c48a358cc46b629ea01f22db60b351f703e00597" + integrity sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ== + +stylelint-config-standard@34.0.0: + version "34.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-34.0.0.tgz#309f3c48118a02aae262230c174282e40e766cf4" + integrity sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ== + dependencies: + stylelint-config-recommended "^13.0.0" + +stylelint-order@6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/stylelint-order/-/stylelint-order-6.0.4.tgz#3e80d876c61a98d2640de181433686f24284748b" + integrity sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA== + dependencies: + postcss "^8.4.32" + postcss-sorting "^8.0.2" + +stylelint-scss@5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-5.3.1.tgz#7f0f5f06d0a2a3c515aa71d3a8de3548045e03e1" + integrity sha512-5I9ZDIm77BZrjOccma5WyW2nJEKjXDd4Ca8Kk+oBapSO4pewSlno3n+OyimcyVJJujQZkBN2D+xuMkIamSc6hA== + dependencies: + known-css-properties "^0.29.0" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" + +stylelint-webpack-plugin@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/stylelint-webpack-plugin/-/stylelint-webpack-plugin-4.1.1.tgz#d7f13f2f8610757ee23a510eca3a6b6bcb1a54e8" + integrity sha512-yOyd2AfrxfawxKDememazGVJX2vMq9o11E6HvBu4+SKvgK3ZulkjpYdI1muBTxItwoxH2UmfIZzQM+/M5V3kTQ== + dependencies: + globby "^11.1.0" + jest-worker "^29.5.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + +stylelint@15.11.0: + version "15.11.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.11.0.tgz#3ff8466f5f5c47362bc7c8c9d382741c58bc3292" + integrity sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw== + dependencies: + "@csstools/css-parser-algorithms" "^2.3.1" + "@csstools/css-tokenizer" "^2.2.0" + "@csstools/media-query-list-parser" "^2.1.4" + "@csstools/selector-specificity" "^3.0.0" + balanced-match "^2.0.0" + colord "^2.9.3" + cosmiconfig "^8.2.0" + css-functions-list "^3.2.1" + css-tree "^2.3.1" + debug "^4.3.4" + fast-glob "^3.3.1" + fastest-levenshtein "^1.0.16" + file-entry-cache "^7.0.0" + global-modules "^2.0.0" + globby "^11.1.0" + globjoin "^0.1.4" + html-tags "^3.3.1" + ignore "^5.2.4" + import-lazy "^4.0.0" + imurmurhash "^0.1.4" + is-plain-object "^5.0.0" + known-css-properties "^0.29.0" + mathml-tag-names "^2.1.3" + meow "^10.1.5" + micromatch "^4.0.5" + normalize-path "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.28" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^6.0.0" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" + resolve-from "^5.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + style-search "^0.1.0" + supports-hyperlinks "^3.0.0" + svg-tags "^1.0.0" + table "^6.8.1" + write-file-atomic "^5.0.1" + +sugarss@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-4.0.1.tgz#128a783ed71ee0fc3b489ce1f7d5a89bc1e24383" + integrity sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz#b56150ff0173baacc15f21956450b61f2b18d3ac" + integrity sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== + +svgo@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" + integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.0.0" + +table@^6.8.1: + version "6.8.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + +tapable@^0.2.7: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8" + integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tcomb@^3.2.21: + version "3.2.29" + resolved "https://registry.yarnpkg.com/tcomb/-/tcomb-3.2.29.tgz#32404fe9456d90c2cf4798682d37439f1ccc386c" + integrity sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ== + +terser-webpack-plugin@5.3.10, terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.10.0, terser@^5.15.1, terser@^5.26.0: + version "5.31.6" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.6.tgz#c63858a0f0703988d0266a82fcbf2d7ba76422b1" + integrity sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-segmentation@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" + integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw== + dependencies: + utrie "^1.0.2" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thingies@^1.20.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" + integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== + +thread-loader@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/thread-loader/-/thread-loader-4.0.2.tgz#f7b4484beab1f928818d8ecd96ef20c618d1fadc" + integrity sha512-UOk/KBydsQjh4Ja5kocxDUzhv11KYptHN/h8gdSwo6/MBkYrWqQua6K2qwlpXnCXS9c/uLs8F/JF8rpveF0+fA== + dependencies: + json-parse-better-errors "^1.0.2" + loader-runner "^4.1.0" + neo-async "^2.6.2" + schema-utils "^4.0.1" + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + +tinycolor2@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" + integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== + +tippy.js@6.3.7, tippy.js@^6.2.3: + version "6.3.7" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c" + integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ== + dependencies: + "@popperjs/core" "^2.9.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tree-dump@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.0.2.tgz#c460d5921caeb197bde71d0e9a7b479848c5b8ac" + integrity sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ== + +trim-newlines@^4.0.2: + version "4.1.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.1.1.tgz#28c88deb50ed10c7ba6dc2474421904a00139125" + integrity sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ== + +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + +tslib@^2.0.0, tslib@^2.0.3: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^1.0.1, type-fest@^1.2.1, type-fest@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +utrie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645" + integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw== + dependencies: + base64-arraybuffer "^1.0.2" + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vue-content-loader@0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/vue-content-loader/-/vue-content-loader-0.2.2.tgz#c093b656edac3f2a2e3215eaa198ba1852ad6b7b" + integrity sha512-8jcb0dJFiVAz7EPwpQjOd/GnswUiSDeKihEABkq/iAYxAI2MHSS7+VWlRblQDH3D1rm3Lewt7fDJoOpJKbYHjw== + dependencies: + babel-helper-vue-jsx-merge-props "^2.0.3" + +vue-eslint-parser@^9.4.3: + version "9.4.3" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz#9b04b22c71401f1e8bca9be7c3e3416a4bde76a8" + integrity sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg== + dependencies: + debug "^4.3.4" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.6" + +vue-hot-reload-api@^2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" + integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== + +vue-i18n@8.14.1: + version "8.14.1" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.14.1.tgz#0ca0a2742c14e0144481655157fffcc7cc313e50" + integrity sha512-uHzw5GTFyf/TmjJXveSl3L4CG61KI4lvhKOQvx8W4Y8P2LZ3v3l/qw4KRs1C6pWyjkfY9p0rezYNFO5YzMEQ8A== + +vue-loader@15.10.0: + version "15.10.0" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.10.0.tgz#2a12695c421a2a2cc2138f05a949d04ed086e38b" + integrity sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg== + dependencies: + "@vue/component-compiler-utils" "^3.1.0" + hash-sum "^1.0.2" + loader-utils "^1.1.0" + vue-hot-reload-api "^2.3.0" + vue-style-loader "^4.1.0" + +vue-progressbar@0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/vue-progressbar/-/vue-progressbar-0.7.5.tgz#414730892252b1e45582d4979dec93038e007f79" + integrity sha512-VeNG/inMsFbvdCTS7lJ1gjDAKSUZdqkhW9gS1tb0we1rqFKxR+BCEiVUgw5C84vODKb14M2GWHTw0WVdpoVg6g== + +vue-router@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.2.tgz#5f55e3f251970e36c3e8d88a7cd2d67a350ade5c" + integrity sha512-807gn82hTnjCYGrnF3eNmIw/dk7/GE4B5h69BlyCK9KHASwSloD1Sjcn06zg9fVG4fYH2DrsNBZkpLtb25WtaQ== + +vue-style-loader@4.1.3, vue-style-loader@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35" + integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg== + dependencies: + hash-sum "^1.0.2" + loader-utils "^1.0.2" + +vue-template-es2015-compiler@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" + integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== + +vue@2.7.8: + version "2.7.8" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.8.tgz#34e06553137611d8cecc4b0cd78b7a59f80b1299" + integrity sha512-ncwlZx5qOcn754bCu5/tS/IWPhXHopfit79cx+uIlLMyt3vCMGcXai5yCG5y+I6cDmEj4ukRYyZail9FTQh7lQ== + dependencies: + "@vue/compiler-sfc" "2.7.8" + csstype "^3.1.0" + +vuedraggable@2.24.3: + version "2.24.3" + resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19" + integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g== + dependencies: + sortablejs "1.10.2" + +vuex@3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71" + integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw== + +watchpack@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.2.tgz#2feeaed67412e7c33184e5a79ca738fbd38564da" + integrity sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webpack-cli@5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz#40e265a3d3d26795585cff8207630d3a8ff05877" + integrity sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA== + dependencies: + colorette "^2.0.10" + memfs "^4.6.0" + mime-types "^2.1.31" + on-finished "^2.4.1" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.1.0.tgz#8f44147402b4d8ab99bfeb9b6880daa1411064e5" + integrity sha512-aQpaN81X6tXie1FoOB7xlMfCsN19pSvRAeYUHOdFWOlhpQ/LlbfTqYwwmEDFV0h8GGuqmCmKmT+pxcUV/Nt2gQ== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" + ansi-html-community "^0.0.8" + bonjour-service "^1.2.1" + chokidar "^3.6.0" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + express "^4.19.2" + graceful-fs "^4.2.6" + html-entities "^2.4.0" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + schema-utils "^4.2.0" + selfsigned "^2.4.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^7.4.2" + ws "^8.18.0" + +webpack-merge@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" + integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.1" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.94.0: + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== + dependencies: + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.1" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0, wildcard@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + +ws@^8.18.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +"xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz": + version "0.20.2" + resolved "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz#0f64eeed3f1a46e64724620c3553f2dbd3cd2d7d" + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xss@1.0.15, xss@^1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a" + integrity sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg== + dependencies: + commander "^2.20.3" + cssfilter "0.0.10" + +xxhashjs@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8" + integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw== + dependencies: + cuint "^0.2.2" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== + +zrender@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.6.0.tgz#01325b0bb38332dd5e87a8dbee7336cafc0f4a5b" + integrity sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg== + dependencies: + tslib "2.3.0" From a08a72139f57bff76e4310290e5169c6e2a09549 Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Mon, 5 Aug 2024 10:28:13 +0800 Subject: [PATCH 072/115] =?UTF-8?q?feat:=20AI+JOB=20=E4=B8=80=E6=9C=9F=20#?= =?UTF-8?q?2995=20#=20Reviewed,=20transaction=20id:=2013969?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/domain/service/ai.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/domain/service/ai.js b/src/frontend/src/domain/service/ai.js index fdc82bf90f..e65a22247e 100644 --- a/src/frontend/src/domain/service/ai.js +++ b/src/frontend/src/domain/service/ai.js @@ -49,16 +49,19 @@ export default { fetchLatestChatHistoryList(params = {}) { return AiSource.getLatestChatHistoryList(params) .then(({ data }) => data.reduce((result, item) => { + if (item.userInput.content) { + result.push({ + type: 'user', + content: item.userInput.content, + status: '', + }); + } + result.push({ type: 'ai', content: item.aiAnswer.content, status: item.aiAnswer.errorCode === 0 ? 'success' : 'error', }); - result.push({ - type: 'user', - content: item.userInput.content, - status: '', - }); return result; }, [])); From ddfb04982797e44496661e090df2283667b60984 Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Mon, 5 Aug 2024 11:43:10 +0800 Subject: [PATCH 073/115] =?UTF-8?q?feat:=20AI+JOB=20=E4=B8=80=E6=9C=9F=20#?= =?UTF-8?q?2995=20#=20Reviewed,=20transaction=20id:=2014021?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/components/jb-ai/index.vue | 2 +- src/frontend/src/domain/service/ai.js | 2 +- src/frontend/src/domain/source/ai.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/components/jb-ai/index.vue b/src/frontend/src/components/jb-ai/index.vue index 4355944b2a..72d4d767db 100644 --- a/src/frontend/src/components/jb-ai/index.vue +++ b/src/frontend/src/components/jb-ai/index.vue @@ -103,7 +103,7 @@ }; const handleClear = () => { - AiService.fetchGeneraChat(); + AiService.deleteChatHistory(); messages.value = []; }; diff --git a/src/frontend/src/domain/service/ai.js b/src/frontend/src/domain/service/ai.js index e65a22247e..57071963dd 100644 --- a/src/frontend/src/domain/service/ai.js +++ b/src/frontend/src/domain/service/ai.js @@ -60,7 +60,7 @@ export default { result.push({ type: 'ai', content: item.aiAnswer.content, - status: item.aiAnswer.errorCode === 0 ? 'success' : 'error', + status: item.aiAnswer.errorCode === '0' ? 'success' : 'error', }); return result; diff --git a/src/frontend/src/domain/source/ai.js b/src/frontend/src/domain/source/ai.js index a1da7d1407..8857ee3558 100644 --- a/src/frontend/src/domain/source/ai.js +++ b/src/frontend/src/domain/source/ai.js @@ -64,7 +64,7 @@ class Ai extends ModuleBase { } deleteChatHistory() { - return Request.get(`${this.path}/clearChatHistory`); + return Request.delete(`${this.path}/clearChatHistory`); } } From 647342228debb4ebf8e62cc93744566e5ef22a30 Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Sun, 8 Sep 2024 17:17:56 +0800 Subject: [PATCH 074/115] =?UTF-8?q?feat:=20AI+JOB=20=E4=B8=80=E6=9C=9F=20#?= =?UTF-8?q?2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/.gitignore | 2 +- src/frontend/package.json | 71 ++++----- .../ace-editor/components/ai-tool.vue | 13 +- src/frontend/src/components/jb-ai/index.vue | 142 +++++++++++------- .../components/jb-ai/use-stream-content.js | 98 ++++++++++++ src/frontend/src/domain/service/ai.js | 15 +- src/frontend/src/domain/source/ai.js | 23 ++- src/frontend/src/utils/request/lib/request.js | 9 +- src/frontend/webpack.config.js | 1 + 9 files changed, 273 insertions(+), 101 deletions(-) create mode 100644 src/frontend/src/components/jb-ai/use-stream-content.js diff --git a/src/frontend/.gitignore b/src/frontend/.gitignore index 041fef9c36..bfe0114e0e 100644 --- a/src/frontend/.gitignore +++ b/src/frontend/.gitignore @@ -11,7 +11,7 @@ dist/ # Dependency directories node_modules/ - +yarn.lock # Typescript v1 declaration files typings/ diff --git a/src/frontend/package.json b/src/frontend/package.json index ea08044c01..38e4a4a25a 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "echo test", "dev": "webpack serve --hot --env development --env mode=development --config ./webpack.config.js", - "dev-external": "webpack serve --https --hot --env development --env mode=external --config ./webpack.config.js", + "dev-external": "webpack serve --server-type https --hot --env development --env mode=external --config ./webpack.config.js", "build": "webpack --env --config ./webpack.config.js", "lint:js": "eslint ./src --fix --ext .js --ext .vue", "lint:style": "stylelint --fix ./src/**/*.{vue,css} --custom-syntax" @@ -16,30 +16,31 @@ "dependencies": { "@blueking/ai-blueking": "0.2.7", "@blueking/ip-selector": "0.3.0-beta.33", - "@blueking/login-modal": "1.0.4", + "@blueking/login-modal": "1.0.5", "@blueking/notice-component-vue2": "2.0.0-beta.2", "@blueking/paas-login": "0.0.11", "@blueking/platform-config": "1.0.5", "@blueking/sub-saas": "0.0.0-beta.6", - "@blueking/user-selector": "1.0.12", + "@blueking/user-selector": "1.0.14", "@vue/babel-preset-jsx": "1.4.0", "@vue/composition-api": "1.7.2", - "ace-builds": "1.32.9", - "axios": "1.7.4", + "ace-builds": "1.36.2", + "axios": "1.7.7", "bk-magic-vue": "2.5.9-beta.35", - "core-js": "3.36.1", + "core-js": "3.38.1", "cron-parser-custom": "2.13.0", + "dayjs": "1.11.13", "diff": "5.2.0", "diff2html": "3.4.48", - "echarts": "5.5.0", - "highlight.js": "11.9.0", + "echarts": "5.5.1", + "highlight.js": "11.10.0", "html2canvas": "1.4.1", "js-base64": "3.7.7", "js-cookie": "3.0.5", "lodash": "4.17.21", "marked": "5.1.1", "marked-gfm-heading-id": "3.1.3", - "marked-mangle": "1.1.7", + "marked-mangle": "1.1.9", "tippy.js": "6.3.7", "vue": "2.7.8", "vue-content-loader": "0.2.2", @@ -51,55 +52,55 @@ "xss": "1.0.15" }, "devDependencies": { - "@babel/core": "7.24.4", - "@babel/eslint-parser": "7.24.1", + "@babel/core": "7.25.2", + "@babel/eslint-parser": "7.25.1", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/plugin-proposal-export-namespace-from": "7.18.9", "@babel/plugin-syntax-dynamic-import": "7.8.3", - "@babel/plugin-syntax-jsx": "7.24.1", - "@babel/plugin-transform-modules-commonjs": "7.24.1", - "@babel/plugin-transform-runtime": "7.24.3", - "@babel/preset-env": "7.24.4", + "@babel/plugin-syntax-jsx": "7.24.7", + "@babel/plugin-transform-modules-commonjs": "7.24.8", + "@babel/plugin-transform-runtime": "7.25.4", + "@babel/preset-env": "7.25.4", "@vue/babel-helper-vue-jsx-merge-props": "1.4.0", "@vue/babel-plugin-transform-vue-jsx": "1.4.0", "@vue/babel-sugar-inject-h": "1.4.0", "@vue/eslint-config-standard": "8.0.1", - "ajv": "8.12.0", + "ajv": "8.17.1", "babel-loader": "9.1.3", "babel-plugin-import-bk-magic-vue": "2.1.11", "babel-plugin-lodash": "3.3.4", "copy-webpack-plugin": "12.0.2", - "css-loader": "7.1.0", - "css-minimizer-webpack-plugin": "6.0.0", + "css-loader": "7.1.2", + "css-minimizer-webpack-plugin": "7.0.0", "dotenv": "16.4.5", "eslint": "8.53.0", "eslint-config-standard": "17.1.0", "eslint-config-tencent": "1.0.4", - "eslint-plugin-html": "8.0.0", - "eslint-plugin-import": "2.29.1", - "eslint-plugin-n": "17.1.0", + "eslint-plugin-html": "8.1.1", + "eslint-plugin-import": "2.30.0", + "eslint-plugin-n": "17.10.2", "eslint-plugin-node": "11.1.0", "eslint-plugin-promise": "6.1.1", - "eslint-plugin-simple-import-sort": "12.0.0", - "eslint-plugin-vue": "9.24.1", - "eslint-webpack-plugin": "4.1.0", + "eslint-plugin-simple-import-sort": "12.1.1", + "eslint-plugin-vue": "9.28.0", + "eslint-webpack-plugin": "4.2.0", "figlet": "1.7.0", - "html-loader": "5.0.0", + "html-loader": "5.1.0", "html-webpack-plugin": "5.6.0", "lodash-webpack-plugin": "0.11.6", "markdown-loader": "8.0.0", - "mini-css-extract-plugin": "2.8.1", - "node-xlsx": "0.23.0", - "postcss": "8.4.38", + "mini-css-extract-plugin": "2.9.1", + "node-xlsx": "0.24.0", + "postcss": "8.4.44", "postcss-advanced-variables": "4.0.0", "postcss-atroot": "0.2.4", "postcss-extend-rule": "4.0.0", - "postcss-html": "1.6.0", + "postcss-html": "1.7.0", "postcss-import": "16.1.0", "postcss-import-webpack-resolver": "~1.0.1", "postcss-loader": "8.1.1", - "postcss-mixins": "10.0.0", - "postcss-nested": "6.0.1", + "postcss-mixins": "10.0.1", + "postcss-nested": "6.2.0", "postcss-preset-env": "9.5.4", "postcss-property-lookup": "3.0.0", "postcss-syntax": "0.36.2", @@ -108,17 +109,17 @@ "style-loader": "4.0.0", "stylelint": "15.11.0", "stylelint-config-standard": "34.0.0", - "stylelint-order": "6.0.3", + "stylelint-order": "6.0.4", "stylelint-scss": "5.3.1", "stylelint-webpack-plugin": "4.1.1", "terser-webpack-plugin": "5.3.10", "thread-loader": "4.0.2", "vue-loader": "15.10.0", "vue-style-loader": "4.1.3", - "webpack": "5.91.0", + "webpack": "5.94.0", "webpack-cli": "5.1.4", - "webpack-dev-server": "5.0.4", - "webpack-merge": "5.10.0" + "webpack-dev-server": "5.1.0", + "webpack-merge": "6.0.1" }, "engines": { "node": ">= 16.6.0", diff --git a/src/frontend/src/components/ace-editor/components/ai-tool.vue b/src/frontend/src/components/ace-editor/components/ai-tool.vue index 217153f8cd..c3af07902c 100644 --- a/src/frontend/src/components/ace-editor/components/ai-tool.vue +++ b/src/frontend/src/components/ace-editor/components/ai-tool.vue @@ -1,7 +1,9 @@ diff --git a/src/frontend/src/components/jb-ai/use-stream-content.js b/src/frontend/src/components/jb-ai/use-stream-content.js new file mode 100644 index 0000000000..a32241ef6f --- /dev/null +++ b/src/frontend/src/components/jb-ai/use-stream-content.js @@ -0,0 +1,98 @@ +import { onMounted, ref } from 'vue'; + +import AiService from '@service/ai'; + +export default () => { + const loading = ref(false); + const messageList = ref([]); + + const apiPath = `${window.PROJECT_CONFIG.AJAX_URL_PREFIX}/job-analysis/web/ai/scope/${window.PROJECT_CONFIG.SCOPE_TYPE}/${window.PROJECT_CONFIG.SCOPE_ID}`; + + const fetchLatestChatHistoryList = () => { + loading.value = true; + AiService.fetchLatestChatHistoryList() + .then((result) => { + messageList.value = result; + }) + .finally(() => { + loading.value = false; + }); + }; + + const fetchContent = (recordId) => { + loading.value = true; + const latestChatMessage = { + role: 'assistant', + content: '内容正在生成中...', + status: 'loading', + }; + messageList.value.push(latestChatMessage); + + fetch(`${apiPath}/chatStream`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify({ + recordId, + }), + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(response => new Promise((resolve, reject) => { + const reader = response.body.getReader(); + const decoder = new TextDecoder('utf-8'); + + latestChatMessage.content = ''; + + let fragment = ''; + // 递归函数来读取数据 + const read = () => { + reader.read() + .then(({ done, value }) => { + if (done) { + latestChatMessage.status = 'success'; + return resolve(); + } + + // 解码二进制数据块并添加到内容字符串 + const chunk = `${fragment}${decoder.decode(value, { stream: true })}`; + + chunk.split('\n') + .forEach((item) => { + try { + latestChatMessage.content += (item ? JSON.parse(item).data.content : ''); + } catch { + fragment += item; + } + }); + + // 递归读取下一块数据 + read(); + }) + .catch(error => reject(error)); + }; + + // 开始读取流 + read(); + })) + .then(() => { + fetchLatestChatHistoryList(); + }) + .catch((error) => { + console.error('Fetch error:', error); + latestChatMessage.status = 'error'; + messageList.value = [...messageList.value]; + loading.value = false; + }); + }; + + onMounted(() => { + fetchLatestChatHistoryList(); + }); + + return { + loading, + messageList, + fetchContent, + }; +}; diff --git a/src/frontend/src/domain/service/ai.js b/src/frontend/src/domain/service/ai.js index 57071963dd..3bdb79c884 100644 --- a/src/frontend/src/domain/service/ai.js +++ b/src/frontend/src/domain/service/ai.js @@ -37,7 +37,8 @@ export default { }, fetchConfig(params = {}) { - return AiSource.getConfig(params) + return AiSource.getConfig(params, { + }) .then(({ data }) => data); }, @@ -51,16 +52,18 @@ export default { .then(({ data }) => data.reduce((result, item) => { if (item.userInput.content) { result.push({ - type: 'user', + role: 'user', content: item.userInput.content, status: '', + time: item.userInput.time, }); } result.push({ - type: 'ai', + role: 'assistant', content: item.aiAnswer.content, status: item.aiAnswer.errorCode === '0' ? 'success' : 'error', + time: item.aiAnswer.time, }); return result; @@ -69,4 +72,10 @@ export default { deleteChatHistory() { return AiSource.deleteChatHistory(); }, + fetchChatStream(params) { + return AiSource.getChatStream(params); + }, + terminateChat(params) { + return AiSource.terminateChat(params); + }, }; diff --git a/src/frontend/src/domain/source/ai.js b/src/frontend/src/domain/source/ai.js index 8857ee3558..3083148a57 100644 --- a/src/frontend/src/domain/source/ai.js +++ b/src/frontend/src/domain/source/ai.js @@ -33,27 +33,31 @@ class Ai extends ModuleBase { this.module = '/job-analysis/web/ai/'; } - getAnalyzeError(params) { + getAnalyzeError(params, payload = {}) { return Request.post(`${this.path}/analyzeError`, { params, + payload, }); } - getCheckScript(params) { + getCheckScript(params, payload = {}) { return Request.post(`${this.path}/checkScript`, { params, + payload, }); } - getConfig(params) { + getConfig(params, payload = {}) { return Request.get(`${this.path}/config`, { params, + payload, }); } - getGeneraChat(params) { + getGeneraChat(params, payload = {}) { return Request.post(`${this.path}/general/chat`, { params, + payload, }); } @@ -66,6 +70,17 @@ class Ai extends ModuleBase { deleteChatHistory() { return Request.delete(`${this.path}/clearChatHistory`); } + + getChatStream(params) { + return Request.get(`${this.path}/chatStream`, { + params, + }); + } + terminateChat(params) { + return Request.put(`${this.path}/terminateChat`, { + params, + }); + } } export default new Ai(); diff --git a/src/frontend/src/utils/request/lib/request.js b/src/frontend/src/utils/request/lib/request.js index a6bc69f27c..2831be7372 100644 --- a/src/frontend/src/utils/request/lib/request.js +++ b/src/frontend/src/utils/request/lib/request.js @@ -113,8 +113,9 @@ export default class Request { // axio 支持更过配置项通过config.payload获取 const axioMoreConfig = [ - 'onUploadProgress', + 'onUploadProgress', 'timeout', + 'responseType' ] axioMoreConfig.forEach(configExtend => { if (Object.prototype.hasOwnProperty.call(this.config.payload, configExtend)) { @@ -126,7 +127,7 @@ export default class Request { // 接口请求额外的业务相关配置项 get requestPayload () { const payload = this.config.payload || {} - + return { ...requestPayloadDefaul, ...payload, @@ -160,7 +161,7 @@ export default class Request { this.config = { ...config, } - + if (this.checkCache()) { return this.cache.get(this.name) } @@ -169,7 +170,7 @@ export default class Request { if (config.payload && config.payload.setCancelSource) { config.payload.setCancelSource(source) } - + const requestHandler = axios({ url: this.url, cancelToken: source.token, diff --git a/src/frontend/webpack.config.js b/src/frontend/webpack.config.js index 2c27496598..7d2d5e4dd6 100644 --- a/src/frontend/webpack.config.js +++ b/src/frontend/webpack.config.js @@ -37,6 +37,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); // const StylelintPlugin = require('stylelint-webpack-plugin'); const figlet = require('figlet'); const marked = require('marked'); +const includes = require('lodash/includes'); const renderer = new marked.Renderer(); From 43a35252dd6e9d91a9b1a0158847a91420b7afee Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Mon, 9 Sep 2024 15:28:48 +0800 Subject: [PATCH 075/115] =?UTF-8?q?feat:=20AI+JOB=20=E4=B8=80=E6=9C=9F=20#?= =?UTF-8?q?2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/components/jb-ai/index.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/src/components/jb-ai/index.vue b/src/frontend/src/components/jb-ai/index.vue index 8b67ad63ff..91585a7b3d 100644 --- a/src/frontend/src/components/jb-ai/index.vue +++ b/src/frontend/src/components/jb-ai/index.vue @@ -14,6 +14,7 @@ :loading="isLoading" :messages="messageList" :scroll-loading="isLoadingMoore" + :scroll-loading-end="isScrollLoadingEnd" @clear="handleClear" @close="handleClose" @scroll-load="handleLoadingMore" @@ -46,6 +47,7 @@ const isBluekingShow = ref(false); const isHandleShow = ref(true); const isLoadingMoore = ref(false); + const isScrollLoadingEnd = ref(false); const { messageList, From e3eecc70117a0e2f67960c10356780e7deee61fd Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Tue, 10 Sep 2024 16:59:42 +0800 Subject: [PATCH 076/115] =?UTF-8?q?feat:=20AI+JOB=20=E4=B8=80=E6=9C=9F=20#?= =?UTF-8?q?2995=20#=20Reviewed,=20transaction=20id:=2017991?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/components/jb-ai/index.vue | 60 ++++++++---- .../src/components/jb-ai/use-chat-history.js | 53 ++++++++++ .../components/jb-ai/use-stream-content.js | 96 ++++++++----------- .../views/executive-history/language/en.json | 4 +- .../views/executive-history/language/zh.json | 4 +- .../src/views/executive-history/local.js | 2 + .../execution-info/components/ai-helper.vue | 6 +- .../execution-info/components/script-log.vue | 12 ++- 8 files changed, 152 insertions(+), 85 deletions(-) create mode 100644 src/frontend/src/components/jb-ai/use-chat-history.js diff --git a/src/frontend/src/components/jb-ai/index.vue b/src/frontend/src/components/jb-ai/index.vue index 91585a7b3d..891473bd46 100644 --- a/src/frontend/src/components/jb-ai/index.vue +++ b/src/frontend/src/components/jb-ai/index.vue @@ -11,10 +11,11 @@ { isHandleShow.value = true; }); @@ -69,7 +84,7 @@ }; const handleCheckScript = (params) => { - isLoading.value = true; + isContentLoading.value = true; AiService.fetchCheckScript(params) .then((data) => { messageList.value.push({ @@ -84,7 +99,7 @@ }; const handlerAnalyzeError = (params) => { - isLoading.value = true; + isContentLoading.value = true; AiService.fetchAnalyzeError(params) .then((data) => { messageList.value.push({ @@ -99,7 +114,7 @@ }; const handleGeneraChat = (content) => { - isLoading.value = true; + isContentLoading.value = true; messageList.value.push({ role: 'user', content, @@ -115,30 +130,27 @@ }); }; + + const handleStop = () => { + AiService.terminateChat({ + recordId: currentRecordId, + }); + }; + const handleClear = () => { + handleStop(); AiService.deleteChatHistory() .then(() => { messageList.value = []; }); }; - const handleStop = () => { - AiService.terminateChat({ - recordId: currentRecordId, - }); - }; const handleClose = () => { isBluekingShow.value = false; - handleStop(); runCloseAnimate(); }; - const handleLoadingMore = () => { - isLoadingMoore.value = true; - console.log('asdasdasd'); - }; - eventBus.$on('ai:generaChat', () => { handleShow(); @@ -176,6 +188,10 @@ transform: translateX(20px); } } + } + + .ai-modal{ + box-shadow: 0 0 30px 8px #0000001a !important; .ai-modal-header{ background: none !important; @@ -194,4 +210,6 @@ } } } + + diff --git a/src/frontend/src/components/jb-ai/use-chat-history.js b/src/frontend/src/components/jb-ai/use-chat-history.js new file mode 100644 index 0000000000..54830e9ae9 --- /dev/null +++ b/src/frontend/src/components/jb-ai/use-chat-history.js @@ -0,0 +1,53 @@ +import { onMounted, reactive, ref } from 'vue'; + +import AiService from '@service/ai'; + +export default () => { + const hasMore = ref(true); + const isLoadingMore = ref(false); + const isLoading = ref(false); + const messageList = ref([]); + + const pagination = reactive({ + start: 0, + length: 15, + }); + + const fetchLatestChatHistoryList = () => { + isLoading.value = true; + AiService.fetchLatestChatHistoryList(pagination) + .then((result) => { + messageList.value = result; + hasMore.value = result.length < pagination.length; + }) + .finally(() => { + isLoading.value = false; + }); + }; + + const loadMore = () => { + pagination.start += pagination.length; + isLoadingMore.value = true; + AiService.fetchLatestChatHistoryList(pagination) + .then((result) => { + hasMore.value = result.length === pagination.length; + messageList.value = [...result, ...messageList.value]; + }) + .finally(() => { + isLoadingMore.value = false; + }); + }; + + onMounted(() => { + fetchLatestChatHistoryList(); + }); + + return { + messageList, + loading: isLoading, + hasMore, + loadingMore: isLoadingMore, + pagination, + loadMore, + }; +}; diff --git a/src/frontend/src/components/jb-ai/use-stream-content.js b/src/frontend/src/components/jb-ai/use-stream-content.js index a32241ef6f..cff845e5a8 100644 --- a/src/frontend/src/components/jb-ai/use-stream-content.js +++ b/src/frontend/src/components/jb-ai/use-stream-content.js @@ -1,24 +1,11 @@ -import { onMounted, ref } from 'vue'; +import { ref } from 'vue'; -import AiService from '@service/ai'; -export default () => { +export default (messageList) => { const loading = ref(false); - const messageList = ref([]); const apiPath = `${window.PROJECT_CONFIG.AJAX_URL_PREFIX}/job-analysis/web/ai/scope/${window.PROJECT_CONFIG.SCOPE_TYPE}/${window.PROJECT_CONFIG.SCOPE_ID}`; - const fetchLatestChatHistoryList = () => { - loading.value = true; - AiService.fetchLatestChatHistoryList() - .then((result) => { - messageList.value = result; - }) - .finally(() => { - loading.value = false; - }); - }; - const fetchContent = (recordId) => { loading.value = true; const latestChatMessage = { @@ -38,61 +25,62 @@ export default () => { 'Content-Type': 'application/json', }, }) - .then(response => new Promise((resolve, reject) => { - const reader = response.body.getReader(); - const decoder = new TextDecoder('utf-8'); + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP 错误!状态:${response.status}`); + } + + return new Promise((resolve, reject) => { + const reader = response.body.getReader(); + const decoder = new TextDecoder('utf-8'); - latestChatMessage.content = ''; + latestChatMessage.content = ''; - let fragment = ''; - // 递归函数来读取数据 - const read = () => { - reader.read() - .then(({ done, value }) => { - if (done) { - latestChatMessage.status = 'success'; - return resolve(); - } + let fragment = ''; + // 递归函数来读取数据 + const read = () => { + reader.read() + .then(({ done, value }) => { + if (done) { + latestChatMessage.status = 'success'; + latestChatMessage.content = `${latestChatMessage.content}`; + return resolve(); + } - // 解码二进制数据块并添加到内容字符串 - const chunk = `${fragment}${decoder.decode(value, { stream: true })}`; + // 解码二进制数据块并添加到内容字符串 + const chunk = `${fragment}${decoder.decode(value, { stream: true })}`; - chunk.split('\n') - .forEach((item) => { - try { - latestChatMessage.content += (item ? JSON.parse(item).data.content : ''); - } catch { - fragment += item; - } - }); + chunk.split('\n') + .forEach((item) => { + try { + latestChatMessage.content += (item ? JSON.parse(item).data.content : ''); + } catch { + fragment += item; + } + }); - // 递归读取下一块数据 - read(); - }) - .catch(error => reject(error)); - }; + // 递归读取下一块数据 + read(); + }) + .catch(error => reject(error)); + }; - // 开始读取流 - read(); - })) - .then(() => { - fetchLatestChatHistoryList(); + // 开始读取流 + read(); + }); }) .catch((error) => { console.error('Fetch error:', error); + latestChatMessage.content = '内容生成失败!'; latestChatMessage.status = 'error'; - messageList.value = [...messageList.value]; + }) + .finally(() => { loading.value = false; }); }; - onMounted(() => { - fetchLatestChatHistoryList(); - }); - return { loading, - messageList, fetchContent, }; }; diff --git a/src/frontend/src/views/executive-history/language/en.json b/src/frontend/src/views/executive-history/language/en.json index db3b84b2be..3c807b0e44 100644 --- a/src/frontend/src/views/executive-history/language/en.json +++ b/src/frontend/src/views/executive-history/language/en.json @@ -188,5 +188,7 @@ "容器 ID": "Container ID", "所属 Pod 名称": "Pod name", "没有可复制的容器名称": "Container name not found", - "文件源": "File source" + "文件源": "File source", + "日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。": "The length of log text exceeds the scope of AI analysis, please manually select some key logs and resubmit.", + "报错日志看不懂?来提问小鲸吧~": "Click here to let AI-assistant help you analyze" } \ No newline at end of file diff --git a/src/frontend/src/views/executive-history/language/zh.json b/src/frontend/src/views/executive-history/language/zh.json index d0e4f82a83..382b9efd07 100644 --- a/src/frontend/src/views/executive-history/language/zh.json +++ b/src/frontend/src/views/executive-history/language/zh.json @@ -188,5 +188,7 @@ "容器 ID": "容器 ID", "所属 Pod 名称": "所属 Pod 名称", "没有可复制的容器名称": "没有可复制的容器名称", - "文件源": "文件源" + "文件源": "文件源", + "日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。": "日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。", + "报错日志看不懂?来提问小鲸吧~": "报错日志看不懂?来提问小鲸吧~" } \ No newline at end of file diff --git a/src/frontend/src/views/executive-history/local.js b/src/frontend/src/views/executive-history/local.js index 677d765ba9..819557cf33 100644 --- a/src/frontend/src/views/executive-history/local.js +++ b/src/frontend/src/views/executive-history/local.js @@ -233,5 +233,7 @@ export default { '所属 Pod 名称': 'Pod name', 没有可复制的容器名称: 'Container name not found', 文件源: 'File source', + '日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。': '日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。', + '报错日志看不懂?来提问小鲸吧~': '报错日志看不懂?来提问小鲸吧~', }, }; diff --git a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/ai-helper.vue b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/ai-helper.vue index 640310fccb..709c2dbe13 100644 --- a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/ai-helper.vue +++ b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/ai-helper.vue @@ -13,13 +13,13 @@
-
{{ $t('日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。') }}
+
{{ $t('history.日志文本过长,超出 AI 解析范围,请框选部分日志,再次出发点小鲸分析。') }}
- {{ $t('确定') }} + {{ $t('history.确定') }}
- {{ $t('报错日志看不懂?来提问小鲸吧~') }} + {{ $t('history.报错日志看不懂?来提问小鲸吧~') }}
diff --git a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue index a23313002d..f1a110da8f 100644 --- a/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue +++ b/src/frontend/src/views/executive-history/step-detail/components/execution-info/components/script-log.vue @@ -229,8 +229,11 @@ this.isRunning = !finished; this.logContent = _.trim(logContent || '', '\n'); this.$nextTick(() => { - this.editor.setValue(this.logContent); + this.editor.setValue(logContent); this.editor.clearSelection(); + setTimeout(() => { + document.body.querySelector('.ace_layer.ace_text-layer').appendChild(this.$refs.aiExtendTool); + }, 1000); }); // 当前主机执行结束 if (!finished) { @@ -269,7 +272,6 @@ this.isWillAutoScroll = height + scrollTop + 30 >= maxHeight; }); this.editor = editor; - document.body.querySelector('.ace_layer.ace_text-layer').appendChild(this.$refs.aiExtendTool); this.$once('hook:beforeDestroy', () => { editor.destroy(); @@ -299,7 +301,7 @@ executeObjectResourceId: this.taskExecuteDetail.executeObject.executeObjectResourceId, executeCount: this.executeCount, batch: this.taskExecuteDetail.batch, - content: 'string', + content: this.editor.getValue(), }); }); }, @@ -343,8 +345,8 @@ const { pageX, pageY } = event; this.aiExtendToolStyle = { display: 'flex', - top: `${pageY - 40 - contentBoxTop + parseInt(transformY, 10)}px`, - left: `${pageX + 4 - contentBoxLeft}px`, + top: `${Math.max(pageY - 40 - contentBoxTop + parseInt(transformY, 10), 8)}px`, + left: `${Math.max(pageX + 4 - contentBoxLeft, 8)}px`, }; }); }, From d749e1035d927a4c7cf9a999561538c1ce9656f0 Mon Sep 17 00:00:00 2001 From: hLinx <327159425@qq.com> Date: Sat, 14 Sep 2024 10:29:23 +0800 Subject: [PATCH 077/115] =?UTF-8?q?feat:=20AI+JOB=20=E4=B8=80=E6=9C=9F=20#?= =?UTF-8?q?2995?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/App.vue | 11 ++++++++++- .../src/components/ace-editor/index.vue | 14 +++++++++++++- src/frontend/src/components/jb-ai/index.vue | 13 ++++++++----- .../src/components/jb-ai/use-stream-content.js | 9 ++++++--- src/frontend/src/domain/service/ai.js | 3 +-- src/frontend/src/domain/source/ai.js | 7 +++---- src/frontend/src/i18n/language/en.json | 5 ++++- src/frontend/src/i18n/language/zh.json | 5 ++++- .../execution-info/components/ai-helper.vue | 18 +++++++++++------- .../components/file-log/index.vue | 13 +++++++++++++ .../execution-info/components/script-log.vue | 12 ++++++++++++ 11 files changed, 85 insertions(+), 25 deletions(-) diff --git a/src/frontend/src/App.vue b/src/frontend/src/App.vue index a7fe34bee8..eda39b67dc 100644 --- a/src/frontend/src/App.vue +++ b/src/frontend/src/App.vue @@ -112,12 +112,13 @@ - +