增加讯飞星火

This commit is contained in:
cherishsince 2024-03-13 20:51:53 +08:00
parent 1828d1953a
commit de32611794
14 changed files with 861 additions and 0 deletions

View File

@ -14,6 +14,7 @@
<modules>
<module>yudao-module-ai-api</module>
<module>yudao-module-ai-biz</module>
<module>yudao-spring-boot-starter-ai</module>
</modules>
<properties>

View File

@ -100,6 +100,14 @@
<version>4.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,154 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletion;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionRequest;
import lombok.Data;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* 讯飞星火 属性api
* <p>
* 文档地址https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
* <p>
* author: fansili
* time: 2024/3/11 10:12
*/
@Data
public class XingHuoApi {
private static final String DEFAULT_BASE_URL = "wss://spark-api.xf-yun.com";
private String appId;
private String appKey;
private String secretKey;
private WebClient webClient;
private XingHuoChatModel useChatModel;
// 创建 WebSocketClient 实例
private ReactorNettyWebSocketClient socketClient = new ReactorNettyWebSocketClient();
public XingHuoApi(String appId, String appKey, String secretKey, XingHuoChatModel useChatModel) {
this.appId = appId;
this.appKey = appKey;
this.secretKey = secretKey;
this.useChatModel = useChatModel;
}
public ResponseEntity<XingHuoChatCompletion> chatCompletionEntity(XingHuoChatCompletionRequest request) {
String authUrl;
try {
authUrl = getAuthorizationUrl("spark-api.xf-yun.com", useChatModel.getUri());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
// wss 请求的 URI
URI uri = URI.create(authUrl);
// 发起 wss 请求并处理响应
Flux<XingHuoChatCompletion> messageFlux = Flux.create(sink -> {
socketClient.execute(uri, session ->
session.send(Mono.just(session.textMessage(JSONUtil.toJsonStr(request))))
.thenMany(session.receive()
.map(WebSocketMessage -> {
return JSONUtil.toBean(WebSocketMessage.getPayloadAsText(), XingHuoChatCompletion.class);
})
.doOnNext(sink::next) // 将接收到的消息推送到 Flux
.doOnError(sink::error) // 处理错误
.doOnTerminate(sink::complete)) // 完成时关闭 sink
.then())
.subscribe(); // 订阅以开始会话
});
// 阻塞获取所有结果
List<XingHuoChatCompletion> responseList = messageFlux.collectList().block();
// 拼接 content
String responseContent = responseList.stream().map(item -> {
// 获取 content
return item.getPayload().getChoices().getText().stream().map(XingHuoChatCompletion.Text::getContent).collect(Collectors.joining());
}).collect(Collectors.joining());
// 将多个合并成一个
XingHuoChatCompletion xingHuoChatCompletion = new XingHuoChatCompletion();
xingHuoChatCompletion.setPayload(new XingHuoChatCompletion.Payload().setChoices(new XingHuoChatCompletion.Choices().setText(List.of(new XingHuoChatCompletion.Text().setContent(responseContent)))));
return new ResponseEntity<>(xingHuoChatCompletion, HttpStatusCode.valueOf(200));
}
/**
* 获取验证请求url
*
* @return
*/
public String getAuthorizationUrl(String host, String path) throws NoSuchAlgorithmException, InvalidKeyException {
// 获取鉴权时间 date
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = format.format(new Date());
// 获取signature_origin字段
StringBuilder builder = new StringBuilder("host: ").append(host).append("\n").
append("date: ").append(date).append("\n").
append("GET ").append(path).append(" HTTP/1.1");
// 获得signatue
Charset charset = Charset.forName("UTF-8");
Mac mac = Mac.getInstance("hmacsha256");
SecretKeySpec sp = new SecretKeySpec(secretKey.getBytes(charset), "hmacsha256");
mac.init(sp);
byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset));
String signature = Base64.getEncoder().encodeToString(basebefore);
//获得 authorization_origin
String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"", appKey, "hmac-sha256", "host date request-line", signature);
//获得authorization
String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset));
// 获取httpUrl
Map<String, Object> param = new HashMap<>();
param.put("authorization", authorization);
param.put("date", date);
param.put("host", host);
String toParams = HttpUtil.toParams(param);
return "wss://" + host + path + "?" + toParams;
}
public Flux<XingHuoChatCompletion> chatCompletionStream(XingHuoChatCompletionRequest request) {
String authUrl;
try {
authUrl = getAuthorizationUrl("spark-api.xf-yun.com", useChatModel.getUri());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
System.err.println(authUrl);
System.err.println(JSONUtil.toJsonPrettyStr(request));
// wss 请求的 URI
URI uri = URI.create(authUrl);
// 发起 wss 请求并处理响应
// 创建一个 Flux 来处理接收到的消息
Flux<XingHuoChatCompletion> messageFlux = Flux.create(sink -> {
socketClient.execute(uri, session ->
session.send(Mono.just(session.textMessage(JSONUtil.toJsonStr(request))))
.thenMany(session.receive()
.map(WebSocketMessage -> JSONUtil.toBean(WebSocketMessage.getPayloadAsText(), XingHuoChatCompletion.class))
.doOnNext(sink::next) // 将接收到的消息推送到 Flux
.doOnError(sink::error) // 处理错误
.doOnTerminate(sink::complete)) // 完成时关闭 sink
.then())
.subscribe(); // 订阅以开始会话
});
return messageFlux;
}
}

View File

@ -0,0 +1,130 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo;
import cn.iocoder.yudao.framework.ai.chat.ChatClient;
import cn.iocoder.yudao.framework.ai.chat.ChatResponse;
import cn.iocoder.yudao.framework.ai.chat.Generation;
import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient;
import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletion;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionMessage;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionRequest;
import cn.iocoder.yudao.framework.ai.chatxinghuo.exception.XingHuoApiException;
import cn.iocoder.yudao.framework.ai.model.function.AbstractFunctionCallSupport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.support.RetryTemplate;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;
/**
* 讯飞星火 client
* <p>
* author: fansili
* time: 2024/3/11 10:19
*/
@Slf4j
public class XingHuoChatClient extends AbstractFunctionCallSupport<XingHuoChatCompletionMessage, XingHuoChatCompletionRequest, ResponseEntity<XingHuoChatCompletion>>
implements ChatClient, StreamingChatClient {
private XingHuoApi xingHuoApi;
public final RetryTemplate retryTemplate = RetryTemplate.builder()
// 最大重试次数 10
.maxAttempts(10)
.retryOn(XingHuoApiException.class)
// 最大重试5次第一次间隔3000ms第二次3000ms * 2第三次3000ms * 3以此类推最大间隔3 * 60000ms
.exponentialBackoff(Duration.ofMillis(3000), 2, Duration.ofMillis(3 * 60000))
.withListener(new RetryListener() {
@Override
public <T extends Object, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
log.warn("重试异常:" + context.getRetryCount(), throwable);
}
;
})
.build();
public XingHuoChatClient(XingHuoApi xingHuoApi) {
super(null);
this.xingHuoApi = xingHuoApi;
}
@Override
public ChatResponse call(Prompt prompt) {
return this.retryTemplate.execute(ctx -> {
// ctx 会有重试的信息
// 创建 request 请求stream模式需要供应商支持
XingHuoChatCompletionRequest request = this.createRequest(prompt, false);
// 调用 callWithFunctionSupport 发送请求
ResponseEntity<XingHuoChatCompletion> response = this.callWithFunctionSupport(request);
// 获取结果封装 ChatResponse
return new ChatResponse(List.of(new Generation(response.getBody().getPayload().getChoices().getText().get(0).getContent())));
});
}
private XingHuoChatCompletionRequest createRequest(Prompt prompt, boolean b) {
// 创建 header
XingHuoChatCompletionRequest.Header header = new XingHuoChatCompletionRequest.Header().setApp_id(xingHuoApi.getAppId());
// 创建 params
XingHuoChatCompletionRequest.Parameter parameter = new XingHuoChatCompletionRequest.Parameter()
.setChat(new XingHuoChatCompletionRequest.Parameter.Chat().setDomain(xingHuoApi.getUseChatModel().getValue()));
// 创建 payload text 信息
XingHuoChatCompletionRequest.Payload.Message.Text text = new XingHuoChatCompletionRequest.Payload.Message.Text();
text.setRole(XingHuoChatCompletionRequest.Payload.Message.Text.Role.USER.getName());
text.setContent(prompt.getContents());
// 创建 payload
XingHuoChatCompletionRequest.Payload payload = new XingHuoChatCompletionRequest.Payload()
.setMessage(new XingHuoChatCompletionRequest.Payload.Message().setText(List.of(text)));
// 创建 request
return new XingHuoChatCompletionRequest()
.setHeader(header)
.setParameter(parameter)
.setPayload(payload);
}
@Override
public Flux<ChatResponse> stream(Prompt prompt) {
// 创建 request 请求stream模式需要供应商支持
XingHuoChatCompletionRequest request = this.createRequest(prompt, false);
// 发送请求
Flux<XingHuoChatCompletion> response = this.xingHuoApi.chatCompletionStream(request);
return response.map(res -> {
String content = res.getPayload().getChoices().getText().stream()
.map(item -> item.getContent()).collect(Collectors.joining());
return new ChatResponse(List.of(new Generation(content)));
});
}
@Override
protected XingHuoChatCompletionRequest doCreateToolResponseRequest(XingHuoChatCompletionRequest previousRequest, XingHuoChatCompletionMessage responseMessage, List<XingHuoChatCompletionMessage> conversationHistory) {
return null;
}
@Override
protected List<XingHuoChatCompletionMessage> doGetUserMessages(XingHuoChatCompletionRequest request) {
return null;
}
@Override
protected XingHuoChatCompletionMessage doGetToolResponseMessage(ResponseEntity<XingHuoChatCompletion> response) {
return null;
}
@Override
protected ResponseEntity<XingHuoChatCompletion> doChatCompletion(XingHuoChatCompletionRequest request) {
return xingHuoApi.chatCompletionEntity(request);
}
@Override
protected boolean isToolFunctionCall(ResponseEntity<XingHuoChatCompletion> response) {
return false;
}
}

View File

@ -0,0 +1,44 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo;
import lombok.Getter;
/**
* 讯飞星火 模型
*
* 文档地址https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
*
* 1tokens 约等于1.5个中文汉字 或者 0.8个英文单词
* 星火V1.5支持[搜索]内置插件星火V2.0V3.0和V3.5支持[搜索][天气][日期][诗词][字词][股票]六个内置插件
* 星火V3.5 现已支持systemFunction Call 功能
*
* author: fansili
* time: 2024/3/11 10:12
*/
@Getter
public enum XingHuoChatModel {
// 文档地址https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
// general指向V1.5版本;
// generalv2指向V2版本;
// generalv3指向V3版本;
// generalv3.5指向V3.5版本;
XING_HUO_1_5("星火大模型1.5", "general", "/v1.1/chat"),
XING_HUO_2_0("星火大模型2.0", "generalv2", "/v2.1/chat"),
XING_HUO_3_0("星火大模型3.0", "generalv3", "/v3.1/chat"),
XING_HUO_3_5("星火大模型3.5", "generalv3.5", "/v3.5/chat"),
;
XingHuoChatModel(String name, String value, String uri) {
this.name = name;
this.value = value;
this.uri = uri;
}
private String name;
private String value;
private String uri;
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo.api;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* author: fansili
* time: 2024/3/11 10:20
*/
@Data
@Accessors(chain = true)
public class XingHuoChatCompletion {
private Header header;
private Payload payload;
@Data
@Accessors(chain = true)
public static class Header {
private int code;
private String message;
private String sid;
private int status;
}
@Data
@Accessors(chain = true)
public static class Payload {
private Choices choices;
}
@Data
@Accessors(chain = true)
public static class Choices {
private int status;
private int seq;
private List<Text> text;
}
@Data
@Accessors(chain = true)
public static class Text {
private String content;
private String role;
private int index;
}
}

View File

@ -0,0 +1,8 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo.api;
/**
* author: fansili
* time: 2024/3/11 10:20
*/
public class XingHuoChatCompletionMessage {
}

View File

@ -0,0 +1,92 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo.api;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 讯飞星火 request
*
* author: fansili
* time: 2024/3/11 10:20
*/
@Data
@Accessors(chain = true)
public class XingHuoChatCompletionRequest {
private Header header;
private Parameter parameter;
private Payload payload;
@Data
@Accessors(chain = true)
public static class Header {
private String app_id;
private String uid;
}
@Data
@Accessors(chain = true)
public static class Parameter {
private Chat chat;
@Data
@Accessors(chain = true)
public static class Chat {
/**
* https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
*
* 指定访问的领域:
* general指向V1.5版本;
* generalv2指向V2版本;
* generalv3指向V3版本;
* generalv3.5指向V3.5版本;
* 注意不同的取值对应的url也不一样
*/
private String domain = "general";
private Double temperature = 0.5;
private Integer max_tokens = 2048;
}
}
@Data
@Accessors(chain = true)
public static class Payload {
private Message message;
@Data
@Accessors(chain = true)
public static class Message {
private List<Text> text;
@Data
@Accessors(chain = true)
public static class Text {
/**
* 角色
*/
private String role;
/**
* 消息内容
*/
private String content;
private Integer index;
@Getter
public static enum Role {
SYSTEM("system"),
USER("user"),
ASSISTANT("assistant");
private String name;
private Role(String name) {
this.name = name;
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
package cn.iocoder.yudao.framework.ai.chatxinghuo.exception;
/**
* 讯飞星火 exception
*
* author: fansili
* time: 2024/3/11 10:22
*/
public class XingHuoApiException extends RuntimeException {
public XingHuoApiException(String message) {
super(message);
}
}

View File

@ -0,0 +1,117 @@
package cn.iocoder.yudao.framework.ai.chat;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletion;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionRequest;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import reactor.core.publisher.Flux;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* author: fansili
* time: 2024/3/13 20:47
*/
public class XingHuoChatClientMainTests {
private static final String HOST_URL = "http://spark-api.xf-yun.com/v3.5/chat";
private static final String API_KEY = "cb6415c19d6162cda07b47316fcb0416";
private static final String API_SECRET = "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh";
public static void main(String[] args) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException {
String authUrl = getAuthorizationUrl("spark-api.xf-yun.com", "/v3.5/chat");
System.err.println(authUrl);
XingHuoChatCompletionRequest.Header header = new XingHuoChatCompletionRequest.Header().setApp_id("13c8cca6");
XingHuoChatCompletionRequest.Parameter parameter
= new XingHuoChatCompletionRequest.Parameter()
.setChat(new XingHuoChatCompletionRequest.Parameter.Chat().setDomain("generalv3.5"));
XingHuoChatCompletionRequest.Payload.Message.Text text = new XingHuoChatCompletionRequest.Payload.Message.Text();
text.setRole(XingHuoChatCompletionRequest.Payload.Message.Text.Role.USER.getName());
text.setContent("世界上最好的开发语言是什么?");
XingHuoChatCompletionRequest.Payload payload = new XingHuoChatCompletionRequest.Payload()
.setMessage(new XingHuoChatCompletionRequest.Payload.Message().setText(List.of(text)));
XingHuoChatCompletionRequest request = new XingHuoChatCompletionRequest()
.setHeader(header)
.setParameter(parameter)
.setPayload(payload);
System.err.println(JSONUtil.toJsonPrettyStr(request));
// 创建 WebSocketClient 实例
WebSocketClient client = new ReactorNettyWebSocketClient();
// wss 请求的 URI
URI uri = URI.create(authUrl);
// 发起 wss 请求并处理响应
client.execute(uri, session ->
// 使用会话发送消息并接收回应
session.send(Flux.just(session.textMessage(JSONUtil.toJsonStr(request))))
.thenMany(session.receive()
.map(WebSocketMessage -> {
System.err.println(WebSocketMessage.getPayloadAsText());
return JSONUtil.toBean(WebSocketMessage.getPayloadAsText(), XingHuoChatCompletion.class);
})
.log()) // 打印接收到的消息
.then())
.block(); // 等待操作完成或超时
// 阻止退出
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
}
/**
* 获取验证请求url
*
* @return
*/
public static String getAuthorizationUrl(String host, String path) throws NoSuchAlgorithmException, InvalidKeyException {
// 获取鉴权时间 date
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = format.format(new Date());
// 获取signature_origin字段
StringBuilder builder = new StringBuilder("host: ").append(host).append("\n").
append("date: ").append(date).append("\n").
append("GET ").append(path).append(" HTTP/1.1");
// 获得signatue
Charset charset = Charset.forName("UTF-8");
Mac mac = Mac.getInstance("hmacsha256");
SecretKeySpec sp = new SecretKeySpec(API_SECRET.getBytes(charset), "hmacsha256");
mac.init(sp);
byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset));
String signature = Base64.getEncoder().encodeToString(basebefore);
//获得 authorization_origin
String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"", API_KEY, "hmac-sha256", "host date request-line", signature);
//获得authorization
String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset));
// 获取httpUrl
Map<String, Object> param = new HashMap<>();
param.put("authorization", authorization);
param.put("date", date);
param.put("host", host);
String toParams = HttpUtil.toParams(param);
return "wss://" + host + path + "?" + toParams;
}
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.framework.ai.chat;
import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoApi;
import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatClient;
import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatModel;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import java.util.Scanner;
import java.util.function.Consumer;
/**
* 讯飞星火 tests
* <p>
* author: fansili
* time: 2024/3/11 11:00
*/
public class XingHuoChatClientTests {
private XingHuoChatClient xingHuoChatClient;
@Before
public void setup() {
// 初始化 xingHuoChatClient
xingHuoChatClient = new XingHuoChatClient(
new XingHuoApi(
"13c8cca6",
"cb6415c19d6162cda07b47316fcb0416",
"Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh",
XingHuoChatModel.XING_HUO_3_5
)
);
}
@Test
public void callTest() {
ChatResponse call = xingHuoChatClient.call(new Prompt("java和go那个性能更好!"));
System.err.println(call.getResult());
}
@Test
public void streamTest() {
Flux<ChatResponse> stream = xingHuoChatClient.stream(new Prompt("java和go那个性能更好!"));
stream.subscribe(new Consumer<ChatResponse>() {
@Override
public void accept(ChatResponse chatResponse) {
System.err.print(chatResponse.getResult().getOutput().getContent());
}
});
// 阻止退出
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
}
}

View File

@ -0,0 +1,130 @@
package cn.iocoder.yudao.framework.ai.chat;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.ai.chatxinghuo.XingHuoChatClient;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletion;
import cn.iocoder.yudao.framework.ai.chatxinghuo.api.XingHuoChatCompletionRequest;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 讯飞星火 tests
* <p>
* author: fansili
* time: 2024/3/11 11:00
*/
public class XingHuoOkHttpTests {
private static final String HOST_URL = "http://spark-api.xf-yun.com/v3.5/chat";
private static final String API_KEY = "cb6415c19d6162cda07b47316fcb0416";
private static final String API_SECRET = "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh";
private XingHuoChatClient xingHuoChatClient;
public static void main(String[] args) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException {
String authUrl = getAuthorizationUrl("spark-api.xf-yun.com", "/v3.5/chat");
System.err.println(authUrl);
XingHuoChatCompletionRequest.Header header = new XingHuoChatCompletionRequest.Header().setApp_id("13c8cca6");
XingHuoChatCompletionRequest.Parameter parameter
= new XingHuoChatCompletionRequest.Parameter()
.setChat(new XingHuoChatCompletionRequest.Parameter.Chat().setDomain("generalv3.5"));
XingHuoChatCompletionRequest.Payload.Message.Text text = new XingHuoChatCompletionRequest.Payload.Message.Text();
text.setRole(XingHuoChatCompletionRequest.Payload.Message.Text.Role.USER.getName());
text.setContent("世界上最好的开发语言是什么?");
XingHuoChatCompletionRequest.Payload payload = new XingHuoChatCompletionRequest.Payload()
.setMessage(new XingHuoChatCompletionRequest.Payload.Message().setText(List.of(text)));
XingHuoChatCompletionRequest request = new XingHuoChatCompletionRequest()
.setHeader(header)
.setParameter(parameter)
.setPayload(payload);
System.err.println(JSONUtil.toJsonPrettyStr(request));
OkHttpClient client = new OkHttpClient();
Request request2 = new Request.Builder()
.url(authUrl) // 替换为你的 wss URL
.build();
WebSocketListener webSocketListener = new WebSocketListener() {
@Override
public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) {
boolean send = webSocket.send(JSONUtil.toJsonStr(request));
System.err.println("发送 -> " + send);
System.err.println("链接成功!");
}
@Override
public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
super.onMessage(webSocket, text);
// System.err.println(text);
XingHuoChatCompletion response = JSONUtil.toBean(text, XingHuoChatCompletion.class);
for (XingHuoChatCompletion.Text text1 : response.getPayload().getChoices().getText()) {
System.err.print(text1.getContent());
}
}
};
WebSocket webSocket = client.newWebSocket(request2, webSocketListener);
// webSocket.send(JSONUtil.toJsonStr(request));
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
client.dispatcher().executorService().shutdown();
// 阻止退出
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
}
/**
* 获取验证请求url
*
* @return
*/
public static String getAuthorizationUrl(String host, String path) throws NoSuchAlgorithmException, InvalidKeyException {
// 获取鉴权时间 date
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = format.format(new Date());
// 获取signature_origin字段
StringBuilder builder = new StringBuilder("host: ").append(host).append("\n").
append("date: ").append(date).append("\n").
append("GET ").append(path).append(" HTTP/1.1");
// 获得signatue
Charset charset = Charset.forName("UTF-8");
Mac mac = Mac.getInstance("hmacsha256");
SecretKeySpec sp = new SecretKeySpec(API_SECRET.getBytes(charset), "hmacsha256");
mac.init(sp);
byte[] basebefore = mac.doFinal(builder.toString().getBytes(charset));
String signature = Base64.getEncoder().encodeToString(basebefore);
//获得 authorization_origin
String authorization_origin = String.format("api_key=\"%s\",algorithm=\"%s\",headers=\"%s\",signature=\"%s\"", API_KEY, "hmac-sha256", "host date request-line", signature);
//获得authorization
String authorization = Base64.getEncoder().encodeToString(authorization_origin.getBytes(charset));
// 获取httpUrl
Map<String, Object> param = new HashMap<>();
param.put("authorization", authorization);
param.put("date", date);
param.put("host", host);
String toParams = HttpUtil.toParams(param);
return "wss://" + host + path + "?" + toParams;
}
}

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.framework.ai.chat;
import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanApi;
import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient;
import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel;
import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import java.util.Scanner;
import java.util.function.Consumer;
/**
* chat 文心一言
*
* author: fansili
* time: 2024/3/12 20:59
*/
public class YiYanChatTests {
private YiYanChatClient yiYanChatClient;
@Before
public void setup() {
YiYanApi yiYanApi = new YiYanApi(
"x0cuLZ7XsaTCU08vuJWO87Lg",
"R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK",
YiYanChatModel.ERNIE4_3_5_8K,
86400
);
yiYanChatClient = new YiYanChatClient(yiYanApi);
}
@Test
public void callTest() {
ChatResponse call = yiYanChatClient.call(new Prompt("什么编程语言最好?"));
System.err.println(call.getResult());
}
@Test
public void streamTest() {
Flux<ChatResponse> fluxResponse = yiYanChatClient.stream(new Prompt("用java帮我写一个快排算法"));
fluxResponse.subscribe(new Consumer<ChatResponse>() {
@Override
public void accept(ChatResponse chatResponse) {
System.err.print(chatResponse.getResult().getOutput().getContent());
}
});
// 阻止退出
Scanner scanner = new Scanner(System.in);
scanner.nextLine();
}
}

View File

@ -0,0 +1,5 @@
/**
* author: fansili
* time: 2024/3/12 21:03
*/
package cn.iocoder.yudao.framework.ai;