codeplot
Version:
Interactive CLI tool for feature planning and ADR generation using Gemini 2.5 Pro
84 lines (74 loc) • 3.11 kB
text/typescript
import 'reflect-metadata';
import { injectable, inject } from 'tsyringe';
import { ChatGoogleGenerativeAI } from '@langchain/google-genai';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { BaseMessage, HumanMessage } from '@langchain/core/messages';
import { logger } from '../utils/logger';
import { PRD } from '../types/prd';
@injectable()
export class PRDGeneratorAgent {
private model: ChatGoogleGenerativeAI;
private systemPrompt: string;
constructor(
@inject('ApiKey') apiKey: string,
@inject('ModelName') modelName: string = 'gemini-2.5-pro'
) {
this.model = new ChatGoogleGenerativeAI({
model: modelName,
apiKey,
temperature: 0.7,
});
this.systemPrompt = `You are a Senior Product Manager responsible for creating a well-structured Product Requirements Document (PRD).
Your SOLE task is to synthesize a conversation into a structured PRD.
## Your Process:
1. Review the entire conversation history.
2. Extract key information related to the problem, goals, users, and requirements.
3. Structure this information into a formal PRD.
## Response Format:
You MUST respond with a valid JSON object in this exact format:
{{
"title": "PRD Title",
"sections": [
{{ "title": "Problem Statement", "content": "..." }},
{{ "title": "Goals & Success Metrics", "content": "..." }},
{{ "title": "User Personas", "content": "..." }},
{{ "title": "User Stories", "content": "..." }},
{{ "title": "Functional Requirements", "content": "..." }},
{{ "title": "Out of Scope", "content": "..." }}
]
}}
Do not include any additional text, markdown, or explanations outside of the JSON object.`;
}
async generatePRD(featureRequest: string, conversationHistory: BaseMessage[]): Promise<PRD> {
logger.debug('PRDGeneratorAgent: generatePRD called', {
featureRequestLength: featureRequest.length,
conversationHistoryLength: conversationHistory.length,
});
const prompt = ChatPromptTemplate.fromMessages([
['system', this.systemPrompt],
new MessagesPlaceholder('history'),
]);
const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
const initialMessage = new HumanMessage(`Feature Request: ${featureRequest}`);
const history = [initialMessage, ...conversationHistory.slice(1)];
const prdResultString = await chain.invoke({
history,
});
try {
let cleanResponse = prdResultString.trim();
if (cleanResponse.startsWith('```json')) {
cleanResponse = cleanResponse.replace(/^```json\s*/, '').replace(/\s*```$/, '');
}
const parsed: PRD = JSON.parse(cleanResponse);
logger.info('PRDGeneratorAgent: PRD generated and parsed successfully');
return parsed;
} catch (error) {
logger.error('PRDGeneratorAgent: Failed to parse PRD response', {
error: (error as Error).message,
response: prdResultString,
});
throw new Error('Failed to generate a valid PRD.');
}
}
}