mirror of
https://github.com/tencentmusic/supersonic.git
synced 2026-04-19 21:14:25 +08:00
Compare commits
14 Commits
a32014d40b
...
6b3250fdbb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b3250fdbb | ||
|
|
37673c82da | ||
|
|
3ae0d645a7 | ||
|
|
256a6bcb3f | ||
|
|
1faf84e372 | ||
|
|
7e6639df83 | ||
|
|
075ae4c0af | ||
|
|
08133ccbfb | ||
|
|
164d2a9e23 | ||
|
|
f899d23b63 | ||
|
|
944beddafc | ||
|
|
019d737f07 | ||
|
|
f489ea2f59 | ||
|
|
87355533b4 |
@@ -43,10 +43,26 @@ if "%service%"=="webapp" (
|
|||||||
call mvn -f %projectDir% clean package -DskipTests -Dspotless.skip=true
|
call mvn -f %projectDir% clean package -DskipTests -Dspotless.skip=true
|
||||||
IF ERRORLEVEL 1 (
|
IF ERRORLEVEL 1 (
|
||||||
ECHO Failed to build backend Java modules.
|
ECHO Failed to build backend Java modules.
|
||||||
|
ECHO Please check Maven and Java versions are compatible.
|
||||||
|
ECHO Current Java: %JAVA_HOME%
|
||||||
|
ECHO Current Maven: %MAVEN_HOME%
|
||||||
EXIT /B 1
|
EXIT /B 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
REM extract and copy files to deployment directory
|
||||||
|
cd %projectDir%\launchers\%model_name%\target
|
||||||
|
if exist "launchers-%model_name%-%MVN_VERSION%-bin.tar.gz" (
|
||||||
|
echo "Extracting launchers-%model_name%-%MVN_VERSION%-bin.tar.gz..."
|
||||||
|
tar -xf "launchers-%model_name%-%MVN_VERSION%-bin.tar.gz"
|
||||||
|
if exist "launchers-%model_name%-%MVN_VERSION%" (
|
||||||
|
echo "Copying files to deployment directory..."
|
||||||
|
xcopy /E /Y "launchers-%model_name%-%MVN_VERSION%\*" "%buildDir%\supersonic-%model_name%-%MVN_VERSION%\"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
copy /y %projectDir%\launchers\%model_name%\target\*.tar.gz %buildDir%\
|
copy /y %projectDir%\launchers\%model_name%\target\*.tar.gz %buildDir%\
|
||||||
echo "finished building supersonic-%model_name% service"
|
echo "finished building supersonic-%model_name% service"
|
||||||
|
cd %baseDir%
|
||||||
goto :EOF
|
goto :EOF
|
||||||
|
|
||||||
|
|
||||||
@@ -72,22 +88,55 @@ if "%service%"=="webapp" (
|
|||||||
cd %buildDir%
|
cd %buildDir%
|
||||||
if exist %release_dir% rmdir /s /q %release_dir%
|
if exist %release_dir% rmdir /s /q %release_dir%
|
||||||
if exist %release_dir%.zip del %release_dir%.zip
|
if exist %release_dir%.zip del %release_dir%.zip
|
||||||
mkdir %release_dir%
|
|
||||||
rem package webapp
|
rem check if release directory already exists from buildJavaService
|
||||||
tar xvf supersonic-webapp.tar.gz
|
if exist %release_dir% (
|
||||||
move /y supersonic-webapp webapp
|
echo "Release directory already prepared by buildJavaService"
|
||||||
echo {"env": ""} > webapp\supersonic.config.json
|
) else (
|
||||||
move /y webapp %release_dir%
|
mkdir %release_dir%
|
||||||
rem package java service
|
|
||||||
tar xvf %service_name%-bin.tar.gz
|
rem package java service
|
||||||
for /d %%D in ("%service_name%\*") do (
|
tar xvf %service_name%-bin.tar.gz 2>nul
|
||||||
move "%%D" "%release_dir%"
|
if errorlevel 1 (
|
||||||
|
echo "Warning: tar command failed, trying PowerShell extraction..."
|
||||||
|
powershell -Command "Expand-Archive -Path '%service_name%-bin.tar.gz' -DestinationPath '.' -Force"
|
||||||
|
)
|
||||||
|
for /d %%D in ("%service_name%\*") do (
|
||||||
|
move "%%D" "%release_dir%"
|
||||||
|
)
|
||||||
|
rmdir /s /q %service_name% 2>nul
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rem package webapp
|
||||||
|
if exist supersonic-webapp.tar.gz (
|
||||||
|
tar xvf supersonic-webapp.tar.gz 2>nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo "Warning: tar command failed, trying PowerShell extraction..."
|
||||||
|
powershell -Command "Expand-Archive -Path 'supersonic-webapp.tar.gz' -DestinationPath '.' -Force"
|
||||||
|
)
|
||||||
|
move /y supersonic-webapp webapp
|
||||||
|
echo {"env": ""} > webapp\supersonic.config.json
|
||||||
|
move /y webapp %release_dir%
|
||||||
|
del supersonic-webapp.tar.gz 2>nul
|
||||||
|
)
|
||||||
|
|
||||||
|
rem verify deployment structure
|
||||||
|
if exist "%release_dir%\lib\launchers-%model_name%-%MVN_VERSION%.jar" (
|
||||||
|
echo "Deployment structure verified successfully"
|
||||||
|
) else (
|
||||||
|
echo "Warning: Main jar file not found in deployment structure"
|
||||||
|
echo "Expected: %release_dir%\lib\launchers-%model_name%-%MVN_VERSION%.jar"
|
||||||
|
)
|
||||||
|
|
||||||
rem generate zip file
|
rem generate zip file
|
||||||
powershell Compress-Archive -Path %release_dir% -DestinationPath %release_dir%.zip
|
powershell -Command "Compress-Archive -Path '%release_dir%' -DestinationPath '%release_dir%.zip' -Force"
|
||||||
del %service_name%-bin.tar.gz
|
if errorlevel 1 (
|
||||||
del supersonic-webapp.tar.gz
|
echo "Warning: PowerShell compression failed, release directory still available: %release_dir%"
|
||||||
rmdir /s /q %service_name%
|
) else (
|
||||||
|
echo "Successfully created release package: %release_dir%.zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
del %service_name%-bin.tar.gz 2>nul
|
||||||
echo "finished packaging supersonic release"
|
echo "finished packaging supersonic release"
|
||||||
goto :EOF
|
goto :EOF
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
|
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -222,8 +223,9 @@ public class DefaultUserAdaptor implements UserAdaptor {
|
|||||||
new UserWithPassword(userDO.getId(), userDO.getName(), userDO.getDisplayName(),
|
new UserWithPassword(userDO.getId(), userDO.getName(), userDO.getDisplayName(),
|
||||||
userDO.getEmail(), userDO.getPassword(), userDO.getIsAdmin());
|
userDO.getEmail(), userDO.getPassword(), userDO.getIsAdmin());
|
||||||
|
|
||||||
String token =
|
// 使用令牌名称作为生成key ,这样可以区分正常请求和api 请求,api 的令牌失效时间很长,需考虑令牌泄露的情况
|
||||||
tokenService.generateToken(UserWithPassword.convert(userWithPassword), expireTime);
|
String token = tokenService.generateToken(UserWithPassword.convert(userWithPassword),
|
||||||
|
"SysDbToken:" + name, (new Date().getTime() + expireTime));
|
||||||
UserTokenDO userTokenDO = saveUserToken(name, userName, token, expireTime);
|
UserTokenDO userTokenDO = saveUserToken(name, userName, token, expireTime);
|
||||||
return convertUserToken(userTokenDO);
|
return convertUserToken(userTokenDO);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public interface UserRepository {
|
|||||||
|
|
||||||
UserTokenDO getUserToken(Long tokenId);
|
UserTokenDO getUserToken(Long tokenId);
|
||||||
|
|
||||||
|
UserTokenDO getUserTokenByName(String tokenName);
|
||||||
|
|
||||||
void deleteUserTokenByName(String userName);
|
void deleteUserTokenByName(String userName);
|
||||||
|
|
||||||
void deleteUserToken(Long tokenId);
|
void deleteUserToken(Long tokenId);
|
||||||
|
|||||||
@@ -65,6 +65,13 @@ public class UserRepositoryImpl implements UserRepository {
|
|||||||
return userTokenDOMapper.selectById(tokenId);
|
return userTokenDOMapper.selectById(tokenId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserTokenDO getUserTokenByName(String tokenName) {
|
||||||
|
QueryWrapper<UserTokenDO> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.lambda().eq(UserTokenDO::getName, tokenName);
|
||||||
|
return userTokenDOMapper.selectOne(queryWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteUserTokenByName(String userName) {
|
public void deleteUserTokenByName(String userName) {
|
||||||
QueryWrapper<UserTokenDO> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<UserTokenDO> queryWrapper = new QueryWrapper<>();
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
|
|
||||||
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
import com.tencent.supersonic.auth.api.authentication.config.AuthenticationConfig;
|
||||||
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
|
import com.tencent.supersonic.auth.api.authentication.pojo.UserWithPassword;
|
||||||
|
import com.tencent.supersonic.auth.authentication.persistence.dataobject.UserTokenDO;
|
||||||
|
import com.tencent.supersonic.auth.authentication.persistence.repository.UserRepository;
|
||||||
import com.tencent.supersonic.common.pojo.exception.AccessException;
|
import com.tencent.supersonic.common.pojo.exception.AccessException;
|
||||||
|
import com.tencent.supersonic.common.util.ContextUtils;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
@@ -71,6 +74,7 @@ public class TokenService {
|
|||||||
return generateToken(UserWithPassword.convert(appUser), request);
|
return generateToken(UserWithPassword.convert(appUser), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Optional<Claims> getClaims(HttpServletRequest request) {
|
public Optional<Claims> getClaims(HttpServletRequest request) {
|
||||||
String token = request.getHeader(authenticationConfig.getTokenHttpHeaderKey());
|
String token = request.getHeader(authenticationConfig.getTokenHttpHeaderKey());
|
||||||
String appKey = getAppKey(request);
|
String appKey = getAppKey(request);
|
||||||
@@ -90,6 +94,14 @@ public class TokenService {
|
|||||||
|
|
||||||
public Optional<Claims> getClaims(String token, String appKey) {
|
public Optional<Claims> getClaims(String token, String appKey) {
|
||||||
try {
|
try {
|
||||||
|
if (StringUtils.isNotBlank(appKey) && appKey.startsWith("SysDbToken:")) {// 如果是配置的长期令牌,需校验数据库是否存在该配置
|
||||||
|
UserRepository userRepository = ContextUtils.getBean(UserRepository.class);
|
||||||
|
UserTokenDO dbToken =
|
||||||
|
userRepository.getUserTokenByName(appKey.substring("SysDbToken:".length()));
|
||||||
|
if (dbToken == null || !dbToken.getToken().equals(token.replace("Bearer ", ""))) {
|
||||||
|
throw new AccessException("Token does not exist :" + appKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
String tokenSecret = getTokenSecret(appKey);
|
String tokenSecret = getTokenSecret(appKey);
|
||||||
Claims claims =
|
Claims claims =
|
||||||
Jwts.parser().setSigningKey(tokenSecret.getBytes(StandardCharsets.UTF_8))
|
Jwts.parser().setSigningKey(tokenSecret.getBytes(StandardCharsets.UTF_8))
|
||||||
@@ -122,6 +134,16 @@ public class TokenService {
|
|||||||
Map<String, String> appKeyToSecretMap = authenticationConfig.getAppKeyToSecretMap();
|
Map<String, String> appKeyToSecretMap = authenticationConfig.getAppKeyToSecretMap();
|
||||||
String secret = appKeyToSecretMap.get(appKey);
|
String secret = appKeyToSecretMap.get(appKey);
|
||||||
if (StringUtils.isBlank(secret)) {
|
if (StringUtils.isBlank(secret)) {
|
||||||
|
if (StringUtils.isNotBlank(appKey) && appKey.startsWith("SysDbToken:")) { // 是配置的长期令牌
|
||||||
|
String realAppKey = appKey.substring("SysDbToken:".length());
|
||||||
|
String tmp =
|
||||||
|
"WIaO9YRRVt+7QtpPvyWsARFngnEcbaKBk783uGFwMrbJBaochsqCH62L4Kijcb0sZCYoSsiKGV/zPml5MnZ3uQ==";
|
||||||
|
if (tmp.length() <= realAppKey.length()) {
|
||||||
|
return realAppKey;
|
||||||
|
} else {
|
||||||
|
return realAppKey + tmp.substring(realAppKey.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new AccessException("get secret from appKey failed :" + appKey);
|
throw new AccessException("get secret from appKey failed :" + appKey);
|
||||||
}
|
}
|
||||||
return secret;
|
return secret;
|
||||||
|
|||||||
@@ -75,8 +75,12 @@ public class SqlExecutor implements ChatQueryExecutor {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuerySqlReq sqlReq =
|
// 使用querySQL,它已经包含了所有修正(包括物理SQL修正)
|
||||||
QuerySqlReq.builder().sql(parseInfo.getSqlInfo().getCorrectedS2SQL()).build();
|
String finalSql = StringUtils.isNotBlank(parseInfo.getSqlInfo().getQuerySQL())
|
||||||
|
? parseInfo.getSqlInfo().getQuerySQL()
|
||||||
|
: parseInfo.getSqlInfo().getCorrectedS2SQL();
|
||||||
|
|
||||||
|
QuerySqlReq sqlReq = QuerySqlReq.builder().sql(finalSql).build();
|
||||||
sqlReq.setSqlInfo(parseInfo.getSqlInfo());
|
sqlReq.setSqlInfo(parseInfo.getSqlInfo());
|
||||||
sqlReq.setDataSetId(parseInfo.getDataSetId());
|
sqlReq.setDataSetId(parseInfo.getDataSetId());
|
||||||
|
|
||||||
@@ -90,7 +94,7 @@ public class SqlExecutor implements ChatQueryExecutor {
|
|||||||
queryResult.setQueryTimeCost(System.currentTimeMillis() - startTime);
|
queryResult.setQueryTimeCost(System.currentTimeMillis() - startTime);
|
||||||
if (queryResp != null) {
|
if (queryResp != null) {
|
||||||
queryResult.setQueryAuthorization(queryResp.getQueryAuthorization());
|
queryResult.setQueryAuthorization(queryResp.getQueryAuthorization());
|
||||||
queryResult.setQuerySql(queryResp.getSql());
|
queryResult.setQuerySql(finalSql);
|
||||||
queryResult.setQueryResults(queryResp.getResultList());
|
queryResult.setQueryResults(queryResp.getResultList());
|
||||||
queryResult.setQueryColumns(queryResp.getColumns());
|
queryResult.setQueryColumns(queryResp.getColumns());
|
||||||
queryResult.setQueryState(QueryState.SUCCESS);
|
queryResult.setQueryState(QueryState.SUCCESS);
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ public class DataInterpretProcessor implements ExecuteResultProcessor {
|
|||||||
Agent agent = executeContext.getAgent();
|
Agent agent = executeContext.getAgent();
|
||||||
ChatApp chatApp = agent.getChatAppConfig().get(APP_KEY);
|
ChatApp chatApp = agent.getChatAppConfig().get(APP_KEY);
|
||||||
return Objects.nonNull(chatApp) && chatApp.isEnable()
|
return Objects.nonNull(chatApp) && chatApp.isEnable()
|
||||||
&& StringUtils.isNotBlank(executeContext.getResponse().getTextResult()); // 如果都没结果,则无法处理,直接跳过
|
&& StringUtils.isNotBlank(executeContext.getResponse().getTextResult()) // 如果都没结果,则无法处理
|
||||||
|
&& StringUtils.isBlank(executeContext.getResponse().getTextSummary()); // 如果已经有汇总的结果了,无法再次处理
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,7 +58,16 @@ public class DataInterpretProcessor implements ExecuteResultProcessor {
|
|||||||
ChatApp chatApp = agent.getChatAppConfig().get(APP_KEY);
|
ChatApp chatApp = agent.getChatAppConfig().get(APP_KEY);
|
||||||
|
|
||||||
Map<String, Object> variable = new HashMap<>();
|
Map<String, Object> variable = new HashMap<>();
|
||||||
variable.put("question", executeContext.getRequest().getQueryText());
|
String question = executeContext.getResponse().getTextResult();// 结果解析应该用改写的问题,因为改写的内容信息量更大
|
||||||
|
if (executeContext.getParseInfo().getProperties() != null
|
||||||
|
&& executeContext.getParseInfo().getProperties().containsKey("CONTEXT")) {
|
||||||
|
Map<String, Object> context = (Map<String, Object>) executeContext.getParseInfo()
|
||||||
|
.getProperties().get("CONTEXT");
|
||||||
|
if (context.get("queryText") != null && "".equals(context.get("queryText"))) {
|
||||||
|
question = context.get("queryText").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variable.put("question", question);
|
||||||
variable.put("data", queryResult.getTextResult());
|
variable.put("data", queryResult.getTextResult());
|
||||||
|
|
||||||
Prompt prompt = PromptTemplate.from(chatApp.getPrompt()).apply(variable);
|
Prompt prompt = PromptTemplate.from(chatApp.getPrompt()).apply(variable);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ public class AgentServiceImpl extends ServiceImpl<AgentDOMapper, AgentDO> implem
|
|||||||
private MemoryService memoryService;
|
private MemoryService memoryService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Lazy
|
||||||
private ChatQueryService chatQueryService;
|
private ChatQueryService chatQueryService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import net.sf.jsqlparser.schema.Column;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ public class ChatQueryServiceImpl implements ChatQueryService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SemanticLayerService semanticLayerService;
|
private SemanticLayerService semanticLayerService;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Lazy
|
||||||
private AgentService agentService;
|
private AgentService agentService;
|
||||||
|
|
||||||
private final List<ChatQueryParser> chatQueryParsers = ComponentFactory.getChatParsers();
|
private final List<ChatQueryParser> chatQueryParsers = ComponentFactory.getChatParsers();
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ public class LoadRemoveService {
|
|||||||
List<String> resultList = new ArrayList<>(value);
|
List<String> resultList = new ArrayList<>(value);
|
||||||
if (!CollectionUtils.isEmpty(modelIdOrDataSetIds)) {
|
if (!CollectionUtils.isEmpty(modelIdOrDataSetIds)) {
|
||||||
resultList.removeIf(nature -> {
|
resultList.removeIf(nature -> {
|
||||||
if (Objects.isNull(nature)) {
|
if (Objects.isNull(nature) || !nature.startsWith("_")) { // 系统的字典是以 _ 开头的,
|
||||||
|
// 过滤因引用外部字典导致的异常
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Long id = getId(nature);
|
Long id = getId(nature);
|
||||||
|
|||||||
@@ -22,4 +22,6 @@ public class Text2SQLExemplar implements Serializable {
|
|||||||
private String dbSchema;
|
private String dbSchema;
|
||||||
|
|
||||||
private String sql;
|
private String sql;
|
||||||
|
|
||||||
|
protected double similarity; // 传递相似度,可以作为样本筛选的依据
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,10 @@ public class ExemplarServiceImpl implements ExemplarService, CommandLineRunner {
|
|||||||
embeddingService.retrieveQuery(collection, retrieveQuery, num);
|
embeddingService.retrieveQuery(collection, retrieveQuery, num);
|
||||||
results.forEach(ret -> {
|
results.forEach(ret -> {
|
||||||
ret.getRetrieval().forEach(r -> {
|
ret.getRetrieval().forEach(r -> {
|
||||||
exemplars.add(JsonUtil.mapToObject(r.getMetadata(), Text2SQLExemplar.class));
|
Text2SQLExemplar tmp = // 传递相似度,可以作为样本筛选的依据
|
||||||
|
JsonUtil.mapToObject(r.getMetadata(), Text2SQLExemplar.class);
|
||||||
|
tmp.setSimilarity(r.getSimilarity());
|
||||||
|
exemplars.add(tmp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.tencent.supersonic.common.pojo.Constants.DEFAULT_DETAIL_LIMIT;
|
import static com.tencent.supersonic.common.pojo.Constants.DEFAULT_DETAIL_LIMIT;
|
||||||
import static com.tencent.supersonic.common.pojo.Constants.DEFAULT_METRIC_LIMIT;
|
import static com.tencent.supersonic.common.pojo.Constants.DEFAULT_METRIC_LIMIT;
|
||||||
@@ -65,12 +66,23 @@ public class SemanticParseInfo implements Serializable {
|
|||||||
DataSetMatchResult mr2 = getDataSetMatchResult(o2.getElementMatches());
|
DataSetMatchResult mr2 = getDataSetMatchResult(o2.getElementMatches());
|
||||||
|
|
||||||
double difference = mr1.getMaxDatesetSimilarity() - mr2.getMaxDatesetSimilarity();
|
double difference = mr1.getMaxDatesetSimilarity() - mr2.getMaxDatesetSimilarity();
|
||||||
if (difference == 0) {
|
if (Math.abs(difference) < 0.0005) { // 看完全匹配的个数,实践证明,可以用户输入规范后,该逻辑具有优势
|
||||||
|
if (!o1.getDataSetId().equals(o2.getDataSetId())) {
|
||||||
|
List<SchemaElementMatch> elementMatches1 = o1.getElementMatches().stream()
|
||||||
|
.filter(e -> e.getSimilarity() == 1).collect(Collectors.toList());
|
||||||
|
List<SchemaElementMatch> elementMatches2 = o2.getElementMatches().stream()
|
||||||
|
.filter(e -> e.getSimilarity() == 1).collect(Collectors.toList());
|
||||||
|
if (elementMatches1.size() > elementMatches2.size()) {
|
||||||
|
return -1;
|
||||||
|
} else if (elementMatches1.size() < elementMatches2.size()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
difference = mr1.getMaxMetricSimilarity() - mr2.getMaxMetricSimilarity();
|
difference = mr1.getMaxMetricSimilarity() - mr2.getMaxMetricSimilarity();
|
||||||
if (difference == 0) {
|
if (Math.abs(difference) < 0.0005) {
|
||||||
difference = mr1.getTotalSimilarity() - mr2.getTotalSimilarity();
|
difference = mr1.getTotalSimilarity() - mr2.getTotalSimilarity();
|
||||||
}
|
}
|
||||||
if (difference == 0) {
|
if (Math.abs(difference) < 0.0005) {
|
||||||
difference = mr1.getMaxMetricUseCnt() - mr2.getMaxMetricUseCnt();
|
difference = mr1.getMaxMetricUseCnt() - mr2.getMaxMetricUseCnt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,7 @@ public class SqlInfo implements Serializable {
|
|||||||
|
|
||||||
// SQL to be executed finally
|
// SQL to be executed finally
|
||||||
private String querySQL;
|
private String querySQL;
|
||||||
|
|
||||||
|
// Physical SQL corrected by LLM for performance optimization
|
||||||
|
private String correctedQuerySQL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ public enum ChatWorkflowState {
|
|||||||
VALIDATING,
|
VALIDATING,
|
||||||
SQL_CORRECTING,
|
SQL_CORRECTING,
|
||||||
PROCESSING,
|
PROCESSING,
|
||||||
|
PHYSICAL_SQL_CORRECTING,
|
||||||
FINISHED
|
FINISHED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.tencent.supersonic.headless.chat.corrector;
|
||||||
|
|
||||||
|
import com.tencent.supersonic.common.pojo.ChatApp;
|
||||||
|
import com.tencent.supersonic.common.pojo.enums.AppModule;
|
||||||
|
import com.tencent.supersonic.common.util.ChatAppManager;
|
||||||
|
import com.tencent.supersonic.headless.api.pojo.SemanticParseInfo;
|
||||||
|
import com.tencent.supersonic.headless.chat.ChatQueryContext;
|
||||||
|
import dev.langchain4j.model.chat.ChatLanguageModel;
|
||||||
|
import dev.langchain4j.model.input.Prompt;
|
||||||
|
import dev.langchain4j.model.input.PromptTemplate;
|
||||||
|
import dev.langchain4j.model.output.structured.Description;
|
||||||
|
import dev.langchain4j.provider.ModelProvider;
|
||||||
|
import dev.langchain4j.service.AiServices;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 物理SQL修正器 - 使用LLM优化物理SQL性能
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class LLMPhysicalSqlCorrector extends BaseSemanticCorrector {
|
||||||
|
|
||||||
|
private static final Logger keyPipelineLog = LoggerFactory.getLogger("keyPipeline");
|
||||||
|
|
||||||
|
public static final String APP_KEY = "PHYSICAL_SQL_CORRECTOR";
|
||||||
|
private static final String INSTRUCTION = ""
|
||||||
|
+ "#Role: You are a senior database performance optimization expert experienced in SQL tuning."
|
||||||
|
+ "\n\n#Task: You will be provided with a user question and the corresponding physical SQL query,"
|
||||||
|
+ " please analyze and optimize this SQL to improve query performance." + "\n\n#Rules:"
|
||||||
|
+ "\n1. DO NOT add or introduce any new fields, columns, or aliases that are not in the original SQL."
|
||||||
|
+ "\n2. Push WHERE conditions into JOIN ON clauses when possible to reduce intermediate result sets."
|
||||||
|
+ "\n3. Optimize JOIN order by placing smaller tables or tables with selective conditions first."
|
||||||
|
+ "\n4. For date range conditions, ensure they are applied as early as possible in the query execution."
|
||||||
|
+ "\n5. Remove or comment out database-specific index hints (like USE INDEX) that may cause syntax errors."
|
||||||
|
+ "\n6. ONLY modify the structure and order of existing elements, do not change field names or add new ones."
|
||||||
|
+ "\n7. Ensure the optimized SQL is syntactically correct and logically equivalent to the original."
|
||||||
|
+ "\n\n#Question: {{question}}" + "\n\n#OriginalSQL: {{sql}}";
|
||||||
|
|
||||||
|
public LLMPhysicalSqlCorrector() {
|
||||||
|
ChatAppManager.register(APP_KEY, ChatApp.builder().prompt(INSTRUCTION).name("物理SQL修正")
|
||||||
|
.appModule(AppModule.CHAT).description("通过大模型对物理SQL做性能优化").enable(false).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ToString
|
||||||
|
static class PhysicalSql {
|
||||||
|
@Description("either positive or negative")
|
||||||
|
private String opinion;
|
||||||
|
|
||||||
|
@Description("optimized sql if negative")
|
||||||
|
private String sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PhysicalSqlExtractor {
|
||||||
|
PhysicalSql generatePhysicalSql(String text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doCorrect(ChatQueryContext chatQueryContext, SemanticParseInfo semanticParseInfo) {
|
||||||
|
ChatApp chatApp = chatQueryContext.getRequest().getChatAppConfig().get(APP_KEY);
|
||||||
|
if (!chatQueryContext.getRequest().getText2SQLType().enableLLM() || Objects.isNull(chatApp)
|
||||||
|
|| !chatApp.isEnable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatLanguageModel chatLanguageModel =
|
||||||
|
ModelProvider.getChatModel(chatApp.getChatModelConfig());
|
||||||
|
PhysicalSqlExtractor extractor =
|
||||||
|
AiServices.create(PhysicalSqlExtractor.class, chatLanguageModel);
|
||||||
|
Prompt prompt = generatePrompt(chatQueryContext.getRequest().getQueryText(),
|
||||||
|
semanticParseInfo, chatApp.getPrompt());
|
||||||
|
PhysicalSql physicalSql =
|
||||||
|
extractor.generatePhysicalSql(prompt.toUserMessage().singleText());
|
||||||
|
keyPipelineLog.info("LLMPhysicalSqlCorrector modelReq:\n{} \nmodelResp:\n{}", prompt.text(),
|
||||||
|
physicalSql);
|
||||||
|
if ("NEGATIVE".equalsIgnoreCase(physicalSql.getOpinion())
|
||||||
|
&& StringUtils.isNotBlank(physicalSql.getSql())) {
|
||||||
|
semanticParseInfo.getSqlInfo().setCorrectedQuerySQL(physicalSql.getSql());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Prompt generatePrompt(String queryText, SemanticParseInfo semanticParseInfo,
|
||||||
|
String promptTemplate) {
|
||||||
|
Map<String, Object> variable = new HashMap<>();
|
||||||
|
variable.put("question", queryText);
|
||||||
|
variable.put("sql", semanticParseInfo.getSqlInfo().getQuerySQL());
|
||||||
|
|
||||||
|
return PromptTemplate.from(promptTemplate).apply(variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,10 +14,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
import java.util.stream.Collectors;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static com.tencent.supersonic.headless.chat.parser.ParserConfig.*;
|
import static com.tencent.supersonic.headless.chat.parser.ParserConfig.*;
|
||||||
|
|
||||||
@@ -51,13 +49,33 @@ public class PromptHelper {
|
|||||||
// use random collection of exemplars for each self-consistency inference
|
// use random collection of exemplars for each self-consistency inference
|
||||||
for (int i = 0; i < selfConsistencyNumber; i++) {
|
for (int i = 0; i < selfConsistencyNumber; i++) {
|
||||||
List<Text2SQLExemplar> shuffledList = new ArrayList<>(exemplars);
|
List<Text2SQLExemplar> shuffledList = new ArrayList<>(exemplars);
|
||||||
// only shuffle the exemplars from config
|
List<Text2SQLExemplar> same = shuffledList.stream() // 相似度极高的话,先找出来
|
||||||
List<Text2SQLExemplar> subList =
|
.filter(e -> e.getSimilarity() > 0.989).collect(Collectors.toList());
|
||||||
shuffledList.subList(llmReq.getDynamicExemplars().size(), shuffledList.size());
|
List<Text2SQLExemplar> noSame = shuffledList.stream()
|
||||||
Collections.shuffle(subList);
|
.filter(e -> e.getSimilarity() <= 0.989).collect(Collectors.toList());
|
||||||
results.add(shuffledList.subList(0, Math.min(shuffledList.size(), fewShotNumber)));
|
if ((noSame.size() - same.size()) > fewShotNumber) {// 去除部分最低分
|
||||||
|
noSame.sort(Comparator.comparingDouble(Text2SQLExemplar::getSimilarity));
|
||||||
|
noSame = noSame.subList((noSame.size() - fewShotNumber) / 2, noSame.size());
|
||||||
|
}
|
||||||
|
Text2SQLExemplar mostSimilar = noSame.get(noSame.size() - 1);
|
||||||
|
Collections.shuffle(noSame);
|
||||||
|
List<Text2SQLExemplar> ts;
|
||||||
|
if (same.size() > 0) {// 一样的话,必须作为提示语
|
||||||
|
ts = new ArrayList<>();
|
||||||
|
int needSize = Math.min(noSame.size() + same.size(), fewShotNumber);
|
||||||
|
if (needSize > same.size()) {
|
||||||
|
ts.addAll(noSame.subList(0, needSize - same.size()));
|
||||||
|
}
|
||||||
|
ts.addAll(same);
|
||||||
|
} else { // 至少要一个最像的
|
||||||
|
ts = noSame.subList(0, Math.min(noSame.size(), fewShotNumber));
|
||||||
|
if (!ts.contains(mostSimilar)) {
|
||||||
|
ts.remove(ts.size() - 1);
|
||||||
|
ts.add(mostSimilar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results.add(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.tencent.supersonic.headless.api.pojo.request.SemanticQueryReq;
|
|||||||
import com.tencent.supersonic.headless.api.pojo.response.ParseResp;
|
import com.tencent.supersonic.headless.api.pojo.response.ParseResp;
|
||||||
import com.tencent.supersonic.headless.api.pojo.response.SemanticTranslateResp;
|
import com.tencent.supersonic.headless.api.pojo.response.SemanticTranslateResp;
|
||||||
import com.tencent.supersonic.headless.chat.ChatQueryContext;
|
import com.tencent.supersonic.headless.chat.ChatQueryContext;
|
||||||
|
import com.tencent.supersonic.headless.chat.corrector.LLMPhysicalSqlCorrector;
|
||||||
import com.tencent.supersonic.headless.chat.corrector.SemanticCorrector;
|
import com.tencent.supersonic.headless.chat.corrector.SemanticCorrector;
|
||||||
import com.tencent.supersonic.headless.chat.mapper.SchemaMapper;
|
import com.tencent.supersonic.headless.chat.mapper.SchemaMapper;
|
||||||
import com.tencent.supersonic.headless.chat.parser.SemanticParser;
|
import com.tencent.supersonic.headless.chat.parser.SemanticParser;
|
||||||
@@ -76,6 +77,10 @@ public class ChatWorkflowEngine {
|
|||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
performTranslating(queryCtx, parseResult);
|
performTranslating(queryCtx, parseResult);
|
||||||
parseResult.getParseTimeCost().setSqlTime(System.currentTimeMillis() - start);
|
parseResult.getParseTimeCost().setSqlTime(System.currentTimeMillis() - start);
|
||||||
|
queryCtx.setChatWorkflowState(ChatWorkflowState.PHYSICAL_SQL_CORRECTING);
|
||||||
|
break;
|
||||||
|
case PHYSICAL_SQL_CORRECTING:
|
||||||
|
performPhysicalSqlCorrecting(queryCtx);
|
||||||
queryCtx.setChatWorkflowState(ChatWorkflowState.FINISHED);
|
queryCtx.setChatWorkflowState(ChatWorkflowState.FINISHED);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -162,4 +167,25 @@ public class ChatWorkflowEngine {
|
|||||||
parseResult.setErrorMsg(String.join("\n", errorMsg));
|
parseResult.setErrorMsg(String.join("\n", errorMsg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void performPhysicalSqlCorrecting(ChatQueryContext queryCtx) {
|
||||||
|
List<SemanticQuery> candidateQueries = queryCtx.getCandidateQueries();
|
||||||
|
if (CollectionUtils.isNotEmpty(candidateQueries)) {
|
||||||
|
for (SemanticQuery semanticQuery : candidateQueries) {
|
||||||
|
for (SemanticCorrector corrector : semanticCorrectors) {
|
||||||
|
if (corrector instanceof LLMPhysicalSqlCorrector) {
|
||||||
|
corrector.correct(queryCtx, semanticQuery.getParseInfo());
|
||||||
|
// 如果物理SQL被修正了,更新querySQL为修正后的版本
|
||||||
|
SemanticParseInfo parseInfo = semanticQuery.getParseInfo();
|
||||||
|
if (StringUtils.isNotBlank(parseInfo.getSqlInfo().getCorrectedQuerySQL())) {
|
||||||
|
parseInfo.getSqlInfo().setQuerySQL(parseInfo.getSqlInfo().getCorrectedQuerySQL());
|
||||||
|
log.info("Physical SQL corrected and updated querySQL: {}",
|
||||||
|
parseInfo.getSqlInfo().getQuerySQL());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public class QueryUtils {
|
|||||||
column.setModelId(metric.getModelId());
|
column.setModelId(metric.getModelId());
|
||||||
}
|
}
|
||||||
// if column nameEn contains metric alias, use metric dataFormatType
|
// if column nameEn contains metric alias, use metric dataFormatType
|
||||||
if (column.getDataFormatType() == null && metric.getAlias() != null) {
|
if (column.getDataFormatType() == null && StringUtils.isNotEmpty(metric.getAlias())) {
|
||||||
for (String alias : metric.getAlias().split(",")) {
|
for (String alias : metric.getAlias().split(",")) {
|
||||||
if (nameEn.contains(alias)) {
|
if (nameEn.contains(alias)) {
|
||||||
column.setDataFormatType(metric.getDataFormatType());
|
column.setDataFormatType(metric.getDataFormatType());
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ com.tencent.supersonic.headless.chat.parser.SemanticParser=\
|
|||||||
|
|
||||||
com.tencent.supersonic.headless.chat.corrector.SemanticCorrector=\
|
com.tencent.supersonic.headless.chat.corrector.SemanticCorrector=\
|
||||||
com.tencent.supersonic.headless.chat.corrector.RuleSqlCorrector,\
|
com.tencent.supersonic.headless.chat.corrector.RuleSqlCorrector,\
|
||||||
com.tencent.supersonic.headless.chat.corrector.LLMSqlCorrector
|
com.tencent.supersonic.headless.chat.corrector.LLMSqlCorrector,\
|
||||||
|
com.tencent.supersonic.headless.chat.corrector.LLMPhysicalSqlCorrector
|
||||||
|
|
||||||
com.tencent.supersonic.headless.chat.knowledge.file.FileHandler=\
|
com.tencent.supersonic.headless.chat.knowledge.file.FileHandler=\
|
||||||
com.tencent.supersonic.headless.chat.knowledge.file.FileHandlerImpl
|
com.tencent.supersonic.headless.chat.knowledge.file.FileHandlerImpl
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ com.tencent.supersonic.headless.chat.parser.SemanticParser=\
|
|||||||
|
|
||||||
com.tencent.supersonic.headless.chat.corrector.SemanticCorrector=\
|
com.tencent.supersonic.headless.chat.corrector.SemanticCorrector=\
|
||||||
com.tencent.supersonic.headless.chat.corrector.RuleSqlCorrector,\
|
com.tencent.supersonic.headless.chat.corrector.RuleSqlCorrector,\
|
||||||
com.tencent.supersonic.headless.chat.corrector.LLMSqlCorrector
|
com.tencent.supersonic.headless.chat.corrector.LLMSqlCorrector,\
|
||||||
|
com.tencent.supersonic.headless.chat.corrector.LLMPhysicalSqlCorrector
|
||||||
|
|
||||||
com.tencent.supersonic.headless.chat.knowledge.file.FileHandler=\
|
com.tencent.supersonic.headless.chat.knowledge.file.FileHandler=\
|
||||||
com.tencent.supersonic.headless.chat.knowledge.file.FileHandlerImpl
|
com.tencent.supersonic.headless.chat.knowledge.file.FileHandlerImpl
|
||||||
|
|||||||
10
pom.xml
10
pom.xml
@@ -10,9 +10,9 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
<module>common</module>
|
||||||
<module>auth</module>
|
<module>auth</module>
|
||||||
<module>chat</module>
|
<module>chat</module>
|
||||||
<module>common</module>
|
|
||||||
<module>launchers</module>
|
<module>launchers</module>
|
||||||
<module>headless</module>
|
<module>headless</module>
|
||||||
</modules>
|
</modules>
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
<java.target.version>21</java.target.version>
|
<java.target.version>21</java.target.version>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<maven.compiler.release>21</maven.compiler.release>
|
||||||
<file.encoding>UTF-8</file.encoding>
|
<file.encoding>UTF-8</file.encoding>
|
||||||
<jsqlparser.version>4.9</jsqlparser.version>
|
<jsqlparser.version>4.9</jsqlparser.version>
|
||||||
<pagehelper.version>6.1.0</pagehelper.version>
|
<pagehelper.version>6.1.0</pagehelper.version>
|
||||||
@@ -254,6 +255,13 @@
|
|||||||
<source>${java.source.version}</source>
|
<source>${java.source.version}</source>
|
||||||
<target>${java.target.version}</target>
|
<target>${java.target.version}</target>
|
||||||
<encoding>${file.encoding}</encoding>
|
<encoding>${file.encoding}</encoding>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
5
webapp/package.json
Normal file
5
webapp/package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"supersonic-chat-sdk": "link:packages/chat-sdk"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -79,6 +79,7 @@ export type SqlInfoType = {
|
|||||||
parsedS2SQL: string;
|
parsedS2SQL: string;
|
||||||
correctedS2SQL: string;
|
correctedS2SQL: string;
|
||||||
querySQL: string;
|
querySQL: string;
|
||||||
|
correctedQuerySQL?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChatContextType = {
|
export type ChatContextType = {
|
||||||
|
|||||||
@@ -58,32 +58,28 @@ const SqlItem: React.FC<Props> = ({
|
|||||||
const getSchemaMapText = () => {
|
const getSchemaMapText = () => {
|
||||||
return `
|
return `
|
||||||
Schema映射
|
Schema映射
|
||||||
${schema?.fieldNameList?.length > 0 ? `名称:${schema.fieldNameList.join('、')}` : ''}${
|
${schema?.fieldNameList?.length > 0 ? `名称:${schema.fieldNameList.join('、')}` : ''}${schema?.values?.length > 0
|
||||||
schema?.values?.length > 0
|
|
||||||
? `
|
? `
|
||||||
取值:${schema.values
|
取值:${schema.values
|
||||||
.map((item: any) => {
|
.map((item: any) => {
|
||||||
return `${item.fieldName}: ${item.fieldValue}`;
|
return `${item.fieldName}: ${item.fieldValue}`;
|
||||||
})
|
})
|
||||||
.join('、')}`
|
.join('、')}`
|
||||||
: ''
|
: ''
|
||||||
}${
|
}${priorExts
|
||||||
priorExts
|
|
||||||
? `
|
? `
|
||||||
附加:${priorExts}`
|
附加:${priorExts}`
|
||||||
: ''
|
: ''
|
||||||
}${
|
}${terms?.length > 0
|
||||||
terms?.length > 0
|
|
||||||
? `
|
? `
|
||||||
术语:${terms
|
术语:${terms
|
||||||
.map((item: any) => {
|
.map((item: any) => {
|
||||||
return `${item.name}${item.alias?.length > 0 ? `(${item.alias.join(',')})` : ''}: ${
|
return `${item.name}${item.alias?.length > 0 ? `(${item.alias.join(',')})` : ''}: ${item.description
|
||||||
item.description
|
|
||||||
}`;
|
}`;
|
||||||
})
|
})
|
||||||
.join('、')}`
|
.join('、')}`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
@@ -91,16 +87,16 @@ ${schema?.fieldNameList?.length > 0 ? `名称:${schema.fieldNameList.join('、
|
|||||||
const getFewShotText = () => {
|
const getFewShotText = () => {
|
||||||
return `
|
return `
|
||||||
Few-shot示例${fewShots
|
Few-shot示例${fewShots
|
||||||
.map((item: any, index: number) => {
|
.map((item: any, index: number) => {
|
||||||
return `
|
return `
|
||||||
|
|
||||||
示例${index + 1}:
|
示例${index + 1}:
|
||||||
问题:${item.question}
|
问题:${item.question}
|
||||||
SQL:
|
SQL:
|
||||||
${format(item.sql)}
|
${format(item.sql)}
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join('')}
|
.join('')}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,6 +116,14 @@ ${format(sqlInfo.correctedS2SQL)}
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCorrectedQuerySQLText = () => {
|
||||||
|
return `
|
||||||
|
物理SQL修正
|
||||||
|
|
||||||
|
${format(sqlInfo.correctedQuerySQL || '')}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
const getQuerySQLText = () => {
|
const getQuerySQLText = () => {
|
||||||
return `
|
return `
|
||||||
最终执行SQL
|
最终执行SQL
|
||||||
@@ -155,6 +159,9 @@ ${executeErrorMsg}
|
|||||||
if (sqlInfo.correctedS2SQL) {
|
if (sqlInfo.correctedS2SQL) {
|
||||||
text += getCorrectedS2SQLText();
|
text += getCorrectedS2SQLText();
|
||||||
}
|
}
|
||||||
|
if (sqlInfo.correctedQuerySQL) {
|
||||||
|
text += getCorrectedQuerySQLText();
|
||||||
|
}
|
||||||
if (sqlInfo.querySQL) {
|
if (sqlInfo.querySQL) {
|
||||||
text += getQuerySQLText();
|
text += getQuerySQLText();
|
||||||
}
|
}
|
||||||
@@ -183,9 +190,8 @@ ${executeErrorMsg}
|
|||||||
<div className={`${tipPrefixCls}-content-options`}>
|
<div className={`${tipPrefixCls}-content-options`}>
|
||||||
{llmReq && (
|
{llmReq && (
|
||||||
<div
|
<div
|
||||||
className={`${tipPrefixCls}-content-option ${
|
className={`${tipPrefixCls}-content-option ${sqlType === 'schemaMap' ? `${tipPrefixCls}-content-option-active` : ''
|
||||||
sqlType === 'schemaMap' ? `${tipPrefixCls}-content-option-active` : ''
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSqlType(sqlType === 'schemaMap' ? '' : 'schemaMap');
|
setSqlType(sqlType === 'schemaMap' ? '' : 'schemaMap');
|
||||||
}}
|
}}
|
||||||
@@ -195,9 +201,8 @@ ${executeErrorMsg}
|
|||||||
)}
|
)}
|
||||||
{fewShots.length > 0 && (
|
{fewShots.length > 0 && (
|
||||||
<div
|
<div
|
||||||
className={`${tipPrefixCls}-content-option ${
|
className={`${tipPrefixCls}-content-option ${sqlType === 'fewShots' ? `${tipPrefixCls}-content-option-active` : ''
|
||||||
sqlType === 'fewShots' ? `${tipPrefixCls}-content-option-active` : ''
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSqlType(sqlType === 'fewShots' ? '' : 'fewShots');
|
setSqlType(sqlType === 'fewShots' ? '' : 'fewShots');
|
||||||
}}
|
}}
|
||||||
@@ -207,9 +212,8 @@ ${executeErrorMsg}
|
|||||||
)}
|
)}
|
||||||
{sqlInfo.parsedS2SQL && (
|
{sqlInfo.parsedS2SQL && (
|
||||||
<div
|
<div
|
||||||
className={`${tipPrefixCls}-content-option ${
|
className={`${tipPrefixCls}-content-option ${sqlType === 'parsedS2SQL' ? `${tipPrefixCls}-content-option-active` : ''
|
||||||
sqlType === 'parsedS2SQL' ? `${tipPrefixCls}-content-option-active` : ''
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSqlType(sqlType === 'parsedS2SQL' ? '' : 'parsedS2SQL');
|
setSqlType(sqlType === 'parsedS2SQL' ? '' : 'parsedS2SQL');
|
||||||
}}
|
}}
|
||||||
@@ -219,9 +223,8 @@ ${executeErrorMsg}
|
|||||||
)}
|
)}
|
||||||
{sqlInfo.correctedS2SQL && (
|
{sqlInfo.correctedS2SQL && (
|
||||||
<div
|
<div
|
||||||
className={`${tipPrefixCls}-content-option ${
|
className={`${tipPrefixCls}-content-option ${sqlType === 'correctedS2SQL' ? `${tipPrefixCls}-content-option-active` : ''
|
||||||
sqlType === 'correctedS2SQL' ? `${tipPrefixCls}-content-option-active` : ''
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSqlType(sqlType === 'correctedS2SQL' ? '' : 'correctedS2SQL');
|
setSqlType(sqlType === 'correctedS2SQL' ? '' : 'correctedS2SQL');
|
||||||
}}
|
}}
|
||||||
@@ -229,16 +232,26 @@ ${executeErrorMsg}
|
|||||||
修正S2SQL
|
修正S2SQL
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{sqlInfo.correctedQuerySQL && (
|
||||||
|
<div
|
||||||
|
className={`${tipPrefixCls}-content-option ${sqlType === 'correctedQuerySQL' ? `${tipPrefixCls}-content-option-active` : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
setSqlType(sqlType === 'correctedQuerySQL' ? '' : 'correctedQuerySQL');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
物理SQL修正
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{sqlInfo.querySQL && (
|
{sqlInfo.querySQL && (
|
||||||
<div
|
<div
|
||||||
className={`${tipPrefixCls}-content-option ${
|
className={`${tipPrefixCls}-content-option ${sqlType === 'querySQL' ? `${tipPrefixCls}-content-option-active` : ''
|
||||||
sqlType === 'querySQL' ? `${tipPrefixCls}-content-option-active` : ''
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSqlType(sqlType === 'querySQL' ? '' : 'querySQL');
|
setSqlType(sqlType === 'querySQL' ? '' : 'querySQL');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
最终执行SQL
|
{sqlInfo.correctedQuerySQL ? '最终执行SQL' : '最终执行SQL'}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Button className={`${prefixCls}-export-log`} size="small" onClick={onExportLog}>
|
<Button className={`${prefixCls}-export-log`} size="small" onClick={onExportLog}>
|
||||||
@@ -248,13 +261,12 @@ ${executeErrorMsg}
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`${prefixCls} ${
|
className={`${prefixCls} ${!window.location.pathname.includes('/chat') &&
|
||||||
!window.location.pathname.includes('/chat') &&
|
|
||||||
integrateSystem &&
|
integrateSystem &&
|
||||||
integrateSystem !== 'wiki'
|
integrateSystem !== 'wiki'
|
||||||
? `${prefixCls}-copilot`
|
? `${prefixCls}-copilot`
|
||||||
: ''
|
: ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{sqlType === 'schemaMap' && (
|
{sqlType === 'schemaMap' && (
|
||||||
<div className={`${prefixCls}-code`}>
|
<div className={`${prefixCls}-code`}>
|
||||||
@@ -290,9 +302,8 @@ ${executeErrorMsg}
|
|||||||
<div className={`${prefixCls}-schema-content`}>
|
<div className={`${prefixCls}-schema-content`}>
|
||||||
{terms
|
{terms
|
||||||
.map((item: any) => {
|
.map((item: any) => {
|
||||||
return `${item.name}${
|
return `${item.name}${item.alias?.length > 0 ? `(${item.alias.join(',')})` : ''
|
||||||
item.alias?.length > 0 ? `(${item.alias.join(',')})` : ''
|
}: ${item.description}`;
|
||||||
}: ${item.description}`;
|
|
||||||
})
|
})
|
||||||
.join('、')}
|
.join('、')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -416,11 +416,10 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
|
|
||||||
const onExportData = () => {
|
const onExportData = () => {
|
||||||
const { queryColumns, queryResults } = data || {};
|
const { queryColumns, queryResults } = data || {};
|
||||||
if (!!queryResults) {
|
if (!!queryResults && !!queryColumns) {
|
||||||
const exportData = queryResults.map(item => {
|
const exportData = queryResults.map(item => {
|
||||||
return Object.keys(item).reduce((result, key) => {
|
return queryColumns.reduce((result, column) => {
|
||||||
const columnName = queryColumns?.find(column => column.nameEn === key)?.name || key;
|
result[column.name || column.nameEn] = item[column.nameEn];
|
||||||
result[columnName] = item[key];
|
|
||||||
return result;
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user