cd /news/ai-tools/lean-ai-in-langbear · home topics ai-tools article
[ARTICLE · art-21638] src=dev.to pub= topic=ai-tools verified=true sentiment=↑ positive

Lean AI in LangBear

LangBear, a fully bootstrapped application, has developed a lightweight, provider-agnostic AI interface to avoid the maintenance burden and dependency issues of heavier frameworks like LangChain.js. The custom interface, defined by a simple TypeScript type, allows the app to switch between AI providers such as OpenAI, Gemini, Anthropic, and OpenRouter in a single commit. This lean approach enables flexible migration to cheaper AI options without sacrificing functionality.

read2 min publishedJun 4, 2026

LangBear is fully bootstrapped. This means no investors' money to burn, no big budgets. All funding has been coming from my main job's income. This is why I made a decision early on to stay AI provider-agnostic as much as possible to have the flexibility to migrate to cheaper options.

At first, I tried LangChain.js. It was too heavy and was burdensome to maintain. Too many dependencies. The API wasn't stable and oftentimes broke when I had to bump the version for security fixes.

I decided to write a very lightweight interface that would satisfy all use cases of the app.

I present it to you with all its glory as it is today:

import type z from "zod/v3";

export type AIProvider = {

  generateText<T extends z.ZodTypeAny>(
    prompt: string,
    schema: T,
    options?: AIProviderGenerateTextOptions,
  ): Promise<z.infer<T>>;

  transcribeAudio(filePath: string): Promise<string>;
};

export type AIProviderThinkingConfig =
  | {
      // 0 disables thinking, -1 lets the provider choose automatically
      thinkingBudget: 0 | -1;
      thinkingLevel?: never;
    }
  | {
      thinkingBudget?: never;
      thinkingLevel: "MINIMAL" | "LOW" | "MEDIUM" | "HIGH";
    };

export type AIProviderGenerateTextOptions = {
  model?: string;
  thinkingConfig?: AIProviderThinkingConfig;
};

So far, I've already tried OpenAI, Gemini, Anthropic, OpenRouter and switching between providers only takes 1 commit.

In the code, you would use it like this:

function translateText(
  aiProvider: AIProvider,
  model: string,
  text: string,
  targetLang: string,
): Promise<string> {
  const { translatedText } = await aiProvider.generateText(
    getTranslateTextPrompt(text, targetLang),
    z.object({ translatedText: z.string() }),
    {
      model,
    },
  );

  return translatedText;
}

await translateText(geminiProvider, "gemini-3.5-flash", "hej", "en")
// hello

Implementation of this interface is trivial. Here's a partial implementation for the Gemini provider for generateText

function:

async function generateText<T extends z.ZodTypeAny>(
  prompt: string,
  schema: T,
  { geminiApiKey, model, thinkingConfig }: GenerateTextOptions,
): Promise<z.infer<T>> {
  const gemini = new GoogleGenAI({
    apiKey: geminiApiKey,
  });

  const config: GenerateContentConfig = {
    responseMimeType: "application/json",
    responseJsonSchema: zodToJsonSchema(schema),
  };
  const geminiThinkingConfig = toGeminiThinkingConfig(thinkingConfig);

  if (geminiThinkingConfig) {
    config.thinkingConfig = geminiThinkingConfig;
  }

  const response = await gemini.models.generateContent({
    model,
    contents: prompt,
    config,
  });

  if (!response.text) {
    throw new Error("no content");
  }

  return schema.parse(JSON.parse(response.text));
}
── more in #ai-tools 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/lean-ai-in-langbear] indexed:0 read:2min 2026-06-04 ·