Pilot programme targeting Q3 2026. Register your interest now.Pilot EOI
UPAS
Interfaces

Events and Callbacks

Runtime events and callbacks in UPAS

UPAS provides event hooks and callbacks for monitoring and integration.

Model Load Events

Track model loading progress:

import { CreateMLCEngine } from '@mlc-ai/web-llm';

const engine = await CreateMLCEngine(modelId, {
  initProgressCallback: (progress) => {
    console.log('Progress:', progress.progress);
    console.log('Status:', progress.text);
    
    // Update UI
    updateProgressBar(progress.progress);
    updateStatusText(progress.text);
  },
});

Progress Object

interface InitProgress {
  /** Progress value (0 to 1) */
  progress: number;
  
  /** Human-readable status text */
  text: string;
  
  /** Current phase */
  phase?: "download" | "compile" | "init";
}

Service Worker Events

Installation

self.addEventListener('install', (event) => {
  console.log('Service worker installing');
  
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(APP_ASSETS);
    })
  );
});

Activation

self.addEventListener('activate', (event) => {
  console.log('Service worker activating');
  
  event.waitUntil(
    // Clean up old caches
    caches.keys().then((names) => {
      return Promise.all(
        names
          .filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    })
  );
});

Fetch Interception

self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url);
  
  // Log fetch events
  console.log('Fetch:', url.pathname);
  
  event.respondWith(handleFetch(event.request));
});

Cache Events

Cache Update

async function onCacheUpdate(cacheName: string, request: Request) {
  console.log('Cache updated:', cacheName, request.url);
  
  // Notify main thread
  const clients = await self.clients.matchAll();
  clients.forEach((client) => {
    client.postMessage({
      type: 'CACHE_UPDATE',
      cacheName,
      url: request.url,
    });
  });
}

Cache Miss

async function onCacheMiss(request: Request) {
  console.log('Cache miss:', request.url);
  
  // Track for analytics
  incrementMissCounter(request.url);
}

Main Thread Listeners

Service Worker Messages

navigator.serviceWorker.addEventListener('message', (event) => {
  switch (event.data.type) {
    case 'CACHE_UPDATE':
      handleCacheUpdate(event.data);
      break;
    case 'SYNC_COMPLETE':
      handleSyncComplete(event.data);
      break;
  }
});

Controller Change

navigator.serviceWorker.addEventListener('controllerchange', () => {
  console.log('New service worker active');
  
  // Optionally reload for fresh content
  // location.reload();
});

Custom Events

Query Events

// Dispatch custom events for query lifecycle
function onQueryStart(question: string) {
  window.dispatchEvent(new CustomEvent('upas:query:start', {
    detail: { question, timestamp: Date.now() }
  }));
}

function onQueryComplete(response: QueryResponse, durationMs: number) {
  window.dispatchEvent(new CustomEvent('upas:query:complete', {
    detail: { response, durationMs }
  }));
}

// Listen for events
window.addEventListener('upas:query:complete', (event) => {
  const { durationMs } = event.detail;
  console.log('Query completed in', durationMs, 'ms');
});

Sync Events

// Dispatch sync lifecycle events
function onSyncStart() {
  window.dispatchEvent(new CustomEvent('upas:sync:start'));
}

function onSyncComplete(updatedPacks: string[]) {
  window.dispatchEvent(new CustomEvent('upas:sync:complete', {
    detail: { updatedPacks }
  }));
}

function onSyncError(error: Error) {
  window.dispatchEvent(new CustomEvent('upas:sync:error', {
    detail: { error: error.message }
  }));
}

Integration Patterns

React Hook

function useModelStatus() {
  const [status, setStatus] = useState<RuntimeStatus>({
    runtime: 'none',
    modelStatus: 'loading',
  });
  
  useEffect(() => {
    const handleProgress = (event: CustomEvent) => {
      setStatus((prev) => ({
        ...prev,
        loadProgress: event.detail.progress,
      }));
    };
    
    window.addEventListener('upas:model:progress', handleProgress);
    return () => {
      window.removeEventListener('upas:model:progress', handleProgress);
    };
  }, []);
  
  return status;
}

Vue Composable

function useModelStatus() {
  const status = ref<RuntimeStatus>({
    runtime: 'none',
    modelStatus: 'loading',
  });
  
  onMounted(() => {
    window.addEventListener('upas:model:ready', () => {
      status.value.modelStatus = 'ready';
    });
  });
  
  return { status };
}

Next Steps