Création d'une clé API
Pour accéder au service DashScope, il est nécessaire de générer une clé API via la console Alibaba Cloud : https://dahsscope.console.aliyun.com/apiKey.
Intégration avec le SDK Java
L'API DashScope prend en charge deux modes de sortie : la réponse complète, qui fournit l'intégralité du texte en une seule fois, et la réponse incrémentielle, qui simule un effet de frappe progressive. Cette dernière requiert l'utilisation de Server-Sent Events (SSE). L'exemple suivant se concentre sur la diffusion de données en flux continu.
Dépendances Maven
Ajoutez le SDK Java officiel pour DashScope et assurez-vous que le support SSE est inclus via Spring Web MVC.
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
Implémentation backend
Contrôleur REST pour SSE
Le contrôleur gère les connexions SSE, l'envoi de messages et la fermeture des sessions.
package com.example.qwenintegration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.annotation.Resource;
import java.util.concurrent.CompletableFuture;
@RestController
@RequestMapping("/api/qwen")
public class QwenStreamController {
@Resource
private QwenStreamService qwenService;
@GetMapping(path = "/sessions/{sessionId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter initializeSession(@PathVariable String sessionId) {
return qwenService.createConnection(sessionId);
}
@GetMapping("/sessions/{sessionId}/send")
public void sendMessage(@PathVariable String sessionId, @RequestParam String prompt) {
CompletableFuture.runAsync(() -> qwenService.streamResponse(sessionId, prompt));
}
@GetMapping("/sessions/{sessionId}/close")
public void terminateSession(@PathVariable String sessionId) {
qwenService.closeConnection(sessionId);
}
@GetMapping("/generate-image")
public String generateImage(@RequestParam String description) {
return qwenService.fetchImageAsBase64(description);
}
}
Service pour la gestion des connexions SSE
Le service maintient un cache des émetteurs SSE et délègue les appels à l'API DashScope.
package com.example.qwenintegration;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class QwenStreamServiceImpl implements QwenStreamService {
private final ConcurrentMap<String, SseEmitter> sessionStore = new ConcurrentHashMap<>();
@Override
public SseEmitter createConnection(String sessionId) {
SseEmitter existingEmitter = sessionStore.get(sessionId);
if (existingEmitter != null) {
return existingEmitter;
}
SseEmitter newEmitter = new SseEmitter(1800000L); // Timeout de 30 minutes
newEmitter.onTimeout(() -> sessionStore.remove(sessionId));
newEmitter.onError(e -> sessionStore.remove(sessionId));
sessionStore.put(sessionId, newEmitter);
return newEmitter;
}
@Override
public void streamResponse(String sessionId, String prompt) {
SseEmitter emitter = sessionStore.get(sessionId);
if (emitter != null) {
DashScopeClient.streamCompletion(emitter, prompt);
}
}
@Override
public void closeConnection(String sessionId) {
SseEmitter emitter = sessionStore.remove(sessionId);
if (emitter != null) {
emitter.complete();
}
}
@Override
public String fetchImageAsBase64(String prompt) {
return DashScopeClient.generateImageBase64(prompt);
}
}
Client pour l'API DashScope
Encapsule les appels à l'API pour les conversatiosn et la synthèse d'images.
package com.example.qwenintegration;
import com.alibaba.dashscope.aigc.conversation.*;
import com.alibaba.dashscope.aigc.imagesynthesis.*;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.exception.ApiException;
import com.google.common.base.Throwables;
import io.reactivex.Flowable;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import org.apache.commons.io.IOUtils;
public class DashScopeClient {
private static final String API_KEY = System.getenv("DASHSCOPE_API_KEY");
public static void streamCompletion(SseEmitter emitter, String userInput) {
try {
ConversationParam params = ConversationParam.builder()
.model(Conversation.Models.QWEN_TURBO)
.apiKey(API_KEY)
.addMessage(Message.builder().role("user").content(userInput).build())
.incrementalOutput(true)
.build();
Flowable<ConversationResult> resultFlow = new Conversation().streamCall(params);
resultFlow.blockingForEach(chunk -> {
String textChunk = chunk.getOutput().getChoices().get(0).getMessage().getContent();
if (textChunk != null && !textChunk.isEmpty()) {
emitter.send(textChunk.replaceAll("\\n", "<br>"));
}
});
emitter.complete();
} catch (Exception e) {
// Gestion des erreurs
}
}
public static String generateImageBase64(String prompt) {
try {
ImageSynthesisParam imageParams = ImageSynthesisParam.builder()
.model(ImageSynthesis.Models.WANX_V1)
.apiKey(API_KEY)
.prompt(prompt)
.n(1)
.size("1024*1024")
.build();
ImageSynthesisResult result = new ImageSynthesis().call(imageParams);
String imageUrl = result.getOutput().getResults().get(0).get("url");
byte[] imageBytes = IOUtils.toByteArray(new URL(imageUrl));
return Base64.getEncoder().encodeToString(imageBytes);
} catch (Exception e) {
return null;
}
}
}
Interface utilisateur web
Une page HTML simple pour intreagir avec le backend via SSE.
<html>
<head>
<meta charset="UTF-8">
<title>Assistant Qwen</title>
</head>
<body>
<div id="conversation"></div>
<input type="text" id="userInput" placeholder="Entrez votre requête...">
<button onclick="sendMessage()">Envoyer</button>
<script>
const clientId = Date.now();
let eventSource;
function establishConnection() {
eventSource = new EventSource(`/api/qwen/sessions/${clientId}`);
eventSource.onmessage = (event) => {
document.getElementById('conversation').innerHTML += event.data;
};
eventSource.onerror = () => {
console.log("Connexion SSE interrompue.");
};
}
function sendMessage() {
const prompt = document.getElementById('userInput').value;
fetch(`/api/qwen/sessions/${clientId}/send?prompt=${encodeURIComponent(prompt)}`)
.then(() => document.getElementById('userInput').value = '');
}
window.onbeforeunload = () => {
eventSource.close();
fetch(`/api/qwen/sessions/${clientId}/close`);
};
establishConnection();
</script>
</body>
</html>