Vokturz commited on
Commit
08476ef
·
1 Parent(s): c3f0819

Refactor app structure to integrate text classification feature and update pipeline selection

Browse files
src/App.tsx CHANGED
@@ -1,19 +1,71 @@
1
  import { useState } from "react";
2
  import PipelineSelector from "./components/PipelineSelector";
3
  import ZeroShotClassification from "./components/ZeroShotClassification";
4
- import SentimentAnalysis from "./components/SentimentAnalysis";
 
 
5
 
6
  function App() {
7
  const [pipeline, setPipeline] = useState("zero-shot-classification");
8
 
9
  return (
10
- <div className="flex flex-col h-screen w-screen p-1">
11
- <PipelineSelector pipeline={pipeline} setPipeline={setPipeline} />
12
- {pipeline === "zero-shot-classification" && <ZeroShotClassification />}
13
- {pipeline === "sentiment-analysis" && <SentimentAnalysis />}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  </div>
15
  );
16
  }
17
 
18
  export default App;
19
-
 
1
  import { useState } from "react";
2
  import PipelineSelector from "./components/PipelineSelector";
3
  import ZeroShotClassification from "./components/ZeroShotClassification";
4
+ import TextClassification from "./components/TextClassification";
5
+ import Header from "./Header";
6
+ import Footer from "./Footer";
7
 
8
  function App() {
9
  const [pipeline, setPipeline] = useState("zero-shot-classification");
10
 
11
  return (
12
+ <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
13
+ <Header />
14
+
15
+ <main className="max-w-8xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
16
+ {/* Pipeline Selection */}
17
+ <div className="mb-8">
18
+ <div className="bg-white rounded-lg shadow-sm border p-6">
19
+ <h2 className="text-lg font-semibold text-gray-900 mb-4">
20
+ Choose a Pipeline
21
+ </h2>
22
+ <PipelineSelector pipeline={pipeline} setPipeline={setPipeline} />
23
+
24
+ {/* Pipeline Description */}
25
+ <div className="mt-4 p-4 bg-gray-50 rounded-lg">
26
+ <div className="flex items-start space-x-3">
27
+ <div className="flex-shrink-0">
28
+ <svg
29
+ className="w-5 h-5 text-blue-500 mt-0.5"
30
+ fill="currentColor"
31
+ viewBox="0 0 20 20"
32
+ >
33
+ <path
34
+ fillRule="evenodd"
35
+ d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
36
+ clipRule="evenodd"
37
+ />
38
+ </svg>
39
+ </div>
40
+ <div>
41
+ <h3 className="text-sm font-medium text-gray-900">
42
+ {pipeline === "zero-shot-classification"
43
+ ? "Zero-Shot Classification"
44
+ : "Text-Classification"}
45
+ </h3>
46
+ <p className="text-sm text-gray-600 mt-1">
47
+ {pipeline === "zero-shot-classification"
48
+ ? "Classify text into custom categories without training data. Perfect for organizing content, routing messages, or analyzing feedback."
49
+ : "Classify text into predefined categories. Ideal for sentiment analysis, spam detection, or topic categorization."}
50
+ </p>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ {/* Pipeline Component */}
58
+ <div className="bg-white rounded-lg shadow-sm border overflow-hidden">
59
+ {pipeline === "zero-shot-classification" && (
60
+ <ZeroShotClassification />
61
+ )}
62
+ {pipeline === "text-classification" && <TextClassification />}
63
+ </div>
64
+ </main>
65
+
66
+ <Footer />
67
  </div>
68
  );
69
  }
70
 
71
  export default App;
 
src/Footer.tsx ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function Footer() {
2
+ return (
3
+ <footer className="bg-white border-t mt-12">
4
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
5
+ <div className="flex items-center justify-between">
6
+ <div className="flex items-center space-x-4 text-sm text-gray-500">
7
+ <span>Powered by</span>
8
+ <a
9
+ href="https://huggingface.co/docs/transformers.js"
10
+ target="_blank"
11
+ rel="noopener noreferrer"
12
+ className="inline-flex items-center space-x-1 text-blue-600 hover:text-blue-800 font-medium"
13
+ >
14
+ <span>🤗 Transformers.js</span>
15
+ </a>
16
+ </div>
17
+ <div className="text-sm text-gray-500">
18
+ All processing happens in your browser - your data stays private
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </footer>
23
+ );
24
+ }
25
+
26
+ export default Footer;
src/Header.tsx ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function Header() {
2
+ return (
3
+ <header className="bg-white shadow-sm border-b">
4
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
5
+ <div className="flex items-center justify-between h-16">
6
+ <div className="flex items-center space-x-3">
7
+ <div className="w-8 h-8 bg-gradient-to-r from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center">
8
+ <svg
9
+ className="w-5 h-5 text-white"
10
+ fill="currentColor"
11
+ viewBox="0 0 20 20"
12
+ >
13
+ <path
14
+ fillRule="evenodd"
15
+ d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z"
16
+ clipRule="evenodd"
17
+ />
18
+ </svg>
19
+ </div>
20
+ <div>
21
+ <h1 className="text-xl font-bold text-gray-900">
22
+ Transformers.js Playground
23
+ </h1>
24
+ <p className="text-sm text-gray-500">
25
+ Run Hugging Face models in your browser
26
+ </p>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </header>
32
+ )
33
+ }
34
+
35
+ export default Header;
src/components/PipelineSelector.tsx CHANGED
@@ -2,7 +2,7 @@ import React from 'react';
2
 
3
  const pipelines = [
4
  'zero-shot-classification',
5
- 'sentiment-analysis',
6
  'image-classification',
7
  'question-answering',
8
  'translation',
 
2
 
3
  const pipelines = [
4
  'zero-shot-classification',
5
+ 'text-classification',
6
  'image-classification',
7
  'question-answering',
8
  'translation',
src/components/{SentimentAnalysis.tsx → TextClassification.tsx} RENAMED
@@ -1,5 +1,5 @@
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,7 +14,7 @@ const PLACEHOLDER_TEXTS: string[] = [
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");
@@ -44,14 +44,18 @@ function SentimentAnalysis() {
44
  setStatus("ready");
45
  } else if (status === "progress") {
46
  setStatus("progress");
47
- if (e.data.output.progress && (e.data.output.file as string).startsWith('onnx'))
48
- setProgress(e.data.output.progress)
 
 
 
49
  } else if (status === "output") {
50
  const result = e.data.output!;
51
  setResults((prevResults) => [...prevResults, result]);
 
52
  } else if (status === "complete") {
53
  setStatus("idle");
54
- setProgress(100)
55
  }
56
  };
57
 
@@ -66,7 +70,7 @@ function SentimentAnalysis() {
66
  const classify = useCallback(() => {
67
  setStatus("processing");
68
  setResults([]); // Clear previous results
69
- const message: SentimentAnalysisWorkerInput = { text };
70
  worker.current?.postMessage(message);
71
  }, [text]);
72
 
@@ -76,39 +80,11 @@ function SentimentAnalysis() {
76
  setResults([]);
77
  };
78
 
79
- const getSentimentColor = (label: string): string => {
80
- switch (label.toLowerCase()) {
81
- case "positive":
82
- case "label_2":
83
- return "bg-green-100 border-green-300";
84
- case "negative":
85
- case "label_0":
86
- return "bg-red-100 border-red-300";
87
- case "neutral":
88
- case "label_1":
89
- return "bg-yellow-100 border-yellow-300";
90
- default:
91
- return "bg-gray-100 border-gray-300";
92
- }
93
- };
94
-
95
- const formatLabel = (label: string): string => {
96
- switch (label) {
97
- case "LABEL_0":
98
- return "Negative";
99
- case "LABEL_1":
100
- return "Neutral";
101
- case "LABEL_2":
102
- return "Positive";
103
- default:
104
- return label;
105
- }
106
- };
107
 
108
  return (
109
- <div className="flex flex-col h-screen w-screen p-4">
110
  <h1 className="text-2xl font-bold mb-4">Text Classification</h1>
111
-
112
  <div className="flex flex-col lg:flex-row gap-4 h-full">
113
  {/* Input Section */}
114
  <div className="flex flex-col w-full lg:w-1/2">
@@ -119,7 +95,7 @@ function SentimentAnalysis() {
119
  onChange={(e) => setText(e.target.value)}
120
  placeholder="Enter text to classify (one per line)..."
121
  />
122
-
123
  <div className="flex gap-2 mt-4">
124
  <button
125
  className="flex-1 py-2 px-4 bg-blue-500 hover:bg-blue-600 rounded text-white font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
@@ -129,14 +105,12 @@ function SentimentAnalysis() {
129
  {!busy
130
  ? "Classify Text"
131
  : status === "loading"
132
- ? "Model loading..."
133
- : "Processing..."}
134
  </button>
135
- { status === "progress" &&
136
- <div className="text-sm font-medium">
137
- {progress}%
138
- </div>
139
- }
140
  <button
141
  className="py-2 px-4 bg-gray-500 hover:bg-gray-600 rounded text-white font-medium transition-colors"
142
  onClick={handleClear}
@@ -151,7 +125,7 @@ function SentimentAnalysis() {
151
  <label className="text-lg font-medium mb-2">
152
  Classification Results ({results.length}):
153
  </label>
154
-
155
  <div className="border border-gray-300 rounded p-3 flex-grow overflow-y-auto">
156
  {results.length === 0 ? (
157
  <div className="text-gray-500 text-center py-8">
@@ -162,11 +136,11 @@ function SentimentAnalysis() {
162
  {results.map((result, index) => (
163
  <div
164
  key={index}
165
- className={`p-3 rounded border-2 ${getSentimentColor(result.labels[0])}`}
166
  >
167
  <div className="flex justify-between items-start mb-2">
168
  <span className="font-semibold text-sm">
169
- {formatLabel(result.labels[0])}
170
  </span>
171
  <span className="text-sm font-mono">
172
  {(result.scores[0] * 100).toFixed(1)}%
@@ -186,4 +160,4 @@ function SentimentAnalysis() {
186
  );
187
  }
188
 
189
- export default SentimentAnalysis;
 
1
  import { useState, useRef, useEffect, useCallback } from "react";
2
+ import { ClassificationOutput, TextClassificationWorkerInput, 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 TextClassification() {
18
  const [text, setText] = useState<string>(PLACEHOLDER_TEXTS.join("\n"));
19
  const [results, setResults] = useState<ClassificationOutput[]>([]);
20
  const [status, setStatus] = useState<string>("idle");
 
44
  setStatus("ready");
45
  } else if (status === "progress") {
46
  setStatus("progress");
47
+ if (
48
+ e.data.output.progress &&
49
+ (e.data.output.file as string).startsWith("onnx")
50
+ )
51
+ setProgress(e.data.output.progress);
52
  } else if (status === "output") {
53
  const result = e.data.output!;
54
  setResults((prevResults) => [...prevResults, result]);
55
+ console.log(result);
56
  } else if (status === "complete") {
57
  setStatus("idle");
58
+ setProgress(100);
59
  }
60
  };
61
 
 
70
  const classify = useCallback(() => {
71
  setStatus("processing");
72
  setResults([]); // Clear previous results
73
+ const message: TextClassificationWorkerInput = { text };
74
  worker.current?.postMessage(message);
75
  }, [text]);
76
 
 
80
  setResults([]);
81
  };
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
  return (
85
+ <div className="flex flex-col h-[40vh] max-h-[80vh] w-full p-4">
86
  <h1 className="text-2xl font-bold mb-4">Text Classification</h1>
87
+
88
  <div className="flex flex-col lg:flex-row gap-4 h-full">
89
  {/* Input Section */}
90
  <div className="flex flex-col w-full lg:w-1/2">
 
95
  onChange={(e) => setText(e.target.value)}
96
  placeholder="Enter text to classify (one per line)..."
97
  />
98
+
99
  <div className="flex gap-2 mt-4">
100
  <button
101
  className="flex-1 py-2 px-4 bg-blue-500 hover:bg-blue-600 rounded text-white font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
 
105
  {!busy
106
  ? "Classify Text"
107
  : status === "loading"
108
+ ? "Model loading..."
109
+ : "Processing..."}
110
  </button>
111
+ {status === "progress" && (
112
+ <div className="text-sm font-medium">{progress}%</div>
113
+ )}
 
 
114
  <button
115
  className="py-2 px-4 bg-gray-500 hover:bg-gray-600 rounded text-white font-medium transition-colors"
116
  onClick={handleClear}
 
125
  <label className="text-lg font-medium mb-2">
126
  Classification Results ({results.length}):
127
  </label>
128
+
129
  <div className="border border-gray-300 rounded p-3 flex-grow overflow-y-auto">
130
  {results.length === 0 ? (
131
  <div className="text-gray-500 text-center py-8">
 
136
  {results.map((result, index) => (
137
  <div
138
  key={index}
139
+ className="p-3 rounded border-2"
140
  >
141
  <div className="flex justify-between items-start mb-2">
142
  <span className="font-semibold text-sm">
143
+ {result.labels[0]}
144
  </span>
145
  <span className="text-sm font-mono">
146
  {(result.scores[0] * 100).toFixed(1)}%
 
160
  );
161
  }
162
 
163
+ export default TextClassification;
src/components/ZeroShotClassification.tsx CHANGED
@@ -154,7 +154,7 @@ function ZeroShotClassification() {
154
  };
155
 
156
  return (
157
- <div className="flex flex-col h-screen w-screen p-1">
158
  <textarea
159
  className="border w-full p-1 h-1/2"
160
  value={text}
 
154
  };
155
 
156
  return (
157
+ <div className="flex flex-col h-screen w-full p-1">
158
  <textarea
159
  className="border w-full p-1 h-1/2"
160
  value={text}
src/types.ts CHANGED
@@ -22,7 +22,7 @@ export interface ZeroShotWorkerInput {
22
  }
23
 
24
 
25
- export interface SentimentAnalysisWorkerInput {
26
  text: string;
27
  }
28
 
 
22
  }
23
 
24
 
25
+ export interface TextClassificationWorkerInput {
26
  text: string;
27
  }
28
 
src/workers/sentiment-analysis.js CHANGED
@@ -2,8 +2,8 @@
2
  import { pipeline } from "@huggingface/transformers";
3
 
4
  class MyTextClassificationPipeline {
5
- static task = "sentiment-analysis";
6
- static model = 'Xenova/bert-base-multilingual-uncased-sentiment';
7
  static instance = null;
8
 
9
  static async getInstance(progress_callback = null) {
 
2
  import { pipeline } from "@huggingface/transformers";
3
 
4
  class MyTextClassificationPipeline {
5
+ static task = "text-classification";
6
+ static model = "Xenova/bert-base-multilingual-uncased-sentiment";
7
  static instance = null;
8
 
9
  static async getInstance(progress_callback = null) {