Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
*/
package io.agentscope.core.formatter.dashscope.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.agentscope.core.model.EndpointType;

/**
* DashScope API request DTO.
Expand Down Expand Up @@ -53,12 +55,23 @@ public class DashScopeRequest {
@JsonProperty("parameters")
private DashScopeParameters parameters;

public DashScopeRequest() {}
/**
* The endpoint type for endpoint selection (not serialized to JSON).
*
* <p>This is an internal field used to determine which DashScope API endpoint to use.
* It does not get sent to the API.
*/
@JsonIgnore private EndpointType endpointType;

public DashScopeRequest() {
this.endpointType = EndpointType.AUTO;
}

public DashScopeRequest(String model, DashScopeInput input, DashScopeParameters parameters) {
this.model = model;
this.input = input;
this.parameters = parameters;
this.endpointType = EndpointType.AUTO;
}

public String getModel() {
Expand All @@ -85,6 +98,24 @@ public void setParameters(DashScopeParameters parameters) {
this.parameters = parameters;
}

/**
* Gets the endpoint type for endpoint selection.
*
* @return the endpoint type (defaults to AUTO)
*/
public EndpointType getEndpointType() {
return endpointType;
}

/**
* Sets the endpoint type for endpoint selection.
*
* @param endpointType the endpoint type
*/
public void setEndpointType(EndpointType endpointType) {
this.endpointType = endpointType;
}

public static Builder builder() {
return new Builder();
}
Expand All @@ -93,6 +124,7 @@ public static class Builder {
private String model;
private DashScopeInput input;
private DashScopeParameters parameters;
private EndpointType endpointType = EndpointType.AUTO;

public Builder model(String model) {
this.model = model;
Expand All @@ -109,8 +141,21 @@ public Builder parameters(DashScopeParameters parameters) {
return this;
}

/**
* Sets the endpoint type for endpoint selection.
*
* @param endpointType the endpoint type
* @return this builder
*/
public Builder endpointType(EndpointType endpointType) {
this.endpointType = endpointType;
return this;
}

public DashScopeRequest build() {
return new DashScopeRequest(model, input, parameters);
DashScopeRequest request = new DashScopeRequest(model, input, parameters);
request.setEndpointType(endpointType);
return request;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,8 @@
* <p>This implementation uses direct HTTP calls to DashScope API via OkHttp,
* without depending on the DashScope Java SDK.
*
* <p>Supports both text and vision models through automatic endpoint routing:
* <ul>
* <li>Vision models (names starting with "qvq" or containing "-vl") use MultiModalGeneration API
* <li>Text models use TextGeneration API
* </ul>
* <p>Supports both text and vision models through automatic endpoint routing.
* Use {@link EndpointType} to explicitly control the endpoint selection.
*
* <p>Features:
* <ul>
Expand All @@ -60,20 +57,67 @@ public class DashScopeChatModel extends ChatModelBase {
private final boolean stream;
private final Boolean enableThinking; // nullable
private final Boolean enableSearch; // nullable
private final EndpointType endpointType;
private final GenerateOptions defaultOptions;
private final Formatter<DashScopeMessage, DashScopeResponse, DashScopeRequest> formatter;

// HTTP client for API calls
private final DashScopeHttpClient httpClient;

/**
* Creates a new DashScope chat model instance.
* Creates a new DashScope chat model instance with automatic API type detection.
*
* <p>This constructor maintains backward compatibility. API type defaults to AUTO,
* which detects the endpoint based on model name.
*
* @param apiKey the API key for DashScope authentication
* @param modelName the model name (e.g., "qwen-max", "qwen-vl-plus")
* @param stream whether streaming should be enabled (ignored if enableThinking is true)
* @param enableThinking whether thinking mode should be enabled (null for disabled)
* @param enableSearch whether search enhancement should be enabled (null for disabled)
* @param defaultOptions default generation options (null for defaults)
* @param baseUrl custom base URL for DashScope API (null for default)
* @param formatter the message formatter to use (null for default DashScope formatter)
* @param httpTransport custom HTTP transport (null for default from factory)
* @param publicKeyId the RSA public key ID for encryption (null to disable encryption)
* @param publicKey the RSA public key for encryption (Base64-encoded, null to disable encryption)
*/
public DashScopeChatModel(
String apiKey,
String modelName,
boolean stream,
Boolean enableThinking,
Boolean enableSearch,
GenerateOptions defaultOptions,
String baseUrl,
Formatter<DashScopeMessage, DashScopeResponse, DashScopeRequest> formatter,
HttpTransport httpTransport,
String publicKeyId,
String publicKey) {
this(
apiKey,
modelName,
stream,
enableThinking,
enableSearch,
null,
defaultOptions,
baseUrl,
formatter,
httpTransport,
publicKeyId,
publicKey);
}

/**
* Creates a new DashScope chat model instance with explicit API type.
*
* @param apiKey the API key for DashScope authentication
* @param modelName the model name (e.g., "qwen-max", "qwen-vl-plus")
* @param stream whether streaming should be enabled (ignored if enableThinking is true)
* @param enableThinking whether thinking mode should be enabled (null for disabled)
* @param enableSearch whether search enhancement should be enabled (null for disabled)
* @param endpointType the endpoint type to use (null for AUTO detection)
* @param defaultOptions default generation options (null for defaults)
* @param baseUrl custom base URL for DashScope API (null for default)
* @param formatter the message formatter to use (null for default DashScope formatter)
Expand All @@ -87,6 +131,7 @@ public DashScopeChatModel(
boolean stream,
Boolean enableThinking,
Boolean enableSearch,
EndpointType endpointType,
GenerateOptions defaultOptions,
String baseUrl,
Formatter<DashScopeMessage, DashScopeResponse, DashScopeRequest> formatter,
Expand All @@ -103,6 +148,7 @@ public DashScopeChatModel(
this.stream = enableThinking != null && enableThinking ? true : stream;
this.enableThinking = enableThinking;
this.enableSearch = enableSearch;
this.endpointType = endpointType != null ? endpointType : EndpointType.AUTO;
this.defaultOptions =
defaultOptions != null ? defaultOptions : GenerateOptions.builder().build();
this.formatter = formatter != null ? formatter : new DashScopeChatFormatter();
Expand Down Expand Up @@ -150,8 +196,12 @@ protected Flux<ChatResponse> doStream(
List<Msg> messages, List<ToolSchema> tools, GenerateOptions options) {

if (log.isDebugEnabled()) {
boolean useMultimodal = httpClient.requiresMultimodalApi(modelName);
log.debug("DashScope API call: model={}, multimodal={}", modelName, useMultimodal);
boolean useMultimodal = httpClient.requiresMultimodalApi(modelName, endpointType);
log.debug(
"DashScope API call: model={}, endpointType={}, multimodal={}",
modelName,
endpointType,
useMultimodal);
}

Flux<ChatResponse> responseFlux = streamWithHttpClient(messages, tools, options);
Expand All @@ -169,7 +219,7 @@ protected Flux<ChatResponse> doStream(
private Flux<ChatResponse> streamWithHttpClient(
List<Msg> messages, List<ToolSchema> tools, GenerateOptions options) {
Instant start = Instant.now();
boolean useMultimodal = httpClient.requiresMultimodalApi(modelName);
boolean useMultimodal = httpClient.requiresMultimodalApi(modelName, endpointType);

// Merge options with defaultOptions (options takes precedence)
GenerateOptions effectiveOptions = GenerateOptions.mergeOptions(options, defaultOptions);
Expand Down Expand Up @@ -218,6 +268,9 @@ private Flux<ChatResponse> streamWithHttpClient(
// Apply thinking mode if enabled
applyThinkingMode(request, effectiveOptions);

// Set endpoint type for endpoint selection
request.setEndpointType(endpointType);

if (stream) {
// Streaming mode
return httpClient.stream(
Expand Down Expand Up @@ -298,6 +351,7 @@ public static class Builder {
private boolean stream = true;
private Boolean enableThinking;
private Boolean enableSearch;
private EndpointType endpointType;
private GenerateOptions defaultOptions = null;
private String baseUrl;
private Formatter<DashScopeMessage, DashScopeResponse, DashScopeRequest> formatter;
Expand All @@ -318,14 +372,11 @@ public Builder apiKey(String apiKey) {
/**
* Sets the model name to use.
*
* <p>The model name determines which API is used:
* <ul>
* <li>Vision models (qvq* or *-vl*) → MultiModal API</li>
* <li>Text models → Text Generation API</li>
* </ul>
* <p>The model name determines which API is used when endpointType is AUTO.
*
* @param modelName the model name (e.g., "qwen-max", "qwen-vl-plus")
* @return this builder instance
* @see DashScopeHttpClient#isMultimodalModel(String)
*/
public Builder modelName(String modelName) {
this.modelName = modelName;
Expand Down Expand Up @@ -374,6 +425,19 @@ public Builder enableSearch(Boolean enableSearch) {
return this;
}

/**
* Sets the endpoint type to use for endpoint routing.
*
* @param endpointType the endpoint type to use (null for AUTO)
* @return this builder instance
* @see EndpointType
* @see DashScopeHttpClient#isMultimodalModel(String)
*/
public Builder endpointType(EndpointType endpointType) {
this.endpointType = endpointType;
return this;
}

/**
* Sets the default generation options.
*
Expand Down Expand Up @@ -503,6 +567,7 @@ public DashScopeChatModel build() {
stream,
enableThinking,
enableSearch,
endpointType,
effectiveOptions,
baseUrl,
formatter,
Expand Down
Loading