- 新增“代理”下拉:Trae CN/INTL、WorkBuddy CN/INTL - WorkBuddyModelProvider 在线拉取 /v2/enterprises/personal/models - Trae/WorkBuddy 各区域独立记住所选测试模型 - 真实代理转发仍按前缀自动路由,不受测试选择影响 - 版本升至 1.7.6
This commit is contained in:
parent
0283ce6bdc
commit
f13b38c02b
@ -16,8 +16,8 @@ android {
|
|||||||
applicationId = "com.rainy.token"
|
applicationId = "com.rainy.token"
|
||||||
minSdk = 31
|
minSdk = 31
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 25
|
versionCode = 26
|
||||||
versionName = "1.7.5"
|
versionName = "1.7.6"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class TraeChatProxy @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
"https://trae-api-cn.mchost.guru"
|
"https://trae-api-cn.mchost.guru"
|
||||||
}
|
}
|
||||||
val upstreamBody = buildUpstreamBody(requestBody, credential)
|
val upstreamBody = buildUpstreamBody(requestBody, credential, effectiveRegion)
|
||||||
val requestId = UUID.randomUUID().toString()
|
val requestId = UUID.randomUUID().toString()
|
||||||
|
|
||||||
val builder = Request.Builder()
|
val builder = Request.Builder()
|
||||||
@ -99,7 +99,7 @@ class TraeChatProxy @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
"https://trae-api-cn.mchost.guru"
|
"https://trae-api-cn.mchost.guru"
|
||||||
}
|
}
|
||||||
val upstreamBody = buildUpstreamBody(requestBody, credential)
|
val upstreamBody = buildUpstreamBody(requestBody, credential, effectiveRegion)
|
||||||
val requestId = UUID.randomUUID().toString()
|
val requestId = UUID.randomUUID().toString()
|
||||||
|
|
||||||
val builder = Request.Builder()
|
val builder = Request.Builder()
|
||||||
@ -230,13 +230,14 @@ class TraeChatProxy @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 外部模型名 -> Trae 内部模型名;空/auto 用设置页选择的默认模型。 */
|
/** 外部模型名 -> Trae 内部模型名;空/auto 用设置页选择的默认模型。 */
|
||||||
private fun normalizeModel(raw: String?): String {
|
private fun normalizeModel(raw: String?, region: ProviderRegion?): String {
|
||||||
val id = raw?.trim()?.takeIf { it.isNotBlank() } ?: return modelProvider.selectedModel
|
val selected = modelProvider.selectedFor(region ?: ProviderRegion.CN)
|
||||||
if (id.equals("auto", ignoreCase = true)) return modelProvider.selectedModel
|
val id = raw?.trim()?.takeIf { it.isNotBlank() } ?: return selected
|
||||||
|
if (id.equals("auto", ignoreCase = true)) return selected
|
||||||
return MODEL_ALIASES[id.lowercase()] ?: id
|
return MODEL_ALIASES[id.lowercase()] ?: id
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildUpstreamBody(raw: String, credential: Credential.TraeCredential): String {
|
private fun buildUpstreamBody(raw: String, credential: Credential.TraeCredential, region: ProviderRegion?): String {
|
||||||
val src = try {
|
val src = try {
|
||||||
json.parseToJsonElement(raw) as? JsonObject
|
json.parseToJsonElement(raw) as? JsonObject
|
||||||
} catch (_: Throwable) {
|
} catch (_: Throwable) {
|
||||||
@ -244,7 +245,7 @@ class TraeChatProxy @Inject constructor(
|
|||||||
} ?: return raw
|
} ?: return raw
|
||||||
|
|
||||||
val rawModel = (src["model"] as? JsonPrimitive)?.contentOrNull?.takeIf { it.isNotBlank() }
|
val rawModel = (src["model"] as? JsonPrimitive)?.contentOrNull?.takeIf { it.isNotBlank() }
|
||||||
val model = normalizeModel(rawModel)
|
val model = normalizeModel(rawModel, region)
|
||||||
val messages = convertNativeMessages(src["messages"])
|
val messages = convertNativeMessages(src["messages"])
|
||||||
val sessionId = UUID.randomUUID().toString().replace("-", "")
|
val sessionId = UUID.randomUUID().toString().replace("-", "")
|
||||||
|
|
||||||
|
|||||||
@ -41,16 +41,20 @@ class TraeModelProvider @Inject constructor(
|
|||||||
private val _models = MutableStateFlow(defaultModels())
|
private val _models = MutableStateFlow(defaultModels())
|
||||||
val models: StateFlow<List<TraeModel>> = _models.asStateFlow()
|
val models: StateFlow<List<TraeModel>> = _models.asStateFlow()
|
||||||
|
|
||||||
/** 当前默认模型(用户在设置页选择的 Trae 模型名)。 */
|
|
||||||
var selectedModel: String = prefs.getString(KEY_MODEL, "glm-5.2") ?: "glm-5.2"
|
|
||||||
private set
|
|
||||||
|
|
||||||
private val json = Json { ignoreUnknownKeys = true }
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
fun setSelectedModel(id: String) {
|
/** 当前区域选择的默认模型(未设置时 CN 用 GLM-5.2)。 */
|
||||||
|
fun selectedFor(region: ProviderRegion): String {
|
||||||
|
if (region == ProviderRegion.INTL) {
|
||||||
|
return prefs.getString(KEY_MODEL_INTL, DEFAULT_MODEL_INTL) ?: DEFAULT_MODEL_INTL
|
||||||
|
}
|
||||||
|
return prefs.getString(KEY_MODEL_CN, "glm-5.2") ?: "glm-5.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelectedFor(region: ProviderRegion, id: String) {
|
||||||
if (id.isBlank()) return
|
if (id.isBlank()) return
|
||||||
selectedModel = id
|
val key = if (region == ProviderRegion.INTL) KEY_MODEL_INTL else KEY_MODEL_CN
|
||||||
prefs.edit().putString(KEY_MODEL, id).apply()
|
prefs.edit().putString(key, id).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 拉取账号在线模型列表;失败时保留内置列表并返回 false。 */
|
/** 拉取账号在线模型列表;失败时保留内置列表并返回 false。 */
|
||||||
@ -120,7 +124,9 @@ class TraeModelProvider @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val KEY_MODEL = "trae_default_model"
|
private const val KEY_MODEL_CN = "trae_default_model_cn"
|
||||||
|
private const val KEY_MODEL_INTL = "trae_default_model_intl"
|
||||||
|
private const val DEFAULT_MODEL_INTL = "glm-5.2"
|
||||||
|
|
||||||
/** 内置兜底模型(参考 trae2api-cn 的模型映射 /v1/models 列表)。 */
|
/** 内置兜底模型(参考 trae2api-cn 的模型映射 /v1/models 列表)。 */
|
||||||
fun defaultModels(): List<TraeModel> = listOf(
|
fun defaultModels(): List<TraeModel> = listOf(
|
||||||
|
|||||||
@ -0,0 +1,119 @@
|
|||||||
|
package com.rainy.token.data.proxy
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import com.rainy.token.data.repository.CredentialRepository
|
||||||
|
import com.rainy.token.domain.model.Credential
|
||||||
|
import com.rainy.token.domain.service.ServiceType
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import kotlinx.serialization.json.contentOrNull
|
||||||
|
import okhttp3.Headers.Companion.toHeaders
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorkBuddy / CodeBuddy 模型列表:
|
||||||
|
* 在线接口 GET {base}/v2/enterprises/personal/models + 内置兜底 + 按区域记住选择。
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class WorkBuddyModelProvider @Inject constructor(
|
||||||
|
private val okHttpClient: OkHttpClient,
|
||||||
|
private val credentialRepository: CredentialRepository,
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val prefs: SharedPreferences =
|
||||||
|
context.getSharedPreferences("rainytoken_proxy", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
private val _models = MutableStateFlow(defaultModels())
|
||||||
|
val models: StateFlow<List<TraeModelProvider.TraeModel>> = _models.asStateFlow()
|
||||||
|
|
||||||
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
|
fun selectedFor(region: ProviderRegion): String {
|
||||||
|
val key = if (region == ProviderRegion.INTL) KEY_MODEL_INTL else KEY_MODEL_CN
|
||||||
|
return prefs.getString(key, DEFAULT_MODEL) ?: DEFAULT_MODEL
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelectedFor(region: ProviderRegion, id: String) {
|
||||||
|
if (id.isBlank()) return
|
||||||
|
val key = if (region == ProviderRegion.INTL) KEY_MODEL_INTL else KEY_MODEL_CN
|
||||||
|
prefs.edit().putString(key, id).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun refreshFor(region: ProviderRegion): Boolean = withContext(Dispatchers.IO) {
|
||||||
|
val credential = credentialRepository.get(ServiceType.WORKBUDDY, null)
|
||||||
|
?: return@withContext false
|
||||||
|
if (credential !is Credential.WorkBuddyCredential) return@withContext false
|
||||||
|
val accessToken = credential.accessToken.trim().takeIf { it.isNotBlank() }
|
||||||
|
?: return@withContext false
|
||||||
|
val intl = region == ProviderRegion.INTL
|
||||||
|
val base = if (intl) "https://www.workbuddy.ai" else "https://copilot.tencent.com"
|
||||||
|
val url = base + "/v2/enterprises/personal/models"
|
||||||
|
|
||||||
|
val headers = linkedMapOf(
|
||||||
|
"Authorization" to "Bearer $accessToken",
|
||||||
|
"Content-Type" to "application/json",
|
||||||
|
"Accept" to "application/json",
|
||||||
|
"X-Client-ID" to "codebuddy-cli",
|
||||||
|
"X-Client-Version" to "2.143.1",
|
||||||
|
"X-Product" to "SaaS",
|
||||||
|
"User-Agent" to "CLI/2.143.1 CodeBuddy/2.143.1",
|
||||||
|
"Origin" to (if (intl) "https://www.workbuddy.ai" else "https://www.codebuddy.cn"),
|
||||||
|
)
|
||||||
|
val request = Request.Builder().url(url).headers(headers.toHeaders()).get().build()
|
||||||
|
val parsed = try {
|
||||||
|
okHttpClient.newCall(request).execute().use { resp ->
|
||||||
|
if (!resp.isSuccessful) return@use null
|
||||||
|
val body = resp.body?.string() ?: return@use null
|
||||||
|
parseModelList(body)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (parsed.isNullOrEmpty()) return@withContext false
|
||||||
|
_models.value = parsed
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseModelList(body: String): List<TraeModelProvider.TraeModel>? {
|
||||||
|
val root = runCatching { json.parseToJsonElement(body) as? JsonObject }.getOrNull()
|
||||||
|
?: return null
|
||||||
|
val data = root["data"] as? JsonObject ?: return null
|
||||||
|
val models = data["models"] as? JsonArray ?: return null
|
||||||
|
val seen = LinkedHashMap<String, TraeModelProvider.TraeModel>()
|
||||||
|
for (rawEl in models) {
|
||||||
|
val raw = rawEl as? JsonObject ?: continue
|
||||||
|
val id = (raw["id"] as? JsonPrimitive)?.contentOrNull?.trim()
|
||||||
|
?.takeIf { it.isNotEmpty() } ?: continue
|
||||||
|
val label = (raw["name"] as? JsonPrimitive)?.contentOrNull?.takeIf { it.isNotBlank() } ?: id
|
||||||
|
seen[id] = TraeModelProvider.TraeModel(id, label)
|
||||||
|
}
|
||||||
|
return seen.values.toList().ifEmpty { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val KEY_MODEL_CN = "wb_default_model_cn"
|
||||||
|
private const val KEY_MODEL_INTL = "wb_default_model_intl"
|
||||||
|
private const val DEFAULT_MODEL = "hy4-preview"
|
||||||
|
|
||||||
|
/** 内置兜底(参考 workbuddy-gateway 模型目录,在线列表成功后会被替换)。 */
|
||||||
|
fun defaultModels(): List<TraeModelProvider.TraeModel> = listOf(
|
||||||
|
TraeModelProvider.TraeModel("hy4-preview", "hy4-preview"),
|
||||||
|
TraeModelProvider.TraeModel("hy3", "hy3"),
|
||||||
|
TraeModelProvider.TraeModel("hy3-preview", "hy3-preview"),
|
||||||
|
TraeModelProvider.TraeModel("deepseek-v4.1-flash", "deepseek-v4.1-flash"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -109,6 +109,7 @@ fun SettingsScreen(
|
|||||||
val proxyDebug by viewModel.proxyDebug.collectAsStateWithLifecycle()
|
val proxyDebug by viewModel.proxyDebug.collectAsStateWithLifecycle()
|
||||||
val proxyDebugRaw by viewModel.proxyDebugRaw.collectAsStateWithLifecycle()
|
val proxyDebugRaw by viewModel.proxyDebugRaw.collectAsStateWithLifecycle()
|
||||||
val proxyModels by viewModel.proxyModels.collectAsStateWithLifecycle()
|
val proxyModels by viewModel.proxyModels.collectAsStateWithLifecycle()
|
||||||
|
val selectedProxy by viewModel.selectedProxy.collectAsStateWithLifecycle()
|
||||||
val proxyModel by viewModel.proxyModel.collectAsStateWithLifecycle()
|
val proxyModel by viewModel.proxyModel.collectAsStateWithLifecycle()
|
||||||
var showLanguageDialog by remember { mutableStateOf(false) }
|
var showLanguageDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@ -187,6 +188,9 @@ fun SettingsScreen(
|
|||||||
error = proxyError,
|
error = proxyError,
|
||||||
debug = proxyDebug,
|
debug = proxyDebug,
|
||||||
debugRaw = proxyDebugRaw,
|
debugRaw = proxyDebugRaw,
|
||||||
|
proxyTargets = viewModel.proxyTargets,
|
||||||
|
selectedProxy = selectedProxy,
|
||||||
|
onSelectProxy = viewModel::selectProxy,
|
||||||
models = proxyModels,
|
models = proxyModels,
|
||||||
selectedModel = proxyModel,
|
selectedModel = proxyModel,
|
||||||
onSelectModel = viewModel::selectProxyModel,
|
onSelectModel = viewModel::selectProxyModel,
|
||||||
@ -225,6 +229,9 @@ private fun ProxyGatewayCard(
|
|||||||
error: String?,
|
error: String?,
|
||||||
debug: String?,
|
debug: String?,
|
||||||
debugRaw: String?,
|
debugRaw: String?,
|
||||||
|
proxyTargets: List<ProxyTarget>,
|
||||||
|
selectedProxy: ProxyTarget,
|
||||||
|
onSelectProxy: (ProxyTarget) -> Unit,
|
||||||
models: List<TraeModelProvider.TraeModel>,
|
models: List<TraeModelProvider.TraeModel>,
|
||||||
selectedModel: String,
|
selectedModel: String,
|
||||||
onSelectModel: (String) -> Unit,
|
onSelectModel: (String) -> Unit,
|
||||||
@ -234,6 +241,7 @@ private fun ProxyGatewayCard(
|
|||||||
onTestWorkBuddy: () -> Unit
|
onTestWorkBuddy: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
var proxyMenuExpanded by remember { mutableStateOf(false) }
|
||||||
var modelMenuExpanded by remember { mutableStateOf(false) }
|
var modelMenuExpanded by remember { mutableStateOf(false) }
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
@ -284,6 +292,36 @@ private fun ProxyGatewayCard(
|
|||||||
Text("测试 WorkBuddy 流")
|
Text("测试 WorkBuddy 流")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "代理",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = InkMuted
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Box {
|
||||||
|
TextButton(onClick = { proxyMenuExpanded = true }) {
|
||||||
|
Text(selectedProxy.label + " ▾")
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = proxyMenuExpanded,
|
||||||
|
onDismissRequest = { proxyMenuExpanded = false }
|
||||||
|
) {
|
||||||
|
proxyTargets.forEach { t ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(t.label) },
|
||||||
|
onClick = {
|
||||||
|
onSelectProxy(t)
|
||||||
|
proxyMenuExpanded = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import com.rainy.token.data.proxy.ProxyUpstreamStream
|
|||||||
import com.rainy.token.data.proxy.ProxyServerConfig
|
import com.rainy.token.data.proxy.ProxyServerConfig
|
||||||
import com.rainy.token.data.proxy.TraeChatProxy
|
import com.rainy.token.data.proxy.TraeChatProxy
|
||||||
import com.rainy.token.data.proxy.TraeModelProvider
|
import com.rainy.token.data.proxy.TraeModelProvider
|
||||||
|
import com.rainy.token.data.proxy.WorkBuddyModelProvider
|
||||||
import com.rainy.token.data.proxy.WorkBuddyChatProxy
|
import com.rainy.token.data.proxy.WorkBuddyChatProxy
|
||||||
import com.rainy.token.data.repository.CredentialRepository
|
import com.rainy.token.data.repository.CredentialRepository
|
||||||
import com.rainy.token.domain.model.CredentialStatus
|
import com.rainy.token.domain.model.CredentialStatus
|
||||||
@ -41,6 +42,7 @@ class SettingsViewModel @Inject constructor(
|
|||||||
private val traeChatProxy: TraeChatProxy,
|
private val traeChatProxy: TraeChatProxy,
|
||||||
private val workBuddyChatProxy: WorkBuddyChatProxy,
|
private val workBuddyChatProxy: WorkBuddyChatProxy,
|
||||||
private val traeModelProvider: TraeModelProvider,
|
private val traeModelProvider: TraeModelProvider,
|
||||||
|
private val workBuddyModelProvider: WorkBuddyModelProvider,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(SettingsUiState())
|
private val _uiState = MutableStateFlow(SettingsUiState())
|
||||||
@ -76,23 +78,56 @@ class SettingsViewModel @Inject constructor(
|
|||||||
private val _proxyDebugRaw = MutableStateFlow<String?>(null)
|
private val _proxyDebugRaw = MutableStateFlow<String?>(null)
|
||||||
val proxyDebugRaw: StateFlow<String?> = _proxyDebugRaw.asStateFlow()
|
val proxyDebugRaw: StateFlow<String?> = _proxyDebugRaw.asStateFlow()
|
||||||
|
|
||||||
/** Trae 可用模型列表(内置兜底 + 在线拉取账号模型)。 */
|
/** 代理目标(用于测试和模型选择;真实转发仍由 ProviderRouter 自动路由)。 */
|
||||||
val proxyModels: StateFlow<List<TraeModelProvider.TraeModel>> = traeModelProvider.models
|
val proxyTargets: List<ProxyTarget> = ProxyTarget.entries
|
||||||
|
|
||||||
/** 当前选择的 Trae 默认模型。 */
|
private val _selectedProxy = MutableStateFlow(ProxyTarget.TRAE_CN)
|
||||||
private val _proxyModel = MutableStateFlow(traeModelProvider.selectedModel)
|
val selectedProxy: StateFlow<ProxyTarget> = _selectedProxy.asStateFlow()
|
||||||
|
|
||||||
|
private val _proxyModels = MutableStateFlow<List<TraeModelProvider.TraeModel>>(traeModelProvider.models.value)
|
||||||
|
val proxyModels: StateFlow<List<TraeModelProvider.TraeModel>> = _proxyModels.asStateFlow()
|
||||||
|
|
||||||
|
private val _proxyModel = MutableStateFlow(traeModelProvider.selectedFor(ProviderRegion.CN))
|
||||||
val proxyModel: StateFlow<String> = _proxyModel.asStateFlow()
|
val proxyModel: StateFlow<String> = _proxyModel.asStateFlow()
|
||||||
|
|
||||||
/** 切换 Trae 默认模型并持久化。 */
|
/** 切换当前测试目标代理。 */
|
||||||
|
fun selectProxy(target: ProxyTarget) {
|
||||||
|
_selectedProxy.value = target
|
||||||
|
val region = target.region()
|
||||||
|
_proxyModels.value = when (target) {
|
||||||
|
ProxyTarget.TRAE_CN, ProxyTarget.TRAE_INTL -> traeModelProvider.models.value
|
||||||
|
else -> workBuddyModelProvider.models.value
|
||||||
|
}
|
||||||
|
_proxyModel.value = when (target) {
|
||||||
|
ProxyTarget.TRAE_CN, ProxyTarget.TRAE_INTL -> traeModelProvider.selectedFor(region)
|
||||||
|
else -> workBuddyModelProvider.selectedFor(region)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 切换当前代理的测试模型并持久化。 */
|
||||||
fun selectProxyModel(id: String) {
|
fun selectProxyModel(id: String) {
|
||||||
traeModelProvider.setSelectedModel(id)
|
val target = _selectedProxy.value
|
||||||
|
val region = target.region()
|
||||||
|
when (target) {
|
||||||
|
ProxyTarget.TRAE_CN, ProxyTarget.TRAE_INTL -> traeModelProvider.setSelectedFor(region, id)
|
||||||
|
else -> workBuddyModelProvider.setSelectedFor(region, id)
|
||||||
|
}
|
||||||
_proxyModel.value = id
|
_proxyModel.value = id
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 拉取当前账号可用的 Trae 模型列表。 */
|
/** 拉取当前代理可用模型;失败保留内置列表。 */
|
||||||
fun refreshProxyModels() {
|
fun refreshProxyModels() {
|
||||||
|
val target = _selectedProxy.value
|
||||||
|
val region = target.region()
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val ok = traeModelProvider.refreshFor(ProviderRegion.CN)
|
val ok = when (target) {
|
||||||
|
ProxyTarget.TRAE_CN, ProxyTarget.TRAE_INTL -> traeModelProvider.refreshFor(region)
|
||||||
|
else -> workBuddyModelProvider.refreshFor(region)
|
||||||
|
}
|
||||||
|
_proxyModels.value = when (target) {
|
||||||
|
ProxyTarget.TRAE_CN, ProxyTarget.TRAE_INTL -> traeModelProvider.models.value
|
||||||
|
else -> workBuddyModelProvider.models.value
|
||||||
|
}
|
||||||
if (!ok) _proxyDebug.value = "模型列表更新失败(当前显示内置列表)"
|
if (!ok) _proxyDebug.value = "模型列表更新失败(当前显示内置列表)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +137,7 @@ class SettingsViewModel @Inject constructor(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_proxyDebug.value = "正在请求 Trae CN 流..."
|
_proxyDebug.value = "正在请求 Trae CN 流..."
|
||||||
_proxyDebugRaw.value = null
|
_proxyDebugRaw.value = null
|
||||||
val model = traeModelProvider.selectedModel
|
val model = traeModelProvider.selectedFor(ProviderRegion.CN)
|
||||||
val body = """{"model":"$model","stream":true,"messages":[{"role":"user","content":"只回复OK两个字"}],"max_tokens":100}"""
|
val body = """{"model":"$model","stream":true,"messages":[{"role":"user","content":"只回复OK两个字"}],"max_tokens":100}"""
|
||||||
try {
|
try {
|
||||||
val stream = traeChatProxy.openStreamingChat(body, null, ProviderRegion.CN)
|
val stream = traeChatProxy.openStreamingChat(body, null, ProviderRegion.CN)
|
||||||
@ -132,7 +167,8 @@ class SettingsViewModel @Inject constructor(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_proxyDebug.value = "正在请求 WorkBuddy CN 流..."
|
_proxyDebug.value = "正在请求 WorkBuddy CN 流..."
|
||||||
_proxyDebugRaw.value = null
|
_proxyDebugRaw.value = null
|
||||||
val body = """{"model":"glm-5.2","stream":true,"messages":[{"role":"user","content":"只回复OK两个字"}],"max_tokens":100}"""
|
val model = workBuddyModelProvider.selectedFor(ProviderRegion.CN)
|
||||||
|
val body = """{"model":"$model","stream":true,"messages":[{"role":"user","content":"只回复OK两个字"}],"max_tokens":100}"""
|
||||||
try {
|
try {
|
||||||
val stream = workBuddyChatProxy.openStreamingChat(body, null, ProviderRegion.CN)
|
val stream = workBuddyChatProxy.openStreamingChat(body, null, ProviderRegion.CN)
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
@ -313,6 +349,18 @@ class SettingsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ProxyTarget(val key: String, val label: String) {
|
||||||
|
TRAE_CN("trae_cn", "Trae CN"),
|
||||||
|
TRAE_INTL("trae_intl", "Trae INTL"),
|
||||||
|
WORKBUDDY_CN("wb_cn", "WorkBuddy CN"),
|
||||||
|
WORKBUDDY_INTL("wb_intl", "WorkBuddy INTL");
|
||||||
|
|
||||||
|
fun region(): ProviderRegion = when (this) {
|
||||||
|
TRAE_CN, WORKBUDDY_CN -> ProviderRegion.CN
|
||||||
|
else -> ProviderRegion.INTL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class StreamProbeResult(
|
data class StreamProbeResult(
|
||||||
val summary: String,
|
val summary: String,
|
||||||
val raw: String,
|
val raw: String,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user