Accessing flows from the client

There are two primary ways to access Genkit flows from client-side applications:

  • Using a Genkit client library
  • Using the client SDK for your server platform (e.g., the Cloud Functions for Firebase callable function client SDK)

This guide covers the Genkit client libraries.

Using the Genkit client library

Section titled “Using the Genkit client library”

You can call your deployed flows using a Genkit client library. The libraries provide a type-safe way to interact with both non-streaming and streaming flows.

Learn about flows in “Defining AI workflows”.

Non-streaming Flow Calls

Section titled “Non-streaming Flow Calls”

For a non-streaming response, use the runFlow function (in JS) or await the action (in Dart). This is suitable for flows that return a single, complete output.

import { runFlow } from 'genkit/beta/client';

async function callHelloFlow() {

try {

const result = await runFlow({

url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL

input: { name: 'Genkit User' },

});

console.log('Non-streaming result:', result.greeting);

} catch (error) {

console.error('Error calling helloFlow:', error);

}

}

callHelloFlow();

Streaming Flow Calls

Section titled “Streaming Flow Calls”

For flows that are designed to stream responses (e.g., for real-time updates or long-running operations), use the streamFlow function (in JS) or the .stream() method (in Dart).

import { streamFlow } from 'genkit/beta/client';

async function streamHelloFlow() {

try {

const result = streamFlow({

url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL

input: { name: 'Streaming User' },

});

// Process the stream chunks as they arrive

for await (const chunk of result.stream) {

console.log('Stream chunk:', chunk);

}

// Get the final complete response

const finalOutput = await result.output;

console.log('Final streaming output:', finalOutput.greeting);

} catch (error) {

console.error('Error streaming helloFlow:', error);

}

}

streamHelloFlow();

Custom Object Streaming

Section titled “Custom Object Streaming”

You can also stream custom objects. For robust JSON serialization in Dart, it’s recommended to use a code generation library like json_serializable. In TypeScript, you can use standard interfaces to define the shape of your data.

// Define the shape of your data

interface StreamChunk {

content: string;

}

interface MyOutput {

reply: string;

}

// In your streaming call, the client will handle JSON parsing

async function streamCustomObjects() {

try {

const result = streamFlow<MyOutput, StreamChunk>({

url: 'http://localhost:3400/stream-process',

input: { message: 'Stream this data', count: 5 },

});

console.log('Streaming chunks:');

for await (const chunk of result.stream) {

console.log('Chunk:', chunk.content);

}

const finalResult = await result.output;

console.log('\nFinal Response:', finalResult.reply);

} catch (e) {

console.error('Error calling streaming flow:', e);

}

}

Working with Genkit Data Objects

Section titled “Working with Genkit Data Objects”

When interacting with Genkit models, you’ll often work with standardized data classes. The client libraries provide these classes for type-safe interaction.

import { streamFlow } from 'genkit/beta/client';

import type {

MessageData,

GenerateResponseChunkData,

GenerateResponseData,

} from 'genkit/model';

async function streamGenerate() {

try {

const result = streamFlow<GenerateResponseData, GenerateResponseChunkData>({

url: 'http://localhost:3400/generate',

input: {

role: 'user',

content: [{ text: 'hello' }],

} as MessageData,

});

console.log('Streaming chunks:');

for await (const chunk of result.stream) {

// Note: A chunk may have multiple parts, and not all parts are text.

console.log('Chunk:', chunk.content[0].text);

}

const finalResult = await result.output;

// Note: A response may have multiple candidates.

console.log(

'\nFinal Response:',

finalResult.message.content[0].text

);

} catch (e) {

console.error('Error calling streaming flow:', e);

}

}

If your deployed flow requires authentication, you can pass headers with your requests:

const result = await runFlow({

url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL

headers: {

Authorization: 'Bearer your-token-here', // Replace with your actual token

},

input: { name: 'Authenticated User' },

});

When deploying to Cloud Functions for Firebase

Section titled “When deploying to Cloud Functions for Firebase”

When deploying to Cloud Functions for Firebase, use the Firebase callable functions client library.

Detailed documentation can be found at https://firebase.google.com/docs/functions/callable?gen=2nd

Here’s a sample for the web:

// Get the callable by passing an initialized functions SDK.

const getForecast = httpsCallable(functions, 'getForecast');

// Call the function with the `.stream()` method to start streaming.

const { stream, data } = await getForecast.stream({

locations: favoriteLocations,

});

// The `stream` async iterable returned by `.stream()`

// will yield a new value every time the callable

// function calls `sendChunk()`.

for await (const forecastDataChunk of stream) {

// update the UI every time a new chunk is received

// from the callable function

updateUi(forecastDataChunk);

}

// The `data` promise resolves when the callable

// function completes.

const allWeatherForecasts = await data;

finalizeUi(allWeatherForecasts);

source