symfonic.services.models.family¶
family ¶
Provider-family classification — the one implementation.
Moved here from symfonic.agent._provider_family (T3.1.1). The classifier
was a facade-layer private, which is why the plan compiler could not reach it
and stamped the provider class name into ModelResolution.provider_family
instead. symfonic.agent._provider_family and symfonic.agent.engine now
re-export these objects, so is identity holds across every historical
import path and a second copy cannot quietly appear.
Detection precedence (unchanged from v7.13.4):
- Duck-typed declaration —
_symfonic_provider_familyread off the instance so adapters that classify per model at runtime (Bedrock) are honoured, falling back to the ClassVar for declare-once adapters, and then to the nearest base class that declares a canonical family. That last step is the adopter-fork defence: a fork that subclassesKimiProviderand overrides the ClassVar to something non-canonical ("kimi") still classifiesopenai, becauseKimiProvideritself declares it. - Routing-wrapper descent through
_default/default, bounded to depth 4 with an identity-cycle guard.
That base-class step used to be a separate isinstance() pass against six
adapter classes imported lazily from symfonic.core.providers. TA2.2
replaced it with the MRO walk in :func:_declared_family, which decides the
same cases — the six shipped classes are exactly the ones declaring a
canonical family — without a runtime-service -> facade-compiler import,
the one edge in this package the dependency matrix forbids outright with no
port option. See RET-PREP/core-mapping.md.
"unknown" is the wire-neutral verdict, not a failure: the cache layer
refuses to place a cache_control annotation on a payload whose dialect it
cannot name, which is what preserves the wire-neutrality contract for custom
Llama / vLLM / gateway deployments.
detect_provider_family ¶
Classify provider into the content-block dialect it expects.
resolved_config is optional and, when given, decides routing wrappers:
the family returned is the family of the leaf that will serve that
config, not the wrapper's default leaf.
Source code in src/symfonic/services/models/family.py
leaf_provider ¶
The provider that will actually serve resolved_config.
Follows route maps first (when a config is supplied), then plain wrapper
_default / default chains, under the same depth bound and cycle
guard as :func:detect_provider_family.