Configuração
Guia de configuração e configuração para Qwen Image Edit
Configuração Inicial
Variáveis de Ambiente
Configure as variáveis de ambiente para diferentes ambientes:
# .env.development
QWEN_API_KEY=sua-chave-api-desenvolvimento
QWEN_REGION=us-east-1
QWEN_DEBUG=true
QWEN_CACHE_ENABLED=true
QWEN_CACHE_TTL=3600
QWEN_MAX_RETRIES=3
QWEN_TIMEOUT=30000
# .env.production
QWEN_API_KEY=sua-chave-api-producao
QWEN_REGION=us-east-1
QWEN_DEBUG=false
QWEN_CACHE_ENABLED=true
QWEN_CACHE_TTL=7200
QWEN_MAX_RETRIES=5
QWEN_TIMEOUT=60000
# .env.test
QWEN_API_KEY=sua-chave-api-teste
QWEN_REGION=us-east-1
QWEN_DEBUG=true
QWEN_CACHE_ENABLED=false
QWEN_MOCK_ENABLED=true
Configuração do Cliente
import { QwenImageEdit } from 'qwen-image-edit';
// Configuração básica
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
region: process.env.QWEN_REGION || 'us-east-1'
});
// Configuração avançada
const editor = new QwenImageEdit({
// Autenticação
apiKey: process.env.QWEN_API_KEY,
region: 'us-east-1',
// Timeouts
timeout: 30000, // Timeout geral (30s)
uploadTimeout: 60000, // Timeout de upload (60s)
downloadTimeout: 30000, // Timeout de download (30s)
// Retry e Rate Limiting
maxRetries: 3, // Máximo de tentativas
retryDelay: 1000, // Delay inicial entre tentativas
retryMultiplier: 2, // Multiplicador do delay (backoff exponencial)
maxRetryDelay: 10000, // Delay máximo entre tentativas
// Cache
cache: {
enabled: true, // Habilitar cache
ttl: 3600, // TTL em segundos (1 hora)
maxSize: 100, // Máximo de itens no cache
storage: 'memory' // 'memory', 'redis', 'file'
},
// Logging
debug: process.env.NODE_ENV === 'development',
logLevel: 'info', // 'error', 'warn', 'info', 'debug'
// Headers customizados
headers: {
'User-Agent': 'MeuApp/1.0.0',
'X-App-Version': '1.0.0'
}
});
Configuração Avançada
Cache
Cache em Memória
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
cache: {
enabled: true,
storage: 'memory',
ttl: 3600, // 1 hora
maxSize: 100, // 100 itens
checkPeriod: 600 // Verificar expiração a cada 10 min
}
});
Cache com Redis
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
password: 'sua-senha-redis'
});
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
cache: {
enabled: true,
storage: 'redis',
client: redis,
ttl: 7200, // 2 horas
keyPrefix: 'qwen:cache:'
}
});
Cache em Arquivo
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
cache: {
enabled: true,
storage: 'file',
directory: './cache', // Diretório do cache
ttl: 86400, // 24 horas
maxSize: 1000 // 1000 arquivos
}
});
Proxy
Proxy HTTP
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
proxy: {
protocol: 'http',
host: 'proxy.empresa.com',
port: 8080,
auth: {
username: 'usuario',
password: 'senha'
}
}
});
Proxy SOCKS
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
proxy: {
protocol: 'socks5',
host: 'socks-proxy.com',
port: 1080
}
});
Configurações por Ambiente
Desenvolvimento
// config/development.js
export default {
qwen: {
apiKey: process.env.QWEN_API_KEY,
region: 'us-east-1',
debug: true,
timeout: 30000,
maxRetries: 2,
cache: {
enabled: true,
storage: 'memory',
ttl: 1800, // 30 minutos
maxSize: 50
},
mock: {
enabled: false, // Usar API real em desenvolvimento
delay: 1000 // Simular delay da API
}
}
};
Produção
// config/production.js
export default {
qwen: {
apiKey: process.env.QWEN_API_KEY,
region: process.env.QWEN_REGION || 'us-east-1',
debug: false,
timeout: 60000,
maxRetries: 5,
retryDelay: 2000,
cache: {
enabled: true,
storage: 'redis',
ttl: 7200, // 2 horas
maxSize: 1000
},
monitoring: {
enabled: true,
metricsEndpoint: '/metrics',
healthEndpoint: '/health'
},
rateLimit: {
enabled: true,
maxRequests: 100,
windowMs: 60000 // 1 minuto
}
}
};
Teste
// config/test.js
export default {
qwen: {
apiKey: 'test-api-key',
region: 'us-east-1',
debug: true,
timeout: 10000,
maxRetries: 1,
cache: {
enabled: false // Desabilitar cache em testes
},
mock: {
enabled: true, // Usar mock em testes
responses: {
editText: {
imagemUrl: 'https://mock.qwen.com/result.jpg',
creditos: 1
}
}
}
}
};
Logging
Configuração de Logs
import winston from 'winston';
// Logger personalizado
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
// Configurar editor com logger personalizado
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
logger: logger,
logLevel: 'debug'
});
Logs Estruturados
// Configuração para logs estruturados
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
logging: {
enabled: true,
level: 'info',
format: 'json',
includeRequestId: true,
includeTimestamp: true,
fields: {
service: 'qwen-image-edit',
version: '1.0.0',
environment: process.env.NODE_ENV
}
}
});
Segurança
Validação de Entrada
import Joi from 'joi';
// Schema de validação
const editTextSchema = Joi.object({
imagem: Joi.alternatives().try(
Joi.string().uri(),
Joi.string().dataUri(),
Joi.object().instance(Buffer),
Joi.object().instance(File)
).required(),
prompt: Joi.string().min(1).max(1000).required(),
cor: Joi.string().pattern(/^#[0-9A-F]{6}$/i),
tamanho: Joi.alternatives().try(
Joi.string().valid('pequeno', 'medio', 'grande'),
Joi.number().min(8).max(72)
)
});
// Wrapper com validação
class SecureQwenImageEdit {
constructor(config) {
this.editor = new QwenImageEdit(config);
}
async editText(opcoes) {
// Validar entrada
const { error, value } = editTextSchema.validate(opcoes);
if (error) {
throw new Error(`Entrada inválida: ${error.message}`);
}
// Sanitizar prompt
value.prompt = this.sanitizePrompt(value.prompt);
return this.editor.editText(value);
}
sanitizePrompt(prompt) {
// Remover caracteres perigosos
return prompt
.replace(/<script[^>]*>.*?<\/script>/gi, '')
.replace(/<[^>]*>/g, '')
.trim();
}
}
const secureEditor = new SecureQwenImageEdit({
apiKey: process.env.QWEN_API_KEY
});
Rotação de Chaves API
class QwenWithKeyRotation {
constructor(keys, rotationInterval = 3600000) { // 1 hora
this.keys = keys;
this.currentKeyIndex = 0;
this.rotationInterval = rotationInterval;
// Rotacionar chaves automaticamente
setInterval(() => {
this.rotateKey();
}, rotationInterval);
this.editor = new QwenImageEdit({
apiKey: this.getCurrentKey()
});
}
getCurrentKey() {
return this.keys[this.currentKeyIndex];
}
rotateKey() {
this.currentKeyIndex = (this.currentKeyIndex + 1) % this.keys.length;
this.editor.updateApiKey(this.getCurrentKey());
console.log(`Chave API rotacionada para índice ${this.currentKeyIndex}`);
}
async editText(opcoes) {
try {
return await this.editor.editText(opcoes);
} catch (error) {
if (error.codigo === 'AUTH_INVALID') {
// Tentar próxima chave
this.rotateKey();
return this.editor.editText(opcoes);
}
throw error;
}
}
}
const editorComRotacao = new QwenWithKeyRotation([
process.env.QWEN_API_KEY_1,
process.env.QWEN_API_KEY_2,
process.env.QWEN_API_KEY_3
]);
Performance
Pool de Conexões
import { Agent } from 'https';
// Configurar pool de conexões
const httpsAgent = new Agent({
keepAlive: true,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000
});
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
httpsAgent: httpsAgent
});
Compressão de Imagem
import sharp from 'sharp';
class OptimizedQwenImageEdit {
constructor(config) {
this.editor = new QwenImageEdit(config);
this.maxImageSize = 5 * 1024 * 1024; // 5MB
}
async optimizeImage(imagePath) {
const stats = await sharp(imagePath).stats();
if (stats.size > this.maxImageSize) {
// Redimensionar se muito grande
const optimized = await sharp(imagePath)
.resize(1920, 1080, {
fit: 'inside',
withoutEnlargement: true
})
.jpeg({ quality: 85 })
.toBuffer();
return optimized;
}
return imagePath;
}
async editText(opcoes) {
// Otimizar imagem antes de enviar
if (typeof opcoes.imagem === 'string') {
opcoes.imagem = await this.optimizeImage(opcoes.imagem);
}
return this.editor.editText(opcoes);
}
}
const optimizedEditor = new OptimizedQwenImageEdit({
apiKey: process.env.QWEN_API_KEY
});
Gestão de Memória
class MemoryEfficientEditor {
constructor(config) {
this.editor = new QwenImageEdit(config);
this.activeRequests = new Map();
this.maxConcurrentRequests = 10;
}
async editText(opcoes) {
// Limitar requisições concorrentes
if (this.activeRequests.size >= this.maxConcurrentRequests) {
await this.waitForSlot();
}
const requestId = this.generateRequestId();
this.activeRequests.set(requestId, Date.now());
try {
const resultado = await this.editor.editText(opcoes);
// Limpar referências para liberar memória
if (Buffer.isBuffer(opcoes.imagem)) {
opcoes.imagem = null;
}
return resultado;
} finally {
this.activeRequests.delete(requestId);
}
}
async waitForSlot() {
return new Promise((resolve) => {
const checkSlot = () => {
if (this.activeRequests.size < this.maxConcurrentRequests) {
resolve();
} else {
setTimeout(checkSlot, 100);
}
};
checkSlot();
});
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
const memoryEfficientEditor = new MemoryEfficientEditor({
apiKey: process.env.QWEN_API_KEY
});
Monitoramento
Métricas
import prometheus from 'prom-client';
// Métricas Prometheus
const requestCounter = new prometheus.Counter({
name: 'qwen_requests_total',
help: 'Total de requisições para Qwen API',
labelNames: ['method', 'status']
});
const requestDuration = new prometheus.Histogram({
name: 'qwen_request_duration_seconds',
help: 'Duração das requisições para Qwen API',
labelNames: ['method'],
buckets: [0.1, 0.5, 1, 2, 5, 10]
});
const activeRequests = new prometheus.Gauge({
name: 'qwen_active_requests',
help: 'Número de requisições ativas'
});
class MonitoredQwenImageEdit {
constructor(config) {
this.editor = new QwenImageEdit(config);
}
async editText(opcoes) {
const startTime = Date.now();
activeRequests.inc();
try {
const resultado = await this.editor.editText(opcoes);
requestCounter.inc({ method: 'editText', status: 'success' });
return resultado;
} catch (error) {
requestCounter.inc({ method: 'editText', status: 'error' });
throw error;
} finally {
const duration = (Date.now() - startTime) / 1000;
requestDuration.observe({ method: 'editText' }, duration);
activeRequests.dec();
}
}
}
const monitoredEditor = new MonitoredQwenImageEdit({
apiKey: process.env.QWEN_API_KEY
});
Health Check
class HealthCheckEditor {
constructor(config) {
this.editor = new QwenImageEdit(config);
this.lastHealthCheck = null;
this.isHealthy = true;
}
async healthCheck() {
try {
// Fazer uma requisição simples para verificar saúde
const testImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==';
const startTime = Date.now();
await this.editor.analyzeImage({
imagem: testImage,
detectar: ['cores']
});
const responseTime = Date.now() - startTime;
this.lastHealthCheck = {
timestamp: new Date().toISOString(),
status: 'healthy',
responseTime: responseTime
};
this.isHealthy = true;
return this.lastHealthCheck;
} catch (error) {
this.lastHealthCheck = {
timestamp: new Date().toISOString(),
status: 'unhealthy',
error: error.message
};
this.isHealthy = false;
return this.lastHealthCheck;
}
}
getHealth() {
return {
status: this.isHealthy ? 'healthy' : 'unhealthy',
lastCheck: this.lastHealthCheck
};
}
}
const healthCheckEditor = new HealthCheckEditor({
apiKey: process.env.QWEN_API_KEY
});
// Verificar saúde a cada 5 minutos
setInterval(() => {
healthCheckEditor.healthCheck();
}, 5 * 60 * 1000);
Configuração para Desenvolvimento
Hot Reload
// webpack.config.js
module.exports = {
// ... outras configurações
devServer: {
hot: true,
proxy: {
'/api/qwen': {
target: 'https://api.qwen.com',
changeOrigin: true,
pathRewrite: {
'^/api/qwen': ''
}
}
}
}
};
Mock para Desenvolvimento
// mock/qwen-mock.js
class QwenMock {
constructor() {
this.delay = 1000; // Simular delay da API
}
async editText(opcoes) {
await this.simulateDelay();
return {
imagemUrl: `https://mock.qwen.com/result_${Date.now()}.jpg`,
imagemOriginal: opcoes.imagem,
alteracoes: [
{
tipo: 'texto_editado',
textoOriginal: 'MOCK_ORIGINAL',
textoNovo: 'MOCK_EDITADO',
posicao: { x: 100, y: 100 }
}
],
tempoProcessamento: this.delay / 1000,
creditos: 1
};
}
async simulateDelay() {
return new Promise(resolve => setTimeout(resolve, this.delay));
}
}
// Usar mock em desenvolvimento
const editor = process.env.NODE_ENV === 'development' && process.env.USE_MOCK
? new QwenMock()
: new QwenImageEdit({ apiKey: process.env.QWEN_API_KEY });
Melhores Práticas
1. Configuração por Ambiente
// config/index.js
const configs = {
development: require('./development'),
production: require('./production'),
test: require('./test')
};
const env = process.env.NODE_ENV || 'development';
export default configs[env];
2. Validação de Configuração
import Joi from 'joi';
const configSchema = Joi.object({
qwen: Joi.object({
apiKey: Joi.string().required(),
region: Joi.string().valid('us-east-1', 'us-west-1', 'eu-west-1', 'ap-southeast-1'),
timeout: Joi.number().min(1000).max(300000),
maxRetries: Joi.number().min(0).max(10),
cache: Joi.object({
enabled: Joi.boolean(),
ttl: Joi.number().min(60),
maxSize: Joi.number().min(1)
})
}).required()
});
export function validateConfig(config) {
const { error, value } = configSchema.validate(config);
if (error) {
throw new Error(`Configuração inválida: ${error.message}`);
}
return value;
}
3. Configuração Centralizada
// services/qwen-service.js
import config from '../config';
import { QwenImageEdit } from 'qwen-image-edit';
class QwenService {
constructor() {
this.editor = new QwenImageEdit(config.qwen);
}
static getInstance() {
if (!QwenService.instance) {
QwenService.instance = new QwenService();
}
return QwenService.instance;
}
getEditor() {
return this.editor;
}
}
export default QwenService.getInstance();
Solução de Problemas de Configuração
Verificar Configuração
// scripts/check-config.js
import { QwenImageEdit } from 'qwen-image-edit';
async function checkConfiguration() {
try {
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
region: process.env.QWEN_REGION
});
// Testar conexão
const quota = await editor.getQuota();
console.log('✅ Configuração válida');
console.log('📊 Cota atual:', quota);
} catch (error) {
console.error('❌ Erro na configuração:', error.message);
process.exit(1);
}
}
checkConfiguration();
Debug de Configuração
// Habilitar debug detalhado
const editor = new QwenImageEdit({
apiKey: process.env.QWEN_API_KEY,
debug: true,
logLevel: 'debug',
onRequest: (config) => {
console.log('📤 Requisição:', {
url: config.url,
method: config.method,
headers: config.headers
});
},
onResponse: (response) => {
console.log('📥 Resposta:', {
status: response.status,
headers: response.headers
});
},
onError: (error) => {
console.error('❌ Erro:', error);
}
});
Próximos Passos:
- Solução de Problemas - Resolva problemas comuns
- Funcionalidades Avançadas - Recursos avançados
- Exemplos - Casos de uso práticos