mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-19 03:30:06 +08:00
增加讯飞星火
This commit is contained in:
parent
1828d1953a
commit
de32611794
@ -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>
|
||||
|
@ -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>
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.0、V3.0和V3.5支持[搜索]、[天气]、[日期]、[诗词]、[字词]、[股票]六个内置插件
|
||||
* 星火V3.5 现已支持system、Function 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package cn.iocoder.yudao.framework.ai.chatxinghuo.api;
|
||||
|
||||
/**
|
||||
* author: fansili
|
||||
* time: 2024/3/11 10:20
|
||||
*/
|
||||
public class XingHuoChatCompletionMessage {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
/**
|
||||
* author: fansili
|
||||
* time: 2024/3/12 21:03
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.ai;
|
Loading…
Reference in New Issue
Block a user