SpringBoot connects to iFlytek Spark model to implement dialogue

Application address

https://xinghuo.xfyun.cn/sparkapi?scr=price
Apply for 2 million Tokens for free

Development documentation

https://www.xfyun.cn/doc/spark/Web.html#_1-Interface Description

There are related demos at the bottom of the page for reference.

introduce

The interface is returned in segments in the form of sockets, and non-http requests are relatively cumbersome. The official only provides a relatively simple demo.

Dependencies

 <!--okhttp3-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>

     <!-- AliJSONparser -->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
            </dependency> 

Configuration file

xunfei:
    ai:
        hostUrl: https://spark-api.xf-yun.com/v3.5/chat
        appId: xxx
        apiSecret: xxx
        apiKey: xxx 

You can view the API authentication string on the console

Read configuration file

@Value("{xunfei.ai.hostUrl}")
private  String  hostUrl;

@Value("{xunfei.ai.appId}")
private  String appId;

@Value("{xunfei.ai.apiSecret}")
private  String apiSecret;

@Value("{xunfei.ai.apiKey}")
private  String apiKey; 

Permission verification

What you get is a url, you need to replace http with ws

/**
     * Permission verification
     * @return String
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws MalformedURLException
     */
    private String getAuthUrl() throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException {
        URL url = new URL(hostUrl);
        // time
        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());
        // Splicing
        String preStr = "host: " + url.getHost() + "\n" +
                "date: " + date + "\n" +
                "GET " + url.getPath() + " HTTP/1.1";
        // System.err.println(preStr);
        // SHA256encryption
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        // Base64encryption
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // System.err.println(sha);
        // Splicing
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // splicing address
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).
                addQueryParameter("date", date).
                addQueryParameter("host", url.getHost()).
                build();
        return httpUrl.toString();
    } 

Build request parameters

The request parameters are sent in json format. If you need to combine previous information to continue answering, you need to bring the history record. You can also view it in the official api documentation

#Parameter construction example is as follows

{
        "header": {
            "app_id": "12345",
            "uid": "12345"
        },
        "parameter": {
            "chat": {
                "domain": "generalv3.5",
                "temperature": 0.5,
                "max_tokens": 1024, 
            }
        },
        "payload": {
            "message": {
                # If you want a contextual answer,The developer needs to transmit the historical question and answer information to the server every time.,Example as below
                # Notice:textall insidecontentcontent added togethertokensneed to be controlled8192within,If developers need longer conversations,Historical information needs to be appropriately tailored
                "text": [
                    {"role":"system","content":"You are now playing Li Bai,You are so proud,Unruly;Next, please use Li Bai’s tone to talk to the user.。"} #Set dialogue background or model character
                    {"role": "user", "content": "Who are you"} # User history issues
                    {"role": "assistant", "content": "....."}  # AIHistorical answer results for
                    # ....... omitted historical dialogue
                    {"role": "user", "content": "what will you do"}  # The latest question,without context,You can only upload the latest question
                ]
        }
    }
} 

JAVA BUILD

 private    String buildBody(String text,String uid){
        JSONObject body =new JSONObject();

       JSONObject header =new JSONObject();
        header.put("app_id",appId);
        header.put("uid",uid);
        body.put("header",header);

        JSONObject parameter =new JSONObject();
        JSONObject chat =new JSONObject();
        chat.put("domain","generalv3.5");
        parameter.put("chat",chat);

        body.put("parameter",parameter);

        JSONObject history =JSONObject.parseObject(text);
        body.put("payload",history);

        JSONObject back =new JSONObject();
        back.put("role","system");
        back.put("content","Please answer me about some production safety content");
        //Define session context

        history.getJSONObject("message").getJSONArray("text").add(0,back);
        return body.toJSONString();
    } 

Reply message

Based on OKHTTP3 request library, connect websocket

 /**
     * Reply message
     * @param text
     * @return
     * @throws MalformedURLException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public String answer(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        String authUrl =getAuthUrl().replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(authUrl).build();
        OkHttpClient client = new OkHttpClient.Builder().build();
        StringBuilder sb =new StringBuilder();
        CompletableFuture<String> messageReceived = new CompletableFuture<>();
        String body = buildBody(text,uid);
        WebSocket webSocket =client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
            webSocket.send(body);
            //Send a message
            }
            @Override
            public void onMessage(WebSocket webSocket, String text) {
                JSONObject obj = JSON.parseObject(text);
                  String str= obj.getJSONObject("payload").getJSONObject("choices").getJSONArray("text").getJSONObject(0).getString("content");
                  sb.append(str);
                  if(obj.getJSONObject("header").getLong("status")==2){
                      webSocket.close(1000, "Closing WebSocket connection");
                      messageReceived.complete(text); // Pass the received message to CompletableFuture
                  }
            }

        } );
        String result = messageReceived.get(30, TimeUnit.SECONDS);; // Block waiting for message return
        webSocket.close(1000, "Closing WebSocket connection");
        return sb.toString();
    } 

Controller

 @PostMapping("/chat")
    public AjaxResult chat(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        return  success( model.answer(text,uid));
    } 

Complete code

Controller layer

@RestController
@RequestMapping("/course")
public class QueryController extends BaseController {
    @Autowired
    private CognitiveMode model;

    @PostMapping("/chat")
    public AjaxResult chat(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        return  success( model.answer(text,uid));
    }

} 
package com.ruoyi.framework.ai;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Component
public class CognitiveMode {
    @Value("{xunfei.ai.hostUrl}")
    private  String  hostUrl;
    @Value("{xunfei.ai.appId}")
    private  String appId;
    @Value("{xunfei.ai.apiSecret}")
    private  String apiSecret;
    @Value("{xunfei.ai.apiKey}")
    private  String apiKey;

    /**
     * Reply message
     * @param text
     * @return
     * @throws MalformedURLException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    public String answer(String text,String uid) throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException, ExecutionException, InterruptedException, TimeoutException {
        String authUrl =getAuthUrl().replace("http://", "ws://").replace("https://", "wss://");
        Request request = new Request.Builder().url(authUrl).build();
        OkHttpClient client = new OkHttpClient.Builder().build();
        StringBuilder sb =new StringBuilder();
        CompletableFuture<String> messageReceived = new CompletableFuture<>();
        String body = buildBody(text,uid);
        WebSocket webSocket =client.newWebSocket(request, new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
            webSocket.send(body);
            }
            @Override
            public void onMessage(WebSocket webSocket, String text) {
                JSONObject obj = JSON.parseObject(text);
                  String str= obj.getJSONObject("payload").getJSONObject("choices").getJSONArray("text").getJSONObject(0).getString("content");
                  sb.append(str);
                  if(obj.getJSONObject("header").getLong("status")==2){
                      webSocket.close(1000, "Closing WebSocket connection");
                      messageReceived.complete(text); // Pass the received message to CompletableFuture
                  }
            }

        } );
        String result = messageReceived.get(30, TimeUnit.SECONDS);; // Block waiting for message return
        webSocket.close(1000, "Closing WebSocket connection");
        return sb.toString();
    }

    private    String buildBody(String text,String uid){
        JSONObject body =new JSONObject();

       JSONObject header =new JSONObject();
        header.put("app_id",appId);
        header.put("uid",uid);
        body.put("header",header);

        JSONObject parameter =new JSONObject();
        JSONObject chat =new JSONObject();
        chat.put("domain","generalv3.5");
        parameter.put("chat",chat);

        body.put("parameter",parameter);

        JSONObject history =JSONObject.parseObject(text);
        body.put("payload",history);

        JSONObject back =new JSONObject();
        back.put("role","system");
        back.put("content","please answer me about somexxxContent");
        history.getJSONObject("message").getJSONArray("text").add(0,back);

        return body.toJSONString();
    }

    /**
     * Permission verification
     * @return String
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws MalformedURLException
     */
    private String getAuthUrl() throws NoSuchAlgorithmException, InvalidKeyException, MalformedURLException {
        URL url = new URL(hostUrl);
        // time
        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());
        // Splicing
        String preStr = "host: " + url.getHost() + "\n" +
                "date: " + date + "\n" +
                "GET " + url.getPath() + " HTTP/1.1";
        // System.err.println(preStr);
        // SHA256encryption
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        // Base64encryption
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // System.err.println(sha);
        // Splicing
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // splicing address
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).
                addQueryParameter("date", date).
                addQueryParameter("host", url.getHost()).
                build();
        return httpUrl.toString();
    }

}