Documentación

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_DIGESTS plano -- {artifact: digest} en lugar de {model: {...}} -- no es una capa aquí en absoluto. verify_digests lo aplica por sí mismo, pero solo cuando nada más fija (if expected is None), así que CUALQUIER expected_sha256 que llegue a Agent, 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 fija model.safetensors más un mapa agent_kwargs que fija tokenizer.json carga un model.safetensors manipulado. Usa la forma por checkpoint, o nombra cada archivo que te importe en un solo mapa, si necesitas ambos. Esto no cambia respecto a main.
  • Una entrada explícita {} o None ENMASCARA lo que de otro modo se aplicaría -- eso es lo que "cargar este sin verificar" tiene que significar, y test_an_explicit_none_entry_masks_a_flat_environment_map lo 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]]= None
deviceOptional[str]= None
tokenOptional[str]= None
revisionOptional[str]= None
revisionsOptional[Dict[str, Optional[str]]]= None
max_loadedint= 2
defaultstr= "english"
auto_task_detectionbool= False
standalone_reposbool= False
preloadbool= False
lang_guessOptional[Any]= None
hooks= None
on_predict_start= None
on_predict_end= None
hooks_raisebool= True
hooks_concurrentbool= True
hooks_timeoutOptional[float]= None
agent_kwargsOptional[Dict[str, Any]]= None
sha256_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

namestr
agentAny

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,
) -> RouteDecision

Decide 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]]= None
modelOptional[str]= None
taskOptional[str]= None
langOptional[str]= None
lang_guessOptional[Any]= None
hooks= None
hooks_raiseOptional[bool]= None
hooks_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]= None
taskOptional[str]= None
langOptional[str]= None
lang_guessOptional[Any]= None
hooks= None
on_predict_start= None
on_predict_end= None
hooks_raiseOptional[bool]= None
hooks_timeoutOptional[float]= None
max_lenOptional[int]= None
head_max_lenOptional[int]= None
min_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]= None
taskOptional[str]= None
langOptional[str]= None
lang_guessOptional[Any]= None
windowOptional[int]= None

tokens 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]= None

paso 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]= None

límite de ventanas por pasada hacia adelante, para acotar la memoria en estados muy largos.

hooks= None
on_predict_start= None
on_predict_end= None
hooks_raiseOptional[bool]= None
hooks_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,
) -> Any

Responde 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= None
questionsOptional[Dict[str, Any]]= None
return_detailsbool= False
min_confidenceOptional[float]= None
predict_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= None
questionsOptional[Dict[str, Any]]= None
return_detailsbool= False
min_confidenceOptional[float]= None
predict_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 state y questions.

hooks_timeoutOptional[float]= None

Sobrescribe el hooks_timeout del Router para esta llamada, aplicado al despacho on_route de cada petición, como en :meth:route.

hooksHookArg= None

Hook por llamada o secuencia de hooks para esta llamada.

hooks_raiseOptional[bool]= None

Sobrescribe la política hooks_raise del 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 state y questions y puede incluir las sobrescrituras de enrutamiento model, task, lang o lang_guess y las sobrescrituras del presupuesto de tokens max_len / head_max_len.

batch_sizeOptional[int]= None

Número máximo opcional de estados por lote de pasada hacia adelante del Agent.

hooks_timeoutOptional[float]= None

Sobrescribe el hooks_timeout del Router para esta llamada.

min_confidenceOptional[float]= None

Float opcional o mapping por cubo para el gating por confianza.

sort_by_lengthbool= False

Se reenvía a cada llamada a Agent.predict_batch, así que cada grupo de preguntas rellena hasta un máximo más corto; consulta Agent.predict_batch. Los resultados conservan el orden de entrada en cualquier caso. Se descarta en silencio para un agent adjuntado cuyo predict_batch es anterior a esta opción (#294).

hooksHookArg= None

Hook por llamada o secuencia de hooks para esta llamada.

on_predict_startPredictHookArg= None

Callable simple o secuencia de callables para eventos de inicio.

on_predict_endPredictHookArg= None

Callable simple o secuencia de callables para eventos de fin.

hooks_raiseOptional[bool]= None

Sobrescribe la política hooks_raise del 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"),
}