feat: add GenAI implementation guide and demo ask flows - #27
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new “GenAI implementation” guide + demo ask flows to the API, and hardens startup/runtime behavior when optional integrations (Google OAuth, Razorpay, email) are not configured.
Changes:
- Introduces
/api/v1/gen-ai-implementationguide endpoint plus three runnable demo ask endpoints (single/multi/langgraph) with input validation and tests. - Makes Google OAuth strategy + routes conditional and returns a controlled 503 when OAuth isn’t configured.
- Guards Razorpay and email transporter initialization so missing credentials don’t crash the app.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/services/payment.service.js | Make Razorpay optional; add a 503 guard when credentials are missing. |
| src/services/gen-ai-implementation.service.js | Adds structured “implementation guide” content + demo response builders + question validation. |
| src/controllers/gen-ai-implementation.controller.js | Adds controller methods for guide + demo ask endpoints. |
| src/routes/gen-ai-implementation.routes.js | Wires the new guide + ask routes. |
| src/app.js | Registers the new GenAI implementation routes. |
| src/tests/gen-ai-implementation.test.js | Adds coverage for guide retrieval, demo ask responses, and validation failures. |
| src/routes/auth.routes.js | Makes Google OAuth routes conditional; returns 503 when disabled. |
| src/config/passport.js | Conditionally registers Google strategy and exports googleOAuthEnabled. |
| src/config/email.config.js | Moves OAuth2 client creation inside createTransporter and throws 503 if not configured. |
| src/config/config.js | Freezes config only in production (keeps mutable for dev/testing). |
| readme.md | Documents the new GenAI guide endpoint. |
| package-lock.json | Lockfile update (removes gcp-metadata subtree). |
Comments suppressed due to low confidence (1)
src/services/payment.service.js:475
- In
handleWebhook, thecatchblock always wraps errors into a newAppError(..., 500), which overrides earlierAppErrors thrown in thetry(e.g., invalid webhook signature should stay 400; missing Razorpay credentials should stay 503). Preserve existingAppErrorinstances (rethrow them) and only wrap unknown errors as 500 so the correct status codes reach clients/webhook senders.
async handleWebhook(signature, payload) {
try {
this.ensureRazorpayConfigured();
// Verify webhook signature
const expectedSignature = crypto
.createHmac('sha256', config.razorpay.keySecret)
.update(JSON.stringify(payload))
.digest('hex');
if (signature !== expectedSignature) {
throw new AppError('Invalid webhook signature', 400);
}
const { event, payload: eventPayload } = payload;
switch (event) {
case 'payment.captured':
await this.handlePaymentCaptured(eventPayload.payment.entity);
break;
case 'payment.failed':
await this.handlePaymentFailed(eventPayload.payment.entity);
break;
case 'refund.processed':
await this.handleRefundProcessed(eventPayload.refund.entity);
break;
default:
logger.info(`Unhandled webhook event: ${event}`);
}
return { success: true, message: 'Webhook processed successfully' };
} catch (error) {
logger.error('Webhook processing error:', error);
throw new AppError(`Webhook processing failed: ${error.message}`, 500);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * Get the GenAI implementation guide. | ||
| * @route GET /api/v1/gen-ai-implementation | ||
| * @access Public | ||
| */ | ||
| getGuide = asyncHandler(async (req, res) => { | ||
| const mode = req.query.mode || 'all'; | ||
| const guide = genAiImplementationService.buildGuide(mode); | ||
|
|
||
| res.status(200).json({ | ||
| success: true, | ||
| message: 'GenAI implementation guide retrieved successfully', | ||
| data: guide, | ||
| }); | ||
| }); | ||
|
|
||
| /** | ||
| * Run single-agent demo flow. | ||
| * @route POST /api/v1/gen-ai-implementation/single/ask | ||
| * @access Public | ||
| */ | ||
| askSingle = asyncHandler(async (req, res) => { | ||
| const { question } = req.body; | ||
| const result = genAiImplementationService.runSingleAgentDemo(question); | ||
|
|
||
| res.status(200).json({ | ||
| success: true, | ||
| message: 'Single-agent demo response generated successfully', | ||
| data: result, | ||
| }); | ||
| }); | ||
|
|
||
| /** | ||
| * Run multi-agent demo flow. | ||
| * @route POST /api/v1/gen-ai-implementation/multi/ask | ||
| * @access Public | ||
| */ | ||
| askMulti = asyncHandler(async (req, res) => { | ||
| const { question } = req.body; | ||
| const result = genAiImplementationService.runMultiAgentDemo(question); | ||
|
|
||
| res.status(200).json({ | ||
| success: true, | ||
| message: 'Multi-agent demo response generated successfully', | ||
| data: result, | ||
| }); | ||
| }); | ||
|
|
||
| /** | ||
| * Run LangGraph demo flow. | ||
| * @route POST /api/v1/gen-ai-implementation/langgraph/ask | ||
| * @access Public | ||
| */ | ||
| askLangGraph = asyncHandler(async (req, res) => { | ||
| const { question } = req.body; | ||
| const result = genAiImplementationService.runLangGraphDemo(question); | ||
|
|
||
| res.status(200).json({ | ||
| success: true, | ||
| message: 'LangGraph demo response generated successfully', | ||
| data: result, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
This new controller file is indented with 4 spaces, while existing controllers use 2-space indentation (e.g., src/controllers/auth.controller.js). Running the formatter (Prettier) / aligning indentation will keep code style consistent across the repo and reduce diff churn in future edits.
| /** | |
| * Get the GenAI implementation guide. | |
| * @route GET /api/v1/gen-ai-implementation | |
| * @access Public | |
| */ | |
| getGuide = asyncHandler(async (req, res) => { | |
| const mode = req.query.mode || 'all'; | |
| const guide = genAiImplementationService.buildGuide(mode); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'GenAI implementation guide retrieved successfully', | |
| data: guide, | |
| }); | |
| }); | |
| /** | |
| * Run single-agent demo flow. | |
| * @route POST /api/v1/gen-ai-implementation/single/ask | |
| * @access Public | |
| */ | |
| askSingle = asyncHandler(async (req, res) => { | |
| const { question } = req.body; | |
| const result = genAiImplementationService.runSingleAgentDemo(question); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'Single-agent demo response generated successfully', | |
| data: result, | |
| }); | |
| }); | |
| /** | |
| * Run multi-agent demo flow. | |
| * @route POST /api/v1/gen-ai-implementation/multi/ask | |
| * @access Public | |
| */ | |
| askMulti = asyncHandler(async (req, res) => { | |
| const { question } = req.body; | |
| const result = genAiImplementationService.runMultiAgentDemo(question); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'Multi-agent demo response generated successfully', | |
| data: result, | |
| }); | |
| }); | |
| /** | |
| * Run LangGraph demo flow. | |
| * @route POST /api/v1/gen-ai-implementation/langgraph/ask | |
| * @access Public | |
| */ | |
| askLangGraph = asyncHandler(async (req, res) => { | |
| const { question } = req.body; | |
| const result = genAiImplementationService.runLangGraphDemo(question); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'LangGraph demo response generated successfully', | |
| data: result, | |
| }); | |
| }); | |
| /** | |
| * Get the GenAI implementation guide. | |
| * @route GET /api/v1/gen-ai-implementation | |
| * @access Public | |
| */ | |
| getGuide = asyncHandler(async (req, res) => { | |
| const mode = req.query.mode || 'all'; | |
| const guide = genAiImplementationService.buildGuide(mode); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'GenAI implementation guide retrieved successfully', | |
| data: guide, | |
| }); | |
| }); | |
| /** | |
| * Run single-agent demo flow. | |
| * @route POST /api/v1/gen-ai-implementation/single/ask | |
| * @access Public | |
| */ | |
| askSingle = asyncHandler(async (req, res) => { | |
| const { question } = req.body; | |
| const result = genAiImplementationService.runSingleAgentDemo(question); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'Single-agent demo response generated successfully', | |
| data: result, | |
| }); | |
| }); | |
| /** | |
| * Run multi-agent demo flow. | |
| * @route POST /api/v1/gen-ai-implementation/multi/ask | |
| * @access Public | |
| */ | |
| askMulti = asyncHandler(async (req, res) => { | |
| const { question } = req.body; | |
| const result = genAiImplementationService.runMultiAgentDemo(question); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'Multi-agent demo response generated successfully', | |
| data: result, | |
| }); | |
| }); | |
| /** | |
| * Run LangGraph demo flow. | |
| * @route POST /api/v1/gen-ai-implementation/langgraph/ask | |
| * @access Public | |
| */ | |
| askLangGraph = asyncHandler(async (req, res) => { | |
| const { question } = req.body; | |
| const result = genAiImplementationService.runLangGraphDemo(question); | |
| res.status(200).json({ | |
| success: true, | |
| message: 'LangGraph demo response generated successfully', | |
| data: result, | |
| }); | |
| }); |
| name: 'Midnight Graph', | ||
| mode: 'dark', | ||
| palette: { | ||
| background: '#08111f', | ||
| surface: '#111a2e', | ||
| surfaceAlt: '#16233d', | ||
| primary: '#7c9cff', | ||
| secondary: '#6be7c8', | ||
| text: '#e8eefc', | ||
| muted: '#9bb0d0', | ||
| border: '#243354', | ||
| }, | ||
| layout: 'card-grid', | ||
| }; | ||
|
|
||
| const implementationProcess = { | ||
| prerequisites: [ | ||
| 'Node.js 18+ and npm installed', | ||
| 'Working Express server with controller/service pattern', | ||
| 'One LLM provider key (for example OpenAI)', | ||
| ], | ||
| installCommands: [ | ||
| 'npm i langchain @langchain/openai @langchain/langgraph dotenv', | ||
| ], | ||
| envSetup: [ | ||
| 'Add OPENAI_API_KEY in your .env file', | ||
| 'Load environment variables once in server bootstrap', | ||
| 'Never hardcode API keys inside services', | ||
| ], | ||
| folderIntegration: [ | ||
| 'Create src/services/gen-ai-single-agent.service.js for single-agent flow', | ||
| 'Create src/services/gen-ai-langgraph.service.js for graph orchestration flow', | ||
| 'Use existing src/controllers/gen-ai-implementation.controller.js to call service methods', | ||
| 'Expose POST endpoints from src/routes/gen-ai-implementation.routes.js for runnable demos', | ||
| 'Keep GET /api/v1/gen-ai-implementation as a documentation/guide endpoint', | ||
| ], | ||
| validationAndSafety: [ | ||
| 'Validate question input before calling model', | ||
| 'Use short system prompts with clear output format', | ||
| 'Add request timeout and retry policy at service layer', | ||
| 'Log only request metadata, do not log secrets', | ||
| ], | ||
| rolloutPlan: [ | ||
| 'Phase 1: Ship single-agent first', | ||
| 'Phase 2: Add specialist agents for research/writer split', | ||
| 'Phase 3: Move to LangGraph for branching, retries, and checkpoints', | ||
| ], | ||
| }; | ||
|
|
||
| const implementationModes = { | ||
| single: { | ||
| slug: 'single', | ||
| title: 'Single-Agent LangChain', | ||
| summary: | ||
| 'Use one orchestrator when the task has a clear path, a limited toolset, and predictable handoffs.', | ||
| whenToUse: [ | ||
| 'FAQ answering and support automation', | ||
| 'Simple retrieval augmented generation flows', | ||
| 'One model plus a few tools or retrievers', | ||
| ], | ||
| architecture: [ | ||
| 'User request enters one agent loop', | ||
| 'Agent selects tools or retrievers', | ||
| 'Model synthesizes the final answer', | ||
| 'Response is formatted for the client UI', | ||
| ], | ||
| implementationSteps: [ | ||
| 'Install LangChain core packages and choose a model provider.', | ||
| 'Define a system prompt with the expected output format.', | ||
| 'Attach tools for search, database lookup, or API calls.', | ||
| 'Wrap the agent in a service method and expose it through a controller.', | ||
| 'Return structured JSON so the frontend can render cards, steps, and code snippets.', | ||
| ], | ||
| processFlow: [ | ||
| 'Receive user question in controller', | ||
| 'Validate and sanitize input', | ||
| 'Build prompt template and invoke one LLM', | ||
| 'Return final structured answer', | ||
| ], | ||
| codeSnippet: `import 'dotenv/config'; |
There was a problem hiding this comment.
gen-ai-implementation.service.js is formatted with 4-space indentation, whereas the rest of the codebase appears to use 2 spaces. Please run the repo formatter (Prettier) or adjust indentation to match the established style, to keep the new service readable and consistent.
| name: 'Midnight Graph', | |
| mode: 'dark', | |
| palette: { | |
| background: '#08111f', | |
| surface: '#111a2e', | |
| surfaceAlt: '#16233d', | |
| primary: '#7c9cff', | |
| secondary: '#6be7c8', | |
| text: '#e8eefc', | |
| muted: '#9bb0d0', | |
| border: '#243354', | |
| }, | |
| layout: 'card-grid', | |
| }; | |
| const implementationProcess = { | |
| prerequisites: [ | |
| 'Node.js 18+ and npm installed', | |
| 'Working Express server with controller/service pattern', | |
| 'One LLM provider key (for example OpenAI)', | |
| ], | |
| installCommands: [ | |
| 'npm i langchain @langchain/openai @langchain/langgraph dotenv', | |
| ], | |
| envSetup: [ | |
| 'Add OPENAI_API_KEY in your .env file', | |
| 'Load environment variables once in server bootstrap', | |
| 'Never hardcode API keys inside services', | |
| ], | |
| folderIntegration: [ | |
| 'Create src/services/gen-ai-single-agent.service.js for single-agent flow', | |
| 'Create src/services/gen-ai-langgraph.service.js for graph orchestration flow', | |
| 'Use existing src/controllers/gen-ai-implementation.controller.js to call service methods', | |
| 'Expose POST endpoints from src/routes/gen-ai-implementation.routes.js for runnable demos', | |
| 'Keep GET /api/v1/gen-ai-implementation as a documentation/guide endpoint', | |
| ], | |
| validationAndSafety: [ | |
| 'Validate question input before calling model', | |
| 'Use short system prompts with clear output format', | |
| 'Add request timeout and retry policy at service layer', | |
| 'Log only request metadata, do not log secrets', | |
| ], | |
| rolloutPlan: [ | |
| 'Phase 1: Ship single-agent first', | |
| 'Phase 2: Add specialist agents for research/writer split', | |
| 'Phase 3: Move to LangGraph for branching, retries, and checkpoints', | |
| ], | |
| }; | |
| const implementationModes = { | |
| single: { | |
| slug: 'single', | |
| title: 'Single-Agent LangChain', | |
| summary: | |
| 'Use one orchestrator when the task has a clear path, a limited toolset, and predictable handoffs.', | |
| whenToUse: [ | |
| 'FAQ answering and support automation', | |
| 'Simple retrieval augmented generation flows', | |
| 'One model plus a few tools or retrievers', | |
| ], | |
| architecture: [ | |
| 'User request enters one agent loop', | |
| 'Agent selects tools or retrievers', | |
| 'Model synthesizes the final answer', | |
| 'Response is formatted for the client UI', | |
| ], | |
| implementationSteps: [ | |
| 'Install LangChain core packages and choose a model provider.', | |
| 'Define a system prompt with the expected output format.', | |
| 'Attach tools for search, database lookup, or API calls.', | |
| 'Wrap the agent in a service method and expose it through a controller.', | |
| 'Return structured JSON so the frontend can render cards, steps, and code snippets.', | |
| ], | |
| processFlow: [ | |
| 'Receive user question in controller', | |
| 'Validate and sanitize input', | |
| 'Build prompt template and invoke one LLM', | |
| 'Return final structured answer', | |
| ], | |
| codeSnippet: `import 'dotenv/config'; | |
| name: 'Midnight Graph', | |
| mode: 'dark', | |
| palette: { | |
| background: '#08111f', | |
| surface: '#111a2e', | |
| surfaceAlt: '#16233d', | |
| primary: '#7c9cff', | |
| secondary: '#6be7c8', | |
| text: '#e8eefc', | |
| muted: '#9bb0d0', | |
| border: '#243354', | |
| }, | |
| layout: 'card-grid', | |
| }; | |
| const implementationProcess = { | |
| prerequisites: [ | |
| 'Node.js 18+ and npm installed', | |
| 'Working Express server with controller/service pattern', | |
| 'One LLM provider key (for example OpenAI)', | |
| ], | |
| installCommands: [ | |
| 'npm i langchain @langchain/openai @langchain/langgraph dotenv', | |
| ], | |
| envSetup: [ | |
| 'Add OPENAI_API_KEY in your .env file', | |
| 'Load environment variables once in server bootstrap', | |
| 'Never hardcode API keys inside services', | |
| ], | |
| folderIntegration: [ | |
| 'Create src/services/gen-ai-single-agent.service.js for single-agent flow', | |
| 'Create src/services/gen-ai-langgraph.service.js for graph orchestration flow', | |
| 'Use existing src/controllers/gen-ai-implementation.controller.js to call service methods', | |
| 'Expose POST endpoints from src/routes/gen-ai-implementation.routes.js for runnable demos', | |
| 'Keep GET /api/v1/gen-ai-implementation as a documentation/guide endpoint', | |
| ], | |
| validationAndSafety: [ | |
| 'Validate question input before calling model', | |
| 'Use short system prompts with clear output format', | |
| 'Add request timeout and retry policy at service layer', | |
| 'Log only request metadata, do not log secrets', | |
| ], | |
| rolloutPlan: [ | |
| 'Phase 1: Ship single-agent first', | |
| 'Phase 2: Add specialist agents for research/writer split', | |
| 'Phase 3: Move to LangGraph for branching, retries, and checkpoints', | |
| ], | |
| }; | |
| const implementationModes = { | |
| single: { | |
| slug: 'single', | |
| title: 'Single-Agent LangChain', | |
| summary: | |
| 'Use one orchestrator when the task has a clear path, a limited toolset, and predictable handoffs.', | |
| whenToUse: [ | |
| 'FAQ answering and support automation', | |
| 'Simple retrieval augmented generation flows', | |
| 'One model plus a few tools or retrievers', | |
| ], | |
| architecture: [ | |
| 'User request enters one agent loop', | |
| 'Agent selects tools or retrievers', | |
| 'Model synthesizes the final answer', | |
| 'Response is formatted for the client UI', | |
| ], | |
| implementationSteps: [ | |
| 'Install LangChain core packages and choose a model provider.', | |
| 'Define a system prompt with the expected output format.', | |
| 'Attach tools for search, database lookup, or API calls.', | |
| 'Wrap the agent in a service method and expose it through a controller.', | |
| 'Return structured JSON so the frontend can render cards, steps, and code snippets.', | |
| ], | |
| processFlow: [ | |
| 'Receive user question in controller', | |
| 'Validate and sanitize input', | |
| 'Build prompt template and invoke one LLM', | |
| 'Return final structured answer', | |
| ], | |
| codeSnippet: `import 'dotenv/config'; |
…on controller and service
Summary
This PR introduces a complete GenAI implementation feature with beginner-friendly guidance and runnable demo endpoints. It also improves startup stability by making OAuth, payment, and email integrations safe when credentials are not configured.
What was added
New GenAI Implementation Guide:
Overview and implementation path.
Step-by-step process for setup, environment, safety, and rollout.
Mode-wise details for Single Agent, Multi-Agent, and LangGraph.
Beginner-friendly code snippets.
New Runnable Demo Endpoints:
POST /api/v1/gen-ai-implementation/single/ask
POST /api/v1/gen-ai-implementation/multi/ask
POST /api/v1/gen-ai-implementation/langgraph/ask
Input Validation for Ask Flows:
Mandatory question field and length validation.
Consistent error responses for invalid inputs.
Reliability and Startup Hardening:
Conditional Google OAuth strategy registration.
Controlled 503 responses for OAuth routes when not configured.
Guarded Razorpay client initialization to avoid crashes.
Graceful failure for Email transporter if env values are missing.
Config freezing restricted to production only.
Testing
Added/Updated tests for guide and ask endpoints.
Covered success and validation failure scenarios.
Test Command: npx jest gen-ai-implementation.test.js --runInBand (Passed ✅)
Impact
Existing API behavior remains unchanged.
App no longer crashes at startup in missing-credential environments.
GenAI feature is now usable for documentation and flow visualization.
Notes
The new ask endpoints currently return deterministic demo-style outputs for implementation guidance. Real LLM execution can be enabled in a follow-up PR by wiring provider keys and LangChain/LangGraph calls.
Checklist
[x] Feature implemented within existing project structure.
[x] Tests added and passing.
[x] Error handling added for optional integrations.
[x] No startup regression in unconfigured local environments.