NOUKAI

Integration

Protocol for adding Noukai API calls alongside existing code with an environment variable switch.

Entry condition: Only proceed here after the user has tested their Noukai flows and explicitly said "implement", "continue", or otherwise signaled readiness. You should have a list of created flows with their slugs and API URLs.

Integration Principles

These are hard constraints — follow them exactly:

  1. Never delete existing code. The original LangChain or OpenRouter implementation stays intact.
  2. Side-by-side execution. The Noukai call is added alongside the original, controlled by an environment variable.
  3. Adapt to the user's codebase. Before writing any integration code, examine how the user organizes their code. Follow their patterns — if they use dependency injection, use that. If they use a service layer, add the Noukai client there. If the code is a flat script, keep it flat.
  4. No Noukai SDK required. Use HTTP calls to the slug execution API. If a Noukai SDK exists in the user's dependencies, use it instead.
  5. One env var controls the switch. USE_NOUKAI=true enables the Noukai path. Default is false (original behavior preserved).

Integration Protocol

Analyze the Codebase Structure

Before writing any code, understand how the user organizes their project:

  • Directory structure — where do API clients, services, or utility modules live?
  • Patterns in use — dependency injection, service classes, functional composition, flat scripts?
  • HTTP client — do they use requests, httpx, aiohttp, fetch, axios, or something else?
  • Async or sync — is the codebase async-first or synchronous?
  • Config pattern — how do they load environment variables? (dotenv, pydantic-settings, process.env, config files?)
  • Error handling — how do they handle API errors? (try/except, error boundaries, Result types?)

Match all of these in your integration code.

Add Environment Variables

Add to the user's .env (and .env.example if it exists):

USE_NOUKAI=false
NOUKAI_API_KEY=nk_live_...
NOUKAI_API_URL=https://api.noukai.xyz

If the user already has NOUKAI_API_KEY and NOUKAI_API_URL set (from the Create & Test phase), only add USE_NOUKAI.

Create a Noukai Client Module

Create a single module that handles all Noukai API calls. Place it where the user keeps their API clients or utility modules.

The module should:

  • Accept a flow slug and input data
  • Make an HTTP POST to the slug execution endpoint
  • Return the parsed result
  • Handle errors gracefully (timeout, 4xx, 5xx)
  • Use the user's existing HTTP client library

Example shape (adapt to the user's language, patterns, and style):

# Python example — adapt to match codebase conventions
import os
import httpx
 
NOUKAI_API_URL = os.environ["NOUKAI_API_URL"]
NOUKAI_API_KEY = os.environ["NOUKAI_API_KEY"]
 
async def call_noukai_flow(
    org_slug: str,
    project_slug: str,
    flow_slug: str,
    message: str,
    parameters: dict | None = None,
) -> dict:
    url = f"{NOUKAI_API_URL}/api/v1/seq/{org_slug}/{project_slug}/{flow_slug}/execute"
    async with httpx.AsyncClient() as client:
        response = await client.post(
            url,
            headers={
                "Authorization": f"Bearer {NOUKAI_API_KEY}",
                "Content-Type": "application/json",
            },
            json={"message": message, "parameters": parameters or {}},
            timeout=30.0,
        )
        response.raise_for_status()
        return response.json()["result"]
// TypeScript example — adapt to match codebase conventions
const NOUKAI_API_URL = process.env.NOUKAI_API_URL!;
const NOUKAI_API_KEY = process.env.NOUKAI_API_KEY!;
 
export async function callNoukaiFlow(
  orgSlug: string,
  projectSlug: string,
  flowSlug: string,
  message: string,
  parameters: Record<string, unknown> = {},
): Promise<unknown> {
  const url = `${NOUKAI_API_URL}/api/v1/seq/${orgSlug}/${projectSlug}/${flowSlug}/execute`;
  const response = await fetch(url, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${NOUKAI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ message, parameters }),
  });
 
  if (!response.ok) {
    throw new Error(`Noukai API error: ${response.status} ${response.statusText}`);
  }
 
  const data = await response.json();
  return data.result;
}

These are reference shapes only. The actual implementation must match the user's conventions:

  • If they use classes, wrap it in a class
  • If they use a shared HTTP client instance, use that
  • If they have a custom error type, use that
  • If they use TypeScript strict mode, add proper types

Add the Switch to Each Call Site

For each chain that was migrated to a Noukai flow, add a switch at the call site.

The pattern:

# Python
import os
 
USE_NOUKAI = os.environ.get("USE_NOUKAI", "false").lower() == "true"
 
async def classify_intent(message: str) -> dict:
    if USE_NOUKAI:
        return await call_noukai_flow(
            org_slug="acme-corp",
            project_slug="support-bot",
            flow_slug="classify-intent",
            message=message,
        )
 
    # --- Original implementation below (unchanged) ---
    chain = prompt | llm | parser
    return await chain.ainvoke({"message": message})
// TypeScript
const USE_NOUKAI = process.env.USE_NOUKAI === "true";
 
export async function classifyIntent(message: string) {
  if (USE_NOUKAI) {
    return callNoukaiFlow(
      "acme-corp",
      "support-bot",
      "classify-intent",
      message,
    );
  }
 
  // --- Original implementation below (unchanged) ---
  const chain = prompt.pipe(llm).pipe(parser);
  return chain.invoke({ message });
}

Key rules for the switch:

  • The if USE_NOUKAI branch goes first (at the top)
  • The original code stays exactly as-is below the switch
  • If the original code has side effects (logging, metrics), preserve them in both branches
  • If return types differ between the original and Noukai, add a mapping in the Noukai branch to match the original's return shape so callers don't break

Verify the Integration

After adding all switches:

  1. Run existing tests with USE_NOUKAI=false — everything should pass unchanged
  2. Run existing tests with USE_NOUKAI=true — verify Noukai responses are compatible
  3. If tests fail with USE_NOUKAI=true, check:
    • Return shape mismatch → add a mapping layer in the Noukai branch
    • Missing fields → update the Noukai flow's output schema
    • Timeout → increase the HTTP client timeout

Present the results to the user:

"Integration complete. All {N} call sites now have a Noukai switch controlled by USE_NOUKAI.

  • USE_NOUKAI=false (default): original implementation, no behavior change
  • USE_NOUKAI=true: routes through Noukai flows

Existing tests pass with USE_NOUKAI=false. {Results of USE_NOUKAI=true tests if run.}

To go live with Noukai, set USE_NOUKAI=true in your production environment. You can switch back at any time by setting it to false."

What Comes Next (User-Driven)

Once the user is confident in the Noukai path:

  • Remove the switch — delete the if USE_NOUKAI branches and the old implementation
  • Remove LangChain dependencies — uninstall langchain, langchain-core, etc. from dependencies
  • Clean up — remove the USE_NOUKAI env var

These steps are the user's decision. Do not automate them unless explicitly asked.

On this page