use std::sync::Arc;
use async_trait::async_trait;
use serde_json::json;
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
use super::DynModelAdapter;
use crate::{
adapter::{
ModelAdapter, ModelError, ModelRequestContext, ModelRequestParameters,
ModelResponseEventStream,
},
message::{ModelMessage, ModelResponse},
profile::ModelProfile,
settings::ModelSettings,
stream::ModelResponseStreamEvent,
};
pub struct ConcurrencyLimitedModel {
inner: DynModelAdapter,
semaphore: Arc<Semaphore>,
max_concurrency: usize,
}
impl ConcurrencyLimitedModel {
#[must_use]
pub fn new(inner: DynModelAdapter, max_concurrency: usize) -> Self {
let permits = max_concurrency.max(1);
Self {
inner,
semaphore: Arc::new(Semaphore::new(permits)),
max_concurrency: permits,
}
}
#[must_use]
pub fn with_shared_semaphore(inner: DynModelAdapter, semaphore: Arc<Semaphore>) -> Self {
Self {
inner,
max_concurrency: semaphore.available_permits().max(1),
semaphore,
}
}
#[must_use]
pub const fn max_concurrency(&self) -> usize {
self.max_concurrency
}
async fn acquire(&self) -> Result<OwnedSemaphorePermit, ModelError> {
self.semaphore
.clone()
.acquire_owned()
.await
.map_err(|error| {
ModelError::Transport(format!("model concurrency limiter closed: {error}"))
})
}
}
#[async_trait]
impl ModelAdapter for ConcurrencyLimitedModel {
fn model_name(&self) -> &str {
self.inner.model_name()
}
fn provider_name(&self) -> Option<&str> {
self.inner.provider_name()
}
fn profile(&self) -> &ModelProfile {
self.inner.profile()
}
fn default_settings(&self) -> Option<&ModelSettings> {
self.inner.default_settings()
}
async fn request(
&self,
messages: Vec<ModelMessage>,
settings: Option<ModelSettings>,
params: ModelRequestParameters,
mut context: ModelRequestContext,
) -> Result<ModelResponse, ModelError> {
let _permit = self.acquire().await?;
annotate_limiter(&mut context, self.max_concurrency);
self.inner
.request(messages, settings, params, context)
.await
}
async fn request_stream(
&self,
messages: Vec<ModelMessage>,
settings: Option<ModelSettings>,
params: ModelRequestParameters,
mut context: ModelRequestContext,
) -> Result<Vec<ModelResponseStreamEvent>, ModelError> {
let _permit = self.acquire().await?;
annotate_limiter(&mut context, self.max_concurrency);
self.inner
.request_stream(messages, settings, params, context)
.await
}
async fn request_stream_incremental(
&self,
messages: Vec<ModelMessage>,
settings: Option<ModelSettings>,
params: ModelRequestParameters,
mut context: ModelRequestContext,
) -> Result<ModelResponseEventStream, ModelError> {
let permit = self.acquire().await?;
annotate_limiter(&mut context, self.max_concurrency);
let events = self
.inner
.request_stream(messages, settings, params, context)
.await?;
let (sender, receiver) = tokio::sync::mpsc::channel(events.len().max(1));
tokio::spawn(async move {
let _permit = permit;
for event in events {
if sender.send(Ok(event)).await.is_err() {
return;
}
}
});
Ok(ModelResponseEventStream::new(receiver))
}
async fn count_tokens(
&self,
messages: &[ModelMessage],
settings: Option<&ModelSettings>,
params: &ModelRequestParameters,
) -> Result<starweaver_usage::Usage, ModelError> {
self.inner.count_tokens(messages, settings, params).await
}
}
fn annotate_limiter(context: &mut ModelRequestContext, max_concurrency: usize) {
context.llm_trace_metadata.insert(
"starweaver_model_wrapper".to_string(),
json!({
"kind": "concurrency_limited",
"max_concurrency": max_concurrency,
}),
);
}