OpenAI integrations for LangChain.js providing chat models, embeddings, tools, and Azure support.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Image generation with DALL-E and custom tool creation for the OpenAI Responses API. Extend model capabilities with external functions and specialized tools.
DALL-E image generation tool for creating images from text descriptions.
/**
* DALL-E image generation tool
* Creates images from text descriptions using OpenAI's DALL-E models
*/
class DallEAPIWrapper extends Tool {
constructor(fields?: Partial<DallEAPIWrapperParams>);
/** Tool identification */
name: string; // "dalle_image_generator"
description: string; // "A tool for generating images from text descriptions"
/** Model configuration */
model: string; // Default: "dall-e-3"
style: "natural" | "vivid"; // Image style (default: "vivid")
quality: "standard" | "hd"; // Image quality (default: "standard")
n: number; // Number of images to generate (default: 1)
size: string; // Image size (default: "1024x1024")
dallEResponseFormat: "url" | "b64_json"; // Response format (default: "url")
/** Client configuration */
openAIApiKey?: string; // OpenAI API key
organization?: string; // OpenAI organization ID
baseURL?: string; // Custom base URL
timeout?: number; // Request timeout
maxRetries?: number; // Maximum retry attempts
/** Generate image(s) from text description */
_call(input: string): Promise<string>;
/** Make image generation requests with retry logic */
imageGenerationWithRetry<T>(
request: OpenAIClient.Images.ImageGenerateParams,
options?: OpenAICallOptions
): Promise<T>;
}Configuration interface for DALL-E API wrapper.
interface DallEAPIWrapperParams {
/** DALL-E model version */
model: string;
/** Image generation style */
style: "natural" | "vivid";
/** Image quality level */
quality: "standard" | "hd";
/** Number of images to generate */
n: number;
/** Image dimensions */
size: string;
/** Response format for generated images */
dallEResponseFormat: "url" | "b64_json";
/** OpenAI API configuration */
openAIApiKey?: string;
organization?: string;
baseURL?: string;
timeout?: number;
maxRetries?: number;
}Create custom tools for use with OpenAI's Responses API.
/**
* Create custom tools for OpenAI Responses API
* Converts LangChain runnables into OpenAI-compatible tool definitions
*/
function customTool(
func: RunnableFunc<string, string, ToolRunnableConfig>,
fields: CustomToolFields
): DynamicTool<string>;
type CustomToolFields = Omit<OpenAI.Responses.CustomTool, "type">;import { DallEAPIWrapper } from "@langchain/openai";
const dalleGenerator = new DallEAPIWrapper({
model: "dall-e-3",
apiKey: process.env.OPENAI_API_KEY,
quality: "hd",
size: "1024x1024",
style: "vivid"
});
// Generate a single image
const imageUrl = await dalleGenerator._call(
"A futuristic city skyline at sunset with flying cars and neon lights"
);
console.log("Generated image URL:", imageUrl);
// The returned URL can be used to display or download the image// DALL-E 3 (recommended) - high quality, single image
const dalleV3 = new DallEAPIWrapper({
model: "dall-e-3",
quality: "hd", // "standard" or "hd"
style: "natural", // "natural" or "vivid"
size: "1024x1792", // Portrait orientation
dallEResponseFormat: "url"
});
// DALL-E 2 - can generate multiple images
const dalleV2 = new DallEAPIWrapper({
model: "dall-e-2",
n: 4, // Generate 4 images (DALL-E 2 only)
size: "512x512", // Smaller size for DALL-E 2
quality: "standard" // HD not available for DALL-E 2
});
// Generate multiple images with DALL-E 2
const multipleImages = await dalleV2._call(
"A cute robot playing with a cat in a garden"
);
console.log("Multiple image URLs:", multipleImages);// DALL-E 3 supported sizes
const dalle3Sizes = ["1024x1024", "1792x1024", "1024x1792"];
// DALL-E 2 supported sizes
const dalle2Sizes = ["256x256", "512x512", "1024x1024"];
// Create generators for different aspect ratios
const squareGenerator = new DallEAPIWrapper({
model: "dall-e-3",
size: "1024x1024" // Square
});
const landscapeGenerator = new DallEAPIWrapper({
model: "dall-e-3",
size: "1792x1024" // Landscape
});
const portraitGenerator = new DallEAPIWrapper({
model: "dall-e-3",
size: "1024x1792" // Portrait
});const dalle64 = new DallEAPIWrapper({
model: "dall-e-3",
dallEResponseFormat: "b64_json", // Return base64 encoded image
quality: "hd"
});
const base64Image = await dalle64._call("A serene mountain landscape");
// Convert base64 to buffer for saving
const imageBuffer = Buffer.from(base64Image, 'base64');
// Save to file (Node.js)
import { writeFileSync } from 'fs';
writeFileSync('generated_image.png', imageBuffer);
// Or use in web applications
const dataUrl = `data:image/png;base64,${base64Image}`;
// dataUrl can be used directly in <img> tagsimport { customTool } from "@langchain/openai";
import { z } from "zod";
// Create a weather tool
const weatherTool = customTool(
async (input: string) => {
// Parse the input (it will be JSON string from the model)
const { location, units } = JSON.parse(input);
// Simulate weather API call
const temperature = Math.floor(Math.random() * 30) + 10;
const condition = ["sunny", "cloudy", "rainy"][Math.floor(Math.random() * 3)];
return JSON.stringify({
location,
temperature: `${temperature}°${units === 'celsius' ? 'C' : 'F'}`,
condition,
timestamp: new Date().toISOString()
});
},
{
name: "get_weather",
description: "Get current weather information for a specific location",
schema: z.object({
location: z.string().describe("City name or location"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius").describe("Temperature units")
})
}
);
// Create a calculator tool
const calculatorTool = customTool(
async (input: string) => {
const { operation, a, b } = JSON.parse(input);
let result: number;
switch (operation) {
case "add": result = a + b; break;
case "subtract": result = a - b; break;
case "multiply": result = a * b; break;
case "divide": result = b !== 0 ? a / b : NaN; break;
default: throw new Error(`Unknown operation: ${operation}`);
}
return JSON.stringify({ result, operation, operands: [a, b] });
},
{
name: "calculator",
description: "Perform basic mathematical operations",
schema: z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
a: z.number().describe("First number"),
b: z.number().describe("Second number")
})
}
);import { ChatOpenAI } from "@langchain/openai";
// Create chat model that supports Responses API
const chatModel = new ChatOpenAI({
model: "gpt-4o",
temperature: 0,
useResponsesApi: true // Required for custom tools
});
// Bind custom tools to the model
const modelWithCustomTools = chatModel.bindTools([
weatherTool,
calculatorTool
]);
// The model can now call these custom tools
const response = await modelWithCustomTools.invoke(
"What's the weather in Tokyo? Also, what's 15 multiplied by 23?"
);
console.log("Model response:", response.content);
// Handle tool calls if any
if (response.tool_calls && response.tool_calls.length > 0) {
for (const toolCall of response.tool_calls) {
console.log(`Called tool: ${toolCall.name}`);
console.log(`With args: ${JSON.stringify(toolCall.args)}`);
}
}// Database search tool with complex schema
const databaseSearchTool = customTool(
async (input: string) => {
const { query, filters, limit, sortBy } = JSON.parse(input);
// Simulate database search
const mockResults = Array.from({ length: Math.min(limit, 5) }, (_, i) => ({
id: i + 1,
title: `Result ${i + 1} for "${query}"`,
score: Math.random(),
category: filters?.category || "general",
timestamp: new Date(Date.now() - Math.random() * 86400000).toISOString()
}));
// Sort results
if (sortBy === "score") {
mockResults.sort((a, b) => b.score - a.score);
} else if (sortBy === "date") {
mockResults.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
}
return JSON.stringify({
results: mockResults,
total: mockResults.length,
query,
filters
});
},
{
name: "search_database",
description: "Search the company database with advanced filtering and sorting",
schema: z.object({
query: z.string().describe("Search query string"),
filters: z.object({
category: z.string().optional().describe("Filter by category"),
dateRange: z.object({
start: z.string().optional().describe("Start date (ISO format)"),
end: z.string().optional().describe("End date (ISO format)")
}).optional().describe("Date range filter")
}).optional().describe("Search filters"),
limit: z.number().default(10).describe("Maximum number of results"),
sortBy: z.enum(["relevance", "date", "score"]).default("relevance").describe("Sort order")
})
}
);
// File processing tool
const fileProcessorTool = customTool(
async (input: string) => {
const { fileName, operation, options } = JSON.parse(input);
// Simulate file processing
const operations = {
analyze: () => ({ type: "text", lines: 150, words: 2340, size: "12KB" }),
compress: () => ({ originalSize: "12KB", compressedSize: "8KB", ratio: "33%" }),
convert: () => ({ from: "docx", to: options.format, status: "completed" })
};
const result = operations[operation as keyof typeof operations]?.() ||
{ error: "Unknown operation" };
return JSON.stringify({
fileName,
operation,
result,
timestamp: new Date().toISOString()
});
},
{
name: "process_file",
description: "Process files with various operations like analysis, compression, or conversion",
schema: z.object({
fileName: z.string().describe("Name of the file to process"),
operation: z.enum(["analyze", "compress", "convert"]).describe("Operation to perform"),
options: z.object({
format: z.string().optional().describe("Target format for conversion"),
quality: z.enum(["low", "medium", "high"]).optional().describe("Quality level")
}).optional().describe("Additional operation options")
})
}
);// Robust tool with error handling
const apiTool = customTool(
async (input: string) => {
try {
const { endpoint, method, data } = JSON.parse(input);
// Simulate API call
if (endpoint === "/error") {
throw new Error("API endpoint not found");
}
return JSON.stringify({
status: "success",
endpoint,
method,
response: { message: "API call successful", data }
});
} catch (error) {
return JSON.stringify({
status: "error",
error: error instanceof Error ? error.message : "Unknown error",
timestamp: new Date().toISOString()
});
}
},
{
name: "api_call",
description: "Make API calls to external services",
schema: z.object({
endpoint: z.string().describe("API endpoint URL"),
method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET").describe("HTTP method"),
data: z.record(z.any()).optional().describe("Request payload")
})
}
);
// Test error handling
const chatWithErrorHandling = new ChatOpenAI({
model: "gpt-4o",
useResponsesApi: true
}).bindTools([apiTool]);
const errorResponse = await chatWithErrorHandling.invoke(
"Make a GET request to /error endpoint"
);// Create a comprehensive creative assistant
const imageDescriptionTool = customTool(
async (input: string) => {
const { imageUrl, analysisType } = JSON.parse(input);
// Simulate image analysis (in real app, use vision API)
const analyses = {
artistic: "This image shows vibrant colors with impressionist style brushstrokes...",
technical: "Resolution: 1024x1024, Color depth: 24-bit, Format: PNG...",
content: "The image depicts a futuristic cityscape with neon lighting..."
};
return JSON.stringify({
analysis: analyses[analysisType as keyof typeof analyses] || "General analysis not available",
imageUrl,
analysisType
});
},
{
name: "analyze_image",
description: "Analyze generated images for artistic, technical, or content details",
schema: z.object({
imageUrl: z.string().describe("URL of the image to analyze"),
analysisType: z.enum(["artistic", "technical", "content"]).describe("Type of analysis to perform")
})
}
);
// Creative assistant with image generation and analysis
const creativeAssistant = new ChatOpenAI({
model: "gpt-4o",
useResponsesApi: true
}).bindTools([imageDescriptionTool]);
// Also create DALL-E wrapper for image generation
const imageGenerator = new DallEAPIWrapper({
model: "dall-e-3",
quality: "hd",
style: "vivid"
});
// Workflow: generate image, then analyze it
async function createAndAnalyze(prompt: string) {
console.log("Generating image...");
const imageUrl = await imageGenerator._call(prompt);
console.log("Analyzing image...");
const analysis = await creativeAssistant.invoke(
`Analyze this generated image with artistic analysis: ${imageUrl}`
);
return { imageUrl, analysis: analysis.content };
}
const result = await createAndAnalyze(
"A majestic dragon soaring over a crystal castle in a fantasy landscape"
);import {
isBuiltInTool,
isCustomTool,
parseCustomToolCall,
convertCompletionsCustomTool,
convertResponsesCustomTool
} from "@langchain/openai";
// Check tool types
const customWeatherTool = customTool(/* ... */);
const builtInTool = { type: "code_interpreter" };
console.log(isCustomTool(customWeatherTool)); // true
console.log(isBuiltInTool(builtInTool)); // true
// Convert between API formats
const completionsFormat = convertResponsesCustomTool(customWeatherTool);
const responsesFormat = convertCompletionsCustomTool(completionsFormat);import {
formatToOpenAIFunction,
formatToOpenAITool,
formatToOpenAIAssistantTool
} from "@langchain/openai";
import { StructuredTool } from "@langchain/core/tools";
// Convert LangChain tools to OpenAI formats
class WeatherTool extends StructuredTool {
name = "get_weather";
description = "Get weather information";
schema = z.object({
location: z.string()
});
async _call(args: { location: string }) {
return `Weather in ${args.location}: Sunny, 25°C`;
}
}
const weatherTool = new WeatherTool();
// Convert to different OpenAI tool formats
const functionFormat = formatToOpenAIFunction(weatherTool);
const toolFormat = formatToOpenAITool(weatherTool);
const assistantFormat = formatToOpenAIAssistantTool(weatherTool);
console.log("Function format:", functionFormat);
console.log("Tool format:", toolFormat);
console.log("Assistant format:", assistantFormat);// Best practices for DALL-E prompts
const imageGenerator = new DallEAPIWrapper({
model: "dall-e-3",
quality: "hd",
style: "vivid"
});
// Good prompt structure
const goodPrompts = [
"A photorealistic portrait of a wise elderly wizard with a long white beard, wearing star-covered robes, in a mystical library with floating books and magical glowing orbs",
"Vector art style illustration of a modern coffee shop interior with plants, wooden furniture, large windows showing city view, warm lighting, isometric perspective",
"Oil painting style landscape of rolling hills at golden hour, with wildflowers in the foreground, distant mountains, and dramatic clouds in the sky"
];
// Include style, subject, setting, lighting, and perspective for best results
for (const prompt of goodPrompts) {
const imageUrl = await imageGenerator._call(prompt);
console.log(`Generated: ${prompt.substring(0, 50)}... -> ${imageUrl}`);
}// Pattern 1: Stateless functional tools
const statelessTool = customTool(
async (input: string) => {
const params = JSON.parse(input);
// Pure function - no side effects
return JSON.stringify({ result: "processed", params });
},
{ /* schema */ }
);
// Pattern 2: Tools with external service integration
const serviceTool = customTool(
async (input: string) => {
const { query } = JSON.parse(input);
try {
// Make external API call
const response = await fetch(`https://api.example.com/search?q=${query}`);
const data = await response.json();
return JSON.stringify(data);
} catch (error) {
return JSON.stringify({ error: "Service unavailable" });
}
},
{
name: "external_search",
description: "Search external API service",
schema: z.object({ query: z.string() })
}
);
// Pattern 3: Stateful tools with context
class ContextualTool {
private context = new Map<string, any>();
createTool() {
return customTool(
async (input: string) => {
const { action, key, value } = JSON.parse(input);
switch (action) {
case "set":
this.context.set(key, value);
return JSON.stringify({ success: true, action, key });
case "get":
const result = this.context.get(key);
return JSON.stringify({ value: result, key });
case "list":
return JSON.stringify({ keys: Array.from(this.context.keys()) });
default:
return JSON.stringify({ error: "Unknown action" });
}
},
{
name: "memory_tool",
description: "Store and retrieve information in memory",
schema: z.object({
action: z.enum(["set", "get", "list"]),
key: z.string().optional(),
value: z.any().optional()
})
}
);
}
}
const contextualTool = new ContextualTool().createTool();// Comprehensive error handling for tools
const robustTool = customTool(
async (input: string) => {
try {
// Validate input format
let params;
try {
params = JSON.parse(input);
} catch {
return JSON.stringify({
error: "Invalid JSON input",
code: "INVALID_INPUT"
});
}
// Validate required parameters
const { operation, data } = params;
if (!operation) {
return JSON.stringify({
error: "Missing required parameter: operation",
code: "MISSING_PARAMETER"
});
}
// Process based on operation
switch (operation) {
case "validate":
// Perform validation logic
return JSON.stringify({
valid: true,
operation,
timestamp: new Date().toISOString()
});
default:
return JSON.stringify({
error: `Unknown operation: ${operation}`,
code: "UNKNOWN_OPERATION",
availableOperations: ["validate"]
});
}
} catch (error) {
return JSON.stringify({
error: "Internal tool error",
code: "INTERNAL_ERROR",
details: error instanceof Error ? error.message : "Unknown error"
});
}
},
{
name: "robust_processor",
description: "A robust tool with comprehensive error handling",
schema: z.object({
operation: z.string().describe("Operation to perform"),
data: z.any().optional().describe("Operation data")
})
}
);