Jev + companyfabric: How to Build Faster, More Predictable AI Workflows

It sounds cliché, but most AI products don't have a model problem. They have a workflow problem.

A team receives a support ticket. A payment needs a risk decision. An agent has to choose between editing a file, running tests, or asking a larger model for help. In all three cases, people often send the entire task to a powerful chat model.

That works. It also means paying for more latency, more generated text, and more parsing than the workflow actually needs.

The interesting alternative is to split the job in two:

  • Jev makes fast, typed decisions.
  • companyfabric gives the application one gateway to several general-purpose AI models.

This isn't a claim that one service replaces the other. It is a design pattern: use a small, constrained decision contract for routine choices, then use a general-purpose model only when the workflow needs explanation, writing, coding, or deeper reasoning.

The examples below use the official TypeSafe JavaScript SDK shape and the OpenAI-compatible companyfabric client pattern. Check the linked documentation before deploying because SDKs and model names can change.


Table of contents

  1. The problem with using one model for everything
  2. What Jev actually returns
  3. What companyfabric contributes
  4. The architecture: System 1 plus System 2
  5. Example one: support ticket routing
  6. Example two: fraud scoring
  7. Example three: agent control loop
  8. How to test it without API keys
  9. What we learned
  10. What's next?

1. The problem with using one model for everything

A conversational LLM is excellent when the output is open-ended. You want an explanation, a draft email, a refactor plan, or a piece of code. The model has room to generate language.

But many application decisions are not open-ended. The application needs one category, one route, one Boolean probability, or one position on a defined scale.

If the application asks a chat model to return JSON, the workflow still has to parse the text, validate it, handle malformed output, and decide what a confidence-looking number means. That is a lot of machinery around a small decision.

Jev takes a different approach. You define the possible question type and the criteria. Jev returns typed answers that your code can threshold and log.

The important design decision isn't "Which model answers everything?" It is "Which part of this workflow needs language, and which part needs a reliable decision?"


2. What Jev actually returns

TypeSafe's JavaScript SDK is installed with @typesafe-ai/sdk. Its quickstart creates a TypeSafeClient, calls systemOne, and lets TypeScript infer answer types from the questions.

Jev supports three useful primitives:

  • Noul: a probability that a condition is true. It returns noul; it does not return a separate confidence field.
  • Choice: one option from a labelled set. It returns the selected choice, confidence, and probabilities.
  • Score: a probability-weighted position over an ordered list of labelled levels. The score can be fractional and the levels are zero-indexed. A score rubric contains between two and ten levels.

That last point matters. If your score levels are No signal, Frustrated, and Threatens to cancel, the returned value is in the range 0..2. It is not a conventional "1 to 5" score unless you deliberately define five levels.

A Noul value close to 0.5 means the model sees similar probability for true and false. It does not mean "medium intensity." For actions with consequences, we fail closed: uncertain cases go to a human or to a safer fallback.

The official JavaScript example looks like this:

import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient();

const response = await client.systemOne({
  state: { document: "I was charged twice. Please fix this ASAP." },
  questions: {
    category: choice("What is this ticket about?", {
      billing: null,
      technical: null,
      other: null,
    }),
  },
});

console.log(response.answers.category.choice);

The SDK's type inference is not decoration. It is one of the reasons to use the SDK instead of hiding every answer behind any.

Relevant links:


3. What companyfabric contributes

companyfabric is a unified AI gateway. Its website describes one subscription and one API for models from providers including Anthropic, OpenAI, Google, DeepSeek, xAI, Z.ai, and Moonshot.

The practical value is not that companyfabric turns every model into the same model. The value is that an application can use one integration point for general-purpose calls, routing, and usage accounting.

The documented integration pattern is compatible with the OpenAI SDK:

import OpenAI from "openai";

const cf = new OpenAI({
  baseURL: "https://api.companyfabric.com/v1",
  apiKey: process.env.COMPANYFABRIC_API_KEY,
});

For non-streaming responses, inspect usage.cost_usd. Also capture x-fabric-request-id and x-fabric-served-model from the response headers when your client exposes them. Those headers tell you which request was handled and which model the gateway served.

companyfabric's MCP server should be described carefully. Its tools are for searching the model catalogue, quoting prices, and checking usage. That lets an agent inspect available models, estimate a request before running it, and monitor spend. It does not mean that MCP silently selects the perfect model for every request.

The most useful split is simple: Jev decides what should happen; companyfabric helps produce the words, code, or explanation when the workflow needs them.

Relevant links:


4. The architecture: System 1 plus System 2

Here is the flow we use in production-style code:

  1. The application receives an event.
  2. Jev evaluates several typed questions in one call.
  3. The application applies explicit thresholds and safety rules.
  4. Deterministic code or an internal tool handles routine paths.
  5. companyfabric is called only when the workflow needs natural-language reasoning or generation.

This is not magic. A threshold is a product decision, not a model guarantee. You still need monitoring, human review, rate limits, and tests.

The speed and cost claims should also be kept in context. OpenRouter's launch post describes up to 190× faster and 440× cheaper on TypeSafe's published workflows. Those are attributed workflow claims, not a promise that Jev is 190× faster than every version of every general-purpose model on every task.

Relevant links:


5. Example one: support ticket routing

Imagine a customer writes:

"Second time this month my card was charged twice. I need a refund now or I cancel."

The application doesn't need a paragraph first. It needs to know whether the case is urgent, what category it belongs to, whether it should be escalated, and whether there is a churn signal.

Production TypeScript

import OpenAI from "openai";
import {
  TypeSafeClient,
  noul,
  choice,
  score,
} from "@typesafe-ai/sdk";

const cf = new OpenAI({
  baseURL: "https://api.companyfabric.com/v1",
  apiKey: process.env.COMPANYFABRIC_API_KEY,
});

const ts = new TypeSafeClient();
const JEV_USD_PER_MTOK = 0.042;

export async function handleSupportTicket(
  ticketId: string,
  message: string,
) {
  const { answers, usage } = await ts.systemOne({
    state: { ticketId, message },
    questions: {
      urgent: noul("Does this need a reply within the hour?", {
        true: "Blocked, money lost, or a stated deadline",
        false: "Can wait a business day",
      }),
      category: choice("What is this ticket about?", {
        billing: "Charges, invoices, refunds",
        technical: "Bugs, errors, outages",
        account: "Login, access, settings",
        other: null,
      }),
      escalate: noul(
        "Should a human handle this instead of an automated reply?",
      ),
      churnRisk: score("How likely is this customer to leave?", [
        "No signal",
        "Frustrated",
        "Threatens to cancel",
      ]),
    },
  });

  let reply: string | null = null;
  let replyCostUsd: number | null = null;

  // A Noul close to 0.5 is uncertain. Fail closed.
  if (answers.escalate.noul < 0.4) {
    const response = await cf.chat.completions.create({
      model: "companyfabric/cheap",
      max_tokens: 256,
      messages: [
        { role: "system", content: "You are a concise support agent." },
        {
          role: "user",
          content: [
            `Ticket ${ticketId}: ${message}`,
            `Category: ${answers.category.choice}`,
          ].join("\n"),
        },
      ],
    });

    reply = response.choices[0].message.content ?? "";
    replyCostUsd = response.usage?.cost_usd ?? null;
  }

  return {
    ticketId,
    category: answers.category.choice,
    categoryConfidence: answers.category.confidence,
    urgent: answers.urgent.noul,
    escalate: answers.escalate.noul,
    churnRisk: answers.churnRisk.score,
    reply,
    cost: {
      jevUsd: (usage.input_tokens * JEV_USD_PER_MTOK) / 1_000_000,
      replyUsd: replyCostUsd,
    },
  };
}

What happens

  1. Jev returns category = billing and a high urgency probability.
  2. If escalation is above the chosen threshold, the application creates a human task and does not draft an automated answer.
  3. If escalation is low, companyfabric generates a concise reply.
  4. The application logs Jev input-token cost and companyfabric's cost_usd when available.

Notice what we removed from the earlier version: confidence on Noul, options, scale, and as any casts. The question helpers define the contract.

Relevant links:


6. Example two: fraud scoring

Fraud systems need a decision quickly, but analysts also need an explanation. Those are different outputs.

Jev can return a fraud probability, a risk position, and an action. companyfabric can explain a borderline decision for an internal dashboard.

import OpenAI from "openai";
import { TypeSafeClient, noul, choice, score } from "@typesafe-ai/sdk";

const ts = new TypeSafeClient();
const cf = new OpenAI({
  baseURL: "https://api.companyfabric.com/v1",
  apiKey: process.env.COMPANYFABRIC_API_KEY,
});

export async function evaluateTransaction(transaction: {
  id: string;
  amountUsd: number;
  country: string;
  velocity24h: number;
  ipRiskScore: number;
}) {
  const { answers } = await ts.systemOne({
    state: transaction,
    questions: {
      likelyFraud: noul("Is this transaction likely fraudulent?"),
      risk: score("How severe is the transaction risk?", [
        "Low",
        "Moderate",
        "High",
        "Critical",
      ]),
      action: choice("What action should the payment system take?", {
        allow: "Approve the transaction",
        challenge3ds: "Require a 3-D Secure challenge",
        block: "Decline the transaction",
      }),
    },
  });

  const needsExplanation =
    answers.action.choice === "challenge3ds" ||
    answers.risk.score >= 1.5;

  let explanation: string | null = null;
  let explanationCostUsd: number | null = null;

  if (needsExplanation) {
    const response = await cf.chat.completions.create({
      model: "companyfabric/auto",
      max_tokens: 220,
      messages: [
        {
          role: "system",
          content:
            "Explain a fraud decision in two sentences for an internal analyst.",
        },
        {
          role: "user",
          content: JSON.stringify({ transaction, answers }),
        },
      ],
    });

    explanation = response.choices[0].message.content ?? "";
    explanationCostUsd = response.usage?.cost_usd ?? null;
  }

  return {
    action: answers.action.choice,
    fraudProbability: answers.likelyFraud.noul,
    risk: answers.risk.score,
    explanation,
    explanationCostUsd,
  };
}

The score is expected value over the ordered labels. If the result is 1.8, that means the probability mass sits between "Moderate" and "High." It is not a claim that a human-filled 1-to-5 form was completed.

For a payment decision, don't let one model output become the only control. Combine it with deterministic velocity rules, account history, sanctions checks, and a review path.

Relevant links:


7. Example three: agent control loop

The third example is closer to the work many developers are doing today: an agent has to choose its next action.

The task might require a file search, an edit, a test run, or a larger reasoning pass. Jev can classify that step. companyfabric can provide the plan or final explanation.

import OpenAI from "openai";
import { TypeSafeClient, noul, choice } from "@typesafe-ai/sdk";

const ts = new TypeSafeClient();
const cf = new OpenAI({
  baseURL: "https://api.companyfabric.com/v1",
  apiKey: process.env.COMPANYFABRIC_API_KEY,
});

export async function runAgentStep(state: {
  task: string;
  context: string;
}) {
  const { answers } = await ts.systemOne({
    state,
    questions: {
      needsTool: noul("Does this step require an external tool?"),
      tool: choice("Which tool is most appropriate?", {
        editFile: "Modify one or more files",
        runTests: "Run the test suite",
        searchCodebase: "Search files or repository history",
        none: "No tool is needed",
      }),
      needsDeepReasoning: noul(
        "Should this step be handed to a general-purpose model for deeper reasoning?",
      ),
    },
  });

  // Probabilities need thresholds. `!noul` is not a Boolean decision.
  if (answers.needsTool.noul >= 0.7) {
    return {
      mode: "tool",
      tool: answers.tool.choice,
      reason: "Jev found a high probability that a tool is needed.",
    };
  }

  if (answers.needsDeepReasoning.noul >= 0.6) {
    const response = await cf.chat.completions.create({
      model: "companyfabric/auto",
      max_tokens: 500,
      messages: [
        {
          role: "system",
          content:
            "You are a coding agent. Produce a safe, concise plan for the task.",
        },
        { role: "user", content: JSON.stringify(state) },
      ],
    });

    return {
      mode: "llm",
      plan: response.choices[0].message.content ?? "",
      costUsd: response.usage?.cost_usd ?? null,
    };
  }

  return {
    mode: "deterministic",
    reason: "No tool or deep reasoning threshold was reached.",
  };
}

The bug to avoid is easy to miss:

if (!answers.needsTool.noul) {
  // Wrong: every probability except exactly 0 becomes false after negation.
}

A probability is not a Boolean. Use a threshold, define why that threshold exists, and test the boundary cases.

Relevant links:


8. How to test it without API keys

The code should not require visitors to add provider keys to understand the architecture. The repository should contain a mocked simulation for each workflow and run those simulations in CI.

A small test can verify the important business rules:

import { describe, expect, it } from "vitest";

function routeSupport(escalate: number) {
  return escalate < 0.4 ? "draft" : "human";
}

function routeAgent(needsTool: number) {
  return needsTool >= 0.7 ? "tool" : "continue";
}

describe("workflow rules", () => {
  it("fails closed for uncertain support escalation", () => {
    expect(routeSupport(0.5)).toBe("human");
  });

  it("drafts only when escalation probability is low", () => {
    expect(routeSupport(0.2)).toBe("draft");
    expect(routeSupport(0.8)).toBe("human");
  });

  it("uses a threshold for tool routing", () => {
    expect(routeAgent(0.8)).toBe("tool");
    expect(routeAgent(0.2)).toBe("continue");
  });
});

The UI simulation can show five steps per tab with Play, Next, and Back controls. It should label each step as "mocked" so nobody mistakes a demo response for a live production call.

For production tests, mock the SDK clients at the boundary. Test your thresholds and routing rules separately from TypeSafe and companyfabric network calls. Then add a small integration suite that runs only when API keys are present.

Relevant links:


9. What we learned

We started with a simple idea: use Jev for the fast part and companyfabric for the flexible part. The implementation details forced us to be more precise.

First, typed questions are the contract. choice criteria is a map of labels to descriptions. score uses an ordered list of labelled levels. Noul has an optional true/false description, but it returns a probability, not a second confidence field.

Second, confidence has to be interpreted by primitive. Choice and Score expose confidence and distributions. Noul gives a probability of truth. A value near 0.5 is uncertainty, not a middle score.

Third, costs and observability should be real fields. Jev usage can be converted from input tokens using the published rate used in this example. companyfabric exposes usage.cost_usd on non-streaming responses, and its response headers are useful for tracking the served model and request ID.

Fourth, the speed claim needs context. It is attractive to say "100× faster," but builders need to know what was measured. Up to 190× faster and 440× cheaper on published workflows is a much more responsible statement than promising a universal multiplier.

The moment we stopped asking one model to do everything, the architecture became easier to reason about.


10. What's next?

The open-source demo should remain deliberately small. It doesn't need live keys to teach the architecture. It needs clear mocks, correct SDK examples, tests, and links to the official documentation.

A practical roadmap looks like this:

  1. Add the three simulated tabs and five-step controls.
  2. Add mocked tests for support, fraud, and agent routing.
  3. Add optional server-side adapters for TypeSafe and companyfabric.
  4. Record request_id, served model, token usage, costs, and threshold decisions.
  5. Add human review for high-impact actions.
  6. Benchmark your own workflow instead of repeating somebody else's number.

The official TypeSafe JavaScript SDK is MIT-licensed and available on GitHub.  TypeSafe's agent skills are also published openly, including the Claude Code marketplace installation instructions.

For companyfabric, link readers to the companyfabric website and its current documentation from the project README. Verify the exact docs path before publishing, because the public site may change its navigation.

Thanks for reading! If you have feedback or suggestions, open an issue or pull request in the GitHub repository. The demo is intended to be a practical starting point—not a black box—and we would love to see what other builders create with it.


Official resources

TypeSafe / Jev

companyfabric

Testing and tooling


This article is intended for builders who want to ship real products, not chase hype. The patterns here are meant to be adapted, measured, and improved in your own context.