代码初始化
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package com.vetti.common.ai;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 文本转换为语音
|
||||
*/
|
||||
public class ElevenLabsClient {
|
||||
|
||||
private static final String BASE_URL = "https://api.elevenlabs.io/v1";
|
||||
private final String apiKey;
|
||||
private final CloseableHttpClient httpClient;
|
||||
private final Gson gson;
|
||||
|
||||
public ElevenLabsClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.httpClient = HttpClients.createDefault();
|
||||
this.gson = new Gson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文本转换为语音并保存到文件
|
||||
*
|
||||
* @param text 要转换的文本
|
||||
* @param voiceId 语音ID (可从ElevenLabs网站获取)
|
||||
* @param outputFilePath 输出文件路径
|
||||
* @throws IOException 网络请求或文件操作异常
|
||||
*/
|
||||
public void textToSpeech(String text, String voiceId, String outputFilePath) throws IOException {
|
||||
HttpPost httpPost = new HttpPost(BASE_URL + "/text-to-speech/" + voiceId);
|
||||
httpPost.setHeader("xi-api-key", apiKey);
|
||||
httpPost.setHeader("Content-Type", "application/json");
|
||||
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("text", text);
|
||||
payload.put("model_id", "eleven_monolingual_v1");
|
||||
payload.put("voice_settings", new VoiceSettings(0.7, 0.5));
|
||||
|
||||
StringEntity entity = new StringEntity(gson.toJson(payload), ContentType.APPLICATION_JSON);
|
||||
httpPost.setEntity(entity);
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
if (responseEntity != null) {
|
||||
try (InputStream inputStream = responseEntity.getContent();
|
||||
FileOutputStream outputStream = new FileOutputStream(outputFilePath)) {
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的语音列表
|
||||
*
|
||||
* @return 语音列表响应
|
||||
* @throws IOException 网络请求异常
|
||||
*/
|
||||
public VoicesResponse getVoices() throws IOException {
|
||||
HttpGet httpGet = new HttpGet(BASE_URL + "/voices");
|
||||
httpGet.setHeader("xi-api-key", apiKey);
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String responseBody = EntityUtils.toString(responseEntity);
|
||||
return gson.fromJson(responseBody, VoicesResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭HTTP客户端
|
||||
public void close() throws IOException {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
// 语音设置内部类
|
||||
private static class VoiceSettings {
|
||||
private double stability;
|
||||
private double similarity_boost;
|
||||
|
||||
public VoiceSettings(double stability, double similarity_boost) {
|
||||
this.stability = stability;
|
||||
this.similarity_boost = similarity_boost;
|
||||
}
|
||||
|
||||
// getter方法
|
||||
public double getStability() {
|
||||
return stability;
|
||||
}
|
||||
|
||||
public double getSimilarity_boost() {
|
||||
return similarity_boost;
|
||||
}
|
||||
}
|
||||
|
||||
// 语音列表响应模型
|
||||
public static class VoicesResponse {
|
||||
private Voice[] voices;
|
||||
|
||||
public Voice[] getVoices() {
|
||||
return voices;
|
||||
}
|
||||
|
||||
public void setVoices(Voice[] voices) {
|
||||
this.voices = voices;
|
||||
}
|
||||
|
||||
public static class Voice {
|
||||
private String voice_id;
|
||||
private String name;
|
||||
private String category;
|
||||
private FineTuning fine_tuning;
|
||||
|
||||
public String getVoice_id() {
|
||||
return voice_id;
|
||||
}
|
||||
|
||||
public void setVoice_id(String voice_id) {
|
||||
this.voice_id = voice_id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public FineTuning getFine_tuning() {
|
||||
return fine_tuning;
|
||||
}
|
||||
|
||||
public void setFine_tuning(FineTuning fine_tuning) {
|
||||
this.fine_tuning = fine_tuning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
public static void main(String[] args) {
|
||||
String apiKey = "sk_5240d8f56cb1eb5225fffcf903f62479884d1af5b3de6812";
|
||||
ElevenLabsClient client = new ElevenLabsClient(apiKey);
|
||||
|
||||
try {
|
||||
// 获取可用语音
|
||||
VoicesResponse voicesResponse = client.getVoices();
|
||||
if (voicesResponse != null && voicesResponse.getVoices() != null && voicesResponse.getVoices().length > 0) {
|
||||
System.out.println("Available voices:");
|
||||
for (VoicesResponse.Voice voice : voicesResponse.getVoices()) {
|
||||
System.out.println(voice.getName() + " (ID: " + voice.getVoice_id() + ")");
|
||||
}
|
||||
|
||||
// 使用第一个可用语音进行文本转语音
|
||||
String firstVoiceId = voicesResponse.getVoices()[0].getVoice_id();
|
||||
String text = "Come on, Baby,Come on, Baby,Come on, Baby,Come on, Baby";
|
||||
String outputFile = "output.mp3";
|
||||
|
||||
client.textToSpeech(text, firstVoiceId, outputFile);
|
||||
System.out.println("Audio saved to: " + outputFile);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.vetti.common.ai;
|
||||
|
||||
public class FineTuning {
|
||||
|
||||
private String status;
|
||||
private String model_id;
|
||||
|
||||
public String getModel_id() {
|
||||
return model_id;
|
||||
}
|
||||
|
||||
public void setModel_id(String model_id) {
|
||||
this.model_id = model_id;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
159
vetti-common/src/main/java/com/vetti/common/ai/WapiAiClient.java
Normal file
159
vetti-common/src/main/java/com/vetti/common/ai/WapiAiClient.java
Normal file
@@ -0,0 +1,159 @@
|
||||
package com.vetti.common.ai;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 发送文本消息到指定的WhatsApp
|
||||
*/
|
||||
public class WapiAiClient {
|
||||
|
||||
private static final String BASE_URL = "https://wapi.ai/api";
|
||||
private final String apiKey;
|
||||
private final CloseableHttpClient httpClient;
|
||||
private final Gson gson;
|
||||
|
||||
public WapiAiClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.httpClient = HttpClients.createDefault();
|
||||
this.gson = new Gson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文本消息到指定的WhatsApp号码
|
||||
* @param phoneNumber 目标电话号码(带国家代码,如:+1234567890)
|
||||
* @param message 要发送的消息内容
|
||||
* @return API响应
|
||||
* @throws IOException 网络请求异常
|
||||
*/
|
||||
public WapiResponse sendTextMessage(String phoneNumber, String message) throws IOException {
|
||||
HttpPost httpPost = new HttpPost(BASE_URL + "/sendMessage");
|
||||
httpPost.setHeader("Authorization", "Bearer " + apiKey);
|
||||
httpPost.setHeader("Content-Type", "application/json");
|
||||
|
||||
Map<String, Object> payload = new HashMap<>();
|
||||
payload.put("phone", phoneNumber);
|
||||
payload.put("body", message);
|
||||
|
||||
StringEntity entity = new StringEntity(gson.toJson(payload), ContentType.APPLICATION_JSON);
|
||||
httpPost.setEntity(entity);
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String responseBody = EntityUtils.toString(responseEntity);
|
||||
return gson.fromJson(responseBody, WapiResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近的消息
|
||||
* @param limit 消息数量限制
|
||||
* @return 消息列表
|
||||
* @throws IOException 网络请求异常
|
||||
*/
|
||||
public WapiMessagesResponse getRecentMessages(int limit) throws IOException {
|
||||
HttpGet httpGet = new HttpGet(BASE_URL + "/messages?limit=" + limit);
|
||||
httpGet.setHeader("Authorization", "Bearer " + apiKey);
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String responseBody = EntityUtils.toString(responseEntity);
|
||||
return gson.fromJson(responseBody, WapiMessagesResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭HTTP客户端
|
||||
public void close() throws IOException {
|
||||
httpClient.close();
|
||||
}
|
||||
|
||||
// 响应模型类
|
||||
public static class WapiResponse {
|
||||
|
||||
private boolean success;
|
||||
private String message;
|
||||
private Object data;
|
||||
|
||||
// getter和setter方法
|
||||
public boolean isSuccess() { return success; }
|
||||
public void setSuccess(boolean success) { this.success = success; }
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
public Object getData() { return data; }
|
||||
public void setData(Object data) { this.data = data; }
|
||||
}
|
||||
|
||||
// 消息响应模型类
|
||||
public static class WapiMessagesResponse {
|
||||
private boolean success;
|
||||
private Message[] messages;
|
||||
|
||||
// getter和setter方法
|
||||
public boolean isSuccess() { return success; }
|
||||
public void setSuccess(boolean success) { this.success = success; }
|
||||
public Message[] getMessages() { return messages; }
|
||||
public void setMessages(Message[] messages) { this.messages = messages; }
|
||||
|
||||
public static class Message {
|
||||
private String id;
|
||||
private String phone;
|
||||
private String body;
|
||||
private String type;
|
||||
private String timestamp;
|
||||
|
||||
// getter和setter方法
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
public String getBody() { return body; }
|
||||
public void setBody(String body) { this.body = body; }
|
||||
public String getType() { return type; }
|
||||
public void setType(String type) { this.type = type; }
|
||||
public String getTimestamp() { return timestamp; }
|
||||
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
public static void main(String[] args) {
|
||||
String apiKey = "your_wapi_ai_api_key";
|
||||
WapiAiClient client = new WapiAiClient(apiKey);
|
||||
|
||||
try {
|
||||
// 发送消息
|
||||
WapiResponse response = client.sendTextMessage("+1234567890", "Hello from Wapi.ai Java Client!");
|
||||
System.out.println("Message sent: " + response.isSuccess());
|
||||
|
||||
// 获取最近消息
|
||||
WapiMessagesResponse messagesResponse = client.getRecentMessages(5);
|
||||
if (messagesResponse.isSuccess() && messagesResponse.getMessages() != null) {
|
||||
System.out.println("Recent messages:");
|
||||
for (WapiMessagesResponse.Message msg : messagesResponse.getMessages()) {
|
||||
System.out.println(msg.getPhone() + ": " + msg.getBody());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.vetti.common.ai;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import okhttp3.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class WhisperClient {
|
||||
private static final String API_URL = "https://api.openai.com/v1/audio/transcriptions";
|
||||
private static final String MODEL = "whisper-1";
|
||||
private final String apiKey;
|
||||
private final OkHttpClient client;
|
||||
|
||||
// 构造函数,传入API密钥
|
||||
public WhisperClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
this.client = new OkHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将音频文件转换为文本
|
||||
* @param audioFile 音频文件
|
||||
* @param options 可选参数 (language, response_format等)
|
||||
* @return 转换后的文本
|
||||
* @throws IOException 网络或文件操作异常
|
||||
* @throws WhisperException API返回错误
|
||||
*/
|
||||
public String transcribe(File audioFile, Map<String, String> options) throws IOException, WhisperException {
|
||||
// 验证文件是否存在
|
||||
if (!audioFile.exists()) {
|
||||
throw new IllegalArgumentException("音频文件不存在: " + audioFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
// 构建请求体
|
||||
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart("model", MODEL)
|
||||
.addFormDataPart("file", audioFile.getName(),
|
||||
RequestBody.create(audioFile, MediaType.parse("audio/*")));
|
||||
|
||||
// 添加可选参数
|
||||
if (options != null) {
|
||||
for (Map.Entry<String, String> entry : options.entrySet()) {
|
||||
bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// 构建请求
|
||||
Request request = new Request.Builder()
|
||||
.url(API_URL)
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.post(bodyBuilder.build())
|
||||
.build();
|
||||
|
||||
// 发送请求并处理响应
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
String responseBody = response.body().string();
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
throw new WhisperException(
|
||||
"API请求失败: " + response.code() +
|
||||
", 详情: " + responseBody
|
||||
);
|
||||
}
|
||||
|
||||
// 解析JSON响应
|
||||
JSONObject json = new JSONObject(responseBody);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的转写方法,使用默认参数
|
||||
*/
|
||||
public String transcribe(File audioFile) throws IOException, WhisperException {
|
||||
return transcribe(audioFile, null);
|
||||
}
|
||||
|
||||
// 自定义异常类,用于处理Whisper API相关错误
|
||||
public static class WhisperException extends Exception {
|
||||
public WhisperException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
public static void main(String[] args) {
|
||||
// 替换为你的API密钥
|
||||
String apiKey = "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";
|
||||
WhisperClient client = new WhisperClient(apiKey);
|
||||
|
||||
// 音频文件路径
|
||||
File audioFile = new File("/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/output.mp3");
|
||||
|
||||
try {
|
||||
// 添加可选参数,例如指定语言为中文
|
||||
Map<String, String> options = new HashMap<>();
|
||||
options.put("language", "zh");
|
||||
|
||||
String result = client.transcribe(audioFile, options);
|
||||
System.out.println("转写结果: " + result);
|
||||
} catch (IOException e) {
|
||||
System.err.println("IO错误: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
} catch (WhisperException e) {
|
||||
System.err.println("Whisper API错误: " + e.getMessage());
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("参数错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.vetti.common.ai;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
public class WhisperTest {
|
||||
private static final String API_KEY = "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";
|
||||
private static final String AUDIO_PATH = "/Users/wangxiangshun/Public/project-aio/vetti/vetti-service/output.mp3"; // 替换为你的音频文件路径
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 1. 检查音频文件是否存在
|
||||
if (!Files.exists(Paths.get(AUDIO_PATH))) {
|
||||
System.err.println("音频文件不存在: " + AUDIO_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 构建multipart/form-data请求(上传文件)
|
||||
MultipartBodyPublisher multipart = new MultipartBodyPublisher();
|
||||
multipart.addPart("model", "whisper-1");
|
||||
multipart.addFilePart("file", AUDIO_PATH);
|
||||
|
||||
// 3. 发送请求
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create("https://api.openai.com/v1/audio/transcriptions"))
|
||||
.header("Authorization", "Bearer " + API_KEY)
|
||||
.POST(multipart.build())
|
||||
.build();
|
||||
|
||||
// 4. 获取并打印完整响应
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
System.out.println("状态码: " + response.statusCode());
|
||||
System.out.println("响应体: " + response.body());
|
||||
|
||||
// 5. 解析结果(如果成功)
|
||||
if (response.statusCode() == 200) {
|
||||
|
||||
System.out.println("识别结果: " + JSONUtil.toJsonStr(response.body()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助类:处理multipart/form-data格式
|
||||
class MultipartBodyPublisher {
|
||||
private final Map<String, String> parts = new HashMap<>();
|
||||
private String filePartName;
|
||||
private String filePath;
|
||||
|
||||
public void addPart(String name, String value) {
|
||||
parts.put(name, value);
|
||||
}
|
||||
|
||||
public void addFilePart(String name, String path) {
|
||||
this.filePartName = name;
|
||||
this.filePath = path;
|
||||
}
|
||||
|
||||
public HttpRequest.BodyPublisher build() throws Exception {
|
||||
// 实际项目中建议使用成熟的HTTP客户端(如OkHttp)处理multipart,此处为简化示例
|
||||
// 完整实现需处理边界、文件流等,可参考:https://stackoverflow.com/a/65271748
|
||||
return HttpRequest.BodyPublishers.ofString(""); // 仅示例框架,需完善
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user