174 lines
4.5 KiB
TypeScript
174 lines
4.5 KiB
TypeScript
/**
|
|
* Meta AI Crawl4AI Client
|
|
*
|
|
* TypeScript client for the Python Crawl4AI microservice
|
|
* that handles Meta AI image generation with bot detection bypass.
|
|
*/
|
|
|
|
const CRAWL4AI_URL = process.env.CRAWL4AI_URL || 'http://localhost:8000';
|
|
|
|
export interface MetaCrawlImage {
|
|
url: string;
|
|
data?: string; // base64
|
|
prompt: string;
|
|
model: string;
|
|
}
|
|
|
|
export interface MetaCrawlResponse {
|
|
success: boolean;
|
|
images: MetaCrawlImage[];
|
|
error?: string;
|
|
task_id?: string;
|
|
}
|
|
|
|
export interface TaskStatusResponse {
|
|
task_id: string;
|
|
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
images: MetaCrawlImage[];
|
|
error?: string;
|
|
progress?: number;
|
|
}
|
|
|
|
export class MetaCrawlClient {
|
|
private baseUrl: string;
|
|
|
|
constructor(baseUrl?: string) {
|
|
this.baseUrl = baseUrl || CRAWL4AI_URL;
|
|
}
|
|
|
|
/**
|
|
* Generate images synchronously (waits for completion)
|
|
*/
|
|
async generate(
|
|
prompt: string,
|
|
cookies: string,
|
|
numImages: number = 4
|
|
): Promise<MetaCrawlImage[]> {
|
|
console.log(`[MetaCrawl] Sending request to ${this.baseUrl}/generate/sync`);
|
|
|
|
const response = await fetch(`${this.baseUrl}/generate/sync`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
prompt,
|
|
cookies,
|
|
num_images: numImages
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
throw new Error(`MetaCrawl service error: ${response.status} - ${errorText}`);
|
|
}
|
|
|
|
const data: MetaCrawlResponse = await response.json();
|
|
|
|
if (!data.success) {
|
|
throw new Error(data.error || 'Image generation failed');
|
|
}
|
|
|
|
return data.images;
|
|
}
|
|
|
|
/**
|
|
* Start async image generation (returns immediately with task_id)
|
|
*/
|
|
async generateAsync(
|
|
prompt: string,
|
|
cookies: string,
|
|
numImages: number = 4
|
|
): Promise<string> {
|
|
const response = await fetch(`${this.baseUrl}/generate`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
prompt,
|
|
cookies,
|
|
num_images: numImages
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`MetaCrawl service error: ${response.status}`);
|
|
}
|
|
|
|
const data: MetaCrawlResponse = await response.json();
|
|
|
|
if (!data.task_id) {
|
|
throw new Error('No task_id returned from async generation');
|
|
}
|
|
|
|
return data.task_id;
|
|
}
|
|
|
|
/**
|
|
* Get status of an async generation task
|
|
*/
|
|
async getTaskStatus(taskId: string): Promise<TaskStatusResponse> {
|
|
const response = await fetch(`${this.baseUrl}/status/${taskId}`);
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
throw new Error('Task not found');
|
|
}
|
|
throw new Error(`MetaCrawl service error: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
/**
|
|
* Poll for async task completion
|
|
*/
|
|
async waitForCompletion(
|
|
taskId: string,
|
|
pollIntervalMs: number = 2000,
|
|
timeoutMs: number = 120000
|
|
): Promise<MetaCrawlImage[]> {
|
|
const startTime = Date.now();
|
|
|
|
while (Date.now() - startTime < timeoutMs) {
|
|
const status = await this.getTaskStatus(taskId);
|
|
|
|
if (status.status === 'completed') {
|
|
return status.images;
|
|
}
|
|
|
|
if (status.status === 'failed') {
|
|
throw new Error(status.error || 'Generation failed');
|
|
}
|
|
|
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
}
|
|
|
|
throw new Error('Task timed out');
|
|
}
|
|
|
|
/**
|
|
* Check if the Crawl4AI service is healthy
|
|
*/
|
|
async healthCheck(): Promise<boolean> {
|
|
try {
|
|
const response = await fetch(`${this.baseUrl}/health`);
|
|
return response.ok;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get current rate limit status
|
|
*/
|
|
async getRateLimitStatus(): Promise<{
|
|
requests_this_hour: number;
|
|
max_per_hour: number;
|
|
delay_seconds: number;
|
|
}> {
|
|
const response = await fetch(`${this.baseUrl}/rate-limit`);
|
|
return response.json();
|
|
}
|
|
}
|