fix
This commit is contained in:
@@ -54,11 +54,11 @@ import kotlin.time.TimeSource
|
||||
* вмердживаются `patch` (provider → upstream → model).
|
||||
*
|
||||
* Env (только эти):
|
||||
* - PORT (default 8100)
|
||||
* - CONFIG_PATH путь к YAML; default `config.yaml` в каталоге проекта (CWD)
|
||||
*
|
||||
* Порт/интерфейс биндинга задаются блоком `server` в YAML (см. CONFIG.md).
|
||||
*/
|
||||
fun main() {
|
||||
val port = getEnv("PORT")?.toIntOrNull() ?: 8100
|
||||
val path = getEnv("CONFIG_PATH") ?: "config.yaml"
|
||||
val root = Yaml.decodeYamlFromString(readConfigText(path))
|
||||
val config = parseConfig(root)
|
||||
@@ -78,15 +78,23 @@ fun main() {
|
||||
}
|
||||
}
|
||||
|
||||
val active = config.upstreams.associate { up -> up.id to UpstreamCounter(up.max_concurrency ?: Int.MAX_VALUE) }
|
||||
val active = config.upstreams.associate { up ->
|
||||
up.id to UpstreamCounter(effectiveConcurrencyLimit(up, providersById[up.provider]))
|
||||
}
|
||||
|
||||
log.info {
|
||||
"[llm-proxy] загружено: providers=${config.providers.size}, " +
|
||||
"upstreams=${config.upstreams.size}, models=${config.models.size} (config=$path)"
|
||||
}
|
||||
log.info {
|
||||
"[llm-proxy] server: bind host=${config.server.host} port=${config.server.port}"
|
||||
}
|
||||
log.info {
|
||||
"[llm-proxy] upstream: request_timeout=${UPSTREAM_REQUEST_TIMEOUT_MS}ms"
|
||||
}
|
||||
|
||||
val http = createHttpClient()
|
||||
startServer(port) {
|
||||
startServer(config.server.host, config.server.port) {
|
||||
proxyModule(config, providersById, upstreamsById, active, http)
|
||||
}
|
||||
}
|
||||
@@ -308,12 +316,16 @@ internal fun merge(base: JsonObject, patch: JsonObject): JsonObject {
|
||||
internal fun resolveEnv(s: String): String =
|
||||
"""\$\{([^}]+)\}""".toRegex().replace(s) { m -> getEnv(m.groupValues[1]) ?: "" }
|
||||
|
||||
/** Атомарно занять слот у апстрима (по max_concurrency); false, если все заняты. */
|
||||
internal fun tryClaim(up: UpstreamConf, active: Map<String, UpstreamCounter>): Boolean {
|
||||
val counter = active.getValue(up.id)
|
||||
val limit = up.max_concurrency ?: Int.MAX_VALUE
|
||||
return counter.tryClaim(limit)
|
||||
}
|
||||
/** Атомарно занять слот у апстрима (по эффективному лимиту); false, если все заняты. */
|
||||
internal fun tryClaim(up: UpstreamConf, active: Map<String, UpstreamCounter>): Boolean =
|
||||
active.getValue(up.id).tryClaim()
|
||||
|
||||
/**
|
||||
* Эффективный лимит конкурентности апстрима: значение у апстрима (модели), если
|
||||
* задано; иначе у провайдера; иначе безлимит.
|
||||
*/
|
||||
internal fun effectiveConcurrencyLimit(up: UpstreamConf, provider: ProviderConf?): Int =
|
||||
up.max_concurrency ?: provider?.max_concurrency ?: Int.MAX_VALUE
|
||||
|
||||
/** Освободить слот апстрима (в finally по завершении проксирования). */
|
||||
internal fun release(up: UpstreamConf, active: Map<String, UpstreamCounter>) {
|
||||
@@ -325,10 +337,10 @@ class UpstreamCounter(private val limit: Int, initial: Int = 0) {
|
||||
@Volatile
|
||||
private var count: Int = initial
|
||||
|
||||
fun tryClaim(max: Int): Boolean {
|
||||
fun tryClaim(): Boolean {
|
||||
if (!lock.tryLock()) return false
|
||||
try {
|
||||
return if (count >= max) false else {
|
||||
return if (count >= limit) false else {
|
||||
count++
|
||||
true
|
||||
}
|
||||
@@ -483,6 +495,7 @@ data class ProviderConf(
|
||||
val id: String,
|
||||
val url: String,
|
||||
val key: String = "",
|
||||
val max_concurrency: Int? = null,
|
||||
val patch: JsonObject? = null,
|
||||
)
|
||||
|
||||
@@ -500,7 +513,13 @@ data class ModelConf(
|
||||
val patch: JsonObject? = null,
|
||||
)
|
||||
|
||||
data class ServerConf(
|
||||
val host: String = "0.0.0.0",
|
||||
val port: Int = 8100,
|
||||
)
|
||||
|
||||
data class Config(
|
||||
val server: ServerConf,
|
||||
val providers: List<ProviderConf>,
|
||||
val upstreams: List<UpstreamConf>,
|
||||
val models: List<ModelConf>,
|
||||
@@ -521,12 +540,23 @@ internal fun parseConfig(root: YamlElement): Config {
|
||||
return (v as? YamlList)?.map { it } ?: emptyList()
|
||||
}
|
||||
|
||||
val serverMap = (top["server"] as? YamlMap)?.toMap()
|
||||
val server = if (serverMap != null) {
|
||||
ServerConf(
|
||||
host = serverMap.strOrNull("host") ?: "0.0.0.0",
|
||||
port = serverMap.strOrNull("port")?.toIntOrNull() ?: 8100,
|
||||
)
|
||||
} else {
|
||||
ServerConf()
|
||||
}
|
||||
|
||||
val providers = list("providers").map { entry ->
|
||||
val m = (entry as YamlMap).toMap()
|
||||
ProviderConf(
|
||||
id = m.str("id"),
|
||||
url = m.str("url"),
|
||||
key = m.strOrNull("key") ?: "",
|
||||
max_concurrency = m.strOrNull("max_concurrency")?.toIntOrNull(),
|
||||
patch = m.yamlMapOrNull("patch")?.let { yamlToJson(it) as JsonObject },
|
||||
)
|
||||
}
|
||||
@@ -551,7 +581,7 @@ internal fun parseConfig(root: YamlElement): Config {
|
||||
)
|
||||
}
|
||||
|
||||
return Config(providers, upstreams, models)
|
||||
return Config(server, providers, upstreams, models)
|
||||
}
|
||||
|
||||
/** YamlMap -> Map<String, YamlElement> (ключи YAML — строковые скаляры). */
|
||||
|
||||
Reference in New Issue
Block a user