Router
laya.Router detecta el idioma de cada estado y envía la solicitud al checkpoint
correspondiente, y carga los checkpoints en el primer uso.
Los nombres, los tipos, los valores por defecto y el código se mantienen en inglés; el resto está traducido (las entradas aún sin traducir se muestran en el original).
Router
Router(
models: Optional[Dict[str, str]] = None,
device: Optional[str] = None,
token: Optional[str] = None,
revision: Optional[str] = None,
revisions: Optional[Dict[str, Optional[str]]] = None,
max_loaded: int = 2,
default: str = "english",
auto_task_detection: bool = False,
standalone_repos: bool = False,
preload: bool = False,
lang_guess: Optional[Any] = None,
hooks=None,
on_predict_start=None,
on_predict_end=None,
hooks_raise: bool = True,
hooks_concurrent: bool = True,
hooks_timeout: Optional[float] = None,
agent_kwargs: Optional[Dict[str, Any]] = None,
sha256_digests: Optional[Dict[str, Optional[Dict[str, str]]]] = None,
)Clases base: HookRegistry
Carga perezosamente los checkpoints de Laya y envía cada petición al correcto.
from laya import Router
r = Router()
r.predict({"message": "Mein Konto wurde zweimal belastet"}, questions) # -> multilingual
r.predict({"message": "I was charged twice"}, questions) # -> english
r.predict(state, questions, model="typed-decisions") # explicit
Los modelos se descargan y se construyen en el primer uso. max_loaded limita cuántos permanecen residentes
(el menos usado recientemente se desaloja), porque los tres juntos son ~1.16B parámetros.
El valor predeterminado es 2, porque el enrutamiento automático solo elige entre english y
multilingual: un límite de uno reconstruye el checkpoint que acaba de desalojar en cada cambio de idioma,
lo que son segundos por petición justo en el tráfico para el que existe el Router. El tráfico que solo
ve un idioma nunca construye el segundo checkpoint, así que el valor predeterminado no le cuesta nada.
Bájalo a 1 para un host con memoria limitada, y súbelo a 3 (o precarga) cuando
auto_task_detection, un model= explícito o un task= explícito también puedan alcanzar
typed-decisions.
Para un servidor o una demo, precarga mejor: una carga en frío cuesta segundos, mientras que la detección cuesta microsegundos, así que incluso el valor predeterminado todavía paga una carga la primera vez que aparece un idioma.
r = Router(preload=True) # all three resident, routing is free
r = Router(preload=True, device="cuda")
r.preload(["english", "multilingual"]) # or just the two you serve
Las revisiones del Hub son opcionales. revision aplica un commit a todos los modelos;
revisions={"english": "...", "multilingual": "..."} lo sobrescribe por modelo,
lo que es útil cuando repositorios independientes se revisaron en commits distintos.
Sin ninguno de los dos, se usan el valor predeterminado normal de huggingface_hub y la caché offline existente.
Una entrada de revisions que sea None o esté en blanco significa "sin sobrescritura para este modelo", así que el modelo
hereda revision -- la forma que escribe {"english": os.environ.get("EN_SHA")} cuando la
variable no está definida, lo que no debe costarle al llamador el pin que sí pidió. Nada en
revisions puede soltar un modelo mientras revision fija el resto; deja revision sin definir y
nombra los modelos que quieras fijar en su lugar.
Una revision en blanco se lee igual, lo cual es un cambio deliberado en lo que el Router
pasa: Router(revision=" ") antes llegaba a resolve_revision, donde una cadena veraz pero en blanco
suprimía el fallback LAYA_REVISION y dejaba el valor predeterminado de huggingface_hub, y ahora se
descarta antes de llegar allí, así que un Router configurado con espacios se comporta como uno
configurado sin nada y se aplica $LAYA_REVISION. Eso es lo que "esta línea de configuración
nunca se rellenó" tiene que significar si revision y una entrada de revisions deben leerse igual.
Es uno de los dos sitios donde una fuente más débil acaba por delante de un argumento explícito. El otro es una
entrada de digest por checkpoint de LAYA_SHA256_DIGESTS, que gana a un expected_sha256 pasado
mediante agent_kwargs; consulta el docstring de la clase.
Cualquier otra cosa que acepte laya.Agent es accesible mediante agent_kwargs, que se fusiona en
cada checkpoint que construye el Router:
Router(agent_kwargs={"lang_temperatures": {"de": {"temperature": [1.0, 1.4, 2.0]}}})
Router(agent_kwargs={"expected_sha256": {"model.safetensors": "a3f1..."}})
Router(agent_kwargs={"fast": True})
expected_sha256 ahí fija los mismos archivos en cada checkpoint, que es lo que quiere un único
checkpoint residente o un tokenizer.json compartido. Nunca lo descartan por completo los
digests propios de un checkpoint: donde uno también tiene una entrada, de sha256_digests o de un
LAYA_SHA256_DIGESTS por checkpoint, los dos mapas se fusionan archivo por archivo, así que un archivo
que solo uno de ellos nombra se sigue verificando.
Dos excepciones, ambas deliberadas y ambas probadas, porque "fusionado archivo por archivo" no es toda la historia y la diferencia es un control de supply chain:
- Un
LAYA_SHA256_DIGESTSplano --{artifact: digest}en lugar de{model: {...}}-- no es una capa aquí en absoluto.verify_digestslo aplica por sí mismo, pero solo cuando nada más fija (if expected is None), así que CUALQUIERexpected_sha256que llegue aAgent, desde aquí o desde una entrada de checkpoint, significa que la variable plana no se consulta para esa carga. Verificado con archivos reales: una variable plana que fijamodel.safetensorsmás un mapaagent_kwargsque fijatokenizer.jsoncarga unmodel.safetensorsmanipulado. Usa la forma por checkpoint, o nombra cada archivo que te importe en un solo mapa, si necesitas ambos. Esto no cambia respecto amain. - Una entrada explícita
{}oNoneENMASCARA lo que de otro modo se aplicaría -- eso es lo que "cargar este sin verificar" tiene que significar, ytest_an_explicit_none_entry_masks_a_flat_environment_maplo fija.
Y la precedencia es por checkpoint sobre compartido sin importar de dónde venga cada uno, así que una
entrada por checkpoint sintetizada a partir de LAYA_SHA256_DIGESTS gana a un expected_sha256
pasado aquí en el código. Una variable de entorno que gana a un argumento explícito merece decirse
claramente en un control como este; test_an_environment_pin_overrides_the_shared_one_per_checkpoint
es donde eso se fija.
Para un archivo que ambos nombran, gana la entrada por checkpoint. Las dos no son igual de específicas: el
mapa agent_kwargs llega a cada checkpoint que construye el Router, y model.safetensors es el único
nombre que cada checkpoint usa para un archivo distinto, así que una entrada compartida para él no puede ser una afirmación
correcta sobre todos a la vez. No se lanza nada por esa superposición -- rechazarla rechazaría
un pin compartido más una sobrescritura por checkpoint, que es la forma ordinaria y que cargaba
correctamente antes de que existiera nada de esto. Si necesitas saber contra qué digest se verificó un checkpoint,
vuelve a leerlo: el mapa que se entrega a cada Agent es la fusión descrita arriba.
Tanto agent_kwargs como sha256_digests son públicos y mutables, y la entrada de un checkpoint se lee
al cargar, no en la construcción, así que un pin asignado después -- o añadido a una
entrada existente in situ -- cuenta.
Los nombres que el Router fija para sí mismo -- model_id_or_path, device, token, subfolder,
revision y los argumentos de hook -- se rechazan aquí en lugar de sombrearse en silencio, y los
nombres restantes se comprueban contra Agent.__init__ en la construcción, así que una opción mal escrita
falla en la línea Router(...) en lugar de en la primera petición.
Los digests de artefactos son opcionales y siempre por modelo: sha256_digests={"english": {...}}
pasa ese mapa {path relative to the checkpoint dir: hexdigest} al Agent que
lo carga, así que un archivo de pesos manipulado o sustituido se rechaza antes de analizarse. No
hay un equivalente de revision para todo el Router porque los digests, a diferencia de un commit SHA, no
son compartibles: el repositorio empaquetado trae un model.safetensors separado para cada uno de
english, multilingual y typed-decisions, así que un solo mapa plano solo puede coincidir con uno
de ellos. Un modelo listado con None o {} no añade archivos propios, lo que lo carga sin verificar
a menos que agent_kwargs["expected_sha256"] lo fije; sigue enmascarando un
LAYA_SHA256_DIGESTS plano, que es para lo que se lista así.
La misma división está disponible para un proceso configurado solo por entorno: cuando
LAYA_SHA256_DIGESTS contiene un mapa indexado por modelo ({"english": {...}, "multilingual": {...}})
esto lo siembra por checkpoint, así que un servidor que mantiene varios residentes puede fijar cada uno con sus
propios digests en lugar de negarse a arrancar en el segundo. Un LAYA_SHA256_DIGESTS plano
conserva su significado existente, aplicado por laya.revisions a cada checkpoint que el proceso
carga, lo cual es correcto para uno de un solo checkpoint. Una entrada de argumento gana sobre el
entorno para el modelo que nombra. Una variable anidada que nombra algunos checkpoints y no otros no dice nada sobre los demás: conservan
lo que agent_kwargs["expected_sha256"] les fije, porque fijar un checkpoint desde el entorno no es una petición de
dejar de verificar el resto.
Los hooks son opcionales y se ejecutan a nivel del Router: on_route ve la decisión de enrutamiento,
on_load / on_evict ven el ciclo de vida del modelo, y on_predict_start / on_predict_end
envuelven toda la llamada de route+infer. Consulta laya.hooks.
Parámetros
modelsOptional[Dict[str, str]]=NonedeviceOptional[str]=NonetokenOptional[str]=NonerevisionOptional[str]=NonerevisionsOptional[Dict[str, Optional[str]]]=Nonemax_loadedint=2defaultstr="english"auto_task_detectionbool=Falsestandalone_reposbool=Falsepreloadbool=Falselang_guessOptional[Any]=Nonehooks=Noneon_predict_start=Noneon_predict_end=Nonehooks_raisebool=Truehooks_concurrentbool=Truehooks_timeoutOptional[float]=Noneagent_kwargsOptional[Dict[str, Any]]=Nonesha256_digestsOptional[Dict[str, Optional[Dict[str, str]]]]=None
load
load(name: str)Devuelve el Agent para name, descargándolo y construyéndolo en el primer uso.
Los llamadores concurrentes comparten un único Agent en lugar de construir duplicados.
Parámetros
namestr
attach
attach(name: str, agent: Any)Registra un Agent ya construido bajo name en lugar de cargar una segunda copia.
Útil cuando el proceso tiene un checkpoint cargado por otros motivos: una demo que ya
construyó convaiinnovations/laya puede entregárselo al router en vez de pagar por -- y mantener
en memoria -- un duplicado de 421M parámetros.
Parámetros
namestragentAny
preload
preload(names: Optional[List[str]] = None)Descarga y construye los checkpoints por adelantado para que ninguna petición pague una carga de modelo.
Una carga en frío cuesta segundos; la detección de idioma cuesta microsegundos. Con cada
checkpoint residente, el enrutamiento es efectivamente gratis -- que es lo que quieres en un
servidor o una demo. max_loaded se eleva para dar cabida tanto a los checkpoints solicitados como
a todos los agents ya residentes, así que la precarga incremental no desaloja a ninguno de los dos.
Parámetros
namesOptional[List[str]]=None
unload
unload(name: Optional[str] = None)Libera un modelo, o todos ellos.
Parámetros
nameOptional[str]=None
loaded_revisions
loaded_revisions: Dict[str, Optional[str]]Commit SHA desde el que se cargó cada agent residente (None para rutas locales).
route
route(
state: Union[str, dict, list, None],
questions: Optional[Dict[str, Any]] = None,
model: Optional[str] = None,
task: Optional[str] = None,
lang: Optional[str] = None,
lang_guess: Optional[Any] = None,
hooks=None,
hooks_raise: Optional[bool] = None,
hooks_timeout: Optional[float] = None,
) -> RouteDecisionDecide qué checkpoint usar y luego deja que los hooks on_route lo observen o lo reemplacen.
ctx.decision es el RouteDecision; un hook puede reemplazarlo (por ejemplo para fijar un
checkpoint) y el reemplazo es lo que se devuelve y se usa. hooks son hooks por llamada,
añadidos después de los que haya instalados en el Router.
Parámetros
stateUnion[str, dict, list, None]questionsOptional[Dict[str, Any]]=NonemodelOptional[str]=NonetaskOptional[str]=NonelangOptional[str]=Nonelang_guessOptional[Any]=Nonehooks=Nonehooks_raiseOptional[bool]=Nonehooks_timeoutOptional[float]=None
predict
predict(
state: Union[str, dict, list],
questions: Dict[str, Any],
model: Optional[str] = None,
task: Optional[str] = None,
lang: Optional[str] = None,
lang_guess: Optional[Any] = None,
hooks=None,
on_predict_start=None,
on_predict_end=None,
hooks_raise: Optional[bool] = None,
hooks_timeout: Optional[float] = None,
max_len: Optional[int] = None,
head_max_len: Optional[int] = None,
min_confidence: Optional[float] = None,
) -> Dict[str, Any]Enruta y luego responde cada pregunta en una sola pasada hacia adelante sobre el checkpoint elegido.
El resultado es la carga habitual de system_one más una clave routing que registra la decisión.
Los hooks on_predict_start / on_predict_end a nivel del Router envuelven toda la llamada de route+infer
y ven ctx.decision; consulta laya.hooks. max_len / head_max_len sobrescriben el presupuesto
de tokens del agent para esta llamada (un hook de inicio puede fijar ctx.max_len / ctx.head_max_len).
Parámetros
stateUnion[str, dict, list]questionsDict[str, Any]modelOptional[str]=NonetaskOptional[str]=NonelangOptional[str]=Nonelang_guessOptional[Any]=Nonehooks=Noneon_predict_start=Noneon_predict_end=Nonehooks_raiseOptional[bool]=Nonehooks_timeoutOptional[float]=Nonemax_lenOptional[int]=Nonehead_max_lenOptional[int]=Nonemin_confidenceOptional[float]=None
predict_long
predict_long(
state: Union[str, dict, list],
questions: Dict[str, Any],
model: Optional[str] = None,
task: Optional[str] = None,
lang: Optional[str] = None,
lang_guess: Optional[Any] = None,
window: Optional[int] = None,
stride: Optional[int] = None,
aggregate: str = "auto",
batch_size: Optional[int] = None,
hooks=None,
on_predict_start=None,
on_predict_end=None,
hooks_raise: Optional[bool] = None,
hooks_timeout: Optional[float] = None,
) -> Dict[str, Any]Enruta y luego escanea cada ventana del estado en lugar de solo la primera.
predict puntúa un estado desde una sola ventana: todo lo que pasa de max_len se corta (la
primera ventana, o para una lista de conversación la última) y nunca llega al modelo. Esto
enruta exactamente como lo hace predict -- las mismas pistas model/task/lang, los mismos
hooks a nivel del router, la misma clave routing y usage -- y puntúa el estado enrutado con
el predict_long de ese agent, que lo divide en ventanas superpuestas y agrega por
pregunta. Las reglas de agregación son las de laya.agent.Agent.predict_long: noul toma la
ventana más fuerte, choice/score la más segura.
Los hooks por llamada (hooks, on_predict_start, on_predict_end, hooks_raise,
hooks_timeout) envuelven todo el route+scan exactamente como envuelven predict: el escaneo corre
al final, así que gana un hook de inicio que responde (ctx.skip(...)) o reescribe el estado.
max_len / head_max_len no se aceptan aquí -- una ventana se dimensiona por window o por el
presupuesto del checkpoint, y sobrescribir el truncamiento de una sola ventana es para lo que sirve predict_long.
Parámetros
stateUnion[str, dict, list]questionsDict[str, Any]modelOptional[str]=NonetaskOptional[str]=NonelangOptional[str]=Nonelang_guessOptional[Any]=NonewindowOptional[int]=Nonetokens de estado por ventana. Por defecto es el presupuesto del checkpoint enrutado (
max_len - head_max_len - 8), acotado al espacio que las preguntas dejan para el estado, de modo que ninguna ventana se trunca de nuevo de camino hacia dentro; una ventana más pequeña aísla un tramo localizado.strideOptional[int]=Nonepaso de tokens entre ventanas; por defecto es la mitad de la ventana efectiva (50% de superposición). Un paso más allá de esa ventana es un
ValueError, ya que los tokens entre ventanas no llegarían a ningún modelo.aggregatestr="auto""auto" (las reglas por tipo anteriores) es el único modo.
batch_sizeOptional[int]=Nonelímite de ventanas por pasada hacia adelante, para acotar la memoria en estados muy largos.
hooks=Noneon_predict_start=Noneon_predict_end=Nonehooks_raiseOptional[bool]=Nonehooks_timeoutOptional[float]=None
Devuelve
La carga habitual de predict, con usage["windows"] contando las ventanas puntuadas.
Excepciones
TypeError: el agent enrutado no tiene predict_long -- uno adjuntado a
mano, ya que tanto Agent como ONNXAgent lo implementan --, así que no hay con qué escanear.
Siempre se lanza, sea cual sea hooks_raise, y también cualquier otro error que el propio
escaneo lance -- no se captura ninguno: el escaneo es trabajo de este método, no un
hook del llamador, así que la política de errores de hooks no decide si puede
omitirse. Antes corría como hook de inicio, donde hooks_raise=False se lo tragaba
y devolvía una ventana puntuada por system_one -- una pregunta distinta de
la formulada. Un agent cuyo predict_long no tiene parámetro lang se escanea
sin uno y se advierte, no se falla; eso es una comprobación de firma, no un
error tragado.
decide
decide(
state: Union[str, dict, list],
schema: Any = None,
questions: Optional[Dict[str, Any]] = None,
return_details: bool = False,
min_confidence: Optional[float] = None,
predict_kwargs,
) -> AnyResponde a state contra un schema (JSON schema o modelo pydantic) y devuelve valores tipados.
Consulta laya.structured. Pasa exactamente uno de schema o questions; los argumentos de palabra clave adicionales
(por ejemplo model=, task=, hooks=) se reenvían a predict.
Parámetros
stateUnion[str, dict, list]schemaAny=NonequestionsOptional[Dict[str, Any]]=Nonereturn_detailsbool=Falsemin_confidenceOptional[float]=Nonepredict_kwargs
decide_batch
decide_batch(
states: Sequence[Any],
schema: Any = None,
questions: Optional[Dict[str, Any]] = None,
return_details: bool = False,
min_confidence: Optional[float] = None,
predict_kwargs,
) -> List[Any]Responde a muchos estados contra un schema (JSON schema o modelo pydantic) en una sola llamada por lotes.
La forma de rendimiento de :meth:decide: el schema se planifica una vez y sus preguntas
corren sobre cada estado a través de :meth:predict_batch (pasadas hacia adelante agrupadas, resultados en
orden de entrada), y luego las respuestas de cada estado se proyectan como lo hace decide. Los argumentos de palabra
clave adicionales (batch_size=, model=, hooks=, ...) se reenvían a
predict_batch. Consulta laya.structured.
Parámetros
statesSequence[Any]schemaAny=NonequestionsOptional[Dict[str, Any]]=Nonereturn_detailsbool=Falsemin_confidenceOptional[float]=Nonepredict_kwargs
route_batch
route_batch(
requests: Sequence[Dict[str, Any]],
hooks_timeout: Optional[float] = None,
hooks=None,
hooks_raise: Optional[bool] = None,
) -> List[RouteDecision]Enruta un lote de peticiones heterogéneas sin cargar ningún checkpoint.
Cada petición es un mapping con state y questions más las mismas sobrescrituras
de enrutamiento opcionales que acepta :meth:route: model, task, lang y
lang_guess. Las decisiones devueltas conservan el orden de entrada.
Esto está separado de la inferencia a propósito, para que los llamadores puedan inspeccionar o agregar las decisiones de enrutamiento antes de pagar el costo de carga del modelo.
Parámetros
requestsSequence[Dict[str, Any]]Secuencia de diccionarios de petición, cada uno requiere
stateyquestions.hooks_timeoutOptional[float]=NoneSobrescribe el
hooks_timeoutdel Router para esta llamada, aplicado al despachoon_routede cada petición, como en :meth:route.hooksHookArg=NoneHook por llamada o secuencia de hooks para esta llamada.
hooks_raiseOptional[bool]=NoneSobrescribe la política
hooks_raisedel Router para esta llamada.
predict_batch
predict_batch(
requests: Sequence[Dict[str, Any]],
batch_size: Optional[int] = None,
hooks_timeout: Optional[float] = None,
min_confidence: Optional[float] = None,
sort_by_length: bool = False,
hooks=None,
on_predict_start=None,
on_predict_end=None,
hooks_raise: Optional[bool] = None,
) -> List[Dict[str, Any]]Enruta y ejecuta un lote de peticiones heterogéneas con el mínimo cambio de modelo.
Las peticiones se enrutan primero y se agrupan por checkpoint. Dentro de cada checkpoint,
las peticiones que comparten el mismo schema de preguntas se pasan a
Agent.predict_batch para que sus estados puedan compartir pasadas hacia adelante. Luego los resultados
se restauran al orden original de las peticiones.
Las peticiones pueden especificar de forma independiente model, task, lang,
lang_guess, max_len o head_max_len y pueden usar schemas de preguntas distintos.
max_len / head_max_len son la forma por petición de la sobrescritura del presupuesto de tokens que
predict toma como argumentos de llamada: fijan los presupuestos de estado y de cabeza de preguntas
del checkpoint para esa única petición, de modo que se puede hacer una pregunta amplia sin encoger las
otras peticiones del lote a la misma ventana. Las peticiones que piden presupuestos distintos se
dividen en pasadas hacia adelante separadas, ya que una llamada a Agent.predict_batch lleva un solo
presupuesto para todos sus estados. Un hook de inicio todavía puede reemplazar cualquiera de los dos valores en ctx.
Los hooks de predicción a nivel del Router se ejecutan por petición, como los ejecuta predict: cada petición
obtiene su propio PredictContext, así que on_predict_start puede reemplazar el estado,
las preguntas o el presupuesto de tokens de esa petición, o hacerle ctx.skip(...), y on_predict_end
ve y puede reemplazar su resultado. Las peticiones se agrupan para la pasada hacia adelante después de
que han corrido sus hooks de inicio, y las peticiones de un grupo de checkpoint terminan en orden inverso al
que empezaron. Si un grupo de checkpoint falla, todas sus peticiones cuyo hook de inicio
corrió fallan con la excepción, incluida una que haya acertado en caché: cada una recibe on_error y luego
on_predict_end antes de que la excepción se propague.
Parámetros
requestsSequence[Dict[str, Any]]Secuencia de diccionarios de petición. Cada elemento requiere
stateyquestionsy puede incluir las sobrescrituras de enrutamientomodel,task,langolang_guessy las sobrescrituras del presupuesto de tokensmax_len/head_max_len.batch_sizeOptional[int]=NoneNúmero máximo opcional de estados por lote de pasada hacia adelante del Agent.
hooks_timeoutOptional[float]=NoneSobrescribe el
hooks_timeoutdel Router para esta llamada.min_confidenceOptional[float]=NoneFloat opcional o mapping por cubo para el gating por confianza.
sort_by_lengthbool=FalseSe reenvía a cada llamada a
Agent.predict_batch, así que cada grupo de preguntas rellena hasta un máximo más corto; consultaAgent.predict_batch. Los resultados conservan el orden de entrada en cualquier caso. Se descarta en silencio para un agent adjuntado cuyopredict_batches anterior a esta opción (#294).hooksHookArg=NoneHook por llamada o secuencia de hooks para esta llamada.
on_predict_startPredictHookArg=NoneCallable simple o secuencia de callables para eventos de inicio.
on_predict_endPredictHookArg=NoneCallable simple o secuencia de callables para eventos de fin.
hooks_raiseOptional[bool]=NoneSobrescribe la política
hooks_raisedel Router para esta llamada.
Devuelve
Un resultado normal de predicción del Router por petición, en el mismo orden que la entrada.
RouteDecision
RouteDecision()Clases base: dict
El resultado del enrutamiento: qué modelo, por qué y qué se detectó.
Se comporta como un dict, así que se serializa directamente en una respuesta de API.
DEFAULT_MODELS
DEFAULT_MODELS = {
"english": (BUNDLE_REPO, None),
"multilingual": (BUNDLE_REPO, "multilingual"),
"typed-decisions": (BUNDLE_REPO, "typed-decisions"),
}