# How Instatic AI Agent Integration Works with External LLM Providers

> Discover how Instatic AI agent integration connects with external LLM providers using HTTP calls and a shared tool loop. Learn about its provider-agnostic runtime and adapter pattern for seamless service connections.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: internals
- Published: 2026-08-01

---

**Instatic's AI agent integrates with external providers through a provider-agnostic runtime that uses plain HTTP calls instead of vendor SDKs, featuring a shared tool loop and adapter pattern that enables lightweight, sandbox-friendly connections to services like OpenAI and OpenRouter.**

The CoreBunch/Instatic repository implements a unique approach to LLM integration that prioritizes minimal dependencies and maximum flexibility. By avoiding vendor SDKs entirely, the system reduces bundle size and maintains compatibility with sandboxed environments while supporting multiple providers through a unified interface.

## The Three-Layer Architecture

Instatic's AI subsystem organizes provider communication into three distinct layers that separate transport concerns from business logic.

### Common HTTP and Tool Loop

The [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts) module contains the **provider-agnostic runtime** that orchestrates all LLM interactions. This shared loop handles Server-Sent Events (SSE) parsing, multi-turn tool execution, and error classification for every supported provider.

Rather than duplicating streaming logic per vendor, all drivers delegate to `runToolLoop()`, which manages the HTTP POST request, parses the SSE stream, and yields standardized events.

### Responses Adapter

The [`server/ai/drivers/responses-shared.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/responses-shared.ts) file implements the **Responses Adapter** pattern that isolates vendor-specific wire protocols. This adapter translates the "Responses" format used by OpenAI and OpenRouter into Instatic's internal `AiStreamEvent` shape.

It handles endpoint-specific details including URL construction, authentication header formatting, and request payload field mapping. Validation occurs at the boundary using TypeBox schemas (`parseValue`), ensuring type safety without type assertions.

### Provider Drivers

Each external service implements a minimal **provider driver** that specifies only transport details and model catalogue handling. These drivers implement the `AiProvider` interface defined in [`server/ai/runtime/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/runtime/types.ts).

The drivers never import vendor SDKs. Instead, they use native `fetch` to communicate with HTTP endpoints, making the system bundle-friendly and runtime-agnostic.

## Provider Implementation Examples

Instatic currently supports OpenAI and OpenRouter through dedicated driver files that demonstrate the minimal implementation surface required.

### OpenAI Driver

The [`server/ai/drivers/openai.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/openai.ts) file defines the OpenAI integration using direct HTTP calls to `https://api.openai.com/v1/responses` for chat completions and `https://api.openai.com/v1/models` for the model catalogue.

The driver builds required headers, defines a stable-hash prompt-cache key, and forwards streaming requests to the shared tool loop:

```typescript
// server/ai/drivers/openai.ts
export const openaiDriver: AiProvider = {
  id: 'openai' as AiProviderId,
  label: 'OpenAI',
  supportedAuthModes: ['apiKey'],
  capabilities(_modelId) {
    return {
      toolCalling: true,
      visionInput: true,
      toolResultImages: false,
      promptCache: false,
      streaming: true,
    }
  },
  async listModels(creds, signal) {
    return fetchOpenAiModels(creds, signal)   // live catalogue
  },
  async *stream(req) {
    if (req.credentials.authMode !== 'apiKey' || !req.credentials.apiKey) {
      yield { type: 'error', message: 'OpenAI requires an API key …' }
      return
    }
    // Delegate to shared tool loop
    yield* runToolLoop(openaiAdapter, req)
  },
}

```

### OpenRouter Driver

Similarly, [`server/ai/drivers/openrouter.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/openrouter.ts) communicates with `https://openrouter.ai/api/v1/responses` and fetches the model catalogue from `https://openrouter.ai/api/v1/models`.

The driver adds bearer token authentication to catalogue requests and delegates streaming to `runToolLoop()`:

```typescript
// server/ai/drivers/openrouter.ts
async function fetchOpenRouterModels(
  creds: AiResolvedCredential,
  signal?: AbortSignal,
): Promise<AiProviderModel[]> {
  const headers: Record<string, string> = {}
  if (creds.apiKey) headers.Authorization = `Bearer ${creds.apiKey}`

  const res = await fetch(`${OPENROUTER_BASE_URL}/models`, { headers, signal })
  const parsed = parseValue(OpenRouterModelsResponseSchema, await res.json())
  // Build AiProviderModel objects with capabilities, pricing, etc.
  return parsed.data.map(model => ({
    id: model.id,
    label: model.name ?? model.id,
    capabilities: { /* ... */ },
    pricing: /* ... */,
    contextWindow: /* ... */,
  }))
}

```

## The Shared Tool Loop in Action

When a client initiates a request, the flow traverses from the specific driver through the shared infrastructure and back to the client as standardized events.

The `runToolLoop` function in [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts) handles the actual HTTP communication:

```typescript
// server/ai/drivers/http/toolLoop.ts (simplified)
export async function* runToolLoop(
  adapter: ResponsesAdapter,
  req: AiStreamRequest,
): AsyncIterable<AiStreamEvent> {
  // 1️⃣ POST request → SSE stream
  const response = await fetch(adapter.endpoint, {
    method: 'POST',
    headers: adapter.buildHeaders(req),
    body: JSON.stringify(req),
  })
  // 2️⃣ Parse SSE, translate with `adapter.translate` into AiStreamEvent
  for await (const raw of parseSse(response.body)) {
    yield adapter.translate(raw)
  }
}

```

Low-level SSE parsing occurs in [`server/ai/drivers/http/sse.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/sse.ts), which the tool loop consumes to handle streaming responses.

## Live Model Catalogue Management

Instatic fetches model catalogues dynamically rather than using static definitions. Each driver implements custom logic for processing vendor-specific catalogue formats.

For OpenAI, the driver filters the raw list to include only chat and reasoning models, deriving user-friendly labels and tiers heuristically via `deriveLabel` and `deriveTier` functions.

For OpenRouter, `fetchOpenRouterModels` extracts capabilities, pricing, and context windows directly from the API response, building `AiProviderModel` objects that include rich metadata about each available model.

## Summary

- **Instatic AI agent integration** avoids vendor SDKs entirely, using plain HTTP fetch requests to communicate with LLM providers.
- The **three-layer architecture** separates concerns through the shared tool loop ([`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts)), the Responses Adapter ([`server/ai/drivers/responses-shared.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/responses-shared.ts)), and minimal provider drivers.
- Adding new providers requires only implementing the `AiProvider` interface and defining transport details, without pulling in heavy dependencies.
- **TypeBox schemas** validate all data at system boundaries, ensuring runtime type safety without casting.
- The system supports **live model catalogue fetching**, filtering, and metadata extraction for each provider.

## Frequently Asked Questions

### Does Instatic use official SDKs for AI providers?

No. According to the CoreBunch/Instatic source code, the system deliberately avoids vendor SDKs to reduce bundle size and maintain sandbox compatibility. All communication occurs through native `fetch` calls to provider HTTP endpoints, with SSE parsing handled by internal utilities in [`server/ai/drivers/http/sse.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/sse.ts).

### How does the shared tool loop handle streaming?

The `runToolLoop` function in [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts) manages the entire streaming lifecycle. It POSTs the request to the provider endpoint, consumes the response body as a stream, parses Server-Sent Events using `parseSse()`, and translates raw events into `AiStreamEvent` objects through the adapter's `translate` method.

### What is the AiProvider interface?

The `AiProvider` interface, defined in [`server/ai/runtime/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/runtime/types.ts), is the contract that all LLM integrations must implement. It specifies methods for listing models, streaming completions, and reporting capabilities, along with metadata like supported authentication modes and provider labels. Both OpenAI and OpenRouter drivers implement this interface.

### How are new providers added to Instatic?

Developers add new providers by creating a driver file that implements the `AiProvider` interface and plugs into the existing `runToolLoop`. The driver only needs to specify endpoint URLs, header construction, and model catalogue fetching logic. Because the heavy lifting happens in the shared tool loop and adapter layers, new integrations require minimal code and zero additional dependencies.