Implement sentiment analysis feature and update pipeline selection
Browse files
src/App.tsx
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
import { useState } from "react";
|
3 |
import PipelineSelector from "./components/PipelineSelector";
|
4 |
import ZeroShotClassification from "./components/ZeroShotClassification";
|
5 |
-
import
|
6 |
|
7 |
function App() {
|
8 |
const [pipeline, setPipeline] = useState("zero-shot-classification");
|
@@ -11,7 +11,7 @@ function App() {
|
|
11 |
<div className="flex flex-col h-screen w-screen p-1">
|
12 |
<PipelineSelector pipeline={pipeline} setPipeline={setPipeline} />
|
13 |
{pipeline === "zero-shot-classification" && <ZeroShotClassification />}
|
14 |
-
{pipeline === "
|
15 |
</div>
|
16 |
);
|
17 |
}
|
|
|
2 |
import { useState } from "react";
|
3 |
import PipelineSelector from "./components/PipelineSelector";
|
4 |
import ZeroShotClassification from "./components/ZeroShotClassification";
|
5 |
+
import SentimentAnalysis from "./components/SentimentAnalysis";
|
6 |
|
7 |
function App() {
|
8 |
const [pipeline, setPipeline] = useState("zero-shot-classification");
|
|
|
11 |
<div className="flex flex-col h-screen w-screen p-1">
|
12 |
<PipelineSelector pipeline={pipeline} setPipeline={setPipeline} />
|
13 |
{pipeline === "zero-shot-classification" && <ZeroShotClassification />}
|
14 |
+
{pipeline === "sentiment-analysis" && <SentimentAnalysis />}
|
15 |
</div>
|
16 |
);
|
17 |
}
|
src/components/PipelineSelector.tsx
CHANGED
@@ -3,7 +3,7 @@ import React from 'react';
|
|
3 |
|
4 |
const pipelines = [
|
5 |
'zero-shot-classification',
|
6 |
-
'
|
7 |
'image-classification',
|
8 |
'question-answering',
|
9 |
'translation',
|
|
|
3 |
|
4 |
const pipelines = [
|
5 |
'zero-shot-classification',
|
6 |
+
'sentiment-analysis',
|
7 |
'image-classification',
|
8 |
'question-answering',
|
9 |
'translation',
|
src/components/{TextClassification.tsx → SentimentAnalysis.tsx}
RENAMED
@@ -1,19 +1,5 @@
|
|
1 |
import { useState, useRef, useEffect, useCallback } from "react";
|
2 |
-
|
3 |
-
interface ClassificationResult {
|
4 |
-
sequence: string;
|
5 |
-
label: string;
|
6 |
-
score: number;
|
7 |
-
}
|
8 |
-
|
9 |
-
interface TextClassificationWorkerMessage {
|
10 |
-
status: "initiate" | "ready" | "output" | "complete";
|
11 |
-
output?: ClassificationResult;
|
12 |
-
}
|
13 |
-
|
14 |
-
interface TextClassificationWorkerInput {
|
15 |
-
text: string;
|
16 |
-
}
|
17 |
|
18 |
const PLACEHOLDER_TEXTS: string[] = [
|
19 |
"I absolutely love this product! It exceeded all my expectations.",
|
@@ -28,9 +14,9 @@ const PLACEHOLDER_TEXTS: string[] = [
|
|
28 |
"Outstanding! This company really knows how to treat their customers.",
|
29 |
].sort(() => Math.random() - 0.5);
|
30 |
|
31 |
-
function
|
32 |
const [text, setText] = useState<string>(PLACEHOLDER_TEXTS.join("\n"));
|
33 |
-
const [results, setResults] = useState<
|
34 |
const [status, setStatus] = useState<string>("idle");
|
35 |
|
36 |
// Create a reference to the worker object.
|
@@ -41,7 +27,7 @@ function TextClassification() {
|
|
41 |
if (!worker.current) {
|
42 |
// Create the worker if it does not yet exist.
|
43 |
worker.current = new Worker(
|
44 |
-
new URL("../workers/
|
45 |
{
|
46 |
type: "module",
|
47 |
}
|
@@ -49,7 +35,7 @@ function TextClassification() {
|
|
49 |
}
|
50 |
|
51 |
// Create a callback function for messages from the worker thread.
|
52 |
-
const onMessageReceived = (e: MessageEvent<
|
53 |
const status = e.data.status;
|
54 |
if (status === "initiate") {
|
55 |
setStatus("loading");
|
@@ -74,7 +60,7 @@ function TextClassification() {
|
|
74 |
const classify = useCallback(() => {
|
75 |
setStatus("processing");
|
76 |
setResults([]); // Clear previous results
|
77 |
-
const message:
|
78 |
worker.current?.postMessage(message);
|
79 |
}, [text]);
|
80 |
|
@@ -166,14 +152,14 @@ function TextClassification() {
|
|
166 |
{results.map((result, index) => (
|
167 |
<div
|
168 |
key={index}
|
169 |
-
className={`p-3 rounded border-2 ${getSentimentColor(result.
|
170 |
>
|
171 |
<div className="flex justify-between items-start mb-2">
|
172 |
<span className="font-semibold text-sm">
|
173 |
-
{formatLabel(result.
|
174 |
</span>
|
175 |
<span className="text-sm font-mono">
|
176 |
-
{(result.
|
177 |
</span>
|
178 |
</div>
|
179 |
<div className="text-sm text-gray-700">
|
@@ -190,4 +176,4 @@ function TextClassification() {
|
|
190 |
);
|
191 |
}
|
192 |
|
193 |
-
export default
|
|
|
1 |
import { useState, useRef, useEffect, useCallback } from "react";
|
2 |
+
import { ClassificationOutput, SentimentAnalysisWorkerInput, WorkerMessage } from "../types";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
4 |
const PLACEHOLDER_TEXTS: string[] = [
|
5 |
"I absolutely love this product! It exceeded all my expectations.",
|
|
|
14 |
"Outstanding! This company really knows how to treat their customers.",
|
15 |
].sort(() => Math.random() - 0.5);
|
16 |
|
17 |
+
function SentimentAnalysis() {
|
18 |
const [text, setText] = useState<string>(PLACEHOLDER_TEXTS.join("\n"));
|
19 |
+
const [results, setResults] = useState<ClassificationOutput[]>([]);
|
20 |
const [status, setStatus] = useState<string>("idle");
|
21 |
|
22 |
// Create a reference to the worker object.
|
|
|
27 |
if (!worker.current) {
|
28 |
// Create the worker if it does not yet exist.
|
29 |
worker.current = new Worker(
|
30 |
+
new URL("../workers/sentiment-analysis.js", import.meta.url),
|
31 |
{
|
32 |
type: "module",
|
33 |
}
|
|
|
35 |
}
|
36 |
|
37 |
// Create a callback function for messages from the worker thread.
|
38 |
+
const onMessageReceived = (e: MessageEvent<WorkerMessage>) => {
|
39 |
const status = e.data.status;
|
40 |
if (status === "initiate") {
|
41 |
setStatus("loading");
|
|
|
60 |
const classify = useCallback(() => {
|
61 |
setStatus("processing");
|
62 |
setResults([]); // Clear previous results
|
63 |
+
const message: SentimentAnalysisWorkerInput = { text };
|
64 |
worker.current?.postMessage(message);
|
65 |
}, [text]);
|
66 |
|
|
|
152 |
{results.map((result, index) => (
|
153 |
<div
|
154 |
key={index}
|
155 |
+
className={`p-3 rounded border-2 ${getSentimentColor(result.labels[0])}`}
|
156 |
>
|
157 |
<div className="flex justify-between items-start mb-2">
|
158 |
<span className="font-semibold text-sm">
|
159 |
+
{formatLabel(result.labels[0])}
|
160 |
</span>
|
161 |
<span className="text-sm font-mono">
|
162 |
+
{(result.scores[0] * 100).toFixed(1)}%
|
163 |
</span>
|
164 |
</div>
|
165 |
<div className="text-sm text-gray-700">
|
|
|
176 |
);
|
177 |
}
|
178 |
|
179 |
+
export default SentimentAnalysis;
|
src/components/ZeroShotClassification.tsx
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
// src/App.tsx
|
2 |
import { useState, useRef, useEffect, useCallback } from "react";
|
3 |
-
import { Section, WorkerMessage,
|
4 |
|
5 |
const PLACEHOLDER_REVIEWS: string[] = [
|
6 |
// battery/charging problems
|
@@ -70,7 +70,7 @@ function ZeroShotClassification() {
|
|
70 |
setStatus("ready");
|
71 |
} else if (status === "output") {
|
72 |
const { sequence, labels, scores } = e.data.output!;
|
73 |
-
|
74 |
// Threshold for classification
|
75 |
const label = scores[0] > 0.5 ? labels[0] : "Other";
|
76 |
|
@@ -99,7 +99,7 @@ function ZeroShotClassification() {
|
|
99 |
|
100 |
const classify = useCallback(() => {
|
101 |
setStatus("processing");
|
102 |
-
const message:
|
103 |
text,
|
104 |
labels: sections
|
105 |
.slice(0, sections.length - 1)
|
|
|
1 |
// src/App.tsx
|
2 |
import { useState, useRef, useEffect, useCallback } from "react";
|
3 |
+
import { Section, WorkerMessage, ZeroShotWorkerInput } from "../types";
|
4 |
|
5 |
const PLACEHOLDER_REVIEWS: string[] = [
|
6 |
// battery/charging problems
|
|
|
70 |
setStatus("ready");
|
71 |
} else if (status === "output") {
|
72 |
const { sequence, labels, scores } = e.data.output!;
|
73 |
+
|
74 |
// Threshold for classification
|
75 |
const label = scores[0] > 0.5 ? labels[0] : "Other";
|
76 |
|
|
|
99 |
|
100 |
const classify = useCallback(() => {
|
101 |
setStatus("processing");
|
102 |
+
const message: ZeroShotWorkerInput = {
|
103 |
text,
|
104 |
labels: sections
|
105 |
.slice(0, sections.length - 1)
|
src/types.ts
CHANGED
@@ -9,14 +9,21 @@ export interface ClassificationOutput {
|
|
9 |
scores: number[];
|
10 |
}
|
11 |
|
|
|
|
|
12 |
export interface WorkerMessage {
|
13 |
status: 'initiate' | 'ready' | 'output' | 'complete';
|
14 |
output?: any;
|
15 |
}
|
16 |
|
17 |
-
export interface
|
18 |
text: string;
|
19 |
labels: string[];
|
20 |
}
|
21 |
|
|
|
|
|
|
|
|
|
|
|
22 |
export type AppStatus = 'idle' | 'loading' | 'processing';
|
|
|
9 |
scores: number[];
|
10 |
}
|
11 |
|
12 |
+
|
13 |
+
|
14 |
export interface WorkerMessage {
|
15 |
status: 'initiate' | 'ready' | 'output' | 'complete';
|
16 |
output?: any;
|
17 |
}
|
18 |
|
19 |
+
export interface ZeroShotWorkerInput {
|
20 |
text: string;
|
21 |
labels: string[];
|
22 |
}
|
23 |
|
24 |
+
|
25 |
+
export interface SentimentAnalysisWorkerInput {
|
26 |
+
text: string;
|
27 |
+
}
|
28 |
+
|
29 |
export type AppStatus = 'idle' | 'loading' | 'processing';
|
src/workers/{text-classification.js → sentiment-analysis.js}
RENAMED
@@ -36,8 +36,8 @@ self.addEventListener("message", async (event) => {
|
|
36 |
status: "output",
|
37 |
output: {
|
38 |
sequence: line,
|
39 |
-
|
40 |
-
|
41 |
}
|
42 |
});
|
43 |
}
|
|
|
36 |
status: "output",
|
37 |
output: {
|
38 |
sequence: line,
|
39 |
+
labels: [output[0].label],
|
40 |
+
scores: [output[0].score]
|
41 |
}
|
42 |
});
|
43 |
}
|