scn-consultation-api / src /nodes /system_design /lld_code_generator.py
SS-2005's picture
Initial HF Space deployment
59ec65a
Raw
History Blame Contribute Delete
14.4 kB
import os
import logging
from typing import Dict, List, Optional
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate
# Setup Logger
logger = logging.getLogger(__name__)
# --- Pydantic Models for Structured Output ---
class CodeFile(BaseModel):
filename: str = Field(description="Name of the file, e.g., 'NotificationFactory.java'")
content: str = Field(description="Complete source code for the file")
class LLDCodeResponse(BaseModel):
title: str = Field(description="Title of the design pattern being demonstrated")
subtitle: str = Field(description="Subtitle explaining the specific implementation")
files: List[CodeFile] = Field(description="List of code files. Max 3-4 files.")
execution_output: str = Field(description="Console output for the Main execution file.")
typing_speed: int = Field(default=25, description="Typing animation speed in ms per char")
auto_run: bool = Field(default=True, description="Whether to auto-run the animation")
# Pattern-specific guidance
PATTERN_GUIDANCE = {
"singleton": {
"description": "Ensures a class has only one instance and provides global access",
"files_needed": ["Singleton class", "Main/Demo class"],
"key_concepts": ["Private constructor", "Static instance", "Thread safety"],
"example_domain": "Database connection, Logger, Configuration manager"
},
"factory": {
"description": "Creates objects without specifying exact class to create",
"files_needed": ["Product interface", "Concrete products", "Factory class", "Main/Demo"],
"key_concepts": ["Product interface", "Concrete implementations", "Factory method"],
"example_domain": "Notification system (Email, SMS, Push), Vehicle factory, Payment processors"
},
"observer": {
"description": "Defines one-to-many dependency between objects",
"files_needed": ["Subject interface", "Observer interface", "Concrete subject", "Concrete observers", "Main/Demo"],
"key_concepts": ["Subject", "Observer", "Notify mechanism", "Subscription"],
"example_domain": "Event system, Stock price updates, News feed, Weather station"
},
"strategy": {
"description": "Defines family of algorithms, encapsulates each, makes them interchangeable",
"files_needed": ["Strategy interface", "Concrete strategies", "Context class", "Main/Demo"],
"key_concepts": ["Strategy interface", "Concrete strategies", "Context switching"],
"example_domain": "Payment methods, Sorting algorithms, Compression methods, Route calculation"
},
"builder": {
"description": "Separates construction of complex object from representation",
"files_needed": ["Product class", "Builder interface/class", "Concrete builder", "Director (optional)", "Main/Demo"],
"key_concepts": ["Step-by-step construction", "Fluent interface", "Immutable product"],
"example_domain": "Building complex objects (House, Pizza, Document), Query builder"
},
"decorator": {
"description": "Attaches additional responsibilities to object dynamically",
"files_needed": ["Component interface", "Concrete component", "Decorator base", "Concrete decorators", "Main/Demo"],
"key_concepts": ["Component interface", "Wrapping", "Composition over inheritance"],
"example_domain": "Coffee with addons, Text formatting, Stream wrappers"
},
"adapter": {
"description": "Converts interface of class into another interface clients expect",
"files_needed": ["Target interface", "Adaptee class", "Adapter class", "Main/Demo"],
"key_concepts": ["Target interface", "Adaptee", "Adapter wrapping"],
"example_domain": "Legacy system integration, Third-party API wrapper"
},
"command": {
"description": "Encapsulates request as object, allowing parameterization",
"files_needed": ["Command interface", "Concrete commands", "Receiver", "Invoker", "Main/Demo"],
"key_concepts": ["Command interface", "Execute method", "Receiver", "Undo capability"],
"example_domain": "Text editor operations, Remote control, Transaction system"
},
"design_pattern": {
"description": "Generic design pattern implementation",
"files_needed": ["Interface", "Implementation", "Main/Demo"],
"key_concepts": ["Clean code", "SOLID principles", "Best practices"],
"example_domain": "Based on topic context"
}
}
class LLDCodeGenerator:
"""
Generator for Low-Level Design code examples using LangChain for structured multi-file output.
"""
def __init__(self, client=None):
api_key = os.getenv("OPENAI_API_KEY")
if not api_key and client and hasattr(client, "api_key"):
api_key = client.api_key
self.llm = ChatOpenAI(
model="gpt-4o",
temperature=0.2,
api_key=api_key
)
self.logger = logging.getLogger(__name__)
def generate_lld_code_content(
self,
slide_config: Dict,
topic: str,
architecture_description: str
) -> Dict:
"""
Generate multi-file LLD code examples with pattern-specific guidance.
"""
focus = slide_config.get('focus', 'Low-Level Design')
language = slide_config.get('language', 'java')
lld_pattern = slide_config.get('lld_pattern', 'design_pattern')
self.logger.info(f"Generating LLD code for: {focus} in {language} using pattern: {lld_pattern}")
# Get pattern-specific guidance
pattern_info = PATTERN_GUIDANCE.get(lld_pattern.lower(), PATTERN_GUIDANCE['design_pattern'])
# Define input model for type safety
class PromptInput(BaseModel):
language: str
topic: str
focus: str
lld_pattern: str
pattern_description: str
files_needed: str
key_concepts: str
example_domain: str
context: str
# Prepare Prompt
system_prompt = """You are an expert software architect creating clean, educational code examples for Low-Level Design patterns.
Generate complete, working multi-file code that demonstrates design patterns and SOLID principles.
CRITICAL REQUIREMENTS:
1. **Pattern Implementation**:
- Implement the EXACT pattern specified: {lld_pattern}
- Pattern description: {pattern_description}
- Required files: {files_needed}
- Key concepts to demonstrate: {key_concepts}
- Use domain context: {example_domain}
2. **Multiple Files** (3-4 files):
- Interface/Abstract class (if applicable)
- Concrete implementation classes (2-3 variants)
- Main/Demo class showing usage
- Each file should be focused and follow Single Responsibility Principle
3. **REAL-WORLD NAMING**:
- **NEVER** use generic names like `ConcreteProductA`, `Component1`, `Observer1`, `StrategyA`
- **ALWAYS** use domain-specific names based on the TOPIC ({topic}) and FOCUS ({focus})
- Example for Notification System + Factory pattern:
✓ GOOD: `NotificationFactory`, `EmailNotification`, `SMSNotification`, `PushNotification`
✗ BAD: `Factory`, `ProductA`, `ProductB`, `ConcreteProduct1`
- Example for Payment System + Strategy pattern:
✓ GOOD: `PaymentStrategy`, `CreditCardPayment`, `PayPalPayment`, `CryptoPayment`
✗ BAD: `Strategy`, `ConcreteStrategyA`, `ConcreteStrategyB`
4. **Clean Code**:
- Use proper naming conventions for {language}
- Add helpful comments explaining pattern-specific parts
- COMPACT formatting: Use MINIMAL blank lines (max 1 blank line between methods/classes)
- NO excessive spacing or empty lines at start of files
- Keep code visually compact and professional
5. **Pattern-Specific Implementation**:
- For **Factory**: Show polymorphic object creation without exposing instantiation logic
- For **Singleton**: Include thread-safe implementation, private constructor, static instance
- For **Observer**: Demonstrate subject-observer relationship, notify mechanism
- For **Strategy**: Show algorithm family, context switching between strategies
- For **Builder**: Implement step-by-step construction, fluent interface
- For **Decorator**: Show component wrapping, responsibility addition
- For **Adapter**: Demonstrate interface conversion between incompatible interfaces
- For **Command**: Encapsulate requests as objects, show execute/undo
6. **Execution Output**:
- Provide realistic console output showing the pattern in action
- Output should demonstrate key pattern benefits
- Show object creation, method calls, state changes
7. **Language**: {language}
OUTPUT STRUCTURE:
Return a JSON object with:
- title: Pattern name + brief description
- subtitle: Specific implementation focus
- files: list of objects {{filename, content}}
- execution_output: string containing the console output
- typing_speed: 25-35 (ms per character)
- auto_run: true
"""
user_prompt = """
TOPIC: {topic}
FOCUS: {focus}
PATTERN: {lld_pattern}
PATTERN DESCRIPTION: {pattern_description}
KEY CONCEPTS: {key_concepts}
EXAMPLE DOMAIN: {example_domain}
CONTEXT: {context}
Generate the complete pattern implementation now with proper domain-specific names.
"""
try:
structured_llm = self.llm.with_structured_output(LLDCodeResponse)
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("user", user_prompt)
])
chain = prompt | structured_llm
# Create input with Pydantic model
prompt_input = PromptInput(
language=language,
topic=topic,
focus=focus,
lld_pattern=lld_pattern,
pattern_description=pattern_info['description'],
files_needed=", ".join(pattern_info['files_needed']),
key_concepts=", ".join(pattern_info['key_concepts']),
example_domain=pattern_info['example_domain'],
context=architecture_description[:800]
)
result: LLDCodeResponse = chain.invoke(prompt_input.model_dump())
# Convert List[CodeFile] back to Dict[str, str] for frontend compatibility
code_files_dict = {f.filename: f.content for f in result.files}
# Construct execution output dict (Main file gets the output, others empty)
exec_output_dict = {name: "" for name in code_files_dict}
# Heuristic: Find Main file or use the last one
main_file = next(
(f for f in code_files_dict if "Main" in f or "Demo" in f or "App" in f or "Test" in f),
list(code_files_dict.keys())[-1]
)
if main_file:
exec_output_dict[main_file] = result.execution_output
self.logger.info(f"Generated {len(code_files_dict)} files for {lld_pattern} pattern")
return {
"title": result.title,
"subtitle": result.subtitle,
"code_files": code_files_dict,
"execution_output": exec_output_dict,
"typing_speed": result.typing_speed,
"auto_run": result.auto_run,
"pattern": lld_pattern
}
except Exception as e:
self.logger.error(f"Error generating LLD code with LangChain: {e}")
return self._get_fallback_lld_code(focus, language, lld_pattern)
def _get_fallback_lld_code(self, focus: str, language: str, pattern: str) -> Dict:
"""Fallback LLD code if generation fails - pattern-specific"""
self.logger.warning(f"Using fallback LLD code for {pattern}")
if pattern.lower() == "singleton":
return {
"title": "Singleton Pattern",
"subtitle": "Thread-Safe Database Connection",
"code_files": {
"DatabaseConnection.java": """public class DatabaseConnection {
private static volatile DatabaseConnection instance;
private String connectionString;
private DatabaseConnection() {
// Private constructor
this.connectionString = "jdbc:mysql://localhost:3306/mydb";
System.out.println("Database connection initialized");
}
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public void query(String sql) {
System.out.println("Executing: " + sql);
}
}""",
"Main.java": """public class Main {
public static void main(String[] args) {
// Only one instance created
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println("Same instance? " + (db1 == db2));
db1.query("SELECT * FROM users");
}
}"""
},
"execution_output": {
"Main.java": """Database connection initialized
Same instance? true
Executing: SELECT * FROM users"""
},
"typing_speed": 25,
"auto_run": True,
"pattern": "singleton"
}
# Generic fallback
return {
"title": focus,
"subtitle": "Fallback Example",
"code_files": {
"Main.java": f"""public class Main {{
public static void main(String[] args) {{
System.out.println("Error generating {pattern} pattern code.");
System.out.println("Focus: {focus}");
}}
}}"""
},
"execution_output": {
"Main.java": f"Error generating {pattern} pattern code.\nFocus: {focus}"
},
"typing_speed": 25,
"auto_run": True,
"pattern": pattern
}