migrate ai format conversion to responses adapters

This commit is contained in:
fawney19
2026-04-26 20:32:55 +08:00
parent e36fb8c07a
commit 5b914aa78c
183 changed files with 13539 additions and 3401 deletions

View File

@@ -39,7 +39,7 @@ impl MinimalCandidateSelectionReadRepository for InMemoryMinimalCandidateSelecti
&& row.key_is_active
&& row.model_is_active
&& row.model_is_available
&& row.endpoint_api_format.eq_ignore_ascii_case(api_format)
&& api_format_matches(&row.endpoint_api_format, api_format)
&& row.key_supports_api_format(api_format)
})
.cloned()
@@ -69,6 +69,18 @@ impl MinimalCandidateSelectionReadRepository for InMemoryMinimalCandidateSelecti
}
}
fn normalize_api_format(value: &str) -> String {
match value.trim().to_ascii_lowercase().as_str() {
"openai:cli" => "openai:responses".to_string(),
"openai:compact" => "openai:responses:compact".to_string(),
other => other.to_string(),
}
}
fn api_format_matches(left: &str, right: &str) -> bool {
normalize_api_format(left) == normalize_api_format(right)
}
#[cfg(test)]
mod tests {
use super::InMemoryMinimalCandidateSelectionReadRepository;

View File

@@ -1,6 +1,7 @@
use async_trait::async_trait;
use futures_util::{stream::TryStream, TryStreamExt};
use sqlx::{PgPool, Row};
use std::collections::BTreeSet;
use super::{
MinimalCandidateSelectionReadRepository, StoredMinimalCandidateSelectionRow,
@@ -226,13 +227,19 @@ impl SqlxMinimalCandidateSelectionReadRepository {
&self,
api_format: &str,
) -> Result<Vec<StoredMinimalCandidateSelectionRow>, DataLayerError> {
Self::collect_query_rows(
sqlx::query(LIST_FOR_EXACT_API_FORMAT_SQL)
.bind(api_format)
.fetch(&self.pool),
map_candidate_selection_row,
)
.await
let mut rows = Vec::new();
for api_format in api_format_aliases(api_format) {
rows.extend(
Self::collect_query_rows(
sqlx::query(LIST_FOR_EXACT_API_FORMAT_SQL)
.bind(api_format)
.fetch(&self.pool),
map_candidate_selection_row,
)
.await?,
);
}
Ok(dedupe_candidate_selection_rows(rows))
}
pub async fn list_for_exact_api_format_and_global_model(
@@ -240,17 +247,48 @@ impl SqlxMinimalCandidateSelectionReadRepository {
api_format: &str,
global_model_name: &str,
) -> Result<Vec<StoredMinimalCandidateSelectionRow>, DataLayerError> {
Self::collect_query_rows(
sqlx::query(LIST_FOR_EXACT_API_FORMAT_AND_GLOBAL_MODEL_SQL)
.bind(api_format)
.bind(global_model_name)
.fetch(&self.pool),
map_candidate_selection_row,
)
.await
let mut rows = Vec::new();
for api_format in api_format_aliases(api_format) {
rows.extend(
Self::collect_query_rows(
sqlx::query(LIST_FOR_EXACT_API_FORMAT_AND_GLOBAL_MODEL_SQL)
.bind(api_format)
.bind(global_model_name)
.fetch(&self.pool),
map_candidate_selection_row,
)
.await?,
);
}
Ok(dedupe_candidate_selection_rows(rows))
}
}
fn api_format_aliases(api_format: &str) -> Vec<&str> {
match api_format.trim().to_ascii_lowercase().as_str() {
"openai:responses" => vec!["openai:responses", "openai:cli"],
"openai:cli" => vec!["openai:responses", "openai:cli"],
"openai:responses:compact" => vec!["openai:responses:compact", "openai:compact"],
"openai:compact" => vec!["openai:responses:compact", "openai:compact"],
_ => vec![api_format],
}
}
fn dedupe_candidate_selection_rows(
rows: Vec<StoredMinimalCandidateSelectionRow>,
) -> Vec<StoredMinimalCandidateSelectionRow> {
let mut seen = BTreeSet::new();
rows.into_iter()
.filter(|row| {
seen.insert((
row.endpoint_id.clone(),
row.key_id.clone(),
row.model_id.clone(),
))
})
.collect()
}
#[async_trait]
impl MinimalCandidateSelectionReadRepository for SqlxMinimalCandidateSelectionReadRepository {
async fn list_for_exact_api_format(

View File

@@ -595,7 +595,7 @@ mod tests {
latency_ms: Some(25),
concurrent_requests: Some(2),
extra_data: Some(json!({
"provider_api_format": "openai:cli",
"provider_api_format": "openai:responses",
"provider_name": "updated",
})),
required_capabilities: None,
@@ -621,7 +621,7 @@ mod tests {
.extra_data
.as_ref()
.and_then(|value| value.get("provider_api_format")),
Some(&json!("openai:cli"))
Some(&json!("openai:responses"))
);
assert_eq!(
updated