feat(providers): Add research API and image generation/editing support; extend providers and tests
This commit is contained in:
203
test/test.image.openai.ts
Normal file
203
test/test.image.openai.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as qenv from '@push.rocks/qenv';
|
||||
import * as smartai from '../ts/index.js';
|
||||
import * as path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const testQenv = new qenv.Qenv('./', './.nogit/');
|
||||
|
||||
let openaiProvider: smartai.OpenAiProvider;
|
||||
|
||||
// Helper function to save image results
|
||||
async function saveImageResult(testName: string, result: any) {
|
||||
const sanitizedName = testName.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const filename = `openai_${sanitizedName}_${timestamp}.json`;
|
||||
const filepath = path.join('.nogit', 'testresults', 'images', filename);
|
||||
|
||||
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
||||
await fs.writeFile(filepath, JSON.stringify(result, null, 2), 'utf-8');
|
||||
|
||||
console.log(` 💾 Saved to: ${filepath}`);
|
||||
|
||||
// Also save the actual image if b64_json is present
|
||||
if (result.images && result.images[0]?.b64_json) {
|
||||
const imageFilename = `openai_${sanitizedName}_${timestamp}.png`;
|
||||
const imageFilepath = path.join('.nogit', 'testresults', 'images', imageFilename);
|
||||
await fs.writeFile(imageFilepath, Buffer.from(result.images[0].b64_json, 'base64'));
|
||||
console.log(` 🖼️ Image saved to: ${imageFilepath}`);
|
||||
}
|
||||
}
|
||||
|
||||
tap.test('OpenAI Image Generation: should initialize provider', async () => {
|
||||
const openaiToken = await testQenv.getEnvVarOnDemand('OPENAI_TOKEN');
|
||||
expect(openaiToken).toBeTruthy();
|
||||
|
||||
openaiProvider = new smartai.OpenAiProvider({
|
||||
openaiToken,
|
||||
imageModel: 'gpt-image-1'
|
||||
});
|
||||
|
||||
await openaiProvider.start();
|
||||
expect(openaiProvider).toBeInstanceOf(smartai.OpenAiProvider);
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: Basic generation with gpt-image-1', async () => {
|
||||
const result = await openaiProvider.imageGenerate({
|
||||
prompt: 'A cute robot reading a book in a cozy library, digital art style',
|
||||
model: 'gpt-image-1',
|
||||
quality: 'medium',
|
||||
size: '1024x1024'
|
||||
});
|
||||
|
||||
console.log('Basic gpt-image-1 Generation:');
|
||||
console.log('- Images generated:', result.images.length);
|
||||
console.log('- Model used:', result.metadata?.model);
|
||||
console.log('- Quality:', result.metadata?.quality);
|
||||
console.log('- Size:', result.metadata?.size);
|
||||
console.log('- Tokens used:', result.metadata?.tokensUsed);
|
||||
|
||||
await saveImageResult('basic_generation_gptimage1', result);
|
||||
|
||||
expect(result.images).toBeTruthy();
|
||||
expect(result.images.length).toEqual(1);
|
||||
expect(result.images[0].b64_json).toBeTruthy();
|
||||
expect(result.metadata?.model).toEqual('gpt-image-1');
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: High quality with transparent background', async () => {
|
||||
const result = await openaiProvider.imageGenerate({
|
||||
prompt: 'A simple geometric logo of a mountain peak, minimal design, clean lines',
|
||||
model: 'gpt-image-1',
|
||||
quality: 'high',
|
||||
size: '1024x1024',
|
||||
background: 'transparent',
|
||||
outputFormat: 'png'
|
||||
});
|
||||
|
||||
console.log('High Quality Transparent:');
|
||||
console.log('- Quality:', result.metadata?.quality);
|
||||
console.log('- Background: transparent');
|
||||
console.log('- Format:', result.metadata?.outputFormat);
|
||||
console.log('- Tokens used:', result.metadata?.tokensUsed);
|
||||
|
||||
await saveImageResult('high_quality_transparent', result);
|
||||
|
||||
expect(result.images.length).toEqual(1);
|
||||
expect(result.images[0].b64_json).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: WebP format with compression', async () => {
|
||||
const result = await openaiProvider.imageGenerate({
|
||||
prompt: 'A futuristic cityscape at sunset with flying cars, photorealistic',
|
||||
model: 'gpt-image-1',
|
||||
quality: 'high',
|
||||
size: '1536x1024',
|
||||
outputFormat: 'webp',
|
||||
outputCompression: 85
|
||||
});
|
||||
|
||||
console.log('WebP with Compression:');
|
||||
console.log('- Format:', result.metadata?.outputFormat);
|
||||
console.log('- Compression: 85%');
|
||||
console.log('- Size:', result.metadata?.size);
|
||||
|
||||
await saveImageResult('webp_compression', result);
|
||||
|
||||
expect(result.images.length).toEqual(1);
|
||||
expect(result.images[0].b64_json).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: Text rendering with gpt-image-1', async () => {
|
||||
const result = await openaiProvider.imageGenerate({
|
||||
prompt: 'A vintage cafe sign that says "COFFEE & CODE" in elegant hand-lettered typography, warm colors',
|
||||
model: 'gpt-image-1',
|
||||
quality: 'high',
|
||||
size: '1024x1024'
|
||||
});
|
||||
|
||||
console.log('Text Rendering:');
|
||||
console.log('- Prompt includes text: "COFFEE & CODE"');
|
||||
console.log('- gpt-image-1 has superior text rendering');
|
||||
console.log('- Tokens used:', result.metadata?.tokensUsed);
|
||||
|
||||
await saveImageResult('text_rendering', result);
|
||||
|
||||
expect(result.images.length).toEqual(1);
|
||||
expect(result.images[0].b64_json).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: Multiple images generation', async () => {
|
||||
const result = await openaiProvider.imageGenerate({
|
||||
prompt: 'Abstract colorful geometric patterns, modern minimalist art',
|
||||
model: 'gpt-image-1',
|
||||
n: 2,
|
||||
quality: 'medium',
|
||||
size: '1024x1024'
|
||||
});
|
||||
|
||||
console.log('Multiple Images:');
|
||||
console.log('- Images requested: 2');
|
||||
console.log('- Images generated:', result.images.length);
|
||||
|
||||
await saveImageResult('multiple_images', result);
|
||||
|
||||
expect(result.images.length).toEqual(2);
|
||||
expect(result.images[0].b64_json).toBeTruthy();
|
||||
expect(result.images[1].b64_json).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: Low moderation setting', async () => {
|
||||
const result = await openaiProvider.imageGenerate({
|
||||
prompt: 'A fantasy battle scene with warriors and dragons',
|
||||
model: 'gpt-image-1',
|
||||
moderation: 'low',
|
||||
quality: 'medium'
|
||||
});
|
||||
|
||||
console.log('Low Moderation:');
|
||||
console.log('- Moderation: low (less restrictive filtering)');
|
||||
console.log('- Tokens used:', result.metadata?.tokensUsed);
|
||||
|
||||
await saveImageResult('low_moderation', result);
|
||||
|
||||
expect(result.images.length).toEqual(1);
|
||||
expect(result.images[0].b64_json).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image Editing: edit with gpt-image-1', async () => {
|
||||
// First, generate a base image
|
||||
const baseResult = await openaiProvider.imageGenerate({
|
||||
prompt: 'A simple white cat sitting on a red cushion',
|
||||
model: 'gpt-image-1',
|
||||
quality: 'low',
|
||||
size: '1024x1024'
|
||||
});
|
||||
|
||||
const baseImageBuffer = Buffer.from(baseResult.images[0].b64_json!, 'base64');
|
||||
|
||||
// Now edit it
|
||||
const editResult = await openaiProvider.imageEdit({
|
||||
image: baseImageBuffer,
|
||||
prompt: 'Change the cat to orange and add stylish sunglasses',
|
||||
model: 'gpt-image-1',
|
||||
quality: 'medium'
|
||||
});
|
||||
|
||||
console.log('Image Editing:');
|
||||
console.log('- Base image created');
|
||||
console.log('- Edit: change color and add sunglasses');
|
||||
console.log('- Result images:', editResult.images.length);
|
||||
|
||||
await saveImageResult('image_edit', editResult);
|
||||
|
||||
expect(editResult.images.length).toEqual(1);
|
||||
expect(editResult.images[0].b64_json).toBeTruthy();
|
||||
});
|
||||
|
||||
tap.test('OpenAI Image: should clean up provider', async () => {
|
||||
await openaiProvider.stop();
|
||||
console.log('OpenAI image provider stopped successfully');
|
||||
});
|
||||
|
||||
export default tap.start();
|
Reference in New Issue
Block a user