Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MongoFeed project #77

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/mongo-feed/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.env
.env.local
package-lock.json
.next
117 changes: 117 additions & 0 deletions apps/mongo-feed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# MongoFeed

MongoFeed is a comprehensive platform for product feedback analysis and sentiment tracking. It leverages MongoDB for data storage and Amazon Bedrock for AI-powered sentiment analysis, providing valuable insights into customer feedback and product reviews.

> ♥️ Inpired by a customer success story : [Syncly](https://www.mongodb.com/customers/syncly)

## Hosted Version

https://mongo-feed.vercel.app

## Features

- File upload for product feedback analysis (JSON, HTML, images)
- Chat paste functionality for direct input of customer interactions
- Sentiment analysis using Amazon Bedrock AI
- Real-time processing queue for feedback analysis
- Interactive charts and visualizations:
- Feedback trends over time
- Sentiment distribution
- Top issues identification
- Agent performance tracking and sentiment analysis

## Prerequisites

Before you begin, ensure you have the following installed:
- Node.js (v14 or later)
- npm (v6 or later)
- MongoDB (6.0+)
- An AWS account with access to Amazon Bedrock and Claude 3.5 V2 model

## Installation

1. **Clone the repository:**
```bash
git clone <repository-url>
cd mongo-feed
```

2. **Install dependencies:**
```bash
npm install
```

3. **Configure environment variables:**
- Create a `.env.local` file in the root directory.
- Add your MongoDB connection string and AWS Bedrock credentials.
```env
MONGODB_URI=your_mongodb_connection_string
AWS_REGION=your_aws_region
AWS_ACCESS_KEY_ID=your_aws_access_key_id
AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key
```
**Note:** Ensure you have the necessary permissions for Amazon Bedrock and MongoDB.

## Development

1. **Run the development server:**
```bash
npm run dev
```
Open [http://localhost:3000](http://localhost:3000) in your browser to view the application.

## Building for Production

1. **Build the application:**
```bash
npm run build
```

2. **Start the production server:**
```bash
npm run start
```

## Usage

To use MongoFeed:

1. **Access the application** in your browser at [http://localhost:3000](http://localhost:3000) after running the development or production server.
2. **Upload Feedback Files or Paste Chat Interactions:**
- Navigate to the feedback input section.
- Choose to upload files (JSON, HTML, images) or paste text from chat interactions.
- Follow the on-screen instructions to input your feedback data.
3. **View Sentiment Analysis Results and Visualizations:**
- Once the feedback is processed, navigate to the dashboard.
- Explore interactive charts and visualizations to understand:
- Feedback trends over time
- Sentiment distribution across feedback
- Top issues identified from the feedback
4. **Navigate the Dashboard:**
- Use the dashboard to access different features, such as:
- Real-time processing queue monitoring.
- Agent performance tracking and sentiment analysis (if applicable).
- Detailed views of individual feedback entries and their sentiment analysis.

## Configuration

- **Environment Variables:**
- `MONGODB_URI`: MongoDB connection string for your MongoDB database.
- `AWS_REGION`: AWS region where your Bedrock service is configured.
- `AWS_ACCESS_KEY_ID`: AWS access key ID for authentication.
- `AWS_SECRET_ACCESS_KEY`: AWS secret access key for authentication.

- **Other configurations:**
- The application may have additional configurations that can be set in the `.env.local` file or through the application's settings panel. Refer to the application documentation for advanced configuration options.

## Contributing

If you'd like to contribute to MongoFeed, please follow these guidelines:
1. Fork the repository.
2. Create a branch for your feature or bug fix.
3. Ensure your code adheres to the project's coding standards.
4. Submit a pull request with a clear description of your changes.

## License

[Specify the project license, e.g., MIT License]
46 changes: 46 additions & 0 deletions apps/mongo-feed/app/api/agent-analysis/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { NextResponse } from "next/server"
import clientPromise from "@/lib/mongodb"

export const dynamic = "force-dynamic"
export const revalidate = 0

export async function GET() {
try {
const client = await clientPromise
const db = client.db("mongofeed")

const agentAnalysis = await db
.collection("chat_analyses")
.aggregate([
{ $unwind: "$messages" },
{ $match: { "messages.role": "Agent" } },
{
$group: {
_id: "$messages.agentName",
positiveSentiment: { $sum: { $cond: [{ $eq: ["$messages.sentiment", "positive"] }, 1, 0] } },
neutralSentiment: { $sum: { $cond: [{ $eq: ["$messages.sentiment", "neutral"] }, 1, 0] } },
negativeSentiment: { $sum: { $cond: [{ $eq: ["$messages.sentiment", "negative"] }, 1, 0] } },
totalInteractions: { $sum: 1 },
},
},
{
$project: {
agentName: "$_id",
positiveSentiment: 1,
neutralSentiment: 1,
negativeSentiment: 1,
totalInteractions: 1,
_id: 0,
},
},
{ $sort: { totalInteractions: -1 } },
])
.toArray()

return NextResponse.json(agentAnalysis)
} catch (error) {
console.error("Error fetching agent analysis:", error)
return NextResponse.json({ error: "An error occurred while fetching agent analysis." }, { status: 500 })
}
}

51 changes: 51 additions & 0 deletions apps/mongo-feed/app/api/agent-sentiment/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { NextResponse } from "next/server"
import clientPromise from "@/lib/mongodb"

export const dynamic = "force-dynamic"
export const revalidate = 0

export async function GET() {
try {
const client = await clientPromise
const db = client.db("mongofeed")

// First get all agents with their sentiment data
const agentData = await db
.collection("agent_sentiment")
.aggregate([
{
$lookup: {
from: "chat_analyses",
let: { agentName: "$agentName" },
pipeline: [
{
$match: {
$expr: { $eq: ["$analysis.agentName", "$$agentName"] },
},
},
{ $sort: { createdAt: -1 } },
{ $limit: 5 },
{
$project: {
id: { $toString: "$_id" },
date: "$createdAt",
summary: "$analysis.summary",
sentiment: "$analysis.overallSentiment",
issues: "$analysis.mainTopics",
},
},
],
as: "recentChats",
},
},
{ $sort: { totalInteractions: -1 } },
])
.toArray()

return NextResponse.json(agentData)
} catch (error) {
console.error("Error fetching agent sentiment:", error)
return NextResponse.json({ error: "An error occurred while fetching agent sentiment." }, { status: 500 })
}
}

56 changes: 56 additions & 0 deletions apps/mongo-feed/app/api/analyze-feedback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { type NextRequest, NextResponse } from "next/server"
import { analyzeAgentFeedback, analyzeProductReview } from "@/lib/analyze-content"
import clientPromise from "@/lib/mongodb"

export const dynamic = "force-dynamic"
export const runtime = "nodejs"

export async function POST(req: NextRequest) {
try {
const formData = await req.formData()
const file = formData.get("file") as File
const type = formData.get("type") as string

if (!file) {
return NextResponse.json({ error: "No file uploaded" }, { status: 400 })
}

const contentType = file.type
const fileName = file.name

let content: string | ArrayBuffer
if (contentType.startsWith("image/")) {
content = await file.arrayBuffer()
} else {
const buffer = await file.arrayBuffer()
content = new TextDecoder().decode(buffer)
}

let analysisResult

if (type === "agent") {
analysisResult = await analyzeAgentFeedback(content as string)
} else if (type === "product") {
analysisResult = await analyzeProductReview(content, contentType, fileName)
} else {
return NextResponse.json({ error: "Invalid analysis type" }, { status: 400 })
}

// Store the analysis result in MongoDB
const client = await clientPromise
const db = client.db("mongofeed")
await db.collection("chat_analyses").insertOne({
type,
contentType,
fileName,
analysis: analysisResult,
createdAt: new Date(),
})

return NextResponse.json({ sentiments: analysisResult })
} catch (error) {
console.error("Error analyzing feedback:", error)
return NextResponse.json({ error: "An error occurred while analyzing feedback." }, { status: 500 })
}
}

35 changes: 35 additions & 0 deletions apps/mongo-feed/app/api/analyze-sentiment/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { NextRequest, NextResponse } from 'next/server';
import { bedrock } from '@/lib/bedrock';
import { generateText } from 'ai';

export async function POST(req: NextRequest) {
try {
const data = await req.json();
const documents = data.documents;

if (!Array.isArray(documents)) {
return NextResponse.json({ error: 'Invalid input. Expected an array of documents.' }, { status: 400 });
}

const sentiments = await Promise.all(
documents.map(async (doc) => {
try {
const { text } = await generateText({
model: bedrock('aanthropic.claude-3-5-sonnet-20241022-v2:0'),
prompt: `Analyze the sentiment of the following text and respond with only one word: "positive", "negative", or "neutral". Text: "${doc}"`,
});
return text.trim().toLowerCase();
} catch (error) {
console.error('Error analyzing individual document:', error);
return 'error';
}
})
);

return NextResponse.json({ sentiments });
} catch (error) {
console.error('Error analyzing sentiment:', error);
return NextResponse.json({ error: 'An error occurred while analyzing sentiment.' }, { status: 500 });
}
}

Loading
Loading