package com.smtaiserver.smtaiserver.core; import java.io.File; import java.net.URLEncoder; import java.sql.Connection; import java.sql.DriverManager; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.duckdb.DuckDBAppender; import org.duckdb.DuckDBConnection; import org.springframework.web.context.request.NativeWebRequest; import com.smtservlet.core.SMTRequest; import com.smtservlet.exception.SMTAuthorityException; import com.smtservlet.util.Json; import com.smtservlet.util.SMTJsonWriter; import com.smtservlet.util.SMTStatic; import com.smtaiserver.smtaiserver.database.SMTDatabase; import com.smtaiserver.smtaiserver.database.SMTDatabase.DBRecord; import com.smtaiserver.smtaiserver.database.SMTDatabase.DBRecords; import com.smtaiserver.smtaiserver.javaai.qwen.SMTQwenApp; import com.smtservlet.core.SMTApp.SMTRequestConfig; public class SMTAIServerRequest extends SMTRequest { public static class AIAttachFile { public String _fileId; public String _fileName; public String _fileText; public AIAttachFile(String fileId, String fileName, String fileText) { _fileId = fileId; _fileName = fileName; _fileText = fileText; } public String toAttachMessage() { return "附加文件 : " + _fileName + "\n" + _fileText + "\n"; } public void addToJson(SMTJsonWriter jsonWr) { jsonWr.addKeyValue("file_id", _fileId); jsonWr.addKeyValue("file_name", _fileName); jsonWr.addKeyValue("file_content", _fileText); } } private static Logger _logger = LogManager.getLogger(SMTAIServerRequest.class); private static Set _setPublicUrl; private static List _listPublicUrl; static { _listPublicUrl = new ArrayList<>(); _listPublicUrl.add("swagger-resources"); _listPublicUrl.add("v2/api-docs"); _listPublicUrl.add("swagger-json"); _setPublicUrl = new HashSet(); } private String _curGroupType = null; private boolean _isChunked = false; private String _chatHistoryId; private String _aiQuestion; private String _rawQuestion; private String _asyncProcessId; private StringBuilder _sbTraceQuestion; private SMTJsonWriter _jsonWrParamJson; private SMTJsonWriter _jsonWrExtCallJson; private SMTJsonWriter _jsonWrAsyncQueryJson; private List _listSupervisorJson; private Map _mapId2CallFuncJson; private boolean _isAgentCheckMode = false; private List _listChatExecProcess = null; private String[] _chatOrderDimName = null; private List _contentSampleQuestion = null; private double[] _curQuestionPos; private SMTDatabase _dbDuckResult = null; private SMTJsonWriter _jsonWrResult = null; private List _listJsonWrResult = null; private boolean _disableConclusion = false; private List _listAttchFile = null; private Json _jsonAttachTables = null; private Json _jsonAttachMetrics = null; private long _lastSendChunkedStreamBlockTick = 0; private boolean _sendStarStream = false; // 将输出文字变成* @Override public void initInstance(NativeWebRequest webRequest, String requestCode, SMTRequestConfig requestConfig) throws Exception { super.initInstance(webRequest, requestCode, requestConfig); boolean isPublicUrl = false; for(String url : _listPublicUrl) { if(requestCode.startsWith(url)) { isPublicUrl = true; break; } } if(!isPublicUrl && !_setPublicUrl.contains(requestCode) && !requestConfig._jsonConfig.safeGetBoolean("no_shrio", false)) { if(this.getLoginUserId() == null) throw new SMTAuthorityException("无权限"); } } public boolean setSendStarStream(boolean isSetStartStream) { boolean org = _sendStarStream; if("true".equals(System.getProperty("show_ai_stream"))) isSetStartStream = false; _sendStarStream = isSetStartStream; return org; } public void setAttachTables(Json jsonTables) { _jsonAttachTables = jsonTables; } public void setJsonAttachMetrics(Json attachMetrics) { _jsonAttachMetrics = attachMetrics; } public String[] getAttachMessage() { List list = new ArrayList<>(); if(_listAttchFile != null) { for(AIAttachFile file : _listAttchFile) { list.add(file.toAttachMessage()); } } if(_jsonAttachTables != null) { for(Json jsonAttachTable : _jsonAttachTables.asJsonList()) { StringBuilder sbText = new StringBuilder(); sbText.append("现有表格:" + jsonAttachTable.getJson("title").asString() + "\n"); List jsonColumns = jsonAttachTable.getJson("columns").asJsonList(); for(int i = 0; i < jsonColumns.size(); i ++) { if(i > 0) sbText.append(","); sbText.append(jsonColumns.get(i).asString()); } sbText.append("\n"); for(Json jsonRecord : jsonAttachTable.getJson("values").asJsonList()) { List listRec = jsonRecord.asJsonList(); for(int i = 0; i < listRec.size(); i ++) { if(i > 0) sbText.append(","); if(listRec.get(i).isNull()) sbText.append(""); else sbText.append(listRec.get(i).asString()); } sbText.append("\n"); } sbText.append("\n"); list.add(sbText.toString()); } } if(_jsonAttachMetrics != null) { for(Json jsonAttachTable : _jsonAttachMetrics.asJsonList()) { StringBuilder sbText = new StringBuilder(); sbText.append("现有表格:" + jsonAttachTable.getJson("title").asString() + "\n"); List jsonColumns = jsonAttachTable.getJson("columns").asJsonList(); for(int i = 0; i < jsonColumns.size(); i ++) { if(i > 0) sbText.append(","); sbText.append(jsonColumns.get(i).asString()); } sbText.append("\n"); for(Json jsonRecord : jsonAttachTable.getJson("values").asJsonList()) { List listRec = jsonRecord.asJsonList(); for(int i = 0; i < listRec.size(); i ++) { if(i > 0) sbText.append(","); if(listRec.get(i).isNull()) sbText.append(""); else sbText.append(listRec.get(i).asString()); } sbText.append("\n"); } sbText.append("\n"); list.add(sbText.toString()); } } return list.size() == 0 ? null : list.toArray(new String[list.size()]); } public void addAttachMessageToJson(Json jsonResult) { if(_listAttchFile != null) { SMTJsonWriter jsonWr = new SMTJsonWriter(true); for(AIAttachFile file : _listAttchFile) { jsonWr.beginMap(null); file.addToJson(jsonWr); jsonWr.endMap(); } jsonResult.set("attach_files", jsonWr.getRootJson()); } if(_jsonAttachTables != null) { jsonResult.set("attach_tables", _jsonAttachTables); } if(_jsonAttachMetrics != null) { jsonResult.set("attach_metrics", _jsonAttachMetrics); } } public void addAttachFile(AIAttachFile attachFile) { if(_listAttchFile == null) _listAttchFile = new ArrayList<>(); _listAttchFile.add(attachFile); } public void setDisableConclusion(boolean set) { _disableConclusion = set; } public boolean isDisableConclusion() { return _disableConclusion; } public SMTJsonWriter finishResultJsonWr() { SMTJsonWriter jsonWrResult = _jsonWrResult; _jsonWrResult = null; if(jsonWrResult == null || jsonWrResult.getRootJson().asJsonMap().size() == 0) { _listChatExecProcess = null; _mapId2CallFuncJson = null; return null; } if(_listJsonWrResult == null) _listJsonWrResult = new ArrayList<>(); // "exec_process"输出,并清空 if(_listChatExecProcess != null) { Object[] array = new Object[_listChatExecProcess.size()]; for(int i = 0; i < _listChatExecProcess.size(); i ++) { array[i] = _listChatExecProcess.get(i); } jsonWrResult.addKeyRaw("exec_process", Json.array(array)); _listChatExecProcess = null; } // call_func不在此处 if(_mapId2CallFuncJson != null) { jsonWrResult.beginMap("call_func"); for(Entry entry : _mapId2CallFuncJson.entrySet()) { jsonWrResult.addKeyRaw(entry.getKey(), entry.getValue()); } jsonWrResult.endMap(); _mapId2CallFuncJson = null; } _listJsonWrResult.add(jsonWrResult); return jsonWrResult; } public List getResultJsonWrList() { return _listJsonWrResult; } public SMTJsonWriter getResultJsonWr() { if(_jsonWrResult == null) _jsonWrResult = new SMTJsonWriter(false); return _jsonWrResult; } public void clearChatStreamReplyValue() throws Exception { SMTAIServerApp.getApp().clearChatStreamReply(this.getSessionId()); } public void setChatStreamReplyValue(String replyId, Json jsonReply) throws Exception { SMTAIServerApp.getApp().setChatStreamReply(this.getSessionId(), replyId, jsonReply); } public Json getChatStreamReplyValue(String replyId) throws Exception { Json jsonReply = SMTAIServerApp.getApp().getChatStreamReply(this.getSessionId(), replyId); return jsonReply; } public void timeBucketTable(String tableName, String timeField, String aggSQL, String aggField, int stepUnit, int stepValue) throws Exception { SMTDatabase dbResult = this.getResultDB(); String oldTableName = "d_" + SMTStatic.newUUID(); String newTableName = tableName; dbResult.executeSQL("ALTER TABLE " + tableName + " RENAME TO " + oldTableName, null); String sql = " SELECT _BUCKET_OTIME_ AS " + timeField + ", " + aggField + " FROM (" + " SELECT time_bucket(INTERVAL '" + SMTStatic.toString(stepValue) + " " + (stepUnit == 0 ? "minute" : "month") + "'," + timeField + ") AS _BUCKET_OTIME_, " + aggSQL + " AS " + aggField + " FROM " + oldTableName + " GROUP BY _BUCKET_OTIME_" + " ) T" + " ORDER BY OTIME" ; DuckDBAppender[] appender = new DuckDBAppender[1]; try { _logger.info("timeBucketTable SQL:\n" + sql); dbResult.querySQLNotify(sql, null, new SMTDatabase.DBQueryNotifyMeta() { @Override public boolean onMetaInfo(DBRecords metaInfo, String[] colTypes) throws Exception { SMTDatabase dbResult = createResultTable(newTableName, metaInfo, colTypes); appender[0] = ((DuckDBConnection)dbResult.getConnection()).createAppender(DuckDBConnection.DEFAULT_SCHEMA, newTableName); return true; } @Override public boolean onNextRecord(DBRecord rec) throws Exception { Object[] values = rec.getValues(); appender[0].beginRow(); for(Object value : values) { if(value == null) appender[0].append(null); else appender[0].append(SMTStatic.toString(value)); } appender[0].endRow(); return true; } }); } finally { if(appender[0] != null) { appender[0].close(); appender[0] = null; } } dbResult.executeSQL("DROP TABLE " + oldTableName, null); } public SMTDatabase createResultTable(String tableName, DBRecords metaInfo, String[] sColTypes) throws Exception { // 生成创建结果表的SQL,并创建结果表 StringBuilder sbFields = new StringBuilder(); for(Entry entry : metaInfo.getFieldMap().entrySet()) { if(sbFields.length() > 0) sbFields.append(","); sbFields.append(entry.getKey() + " " + sColTypes[entry.getValue()]); } SMTDatabase dbResult = this.getResultDB(); dbResult.executeSQL("CREATE TABLE " + tableName + "(" + sbFields.toString() + ")", null); return dbResult; } public SMTDatabase getResultDB() throws Exception { if(_dbDuckResult == null) { String fileName = System.getProperty("result_duckdb", ""); if(!SMTStatic.isNullOrEmpty(fileName)) { File file = new File(fileName); file.delete(); } Connection conn = DriverManager.getConnection("jdbc:duckdb:" + fileName); _dbDuckResult = new SMTDatabase(conn); } return _dbDuckResult; } public void closeQuestionResource() throws Exception { this.clearChatStreamReplyValue(); if(_dbDuckResult != null) { _dbDuckResult.close(); _dbDuckResult = null; } } public void setCurQuestionPos(double[] pos) throws Exception { if(pos != null) _curQuestionPos = SMTAIServerApp.getApp().convGisToMapTransform(pos); else _curQuestionPos = null; } public double[] getCurQuestionPos() { return _curQuestionPos; } public int getContentSampleQuestionCount() { return _contentSampleQuestion == null ? 0 : _contentSampleQuestion.size(); } public void setCurGroupType(String groupType) { _curGroupType = groupType; } public String getCurGroupType() { return _curGroupType; } public void setAgentGroupSet(Set setAgentGroup) throws Exception { setSessionAttribute("login_agent_group", setAgentGroup); } @SuppressWarnings("unchecked") public Set getAgentGroupSet() throws Exception { Set setResult = (Set) this.getSessionAttribute("login_agent_group"); if(setResult == null) return new HashSet<>(); return setResult; } public void addContentSampleQuestion(String question) { if(_contentSampleQuestion == null) _contentSampleQuestion = new ArrayList<>(); _contentSampleQuestion.add(question); } public void limitContentSampleQuestion(int limit) { while(_contentSampleQuestion.size() > limit) _contentSampleQuestion.remove(_contentSampleQuestion.size() - 1); } public List getContentSampleQuestion() { return _contentSampleQuestion; } public void setChatOrderDimName(String dimName, String orderDir) { _chatOrderDimName = new String[] {dimName.toLowerCase(), orderDir.toUpperCase()}; } public String[] getChatOrderDimName() { return _chatOrderDimName; } public List getChatExecProcessList() { return _listChatExecProcess; } public void setChunkedMode(boolean set) { _isChunked = set; } public Json sendReplyChunkedBlock(String replyId, Json jsonAsk) throws Exception { if(!_isChunked) return null; sendChunkedBlock("question", jsonAsk); this.setChatStreamReplyValue(replyId, Json.object()); while(true) { Json jsonReply = this.getChatStreamReplyValue(replyId); if(jsonReply == null) return null; if(jsonReply.safeGetBoolean("json_reply", false)) return jsonReply; Thread.sleep(1000); } } public void sendChunkedResultBlock() throws Exception { SMTJsonWriter jsonWrResult = finishResultJsonWr(); if(jsonWrResult != null) { sendChunkedBlock("result", jsonWrResult.getRootJson()); } } public void sendChunkedStreamBlock(String stream) throws Exception { if(_sendStarStream) { if((System.currentTimeMillis() - _lastSendChunkedStreamBlockTick) < 2000) return; _lastSendChunkedStreamBlockTick = System.currentTimeMillis(); stream = "#"; if(stream.indexOf("\n") >= 0) stream += "\n"; } sendChunkedBlock("send_stream", stream); } public void sendChunkedBlock(String mode, Object value) throws Exception { Json jsonResult; if(value == null) { jsonResult = Json.object("mode", mode, "type", "null"); } else if(value instanceof String) { jsonResult = Json.object("mode", mode, "type", "string", "value", value); } else if(value instanceof Json) { jsonResult = Json.object("mode", mode, "type", "json", "value", value); } else if(value instanceof SMTJsonWriter) { jsonResult = Json.object("mode", mode, "type", "json", "value", ((SMTJsonWriter)value).getRootJson()); } else { throw new Exception("unknow chunked value type : " + value.getClass().toString()); } if(!"result".equals(mode)) { if(_listChatExecProcess == null) _listChatExecProcess = new ArrayList<>(); _listChatExecProcess.add(jsonResult); } if(!_isChunked) { _logger.info("NO sendChunkedBlock:" + jsonResult.toString()); } else { _logger.info("sendChunkedBlock:" + jsonResult.toString()); String text = URLEncoder.encode(jsonResult.toString(), "UTF-8").replace("+", "%20") + "\n"; this.getResponse().getOutputStream().write(text.getBytes("UTF-8")); // this.getResponse().getWriter().print(String.format( // "%d\r\n%s\r\n", text.length(), text // )); this.getResponse().flushBuffer(); } } public String getClientAddr() { return SMTStatic.getClientAddr(this.getRequest()); } public void appendOperateLog(SMTDatabase db, String type, String note) throws Exception { db.executeSQL("INSERT INTO sys_operate_log(op_id, op_type, op_time, op_user, op_note, client_ip)VALUES(?,?,?,?,?,?)", new Object[] { SMTStatic.newUUID(), type, new Date(), this.getLoginUserId(), note, getClientAddr() }); } public boolean isAgentCheckMode() { return _isAgentCheckMode; } public void setAgentCheckMode(boolean set) { _isAgentCheckMode = set; } public void appendCallFuncJson(String key, Json jsonCallFunc) { if(_mapId2CallFuncJson == null) _mapId2CallFuncJson = new LinkedHashMap<>(); _mapId2CallFuncJson.put(key, jsonCallFunc); } public void appendSupervisorJson(List jsonSupervisorList) { _listSupervisorJson = jsonSupervisorList; } public List getSupervisorJsonList() { return _listSupervisorJson; } public Map getCallFuncJsonMap() { return _mapId2CallFuncJson; } public String getChatHistoryId() { if(_chatHistoryId == null) _chatHistoryId = SMTStatic.newUUID(); return _chatHistoryId; } public SMTJsonWriter prepareAsyncQueryJson() { if(_jsonWrAsyncQueryJson == null) _jsonWrAsyncQueryJson = new SMTJsonWriter(false); return _jsonWrAsyncQueryJson; } public SMTJsonWriter getAsyncQueryJson() { if(_jsonWrAsyncQueryJson == null) return null; return _jsonWrAsyncQueryJson; } public SMTJsonWriter prepareExtCallJsonWriter() { if(_jsonWrExtCallJson == null) _jsonWrExtCallJson = new SMTJsonWriter(true); return _jsonWrExtCallJson; } public Json getExtCallJsonWriter() { if(_jsonWrExtCallJson == null) return null; Json json = _jsonWrExtCallJson.getRootJson(); if(json.asJsonList().size() == 0) return null; return json; } public SMTJsonWriter prepareParamJsonWriter() { if(_jsonWrParamJson == null) _jsonWrParamJson = new SMTJsonWriter(false); return _jsonWrParamJson; } public Json getParamJson() { if(_jsonWrParamJson == null) return null; Json json = _jsonWrParamJson.getRootJson(); if(json.asJsonMap().size() == 0) return null; return json; } public void setRawQuestion(String rawQuestion) { _rawQuestion = rawQuestion; } public void setAIQuestion(String aiQuestion) { _aiQuestion = aiQuestion; } public String getAIQuestion() { return _aiQuestion; } public String getRawQuestion() { return _rawQuestion; } public void clearSession() throws Exception { this.getSession().removeAttribute(_requestUserIdKey); } public void setAsynProcessId(String processId) { _asyncProcessId = processId; } public void removeAsynProcessId() throws Exception { if(_asyncProcessId == null) return; SMTAIServerApp.getApp().removeAsyncProcessText(this.getSessionId(), _asyncProcessId); } public void setAsynProcessText(String text) throws Exception { if(_asyncProcessId == null) return; SMTAIServerApp.getApp().setAsyncProcessText(this.getSessionId(), _asyncProcessId, text); } public String getTraceLLMDebug() { if(_sbTraceQuestion == null) return null; return _sbTraceQuestion.toString(); } public void traceLLMDebug(String trace) { if(_sbTraceQuestion == null) _sbTraceQuestion = new StringBuilder(); _sbTraceQuestion.append(SMTStatic.toString(new Date()) + " - " + trace + "\n"); if(SMTQwenApp.getApp().isTraceLLM()) _logger.info(trace); } public void traceLLMPrompt(String trace) { _logger.info("大模型提示词:\n[=======================\n" + trace + "\n=======================]\n"); } }