refactor: 重命名 backend-single 目录为 server
This commit is contained in:
@@ -0,0 +1,571 @@
|
||||
package com.emotion.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.emotion.dto.request.ai.AiRuntimeRequest;
|
||||
import com.emotion.dto.response.ai.AiTestTemplateResponse;
|
||||
import com.emotion.dto.response.ai.AiRuntimeTestResponse;
|
||||
import com.emotion.dto.response.ai.AiStreamEvent;
|
||||
import com.emotion.entity.AiCallLog;
|
||||
import com.emotion.entity.AiEndpointConfig;
|
||||
import com.emotion.entity.AiProvider;
|
||||
import com.emotion.entity.AiSceneBinding;
|
||||
import com.emotion.service.AiCallLogService;
|
||||
import com.emotion.service.AiEndpointConfigService;
|
||||
import com.emotion.service.AiProviderService;
|
||||
import com.emotion.service.AiRuntimeService;
|
||||
import com.emotion.service.AiSceneBindingService;
|
||||
import com.emotion.service.ScriptContextService;
|
||||
import com.emotion.service.ai.AiProviderAdapter;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AiRuntimeServiceImpl implements AiRuntimeService {
|
||||
|
||||
private final AiSceneBindingService sceneBindingService;
|
||||
private final AiEndpointConfigService endpointConfigService;
|
||||
private final AiProviderService providerService;
|
||||
private final AiCallLogService callLogService;
|
||||
private final ScriptContextService scriptContextService;
|
||||
private final List<AiProviderAdapter> adapters;
|
||||
|
||||
public AiRuntimeServiceImpl(AiSceneBindingService sceneBindingService,
|
||||
AiEndpointConfigService endpointConfigService,
|
||||
AiProviderService providerService,
|
||||
AiCallLogService callLogService,
|
||||
ScriptContextService scriptContextService,
|
||||
List<AiProviderAdapter> adapters) {
|
||||
this.sceneBindingService = sceneBindingService;
|
||||
this.endpointConfigService = endpointConfigService;
|
||||
this.providerService = providerService;
|
||||
this.callLogService = callLogService;
|
||||
this.scriptContextService = scriptContextService;
|
||||
this.adapters = adapters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeStream(AiRuntimeRequest request, Consumer<AiStreamEvent> consumer) {
|
||||
String requestId = StringUtils.hasText(request.getRequestId()) ? request.getRequestId() : UUID.randomUUID().toString();
|
||||
request.setRequestId(requestId);
|
||||
enrichInputs(request);
|
||||
long startedAt = System.currentTimeMillis();
|
||||
AtomicLong firstTokenAt = new AtomicLong(0);
|
||||
AtomicInteger chunks = new AtomicInteger(0);
|
||||
StringBuilder output = new StringBuilder();
|
||||
AiCallLog callLog = new AiCallLog();
|
||||
callLog.setRequestId(requestId);
|
||||
callLog.setSceneCode(request.getSceneCode());
|
||||
callLog.setUserId(resolveUserId(request));
|
||||
callLog.setUserName(request.getUserName());
|
||||
callLog.setInputText(JSON.toJSONString(request.getInputs()));
|
||||
callLog.setStatus("running");
|
||||
|
||||
try {
|
||||
RuntimeTarget target = resolveTarget(request);
|
||||
callLog.setProviderCode(target.provider.getProviderCode());
|
||||
callLog.setEndpointCode(target.endpoint.getEndpointCode());
|
||||
callLogService.save(callLog);
|
||||
|
||||
consumer.accept(AiStreamEvent.start(request.getSceneCode()));
|
||||
target.adapter.stream(target.provider, target.endpoint, request, event -> {
|
||||
if ("delta".equals(event.getType())) {
|
||||
chunks.incrementAndGet();
|
||||
if (firstTokenAt.compareAndSet(0, System.currentTimeMillis())) {
|
||||
log.debug("AI first token emitted, scene={}, requestId={}", request.getSceneCode(), requestId);
|
||||
}
|
||||
if (event.getContent() != null) {
|
||||
output.append(event.getContent());
|
||||
}
|
||||
}
|
||||
consumer.accept(event);
|
||||
});
|
||||
|
||||
callLog.setStatus("success");
|
||||
callLog.setOutputText(output.toString());
|
||||
callLog.setStreamChunks(chunks.get());
|
||||
callLog.setFirstTokenMs(firstTokenAt.get() == 0 ? null : firstTokenAt.get() - startedAt);
|
||||
callLog.setDurationMs(System.currentTimeMillis() - startedAt);
|
||||
callLogService.updateById(callLog);
|
||||
emitDone(consumer, Map.of(
|
||||
"requestId", requestId,
|
||||
"streamChunks", chunks.get(),
|
||||
"durationMs", callLog.getDurationMs()
|
||||
));
|
||||
} catch (Exception e) {
|
||||
String code = normalizeErrorCode(e);
|
||||
callLog.setOutputText(output.toString());
|
||||
callLog.setStreamChunks(chunks.get());
|
||||
callLog.setFirstTokenMs(firstTokenAt.get() == 0 ? null : firstTokenAt.get() - startedAt);
|
||||
callLog.setDurationMs(System.currentTimeMillis() - startedAt);
|
||||
if (StringUtils.hasText(output.toString())) {
|
||||
callLog.setStatus("success");
|
||||
callLog.setErrorCode(null);
|
||||
callLog.setErrorMessage(null);
|
||||
saveOrUpdateLog(callLog);
|
||||
emitDone(consumer, recoveredDoneMetadata(requestId, chunks.get(), callLog.getDurationMs(), code, e.getMessage()));
|
||||
return;
|
||||
}
|
||||
callLog.setStatus("failed");
|
||||
callLog.setErrorCode(code);
|
||||
callLog.setErrorMessage(e.getMessage());
|
||||
saveOrUpdateLog(callLog);
|
||||
consumer.accept(AiStreamEvent.error(code, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiRuntimeTestResponse test(AiRuntimeRequest request) {
|
||||
long startedAt = System.currentTimeMillis();
|
||||
StringBuilder output = new StringBuilder();
|
||||
AtomicInteger chunks = new AtomicInteger(0);
|
||||
final String[] errorCode = new String[1];
|
||||
final String[] errorMessage = new String[1];
|
||||
|
||||
invokeStream(request, event -> {
|
||||
if ("delta".equals(event.getType()) && event.getContent() != null) {
|
||||
chunks.incrementAndGet();
|
||||
output.append(event.getContent());
|
||||
} else if ("error".equals(event.getType())) {
|
||||
errorCode[0] = event.getCode();
|
||||
errorMessage[0] = event.getMessage();
|
||||
}
|
||||
});
|
||||
|
||||
return AiRuntimeTestResponse.builder()
|
||||
.sceneCode(request.getSceneCode())
|
||||
.status(errorCode[0] == null || StringUtils.hasText(output.toString()) ? "success" : "failed")
|
||||
.output(output.toString())
|
||||
.streamChunks(chunks.get())
|
||||
.durationMs(System.currentTimeMillis() - startedAt)
|
||||
.errorCode(StringUtils.hasText(output.toString()) ? null : errorCode[0])
|
||||
.errorMessage(StringUtils.hasText(output.toString()) ? null : errorMessage[0])
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiRuntimeTestResponse testEndpoint(String endpointId, Map<String, Object> inputs) {
|
||||
long startedAt = System.currentTimeMillis();
|
||||
StringBuilder output = new StringBuilder();
|
||||
AtomicInteger chunks = new AtomicInteger(0);
|
||||
final String[] errorCode = new String[1];
|
||||
final String[] errorMessage = new String[1];
|
||||
|
||||
invokeEndpointStream(endpointId, inputs, event -> {
|
||||
if ("delta".equals(event.getType()) && event.getContent() != null) {
|
||||
chunks.incrementAndGet();
|
||||
output.append(event.getContent());
|
||||
} else if ("error".equals(event.getType())) {
|
||||
errorCode[0] = event.getCode();
|
||||
errorMessage[0] = event.getMessage();
|
||||
}
|
||||
});
|
||||
|
||||
return AiRuntimeTestResponse.builder()
|
||||
.sceneCode("")
|
||||
.status(errorCode[0] == null || StringUtils.hasText(output.toString()) ? "success" : "failed")
|
||||
.output(output.toString())
|
||||
.streamChunks(chunks.get())
|
||||
.durationMs(System.currentTimeMillis() - startedAt)
|
||||
.errorCode(StringUtils.hasText(output.toString()) ? null : errorCode[0])
|
||||
.errorMessage(StringUtils.hasText(output.toString()) ? null : errorMessage[0])
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeEndpointStream(String endpointId, Map<String, Object> inputs, Consumer<AiStreamEvent> consumer) {
|
||||
long startedAt = System.currentTimeMillis();
|
||||
AtomicLong firstTokenAt = new AtomicLong(0);
|
||||
AtomicInteger chunks = new AtomicInteger(0);
|
||||
Object inputRequestId = inputs == null ? null : inputs.get("requestId");
|
||||
String requestId = inputRequestId != null && StringUtils.hasText(String.valueOf(inputRequestId))
|
||||
? String.valueOf(inputRequestId)
|
||||
: UUID.randomUUID().toString();
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
AiEndpointConfig endpoint = endpointConfigService.getEnabledById(endpointId);
|
||||
if (endpoint == null) {
|
||||
throw new IllegalStateException("AI_ENDPOINT_DISABLED");
|
||||
}
|
||||
AiProvider provider = providerService.getEnabledById(endpoint.getProviderId());
|
||||
if (provider == null) {
|
||||
throw new IllegalStateException("AI_PROVIDER_DISABLED");
|
||||
}
|
||||
AiProviderAdapter adapter = adapters.stream()
|
||||
.filter(item -> item.supports(provider.getProviderType()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("AI_PROVIDER_ADAPTER_NOT_FOUND"));
|
||||
|
||||
AiRuntimeRequest request = new AiRuntimeRequest();
|
||||
request.setInputs(inputs == null ? new com.alibaba.fastjson2.JSONObject() : new com.alibaba.fastjson2.JSONObject(inputs));
|
||||
request.setEndpointId(endpointId);
|
||||
request.setUserId(resolveUserId(request));
|
||||
request.setUserName(UserContextHolder.getCurrentUsername());
|
||||
request.setUserType(UserContextHolder.getCurrentUserType());
|
||||
request.setRequestId(requestId);
|
||||
enrichInputs(request);
|
||||
|
||||
AiCallLog callLog = new AiCallLog();
|
||||
callLog.setRequestId(requestId);
|
||||
callLog.setEndpointCode(endpoint.getEndpointCode());
|
||||
callLog.setProviderCode(provider.getProviderCode());
|
||||
callLog.setUserId(request.getUserId());
|
||||
callLog.setUserName(request.getUserName());
|
||||
callLog.setInputText(JSON.toJSONString(request.getInputs()));
|
||||
callLog.setStatus("running");
|
||||
callLogService.save(callLog);
|
||||
|
||||
consumer.accept(AiStreamEvent.start(endpoint.getEndpointCode()));
|
||||
try {
|
||||
adapter.stream(provider, endpoint, request, event -> {
|
||||
if ("delta".equals(event.getType())) {
|
||||
chunks.incrementAndGet();
|
||||
if (firstTokenAt.compareAndSet(0, System.currentTimeMillis())) {
|
||||
log.debug("AI first token emitted, endpoint={}, requestId={}", endpoint.getEndpointCode(), requestId);
|
||||
}
|
||||
if (event.getContent() != null) {
|
||||
output.append(event.getContent());
|
||||
}
|
||||
}
|
||||
consumer.accept(event);
|
||||
});
|
||||
|
||||
callLog.setStatus("success");
|
||||
callLog.setOutputText(output.toString());
|
||||
callLog.setStreamChunks(chunks.get());
|
||||
callLog.setFirstTokenMs(firstTokenAt.get() == 0 ? null : firstTokenAt.get() - startedAt);
|
||||
callLog.setDurationMs(System.currentTimeMillis() - startedAt);
|
||||
callLogService.updateById(callLog);
|
||||
emitDone(consumer, Map.of(
|
||||
"requestId", requestId,
|
||||
"streamChunks", chunks.get(),
|
||||
"durationMs", callLog.getDurationMs()
|
||||
));
|
||||
} catch (Exception e) {
|
||||
String code = normalizeErrorCode(e);
|
||||
callLog.setOutputText(output.toString());
|
||||
callLog.setStreamChunks(chunks.get());
|
||||
callLog.setFirstTokenMs(firstTokenAt.get() == 0 ? null : firstTokenAt.get() - startedAt);
|
||||
callLog.setDurationMs(System.currentTimeMillis() - startedAt);
|
||||
if (StringUtils.hasText(output.toString())) {
|
||||
callLog.setStatus("success");
|
||||
callLog.setErrorCode(null);
|
||||
callLog.setErrorMessage(null);
|
||||
saveOrUpdateLog(callLog);
|
||||
emitDone(consumer, recoveredDoneMetadata(requestId, chunks.get(), callLog.getDurationMs(), code, e.getMessage()));
|
||||
return;
|
||||
}
|
||||
callLog.setStatus("failed");
|
||||
callLog.setErrorCode(code);
|
||||
callLog.setErrorMessage(e.getMessage());
|
||||
saveOrUpdateLog(callLog);
|
||||
consumer.accept(AiStreamEvent.error(code, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiTestTemplateResponse buildEndpointTestTemplate(String endpointId) {
|
||||
AiEndpointConfig endpoint = endpointConfigService.getById(endpointId);
|
||||
if (endpoint == null || Integer.valueOf(1).equals(endpoint.getIsDeleted())) {
|
||||
throw new IllegalArgumentException("AI_ENDPOINT_NOT_FOUND");
|
||||
}
|
||||
AiProvider provider = providerService.getById(endpoint.getProviderId());
|
||||
if (provider == null || Integer.valueOf(1).equals(provider.getIsDeleted())) {
|
||||
throw new IllegalArgumentException("AI_PROVIDER_NOT_FOUND");
|
||||
}
|
||||
return buildTemplate(null, endpoint, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiTestTemplateResponse buildSceneTestTemplate(String sceneCode) {
|
||||
AiSceneBinding scene = sceneBindingService.resolveScene(sceneCode);
|
||||
if (scene == null) {
|
||||
throw new IllegalArgumentException("AI_SCENE_NOT_BOUND");
|
||||
}
|
||||
AiEndpointConfig endpoint = endpointConfigService.getById(scene.getEndpointId());
|
||||
if (endpoint == null || Integer.valueOf(1).equals(endpoint.getIsDeleted())) {
|
||||
throw new IllegalArgumentException("AI_ENDPOINT_NOT_FOUND");
|
||||
}
|
||||
AiProvider provider = providerService.getById(endpoint.getProviderId());
|
||||
if (provider == null || Integer.valueOf(1).equals(provider.getIsDeleted())) {
|
||||
throw new IllegalArgumentException("AI_PROVIDER_NOT_FOUND");
|
||||
}
|
||||
return buildTemplate(sceneCode, endpoint, provider);
|
||||
}
|
||||
|
||||
private AiTestTemplateResponse buildTemplate(String sceneCode, AiEndpointConfig endpoint, AiProvider provider) {
|
||||
LinkedHashMap<String, Object> inputs = new LinkedHashMap<>();
|
||||
applyDefaultInputs(inputs, endpoint.getDefaultInputs());
|
||||
applyProviderSampleInputs(inputs, sceneCode, endpoint, provider);
|
||||
|
||||
List<AiTestTemplateResponse.ParamField> fields = new ArrayList<>();
|
||||
inputs.forEach((key, value) -> fields.add(AiTestTemplateResponse.ParamField.builder()
|
||||
.name(key)
|
||||
.label(paramLabel(key))
|
||||
.type(paramType(value))
|
||||
.value(value)
|
||||
.required(requiredParam(key, provider.getProviderType()))
|
||||
.placeholder(paramPlaceholder(key))
|
||||
.build()));
|
||||
|
||||
return AiTestTemplateResponse.builder()
|
||||
.sceneCode(sceneCode)
|
||||
.endpointId(endpoint.getId())
|
||||
.endpointCode(endpoint.getEndpointCode())
|
||||
.endpointName(endpoint.getEndpointName())
|
||||
.providerType(provider.getProviderType())
|
||||
.inputs(inputs)
|
||||
.paramFields(fields)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void applyDefaultInputs(LinkedHashMap<String, Object> inputs, String defaultInputs) {
|
||||
if (!StringUtils.hasText(defaultInputs)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JSONObject parsed = JSON.parseObject(defaultInputs);
|
||||
parsed.forEach((key, value) -> {
|
||||
if (value instanceof JSONObject && ((JSONObject) value).containsKey("_meta")) {
|
||||
inputs.put(key, ((JSONObject) value).get("value"));
|
||||
} else {
|
||||
inputs.put(key, value);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void applyProviderSampleInputs(LinkedHashMap<String, Object> inputs,
|
||||
String sceneCode,
|
||||
AiEndpointConfig endpoint,
|
||||
AiProvider provider) {
|
||||
String providerType = provider.getProviderType();
|
||||
String sample = samplePrompt(sceneCode, endpoint);
|
||||
|
||||
if ("dify".equalsIgnoreCase(providerType)) {
|
||||
inputs.putIfAbsent("query", sample);
|
||||
inputs.putIfAbsent("inputs", Map.of("input", sample, "user_id", "admin-test-user"));
|
||||
inputs.putIfAbsent("response_mode", "streaming");
|
||||
inputs.putIfAbsent("conversation_id", "");
|
||||
inputs.putIfAbsent("user", "admin-test-user");
|
||||
inputs.putIfAbsent("user_id", "admin-test-user");
|
||||
return;
|
||||
}
|
||||
|
||||
inputs.putIfAbsent("input", sample);
|
||||
inputs.putIfAbsent("prompt", sample);
|
||||
inputs.putIfAbsent("message", sample);
|
||||
inputs.putIfAbsent("user_id", "admin-test-user");
|
||||
if ("chat".equalsIgnoreCase(endpoint.getEndpointType())) {
|
||||
inputs.putIfAbsent("conversationId", "admin-test-conversation");
|
||||
}
|
||||
}
|
||||
|
||||
private String samplePrompt(String sceneCode, AiEndpointConfig endpoint) {
|
||||
String code = StringUtils.hasText(sceneCode) ? sceneCode : endpoint.getEndpointCode();
|
||||
if (code == null) {
|
||||
return "请用一句中文回复测试成功。";
|
||||
}
|
||||
if (code.contains("script") || code.contains("life.generate")) {
|
||||
return "请生成一个关于普通人重新找回生活热情的短剧本,要求结构清晰、中文输出。";
|
||||
}
|
||||
if (code.contains("short_story") || code.contains("story")) {
|
||||
return "请生成一篇 300 字以内的治愈短篇小说,主题是雨后的新开始。";
|
||||
}
|
||||
if (code.contains("diary") || code.contains("healing") || code.contains("life")) {
|
||||
return "今天工作压力很大,但傍晚散步时感觉心情慢慢平静下来,请给我一段温和的回应。";
|
||||
}
|
||||
if (code.contains("summary")) {
|
||||
return "用户今天先表达焦虑,随后通过散步和朋友聊天逐渐恢复平静,请生成一段情绪总结。";
|
||||
}
|
||||
if (code.contains("chat")) {
|
||||
return "你好,请用一句中文回复:AI 接口测试成功。";
|
||||
}
|
||||
return "请用一句中文回复测试成功。";
|
||||
}
|
||||
|
||||
private String paramLabel(String key) {
|
||||
Map<String, String> labels = Map.ofEntries(
|
||||
Map.entry("input", "输入内容"),
|
||||
Map.entry("prompt", "提示词"),
|
||||
Map.entry("message", "消息内容"),
|
||||
Map.entry("query", "用户问题"),
|
||||
Map.entry("inputs", "Dify 变量"),
|
||||
Map.entry("response_mode", "响应模式"),
|
||||
Map.entry("conversation_id", "会话 ID"),
|
||||
Map.entry("user", "用户标识"),
|
||||
Map.entry("user_id", "用户标识"),
|
||||
Map.entry("conversationId", "会话 ID")
|
||||
);
|
||||
return labels.getOrDefault(key, key);
|
||||
}
|
||||
|
||||
private String paramType(Object value) {
|
||||
if (value instanceof Number) {
|
||||
return "number";
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return "boolean";
|
||||
}
|
||||
if (value instanceof Map || value instanceof JSONObject) {
|
||||
return "json";
|
||||
}
|
||||
String text = value == null ? "" : String.valueOf(value);
|
||||
return text.length() > 60 ? "textarea" : "string";
|
||||
}
|
||||
|
||||
private Boolean requiredParam(String key, String providerType) {
|
||||
if ("dify".equalsIgnoreCase(providerType)) {
|
||||
return "query".equals(key) || "response_mode".equals(key) || "user".equals(key);
|
||||
}
|
||||
return "input".equals(key) || "user_id".equals(key);
|
||||
}
|
||||
|
||||
private String paramPlaceholder(String key) {
|
||||
if ("inputs".equals(key)) {
|
||||
return "{\"input\":\"测试内容\"}";
|
||||
}
|
||||
return "请输入" + paramLabel(key);
|
||||
}
|
||||
|
||||
private RuntimeTarget resolveTarget(AiRuntimeRequest request) {
|
||||
if (!StringUtils.hasText(request.getSceneCode())) {
|
||||
throw new IllegalArgumentException("AI_SCENE_REQUIRED");
|
||||
}
|
||||
if (!StringUtils.hasText(request.getUserId())) {
|
||||
request.setUserId(resolveUserId(request));
|
||||
}
|
||||
|
||||
AiSceneBinding scene = sceneBindingService.resolveScene(request.getSceneCode());
|
||||
if (scene == null) {
|
||||
throw new IllegalStateException("AI_SCENE_NOT_BOUND");
|
||||
}
|
||||
AiEndpointConfig endpoint = endpointConfigService.getEnabledById(scene.getEndpointId());
|
||||
if (endpoint == null) {
|
||||
throw new IllegalStateException("AI_ENDPOINT_DISABLED");
|
||||
}
|
||||
if (Integer.valueOf(1).equals(scene.getRequiredStream()) && !Integer.valueOf(1).equals(endpoint.getSupportStream())) {
|
||||
throw new IllegalStateException("AI_ENDPOINT_STREAM_REQUIRED");
|
||||
}
|
||||
AiProvider provider = providerService.getEnabledById(endpoint.getProviderId());
|
||||
if (provider == null) {
|
||||
throw new IllegalStateException("AI_PROVIDER_DISABLED");
|
||||
}
|
||||
AiProviderAdapter adapter = adapters.stream()
|
||||
.filter(item -> item.supports(provider.getProviderType()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("AI_PROVIDER_ADAPTER_NOT_FOUND"));
|
||||
|
||||
return new RuntimeTarget(scene, endpoint, provider, adapter);
|
||||
}
|
||||
|
||||
private String resolveUserId(AiRuntimeRequest request) {
|
||||
if (StringUtils.hasText(request.getUserId())) {
|
||||
return request.getUserId();
|
||||
}
|
||||
String currentUserId = UserContextHolder.getCurrentUserId();
|
||||
return StringUtils.hasText(currentUserId) ? currentUserId : "anonymous";
|
||||
}
|
||||
|
||||
private void enrichInputs(AiRuntimeRequest request) {
|
||||
if (request.getInputs() == null) {
|
||||
request.setInputs(new com.alibaba.fastjson2.JSONObject());
|
||||
}
|
||||
String userId = resolveUserId(request);
|
||||
request.setUserId(userId);
|
||||
request.getInputs().put("userId", userId);
|
||||
request.getInputs().put("currentUserId", userId);
|
||||
request.getInputs().put("user_id", userId);
|
||||
if (StringUtils.hasText(request.getUserName())) {
|
||||
request.getInputs().put("userName", request.getUserName());
|
||||
request.getInputs().put("username", request.getUserName());
|
||||
}
|
||||
if (StringUtils.hasText(request.getUserType())) {
|
||||
request.getInputs().put("userType", request.getUserType());
|
||||
}
|
||||
if (StringUtils.hasText(request.getRequestId())) {
|
||||
request.getInputs().put("requestId", request.getRequestId());
|
||||
}
|
||||
enrichSceneInputs(request, userId);
|
||||
}
|
||||
|
||||
private void enrichSceneInputs(AiRuntimeRequest request, String userId) {
|
||||
if (!StringUtils.hasText(request.getSceneCode())) {
|
||||
return;
|
||||
}
|
||||
if ("script_generate".equals(request.getSceneCode()) || "short_story_generate".equals(request.getSceneCode())) {
|
||||
Boolean useSocialInsights = request.getInputs().getBoolean("useSocialInsights");
|
||||
String socialInsightContext = scriptContextService.buildSocialInsightContext(userId, useSocialInsights);
|
||||
if (StringUtils.hasText(socialInsightContext)) {
|
||||
request.getInputs().put("socialInsightContext", socialInsightContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeErrorCode(Exception e) {
|
||||
String message = e.getMessage();
|
||||
if (message != null && message.startsWith("AI_")) {
|
||||
return message;
|
||||
}
|
||||
if (message != null && message.contains("timed out")) {
|
||||
return "AI_STREAM_TIMEOUT";
|
||||
}
|
||||
return "AI_STREAM_INTERRUPTED";
|
||||
}
|
||||
|
||||
private Map<String, Object> recoveredDoneMetadata(String requestId, int streamChunks, Long durationMs, String code, String message) {
|
||||
Map<String, Object> metadata = new LinkedHashMap<>();
|
||||
metadata.put("requestId", requestId);
|
||||
metadata.put("streamChunks", streamChunks);
|
||||
metadata.put("durationMs", durationMs);
|
||||
metadata.put("recovered", true);
|
||||
metadata.put("warningCode", code);
|
||||
if (StringUtils.hasText(message)) {
|
||||
metadata.put("warningMessage", message);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private void emitDone(Consumer<AiStreamEvent> consumer, Map<String, Object> metadata) {
|
||||
try {
|
||||
consumer.accept(AiStreamEvent.done(metadata));
|
||||
} catch (Exception e) {
|
||||
log.debug("AI stream done event skipped after output persisted: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOrUpdateLog(AiCallLog callLog) {
|
||||
if (StringUtils.hasText(callLog.getId())) {
|
||||
callLogService.updateById(callLog);
|
||||
} else {
|
||||
callLogService.save(callLog);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RuntimeTarget {
|
||||
private final AiSceneBinding scene;
|
||||
private final AiEndpointConfig endpoint;
|
||||
private final AiProvider provider;
|
||||
private final AiProviderAdapter adapter;
|
||||
|
||||
private RuntimeTarget(AiSceneBinding scene, AiEndpointConfig endpoint, AiProvider provider, AiProviderAdapter adapter) {
|
||||
this.scene = scene;
|
||||
this.endpoint = endpoint;
|
||||
this.provider = provider;
|
||||
this.adapter = adapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user