昨天 8f3bf7050e65fdbe55eaad74fde307c57dab960e
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java
@@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo;
import lombok.RequiredArgsConstructor;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
/**
@@ -14,37 +17,144 @@
 * @author fansili
 */
@Slf4j
@RequiredArgsConstructor
public class XingHuoChatModel implements ChatModel {
    public static final String BASE_URL_V1 = "https://spark-api-open.xf-yun.com";
    public static final String BASE_URL_V2 = "https://spark-api-open.xf-yun.com";
    public static final String BASE_COMPLETIONS_PATH_V2 = "/v2/chat/completions";
    /**
     * 星火 X2
     *
     * @see <a href="https://spark-api-open.xf-yun.com/x2/chat/completions">接口地址</a>
     */
    public static final String MODEL_X2 = "x2";
    /**
     * 已知模型名列表:x1、4.0Ultra、generalv3.5、max-32k、generalv3、pro-128k、lite
     * 星火 X2 Flash
     *
     * @see <a href="https://spark-api-open.xf-yun.com/agent/v1/chat/completions">接口地址</a>
     */
    public static final String MODEL_DEFAULT = "4.0Ultra";
    public static final String MODEL_X2_FLASH = "x2-flash";
    private static final String BASE_URL_X2 = "https://spark-api-open.xf-yun.com/x2";
    private static final String BASE_URL_X2_FLASH = "https://spark-api-open.xf-yun.com/agent/v1";
    public static final String MODEL_DEFAULT = MODEL_X2_FLASH;
    private static String getBaseUrl(String model) {
        if (MODEL_X2_FLASH.equals(model)) {
            return BASE_URL_X2_FLASH;
        }
        return BASE_URL_X2;
    }
    private final String apiKey;
    private final DeepSeekChatOptions options;
    /**
     * v1 兼容 OpenAI 接口,进行复用
     * 兼容 OpenAI 接口,进行复用
     */
    private final ChatModel openAiChatModelV1;
    private final ChatModel chatModelX2;
    private final ChatModel chatModelX2Flash;
    private XingHuoChatModel(String apiKey, DeepSeekChatOptions options) {
        this.apiKey = apiKey;
        this.options = options;
        this.chatModelX2 = buildChatModel(MODEL_X2);
        this.chatModelX2Flash = buildChatModel(MODEL_X2_FLASH);
    }
    public static Builder builder() {
        return new Builder();
    }
    @Override
    public ChatResponse call(Prompt prompt) {
        return openAiChatModelV1.call(prompt);
        return getChatModel(prompt).call(buildApiPrompt(prompt));
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return openAiChatModelV1.stream(prompt);
        return getChatModel(prompt).stream(buildApiPrompt(prompt));
    }
    @Override
    public ChatOptions getOptions() {
        return options;
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModelV1.getDefaultOptions();
        return getOptions();
    }
    private ChatModel getChatModel(Prompt prompt) {
        String model = options.getModel();
        ChatOptions options = prompt.getOptions();
        if (options != null && isBusinessModel(options.getModel())) {
            model = options.getModel();
        }
        return getChatModel(model);
    }
    private ChatModel getChatModel(String model) {
        if (MODEL_X2_FLASH.equals(model)) {
            return chatModelX2Flash;
        }
        return chatModelX2;
    }
    private ChatModel buildChatModel(String model) {
        return DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder()
                        .baseUrl(getBaseUrl(model))
                        .apiKey(apiKey)
                        .build())
                .options(options.mutate().model("spark-x").build())
                .build();
    }
    private static Prompt buildApiPrompt(Prompt prompt) {
        ChatOptions options = prompt.getOptions();
        if (options == null) {
            return prompt;
        }
        return Prompt.builder()
                .messages(prompt.getInstructions())
                .chatOptions(options.mutate().model("spark-x").build())
                .build();
    }
    private static boolean isBusinessModel(String model) {
        return MODEL_X2.equals(model) || MODEL_X2_FLASH.equals(model);
    }
    public static final class Builder {
        private String apiKey;
        private DeepSeekChatOptions options;
        public Builder apiKey(String apiKey) {
            this.apiKey = apiKey;
            return this;
        }
        public Builder options(DeepSeekChatOptions options) {
            this.options = options;
            return this;
        }
        public XingHuoChatModel build() {
            DeepSeekChatOptions options = this.options != null ? this.options : DeepSeekChatOptions.builder().build();
            if (StrUtil.isEmpty(options.getModel())) {
                options = options.mutate().model(MODEL_DEFAULT).build();
            }
            return new XingHuoChatModel(apiKey, options);
        }
    }
}