2026-03-31 19:19:04 +08:00
use async_trait ::async_trait ;
2026-04-12 20:45:47 +08:00
use futures_util ::{ stream ::TryStream , TryStreamExt };
2026-03-31 19:19:04 +08:00
use serde_json ::Value ;
use sqlx ::{ postgres ::PgRow , PgPool , Postgres , QueryBuilder , Row };
2026-07-15 23:47:19 +08:00
use aether_data_contracts ::repository ::global_models ::{
2026-03-31 19:19:04 +08:00
AdminGlobalModelListQuery , AdminProviderModelListQuery , CreateAdminGlobalModelRecord ,
GlobalModelReadRepository , GlobalModelWriteRepository , PublicCatalogModelListQuery ,
PublicCatalogModelSearchQuery , PublicGlobalModelQuery , StoredAdminGlobalModel ,
StoredAdminGlobalModelPage , StoredAdminProviderModel , StoredProviderActiveGlobalModel ,
StoredProviderModelStats , StoredPublicCatalogModel , StoredPublicGlobalModel ,
StoredPublicGlobalModelPage , UpdateAdminGlobalModelRecord , UpsertAdminProviderModelRecord ,
};
2026-07-15 23:47:19 +08:00
use aether_data_contracts ::DataLayerError ;
use crate ::error ::SqlxResultExt ;
2026-03-31 19:19:04 +08:00
const LIST_PUBLIC_GLOBAL_MODELS_PREFIX : & str = r #"
SELECT
id,
name,
display_name,
is_active,
CAST(default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
default_tiered_pricing,
supported_capabilities,
config
FROM global_models
"# ;
const COUNT_PUBLIC_GLOBAL_MODELS_PREFIX : & str = r #"
SELECT COUNT(id) AS total
FROM global_models
"# ;
const LIST_PUBLIC_CATALOG_MODELS_PREFIX : & str = r #"
SELECT
m.id,
m.provider_id,
p.name AS provider_name,
m.provider_model_name,
COALESCE(gm.name, m.provider_model_name) AS name,
COALESCE(NULLIF(gm.display_name, ''), m.provider_model_name) AS display_name,
CASE
WHEN gm.config IS NULL THEN NULL
ELSE gm.config->>'description'
END AS description,
CASE
WHEN gm.config IS NULL THEN NULL
ELSE gm.config->>'icon_url'
END AS icon_url,
COALESCE(
CAST((COALESCE(m.tiered_pricing, gm.default_tiered_pricing)->'tiers'->0->>'input_price_per_1m') AS DOUBLE PRECISION),
0.0
) AS input_price_per_1m,
COALESCE(
CAST((COALESCE(m.tiered_pricing, gm.default_tiered_pricing)->'tiers'->0->>'output_price_per_1m') AS DOUBLE PRECISION),
0.0
) AS output_price_per_1m,
CAST((COALESCE(m.tiered_pricing, gm.default_tiered_pricing)->'tiers'->0->>'cache_creation_price_per_1m') AS DOUBLE PRECISION) AS cache_creation_price_per_1m,
CAST((COALESCE(m.tiered_pricing, gm.default_tiered_pricing)->'tiers'->0->>'cache_read_price_per_1m') AS DOUBLE PRECISION) AS cache_read_price_per_1m,
COALESCE(m.supports_vision, CAST(gm.config->>'vision' AS BOOLEAN), FALSE) AS supports_vision,
COALESCE(m.supports_function_calling, CAST(gm.config->>'function_calling' AS BOOLEAN), FALSE) AS supports_function_calling,
COALESCE(m.supports_streaming, CAST(gm.config->>'streaming' AS BOOLEAN), TRUE) AS supports_streaming,
2026-05-03 17:32:41 +08:00
(
COALESCE(gm.supported_capabilities::jsonb @> '["embedding"]'::jsonb, FALSE)
OR LOWER(COALESCE(gm.config->>'embedding', 'false')) = 'true'
OR LOWER(COALESCE(gm.config->>'model_type', '')) = 'embedding'
OR LOWER(COALESCE(gm.config->>'type', '')) = 'embedding'
OR COALESCE(gm.config->'capabilities' @> '["embedding"]'::jsonb, FALSE)
OR COALESCE(gm.config->'supported_capabilities' @> '["embedding"]'::jsonb, FALSE)
OR COALESCE(gm.config->'api_formats' @> '["openai:embedding"]'::jsonb, FALSE)
OR COALESCE(gm.config->'api_formats' @> '["jina:embedding"]'::jsonb, FALSE)
OR COALESCE(gm.config->'api_formats' @> '["gemini:embedding"]'::jsonb, FALSE)
OR COALESCE(gm.config->'api_formats' @> '["doubao:embedding"]'::jsonb, FALSE)
2026-05-28 16:05:36 +08:00
OR COALESCE(gm.config->'api_formats' @> '["aliyun:multimodal_embedding"]'::jsonb, FALSE)
2026-05-03 17:32:41 +08:00
OR LOWER(COALESCE(m.config->>'embedding', 'false')) = 'true'
OR LOWER(COALESCE(m.config->>'model_type', '')) = 'embedding'
OR LOWER(COALESCE(m.config->>'type', '')) = 'embedding'
OR COALESCE(m.config::jsonb->'capabilities' @> '["embedding"]'::jsonb, FALSE)
OR COALESCE(m.config::jsonb->'supported_capabilities' @> '["embedding"]'::jsonb, FALSE)
OR COALESCE(m.config::jsonb->'api_formats' @> '["openai:embedding"]'::jsonb, FALSE)
OR COALESCE(m.config::jsonb->'api_formats' @> '["jina:embedding"]'::jsonb, FALSE)
OR COALESCE(m.config::jsonb->'api_formats' @> '["gemini:embedding"]'::jsonb, FALSE)
OR COALESCE(m.config::jsonb->'api_formats' @> '["doubao:embedding"]'::jsonb, FALSE)
2026-05-28 16:05:36 +08:00
OR COALESCE(m.config::jsonb->'api_formats' @> '["aliyun:multimodal_embedding"]'::jsonb, FALSE)
2026-05-03 17:32:41 +08:00
) AS supports_embedding,
2026-03-31 19:19:04 +08:00
m.is_active
FROM models m
JOIN providers p ON p.id = m.provider_id
LEFT JOIN global_models gm ON gm.id = m.global_model_id
"# ;
const LIST_PROVIDER_MODEL_STATS_PREFIX : & str = r #"
SELECT
provider_id,
COUNT(id) AS total_models,
COALESCE(SUM(CASE WHEN is_active = TRUE THEN 1 ELSE 0 END), 0) AS active_models
FROM models
WHERE provider_id IN (
"# ;
const LIST_ADMIN_PROVIDER_MODELS_PREFIX : & str = r #"
SELECT
m.id,
m.provider_id,
m.global_model_id,
m.provider_model_name,
m.provider_model_mappings,
CAST(m.price_per_request AS DOUBLE PRECISION) AS price_per_request,
m.tiered_pricing,
m.supports_vision,
m.supports_function_calling,
m.supports_streaming,
m.supports_extended_thinking,
m.supports_image_generation,
m.is_active,
COALESCE(m.is_available, TRUE) AS is_available,
m.config,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM m.created_at)::bigint AS created_at_unix_ms,
2026-03-31 19:19:04 +08:00
EXTRACT(EPOCH FROM m.updated_at)::bigint AS updated_at_unix_secs,
gm.name AS global_model_name,
gm.display_name AS global_model_display_name,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS global_model_default_price_per_request,
gm.default_tiered_pricing AS global_model_default_tiered_pricing,
2026-05-03 17:32:41 +08:00
gm.supported_capabilities AS global_model_supported_capabilities,
2026-03-31 19:19:04 +08:00
gm.config AS global_model_config
FROM models m
LEFT JOIN global_models gm ON gm.id = m.global_model_id
"# ;
const LIST_ADMIN_GLOBAL_MODELS_PREFIX : & str = r #"
SELECT
2026-04-09 13:51:50 +08:00
gm.id,
gm.name,
COALESCE(NULLIF(gm.display_name, ''), gm.name) AS display_name,
gm.is_active,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
gm.default_tiered_pricing,
gm.supported_capabilities,
gm.config,
COALESCE(gm_stats.provider_count, 0) AS provider_count,
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_ms,
2026-04-09 13:51:50 +08:00
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
FROM global_models gm
LEFT JOIN (
SELECT
m.global_model_id,
COUNT(DISTINCT m.provider_id)::bigint AS provider_count,
COUNT(
DISTINCT CASE
2026-04-11 02:55:02 +08:00
WHEN m.is_active = TRUE AND COALESCE(m.is_available, TRUE) = TRUE AND p.is_active = TRUE THEN m.provider_id
2026-04-09 13:51:50 +08:00
ELSE NULL
END
)::bigint AS active_provider_count
FROM models m
JOIN providers p ON p.id = m.provider_id
GROUP BY m.global_model_id
) gm_stats ON gm_stats.global_model_id = gm.id
2026-03-31 19:19:04 +08:00
"# ;
const COUNT_ADMIN_GLOBAL_MODELS_PREFIX : & str = r #"
SELECT COUNT(id) AS total
2026-04-09 13:51:50 +08:00
FROM global_models gm
2026-03-31 19:19:04 +08:00
"# ;
const LIST_ACTIVE_GLOBAL_MODEL_IDS_BY_PROVIDER_IDS_PREFIX : & str = r #"
SELECT DISTINCT
provider_id,
global_model_id
FROM models
WHERE provider_id IN (
"# ;
#[derive(Debug, Clone)]
pub struct SqlxGlobalModelReadRepository {
pool : PgPool ,
}
impl SqlxGlobalModelReadRepository {
pub fn new ( pool : PgPool ) -> Self {
Self { pool }
}
pub async fn list_public_models (
& self ,
query : & PublicGlobalModelQuery ,
) -> Result < StoredPublicGlobalModelPage , DataLayerError > {
let mut count_builder = QueryBuilder ::< Postgres > ::new ( COUNT_PUBLIC_GLOBAL_MODELS_PREFIX );
apply_public_model_filters ( & mut count_builder , query );
2026-04-07 02:50:19 +08:00
let count_row = count_builder
. build ()
. fetch_one ( & self . pool )
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
let total = count_row
. try_get ::< i64 , _ > ( "total" )
2026-04-07 02:50:19 +08:00
. map ( | value | value . max ( 0 ) as usize )
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
let mut list_builder = QueryBuilder ::< Postgres > ::new ( LIST_PUBLIC_GLOBAL_MODELS_PREFIX );
apply_public_model_filters ( & mut list_builder , query );
list_builder
. push ( " ORDER BY name ASC OFFSET " )
. push_bind ( query . offset as i64 )
. push ( " LIMIT " )
. push_bind ( query . limit as i64 );
2026-04-12 20:45:47 +08:00
let query = list_builder . build ();
let items = collect_query_rows ( query . fetch ( & self . pool ), map_row ). await ? ;
2026-03-31 19:19:04 +08:00
Ok ( StoredPublicGlobalModelPage { items , total })
}
pub async fn list_provider_model_stats (
& self ,
provider_ids : & [ String ],
) -> Result < Vec < StoredProviderModelStats > , DataLayerError > {
if provider_ids . is_empty () {
return Ok ( Vec ::new ());
}
2026-04-12 20:45:47 +08:00
let mut builder = build_provider_id_list_query (
2026-03-31 19:19:04 +08:00
LIST_PROVIDER_MODEL_STATS_PREFIX ,
provider_ids ,
") \n GROUP BY provider_id \n ORDER BY provider_id ASC" ,
2026-04-12 20:45:47 +08:00
);
let query = builder . build ();
collect_query_rows ( query . fetch ( & self . pool ), map_provider_model_stats_row ). await
2026-03-31 19:19:04 +08:00
}
pub async fn list_active_global_model_ids_by_provider_ids (
& self ,
provider_ids : & [ String ],
) -> Result < Vec < StoredProviderActiveGlobalModel > , DataLayerError > {
if provider_ids . is_empty () {
return Ok ( Vec ::new ());
}
2026-04-12 20:45:47 +08:00
let mut builder = build_provider_id_list_query (
2026-03-31 19:19:04 +08:00
LIST_ACTIVE_GLOBAL_MODEL_IDS_BY_PROVIDER_IDS_PREFIX ,
provider_ids ,
") \n AND is_active = TRUE \n AND global_model_id IS NOT NULL \n ORDER BY provider_id ASC, global_model_id ASC" ,
2026-04-12 20:45:47 +08:00
);
let query = builder . build ();
collect_query_rows (
query . fetch ( & self . pool ),
map_provider_active_global_model_row ,
2026-03-31 19:19:04 +08:00
)
2026-04-07 02:50:19 +08:00
. await
2026-03-31 19:19:04 +08:00
}
pub async fn list_admin_provider_models (
& self ,
query : & AdminProviderModelListQuery ,
) -> Result < Vec < StoredAdminProviderModel > , DataLayerError > {
let mut builder = QueryBuilder ::< Postgres > ::new ( LIST_ADMIN_PROVIDER_MODELS_PREFIX );
builder
. push ( " WHERE m.provider_id = " )
. push_bind ( query . provider_id . trim (). to_string ());
if let Some ( is_active ) = query . is_active {
builder . push ( " AND m.is_active = " ). push_bind ( is_active );
}
builder
. push ( " ORDER BY m.created_at DESC, m.id ASC OFFSET " )
. push_bind ( query . offset as i64 )
. push ( " LIMIT " )
. push_bind ( query . limit as i64 );
2026-04-12 20:45:47 +08:00
let query = builder . build ();
collect_query_rows ( query . fetch ( & self . pool ), map_admin_provider_model_row ). await
2026-03-31 19:19:04 +08:00
}
pub async fn list_admin_global_models (
& self ,
query : & AdminGlobalModelListQuery ,
) -> Result < StoredAdminGlobalModelPage , DataLayerError > {
let mut count_builder = QueryBuilder ::< Postgres > ::new ( COUNT_ADMIN_GLOBAL_MODELS_PREFIX );
apply_admin_global_model_filters ( & mut count_builder , query );
2026-04-07 02:50:19 +08:00
let count_row = count_builder
. build ()
. fetch_one ( & self . pool )
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
let total = count_row
. try_get ::< i64 , _ > ( "total" )
2026-04-07 02:50:19 +08:00
. map ( | value | value . max ( 0 ) as usize )
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
let mut list_builder = QueryBuilder ::< Postgres > ::new ( LIST_ADMIN_GLOBAL_MODELS_PREFIX );
apply_admin_global_model_filters ( & mut list_builder , query );
list_builder
. push ( " ORDER BY name ASC OFFSET " )
. push_bind ( query . offset as i64 )
. push ( " LIMIT " )
. push_bind ( query . limit as i64 );
2026-04-12 20:45:47 +08:00
let query = list_builder . build ();
let items = collect_query_rows ( query . fetch ( & self . pool ), map_admin_global_model_row ). await ? ;
2026-03-31 19:19:04 +08:00
Ok ( StoredAdminGlobalModelPage { items , total })
}
pub async fn get_admin_provider_model (
& self ,
provider_id : & str ,
model_id : & str ,
) -> Result < Option < StoredAdminProviderModel > , DataLayerError > {
let row = sqlx ::query (
r #"
SELECT
m.id,
m.provider_id,
m.global_model_id,
m.provider_model_name,
m.provider_model_mappings,
CAST(m.price_per_request AS DOUBLE PRECISION) AS price_per_request,
m.tiered_pricing,
m.supports_vision,
m.supports_function_calling,
m.supports_streaming,
m.supports_extended_thinking,
m.supports_image_generation,
m.is_active,
COALESCE(m.is_available, TRUE) AS is_available,
m.config,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM m.created_at)::bigint AS created_at_unix_ms,
2026-03-31 19:19:04 +08:00
EXTRACT(EPOCH FROM m.updated_at)::bigint AS updated_at_unix_secs,
gm.name AS global_model_name,
gm.display_name AS global_model_display_name,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS global_model_default_price_per_request,
gm.default_tiered_pricing AS global_model_default_tiered_pricing,
2026-05-03 17:32:41 +08:00
gm.supported_capabilities AS global_model_supported_capabilities,
2026-03-31 19:19:04 +08:00
gm.config AS global_model_config
FROM models m
LEFT JOIN global_models gm ON gm.id = m.global_model_id
WHERE m.provider_id = $1
AND m.id = $2
LIMIT 1
"# ,
)
. bind ( provider_id )
. bind ( model_id )
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
row . as_ref (). map ( map_admin_provider_model_row ). transpose ()
}
pub async fn list_admin_provider_available_source_models (
& self ,
provider_id : & str ,
) -> Result < Vec < StoredAdminProviderModel > , DataLayerError > {
2026-04-12 20:45:47 +08:00
collect_query_rows (
sqlx ::query (
r #"
2026-03-31 19:19:04 +08:00
SELECT
m.id,
m.provider_id,
m.global_model_id,
m.provider_model_name,
m.provider_model_mappings,
CAST(m.price_per_request AS DOUBLE PRECISION) AS price_per_request,
m.tiered_pricing,
m.supports_vision,
m.supports_function_calling,
m.supports_streaming,
m.supports_extended_thinking,
m.supports_image_generation,
m.is_active,
COALESCE(m.is_available, TRUE) AS is_available,
m.config,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM m.created_at)::bigint AS created_at_unix_ms,
2026-03-31 19:19:04 +08:00
EXTRACT(EPOCH FROM m.updated_at)::bigint AS updated_at_unix_secs,
gm.name AS global_model_name,
gm.display_name AS global_model_display_name,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS global_model_default_price_per_request,
gm.default_tiered_pricing AS global_model_default_tiered_pricing,
2026-05-03 17:32:41 +08:00
gm.supported_capabilities AS global_model_supported_capabilities,
2026-03-31 19:19:04 +08:00
gm.config AS global_model_config
FROM models m
JOIN global_models gm ON gm.id = m.global_model_id
WHERE m.provider_id = $1
AND m.is_active = TRUE
AND gm.is_active = TRUE
ORDER BY gm.name ASC, m.created_at DESC, m.id ASC
"# ,
2026-04-12 20:45:47 +08:00
)
. bind ( provider_id )
. fetch ( & self . pool ),
map_admin_provider_model_row ,
2026-03-31 19:19:04 +08:00
)
2026-04-07 02:50:19 +08:00
. await
2026-03-31 19:19:04 +08:00
}
pub async fn get_admin_global_model_by_id (
& self ,
global_model_id : & str ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
let row = sqlx ::query (
r #"
SELECT
2026-04-09 13:51:50 +08:00
gm.id,
gm.name,
COALESCE(NULLIF(gm.display_name, ''), gm.name) AS display_name,
gm.is_active,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
gm.default_tiered_pricing,
gm.supported_capabilities,
gm.config,
COALESCE(gm_stats.provider_count, 0) AS provider_count,
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_ms,
2026-04-09 13:51:50 +08:00
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
FROM global_models gm
LEFT JOIN (
SELECT
m.global_model_id,
COUNT(DISTINCT m.provider_id)::bigint AS provider_count,
COUNT(
DISTINCT CASE
2026-04-11 02:55:02 +08:00
WHEN m.is_active = TRUE AND COALESCE(m.is_available, TRUE) = TRUE AND p.is_active = TRUE THEN m.provider_id
2026-04-09 13:51:50 +08:00
ELSE NULL
END
)::bigint AS active_provider_count
FROM models m
JOIN providers p ON p.id = m.provider_id
GROUP BY m.global_model_id
) gm_stats ON gm_stats.global_model_id = gm.id
WHERE gm.id = $1
2026-03-31 19:19:04 +08:00
LIMIT 1
"# ,
)
. bind ( global_model_id )
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
row . as_ref (). map ( map_admin_global_model_row ). transpose ()
}
pub async fn get_admin_global_model_by_name (
& self ,
model_name : & str ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
let row = sqlx ::query (
r #"
SELECT
2026-04-09 13:51:50 +08:00
gm.id,
gm.name,
COALESCE(NULLIF(gm.display_name, ''), gm.name) AS display_name,
gm.is_active,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
gm.default_tiered_pricing,
gm.supported_capabilities,
gm.config,
COALESCE(gm_stats.provider_count, 0) AS provider_count,
COALESCE(gm_stats.active_provider_count, 0) AS active_provider_count,
COALESCE(gm.usage_count, 0)::bigint AS usage_count,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM gm.created_at)::bigint AS created_at_unix_ms,
2026-04-09 13:51:50 +08:00
EXTRACT(EPOCH FROM gm.updated_at)::bigint AS updated_at_unix_secs
FROM global_models gm
LEFT JOIN (
SELECT
m.global_model_id,
COUNT(DISTINCT m.provider_id)::bigint AS provider_count,
COUNT(
DISTINCT CASE
2026-04-11 02:55:02 +08:00
WHEN m.is_active = TRUE AND COALESCE(m.is_available, TRUE) = TRUE AND p.is_active = TRUE THEN m.provider_id
2026-04-09 13:51:50 +08:00
ELSE NULL
END
)::bigint AS active_provider_count
FROM models m
JOIN providers p ON p.id = m.provider_id
GROUP BY m.global_model_id
) gm_stats ON gm_stats.global_model_id = gm.id
WHERE gm.name = $1
2026-03-31 19:19:04 +08:00
LIMIT 1
"# ,
)
. bind ( model_name )
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
row . as_ref (). map ( map_admin_global_model_row ). transpose ()
}
pub async fn list_admin_provider_models_by_global_model_id (
& self ,
global_model_id : & str ,
) -> Result < Vec < StoredAdminProviderModel > , DataLayerError > {
2026-04-12 20:45:47 +08:00
collect_query_rows (
sqlx ::query (
r #"
2026-03-31 19:19:04 +08:00
SELECT
m.id,
m.provider_id,
m.global_model_id,
m.provider_model_name,
m.provider_model_mappings,
CAST(m.price_per_request AS DOUBLE PRECISION) AS price_per_request,
m.tiered_pricing,
m.supports_vision,
m.supports_function_calling,
m.supports_streaming,
m.supports_extended_thinking,
m.supports_image_generation,
m.is_active,
COALESCE(m.is_available, TRUE) AS is_available,
m.config,
2026-04-10 01:46:14 +08:00
EXTRACT(EPOCH FROM m.created_at)::bigint AS created_at_unix_ms,
2026-03-31 19:19:04 +08:00
EXTRACT(EPOCH FROM m.updated_at)::bigint AS updated_at_unix_secs,
gm.name AS global_model_name,
gm.display_name AS global_model_display_name,
CAST(gm.default_price_per_request AS DOUBLE PRECISION) AS global_model_default_price_per_request,
gm.default_tiered_pricing AS global_model_default_tiered_pricing,
2026-05-03 17:32:41 +08:00
gm.supported_capabilities AS global_model_supported_capabilities,
2026-03-31 19:19:04 +08:00
gm.config AS global_model_config
FROM models m
LEFT JOIN global_models gm ON gm.id = m.global_model_id
WHERE m.global_model_id = $1
ORDER BY m.created_at DESC, m.id ASC
"# ,
2026-04-12 20:45:47 +08:00
)
. bind ( global_model_id )
. fetch ( & self . pool ),
map_admin_provider_model_row ,
2026-03-31 19:19:04 +08:00
)
2026-04-07 02:50:19 +08:00
. await
2026-03-31 19:19:04 +08:00
}
pub async fn create_admin_provider_model (
& self ,
record : & UpsertAdminProviderModelRecord ,
) -> Result < Option < StoredAdminProviderModel > , DataLayerError > {
let inserted = sqlx ::query (
r #"
INSERT INTO models (
id,
provider_id,
global_model_id,
provider_model_name,
provider_model_mappings,
price_per_request,
tiered_pricing,
supports_vision,
supports_function_calling,
supports_streaming,
supports_extended_thinking,
supports_image_generation,
is_active,
is_available,
config,
created_at,
updated_at
)
VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, NOW(), NOW()
)
RETURNING id
"# ,
)
. bind ( & record . id )
. bind ( & record . provider_id )
. bind ( & record . global_model_id )
. bind ( & record . provider_model_name )
. bind ( record . provider_model_mappings . clone ())
. bind ( record . price_per_request )
. bind ( record . tiered_pricing . clone ())
. bind ( record . supports_vision )
. bind ( record . supports_function_calling )
. bind ( record . supports_streaming )
. bind ( record . supports_extended_thinking )
. bind ( record . supports_image_generation )
. bind ( record . is_active )
. bind ( record . is_available )
. bind ( record . config . clone ())
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
if inserted . is_none () {
return Ok ( None );
}
self . get_admin_provider_model ( & record . provider_id , & record . id )
. await
}
pub async fn update_admin_provider_model (
& self ,
record : & UpsertAdminProviderModelRecord ,
) -> Result < Option < StoredAdminProviderModel > , DataLayerError > {
let updated = sqlx ::query (
r #"
UPDATE models
SET
global_model_id = $3,
provider_model_name = $4,
provider_model_mappings = $5,
price_per_request = $6,
tiered_pricing = $7,
supports_vision = $8,
supports_function_calling = $9,
supports_streaming = $10,
supports_extended_thinking = $11,
supports_image_generation = $12,
is_active = $13,
is_available = $14,
config = $15,
updated_at = NOW()
WHERE id = $1
AND provider_id = $2
RETURNING id
"# ,
)
. bind ( & record . id )
. bind ( & record . provider_id )
. bind ( & record . global_model_id )
. bind ( & record . provider_model_name )
. bind ( record . provider_model_mappings . clone ())
. bind ( record . price_per_request )
. bind ( record . tiered_pricing . clone ())
. bind ( record . supports_vision )
. bind ( record . supports_function_calling )
. bind ( record . supports_streaming )
. bind ( record . supports_extended_thinking )
. bind ( record . supports_image_generation )
. bind ( record . is_active )
. bind ( record . is_available )
. bind ( record . config . clone ())
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
if updated . is_none () {
return Ok ( None );
}
self . get_admin_provider_model ( & record . provider_id , & record . id )
. await
}
pub async fn delete_admin_provider_model (
& self ,
provider_id : & str ,
model_id : & str ,
) -> Result < bool , DataLayerError > {
let deleted = sqlx ::query (
r #"
DELETE FROM models
WHERE provider_id = $1
AND id = $2
RETURNING id
"# ,
)
. bind ( provider_id )
. bind ( model_id )
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
Ok ( deleted . is_some ())
}
pub async fn create_admin_global_model (
& self ,
record : & CreateAdminGlobalModelRecord ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
2026-05-25 15:23:02 +08:00
let usage_count =
optional_admin_global_model_usage_count_i64 ( record . usage_count ) ? . unwrap_or_default ();
2026-03-31 19:19:04 +08:00
let inserted = sqlx ::query (
r #"
INSERT INTO global_models (
id,
name,
display_name,
is_active,
default_price_per_request,
default_tiered_pricing,
supported_capabilities,
2026-04-17 01:09:14 +08:00
usage_count,
2026-03-31 19:19:04 +08:00
config,
created_at,
updated_at
)
2026-04-17 01:09:14 +08:00
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), NOW())
2026-03-31 19:19:04 +08:00
RETURNING id
"# ,
)
. bind ( & record . id )
. bind ( & record . name )
. bind ( & record . display_name )
. bind ( record . is_active )
. bind ( record . default_price_per_request )
. bind ( record . default_tiered_pricing . clone ())
. bind ( record . supported_capabilities . clone ())
2026-05-25 15:23:02 +08:00
. bind ( usage_count )
2026-03-31 19:19:04 +08:00
. bind ( record . config . clone ())
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
if inserted . is_none () {
return Ok ( None );
}
self . get_admin_global_model_by_id ( & record . id ). await
}
pub async fn update_admin_global_model (
& self ,
record : & UpdateAdminGlobalModelRecord ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
2026-05-25 15:23:02 +08:00
let usage_count = optional_admin_global_model_usage_count_i64 ( record . usage_count ) ? ;
2026-03-31 19:19:04 +08:00
let updated = sqlx ::query (
r #"
UPDATE global_models
SET
display_name = $2,
is_active = $3,
default_price_per_request = $4,
default_tiered_pricing = $5,
supported_capabilities = $6,
config = $7,
2026-05-25 15:23:02 +08:00
usage_count = COALESCE($8, usage_count),
2026-03-31 19:19:04 +08:00
updated_at = NOW()
WHERE id = $1
RETURNING id
"# ,
)
. bind ( & record . id )
. bind ( & record . display_name )
. bind ( record . is_active )
. bind ( record . default_price_per_request )
. bind ( record . default_tiered_pricing . clone ())
. bind ( record . supported_capabilities . clone ())
. bind ( record . config . clone ())
2026-05-25 15:23:02 +08:00
. bind ( usage_count )
2026-03-31 19:19:04 +08:00
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
if updated . is_none () {
return Ok ( None );
}
self . get_admin_global_model_by_id ( & record . id ). await
}
pub async fn delete_admin_global_model (
& self ,
global_model_id : & str ,
) -> Result < bool , DataLayerError > {
2026-04-17 12:57:06 +08:00
let mut tx = self . pool . begin (). await . map_postgres_err () ? ;
sqlx ::query (
r #"
DELETE FROM models
WHERE global_model_id = $1
"# ,
)
. bind ( global_model_id )
. execute ( & mut * tx )
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
let deleted = sqlx ::query (
r #"
DELETE FROM global_models
WHERE id = $1
RETURNING id
"# ,
)
. bind ( global_model_id )
2026-04-17 12:57:06 +08:00
. fetch_optional ( & mut * tx )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
2026-04-17 12:57:06 +08:00
tx . commit (). await . map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
Ok ( deleted . is_some ())
}
}
#[async_trait]
impl GlobalModelReadRepository for SqlxGlobalModelReadRepository {
async fn list_public_models (
& self ,
query : & PublicGlobalModelQuery ,
) -> Result < StoredPublicGlobalModelPage , DataLayerError > {
Self ::list_public_models ( self , query ). await
}
async fn get_public_model_by_name (
& self ,
model_name : & str ,
) -> Result < Option < StoredPublicGlobalModel > , DataLayerError > {
let row = sqlx ::query (
r #"
SELECT
id,
name,
display_name,
is_active,
CAST(default_price_per_request AS DOUBLE PRECISION) AS default_price_per_request,
default_tiered_pricing,
supported_capabilities,
config
FROM global_models
WHERE name = $1 AND is_active = TRUE
LIMIT 1
"# ,
)
. bind ( model_name )
. fetch_optional ( & self . pool )
2026-04-07 02:50:19 +08:00
. await
. map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
row . as_ref (). map ( map_row ). transpose ()
}
async fn list_public_catalog_models (
& self ,
query : & PublicCatalogModelListQuery ,
) -> Result < Vec < StoredPublicCatalogModel > , DataLayerError > {
let mut builder = QueryBuilder ::< Postgres > ::new ( LIST_PUBLIC_CATALOG_MODELS_PREFIX );
apply_public_catalog_model_filters ( & mut builder , query . provider_id . as_deref (), None );
builder
. push ( " ORDER BY p.provider_priority ASC, p.name ASC, COALESCE(gm.name, m.provider_model_name) ASC, m.id ASC OFFSET " )
. push_bind ( query . offset as i64 )
. push ( " LIMIT " )
. push_bind ( query . limit as i64 );
2026-04-12 20:45:47 +08:00
let query = builder . build ();
collect_query_rows ( query . fetch ( & self . pool ), map_public_catalog_model_row ). await
2026-03-31 19:19:04 +08:00
}
async fn search_public_catalog_models (
& self ,
query : & PublicCatalogModelSearchQuery ,
) -> Result < Vec < StoredPublicCatalogModel > , DataLayerError > {
let mut builder = QueryBuilder ::< Postgres > ::new ( LIST_PUBLIC_CATALOG_MODELS_PREFIX );
apply_public_catalog_model_filters (
& mut builder ,
query . provider_id . as_deref (),
Some ( query . search . as_str ()),
);
builder
. push ( " ORDER BY p.provider_priority ASC, p.name ASC, COALESCE(gm.name, m.provider_model_name) ASC, m.id ASC LIMIT " )
. push_bind ( query . limit as i64 );
2026-04-12 20:45:47 +08:00
let query = builder . build ();
collect_query_rows ( query . fetch ( & self . pool ), map_public_catalog_model_row ). await
2026-03-31 19:19:04 +08:00
}
async fn list_admin_global_models (
& self ,
query : & AdminGlobalModelListQuery ,
) -> Result < StoredAdminGlobalModelPage , DataLayerError > {
Self ::list_admin_global_models ( self , query ). await
}
async fn list_admin_provider_models (
& self ,
query : & AdminProviderModelListQuery ,
) -> Result < Vec < StoredAdminProviderModel > , DataLayerError > {
Self ::list_admin_provider_models ( self , query ). await
}
async fn get_admin_provider_model (
& self ,
provider_id : & str ,
model_id : & str ,
) -> Result < Option < StoredAdminProviderModel > , DataLayerError > {
Self ::get_admin_provider_model ( self , provider_id , model_id ). await
}
async fn list_admin_provider_available_source_models (
& self ,
provider_id : & str ,
) -> Result < Vec < StoredAdminProviderModel > , DataLayerError > {
Self ::list_admin_provider_available_source_models ( self , provider_id ). await
}
async fn get_admin_global_model_by_id (
& self ,
global_model_id : & str ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
Self ::get_admin_global_model_by_id ( self , global_model_id ). await
}
async fn get_admin_global_model_by_name (
& self ,
model_name : & str ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
Self ::get_admin_global_model_by_name ( self , model_name ). await
}
async fn list_admin_provider_models_by_global_model_id (
& self ,
global_model_id : & str ,
) -> Result < Vec < StoredAdminProviderModel > , DataLayerError > {
Self ::list_admin_provider_models_by_global_model_id ( self , global_model_id ). await
}
async fn list_provider_model_stats (
& self ,
provider_ids : & [ String ],
) -> Result < Vec < StoredProviderModelStats > , DataLayerError > {
Self ::list_provider_model_stats ( self , provider_ids ). await
}
async fn list_active_global_model_ids_by_provider_ids (
& self ,
provider_ids : & [ String ],
) -> Result < Vec < StoredProviderActiveGlobalModel > , DataLayerError > {
Self ::list_active_global_model_ids_by_provider_ids ( self , provider_ids ). await
}
}
#[async_trait]
impl GlobalModelWriteRepository for SqlxGlobalModelReadRepository {
async fn create_admin_provider_model (
& self ,
record : & UpsertAdminProviderModelRecord ,
) -> Result < Option < StoredAdminProviderModel > , DataLayerError > {
Self ::create_admin_provider_model ( self , record ). await
}
async fn update_admin_provider_model (
& self ,
record : & UpsertAdminProviderModelRecord ,
) -> Result < Option < StoredAdminProviderModel > , DataLayerError > {
Self ::update_admin_provider_model ( self , record ). await
}
async fn delete_admin_provider_model (
& self ,
provider_id : & str ,
model_id : & str ,
) -> Result < bool , DataLayerError > {
Self ::delete_admin_provider_model ( self , provider_id , model_id ). await
}
async fn create_admin_global_model (
& self ,
record : & CreateAdminGlobalModelRecord ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
Self ::create_admin_global_model ( self , record ). await
}
async fn update_admin_global_model (
& self ,
record : & UpdateAdminGlobalModelRecord ,
) -> Result < Option < StoredAdminGlobalModel > , DataLayerError > {
Self ::update_admin_global_model ( self , record ). await
}
async fn delete_admin_global_model (
& self ,
global_model_id : & str ,
) -> Result < bool , DataLayerError > {
Self ::delete_admin_global_model ( self , global_model_id ). await
}
}
fn apply_public_model_filters (
builder : & mut QueryBuilder < '_ , Postgres > ,
query : & PublicGlobalModelQuery ,
) {
builder . push ( " WHERE " );
match query . is_active {
Some ( is_active ) => {
builder . push ( "is_active = " ). push_bind ( is_active );
}
None => {
builder . push ( "is_active = TRUE" );
}
}
if let Some ( search ) = query
. search
. as_deref ()
. map ( str ::trim )
. filter ( | value | ! value . is_empty ())
{
let pattern = format! ( "% {search} %" );
builder
. push ( " AND (name ILIKE " )
. push_bind ( pattern . clone ())
. push ( " OR display_name ILIKE " )
. push_bind ( pattern )
. push ( ")" );
}
}
fn apply_admin_global_model_filters (
builder : & mut QueryBuilder < '_ , Postgres > ,
query : & AdminGlobalModelListQuery ,
) {
builder . push ( " WHERE 1=1" );
if let Some ( is_active ) = query . is_active {
2026-04-09 13:51:50 +08:00
builder . push ( " AND gm.is_active = " ). push_bind ( is_active );
2026-03-31 19:19:04 +08:00
}
if let Some ( search ) = query
. search
. as_deref ()
. map ( str ::trim )
. filter ( | value | ! value . is_empty ())
{
let pattern = format! ( "% {search} %" );
builder
2026-04-09 13:51:50 +08:00
. push ( " AND (gm.name ILIKE " )
2026-03-31 19:19:04 +08:00
. push_bind ( pattern . clone ())
2026-04-09 13:51:50 +08:00
. push ( " OR gm.display_name ILIKE " )
2026-03-31 19:19:04 +08:00
. push_bind ( pattern )
. push ( ")" );
}
}
fn map_row ( row : & PgRow ) -> Result < StoredPublicGlobalModel , DataLayerError > {
2026-04-07 02:50:19 +08:00
let supported_capabilities : Option < Value > =
row . try_get ( "supported_capabilities" ). map_postgres_err () ? ;
2026-03-31 19:19:04 +08:00
StoredPublicGlobalModel ::new (
2026-04-07 02:50:19 +08:00
row . try_get ( "id" ). map_postgres_err () ? ,
row . try_get ( "name" ). map_postgres_err () ? ,
row . try_get ( "display_name" ). map_postgres_err () ? ,
row . try_get ( "is_active" ). map_postgres_err () ? ,
row . try_get ( "default_price_per_request" )
. map_postgres_err () ? ,
row . try_get ( "default_tiered_pricing" ). map_postgres_err () ? ,
2026-03-31 19:19:04 +08:00
supported_capabilities ,
2026-04-07 02:50:19 +08:00
row . try_get ( "config" ). map_postgres_err () ? ,
2026-03-31 19:19:04 +08:00
0 ,
)
}
2026-04-12 20:45:47 +08:00
async fn collect_query_rows < T , S > (
mut rows : S ,
map_row : fn ( & PgRow ) -> Result < T , DataLayerError > ,
) -> Result < Vec < T > , DataLayerError >
where
S : TryStream < Ok = PgRow , Error = sqlx ::Error > + Unpin ,
{
let mut items = Vec ::new ();
while let Some ( row ) = rows . try_next (). await . map_postgres_err () ? {
items . push ( map_row ( & row ) ? );
}
Ok ( items )
}
2026-03-31 19:19:04 +08:00
fn apply_public_catalog_model_filters (
builder : & mut QueryBuilder < '_ , Postgres > ,
provider_id : Option <& str > ,
search : Option <& str > ,
) {
2026-05-03 17:32:41 +08:00
builder . push ( " WHERE m.is_active = TRUE AND COALESCE(m.is_available, TRUE) = TRUE AND p.is_active = TRUE AND COALESCE(gm.is_active, TRUE) = TRUE" );
2026-03-31 19:19:04 +08:00
if let Some ( provider_id ) = provider_id . map ( str ::trim ). filter ( | value | ! value . is_empty ()) {
builder
. push ( " AND m.provider_id = " )
. push_bind ( provider_id . to_string ());
}
if let Some ( search ) = search . map ( str ::trim ). filter ( | value | ! value . is_empty ()) {
let pattern = format! ( "% {search} %" );
builder
. push ( " AND (m.provider_model_name ILIKE " )
. push_bind ( pattern . clone ())
. push ( " OR gm.name ILIKE " )
. push_bind ( pattern . clone ())
. push ( " OR gm.display_name ILIKE " )
. push_bind ( pattern )
. push ( ")" );
}
}
fn map_public_catalog_model_row ( row : & PgRow ) -> Result < StoredPublicCatalogModel , DataLayerError > {
StoredPublicCatalogModel ::new (
2026-04-07 02:50:19 +08:00
row . try_get ( "id" ). map_postgres_err () ? ,
row . try_get ( "provider_id" ). map_postgres_err () ? ,
row . try_get ( "provider_name" ). map_postgres_err () ? ,
row . try_get ( "provider_model_name" ). map_postgres_err () ? ,
row . try_get ( "name" ). map_postgres_err () ? ,
row . try_get ( "display_name" ). map_postgres_err () ? ,
row . try_get ( "description" ). map_postgres_err () ? ,
row . try_get ( "icon_url" ). map_postgres_err () ? ,
row . try_get ( "input_price_per_1m" ). map_postgres_err () ? ,
row . try_get ( "output_price_per_1m" ). map_postgres_err () ? ,
row . try_get ( "cache_creation_price_per_1m" )
. map_postgres_err () ? ,
row . try_get ( "cache_read_price_per_1m" ). map_postgres_err () ? ,
row . try_get ( "supports_vision" ). map_postgres_err () ? ,
row . try_get ( "supports_function_calling" )
. map_postgres_err () ? ,
row . try_get ( "supports_streaming" ). map_postgres_err () ? ,
2026-05-03 17:32:41 +08:00
row . try_get ( "supports_embedding" ). map_postgres_err () ? ,
2026-04-07 02:50:19 +08:00
row . try_get ( "is_active" ). map_postgres_err () ? ,
2026-03-31 19:19:04 +08:00
)
}
fn map_admin_provider_model_row ( row : & PgRow ) -> Result < StoredAdminProviderModel , DataLayerError > {
2026-04-10 01:46:14 +08:00
let created_at_unix_ms = row
. try_get ::< Option < i64 > , _ > ( "created_at_unix_ms" )
2026-04-07 02:50:19 +08:00
. map_postgres_err () ?
2026-03-31 19:19:04 +08:00
. map ( | value | value . max ( 0 ) as u64 );
let updated_at_unix_secs = row
2026-04-07 02:50:19 +08:00
. try_get ::< Option < i64 > , _ > ( "updated_at_unix_secs" )
. map_postgres_err () ?
2026-03-31 19:19:04 +08:00
. map ( | value | value . max ( 0 ) as u64 );
StoredAdminProviderModel ::new (
2026-04-07 02:50:19 +08:00
row . try_get ( "id" ). map_postgres_err () ? ,
row . try_get ( "provider_id" ). map_postgres_err () ? ,
row . try_get ( "global_model_id" ). map_postgres_err () ? ,
row . try_get ( "provider_model_name" ). map_postgres_err () ? ,
row . try_get ( "provider_model_mappings" ). map_postgres_err () ? ,
row . try_get ( "price_per_request" ). map_postgres_err () ? ,
row . try_get ( "tiered_pricing" ). map_postgres_err () ? ,
row . try_get ( "supports_vision" ). map_postgres_err () ? ,
row . try_get ( "supports_function_calling" )
. map_postgres_err () ? ,
row . try_get ( "supports_streaming" ). map_postgres_err () ? ,
row . try_get ( "supports_extended_thinking" )
. map_postgres_err () ? ,
row . try_get ( "supports_image_generation" )
. map_postgres_err () ? ,
row . try_get ( "is_active" ). map_postgres_err () ? ,
row . try_get ( "is_available" ). map_postgres_err () ? ,
row . try_get ( "config" ). map_postgres_err () ? ,
2026-04-10 01:46:14 +08:00
created_at_unix_ms ,
2026-03-31 19:19:04 +08:00
updated_at_unix_secs ,
2026-04-07 02:50:19 +08:00
row . try_get ( "global_model_name" ). map_postgres_err () ? ,
row . try_get ( "global_model_display_name" )
. map_postgres_err () ? ,
row . try_get ( "global_model_default_price_per_request" )
. map_postgres_err () ? ,
row . try_get ( "global_model_default_tiered_pricing" )
. map_postgres_err () ? ,
2026-05-03 17:32:41 +08:00
row . try_get ( "global_model_supported_capabilities" )
. map_postgres_err () ? ,
2026-04-07 02:50:19 +08:00
row . try_get ( "global_model_config" ). map_postgres_err () ? ,
2026-03-31 19:19:04 +08:00
)
}
fn map_admin_global_model_row ( row : & PgRow ) -> Result < StoredAdminGlobalModel , DataLayerError > {
2026-04-10 01:46:14 +08:00
let created_at_unix_ms = row
. try_get ::< Option < i64 > , _ > ( "created_at_unix_ms" )
2026-04-07 02:50:19 +08:00
. map_postgres_err () ?
2026-03-31 19:19:04 +08:00
. map ( | value | value . max ( 0 ) as u64 );
let updated_at_unix_secs = row
2026-04-07 02:50:19 +08:00
. try_get ::< Option < i64 > , _ > ( "updated_at_unix_secs" )
. map_postgres_err () ?
2026-03-31 19:19:04 +08:00
. map ( | value | value . max ( 0 ) as u64 );
2026-04-09 13:51:50 +08:00
let provider_count = row
. try_get ::< i64 , _ > ( "provider_count" )
. map_postgres_err () ?
. max ( 0 ) as u64 ;
let active_provider_count = row
. try_get ::< i64 , _ > ( "active_provider_count" )
. map_postgres_err () ?
. max ( 0 ) as u64 ;
let usage_count = row
. try_get ::< i64 , _ > ( "usage_count" )
. map_postgres_err () ?
. max ( 0 ) as u64 ;
2026-03-31 19:19:04 +08:00
StoredAdminGlobalModel ::new (
2026-04-07 02:50:19 +08:00
row . try_get ( "id" ). map_postgres_err () ? ,
row . try_get ( "name" ). map_postgres_err () ? ,
row . try_get ( "display_name" ). map_postgres_err () ? ,
row . try_get ( "is_active" ). map_postgres_err () ? ,
row . try_get ( "default_price_per_request" )
. map_postgres_err () ? ,
row . try_get ( "default_tiered_pricing" ). map_postgres_err () ? ,
row . try_get ( "supported_capabilities" ). map_postgres_err () ? ,
row . try_get ( "config" ). map_postgres_err () ? ,
2026-04-09 13:51:50 +08:00
provider_count ,
active_provider_count ,
usage_count ,
2026-04-10 01:46:14 +08:00
created_at_unix_ms ,
2026-03-31 19:19:04 +08:00
updated_at_unix_secs ,
)
}
fn build_provider_id_list_query < 'a > (
prefix : & 'static str ,
provider_ids : & 'a [ String ],
suffix : & 'static str ,
) -> QueryBuilder < 'a , Postgres > {
let mut builder = QueryBuilder ::< Postgres > ::new ( prefix );
let mut separated = builder . separated ( ", " );
for provider_id in provider_ids {
separated . push_bind ( provider_id );
}
separated . push_unseparated ( suffix );
builder
}
fn map_provider_model_stats_row ( row : & PgRow ) -> Result < StoredProviderModelStats , DataLayerError > {
StoredProviderModelStats ::new (
2026-04-07 02:50:19 +08:00
row . try_get ( "provider_id" ). map_postgres_err () ? ,
row . try_get ( "total_models" ). map_postgres_err () ? ,
row . try_get ( "active_models" ). map_postgres_err () ? ,
2026-03-31 19:19:04 +08:00
)
}
fn map_provider_active_global_model_row (
row : & PgRow ,
) -> Result < StoredProviderActiveGlobalModel , DataLayerError > {
StoredProviderActiveGlobalModel ::new (
2026-04-07 02:50:19 +08:00
row . try_get ( "provider_id" ). map_postgres_err () ? ,
row . try_get ( "global_model_id" ). map_postgres_err () ? ,
2026-03-31 19:19:04 +08:00
)
}
2026-05-25 15:23:02 +08:00
fn optional_admin_global_model_usage_count_i64 (
value : Option < u64 > ,
) -> Result < Option < i64 > , DataLayerError > {
value
. map ( | value | {
i64 ::try_from ( value ). map_err ( | _ | {
DataLayerError ::InvalidInput (
"global_models.usage_count exceeds i64 range" . to_string (),
)
})
})
. transpose ()
}
2026-03-31 19:19:04 +08:00
#[cfg(test)]
mod tests {
2026-05-05 17:34:02 +08:00
use super ::{
SqlxGlobalModelReadRepository , LIST_ADMIN_GLOBAL_MODELS_PREFIX ,
LIST_ADMIN_PROVIDER_MODELS_PREFIX ,
};
2026-07-15 23:47:19 +08:00
use crate ::{ PostgresPoolConfig , PostgresPoolFactory };
2026-03-31 19:19:04 +08:00
2026-05-03 17:32:41 +08:00
const ADMIN_PROVIDER_MODEL_REQUIRED_COLUMNS : & [ & str ] = & [
"global_model_default_tiered_pricing" ,
"global_model_supported_capabilities" ,
"global_model_config" ,
];
fn assert_admin_provider_model_projection_has_required_columns ( sql : & str ) {
for column in ADMIN_PROVIDER_MODEL_REQUIRED_COLUMNS {
assert! (
sql . contains ( column ),
"admin provider model SQL projection should include {column}"
);
}
}
#[test]
fn admin_provider_model_sql_projections_include_supported_capabilities () {
assert_admin_provider_model_projection_has_required_columns (
LIST_ADMIN_PROVIDER_MODELS_PREFIX ,
);
2026-07-15 23:47:19 +08:00
assert_admin_provider_model_projection_has_required_columns ( include_str! (
"global_models.rs"
));
2026-05-03 17:32:41 +08:00
let supported_capabilities_projection = format! (
" {} AS {} " ,
"gm.supported_capabilities" , "global_model_supported_capabilities"
);
assert_eq! (
2026-07-15 23:47:19 +08:00
include_str! ( "global_models.rs" )
2026-05-03 17:32:41 +08:00
. matches ( & supported_capabilities_projection )
. count (),
4
);
}
2026-05-05 17:34:02 +08:00
#[test]
fn admin_global_model_sql_projects_usage_count_from_billing_facts () {
2026-07-15 23:47:19 +08:00
let source = include_str! ( "global_models.rs" );
2026-05-05 17:34:02 +08:00
assert! (
! LIST_ADMIN_GLOBAL_MODELS_PREFIX . contains ( "usage_billing_facts" ),
"admin global model list should read the maintained usage_count field"
);
assert! (
source . contains ( "WHERE usage.model = gm.name" ),
"admin global model detail lookups should count usage facts for the selected model"
);
assert! (
source . contains ( "AND usage.status NOT IN ('pending', 'streaming')" ),
"admin global model detail usage_count should use the maintained read-model status scope"
);
assert! (
source . contains ( "COALESCE(usage_stats.usage_count, gm.usage_count, 0)::bigint AS usage_count" ),
"admin global model usage_count should prefer actual usage facts with stored-count fallback"
);
}
#[test]
fn admin_global_model_list_sql_counts_usage_without_full_fact_aggregate () {
assert! (
LIST_ADMIN_GLOBAL_MODELS_PREFIX . contains ( "COALESCE(gm.usage_count, 0)::bigint AS usage_count" ),
"admin global model list should read maintained usage_count without per-request fact scans"
);
assert! (
! LIST_ADMIN_GLOBAL_MODELS_PREFIX . contains ( "GROUP BY usage.model" ),
"admin global model list must not aggregate the full usage fact table before pagination"
);
}
#[test]
fn global_model_usage_count_read_model_has_backfill_and_delta_maintenance () {
2026-05-06 02:22:32 +08:00
let backfill_sql = include_str! (
2026-07-15 23:47:19 +08:00
"../../../runtime/backfills/postgres/20260505120000_rebuild_global_model_usage_count.sql"
2026-05-06 02:22:32 +08:00
);
2026-07-15 23:47:19 +08:00
let delta_sql = include_str! ( "usage/queries/apply_global_model_usage_delta_sql.sql" );
2026-05-05 17:34:02 +08:00
assert! (
backfill_sql . contains ( "UPDATE global_models AS gm" ),
"global model usage_count backfill should refresh the read model"
);
assert! (
backfill_sql . contains ( "FROM usage_billing_facts AS usage" ),
"global model usage_count backfill should rebuild from canonical usage facts"
);
assert! (
delta_sql . contains ( "UPDATE global_models" ),
"usage writes should maintain the global model usage_count read model"
);
assert! (
delta_sql . contains ( "WHERE name = $1" ),
"global model usage_count delta should target models by canonical model name"
);
}
2026-03-31 19:19:04 +08:00
#[tokio::test]
async fn repository_constructs_from_lazy_pool () {
let factory = PostgresPoolFactory ::new ( PostgresPoolConfig {
database_url : "postgres://localhost/aether" . to_string (),
min_connections : 1 ,
max_connections : 4 ,
acquire_timeout_ms : 1_000 ,
idle_timeout_ms : 5_000 ,
max_lifetime_ms : 30_000 ,
statement_cache_capacity : 64 ,
require_ssl : false ,
})
. expect ( "factory should build" );
let pool = factory . connect_lazy (). expect ( "pool should build" );
let _repository = SqlxGlobalModelReadRepository ::new ( pool );
}
}