0
# Tools
1
2
Image generation with DALL-E and custom tool creation for the OpenAI Responses API. Extend model capabilities with external functions and specialized tools.
3
4
## Capabilities
5
6
### DallEAPIWrapper Class
7
8
DALL-E image generation tool for creating images from text descriptions.
9
10
```typescript { .api }
11
/**
12
* DALL-E image generation tool
13
* Creates images from text descriptions using OpenAI's DALL-E models
14
*/
15
class DallEAPIWrapper extends Tool {
16
17
constructor(fields?: Partial<DallEAPIWrapperParams>);
18
19
/** Tool identification */
20
name: string; // "dalle_image_generator"
21
description: string; // "A tool for generating images from text descriptions"
22
23
/** Model configuration */
24
model: string; // Default: "dall-e-3"
25
style: "natural" | "vivid"; // Image style (default: "vivid")
26
quality: "standard" | "hd"; // Image quality (default: "standard")
27
n: number; // Number of images to generate (default: 1)
28
size: string; // Image size (default: "1024x1024")
29
dallEResponseFormat: "url" | "b64_json"; // Response format (default: "url")
30
31
/** Client configuration */
32
openAIApiKey?: string; // OpenAI API key
33
organization?: string; // OpenAI organization ID
34
baseURL?: string; // Custom base URL
35
timeout?: number; // Request timeout
36
maxRetries?: number; // Maximum retry attempts
37
38
/** Generate image(s) from text description */
39
_call(input: string): Promise<string>;
40
41
/** Make image generation requests with retry logic */
42
imageGenerationWithRetry<T>(
43
request: OpenAIClient.Images.ImageGenerateParams,
44
options?: OpenAICallOptions
45
): Promise<T>;
46
}
47
```
48
49
### DallE Parameters Interface
50
51
Configuration interface for DALL-E API wrapper.
52
53
```typescript { .api }
54
interface DallEAPIWrapperParams {
55
/** DALL-E model version */
56
model: string;
57
58
/** Image generation style */
59
style: "natural" | "vivid";
60
61
/** Image quality level */
62
quality: "standard" | "hd";
63
64
/** Number of images to generate */
65
n: number;
66
67
/** Image dimensions */
68
size: string;
69
70
/** Response format for generated images */
71
dallEResponseFormat: "url" | "b64_json";
72
73
/** OpenAI API configuration */
74
openAIApiKey?: string;
75
organization?: string;
76
baseURL?: string;
77
timeout?: number;
78
maxRetries?: number;
79
}
80
```
81
82
### Custom Tool Creation
83
84
Create custom tools for use with OpenAI's Responses API.
85
86
```typescript { .api }
87
/**
88
* Create custom tools for OpenAI Responses API
89
* Converts LangChain runnables into OpenAI-compatible tool definitions
90
*/
91
function customTool(
92
func: RunnableFunc<string, string, ToolRunnableConfig>,
93
fields: CustomToolFields
94
): DynamicTool<string>;
95
96
type CustomToolFields = Omit<OpenAI.Responses.CustomTool, "type">;
97
```
98
99
## Usage Examples
100
101
### Basic DALL-E Image Generation
102
103
```typescript
104
import { DallEAPIWrapper } from "@langchain/openai";
105
106
const dalleGenerator = new DallEAPIWrapper({
107
model: "dall-e-3",
108
apiKey: process.env.OPENAI_API_KEY,
109
quality: "hd",
110
size: "1024x1024",
111
style: "vivid"
112
});
113
114
// Generate a single image
115
const imageUrl = await dalleGenerator._call(
116
"A futuristic city skyline at sunset with flying cars and neon lights"
117
);
118
console.log("Generated image URL:", imageUrl);
119
120
// The returned URL can be used to display or download the image
121
```
122
123
### Different DALL-E Models and Configurations
124
125
```typescript
126
// DALL-E 3 (recommended) - high quality, single image
127
const dalleV3 = new DallEAPIWrapper({
128
model: "dall-e-3",
129
quality: "hd", // "standard" or "hd"
130
style: "natural", // "natural" or "vivid"
131
size: "1024x1792", // Portrait orientation
132
dallEResponseFormat: "url"
133
});
134
135
// DALL-E 2 - can generate multiple images
136
const dalleV2 = new DallEAPIWrapper({
137
model: "dall-e-2",
138
n: 4, // Generate 4 images (DALL-E 2 only)
139
size: "512x512", // Smaller size for DALL-E 2
140
quality: "standard" // HD not available for DALL-E 2
141
});
142
143
// Generate multiple images with DALL-E 2
144
const multipleImages = await dalleV2._call(
145
"A cute robot playing with a cat in a garden"
146
);
147
console.log("Multiple image URLs:", multipleImages);
148
```
149
150
### Supported Image Sizes
151
152
```typescript
153
// DALL-E 3 supported sizes
154
const dalle3Sizes = ["1024x1024", "1792x1024", "1024x1792"];
155
156
// DALL-E 2 supported sizes
157
const dalle2Sizes = ["256x256", "512x512", "1024x1024"];
158
159
// Create generators for different aspect ratios
160
const squareGenerator = new DallEAPIWrapper({
161
model: "dall-e-3",
162
size: "1024x1024" // Square
163
});
164
165
const landscapeGenerator = new DallEAPIWrapper({
166
model: "dall-e-3",
167
size: "1792x1024" // Landscape
168
});
169
170
const portraitGenerator = new DallEAPIWrapper({
171
model: "dall-e-3",
172
size: "1024x1792" // Portrait
173
});
174
```
175
176
### Base64 Response Format
177
178
```typescript
179
const dalle64 = new DallEAPIWrapper({
180
model: "dall-e-3",
181
dallEResponseFormat: "b64_json", // Return base64 encoded image
182
quality: "hd"
183
});
184
185
const base64Image = await dalle64._call("A serene mountain landscape");
186
187
// Convert base64 to buffer for saving
188
const imageBuffer = Buffer.from(base64Image, 'base64');
189
190
// Save to file (Node.js)
191
import { writeFileSync } from 'fs';
192
writeFileSync('generated_image.png', imageBuffer);
193
194
// Or use in web applications
195
const dataUrl = `data:image/png;base64,${base64Image}`;
196
// dataUrl can be used directly in <img> tags
197
```
198
199
### Custom Tool Creation
200
201
```typescript
202
import { customTool } from "@langchain/openai";
203
import { z } from "zod";
204
205
// Create a weather tool
206
const weatherTool = customTool(
207
async (input: string) => {
208
// Parse the input (it will be JSON string from the model)
209
const { location, units } = JSON.parse(input);
210
211
// Simulate weather API call
212
const temperature = Math.floor(Math.random() * 30) + 10;
213
const condition = ["sunny", "cloudy", "rainy"][Math.floor(Math.random() * 3)];
214
215
return JSON.stringify({
216
location,
217
temperature: `${temperature}°${units === 'celsius' ? 'C' : 'F'}`,
218
condition,
219
timestamp: new Date().toISOString()
220
});
221
},
222
{
223
name: "get_weather",
224
description: "Get current weather information for a specific location",
225
schema: z.object({
226
location: z.string().describe("City name or location"),
227
units: z.enum(["celsius", "fahrenheit"]).default("celsius").describe("Temperature units")
228
})
229
}
230
);
231
232
// Create a calculator tool
233
const calculatorTool = customTool(
234
async (input: string) => {
235
const { operation, a, b } = JSON.parse(input);
236
237
let result: number;
238
switch (operation) {
239
case "add": result = a + b; break;
240
case "subtract": result = a - b; break;
241
case "multiply": result = a * b; break;
242
case "divide": result = b !== 0 ? a / b : NaN; break;
243
default: throw new Error(`Unknown operation: ${operation}`);
244
}
245
246
return JSON.stringify({ result, operation, operands: [a, b] });
247
},
248
{
249
name: "calculator",
250
description: "Perform basic mathematical operations",
251
schema: z.object({
252
operation: z.enum(["add", "subtract", "multiply", "divide"]),
253
a: z.number().describe("First number"),
254
b: z.number().describe("Second number")
255
})
256
}
257
);
258
```
259
260
### Using Custom Tools with Chat Models
261
262
```typescript
263
import { ChatOpenAI } from "@langchain/openai";
264
265
// Create chat model that supports Responses API
266
const chatModel = new ChatOpenAI({
267
model: "gpt-4o",
268
temperature: 0,
269
useResponsesApi: true // Required for custom tools
270
});
271
272
// Bind custom tools to the model
273
const modelWithCustomTools = chatModel.bindTools([
274
weatherTool,
275
calculatorTool
276
]);
277
278
// The model can now call these custom tools
279
const response = await modelWithCustomTools.invoke(
280
"What's the weather in Tokyo? Also, what's 15 multiplied by 23?"
281
);
282
283
console.log("Model response:", response.content);
284
285
// Handle tool calls if any
286
if (response.tool_calls && response.tool_calls.length > 0) {
287
for (const toolCall of response.tool_calls) {
288
console.log(`Called tool: ${toolCall.name}`);
289
console.log(`With args: ${JSON.stringify(toolCall.args)}`);
290
}
291
}
292
```
293
294
### Advanced Custom Tool Example
295
296
```typescript
297
// Database search tool with complex schema
298
const databaseSearchTool = customTool(
299
async (input: string) => {
300
const { query, filters, limit, sortBy } = JSON.parse(input);
301
302
// Simulate database search
303
const mockResults = Array.from({ length: Math.min(limit, 5) }, (_, i) => ({
304
id: i + 1,
305
title: `Result ${i + 1} for "${query}"`,
306
score: Math.random(),
307
category: filters?.category || "general",
308
timestamp: new Date(Date.now() - Math.random() * 86400000).toISOString()
309
}));
310
311
// Sort results
312
if (sortBy === "score") {
313
mockResults.sort((a, b) => b.score - a.score);
314
} else if (sortBy === "date") {
315
mockResults.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
316
}
317
318
return JSON.stringify({
319
results: mockResults,
320
total: mockResults.length,
321
query,
322
filters
323
});
324
},
325
{
326
name: "search_database",
327
description: "Search the company database with advanced filtering and sorting",
328
schema: z.object({
329
query: z.string().describe("Search query string"),
330
filters: z.object({
331
category: z.string().optional().describe("Filter by category"),
332
dateRange: z.object({
333
start: z.string().optional().describe("Start date (ISO format)"),
334
end: z.string().optional().describe("End date (ISO format)")
335
}).optional().describe("Date range filter")
336
}).optional().describe("Search filters"),
337
limit: z.number().default(10).describe("Maximum number of results"),
338
sortBy: z.enum(["relevance", "date", "score"]).default("relevance").describe("Sort order")
339
})
340
}
341
);
342
343
// File processing tool
344
const fileProcessorTool = customTool(
345
async (input: string) => {
346
const { fileName, operation, options } = JSON.parse(input);
347
348
// Simulate file processing
349
const operations = {
350
analyze: () => ({ type: "text", lines: 150, words: 2340, size: "12KB" }),
351
compress: () => ({ originalSize: "12KB", compressedSize: "8KB", ratio: "33%" }),
352
convert: () => ({ from: "docx", to: options.format, status: "completed" })
353
};
354
355
const result = operations[operation as keyof typeof operations]?.() ||
356
{ error: "Unknown operation" };
357
358
return JSON.stringify({
359
fileName,
360
operation,
361
result,
362
timestamp: new Date().toISOString()
363
});
364
},
365
{
366
name: "process_file",
367
description: "Process files with various operations like analysis, compression, or conversion",
368
schema: z.object({
369
fileName: z.string().describe("Name of the file to process"),
370
operation: z.enum(["analyze", "compress", "convert"]).describe("Operation to perform"),
371
options: z.object({
372
format: z.string().optional().describe("Target format for conversion"),
373
quality: z.enum(["low", "medium", "high"]).optional().describe("Quality level")
374
}).optional().describe("Additional operation options")
375
})
376
}
377
);
378
```
379
380
### Error Handling with Tools
381
382
```typescript
383
// Robust tool with error handling
384
const apiTool = customTool(
385
async (input: string) => {
386
try {
387
const { endpoint, method, data } = JSON.parse(input);
388
389
// Simulate API call
390
if (endpoint === "/error") {
391
throw new Error("API endpoint not found");
392
}
393
394
return JSON.stringify({
395
status: "success",
396
endpoint,
397
method,
398
response: { message: "API call successful", data }
399
});
400
401
} catch (error) {
402
return JSON.stringify({
403
status: "error",
404
error: error instanceof Error ? error.message : "Unknown error",
405
timestamp: new Date().toISOString()
406
});
407
}
408
},
409
{
410
name: "api_call",
411
description: "Make API calls to external services",
412
schema: z.object({
413
endpoint: z.string().describe("API endpoint URL"),
414
method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET").describe("HTTP method"),
415
data: z.record(z.any()).optional().describe("Request payload")
416
})
417
}
418
);
419
420
// Test error handling
421
const chatWithErrorHandling = new ChatOpenAI({
422
model: "gpt-4o",
423
useResponsesApi: true
424
}).bindTools([apiTool]);
425
426
const errorResponse = await chatWithErrorHandling.invoke(
427
"Make a GET request to /error endpoint"
428
);
429
```
430
431
### Combining DALL-E with Custom Tools
432
433
```typescript
434
// Create a comprehensive creative assistant
435
const imageDescriptionTool = customTool(
436
async (input: string) => {
437
const { imageUrl, analysisType } = JSON.parse(input);
438
439
// Simulate image analysis (in real app, use vision API)
440
const analyses = {
441
artistic: "This image shows vibrant colors with impressionist style brushstrokes...",
442
technical: "Resolution: 1024x1024, Color depth: 24-bit, Format: PNG...",
443
content: "The image depicts a futuristic cityscape with neon lighting..."
444
};
445
446
return JSON.stringify({
447
analysis: analyses[analysisType as keyof typeof analyses] || "General analysis not available",
448
imageUrl,
449
analysisType
450
});
451
},
452
{
453
name: "analyze_image",
454
description: "Analyze generated images for artistic, technical, or content details",
455
schema: z.object({
456
imageUrl: z.string().describe("URL of the image to analyze"),
457
analysisType: z.enum(["artistic", "technical", "content"]).describe("Type of analysis to perform")
458
})
459
}
460
);
461
462
// Creative assistant with image generation and analysis
463
const creativeAssistant = new ChatOpenAI({
464
model: "gpt-4o",
465
useResponsesApi: true
466
}).bindTools([imageDescriptionTool]);
467
468
// Also create DALL-E wrapper for image generation
469
const imageGenerator = new DallEAPIWrapper({
470
model: "dall-e-3",
471
quality: "hd",
472
style: "vivid"
473
});
474
475
// Workflow: generate image, then analyze it
476
async function createAndAnalyze(prompt: string) {
477
console.log("Generating image...");
478
const imageUrl = await imageGenerator._call(prompt);
479
480
console.log("Analyzing image...");
481
const analysis = await creativeAssistant.invoke(
482
`Analyze this generated image with artistic analysis: ${imageUrl}`
483
);
484
485
return { imageUrl, analysis: analysis.content };
486
}
487
488
const result = await createAndAnalyze(
489
"A majestic dragon soaring over a crystal castle in a fantasy landscape"
490
);
491
```
492
493
## Tool Utilities
494
495
### Built-in Tool Detection
496
497
```typescript
498
import {
499
isBuiltInTool,
500
isCustomTool,
501
parseCustomToolCall,
502
convertCompletionsCustomTool,
503
convertResponsesCustomTool
504
} from "@langchain/openai";
505
506
// Check tool types
507
const customWeatherTool = customTool(/* ... */);
508
const builtInTool = { type: "code_interpreter" };
509
510
console.log(isCustomTool(customWeatherTool)); // true
511
console.log(isBuiltInTool(builtInTool)); // true
512
513
// Convert between API formats
514
const completionsFormat = convertResponsesCustomTool(customWeatherTool);
515
const responsesFormat = convertCompletionsCustomTool(completionsFormat);
516
```
517
518
### Tool Conversion Utilities
519
520
```typescript
521
import {
522
formatToOpenAIFunction,
523
formatToOpenAITool,
524
formatToOpenAIAssistantTool
525
} from "@langchain/openai";
526
import { StructuredTool } from "@langchain/core/tools";
527
528
// Convert LangChain tools to OpenAI formats
529
class WeatherTool extends StructuredTool {
530
name = "get_weather";
531
description = "Get weather information";
532
533
schema = z.object({
534
location: z.string()
535
});
536
537
async _call(args: { location: string }) {
538
return `Weather in ${args.location}: Sunny, 25°C`;
539
}
540
}
541
542
const weatherTool = new WeatherTool();
543
544
// Convert to different OpenAI tool formats
545
const functionFormat = formatToOpenAIFunction(weatherTool);
546
const toolFormat = formatToOpenAITool(weatherTool);
547
const assistantFormat = formatToOpenAIAssistantTool(weatherTool);
548
549
console.log("Function format:", functionFormat);
550
console.log("Tool format:", toolFormat);
551
console.log("Assistant format:", assistantFormat);
552
```
553
554
## Best Practices
555
556
### Image Generation Guidelines
557
558
```typescript
559
// Best practices for DALL-E prompts
560
const imageGenerator = new DallEAPIWrapper({
561
model: "dall-e-3",
562
quality: "hd",
563
style: "vivid"
564
});
565
566
// Good prompt structure
567
const goodPrompts = [
568
"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",
569
570
"Vector art style illustration of a modern coffee shop interior with plants, wooden furniture, large windows showing city view, warm lighting, isometric perspective",
571
572
"Oil painting style landscape of rolling hills at golden hour, with wildflowers in the foreground, distant mountains, and dramatic clouds in the sky"
573
];
574
575
// Include style, subject, setting, lighting, and perspective for best results
576
for (const prompt of goodPrompts) {
577
const imageUrl = await imageGenerator._call(prompt);
578
console.log(`Generated: ${prompt.substring(0, 50)}... -> ${imageUrl}`);
579
}
580
```
581
582
### Custom Tool Design Patterns
583
584
```typescript
585
// Pattern 1: Stateless functional tools
586
const statelessTool = customTool(
587
async (input: string) => {
588
const params = JSON.parse(input);
589
// Pure function - no side effects
590
return JSON.stringify({ result: "processed", params });
591
},
592
{ /* schema */ }
593
);
594
595
// Pattern 2: Tools with external service integration
596
const serviceTool = customTool(
597
async (input: string) => {
598
const { query } = JSON.parse(input);
599
600
try {
601
// Make external API call
602
const response = await fetch(`https://api.example.com/search?q=${query}`);
603
const data = await response.json();
604
return JSON.stringify(data);
605
} catch (error) {
606
return JSON.stringify({ error: "Service unavailable" });
607
}
608
},
609
{
610
name: "external_search",
611
description: "Search external API service",
612
schema: z.object({ query: z.string() })
613
}
614
);
615
616
// Pattern 3: Stateful tools with context
617
class ContextualTool {
618
private context = new Map<string, any>();
619
620
createTool() {
621
return customTool(
622
async (input: string) => {
623
const { action, key, value } = JSON.parse(input);
624
625
switch (action) {
626
case "set":
627
this.context.set(key, value);
628
return JSON.stringify({ success: true, action, key });
629
case "get":
630
const result = this.context.get(key);
631
return JSON.stringify({ value: result, key });
632
case "list":
633
return JSON.stringify({ keys: Array.from(this.context.keys()) });
634
default:
635
return JSON.stringify({ error: "Unknown action" });
636
}
637
},
638
{
639
name: "memory_tool",
640
description: "Store and retrieve information in memory",
641
schema: z.object({
642
action: z.enum(["set", "get", "list"]),
643
key: z.string().optional(),
644
value: z.any().optional()
645
})
646
}
647
);
648
}
649
}
650
651
const contextualTool = new ContextualTool().createTool();
652
```
653
654
### Error Handling and Validation
655
656
```typescript
657
// Comprehensive error handling for tools
658
const robustTool = customTool(
659
async (input: string) => {
660
try {
661
// Validate input format
662
let params;
663
try {
664
params = JSON.parse(input);
665
} catch {
666
return JSON.stringify({
667
error: "Invalid JSON input",
668
code: "INVALID_INPUT"
669
});
670
}
671
672
// Validate required parameters
673
const { operation, data } = params;
674
if (!operation) {
675
return JSON.stringify({
676
error: "Missing required parameter: operation",
677
code: "MISSING_PARAMETER"
678
});
679
}
680
681
// Process based on operation
682
switch (operation) {
683
case "validate":
684
// Perform validation logic
685
return JSON.stringify({
686
valid: true,
687
operation,
688
timestamp: new Date().toISOString()
689
});
690
691
default:
692
return JSON.stringify({
693
error: `Unknown operation: ${operation}`,
694
code: "UNKNOWN_OPERATION",
695
availableOperations: ["validate"]
696
});
697
}
698
699
} catch (error) {
700
return JSON.stringify({
701
error: "Internal tool error",
702
code: "INTERNAL_ERROR",
703
details: error instanceof Error ? error.message : "Unknown error"
704
});
705
}
706
},
707
{
708
name: "robust_processor",
709
description: "A robust tool with comprehensive error handling",
710
schema: z.object({
711
operation: z.string().describe("Operation to perform"),
712
data: z.any().optional().describe("Operation data")
713
})
714
}
715
);
716
```