diff --git a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiCommon.java b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiCommon.java index 6298c9a1f0..9e78234c0a 100644 --- a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiCommon.java +++ b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiCommon.java @@ -20,7 +20,7 @@ package cn.hutool.v7.ai.model.gemini; * gemini公共类 * * @author elichow - * @since 6.0.0 + * @since 7.0.0 */ public class GeminiCommon { diff --git a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiConfig.java b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiConfig.java index cf016e7a4f..cefc808981 100644 --- a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiConfig.java +++ b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiConfig.java @@ -24,7 +24,7 @@ import cn.hutool.v7.ai.core.BaseAIConfig; * Gemini配置类,初始化API接口地址,设置默认的模型 * * @author elichow - * @since 6.0.0 + * @since 7.0.0 */ public class GeminiConfig extends BaseAIConfig { @@ -39,7 +39,7 @@ public class GeminiConfig extends BaseAIConfig { setModel(DEFAULT_MODEL); } - public GeminiConfig(String apiKey) { + public GeminiConfig(final String apiKey) { this(); setApiKey(apiKey); } diff --git a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiProvider.java b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiProvider.java index a1506cbe7c..3ec8494622 100644 --- a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiProvider.java +++ b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiProvider.java @@ -24,7 +24,7 @@ import cn.hutool.v7.ai.core.AIServiceProvider; * 创建Gemini服务实现类 * * @author elichow - * @since 6.0.0 + * @since 7.0.0 */ public class GeminiProvider implements AIServiceProvider { diff --git a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiService.java b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiService.java index 36fb0c1053..c5b0eab325 100644 --- a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiService.java +++ b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiService.java @@ -29,7 +29,7 @@ import java.util.function.Consumer; * Gemini服务支持的扩展接口 * * @author elichow - * @since 6.0.0 + * @since 7.0.0 */ public interface GeminiService extends AIService { diff --git a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiServiceImpl.java b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiServiceImpl.java index 5e1b161812..e2520b92ae 100644 --- a/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiServiceImpl.java +++ b/hutool-ai/src/main/java/cn/hutool/v7/ai/model/gemini/GeminiServiceImpl.java @@ -51,7 +51,7 @@ import java.util.function.Consumer; * Gemini服务,AI具体功能的实现 * * @author elichow - * @since 6.0.0 + * @since 7.0.0 */ public class GeminiServiceImpl extends BaseAIService implements GeminiService { @@ -66,7 +66,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { } private String getEndpoint(final boolean stream) { - String action = stream ? STREAM_GENERATE_CONTENT : GENERATE_CONTENT; + final String action = stream ? STREAM_GENERATE_CONTENT : GENERATE_CONTENT; return "/models/" + config.getModel() + action; } @@ -93,14 +93,14 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { } @Override - public String chatMultimodal(String prompt, final List mediaList) { + public String chatMultimodal(final String prompt, final List mediaList) { final Map paramMap = buildMultimodalRequestMap(prompt, mediaList); final Response response = sendPost(getEndpoint(false), JSONUtil.toJsonStr(paramMap)); return response.bodyStr(); } @Override - public void chatMultimodal(String prompt, final List mediaList, final Consumer callback) { + public void chatMultimodal(final String prompt, final List mediaList, final Consumer callback) { final Map paramMap = buildMultimodalRequestMap(prompt, mediaList); final String endpoint = getEndpoint(true) + "?alt=sse"; ThreadUtil.newThread(() -> sendPostStream(endpoint, paramMap, callback), "gemini-m-sse").start(); @@ -121,28 +121,28 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { } @Override - public String predictImage(String prompt) { + public String predictImage(final String prompt) { final Map paramMap = buildPredictImageRequestMap(prompt); final Response response = sendPost(getPredictImageEndpoint(), JSONUtil.toJsonStr(paramMap)); return response.bodyStr(); } @Override - public String predictVideo(String prompt) { + public String predictVideo(final String prompt) { final Map paramMap = buildPredictVideoRequestMap(prompt); final Response response = sendPost(getPredictVideoEndpoint(), JSONUtil.toJsonStr(paramMap)); return response.bodyStr(); } @Override - public String getVideoOperation(String operationName) { - String endPoint = "/" + operationName; + public String getVideoOperation(final String operationName) { + final String endPoint = "/" + operationName; final Response response = sendGet(endPoint); return response.bodyStr(); } @Override - public void downLoadVideo(String videoUri, String filePath) { + public void downLoadVideo(final String videoUri, final String filePath) { if (StrUtil.isBlank(videoUri)) { throw new AIException("Video URI is empty"); } @@ -159,14 +159,14 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { } @Override - public String textToSpeech(String prompt) { + public String textToSpeech(final String prompt) { final Map paramMap = buildTextToSpeechRequestMap(prompt); final Response response = sendPost(getEndpoint(false), JSONUtil.toJsonStr(paramMap)); return response.bodyStr(); } @Override - public String textToSpeech(String prompt, String voice) { + public String textToSpeech(final String prompt, final String voice) { final Map voiceConfig = MapUtil.of("prebuilt_voice_config", MapUtil.of("voice_name", voice)); config.putAdditionalConfigByKey("speech_config", MapUtil.of("voice_config", voiceConfig)); return this.textToSpeech(prompt); @@ -188,7 +188,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { .setReadTimeout(config.getTimeout()); final ClientEngine engine = ClientEngineFactory.createEngine().init(clientConfig); - String metadata = JSONUtil.toJsonStr(MapUtil.of("file", MapUtil.of("display_name", file.getName()))); + final String metadata = JSONUtil.toJsonStr(MapUtil.of("file", MapUtil.of("display_name", file.getName()))); final Request initRequest = HttpUtil.createRequest(getUploadBaseUrl(), Method.POST) .header("x-goog-api-key", config.getApiKey()) .header("X-Goog-Upload-Protocol", "resumable") @@ -198,8 +198,8 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { .header("Content-Type", "application/json") .body(metadata); - String sessionUrl; - try (Response initRes = engine.send(initRequest)) { + final String sessionUrl; + try (final Response initRes = engine.send(initRequest)) { sessionUrl = initRes.header("X-Goog-Upload-URL"); } @@ -212,20 +212,20 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { .header("X-Goog-Upload-Offset", "0") .header("Content-Length", String.valueOf(file.length())); - FileResource fileResource = new FileResource(file); + final FileResource fileResource = new FileResource(file); - HttpResource httpResource = new HttpResource(fileResource, mimeType); + final HttpResource httpResource = new HttpResource(fileResource, mimeType); uploadRequest.body(new ResourceBody(httpResource)); - try (Response uploadRes = engine.send(uploadRequest)) { + try (final Response uploadRes = engine.send(uploadRequest)) { if (uploadRes.isOk()) { return uploadRes.bodyStr(); } else { throw new AIException("Upload failed with status: " + uploadRes.getStatus()); } } - } catch (Exception e) { + } catch (final Exception e) { throw new AIException("Gemini upload failed: " + e.getMessage(), e); } } @@ -281,7 +281,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { * 动态根据 API 配置生成 Upload 地址 */ private String getUploadBaseUrl() { - String apiUrl = config.getApiUrl(); + final String apiUrl = config.getApiUrl(); //自动提取域名部分 if (StrUtil.contains(apiUrl, "generativelanguage.googleapis.com")) { return "https://generativelanguage.googleapis.com/upload/v1beta/files"; @@ -290,7 +290,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { try { final URL url = new URL(apiUrl); return new URL(url.getProtocol(), url.getHost(), url.getPort(), UPLOAD_BASE_URL).toString(); - } catch (Exception e) { + } catch (final Exception e) { return apiUrl.replace("/models/", "/upload/v1beta/files").split("/models")[0]; } } @@ -300,7 +300,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { final List> contents = new ArrayList<>(); Map systemInstruction = null; - for (Message msg : messages) { + for (final Message msg : messages) { if ("system".equalsIgnoreCase(msg.getRole())) { systemInstruction = MapUtil.ofEntries(MapUtil.entry("parts", Collections.singletonList(MapUtil.ofEntries(MapUtil.entry("text", msg.getContent()))))); @@ -319,12 +319,12 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { return paramMap; } - private Map buildMultimodalRequestMap(String prompt, final List mediaList) { + private Map buildMultimodalRequestMap(final String prompt, final List mediaList) { final List> parts = new ArrayList<>(); parts.add(MapUtil.ofEntries(MapUtil.entry("text", prompt))); if (mediaList != null && !mediaList.isEmpty()) { - for (String media : mediaList) { + for (final String media : mediaList) { if (StrUtil.isBlank(media)) { continue; } @@ -336,7 +336,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { fileUri = "https://generativelanguage.googleapis.com/v1beta/" + media; } //直接从服务端获取该文件上传时真实记录的 mimeType - String realMimeType = getRemoteFileMimeType(fileUri); + final String realMimeType = getRemoteFileMimeType(fileUri); parts.add(MapUtil.ofEntries( MapUtil.entry("file_data", MapUtil.ofEntries( MapUtil.entry("mime_type", realMimeType), @@ -359,7 +359,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { MapUtil.entry("data", Base64.encode(bytes)) )) )); - } catch (Exception e) { + } catch (final Exception e) { throw new AIException("Failed to download media from URL: " + media, e.getMessage()); } } else { @@ -387,7 +387,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { return paramMap; } - private Map buildPredictVideoRequestMap(String prompt) { + private Map buildPredictVideoRequestMap(final String prompt) { final Map instance = new HashMap<>(); instance.put("prompt", prompt); @@ -406,7 +406,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { return paramMap; } - private Map buildPredictImageRequestMap(String prompt) { + private Map buildPredictImageRequestMap(final String prompt) { final Map instance = new HashMap<>(); instance.put("prompt", prompt); @@ -433,7 +433,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { return paramMap; } - private Map buildTextToSpeechRequestMap(String prompt) { + private Map buildTextToSpeechRequestMap(final String prompt) { final Map paramMap = new HashMap<>(); final Map part = new HashMap<>(); part.put("text", prompt); @@ -462,21 +462,21 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { * @param fileUri 文件URI * @return MIME类型 */ - private String getRemoteFileMimeType(String fileUri) { + private String getRemoteFileMimeType(final String fileUri) { try { HttpGlobalConfig.setTimeout(config.getTimeout()); - Request httpRequest = HttpUtil.createGet(fileUri) + final Request httpRequest = HttpUtil.createGet(fileUri) .header(HeaderName.ACCEPT, "application/json") .header("x-goog-api-key", config.getApiKey()); - String responseBody = httpRequest.send().bodyStr(); + final String responseBody = httpRequest.send().bodyStr(); final JSONObject json = JSONUtil.parseObj(responseBody); //提取服务端的mimeType - String mimeType = json.getStr("mimeType"); + final String mimeType = json.getStr("mimeType"); if (StrUtil.isNotBlank(mimeType)) { return mimeType; } - } catch (Exception e) { + } catch (final Exception e) { throw new AIException("Failed to get remote file MIME type", e.getMessage()); } return "application/octet-stream"; @@ -503,7 +503,7 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { } @Override - protected Response sendPost(String endpoint, String paramJson) { + protected Response sendPost(final String endpoint, final String paramJson) { //链式构建请求 try { //设置超时3分钟 @@ -527,11 +527,11 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { * @param callback 流式数据回调函数 */ @Override - protected void sendPostStream(String endpoint, final Map paramMap, final Consumer callback) { + protected void sendPostStream(final String endpoint, final Map paramMap, final Consumer callback) { HttpURLConnection connection = null; try { // 创建连接 - URL apiUrl = new URL(config.getApiUrl() + endpoint); + final URL apiUrl = new URL(config.getApiUrl() + endpoint); connection = (HttpURLConnection) apiUrl.openConnection(); connection.setRequestMethod(Method.POST.name()); connection.setRequestProperty(HeaderName.CONTENT_TYPE.getValue(), "application/json"); @@ -542,21 +542,21 @@ public class GeminiServiceImpl extends BaseAIService implements GeminiService { //设置连接超时 connection.setConnectTimeout(config.getTimeout()); // 发送请求体 - try (OutputStream os = connection.getOutputStream()) { - String jsonInputString = JSONUtil.toJsonStr(paramMap); + try (final OutputStream os = connection.getOutputStream()) { + final String jsonInputString = JSONUtil.toJsonStr(paramMap); os.write(jsonInputString.getBytes()); os.flush(); } // 读取流式响应 - try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { // 调用回调函数处理每一行数据 callback.accept(line); } } - } catch (Exception e) { + } catch (final Exception e) { callback.accept("{\"error\": \"" + e.getMessage() + "\"}"); } finally { // 关闭连接 diff --git a/hutool-core/src/main/java/cn/hutool/v7/core/date/DateUnit.java b/hutool-core/src/main/java/cn/hutool/v7/core/date/DateUnit.java index 42a792fed3..f8d578c152 100644 --- a/hutool-core/src/main/java/cn/hutool/v7/core/date/DateUnit.java +++ b/hutool-core/src/main/java/cn/hutool/v7/core/date/DateUnit.java @@ -81,7 +81,7 @@ public enum DateUnit { */ public static DateUnit of(final ChronoUnit unit) { return switch (unit) { - case MICROS -> DateUnit.MS; + case MILLIS -> DateUnit.MS; case SECONDS -> DateUnit.SECOND; case MINUTES -> DateUnit.MINUTE; case HOURS -> DateUnit.HOUR; @@ -100,7 +100,7 @@ public enum DateUnit { */ public static ChronoUnit toChronoUnit(final DateUnit unit) { return switch (unit) { - case MS -> ChronoUnit.MICROS; + case MS -> ChronoUnit.MILLIS; case SECOND -> ChronoUnit.SECONDS; case MINUTE -> ChronoUnit.MINUTES; case HOUR -> ChronoUnit.HOURS;