# How Instatic's AI Agent Integrates with External Providers Without SDKs

> Discover how Instatic's AI agent integrates with external providers via HTTP and adapters, bypassing SDKs for flexible, type-safe LLM communication. Learn more about its provider-agnostic runtime.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: how-to-guide
- Published: 2026-07-26

---

**Instatic's AI subsystem uses a provider-agnostic runtime that communicates with LLM services through plain HTTP calls and shared adapters, completely eliminating vendor SDK dependencies while maintaining type safety through schema validation.**

Instatic, an open-source project by CoreBunch, implements a lightweight AI agent that integrates with external LLM providers like OpenAI and OpenRouter without relying on official SDKs. This architectural approach reduces bundle size, maintains sandbox compatibility, and enables rapid provider expansion through native `fetch` calls and modular driver implementations. The system allows Instatic's AI agent to integrate with external providers without SDKs by delegating all transport logic to a shared **HTTP tool loop** and **Responses Adapter** layer.

## The Three-Layer Provider Architecture

The integration strategy centers on three distinct layers that abstract away vendor specifics while preserving full functionality.

### Common HTTP & Tool Loop

At the heart of the system sits the shared execution layer. The [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts) module owns the agentic loop, handling Server-Sent Events (SSE) parsing, multi-turn tool execution, and error classification. All providers delegate their streaming work to this shared loop via the `runToolLoop` function, ensuring consistent behavior across different LLM backends.

### Responses Adapter

The [`server/ai/drivers/responses-shared.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/responses-shared.ts) module functions as the translation layer between vendor-specific wire protocols and Instatic's internal event system. This **Responses Adapter** isolates provider-specific details such as endpoint URLs, authentication headers, and request payload fields, converting external "Responses" formats (used by OpenAI and OpenRouter) into the internal `AiStreamEvent` shape defined in [`server/ai/runtime/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/runtime/types.ts).

### Provider Drivers

Each LLM service implements a minimal driver that specifies only transport details and model catalogue handling. These drivers never import vendor SDKs, instead relying on native `fetch` for HTTP communication and TypeBox schemas (`parseValue`) for runtime validation. Because the drivers avoid heavyweight dependencies entirely, the runtime remains sandbox-friendly and bundle-efficient.

## Provider Implementation Examples

The actual integration code demonstrates how lightweight these drivers remain without SDK dependencies.

### OpenAI Driver Implementation

Located in [`server/ai/drivers/openai.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/openai.ts), the OpenAI driver defines the transport layer using raw HTTP requests to `https://api.openai.com/v1/responses`:

```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)
  },
}

```

The driver builds required headers manually, defines a stable-hash prompt-cache key, and retrieves the model list from `https://api.openai.com/v1/models` via `fetchOpenAiModels`.

### OpenRouter Driver Implementation

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 retrieves the catalogue from `https://openrouter.ai/api/v1/models`:

```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: …,
  }))
}

```

This driver adds the bearer token to public catalogue requests when present, then delegates streaming to the shared tool loop exactly like the OpenAI implementation.

### Shared Tool Loop Execution

The `runToolLoop` function in [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts) orchestrates the actual HTTP communication and event translation without SDK intermediaries:

```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)
  }
}

```

This function leverages the low-level SSE parser in [`server/ai/drivers/http/sse.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/sse.ts) to handle streaming responses, maintaining backpressure and cancellation support through AsyncIterable patterns.

## Live Model Catalogue Management

Rather than relying on static fallback lists, Instatic fetches model catalogues dynamically. For OpenAI, the driver filters the raw list to include only chat and reasoning models, applying heuristic functions like `deriveLabel` and `deriveTier` to generate user-friendly metadata. OpenRouter's implementation parses capabilities, pricing tiers, and context windows directly from the API response, constructing `AiProviderModel` objects without intermediate SDK transformation layers. Validation occurs at the boundary via `parseValue` TypeBox schemas, ensuring type safety without "as …" casts.

## Summary

Instatic's SDK-free integration strategy delivers several architectural advantages:

- **Zero SDK Dependencies**: Drivers use native `fetch` and manual header construction, avoiding heavyweight vendor libraries
- **Minimal Bundle Size**: Eliminating SDKs reduces deployment footprint and improves load times
- **Sandbox Compatibility**: Pure HTTP implementations avoid native module dependencies that complicate sandboxed environments
- **Rapid Provider Expansion**: New LLM services require only a thin driver implementing the `AiProvider` interface and plugging into `runToolLoop`
- **Runtime Type Safety**: TypeBox schemas (`parseValue`) validate all external data at system boundaries without TypeScript casting

## Frequently Asked Questions

### Why does Instatic avoid official SDKs for LLM integrations?

Official SDKs introduce significant bundle overhead and often require native dependencies that complicate deployment in sandboxed or edge environments. By using native `fetch` and manual protocol handling, Instatic maintains a lightweight runtime that communicates directly with provider HTTP endpoints while retaining full control over request and response transformation via the Responses Adapter.

### How does Instatic maintain type safety without SDK type definitions?

The codebase uses TypeBox schemas accessed via the `parseValue` function to validate all external API responses at runtime. This approach, combined with the strongly-typed `AiProvider` interface defined in [`server/ai/runtime/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/runtime/types.ts), ensures that vendor-specific payloads conform to internal `AiStreamEvent` shapes without relying on SDK-generated types or unsafe casting.

### What is required to add a new LLM provider to Instatic?

Developers must create a driver file similar to [`server/ai/drivers/openai.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/openai.ts) that implements the `AiProvider` interface, specifying the base endpoint, authentication header construction, and model catalogue fetching logic. The driver then delegates streaming requests to the shared `runToolLoop` function with an appropriate adapter from [`server/ai/drivers/responses-shared.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/responses-shared.ts), enabling immediate integration without installing additional dependencies.

### How does streaming work without SDK abstractions?

Streaming relies on the `runToolLoop` generator function in [`server/ai/drivers/http/toolLoop.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/toolLoop.ts), which POSTs requests to provider endpoints and consumes responses using the native `fetch` API. The function pipes the response body through `parseSse` from [`server/ai/drivers/http/sse.ts`](https://github.com/CoreBunch/Instatic/blob/main/server/ai/drivers/http/sse.ts) to handle Server-Sent Events, then translates raw chunks into internal events using the provider-specific adapter, maintaining full control over the byte stream without SDK buffering layers.