Store Chat Messages & State Without Managing Infrastructure.Check Out DialogueDB
Skip to content

LLM Executor Hooks

Hooks are available mostly for logging purposes, but can have more advanced use-cases. Hooks are functions you can define which get called at certain stages of the execution. Hooks are optional, and you can register more than one function per hook (meaning there can be many functions listening on the same hook).

The following hooks are available:

  • onSuccess — runs after a successful execution, once the output has been produced
  • onError — runs when the executor throws, before the error is re-thrown
  • onComplete — runs after onSuccess or onError, regardless of outcome

Hook Signature

Every hook receives the same two arguments:

typescript
type Hook = (
  executionMetadata: ExecutorExecutionMetadata,
  executorMetadata: ExecutorMetadata
) => void;

executionMetadata contains information about this specific execution (input, output, timings, and — for onError — the thrown error). executorMetadata contains information about the executor instance itself (id, type, name, total executions).

FieldAvailable onDescription
executionMetadata.inputall hooksThe input passed to .execute()
executionMetadata.outputonSuccess, onCompleteThe parsed output returned by the executor
executionMetadata.handlerInputall hooksThe transformed input passed to the internal handler
executionMetadata.handlerOutputonSuccess, onCompleteThe raw handler output before parsing
executionMetadata.erroronError, onCompleteThe thrown Error instance
executionMetadata.errorMessageonError, onCompleteShortcut for error.message
executionMetadata.startall hooksExecution start time (ms since epoch)
executionMetadata.endonCompleteExecution end time (ms since epoch)
executorMetadata.idall hooksStable id of the executor
executorMetadata.nameall hooksName of the executor, if set
executorMetadata.typeall hooksType of executor (e.g. "llm-executor")
executorMetadata.createdall hooksTimestamp when the executor was created (ms since epoch)
executorMetadata.executionsall hooksNumber of times this executor has run

Hooks should be synchronous and lightweight. Errors thrown inside a hook are caught and logged by llm-exe; they will not affect the executor's result.

You can attach hooks:

  • During initialization
  • After initialization using on / off / once

Adding hooks during initialization

typescript
const hooks = {
  onSuccess: (exec) => console.log("output:", exec.output),
  onError: (exec) => console.error("failed:", exec.errorMessage),
};
const llm = useLlm("openai.chat-mock.v1", {});
const prompt = createChatPrompt("This is a prompt.");
const executor = createLlmExecutor({ llm, prompt }, { hooks });

Adding hooks after initialization

Use .on() to attach a hook, and .off() to remove it.

typescript
function logSuccess(exec) {
  console.log("output:", exec.output);
}

function logError(exec) {
  console.error("failed:", exec.errorMessage);
}

const executor = createLlmExecutor({ llm, prompt });
executor.on("onSuccess", logSuccess);
executor.on("onError", logError);

// Later, detach a specific listener:
executor.off("onSuccess", logSuccess);

Listening exactly once

Use .once() to add a hook that fires on the next execution only and then detaches itself.

typescript
const executor = createLlmExecutor({ llm, prompt });

executor.once("onComplete", (exec) => {
  console.log(`first run took ${exec.end - exec.start}ms`);
});

await executor.execute({});
await executor.execute({}); // the once handler no longer fires

TIP

Default behavior is to not add the same handler to the same hook more than once. There is also a per-event hook limit to prevent unbounded memory growth — if you hit it, remove unused hooks with .off() rather than piling on new ones.

Clearing hooks

Use .clearHooks() to remove all hooks for a specific event, or all events at once. This is useful for preventing memory leaks in long-running processes.

typescript
const executor = createLlmExecutor({ llm, prompt });

// Clear hooks for a specific event
executor.clearHooks("onSuccess");

// Clear all hooks across all events
executor.clearHooks();

Inspecting hook count

Use .getHookCount() to check how many hooks are registered, either for a specific event or across all events.

typescript
const executor = createLlmExecutor({ llm, prompt });

// Get count for a specific event
const count = executor.getHookCount("onSuccess"); // number

// Get counts for all events
const counts = executor.getHookCount(); // { onSuccess: 0, onError: 0, onComplete: 0 }

Trace ID

Use .withTraceId() to associate an executor with a trace identifier for observability. The trace ID is included in executorMetadata passed to hooks.

typescript
const executor = createLlmExecutor({ llm, prompt });

executor.withTraceId("req-abc-123");

executor.on("onComplete", (exec, meta) => {
  console.log(meta.traceId); // "req-abc-123"
});