Field report: integrating Jev into a Genkit agent

By ALEXANDRE DERGHAM ยท 2026-11-09

Traditional LLMs are great at writing emails, coding features or holding a fluent conversation. But what happens when your agent simply needs to make a binary decision, classify an intent or route a data flow in a fraction of a second? This article tackles that problem by integrating TypeSafe AI's Jev into an agent's structure (in this case, one built with Genkit).

Tags : Jev, Genkit, Gemini

Rethinking AI for machines, not for humans

Developers know the problem well: forcing a text-generating model to spit out strict JSON makes the code much heavier, pushes latency to unacceptable levels and needlessly inflates the cloud bill.

This is where Jev (developed by TypeSafe AI) comes in: a System One model designed solely to return typed decisions and probabilities instead of blocks of raw text. In this article, you will see how to integrate it cleanly into a Genkit agent to build a decision system that is robust, ultra-fast and cost-effective.

What is TypeSafe AI's Jev and why is it a game changer?

The "System One" concept

Inspired by Daniel Kahneman's work in cognitive psychology (fast, intuitive thinking versus slow, analytical thinking), Jev is built for instant intuition and targeted judgments.

An interface designed for code

Unlike conventional LLMs, Jev leaves text generation aside:

  • Input: a textual or JSON state defining the context to evaluate, combined with atomic questions.
  • Output: structured data, precise scores and probability distributions.

Two ways to get a decision: a generative LLM writes text that has to be parsed (about 1 s), Jev answers typed questions (about 0.2 s)

The three fundamental question types

Jev structures your requests around three essential logical primitives:

Jev's three primitives: choice returns an option from a list, score a step on an ordered scale, noul a probability between 0 and 1

Examples in typescript with @typesafe-ai/sdk:

state: { claim: "My mug has a chipped rim." },
questions: {
  // returns "attemptRepair" or "replace" depending on whether the product can be repaired
  action: choice("what should I do", {
    attemptRepair: "the product can be repaired",
    replace: "the product cannot be repaired"
  }),
      
  // returns "none", "cosmetic" or "unusable"
  // depending on the damage to the product
  severity: score("How severe is the damage?", [
    "none",
    "cosmetic",
    "unusable"
  ]),
    
  // returns a number between 0 and 1,
  // 0 means the product is perfectly usable
  // 1 means the product is unusable with 100% certainty
  damaged: noul("Is the product still usable")
},

Key benefits

Thanks to this specialized approach, Jev is 40x to 200x faster and significantly cheaper than frontier LLMs on classification, filtering or routing tasks.

Why pair Genkit with Jev in an agent architecture?

A quick reminder about Genkit

Developed by Firebase, Genkit is the ideal open-source framework for orchestrating AI agents and workflows in a modular, typed and secure way in TypeScript.

Jev's role in an agent loop

In a modern architecture:

  • Genkit manages the state, the overall tool orchestration and content generation (through Gemini, for example).
  • Jev acts as the agent's instant decision and routing engine (e.g. classifying a user's intent, validating a business rule on the fly, or routing a support ticket).

The running example

Let's take a Genkit-based chatbot agent: the text response is generated by a standard LLM. We need to extend this agent so that an analysis of the chat response gives us a list of category tags.

In the legacy version of this chatbot, we use a prompt generated by the following function to decide which tags to keep:

export const generateTagsAnalyserPrompt =
  (chatBotResponse: string): GenerateOptions => ({
      model: 'googleai/gemini-3.5-flash-lite',
      prompt: `You are a text analyser.
        You have to check if the following text matches the following conditions
          - If the text is an advice then add "isAdvice" to the list of updated tags.
          - If the text contains a warning then add "isWarning" to the list of updated tags.
        The text to analyse is "${chatBotResponse}"`,
      config: {
        temperature: 0.2,
        topK: 20,
        topP: 0.90
      },
      output: {
        schema: z.object({
          updatedTags: z.array(z.string())
        })
      }
    });

In the version we are about to write, Jev will analyze the response and return the list of tags.

The running example before and after: the response is still generated by Gemini, only the analysis that produces the tags moves from Gemini Flash-Lite to Jev, through a custom Genkit provider

Step-by-step tutorial: integrating Jev into a Genkit agent

Step 1: Setup and configuration

Start by installing the required dependencies in your Node.js/TypeScript project, then set up your API key from the TypeSafe AI console:

npm install @typesafe-ai/sdk
export TYPESAFE_API_KEY="your_api_key"

Step 2: Creating a custom Genkit provider

At the time of writing, there is no Genkit plugin that makes it easy to integrate Jev in TypeScript, so we will write the provider ourselves and present it as an LLM model.
Note that this step is not required to call the TypeSafe AI API. It simply integrates the call into the framework so that it shows up in Genkit's monitoring and debugging tools.

import { googleAI } from '@genkit-ai/google-genai';
import { noul, TypeSafeClient } from '@typesafe-ai/sdk';
import { GenerationCommonConfigSchema, genkit, z } from 'genkit';
 
const ai = genkit({
  plugins: [
    googleAI()
  ]
});
  
const typeSafeClient = new TypeSafeClient({ defaultModel: 'jev-latest' });
const TAG_VALIDATION_THRESHOLD = 0.8;

// Custom model definition
const typesafeAIModel = ai.defineModel(
  {
    name: 'typesafeai/jev-latest',
    configSchema: GenerationCommonConfigSchema,
    supports: {
      tools: false,
      systemRole: true
    }
  },
  async (request) => {
    const response = await typeSafeClient.systemOne({
      state: request.messages[0].content[0].text ?? '',
      questions: {
        isAdvice: noul('the response contains an advice'),
        isWarning: noul('the response contains a warning')   
      }
    });
    
    const updatedTags = ['isAdvice', 'isWarning'].filter((tag) => {
      const answer = response.answers[tag];
      return answer.type === 'noul' && answer.noul > TAG_VALIDATION_THRESHOLD;
    });
    
    // response format Genkit expects in order to wrap the call
    return {
      message: {
        role: 'model' as const,
        content: [{ text: '', custom: { updatedTags } }]
      }
    };
  }
 );

Step 3: Implementing the Genkit Flow

Here is how to structure a Genkit Flow in TypeScript that combines classic text generation with Jev's ultra-fast evaluation:

import { GenerationCommonConfigSchema, genkit, z } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';

export const generateResponseFlow = ai.defineFlow({
  name: 'chatbotFlow',
  inputSchema: /* Input schema */,
  outputSchema: z.object({
    response: z.string(),
    updatedTags: z.array(z.string())
  }),
  streamSchema: z.string()
}, async (subject: ChatSubject, { sendChunk }): Promise<ChatResponse> => {
  const prompt = subject.prompt;

  // Streaming call to the main chatbot agent
  // generateChatPrompt builds the prompt and is not detailed here.
  const { response, stream } = ai.generateStream(generateChatPrompt(subject));

  for await (const chunk of stream) {
    sendChunk(chunk.text);
  }

  const chatResponse = (await response).text;
    
  // Call to the wrapped Jev model
  let updatedTags = [];
  const jevResponse = await ai.generate({
    prompt: { text: chatResponse },
    model: typesafeAIModel
  });
  updatedTags = jevResponse.message?.content[0]?.custom?.updatedTags;

  return {
    response: chatResponse,
    updatedTags
  };
});

Other possible use cases

Jev works so simply that it can be applied to many use cases:

  • Enforcing guardrails by validating an agent's output (e.g. no sensitive data in the response).
  • Categorizing and sorting an incoming message so that the right agent or process handles it.
  • Evaluating a message against a simple criterion (e.g. how urgent a ticket is, or a customer's churn risk).

Note that Jev's reliability still depends on how simple the input prompt is. Since Jev cannot break down and spell out a line of reasoning, the best practice is to define choices that are as simple and atomic as possible.

An alternative: Kev

Kev is an alternative to Jev that follows the same "System One" approach.
This Qwen-based solution can be deployed and run locally, with performance that remains attractive compared with a conventional LLM approach as long as the input stays small.

Worth noting: the @typesafe-ai/sdk library makes it very easy for the Genkit agent to call Kev, since the only change needed is in how the typeSafeClient is initialized.

const typeSafeClient = new TypeSafeClient({
    defaultModel: 'kev-latest',
    apiKey: 'local',
    baseURL: 'http://localhost:8009'
  });

Performance comparison

Using two datasets of different sizes, we compared the duration and cost of the analysis step (network latency included).

Duration and cost of the analysis step for Gemini 3.5 Flash-Lite, Jev 1.14 and Kev-4B: Jev is about 4 times faster and 7 to 8 times cheaper than Gemini

In short, we can observe that:

  • Gemini's and Jev's processing times stay stable even as the input grows.
  • Kev's performance collapses as the input grows. This observation should be taken with caution, since the model is hosted locally on a workstation that may not be optimized for this kind of workload.
  • Jev remains faster and cheaper than Gemini.

Note that, unlike LLM-type models, most of Jev's running cost depends only on the number of input tokens.

Conclusion

By combining Genkit's orchestration flexibility with the surgical, instant power of Jev (TypeSafe AI), you remove the bottlenecks that text generation creates in routing. Your agents become not only faster and cheaper but also more reliable, since Jev is deterministic by nature.

Ready to try the System One approach in your next Genkit application? Share your feedback in the comments!

Further reading