Documentação

Router

laya.Router detecta o idioma de cada estado e envia a solicitação ao checkpoint correspondente, carregando os checkpoints no primeiro uso.

Nomes, tipos, valores padrão e código permanecem em inglês; o restante é traduzido (entradas ainda não traduzidas são exibidas no original em inglês).

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

Classes base: HookRegistry

Carrega os checkpoints do Laya sob demanda e envia cada requisição ao correto.

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

Os modelos são baixados e construídos no primeiro uso. max_loaded limita quantos permanecem residentes (o usado menos recentemente é despejado), porque os três juntos são ~1.16B de parâmetros.

O padrão é 2, porque o roteamento automático só escolhe entre english e multilingual: um limite de um reconstrói o checkpoint que acabou de despejar a cada troca de idioma, o que são segundos por requisição exatamente no tráfego para o qual o Router existe. O tráfego que só vê um idioma nunca constrói o segundo checkpoint, então o padrão não lhe custa nada. Reduza-o para 1 em um host com memória limitada, e aumente-o para 3 (ou pré-carregue) quando auto_task_detection, um model= explícito ou um task= explícito também puderem alcançar typed-decisions.

Para um servidor ou uma demo, pré-carregue: um carregamento a frio custa segundos, enquanto a detecção custa microssegundos, então até o padrão ainda paga um carregamento na primeira vez que um idioma aparece.

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

As revisões do Hub são opt-in. revision aplica um commit a todos os modelos; revisions={"english": "...", "multilingual": "..."} sobrescreve isso por modelo, o que é útil quando repositórios independentes foram revisados em commits diferentes. Sem nenhum dos dois, usa o padrão normal do huggingface_hub e o cache offline existente.

Uma entrada de revisions que é None ou em branco significa "sem sobrescritura para este modelo", então o modelo herda revision -- a forma que {"english": os.environ.get("EN_SHA")} escreve quando a variável não está definida, o que não deve custar ao chamador a fixação que ele pediu. Nada em revisions pode desafixar um modelo enquanto revision fixa o resto; deixe revision não definido e nomeie os modelos que você quer fixar.

Um revision em branco é lido da mesma forma, o que é uma mudança deliberada no que o Router repassa: Router(revision=" ") costumava chegar a resolve_revision, onde uma string verdadeira mas em branco suprimia o fallback de LAYA_REVISION e deixava o padrão do huggingface_hub, e agora é descartado antes de chegar lá, então um Router configurado com espaços em branco se comporta como um configurado com nada e $LAYA_REVISION se aplica. É isso que "esta linha de configuração nunca foi preenchida" tem de significar se revision e uma entrada de revisions devem ser lidos da mesma forma.

É um dos dois lugares em que uma fonte mais fraca fica à frente de um argumento explícito. O outro é uma entrada de digest por checkpoint vinda de LAYA_SHA256_DIGESTS, que vence um expected_sha256 passado por agent_kwargs; consulte a docstring da classe.

Qualquer outra coisa que laya.Agent aceite é acessível através de agent_kwargs, que é mesclado em cada checkpoint que o Router constrói:

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 ali fixa os mesmos arquivos em cada checkpoint, que é o que um único checkpoint residente ou um tokenizer.json compartilhado quer. Ele nunca é descartado por completo pelos digests do próprio checkpoint: quando um também tem uma entrada, de sha256_digests ou de um LAYA_SHA256_DIGESTS por checkpoint, os dois mapas são mesclados arquivo por arquivo, então um arquivo que só um deles nomeia ainda é verificado.

Duas exceções, ambas deliberadas e ambas testadas, porque "mesclado arquivo por arquivo" não é a história toda e a diferença é um controle de cadeia de suprimentos:

  • Um LAYA_SHA256_DIGESTS plano -- {artifact: digest} em vez de {model: {...}} -- não é uma camada aqui. O verify_digests o aplica por conta própria, mas apenas quando nada mais fixa (if expected is None), então QUALQUER expected_sha256 que chegue ao Agent, daqui ou de uma entrada de checkpoint, significa que a variável plana não é consultada para aquele carregamento. Verificado em arquivos reais: uma variável plana fixando model.safetensors mais um mapa em agent_kwargs fixando tokenizer.json carrega um model.safetensors adulterado. Use a forma por checkpoint, ou nomeie todos os arquivos que importam em um mapa, se precisar dos dois. Isto não mudou em relação a main.
  • Uma entrada explícita {} ou None MASCARA o que se aplicaria de outra forma -- é o que "carregar este sem verificação" tem de significar, e test_an_explicit_none_entry_masks_a_flat_environment_map fixa isso.

E a precedência é por checkpoint sobre o compartilhado, não importa de onde cada um veio, então uma entrada por checkpoint sintetizada de LAYA_SHA256_DIGESTS vence um expected_sha256 passado aqui no código. Vale dizer com clareza que uma variável de ambiente vence um argumento explícito em um controle como este; test_an_environment_pin_overrides_the_shared_one_per_checkpoint é onde isso é fixado.

Para um arquivo que ambos nomeiam, a entrada por checkpoint vence. As duas não são igualmente específicas: o mapa agent_kwargs alcança todo checkpoint que o Router constrói, e model.safetensors é o único nome que todo checkpoint usa para um arquivo diferente, então uma entrada compartilhada para ele não pode ser uma afirmação correta sobre todos ao mesmo tempo. Nada lança por causa dessa sobreposição -- recusá-la rejeitaria uma fixação compartilhada mais uma sobrescritura por checkpoint, que é a forma comum e que carregava corretamente antes de tudo isto existir. Se você precisa saber contra qual digest um checkpoint foi verificado, leia de volta: o mapa entregue a cada Agent é a mesclagem descrita acima.

Tanto agent_kwargs quanto sha256_digests são públicos e mutáveis, e a entrada de um checkpoint é lida no carregamento e não na construção, então uma fixação atribuída depois -- ou adicionada a uma entrada existente no lugar -- conta.

Os nomes que o Router define para si mesmo -- model_id_or_path, device, token, subfolder, revision e os argumentos de hook -- são recusados aqui em vez de serem sombreados em silêncio, e os nomes restantes são verificados contra Agent.__init__ na construção, então uma opção escrita errado falha na linha Router(...) em vez de na primeira requisição.

Os digests de artefatos são opt-in e sempre por modelo: sha256_digests={"english": {...}} passa esse mapa {path relative to the checkpoint dir: hexdigest} ao Agent que o carrega, então um arquivo de pesos adulterado ou substituído é recusado antes de ser analisado. Não há um equivalente de revision para todo o Router porque os digests, ao contrário de um commit SHA, não são compartilháveis: o repositório empacotado traz um model.safetensors separado para cada um de english, multilingual e typed-decisions, então um único mapa plano só pode corresponder a um deles. Um modelo listado com None ou {} não adiciona arquivos próprios, o que o carrega sem verificação a menos que agent_kwargs["expected_sha256"] o fixe; ele ainda mascara um LAYA_SHA256_DIGESTS plano, que é para o que serve listá-lo assim.

A mesma divisão está disponível para um processo configurado apenas por ambiente: quando LAYA_SHA256_DIGESTS contém um mapa indexado por modelo ({"english": {...}, "multilingual": {...}}) isto o semeia por checkpoint, então um servidor que mantém vários residentes pode fixar cada um com seus próprios digests em vez de se recusar a iniciar no segundo. Um LAYA_SHA256_DIGESTS plano mantém seu significado existente, aplicado por laya.revisions a cada checkpoint que o processo carrega, o que é correto para um de um único checkpoint. Uma entrada de argumento vence o ambiente para o modelo que ela nomeia. Uma variável aninhada que nomeia alguns checkpoints e não outros não diz nada sobre os outros: eles mantêm aquilo com que agent_kwargs["expected_sha256"] os fixa, porque fixar um checkpoint a partir do ambiente não é um pedido para parar de verificar o resto.

Os hooks são opt-in e rodam no nível do Router: on_route vê a decisão de roteamento, on_load / on_evict veem o ciclo de vida do modelo, e on_predict_start / on_predict_end envolvem toda a chamada de route+infer. Consulte 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)

Retorna o Agent para name, baixando-o e construindo-o no primeiro uso.

Chamadores concorrentes compartilham um único Agent em vez de construir duplicatas.

Parâmetros

namestr

attach

attach(name: str, agent: Any)

Registra um Agent já construído sob name em vez de carregar uma segunda cópia.

Útil quando o processo tem um checkpoint carregado por outros motivos: uma demo que já construiu convaiinnovations/laya pode entregá-lo ao router em vez de pagar por -- e manter na memória -- uma duplicata de 421M de parâmetros.

Parâmetros

namestr
agentAny

preload

preload(names: Optional[List[str]] = None)

Baixa e constrói os checkpoints antecipadamente para que nenhuma requisição pague um carregamento de modelo.

Um carregamento a frio custa segundos; a detecção de idioma custa microssegundos. Com cada checkpoint residente, o roteamento é efetivamente grátis -- que é o que você quer em um servidor ou uma demo. max_loaded é elevado para caber tanto os checkpoints solicitados quanto todos os agentes já residentes, então o pré-carregamento incremental não despeja nenhum dos dois.

Parâmetros

namesOptional[List[str]]= None

unload

unload(name: Optional[str] = None)

Libera um modelo, ou todos eles.

Parâmetros

nameOptional[str]= None

loaded_revisions

loaded_revisions: Dict[str, Optional[str]]

Commit SHA do qual cada agente residente foi carregado (None para caminhos locais).

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 qual checkpoint usar e então deixa os hooks on_route observá-lo ou substituí-lo.

ctx.decision é o RouteDecision; um hook pode substituí-lo (por exemplo, para fixar um checkpoint) e a substituição é o que é retornado e usado. hooks são hooks por chamada, anexados depois dos que estiverem instalados no 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]

Roteia e então responde a cada pergunta em uma passagem direta sobre o checkpoint escolhido.

O resultado é o payload habitual de system_one mais uma chave routing que registra a decisão. Os hooks on_predict_start / on_predict_end no nível do Router envolvem toda a chamada de route+infer e veem ctx.decision; consulte laya.hooks. max_len / head_max_len sobrescrevem o orçamento de tokens do agente para esta chamada (um hook de início pode definir 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]

Roteia e então escaneia cada janela do estado em vez de apenas a primeira.

predict pontua um estado a partir de uma única janela: tudo que passa de max_len é cortado (a primeira janela, ou para uma lista de conversa a última) e nunca alcança o modelo. Isto roteia exatamente como predict faz -- as mesmas dicas model/task/lang, os mesmos hooks no nível do router, a mesma chave routing e usage -- e pontua o estado roteado com o predict_long daquele agente, que o divide em janelas sobrepostas e agrega por pergunta. As regras de agregação são as de laya.agent.Agent.predict_long: noul toma a janela mais forte, choice/score a mais confiante.

Os hooks por chamada (hooks, on_predict_start, on_predict_end, hooks_raise, hooks_timeout) envolvem todo o route+scan exatamente como envolvem predict: a varredura roda por último, então vence um hook de início que responde (ctx.skip(...)) ou reescreve o estado. max_len / head_max_len não são aceitos aqui -- uma janela é dimensionada por window ou pelo orçamento do checkpoint, e sobrescrever o truncamento de janela única é para o que serve 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 janela. O padrão é o orçamento do checkpoint roteado (max_len - head_max_len - 8), limitado ao espaço que as perguntas deixam para o estado, para que nenhuma janela seja truncada de novo na entrada; uma janela menor isola um trecho localizado.

strideOptional[int]= None

passo de tokens entre janelas; o padrão é metade da janela efetiva (50% de sobreposição). Um passo além dessa janela é um ValueError, já que os tokens entre as janelas não alcançariam modelo algum.

aggregatestr= "auto"

"auto" (as regras por tipo acima) é o único modo.

batch_sizeOptional[int]= None

limite de janelas por passagem direta, para limitar a memória em estados muito longos.

hooks= None
on_predict_start= None
on_predict_end= None
hooks_raiseOptional[bool]= None
hooks_timeoutOptional[float]= None

Retorna

O payload habitual de predict, com usage["windows"] contando as janelas pontuadas.

Exceções

TypeError: o agente roteado não tem predict_long -- um anexado à mão, já que tanto o Agent quanto o ONNXAgent o implementam -- então não há com o que escanear. Sempre lançado, qualquer que seja o hooks_raise, e o mesmo vale para qualquer outro erro que a própria varredura lance -- nenhum é capturado: a varredura é trabalho deste método, não de um hook do chamador, então a política de erro de hook não decide se ela pode ser pulada. Antes ele rodava como um hook de início, onde hooks_raise=False engolia isto e retornava uma janela pontuada por system_one -- uma pergunta diferente da que foi feita. Um agente cujo predict_long não tem parâmetro lang é varrido sem um e avisado, não falhado; isso é uma verificação de assinatura, não um erro engolido.

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 um schema (JSON schema ou modelo pydantic) e retorna valores tipados.

Consulte laya.structured. Passe exatamente um de schema ou questions; os argumentos de palavra-chave extras (por exemplo model=, task=, hooks=) são repassados 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 muitos estados contra um schema (JSON schema ou modelo pydantic) em uma única chamada em lote.

A forma de rendimento de :meth:decide: o schema é planejado uma vez e suas perguntas rodam sobre cada estado através de :meth:predict_batch (passagens diretas agrupadas, resultados na ordem de entrada), e então as respostas de cada estado são projetadas como decide faz. Os argumentos de palavra-chave extras (batch_size=, model=, hooks=, ...) são repassados a predict_batch. Consulte 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]

Roteia um lote de requisições heterogêneas sem carregar nenhum checkpoint.

Cada requisição é um mapping com state e questions mais as mesmas sobrescrituras de roteamento opcionais aceitas por :meth:route: model, task, lang e lang_guess. As decisões retornadas preservam a ordem de entrada.

Isto é intencionalmente separado da inferência para que os chamadores possam inspecionar ou agregar as decisões de roteamento antes de pagar o custo de carregamento do modelo.

Parâmetros

requestsSequence[Dict[str, Any]]

Sequência de dicionários de requisição, cada um exigindo state e questions.

hooks_timeoutOptional[float]= None

Sobrescreve o hooks_timeout do Router para esta chamada, aplicado ao despacho on_route de cada requisição, como em :meth:route.

hooksHookArg= None

Hook por chamada ou sequência de hooks para esta chamada.

hooks_raiseOptional[bool]= None

Sobrescreve a política hooks_raise do Router para esta chamada.

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]]

Roteia e executa um lote de requisições heterogêneas com o mínimo de troca de modelo.

As requisições são roteadas primeiro e agrupadas por checkpoint. Dentro de cada checkpoint, as requisições que compartilham o mesmo schema de perguntas são passadas a Agent.predict_batch para que seus estados possam compartilhar passagens diretas. Os resultados são então restaurados à ordem original das requisições.

As requisições podem especificar independentemente model, task, lang, lang_guess, max_len ou head_max_len e podem usar schemas de perguntas diferentes. max_len / head_max_len são a forma por requisição da sobrescritura do orçamento de tokens que predict recebe como argumentos de chamada: eles definem os orçamentos de estado e de cabeça de perguntas do checkpoint para aquela única requisição, então uma pergunta ampla pode ser feita sem encolher as outras requisições do lote para a mesma janela. As requisições que pedem orçamentos diferentes são divididas em passagens diretas separadas, já que uma chamada a Agent.predict_batch carrega um único orçamento para todos os seus estados. Um hook de início ainda pode substituir qualquer um dos dois valores em ctx.

Os hooks de predição no nível do Router rodam por requisição, como predict os executa: cada requisição obtém seu próprio PredictContext, então on_predict_start pode substituir o estado, as perguntas ou o orçamento de tokens daquela requisição, ou fazer ctx.skip(...) nela, e on_predict_end vê e pode substituir seu resultado. As requisições são agrupadas para a passagem direta depois que seus hooks de início rodaram, e as requisições de um grupo de checkpoint terminam na ordem inversa à que começaram. Se um grupo de checkpoint falha, toda requisição dele cujo hook de início rodou falha com a exceção, inclusive uma que acertou o cache: cada uma recebe on_error e então on_predict_end antes que a exceção se propague.

Parâmetros

requestsSequence[Dict[str, Any]]

Sequência de dicionários de requisição. Cada item exige state e questions e pode incluir as sobrescrituras de roteamento model, task, lang ou lang_guess e as sobrescrituras do orçamento de tokens max_len / head_max_len.

batch_sizeOptional[int]= None

Número máximo opcional de estados por lote de passagem direta do Agent.

hooks_timeoutOptional[float]= None

Sobrescreve o hooks_timeout do Router para esta chamada.

min_confidenceOptional[float]= None

Float opcional ou mapeamento por bucket para o gating de confiança.

sort_by_lengthbool= False

Repassado a cada chamada a Agent.predict_batch, então cada grupo de perguntas preenche até um máximo mais curto; consulte Agent.predict_batch. Os resultados mantêm a ordem de entrada em qualquer caso. É descartado em silêncio para um agente anexado cujo predict_batch é anterior a esta opção (#294).

hooksHookArg= None

Hook por chamada ou sequência de hooks para esta chamada.

on_predict_startPredictHookArg= None

Callable simples ou sequência de callables para eventos de início.

on_predict_endPredictHookArg= None

Callable simples ou sequência de callables para eventos de fim.

hooks_raiseOptional[bool]= None

Sobrescreve a política hooks_raise do Router para esta chamada.

Retorna

Um resultado normal de predição do Router por requisição, na mesma ordem da entrada.

RouteDecision

RouteDecision()

Classes base: dict

O resultado do roteamento: qual modelo, por quê e o que foi detectado.

Se comporta como um dict, então é serializado diretamente em uma resposta de API.

DEFAULT_MODELS

DEFAULT_MODELS = {
    "english": (BUNDLE_REPO, None),
    "multilingual": (BUNDLE_REPO, "multilingual"),
    "typed-decisions": (BUNDLE_REPO, "typed-decisions"),
}