diff --git a/README.md b/README.md index 59a7682..8f32b76 100644 --- a/README.md +++ b/README.md @@ -365,7 +365,7 @@ It has also node server for Govbot. First 3 endpoint is used for to integrate di ### 1. Create a Proposal -**URL**: +**ENDPOINT**: `POST /api/govbot/proposal` **Headers**: @@ -386,7 +386,7 @@ It has also node server for Govbot. First 3 endpoint is used for to integrate di ### 2. Add Feedback to a Proposal -**URL**: +**ENDPOINT**: `POST /api/govbot/proposal` **Headers**: @@ -404,7 +404,7 @@ It has also node server for Govbot. First 3 endpoint is used for to integrate di ### 3. Health Check -**URL**: +**ENDPOINT**: `GET /api/govbot/` **Headers**: @@ -420,7 +420,7 @@ No header. ### 4. Health Check for Redis Integration and API -**URL**: +**ENDPOINT**: `GET /api/govbot/health` **Headers**: @@ -439,7 +439,7 @@ No header. ### 5. Add Proposal to DB -**URL**: +**ENDPOINT**: `POST /api/govbot/proposals` **Headers**: @@ -475,7 +475,7 @@ No header. ### 6. Summarize Proposal by ID -**URL**: +**ENDPOINT**: `POST /api/govbot/proposals/:id/summarize` **Headers**: @@ -495,7 +495,7 @@ No header. ### 7. Get Proposal Summary by Proposal ID -**URL**: +**ENDPOINT**: `GET /api/govbot/proposals/:id/summary` **Headers**: @@ -513,6 +513,75 @@ No header. } ``` +### 8. Add Feedback + +**ENDPOINT**: +`POST /api/govbot/proposals/:proposalId/feedbacks` + +**Headers**: + +- Content-Type: `application/json` +- Authorization: `Bearer ` + +**Request Body (JSON)**: + +```json +{ + "username": "Alice", + "feedbackContent": "This is a great proposal!" +} +``` + +**Example Response** + +```json +{ + "proposalId": "1234", + "username": "Alice", + "feedbackContent": "This is a great proposal!" +} +``` + +### 9. Summarize Feedbacks for Proposal + +**ENDPOINT**: +`POST /api/govbot/proposals/:proposalId/feedbacks/summary` + +**Headers**: + +- Content-Type: `application/json` +- Authorization: `Bearer ` + +No request body required. + +**Example Response** + +```json +{ + "proposalId": 1234, + "feedbackSummary": "Generated feedback summary text..." +} +``` + +### 10. Get Feedback Summary + +**ENDPOINT**: +`GET /api/govbot/proposals/:proposalId/feedbacks/summary` + +**Headers**: + +- Content-Type: `application/json` +- Authorization: `Bearer ` + +**Example Response** + +```json +{ + "proposalId": 1234, + "feedbackSummary": "Generated feedback summary text..." +} +``` + ## Contributions To make a contribution, follow these steps: diff --git a/package.json b/package.json index eb954f0..ecd2e89 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "discord-bot", "type": "module", - "version": "0.4.2", + "version": "0.4.6", "description": "", "main": "index.js", "scripts": { diff --git a/src/controllers/controllers.ts b/src/controllers/controllers.ts index 1abeb7a..d7b136e 100644 --- a/src/controllers/controllers.ts +++ b/src/controllers/controllers.ts @@ -48,9 +48,36 @@ async function proposalSummarizer( return summary; } -function feedbackSummarizer(text: string): string { - if (!text) return "No content to summarize."; - return `DUMMY SUMMARY: ${text.slice(0, 100)}...`; +type FeedbackDictionary = { [username: string]: string }; + +async function feedbackSummarizer( + proposalName: string, + proposalDescription: string, + proposalAuthor: string, + fundingRoundId: string, + feedbacks: FeedbackDictionary, +): Promise { + const FEEDBACK_PROMPT = FEEDBACK_SUMMARIZE_PROMPT( + proposalName, + proposalDescription, + proposalAuthor, + fundingRoundId, + feedbacks, + ); + const completion = await openai.chat.completions.create({ + model: "gpt-4o-mini", + messages: [ + { + role: "user", + content: FEEDBACK_PROMPT, + }, + ], + }); + + const summary = completion.choices[0].message.content; + log.debug(summary); + + return summary; } // ------------------------------------------------------------------ @@ -265,18 +292,41 @@ export const summarizeFeedbacks = async ( const { proposalId } = req.params; const feedbackKey = `proposal_feedbacks:${proposalId}`; + // Retrieve feedbacks for the proposal const feedbackData = await redisClient.get(feedbackKey); if (!feedbackData) { res.status(404).json({ error: "No feedbacks found for this proposal." }); + return; } - const feedbacks: ProposalFeedback[] = JSON.parse(feedbackData); - // TODO: makes this AI process friendly :D - // Combine all feedback text into one string - const combinedText = feedbacks.map((f) => f.feedbackContent).join("\n"); + const proposalKey = `proposal:${proposalId}`; + const proposalData = await redisClient.get(proposalKey); + if (!proposalData) { + res.status(404).json({ error: "Proposal not found." }); + return; + } + const proposal: GovbotProposal = JSON.parse(proposalData); + + const feedbacksDictionary: FeedbackDictionary = feedbacks.reduce( + (dict, feedback) => { + if (dict[feedback.username]) { + dict[feedback.username] += "\n" + feedback.feedbackContent; + } else { + dict[feedback.username] = feedback.feedbackContent; + } + return dict; + }, + {} as FeedbackDictionary, + ); - const summaryText = feedbackSummarizer(combinedText); + const summaryText = await feedbackSummarizer( + proposal.proposalName, + proposal.proposalDescription, + proposal.proposalAuthor, + proposal.fundingRoundId.toString(), // Ensure fundingRoundId is a string. + feedbacksDictionary, + ); const feedbacksSummary: ProposalFeedbacksSummary = { proposalId: parseInt(proposalId, 10), diff --git a/src/helpers/prompts.ts b/src/helpers/prompts.ts index ed81f7e..fe2b5be 100644 --- a/src/helpers/prompts.ts +++ b/src/helpers/prompts.ts @@ -22,5 +22,42 @@ Summary Guideline: * Keep the summary within 3-6 sentences while maintaining clarity and completeness. `.trim(); -export const FEEDBACK_SUMMARIZE_PROMPT = (username, feedbackContent) => - ``.trim(); +type FeedbackDictionary = { [username: string]: string }; + +function formatFeedbacks(feedbacks: FeedbackDictionary): string { + return Object.entries(feedbacks) + .map(([username, feedback]) => `${username}: "${feedback}"`) + .join("\n"); +} + +export const FEEDBACK_SUMMARIZE_PROMPT = ( + proposalName, + proposalDescription, + proposalAuthor, + fundingRoundId, + feedbacks, +) => + ` +You are an AI assistant that specializes in summarizing project proposals and their associated feedbacks. +Your task is to generate a concise and insightful summary for the given project proposals and especially +their feedbacks for Mina Ecosystem Funding program. You need to include all meaningful summarized feedbacks. + +You are given these datas to summarize: +- Proposal Name: ${proposalName} +- Description: ${proposalDescription} +- Author: ${proposalAuthor} +- Funding Round ID: ${fundingRoundId} +- Feedbacks: ${formatFeedbacks(feedbacks)} + +Summary Guideline: +Your response should focus primarily on analyzing and summarizing the feedback. +* Brief Proposal Summary: A 2-3 sentence summary explaining the core idea, objective, and significance of the proposal. +* Key Strengths (Based on Feedbacks): Highlight the main positive aspects of the proposal as mentioned by users. +* Main Concerns (Based on Feedbacks): Identify common criticisms, potential risks, or areas that need improvement. +* Recurring Themes in Feedback: Identify common points that multiple users have mentioned. +Include insights such as usability concerns, technical feasibility, business potential, or ethical considerations. +* Overall Sentiment: Determine whether the general sentiment is positive, neutral, or negative based on user feedback. +* Constructive Suggestions: If applicable, provide recommendations for improving the proposal. +* Edge Opinions: Include edge sentiments if you thinks it is meaningful as an great AI Agent. + +`.trim();