API リファレンス
API リファレンス
このページのものはすべて laya(よく使う名前)または laya.hooks(全表面)からインポートできます。
from laya import PredictContext, PredictHook, Hook
from laya.hooks import HOOK_EVENTS, normalise_hooks, dispatch, aggregate_usage
PredictContext
PredictContext は公開呼び出しごとに 1 つ作られ、その呼び出しのすべてのフックへ渡されます。唯一の例外は Router.predict_batch で、それはそのリクエストに対する Router.predict 呼び出しがそうするように、リクエストごとに 1 つ作るので、Router レベルのフックはリクエストごとに、それぞれ 1 つの state で発火します。これは可変です。フックは states、questions、results を書き換えられ、on_route は decision を書き換えられます。同一性等価(eq=False)を使うので、コンテキストはハッシュ可能で、2 つのコンテキストが等しくなることはありません。
@dataclass(eq=False)
class PredictContext:
states: List[Any]
questions: Dict[str, Any]
run_id: str = <uuid4 hex>
results: Optional[List[Dict[str, Any]]] = None
decision: Optional[Dict[str, Any]] = None
model: Optional[str] = None
agent: Any = None
router: Any = None
max_len: Optional[int] = None
head_max_len: Optional[int] = None
usage: Optional[Dict[str, int]] = None
started_at: float = <perf_counter()>
elapsed_ms: Optional[float] = None
error: Optional[BaseException] = None
| フィールド | 型 | いつ設定されるか | 可変 | 意味 |
|---|---|---|---|---|
states |
list |
常に | 可(start) | この呼び出しの state。system_one/Router.predict は 1 つ渡し、Agent.predict_batch は多数渡し、Router.predict_batch はリクエストごとに 1 つ渡します。start フックはリストを置き換えられます。 |
questions |
dict |
常に | 可(start) | 質問。start フックは dict を置き換えられます。 |
run_id |
str |
常に | 不可 | この呼び出しのすべてのフックが共有する一意の id。イベントと span の相関に使います。 |
results |
list | None |
終了時(および skip 時) | 可(end) | state ごとの結果 dict。それぞれは system_one の戻り値の形です。推論が終わるまでは None。 |
decision |
dict | None |
Router のみ | 可(route) | チェックポイントを選んだ RouteDecision(dict)。 |
model |
str | None |
常に | 不可 | チェックポイント id。Agent なら Agent.model_id、Router なら解決済みのエイリアス(たとえば "english")。 |
agent |
Agent | ONNXAgent | None |
predict イベント | 不可 | 呼び出しに答えているランタイム。 |
router |
Router | None |
Router イベント | 不可 | Router が関与しているときの Router。 |
max_len |
int | None |
常に | 可(start) | encoder の呼び出しごとのトークン予算。None は agent の config を使います。 |
head_max_len |
int | None |
常に | 可(start) | 質問ヘッドの呼び出しごとのトークン予算。None は agent の config を使います。 |
usage |
dict | None |
終了時 | 可(end) | {"input_tokens", "output_tokens"}。呼び出しの state 全体で合計されます。 |
started_at |
float |
常に | 不可 | 呼び出しが始まったときの time.perf_counter()。 |
elapsed_ms |
float | None |
終了時 | 不可 | 呼び出し全体の実時間(ミリ秒)。 |
error |
BaseException | None |
失敗経路 | 不可 | 例外。on_error と on_predict_end の前に設定されます。 |
PredictContext.skip(results)
推論をショートカットします。on_predict_start から呼ぶと ctx.results を設定してフォワードパスをスキップし、on_predict_end はそれでも走り、渡された結果が返ります。
キーは答えが依存するすべてを覆う必要があり、フックは呼び出しが運ぶすべてを覆う必要があります。フックは呼び出しごとに 1 回発火し、predict_batch はすべての state を一度に渡してそれを呼ぶからです。
CACHE = {}
def key(ctx, index):
# Not sort_keys=True: criteria order is positional, so two orders are two questions,
# and the checkpoint and token budget change the answer too.
payload = json.dumps([ctx.states[index], ctx.questions, ctx.model,
ctx.max_len, ctx.head_max_len], default=str)
return hashlib.sha256(payload.encode()).hexdigest()
def cache_read(ctx):
hits = [CACHE.get(key(ctx, i)) for i in range(len(ctx.states))]
if all(hit is not None for hit in hits):
ctx.skip(hits) # one entry per state, same shape as predict_batch's return
def cache_write(ctx):
for i, result in enumerate(ctx.results or []):
CACHE[key(ctx, i)] = result
laya.load("convaiinnovations/laya", on_predict_start=cache_read, on_predict_end=cache_write)
tests/test_hooks_api.py はこのブロック、examples/hooks/cache.py、および docs/hooks/patterns.md と docs/hooks/examples.md のキャッシュブロックを exec し、4 つすべてが同じキーになることを表明します。だからあるページが、例がとっくに捨てたキーを教えることはありません。
Router では、スキップされたペイロードに routing キーが付加され(すでに持っているものは上書きせず)、Router.predict がその文書化された戻り値の形を保ちます。
Hook プロトコル
Hook は typing.Protocol です。メソッドの任意の部分集合を実装してください。残りはスキップされます。
class Hook(Protocol):
def on_predict_start(self, ctx: PredictContext) -> None: ...
def on_predict_end(self, ctx: PredictContext) -> None: ...
def on_route(self, ctx: PredictContext) -> None: ...
def on_load(self, ctx: PredictContext) -> None: ...
def on_evict(self, ctx: PredictContext) -> None: ...
def on_error(self, ctx: PredictContext) -> None: ...
| イベント | どこで | いつ走るか | 変更できるもの |
|---|---|---|---|
on_predict_start |
Agent, Router | トークン化/フォワードの前 | states、questions、または skip() |
on_predict_end |
Agent, Router | 結果が存在する後(成功でも失敗でも) | results |
on_route |
Router | 検出の後、読み込みの前 | decision |
on_load |
Router | チェックポイントが構築された後 | なし(観察) |
on_evict |
Router | チェックポイントが解放された後 | なし(観察) |
on_error |
Agent, Router | predict 呼び出しが失敗したとき | なし(観察) |
フックは追加の属性やメソッドを自由に定義できます。参照されるのは 6 つのイベント名だけです。フックが 6 つのいずれかを callable でないものとして定義すると、設定は即座に失敗します(検証を参照)。
BaseHook
BaseHook はプロトコルに対する具象の対応物です。すべてのイベントに no-op の本体を持つクラスです。これをサブクラス化し、必要なイベントだけを上書きしてください。
from laya import BaseHook
class Audit(BaseHook):
def on_predict_end(self, ctx):
ship(ctx.run_id, ctx.results)
構造的型付けが欲しい(正しいメソッドを持つ任意のオブジェクト)ときは Hook が最善で、サブクラス化して super() を呼ぶ明示的な基底が欲しいときは BaseHook が最善です。
便利な型
PredictHook = Callable[[PredictContext], None]
PredictHook は on_predict_start= / on_predict_end= で使う素の callable の型です。単一の callable かそのシーケンスを渡します。各々は最小限のフックに包まれます。
実行時登録
すべてのランタイムが HookRegistry をミックスインするので、フックは構築後に追加・削除・スコープできます。変更はスレッドセーフで、呼び出しはリストのスナップショットを読むので、フックの追加や削除が飛行中の呼び出しを乱すことはありません。
agent.add_hook(tracer) # one hook or a sequence; returns self for chaining
agent.remove_hook(tracer) # by identity; True if it was installed
with agent.hooks_installed(debug): # installed for the block, removed on exit
agent.system_one(state, questions)
add_hook は hooks= と同じオブジェクトを受け取ります(素の callable は受け取りません)。hooks_installed は任意個のフックオブジェクトやシーケンスを取り、ブロックが例外を投げた場合も含めて、終了時に以前のリストを復元します。
プロセス全体の既定値
laya.hooks は小さなプロセス全体のレジストリを保つので、tracer、メトリクスフック、テナントタガーをすべての Agent と Router に通す必要がありません。既定は最初に走り、次にインスタンスにインストールされたフック、その後に呼び出しごとのフックが走ります。
from laya import hooks
hooks.set_default_hooks(hooks=[Tracer()]) # replaces the set, accepts the hooks= arguments
hooks.add_default_hook(Metrics()) # appends
hooks.clear_default_hooks() # removes everything
hooks.default_hooks() # a copy of the current list
hooks.compose_hooks(agent.hooks) # defaults + installed (advanced)
既定は Router のライフサイクルイベント on_load と on_evict を含むすべてのイベントに適用されます。レジストリは呼び出し時に読まれるので、Agent や Router が構築された後に設定したフックも依然として適用されます。インスタンスごとのオプトアウトはありません。プロセス全体のセットを切るには clear_default_hooks() を呼んでください。
非同期フック
イベントはコルーチンでもかまいません。フックを AsyncHook で包むと、その async def メソッドは同期のコア内で完走します。
from laya import AsyncHook
class Remote:
async def on_predict_end(self, ctx):
await ship(ctx.results)
agent = laya.load("convaiinnovations/laya", hooks=[AsyncHook(Remote())])
on_predict_start= / on_predict_end= に渡す素の非同期 callable も動きます。dispatch はフックが返す awaitable を実行するからです。
コルーチンがどこで走るか:
- 呼び出しスレッドに走っているループがなければ、
asyncio.runで走ります。 - すでにある場合(非同期関数内の呼び出し元)、専用のバックグラウンドループで走るので、呼び出しスレッドはデッドロックせずにブロックできます。特定のループへ流すには
AsyncHook(hook, loop=...)を渡します。それは走っていて、呼び出しスレッド自身のループであってはなりません。どちらも検査されます。停止したループと呼び出し元自身のループは、永遠にブロックする代わりにそれぞれValueErrorを投げます。
async メソッドを持たないフックは影響を受けません。
設定の表面
すべてのエントリポイントが同じフックパラメータを受け取ります。hooks はオブジェクトかオブジェクトのシーケンスを取り、on_predict_start / on_predict_end は callable かそのシーケンスを取ります。
| パラメータ | 型 | 既定 | 意味 |
|---|---|---|---|
hooks |
Hook | Sequence[Hook] | None |
None |
ライフサイクルフック(6 つのイベントのいずれか)。 |
on_predict_start |
PredictHook | Sequence[PredictHook] | None |
None |
1 つのイベント用の便宜 callable。 |
on_predict_end |
PredictHook | Sequence[PredictHook] | None |
None |
1 つのイベント用の便宜 callable。 |
hooks_raise |
bool |
True |
True:フックの例外が伝播。False:警告して続行。 |
hooks_concurrent |
bool |
True |
False:ロックの下でフックを一度に 1 つずつ dispatch。 |
hooks_timeout |
float | None |
None |
フックごとの秒単位の制限時間。None は制限なし。 |
Agent
Agent(
model_id_or_path="convaiinnovations/laya",
device=None, token=None, subfolder=None, fast=False, compile=False,
hooks=None, on_predict_start=None, on_predict_end=None,
hooks_raise=True, hooks_concurrent=True, hooks_timeout=None,
)
load(..., hooks=None, on_predict_start=None, on_predict_end=None,
hooks_raise=True, hooks_concurrent=True, hooks_timeout=None)
agent.predict_batch(states, questions, batch_size=None,
hooks=None, on_predict_start=None, on_predict_end=None, hooks_raise=None,
hooks_timeout=None, max_len=None, head_max_len=None, sort_by_length=False)
agent.system_one(state, questions,
hooks=None, on_predict_start=None, on_predict_end=None, hooks_raise=None,
hooks_timeout=None, max_len=None, head_max_len=None)
agent.predict_long(state, questions, window=None, stride=None, aggregate="auto",
batch_size=None, lang=None,
hooks=None, on_predict_start=None, on_predict_end=None, hooks_raise=None,
hooks_timeout=None)
agent.predict(...) # alias of system_one
-
呼び出しごとのメソッドの
hooks_raiseとhooks_timeoutは既定でNoneで、「インスタンスの値を使う」という意味です。 -
hooks_concurrentはインスタンスレベルだけです。 -
predict_longでは、フックは state に答える推論を包みます。複数の窓を必要とする文書では、それはそれらに対する単一の共有predict_batchです。on_predict_startは 1 回発火し、ctx.statesは呼び出し元の state(それを生み出すためにトークン化されたもの)ではなく、デコードされた窓テキストをスキャン順に保持します。statesは start フックから可変なので、推論に到達するスキャンはpredict_longが計算した分割である必要はありません。変わるのは答えが主張できることです。start フックがしたこと usage["windows"]answer["window"]ctx.skip([result])で答えた0なし —— どの窓も採点していない 構築されたままのスキャンを残した Nあり —— index、token_start/token_endが決定した span を名指すスキャンを何らかの形で置き換えた 採点された state なし —— オフセットは predict_longの窓を記述し、採点されたテキストではないusage["windows"]はすべての経路で合計で、state がすでに単一の窓に収まった場合も含むので、キャッシュされた答えがモデルの読んだ窓として読まれることはありません。
Router
Router(
models=None, device=None, token=None, max_loaded=2, default="english",
auto_task_detection=False, standalone_repos=False, preload=False, lang_guess=None,
hooks=None, on_predict_start=None, on_predict_end=None,
hooks_raise=True, hooks_concurrent=True,
)
router.route(state, questions=None, model=None, task=None, lang=None, lang_guess=None,
hooks=None, hooks_raise=None)
router.predict(state, questions, model=None, task=None, lang=None, lang_guess=None,
hooks=None, on_predict_start=None, on_predict_end=None, hooks_raise=None,
hooks_timeout=None, max_len=None, head_max_len=None)
router.system_one(...) # alias of predict
router.load(name) # builds on first use; fires on_load
router.preload(names=None) # builds several; fires on_load per build
router.unload(name=None) # frees one or all; fires on_evict
router.attach(name, agent) # registers an existing agent; does not fire on_load
router.loaded # list of resident checkpoint names
routeとpredictの呼び出しごとのhooks=は、on_routeを含む呼び出し全体に適用されます。route()は公開です。それを呼ぶと、インストールされたフックと任意の呼び出しごとのhooksでon_routeを dispatch します。
ONNXAgent
ONNXAgent(model_id_or_path, onnx_path="laya.onnx", subfolder=None,
hooks=None, on_predict_start=None, on_predict_end=None,
hooks_raise=True, hooks_concurrent=True)
onnx_agent.system_one(state, questions,
hooks=None, on_predict_start=None, on_predict_end=None, hooks_raise=None,
hooks_timeout=None, max_len=None, head_max_len=None)
onnx_agent.predict(...) # alias of system_one
ONNXAgent は Router を持たないので、predict レベルのイベントだけを公開します。
イベントのペイロード
イベントとランタイムごとに、どのフィールドが埋まるか:
| イベント | ランタイム | states |
questions |
decision |
model |
agent |
router |
results |
usage |
elapsed_ms |
error |
|---|---|---|---|---|---|---|---|---|---|---|---|
on_predict_start |
Agent | ✓ | ✓ | – | ✓ | ✓ | – | – | – | – | – |
on_predict_start |
Router | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | – | – | – | – |
on_predict_end |
Agent | ✓ | ✓ | – | ✓ | ✓ | – | ✓ | 成功時 | ✓ | 失敗時 |
on_predict_end |
Router | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 成功時 | ✓ | 失敗時 |
on_error |
両方 | ✓ | ✓ | ✓(Router) | ✓ | ✓ | ✓(Router) | – | – | – | ✓ |
on_route |
Router | ✓ | ✓ | ✓ | – | – | ✓ | – | – | – | – |
on_load |
Router | [] |
{} |
– | ✓ | ✓ | ✓ | – | – | – | – |
on_evict |
Router | [] |
{} |
– | ✓ | – | ✓ | – | – | – | – |
タイミングの詳細:
on_predict_endは成功経路でresultsを見ます。失敗経路では、start フックがskip()で設定していない限りresultsはNoneで、usageも同様にNoneです(resultsから導出されるため)。elapsed_msは常に設定されます。on_errorはelapsed_msとusageを計算するfinallyブロックの前に走るので、そこではどちらもNoneです。タイミングと usage は代わりにon_predict_endから読んでください。run_idは常に埋まります。
検証
設定はフックが正規化されるときに検証され、それはインストールされたフックでは構築時に、呼び出しごとのフックでは呼び出し時に行われます。これらは TypeError を投げます。
| ケース | メッセージ |
|---|---|
| インスタンスではなくクラスが渡された | hooks entries must be instances, not classes; ... |
| オブジェクトが 6 つのイベントのどれも実装していない | hooks entries must implement at least one of ... |
| イベント属性が callable でない | hooks entry X.on_predict_start must be callable, got int |
on_predict_start= / on_predict_end= が callable でない |
on_predict_start must be callable, got int |
hooks= は素の callable を受け取りません。素の callable はどのイベント向けかを述べないからです。それらには on_predict_start= / on_predict_end= を使ってください。
高度なヘルパー
これらは内部で使われ、安定していますが、ほとんどの利用者には必要ありません。
HOOK_EVENTS # tuple of the six event names, in dispatch order
normalise_hooks(hooks=None, on_predict_start=None, on_predict_end=None) -> list
dispatch(hooks, event, ctx, *, raise_errors=True, lock=None) -> None
aggregate_usage(results) -> {"input_tokens": int, "output_tokens": int}
dispatch(hooks, event, ctx, *, raise_errors=True, lock=None, timeout=None)
run_coroutine_sync(coro, loop=None)
normalise_hooks は hooks のオブジェクト/シーケンスと 2 つの callable を 1 つの順序付きリストへ平坦化します。dispatch はそれを実装するすべてのフックで event を呼び、raise の方針・ロック・タイムアウトを適用し、フックの結果が awaitable ならそれを実行します。run_coroutine_sync は awaitable を同期コードから完走させ、呼び出し元のループが空いていればそこで、すでに持っていればバックグラウンドループで走らせます。aggregate_usage は state ごとの usage ブロックを合計します。
from laya.hooks import normalise_hooks, dispatch, PredictContext
hooks = normalise_hooks(on_predict_start=[log, redact])
ctx = PredictContext(states=["..."], questions={...})
dispatch(hooks, "on_predict_start", ctx)
関連項目
- ライフサイクル:各イベントがいつ走るか(フローチャート付き)。
- エラー:失敗行列と連鎖の規則。
- パターンとアンチパターン:フックをうまく構造化する方法。
- 例:すべてのユースケース向けのレシピ。