Skip to content

Hooks & Lifecycle

@aeye provides a hook system that lets you intercept, modify, and react to AI operations at every stage.

Hook Types

There are two levels of hooks:

  1. AI-level hooks — fire for every request across all providers
  2. Provider-level hooks — fire only for that specific provider

AI-Level Hooks

Set via ai.withHooks():

typescript
const ai = AI.with<AppContext>()
  .providers({ openai })
  .create()
  .withHooks({
    beforeModelSelection,
    onModelSelected,
    beforeRequest,
    afterRequest,
    onError,
  });

beforeModelSelection

Called before the model registry scores and selects a model. Allows modifying metadata to influence selection.

typescript
beforeModelSelection: async (ctx, request, metadata) => {
  // Force a specific model for certain users
  if (ctx.user.tier === 'premium') {
    return { ...metadata, tier: 'flagship' };
  }
  return metadata;
}

onModelSelected

Called after a model is selected. Allows overriding the selection or modifying the request configuration.

typescript
onModelSelected: async (ctx, request, selected) => {
  console.log(`Selected: ${selected.model.id} from ${selected.provider.name}`);
  // Return a different SelectedModel to override, or void to accept
}

beforeRequest

Called just before the API request is made. Receives estimated usage and cost. Throw to cancel the request.

typescript
beforeRequest: async (ctx, request, selected, estimatedUsage, estimatedCost) => {
  console.log(`Requesting ${selected.model.id}, est. cost: $${estimatedCost.toFixed(4)}`);

  // Cancel if too expensive
  if (estimatedCost > ctx.user.budget) {
    throw new Error('Over budget');
  }
}

afterRequest

Called after a successful response. Receives actual usage and cost.

typescript
afterRequest: async (ctx, request, response, responseComplete, selected, usage, cost) => {
  // Track spending
  await db.logUsage({
    userId: ctx.userId,
    model: selected.model.id,
    tokens: usage.text?.input ?? 0 + (usage.text?.output ?? 0),
    cost,
  });
}

The response parameter is the raw provider response. responseComplete is the normalized Response object.

onError

Called when any error occurs during an AI operation.

typescript
onError: (errorType, message, error, ctx, request) => {
  // errorType examples:
  // 'no-model-found', 'chat-failed', 'chat-stream-failed',
  // 'image-generate-failed', 'speech-failed', etc.

  logger.error(`AI Error [${errorType}]: ${message}`, error);
}

Provider-Level Hooks

Each provider has its own hook system that fires for operations on that provider only:

typescript
const openai = new OpenAIProvider({
  apiKey: '...',
  hooks: {
    chat: {
      beforeRequest: (request, params, ctx, metadata) => { /* ... */ },
      afterRequest: (request, response, responseComplete, ctx, metadata) => { /* ... */ },
      onError: (request, params, error, ctx, metadata) => { /* ... */ },
    },
    // imageGenerate, embed, etc. also available
  },
});

Provider hooks receive provider-specific types (e.g., OpenAI SDK types) rather than normalized @aeye types.

Hook Execution Order

For a typical chat request:

1. beforeModelSelection  (AI hook)
2. Model scoring & selection
3. onModelSelected       (AI hook)
4. beforeRequest         (AI hook)
5. Provider beforeRequest (provider hook)
6. API call
7. Provider afterRequest  (provider hook)
8. afterRequest          (AI hook)

If an error occurs at any step:

→ Provider onError (if provider step)
→ onError (AI hook)

Component-Level Events

Tools, Prompts, and Agents emit events via the Instance system:

typescript
import { withEvents } from '@aeye/core';

const events = {
  onStatus: (instance) => {
    console.log(`${instance.component.name}: ${instance.status}`);
  },
  onChild: (parent, child) => {
    console.log(`${parent.component.name} spawned ${child.component.name}`);
  },
  onPromptEvent: (instance, event) => {
    console.log(`Prompt event: ${event.type}`);
  },
};

const runner = withEvents(events);
const result = await myAgent.run(input, { runner });

See Prompt Events for the full list of prompt events.

Released under the GPL-3.0 License.