@@ -250,6 +250,19 @@ pub fn detect_provider_kind(model: &str) -> ProviderKind {
250250 ProviderKind :: Anthropic
251251}
252252
253+ #[ must_use]
254+ pub const fn model_family_identity_for_kind ( kind : ProviderKind ) -> runtime:: ModelFamilyIdentity {
255+ match kind {
256+ ProviderKind :: Anthropic => runtime:: ModelFamilyIdentity :: Claude ,
257+ ProviderKind :: Xai | ProviderKind :: OpenAi => runtime:: ModelFamilyIdentity :: Generic ,
258+ }
259+ }
260+
261+ #[ must_use]
262+ pub fn model_family_identity_for ( model : & str ) -> runtime:: ModelFamilyIdentity {
263+ model_family_identity_for_kind ( detect_provider_kind ( model) )
264+ }
265+
253266#[ must_use]
254267pub fn max_tokens_for_model ( model : & str ) -> u32 {
255268 let canonical = resolve_model_alias ( model) ;
@@ -484,8 +497,8 @@ mod tests {
484497 use super :: {
485498 anthropic_missing_credentials, anthropic_missing_credentials_hint, detect_provider_kind,
486499 load_dotenv_file, max_tokens_for_model, max_tokens_for_model_with_override,
487- model_token_limit , parse_dotenv , preflight_message_request , resolve_model_alias ,
488- ProviderKind ,
500+ model_family_identity_for , model_family_identity_for_kind , model_token_limit , parse_dotenv ,
501+ preflight_message_request , resolve_model_alias , ProviderKind ,
489502 } ;
490503
491504 /// Serializes every test in this module that mutates process-wide
@@ -544,6 +557,42 @@ mod tests {
544557 ) ;
545558 }
546559
560+ #[ test]
561+ fn maps_provider_kind_to_model_family_identity ( ) {
562+ // given: each supported provider kind
563+ let anthropic = ProviderKind :: Anthropic ;
564+ let openai = ProviderKind :: OpenAi ;
565+ let xai = ProviderKind :: Xai ;
566+
567+ // when: converting provider kinds to prompt model family identities
568+ let anthropic_identity = model_family_identity_for_kind ( anthropic) ;
569+ let openai_identity = model_family_identity_for_kind ( openai) ;
570+ let xai_identity = model_family_identity_for_kind ( xai) ;
571+
572+ // then: Anthropic stays Claude and OpenAI-compatible providers are generic
573+ assert_eq ! ( anthropic_identity, runtime:: ModelFamilyIdentity :: Claude ) ;
574+ assert_eq ! ( openai_identity, runtime:: ModelFamilyIdentity :: Generic ) ;
575+ assert_eq ! ( xai_identity, runtime:: ModelFamilyIdentity :: Generic ) ;
576+ }
577+
578+ #[ test]
579+ fn maps_model_name_to_model_family_identity ( ) {
580+ // given: Anthropic, OpenAI-compatible, and xAI model names
581+ let claude_model = "claude-opus-4-6" ;
582+ let openai_model = "openai/gpt-4.1-mini" ;
583+ let xai_model = "grok-3" ;
584+
585+ // when: detecting prompt model family identities from model names
586+ let claude_identity = model_family_identity_for ( claude_model) ;
587+ let openai_identity = model_family_identity_for ( openai_model) ;
588+ let xai_identity = model_family_identity_for ( xai_model) ;
589+
590+ // then: Anthropic stays Claude and OpenAI-compatible providers are generic
591+ assert_eq ! ( claude_identity, runtime:: ModelFamilyIdentity :: Claude ) ;
592+ assert_eq ! ( openai_identity, runtime:: ModelFamilyIdentity :: Generic ) ;
593+ assert_eq ! ( xai_identity, runtime:: ModelFamilyIdentity :: Generic ) ;
594+ }
595+
547596 #[ test]
548597 fn openai_namespaced_model_routes_to_openai_not_anthropic ( ) {
549598 // Regression: "openai/gpt-4.1-mini" was misrouted to Anthropic when
0 commit comments