2026-06-30 24681c81c09022f584a57006f2534b5f74723414
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo;
 
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;
 
/**
 * 讯飞星火 {@link ChatModel} 实现类
 *
 * @author fansili
 */
@Slf4j
public class XingHuoChatModel implements ChatModel {
 
    /**
     * 星火 X2
     *
     * @see <a href="https://spark-api-open.xf-yun.com/x2/chat/completions">接口地址</a>
     */
    public static final String MODEL_X2 = "x2";
 
    /**
     * 星火 X2 Flash
     *
     * @see <a href="https://spark-api-open.xf-yun.com/agent/v1/chat/completions">接口地址</a>
     */
    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;
 
    /**
     * 兼容 OpenAI 接口,进行复用
     */
    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 getChatModel(prompt).call(buildApiPrompt(prompt));
    }
 
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return getChatModel(prompt).stream(buildApiPrompt(prompt));
    }
 
    @Override
    public ChatOptions getOptions() {
        return options;
    }
 
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions 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);
        }
 
    }
 
}