refactor(config): adjust method init and onReload to support polymorphic config loading

This commit is contained in:
2026-04-04 23:34:00 +08:00
parent 6503ec32b4
commit 50db3fa7b2
3 changed files with 38 additions and 18 deletions

View File

@@ -1,6 +1,8 @@
package work.slhaf.partner.api.agent.model
import com.alibaba.fastjson2.JSONObject
import org.slf4j.LoggerFactory
import work.slhaf.partner.api.agent.model.ProviderConfig.ProviderType.OPENAI_COMPATIBLE
import work.slhaf.partner.api.agent.model.provider.ModelProvider
import work.slhaf.partner.api.agent.model.provider.ProviderOverride
import work.slhaf.partner.api.agent.model.provider.openai.OpenAiCompatibleProvider
@@ -9,6 +11,7 @@ import work.slhaf.partner.api.agent.runtime.config.ConfigDoc
import work.slhaf.partner.api.agent.runtime.config.ConfigRegistration
import work.slhaf.partner.api.agent.runtime.config.Configurable
import java.nio.file.Path
import java.util.Locale.getDefault
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
@@ -62,17 +65,30 @@ object ModelRuntimeRegistry : Configurable, ConfigRegistration<ModelRuntimeRegis
override fun type(): Class<ModelRuntimeRegistryConfig> = ModelRuntimeRegistryConfig::class.java
override fun init(config: ModelRuntimeRegistryConfig) = providerLock.withLock {
override fun init(config: ModelRuntimeRegistryConfig, json: JSONObject?) = providerLock.withLock {
config.providerConfigSet.forEach { registerProvider(it) }
config.runtimeConfigSet.forEach { forkProvider(it) }
}
override fun onReload(config: ModelRuntimeRegistryConfig) = providerLock.withLock {
override fun onReload(config: ModelRuntimeRegistryConfig, json: JSONObject?) = providerLock.withLock {
val root = json ?: return@withLock
val baseProviderSnapshot = baseProvider.toMap()
val runtimeProviderSnapshot = runtimeProvider.toMap()
try {
val providerSetJson = root.getJSONArray("providerConfigSet")
?: throw IllegalStateException("providerConfigSet is missing or not an array")
baseProvider.clear()
config.providerConfigSet.forEach { registerProvider(it) }
for (i in providerSetJson.indices) {
val providerJson = providerSetJson.getJSONObject(i)
?: throw IllegalStateException("providerConfigSet[$i] is not an object")
val typeText = providerJson.getString("type")
?: throw IllegalStateException("providerConfigSet[$i].type is missing")
val providerType = ProviderConfig.ProviderType.valueOf(typeText.uppercase(getDefault()))
val concreteProviderConfig = when (providerType) {
OPENAI_COMPATIBLE -> providerJson.toJavaObject(OpenAiCompatibleProviderConfig::class.java)
}
registerProvider(concreteProviderConfig)
}
runtimeProvider.clear()
config.runtimeConfigSet.forEach { forkProvider(it) }
} catch (e: Exception) {
@@ -92,7 +108,7 @@ object ModelRuntimeRegistry : Configurable, ConfigRegistration<ModelRuntimeRegis
setOf(
OpenAiCompatibleProviderConfig(
"default",
ProviderConfig.ProviderType.OPENAI_COMPATIBLE,
OPENAI_COMPATIBLE,
defaultModel, defaultBaseUrl, defaultApiKey
)
), setOf()
@@ -119,7 +135,7 @@ data class ModelRuntimeRegistryConfig(
[
{
"modelKey": "example_model_key", // 模块通过该 key 定位到对应的提供商配置
"providerName: "example_provider_name", // 该配置对应的提供商名称
"providerName": "example_provider_name", // 该配置对应的提供商名称
"override": { // 该配置需要重写的内容, 如果无需重写,可忽略该字段,该字段的各个子字段均为可选覆写
"model": "example_override_model",
"temperature": "example_override_temperature",
@@ -156,7 +172,7 @@ sealed class ProviderConfig {
data class OpenAiCompatibleProviderConfig(
override val name: String,
override val type: ProviderType = ProviderType.OPENAI_COMPATIBLE,
override val type: ProviderType = OPENAI_COMPATIBLE,
override val defaultModel: String,
val baseUrl: String,

View File

@@ -1,6 +1,7 @@
package work.slhaf.partner.api.agent.runtime.config
import com.alibaba.fastjson2.JSON
import com.alibaba.fastjson2.JSONObject
import org.slf4j.LoggerFactory
import work.slhaf.partner.api.agent.runtime.exception.AgentLaunchFailedException
import work.slhaf.partner.api.common.support.DirectoryWatchSupport
@@ -82,14 +83,14 @@ object ConfigCenter : AutoCloseable {
@Suppress("UNCHECKED_CAST")
fun initAll() {
registrations.forEach { (path, registration) ->
val config = loadConfig(path, registration)
if (config != null) {
(registration as ConfigRegistration<Config>).init(config)
val pair = loadConfig(path, registration)
if (pair != null) {
(registration as ConfigRegistration<Config>).init(pair.first, pair.second)
return
}
val defaultConfig = registration.defaultConfig()
if (defaultConfig != null) {
(registration as ConfigRegistration<Config>).init(defaultConfig)
(registration as ConfigRegistration<Config>).init(defaultConfig, null)
}
val configDoc = resolveConfigDoc(registration.type())
throw AgentLaunchFailedException("Failed to init config, related path: $path, config definition: $configDoc")
@@ -184,18 +185,20 @@ object ConfigCenter : AutoCloseable {
val relativePath = toRelativeConfigPath(file) ?: return
val registration = registrations[relativePath] ?: return
try {
val config = loadConfig(file, registration)
if (config != null) {
(registration as ConfigRegistration<Config>).onReload(config)
val pair = loadConfig(file, registration)
if (pair != null) {
(registration as ConfigRegistration<Config>).onReload(pair.first, pair.second)
}
} catch (e: Exception) {
log.error("Config reload failed: {}", relativePath, e)
}
}
private fun loadConfig(file: Path, registration: ConfigRegistration<out Config>): Config? {
private fun loadConfig(file: Path, registration: ConfigRegistration<out Config>): Pair<Config, JSONObject>? {
return try {
JSON.parseObject(Files.readString(file, StandardCharsets.UTF_8), registration.type()) as Config
val json = JSON.parseObject(Files.readString(file, StandardCharsets.UTF_8))
val config = json.toJavaObject(registration.type())
config to json
} catch (e: Exception) {
log.error("Config reload failed: {}", file, e)
null
@@ -247,8 +250,8 @@ interface Configurable {
interface ConfigRegistration<T : Config> {
fun type(): Class<T>
fun init(config: T)
fun onReload(config: T) {}
fun init(config: T, json: JSONObject?)
fun onReload(config: T, json: JSONObject?) {}
fun defaultConfig(): T?
}