Skip to content

Commit acbb8e8

Browse files
authored
📦 NEW: Images Primitive (#119)
* 📦 NEW: Images Primitive * 👌 IMPROVE: Image Input
1 parent d2d07b1 commit acbb8e8

File tree

10 files changed

+286
-3
lines changed

10 files changed

+286
-3
lines changed

examples/nodejs/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ LANGBASE_API_KEY=""
77
EXA_API_KEY=""
88
CRAWL_KEY=""
99
FIRECRAWL_KEY=""
10+
11+
# Image generation API keys
12+
# https://platform.openai.com/api-keys
13+
OPENAI_API_KEY=""
14+
# https://console.cloud.google.com/apis/credentials
15+
GOOGLE_API_KEY=""
16+
# https://api.together.xyz/settings/api-keys
17+
TOGETHER_API_KEY=""
18+
# https://openrouter.ai/keys
19+
OPENROUTER_API_KEY=""

examples/nodejs/images/google.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'dotenv/config';
2+
import {Langbase} from 'langbase';
3+
4+
async function main() {
5+
// Check if API keys are available
6+
if (!process.env.LANGBASE_API_KEY) {
7+
console.error('❌ LANGBASE_API_KEY is not set in environment variables');
8+
return;
9+
}
10+
11+
if (!process.env.GOOGLE_API_KEY) {
12+
console.error('❌ GOOGLE_API_KEY is not set in environment variables');
13+
console.log('Please add your Google API key to the .env file');
14+
return;
15+
}
16+
17+
const langbase = new Langbase({
18+
apiKey: process.env.LANGBASE_API_KEY!
19+
});
20+
21+
try {
22+
const result = await langbase.images.generate({
23+
prompt: "A futuristic cityscape with flying cars and neon lights, cyberpunk style, highly detailed, 8k resolution",
24+
model: "google:gemini-2.5-flash-image-preview",
25+
apiKey: process.env.GOOGLE_API_KEY!
26+
});
27+
28+
console.log('✅ Image generated successfully!');
29+
console.log('Generated images:', result);
30+
31+
} catch (error) {
32+
console.error('❌ Error generating image:', error);
33+
}
34+
}
35+
36+
main();

examples/nodejs/images/openai.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'dotenv/config';
2+
import {Langbase} from '../../../packages/langbase/src/langbase/langbase';
3+
4+
async function main() {
5+
// Check if API keys are available
6+
if (!process.env.LANGBASE_API_KEY) {
7+
console.error('❌ LANGBASE_API_KEY is not set in environment variables');
8+
return;
9+
}
10+
11+
if (!process.env.OPENAI_API_KEY) {
12+
console.error('❌ OPENAI_API_KEY is not set in environment variables');
13+
console.log('Please add your OpenAI API key to the .env file');
14+
return;
15+
}
16+
17+
const langbase = new Langbase({
18+
apiKey: process.env.LANGBASE_API_KEY!
19+
});
20+
21+
try {
22+
const result = await langbase.images.generate({
23+
prompt: "A futuristic cityscape with flying cars and neon lights, cyberpunk style, highly detailed, 8k resolution",
24+
model: "openai:gpt-image-1",
25+
apiKey: process.env.OPENAI_API_KEY!
26+
});
27+
28+
console.log('✅ Image generated successfully!');
29+
console.log('Generated images:', result);
30+
31+
} catch (error) {
32+
console.error('❌ Error generating image:', error);
33+
}
34+
}
35+
36+
main();
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'dotenv/config';
2+
import {Langbase} from 'langbase';
3+
4+
async function main() {
5+
// Check if API keys are available
6+
if (!process.env.LANGBASE_API_KEY) {
7+
console.error('❌ LANGBASE_API_KEY is not set in environment variables');
8+
return;
9+
}
10+
11+
if (!process.env.OPENROUTER_API_KEY) {
12+
console.error('❌ OPENROUTER_API_KEY is not set in environment variables');
13+
console.log('Please add your OpenRouter API key to the .env file');
14+
return;
15+
}
16+
17+
const langbase = new Langbase({
18+
apiKey: process.env.LANGBASE_API_KEY!,
19+
});
20+
21+
try {
22+
const result = await langbase.images.generate({
23+
prompt: "A majestic dragon soaring through clouds above a fantasy castle, epic fantasy art style, detailed scales and wings",
24+
model: "openrouter:google/gemini-2.5-flash-image-preview",
25+
apiKey: process.env.OPENROUTER_API_KEY
26+
});
27+
28+
console.log('✅ Image generated successfully via OpenRouter!');
29+
console.log('Generated images:', result);
30+
31+
} catch (error) {
32+
console.error('❌ Error generating image:', error);
33+
console.log('Note: Make sure you have a valid OpenRouter API key and credits');
34+
}
35+
}
36+
37+
main();

examples/nodejs/images/together.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'dotenv/config';
2+
import {Langbase} from 'langbase';
3+
4+
async function main() {
5+
// Check if API keys are available
6+
if (!process.env.LANGBASE_API_KEY) {
7+
console.error('❌ LANGBASE_API_KEY is not set in environment variables');
8+
return;
9+
}
10+
11+
if (!process.env.GOOGLE_API_KEY) {
12+
console.error('❌ GOOGLE_API_KEY is not set in environment variables');
13+
console.log('Please add your Google API key to the .env file');
14+
return;
15+
}
16+
17+
const langbase = new Langbase({
18+
apiKey: process.env.LANGBASE_API_KEY!
19+
});
20+
21+
try {
22+
const result = await langbase.images.generate({
23+
prompt: "A serene mountain landscape at sunset with a crystal clear lake reflecting the sky",
24+
model: "together:black-forest-labs/FLUX.1-schnell-Free",
25+
width: 1024,
26+
height: 1024,
27+
n: 1,
28+
apiKey: process.env.TOGETHER_API_KEY!,
29+
});
30+
31+
if (!result?.choices?.[0]?.message?.images?.[0]?.image_url?.url) {
32+
console.error('❌ Image generation did not return an image. Full response:', result);
33+
return;
34+
}
35+
36+
console.log(result);
37+
} catch (error) {
38+
console.error('❌ Error generating image:', error);
39+
}
40+
}
41+
42+
main();

examples/nodejs/package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,25 @@
4444
"threads.messages.list": "npx tsx ./threads/threads.messages.list.ts",
4545
"threads.get": "npx tsx ./threads/threads.get.ts",
4646
"workflow": "npx tsx ./workflows/workflows.ts",
47-
"agent.run.mcp": "npx tsx ./agent/agent.run.mcp.ts"
47+
"agent.run.mcp": "npx tsx ./agent/agent.run.mcp.ts",
48+
"images.openai": "npx tsx ./images/openai.ts",
49+
"images.google": "npx tsx ./images/google.ts",
50+
"images.together": "npx tsx ./images/together.ts",
51+
"images.openrouter": "npx tsx ./images/openrouter.ts"
52+
4853
},
4954
"keywords": [],
5055
"author": "Ahmad Awais <[email protected]> (https://twitter.com/MrAhmadAwais)",
5156
"license": "UNLICENSED",
5257
"dependencies": {
5358
"@langbase/cli": "^0.1.7",
5459
"dotenv": "^16.4.5",
55-
"langbase": "1.2.3",
60+
"langbase": "workspace:*",
5661
"uuid": "^11.1.0",
5762
"zod": "^3.21.4",
5863
"zod-to-json-schema": "^3.24.5"
64+
},
65+
"devDependencies": {
66+
"tsx": "^4.19.1"
5967
}
6068
}

examples/nodejs/readme.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,10 @@ npm run thread.delete
5353

5454
# agent
5555
npm run agent.run.mcp
56+
57+
# images
58+
npm run images.openai
59+
npm run images.google
60+
npm run images.together
61+
npm run images.openrouter
5662
```

packages/langbase/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './langbase/langbase';
22
export * from './pipes/pipes';
33
export * from './lib/helpers';
44
export * from './langbase/workflows';
5+
export * from './langbase/images';
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {Request} from '../common/request';
2+
3+
export interface ImageGenerationOptions {
4+
prompt: string;
5+
model: string;
6+
width?: number;
7+
height?: number;
8+
image_url?: string;
9+
steps?: number;
10+
n?: number;
11+
negative_prompt?: string;
12+
apiKey: string;
13+
[key: string]: any;
14+
}
15+
16+
export interface ImageGenerationResponse {
17+
id: string;
18+
provider: string;
19+
model: string;
20+
object: string;
21+
created: number;
22+
choices: Array<{
23+
logprobs: null;
24+
finish_reason: string;
25+
native_finish_reason: string;
26+
index: number;
27+
message: {
28+
role: string;
29+
content: string | null;
30+
images: Array<{
31+
type: string;
32+
image_url: {
33+
url: string;
34+
};
35+
index: number;
36+
}>;
37+
};
38+
}>;
39+
usage: {
40+
prompt_tokens: number;
41+
completion_tokens: number;
42+
total_tokens: number;
43+
prompt_tokens_details: {
44+
cached_tokens: number;
45+
};
46+
completion_tokens_details: {
47+
reasoning_tokens: number;
48+
image_tokens: number;
49+
};
50+
};
51+
}
52+
53+
export class Images {
54+
private request: Request;
55+
56+
constructor(request: Request) {
57+
this.request = request;
58+
}
59+
60+
/**
61+
* Generate images using various AI providers
62+
*
63+
* @param options - Image generation options
64+
* @returns Promise that resolves to the image generation response
65+
*/
66+
async generate(
67+
options: ImageGenerationOptions,
68+
): Promise<ImageGenerationResponse> {
69+
// Comprehensive input validation
70+
if (!options) {
71+
throw new Error('Image generation options are required.');
72+
}
73+
74+
// Extract apiKey from options for headers, remove from body
75+
const {apiKey, ...imageParams} = options;
76+
77+
try {
78+
return await this.request.post<ImageGenerationResponse>({
79+
endpoint: '/v1/images',
80+
body: imageParams,
81+
headers: {
82+
'LB-LLM-Key': apiKey,
83+
},
84+
});
85+
} catch (error: any) {
86+
console.error(error);
87+
throw new Error(error.message);
88+
}
89+
}
90+
}

packages/langbase/src/langbase/langbase.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import {convertDocToFormData} from '@/lib/utils/doc-to-formdata';
22
import {Request} from '../common/request';
33
import {Workflow} from './workflows';
4+
import {
5+
Images,
6+
ImageGenerationOptions,
7+
ImageGenerationResponse,
8+
} from './images';
49

510
export type Role = 'user' | 'assistant' | 'system' | 'tool';
611

@@ -646,6 +651,12 @@ export class Langbase {
646651
create: (trace: any) => Promise<any>;
647652
};
648653

654+
public images: {
655+
generate: (
656+
options: ImageGenerationOptions,
657+
) => Promise<ImageGenerationResponse>;
658+
};
659+
649660
constructor(options?: LangbaseOptions) {
650661
this.baseUrl = options?.baseUrl ?? 'https://api.langbase.com';
651662
this.apiKey = options?.apiKey ?? '';
@@ -737,6 +748,12 @@ export class Langbase {
737748
this.traces = {
738749
create: this.createTrace.bind(this),
739750
};
751+
752+
// Initialize images primitive
753+
const imagesInstance = new Images(this.request);
754+
this.images = {
755+
generate: imagesInstance.generate.bind(imagesInstance),
756+
};
740757
}
741758

742759
private async runPipe(
@@ -942,7 +959,7 @@ export class Langbase {
942959
Authorization: `Bearer ${this.apiKey}`,
943960
'Content-Type': options.contentType,
944961
},
945-
body: options.document,
962+
body: options.document as any,
946963
});
947964
} catch (error) {
948965
throw error;

0 commit comments

Comments
 (0)