ChatwatsonxAI
Use different IBM's text generation models by following these instructions.
Installation
Install peer dependencies:
npm install @ibm-cloud/watsonx-ai
Prerequisites
Before setting up the component, you'll need to obtain the necessary environment variables from the IBM WatsonX AI platform. Follow these steps:
- Create an IBM Account and API Key
- Sign up for an IBM WatsonX AI account
- Navigate to the API Key section
- Click "Create API Key" to generate your credentials
- Set Up Your Project
- Create a new project if you don't have one
- Access the project's "Manage" section to find your Project ID
- Important: Enable "WatsonMachineLearning" under /Manage/Services & integrations
- Get Service URL
- Navigate to the dashboard
- Locate and copy your service URL
Once you have these credentials, you can proceed with the environment variable setup.
Add Environment Variables
WATSONX_AI_AUTH_TYPE=iam
WATSONX_AI_APIKEY="YOUR_SAMPLE_API_KEY"
WATSONX_AI_PROJECT_ID="YOUR_SAMPLE_PROJECT_ID"
WATSONX_AI_SERVICE_URL="YOUR_SAMPLE_SERVICE_URL"
/* You can get one from - https://www.ibm.com/products/watsonx-ai */
Copy the code
Add the following code to your utils/chatWatsonxAi.ts
file:
import { WatsonXAI } from "@ibm-cloud/watsonx-ai";
import { Stream } from "@ibm-cloud/watsonx-ai/dist/lib/common";
import WatsonxAiMlVml_v1, {
ObjectStreamed,
TextChatStreamResponse,
} from "@ibm-cloud/watsonx-ai/dist/watsonx-ai-ml/vml_v1";
import { UserOptions } from "ibm-cloud-sdk-core";
import fs from "fs";
interface WatsonxAIConfig extends UserOptions {
modelId: string;
projectId: string;
}
interface ChatResponse {
output: string;
}
interface WatsonxAICallOptions {
prompt: string;
stream?: boolean;
context?: string;
outputFormat?: string;
}
interface WatsonxAICallWithChatOptions extends WatsonxAICallOptions {
chatHistory?: Record<"assistant" | "user", string>[];
image?: { path: string; type: "local" | "remote" }[];
systemInstruction?: string;
options?: Omit<
WatsonxAiMlVml_v1.TextChatParams,
"modelId" | "messages" | "projectId"
>;
}
export class ChatWatsonxAI {
private client: WatsonXAI;
private modelId: string;
private projectId: string;
constructor(config: WatsonxAIConfig) {
const { modelId, projectId, serviceUrl, version } = config;
this.client = WatsonXAI.newInstance({
version: version,
serviceUrl,
...config,
});
this.modelId = modelId;
this.projectId = projectId;
}
async chat(
options: WatsonxAICallOptions
): Promise<
| ChatResponse
| Stream<
WatsonxAiMlVml_v1.ObjectStreamed<WatsonxAiMlVml_v1.TextGenResponse>
>
> {
const { prompt, context, stream, outputFormat } = options;
const content = await this.buildMessage(prompt, context, outputFormat);
if (stream) {
const response = await this.client.generateTextStream({
modelId: this.modelId,
projectId: this.projectId,
input: content,
returnObject: true,
});
return response;
}
const response = await this.client.generateText({
modelId: this.modelId,
projectId: this.projectId,
input: content,
});
return {
output:
response.result?.results?.[0]?.generated_text?.replace(
/^```json\n|\n```$/g,
""
) ?? "",
};
}
async chatWithHistory(
options: WatsonxAICallWithChatOptions
): Promise<ChatResponse | Stream<ObjectStreamed<TextChatStreamResponse>>> {
const {
prompt,
context,
stream,
outputFormat,
image,
chatHistory,
systemInstruction,
} = options;
let userMessages: { role: string; content: string }[] = [];
if (systemInstruction) {
userMessages.push({ role: "system", content: systemInstruction });
}
if (chatHistory && chatHistory.length > 0) {
chatHistory.forEach((chat) => {
userMessages.push({ role: "user", content: chat.user });
userMessages.push({ role: "assistant", content: chat.assistant });
});
}
const content = await this.buildMessage(
prompt,
context,
outputFormat,
image
);
userMessages.push({
role: "user",
content: image && image.length > 0 ? JSON.parse(content) : content,
});
if (stream) {
const response = await this.client.textChatStream({
modelId: this.modelId,
projectId: this.projectId,
returnObject: true,
messages: userMessages,
...options.options,
});
return response;
}
const response = await this.client.textChat({
modelId: this.modelId,
projectId: this.projectId,
messages: userMessages,
...options.options,
});
return {
output:
response.result?.choices?.[0]?.message?.content?.replace(
/^```json\n|\n```$/g,
""
) ?? "",
};
}
private async buildMessage(
prompt: string,
context?: string,
outputFormat?: string,
file?: { path: string; type: "local" | "remote" }[]
): Promise<string> {
let content = prompt;
if (context) {
content += `\ncontext:\n${context}`;
}
if (outputFormat) {
content += `\noutput format:\n${outputFormat}`;
}
if (file && file.length > 0) {
const filePromises = file.map(async (f) => ({
type: "image_url",
image_url: {
url: await this.encodeImage(f.path, f.type),
},
}));
const resolvedFiles = await Promise.all(filePromises);
const finalContent = [{ type: "text", text: content }, ...resolvedFiles];
return JSON.stringify(finalContent);
}
return content;
}
private async encodeImage(
path: string,
type: "local" | "remote"
): Promise<string> {
if (type === "remote") {
const response = await fetch(path);
const buffer = Buffer.from(await response.arrayBuffer()).toString(
"base64"
);
return `data:image/jpeg;base64,${buffer}`;
}
return `data:image/jpeg;base64,${fs.readFileSync(path).toString("base64")}`;
}
}
Usage
Initialize client
Model Selection Guide
IBM's Granite models offer various capabilities for different use cases. Before initializing the client, you'll need to select the appropriate model for your needs.
Available Models and Use Cases
Model | Best For |
---|---|
ibm/granite-13b-instruct-v2 | General-purpose AI (chatbots, summaries, reasoning) |
ibm/granite-20b-code-instruct | Code generation and AI-assisted programming |
ibm/granite-3-2b-instruct | Lightweight applications requiring low latency |
ibm/granite-3-8b-instruct | Balanced performance and resource efficiency |
For a complete list of IBM's models, visit the IBM Data Platform Documentation.
Getting Model Information
To obtain the required version
and modelId
:
-
Navigate to the Dashboard and select "Explore Foundational Models"
-
Select your desired model and open it in the prompt lab
-
Click "View Code" in the top right corner to find:
modelId
(e.g.,ibm/granite-13b-instruct-v2
)version
(e.g.,2023-05-29
)
Initialize the WatsonxAIChat client.
import { ChatWatsonxAI} from "@/utils/chatWatsonxAi";
const watsonxChat = new ChatWatsonxAI({
modelId: "ibm/granite-13b-instruct-v2",
projectId: process.env.WATSONX_AI_PROJECT_ID!,
serviceUrl: process.env.WATSONX_AI_SERVICE_URL,
version: "2023-05-29"
});
Simple Chat
Sample chat with a simple single prompt.
Models like granite-13b-instruct-v2
are best to use for single prompt chats.
const data = await watsonxChat.chat({
prompt: "What is YCombinator? Respond in one liner!",
});
console.log(data);
/**
{
"output": "YCombinator is a seed money startup accelerator program."
}
**/
Multi Conversational chat
Sample chats involving addition of chat history.
Models like granite-3-2b-instruct
supports multi-conversational chats.
const watsonxChat = new ChatWatsonxAI({
modelId: "ibm/granite-3-2b-instruct",
projectId: process.env.WATSONX_AI_PROJECT_ID!,
serviceUrl: process.env.WATSONX_AI_SERVICE_URL,
version: "2023-05-29",
});
const dataWithHistory = await watsonxChat.chatWithHistory({
prompt: "Who I am?",
chatHistory: [
{ user: "Hello", assistant: "Hi there! How can I help you today?" },
{
user: "My name is John Doe, and I am an AI developer!",
assistant: "Sure, I will remember that!",
},
],
});
console.log(dataWithHistory);
/**
{
"output": "You are John Doe, an AI developer. How can I assist you further today?"
}
**/
Defining Output Format
Example of defining output format. Though it's not 100% accurate, LLM can hallucinate sometime.
const dataWithFormat = await watsonxChat.chatWithHistory({
prompt: "Who I am?",
chatHistory: [
{ user: "Hello", assistant: "Hi there! How can I help you today?" },
{
user: "My name is John Doe, and I am an AI developer!",
assistant: "Sure, I will remember that!",
},
],
outputFormat: `{name: "", occupation: ""}`,
});
console.log(dataWithFormat);
/**
{
"output": "{\"name\": \"John Doe\", \"occupation\": \"AI developer\"}"
}
**/
Defining Context and other optional parameters
const dataWithContext = await watsonxChat.chatWithHistory({
prompt: "Who I am?",
chatHistory: [
{ user: "Hello", assistant: "Hi there! How can I help you today?" },
{
user: "My name is John Doe, and I am an AI developer!",
assistant: "Sure, I will remember that!",
},
],
context:
"John Doe is an AI developer, specialized in chatbot development and RAG research.",
systemInstruction: "You're a helpful assistant and your name is AgentGenesis!",
outputFormat: `{name: "", occupation: "", speciality: "", your_name : ""}`,
options: {
maxTokens: 100,
temperature: 0.7,
},
});
console.log(dataWithContext);
/**
{
"output": "{\n \"name\": \"John Doe\",\n \"occupation\": \"AI developer\",\n \"speciality\": \"chatbot development and RAG research\",\n \"your_name\": \"AgentGenesis\"\n}"
}
**/
Multimodal Chat
WatsonxAIChat supports multimodal inputs, allowing you to combine text with image files.
For multimodal chat interactions, we use vision-capable models like meta-llama/llama-3-2-11b-vision-instruct
which can process both text and images
Example: Using Public URLs and Local paths
The following examples demonstrate how to send file using public URLs and Local paths.
const watsonxChat = new ChatWatsonxAI({
modelId: "meta-llama/llama-3-2-11b-vision-instruct",
projectId: process.env.WATSONX_AI_PROJECT_ID!,
serviceUrl: process.env.WATSONX_AI_SERVICE_URL,
version: "2023-05-29"
});
const dataWithRemoteImage = await watsonxChat.chatWithHistory({
prompt: "what the image is depicting...?",
image: [
{
path: "https://2.img-dpreview.com/files/p/E~C1000x0S4000x4000T1200x1200~articles/3925134721/0266554465.jpeg",
type: "remote",
},
],
options: {
temperature: 0.6,
},
});
const dataWithLocalImage = await watsonxChat.chatWithHistory({
prompt: "what the image is depicting...?",
image: [
{
path: "./public/sample_image.jpg",
type: "local",
},
],
options: {
temperature: 0.6,
},
});
Streaming text
Sample example of creating and handling stream responses.
You can stream in both the chat
and chatWithHistory
methods.
const dataWithStreaming = await watsonxChat.chatWithHistory({
prompt: "Who I am?",
chatHistory: [
{ user: "Hello", assistant: "Hi there! How can I help you today?" },
{
user: "My name is John Doe, and I am an AI developer!",
assistant: "Sure, I will remember that!",
},
],
context: "John Doe is an AI developer, specialized in chatbot development and RAG research.",
systemInstruction: "You're a helpful assistant and your name is AgentGenesis!",
options: {
temperature: 0.6,
maxTokens: 100,
},
stream: true,
});
// @ts-ignore
for await (const chunk of dataWithStreaming) {
console.log(chunk.data?.choices[0]?.delta?.content ?? "");
}
/**
You are John Doe, an AI developer specialized in chatbot development and RAG research. How can I assist you further, John?
**/
const singleDataWithStreaming = await watsonxChat.chat({
prompt: "Who I am?",
context: "John Doe is an AI developer, specialized in chatbot development and RAG research.",
outputFormat: `{name: "", occupation: "", speciality: "", your_name : ""}`,
stream: true,
});
// @ts-ignore
for await (const chunk of singleDataWithStreaming) {
console.log(chunk.data.results[0].generated_text);
}
Props
chat
Prop | Type | Description | Default |
---|---|---|---|
prompt | string | Prompt provided by user. | "" |
context | string? | Additional context user wants to provide. | "" |
outputFormat | string? | Particular format in which the user wants their output. | "" |
stream | boolean? | Returns streamed text if true | false |
chatWithHistory
Prop | Type | Description | Default |
---|---|---|---|
prompt | string | Prompt provided by user. | "" |
context | string? | Additional context user wants to provide. | "" |
outputFormat | string? | Particular format in which the user wants their output. | "" |
chatHistory | optional | Conversational history. | "" |
systemInstruction | string? | Any specific instruction to the system user wants to feed. | "" |
image | optional | Image path | [] |
options | optional? | Additional args as per IBM docs. |
Credits
This component is built on top of Watsonx AI's Node SDK