Skip to content

Feature: Integrate Barewire for enhanced agent observability and cloud LLM proxying - #14

Open
sh8kme wants to merge 1 commit into
ykhli:mainfrom
sh8kme:add-barewire-integration
Open

Feature: Integrate Barewire for enhanced agent observability and cloud LLM proxying#14
sh8kme wants to merge 1 commit into
ykhli:mainfrom
sh8kme:add-barewire-integration

Conversation

@sh8kme

@sh8kme sh8kme commented Jul 11, 2026

Copy link
Copy Markdown

Hello there!

This PR introduces Barewire integration, significantly enhancing the local-ai-stack template for developers building agentic AI applications. While the current setup excels at 100% local operation with Ollama, agent development often requires transitioning to more robust cloud LLMs and gaining better control and observability over LLM interactions. Barewire provides exactly that.

Key Benefits of this Integration:

  1. Seamless Upgrade Path for Cloud LLMs: This integration makes it easy to switch from a purely local Ollama setup to using cloud-based LLMs like OpenAI, while automatically routing these calls through Barewire.
  2. Agentic Observability: Barewire acts as an intelligent proxy, providing full visibility into your agent's LLM calls, including prompts, responses, token usage, latency, and errors. This is crucial for debugging, optimizing, and understanding complex agent behaviors.
  3. Future-Proofing for Agent Developers: For developers looking to build and deploy advanced AI agents, Barewire offers features like caching, retries, and routing, which can be configured centrally to improve performance, reliability, and cost-efficiency.
  4. No Impact on Local Setup: The Barewire integration is entirely optional and configurable via environment variables. Your 100% local Ollama setup remains the default, ensuring the core promise of the local-ai-stack is untouched.

Technical Changes:

  • New File src/lib/llm-clients.ts (created by this PR): This centralizes LLM client instantiation. It exports getOllamaClient (for your existing local setup) and getOpenAIClient. The getOpenAIClient function now conditionally configures the OpenAI client to use BAREWIRE_BASE_URL and BAREWIRE_API_KEY if provided, effectively proxying all OpenAI requests through Barewire.
  • .env.local.example (requires update): New environment variables OPENAI_API_KEY, OPENAI_MODEL, USE_OPENAI_FOR_CHAT, BAREWIRE_API_KEY, and BAREWIRE_BASE_URL are added. This allows users to easily configure and enable the cloud LLM and Barewire integration.
  • src/app/api/chat/route.ts (requires modification): The main chat API route needs to be updated to import getOllamaClient and getOpenAIClient from src/lib/llm-clients.ts. It can then use the USE_OPENAI_FOR_CHAT environment variable to dynamically switch between the local Ollama LLM and the Barewire-proxied OpenAI LLM. An example snippet for modifying src/app/api/chat/route.ts is provided below.

To Enable & Test:

  1. Update .env.local.example and create/update .env.local with your OPENAI_API_KEY and Barewire credentials (BAREWIRE_API_KEY, BAREWIRE_BASE_URL). Set USE_OPENAI_FOR_CHAT=true.

  2. Modify src/app/api/chat/route.ts to leverage the new llm-clients.ts:

    // src/app/api/chat/route.ts
    
    import { StreamingTextResponse, LangChainStream, Message } from "ai";
    import { CallbackManager } from "langchain/callbacks";
    import { ConversationalRetrievalChain } from "langchain/chains";
    import { BufferMemory } from "langchain/memory";
    import { SupabaseVectorStore } from "langchain/vectorstores/supabase";
    import { PromptTemplate } from "langchain/prompts";
    import { createClient } from "@supabase/supabase-js";
    import { pipeline, env } from '@xenova/transformers';
    
    // --- NEW IMPORT ---
    import { getOllamaClient, getOpenAIClient } from "@/lib/llm-clients";
    // ------------------
    
    export const runtime = 'edge';
    
    // ... (Your existing PROMPT_TEMPLATEs and getEmbeddingsGenerator function)
    
    export async function POST(req: Request) {
      const { messages } = await req.json();
      const currentMessageContent = messages[messages.length - 1].content;
    
      // --- NEW LLM SELECTION LOGIC ---
      const USE_OPENAI = process.env.USE_OPENAI_FOR_CHAT === 'true';
      let llm;
      if (USE_OPENAI) {
        llm = getOpenAIClient();
        if (!llm) {
          return new Response("OpenAI client not initialized. Check OPENAI_API_KEY.", { status: 500 });
        }
      } else {
        llm = getOllamaClient();
      }
      // --------------------------------
    
      // ... (Rest of your existing Supabase and vector store setup)
      const privateKey = process.env.SUPABASE_PRIVATE_KEY;
      if (!privateKey) { throw new Error(`Expected env var SUPABASE_PRIVATE_KEY`); }
      const url = process.env.SUPABASE_URL;
      if (!url) { throw new Error(`Expected env var SUPABASE_URL`); }
      const client = createClient(url, privateKey);
      
      const localEmbeddings = await getEmbeddingsGenerator();
    
      const vectorStore = await SupabaseVectorStore.fromExistingIndex(
        localEmbeddings,
        {
          client,
          tableName: "documents",
          queryName: "match_documents",
        }
      );
    
      const { stream, handlers } = LangChainStream();
    
      const chain = ConversationalRetrievalChain.fromLLM(
        llm, // Uses the selected LLM
        vectorStore.asRetriever(),
        {
          qaChainOptions: {
            prompt: QA_PROMPT,
          },
          questionGeneratorChainOptions: {
            llm, // Uses the selected LLM for question generation
            prompt: CONDENSE_PROMPT,
          },
          memory: new BufferMemory({
            memoryKey: "chat_history",
            inputKey: "question",
            outputKey: "text",
            returnMessages: true,
          }),
          returnSourceDocuments: true,
          callbackManager: CallbackManager.fromHandlers(handlers),
        }
      );
    
      const chatHistory = messages.slice(0, -1).map((m: Message) => `${m.role}: ${m.content}`).join("\n");
    
      chain.call({
        question: currentMessageContent,
        chat_history: chatHistory,
      }).catch(console.error);
    
      return new StreamingTextResponse(stream);
    }

This makes the local-ai-stack an even more versatile starting point for agent developers, offering a clear path from local experimentation to observable, production-ready deployments. Let me know your thoughts!

Best regards,
[Your Name/Handle]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant