Word2PPT

Word2PPT adalah aplikasi konversi dokumen yang memungkinkan pengguna mengubah dokumen Word (DOC & DOCX) menjadi presentasi PowerPoint (PPTX) dengan bantuan AI. Aplikasi ini dibangun dengan fokus pada:

  • Modularitas: Setiap komponen memiliki tanggung jawab spesifik dan dapat diuji secara independen
  • Reliability: Implementasi failover strategy untuk menjamin ketersediaan layanan
  • Type Safety: Penggunaan TypeScript untuk meminimalisir runtime errors

Arsitektur Sistem

1. Server Layer

Server dibangun dengan Express.js + TypeScript untuk menjamin type safety dan developer experience yang lebih baik:

  • API Routes: Penanganan endpoint untuk upload dan konversi dokumen
  • File Upload: Validasi dan manajemen file upload dengan rate limiting
  • Error Handler: Penanganan error terstruktur dengan logging komprehensif
  • Response Formatter: Standarisasi format response API

2. Service Layer

Menggunakan Provider pattern untuk ekstensibilitas optimal:

Document Processing

  • Validator: Memastikan dokumen sesuai format yang didukung
  • Converter: Transformasi DOC/DOCX ke format Markdown,
  • Processor: Ekstraksi dan strukturisasi konten untuk AI processing

AI Summarization

Implementasi failover strategy dengan multiple provider:

  • Gemini (prioritas pertama, tersedia versi gratis dengan limit paling tinggi)
  • Groq (backup, versi gratis memiliki batasan lebih rendah daripada Gemini)
  • Deepseek (backup, versi berbayar)

Penerapan Arsitektur

Proyek ini menggunakan teknologi sebagai berikut

  • Backend: Node.js + Express + TypeScript
  • Document Processing: Mammoth.js
  • AI Integration: Gemini API, Groq API
  • Testing: Jest
  • Deployment: Docker, Nginx

Dan dalam penerapannya, menggunakan prinsip Modularity, Reliability & Maintainability yang contohnya dapat dilihat di bawah

Modularity: Provider Pattern untuk AI Services

interface SummaryProvider {
    readonly priority: number
    isAvailable(): boolean
    summarize(content: string): Promise<string>
}

class GeminiProvider implements SummaryProvider {
    readonly priority = 1
    
    isAvailable(): boolean {
        return !!process.env.GEMINI_API_KEY
    }
    
    async summarize(content: string): Promise<string> {
        const model = genAI.getGenerativeModel({ model: 'gemini-pro' })
        const result = await model.generateContent(content)
        return result.response.text()
    }
}

Reliability: Failover Strategy

class SummarizerService {
    private static providers = [
        new GeminiProvider(),   // Primary
        new GroqProvider(),     // Secondary
        new DeepseekProvider()  // Tertiary
    ].sort((a, b) => a.priority - b.priority)

    static async summarize(content: string): Promise<string> {
        for (const provider of this.providers) {
            if (!provider.isAvailable()) continue
            
            try {
                return await provider.summarize(content)
            } catch (error) {
                Logger.error(`${provider.constructor.name} failed: ${error.message}`)
            }
        }
        throw new Error('No available providers')
    }
}

Maintainability: Unit testing yang komprehensif

describe('GeminiProvider', () => {
    let provider: GeminiProvider;

    beforeEach(() => {
        process.env.GEMINI_API_KEY = 'test_api_key';
        provider = new GeminiProvider();
    });

    it('should be available when API key is set', () => {
        expect(provider.isAvailable()).toBe(true);
    });

    it('should handle API errors', async () => {
        global.fetch = jest.fn().mockResolvedValue({
            ok: false,
            status: 400,
            statusText: 'Bad Request'
        });

        await expect(provider.summarize('Test'))
            .rejects.toThrow('Gemini API request failed');
    });
});

Deployment

Aplikasi di-deploy menggunakan Docker dengan multi-stage build untuk optimasi ukuran image:

# Build Stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production Stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production

ENV NODE_ENV=production
HEALTHCHECK CMD curl -f http://localhost:3000/health
EXPOSE 3000
CMD ["node", "dist/index.js"]

Struktur ini menggunakan dua stage untuk mengoptimalkan ukuran image:

  1. Build Stage: Melakukan kompilasi TypeScript dan menginstall semua dependensi
  2. Production Stage: Hanya menyalin hasil build dan dependensi untuk env production

Keuntungan pendekatan ini:

  • Image size yang lebih kecil (tidak ada dev dependencies dan source code)
  • Keamanan yang lebih baik (source code tidak ada di final image)
  • Startup time yang lebih cepat

Tantangan

1. Parsing Dokumen Word

Parsing dokumen Word menghadirkan beberapa tantangan teknis:

    • Dokumen Word (terutama .doc) memiliki struktur biner kompleks
    • Mempertahankan formatting seperti heading, list, dan tabel
    • Menangani embedded content (gambar, grafik)
      • Memproses paragraf dan menjaga struktur dokumen
      • Mengkonversi tabel ke format teks dengan separator |
      • Menangani nested elements (teks dalam tabel)
      • Menjaga spacing dan formatting dasar
    • Validasi dan Error Handling
      • Validasi format file yang didukung
      • Batasan ukuran file (5MB)
      • Error handling yang konsisten
      • Custom error class untuk konversi

Cara Konversi Dokumen Word 
Sistem mendukung konversi dari dua format dokumen Word: .doc (legacy) dan .docx (modern XML-based). Setiap format memiliki pendekatan konversi yang berbeda:

export class ConverterService {
  // Validasi file input
  static validateFile(file: Express.Multer.File | undefined): void {
    if (!file) throw new ConversionError('No file uploaded')
    
    const ext = extname(file.originalname).toLowerCase()
    if (!['.doc', '.docx'].includes(ext)) {
      throw new ConversionError('Only DOC and DOCX files are allowed')
    }
    
    if (file.size > MAX_FILE_SIZE) {
      throw new ConversionError('File too large')
    }
  }

  // Konversi format .doc menggunakan word.js
  static async convertDoc(buffer: Buffer): Promise<string> {
    try {
      const doc = read(buffer)
      if (!doc || !doc.p) {
        throw new ConversionError('Could not convert DOC file')
      }
      return this.extractText(doc)
    } catch (error) {
      throw new ConversionError('Could not convert DOC file')
    }
  }

  // Konversi format .docx menggunakan mammoth + turndown
  static async convertDocx(buffer: Buffer): Promise<string> {
    try {
      const { value: html } = await mammoth.convertToHtml({ buffer })
      return turndown.turndown(html)
    } catch (error) {
      throw new ConversionError('Could not convert DOCX file')
    }
  }
}

Format Modern (.docx)

// Contoh penggunaan mammoth untuk konversi docx
async function convertDocxToHtml(buffer: Buffer): Promise<string> {
    const result = await mammoth.convertToHtml(
        { buffer },
        {
            styleMap: [
                "p[style-name='Heading 1'] => h1",
                "p[style-name='Heading 2'] => h2",
                "p[style-name='List Paragraph'] => li"
            ]
        }
    );
    return result.value;
}

Format Legacy (.doc) 
Library word membaca dokumen .doc sebagai struktur kompleks yang terdiri dari paragraf, teks, dan tabel. Proses ekstraksi teks memerlukan penanganan khusus:

interface DocElement {
  type: 'text' | 'table'
  content: string | string[][]
}

static extractText(doc: DocElement[]): string {
  return doc.map(element => {
    if (element.type === 'text') {
      return element.content as string
    }
    
    if (element.type === 'table') {
      return (element.content as string[][])
        .map(row => row.join(' | '))
        .join('\n')
    }
    
    return ''
  }).join('\n\n')
}

2. Chunking untuk API Limits

Chunking/membagi teks ke dalam bagian yang lebih kecil dilakukan untuk menghindari limit dari API LLM gratis yang dipakai. Proses chunking yang dilakukan harus:

    • Memastikan chunk tidak memotong kalimat di tengah
    • Menangani referensi antar bagian dokumen
    • Jumlah chunk tidak terlalu banyak agar tidak terkena rate limit
    • Menggabungkan hasil AI summary dari multiple chunks

Pembuatan Chunk

export class SummarizerService {
    private static readonly MAX_CHUNK_SIZE = 20000;
    
    static splitByWhitespace(chunk: string): string[] {
        // Hapus gambar base64 untuk menghemat token
        chunk = this.removeBase64Images(chunk);
        
        const subChunks: string[] = [];
        let currentSubChunk = "";

        // Split berdasarkan baris untuk menjaga konteks
        const lines = chunk.split("\n");

        for (const line of lines) {
            if ((currentSubChunk + line).length > this.MAX_CHUNK_SIZE) {
                subChunks.push(currentSubChunk.trim());
                currentSubChunk = line;
            } else {
                currentSubChunk += "\n" + line;
            }
        }

        if (currentSubChunk.trim()) {
            subChunks.push(currentSubChunk.trim());
        }

        return subChunks;
    }
}
export class ChunkOptimizer {
    static async processBatch(chunks: string[]): Promise<string> {
        // Proses parallel dengan rate limiting
        const summaries = await Promise.all(
            chunks.map(async (chunk, index) => {
                // Delay antar request untuk menghindari rate limit
                await new Promise(r => setTimeout(r, index * 1000));
                return await SummarizerService.summarize(chunk);
            })
        );

        // Gabungkan hasil dengan tetap menjaga struktur
        return this.mergeSummaries(summaries);
    }
}

Solusi untuk tantangan-tantangan ini memerlukan kombinasi dari:

  • Preprocessing dokumen yang robust
  • Strategi chunking yang cerdas
  • Rate limiting dan error handling yang baik
  • Sistem caching untuk mengoptimalkan penggunaan API

Penerapan AI Assisted Coding

Pengembangan Word2PPT memanfaatkan AI dalam beberapa aspek:

  1. Code Generation dan Refactoring
  2. Type Safety dan Error Handling.
    AI membantu mengidentifikasi potential type errors dan mengusulkan penanganan yang lebih baik:
  3. Testing Strategy
    AI membantu mengidentifikasi test cases penting dan edge cases:
  4. Dokumentasi dan Code Review
    AI membantu dalam:
    1. Mengidentifikasi bagian kode yang memerlukan dokumentasi
    2. Mendeteksi potential bugs dan anti-patterns
    3. Memberikan saran untuk optimasi performa
    4. Memastikan konsistensi gaya penulisan kode
  5. Dependency Management
    AI membantu memilih dan mengkonfigurasi dependencies:
    1. Memilih library yang well-maintained (mammothturndown)
    2. Mengidentifikasi potential security vulnerabilities
    3. Menyarankan alternatif package yang lebih ringan
    4. Membantu dalam upgrade dependencies
  6. Continuous Improvement
    AI berperan dalam proses iteratif:
    1. Menganalisis code smells dan technical debt
    2. Menyarankan refactoring opportunities
    3. Membantu implementasi best practices
    4. Memberikan insight dari pattern umum di komunitas

Kesimpulan

Arsitektur Word2PPT dirancang dengan fokus pada extensibility, reliability & maintainability. Penggunaan TypeScript dan implementasi unit testing memastikan codebase tetap terstruktur dan mudah dikembangkan. Provider Pattern yang diimplementasikan memungkinkan penambahan fitur baru tanpa mengubah core functionality.