yangg40 commited on
Commit
a8af144
·
verified ·
1 Parent(s): 75ac4a8

Initial commit

Browse files
Files changed (4) hide show
  1. Dockerfile +25 -0
  2. README.md +61 -11
  3. app.py +145 -0
  4. requirements.txt +3 -0
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ build-essential \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Copy requirements and install Python dependencies
12
+ COPY requirements.txt .
13
+ RUN pip install --no-cache-dir -r requirements.txt
14
+
15
+ # Download the model at build time to speed up startup
16
+ RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')"
17
+
18
+ # Copy application code
19
+ COPY app.py .
20
+
21
+ # Expose port 7860 (required by HuggingFace Spaces)
22
+ EXPOSE 7860
23
+
24
+ # Run the application
25
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,11 +1,61 @@
1
- ---
2
- title: Arxiv Embed Api
3
- emoji: 📈
4
- colorFrom: indigo
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ArXiv Papers Bot - Embedding API
2
+
3
+ This HuggingFace Space provides a lightweight embedding API for the ArXiv Papers Discord Bot.
4
+
5
+ ## Endpoints
6
+
7
+ ### `GET /`
8
+ Health check endpoint
9
+
10
+ ### `POST /embed`
11
+ Generate embedding for a single text query
12
+
13
+ **Request:**
14
+ ```json
15
+ {
16
+ "text": "transformer architecture for computer vision"
17
+ }
18
+ ```
19
+
20
+ **Response:**
21
+ ```json
22
+ {
23
+ "embedding": [0.123, -0.456, ...],
24
+ "dimensions": 384
25
+ }
26
+ ```
27
+
28
+ ### `POST /batch_embed`
29
+ Generate embeddings for multiple texts (max 100)
30
+
31
+ **Request:**
32
+ ```json
33
+ {
34
+ "texts": ["Text 1", "Text 2", "Text 3"]
35
+ }
36
+ ```
37
+
38
+ **Response:**
39
+ ```json
40
+ {
41
+ "embeddings": [[...], [...], [...]],
42
+ "count": 3,
43
+ "dimensions": 384
44
+ }
45
+ ```
46
+
47
+ ## Model
48
+
49
+ - **Model:** sentence-transformers/all-MiniLM-L6-v2
50
+ - **Dimensions:** 384
51
+ - **Size:** ~80MB
52
+ - **Speed:** Optimized for CPU inference
53
+
54
+ ## Deployment to HuggingFace Spaces
55
+
56
+ 1. Create a new Space on HuggingFace
57
+ 2. Select "Docker" as the SDK
58
+ 3. Upload all files from this directory
59
+ 4. The Space will automatically build and deploy
60
+
61
+ No secrets required for this service!
app.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ HuggingFace Space - Embedding API
3
+ Lightweight stateless API for generating text embeddings
4
+ """
5
+
6
+ import os
7
+ from flask import Flask, request, jsonify
8
+ from sentence_transformers import SentenceTransformer
9
+ import logging
10
+
11
+ # Configure logging
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # Initialize Flask app
16
+ app = Flask(__name__)
17
+
18
+ # Load the embedding model (all-MiniLM-L6-v2, 384 dimensions)
19
+ # This model is small (~80MB) and fast on CPU
20
+ logger.info("Loading embedding model...")
21
+ model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
22
+ logger.info("Model loaded successfully!")
23
+
24
+
25
+ @app.route('/')
26
+ def health():
27
+ """Health check endpoint"""
28
+ return jsonify({
29
+ "status": "healthy",
30
+ "model": "all-MiniLM-L6-v2",
31
+ "dimensions": 384,
32
+ "endpoints": {
33
+ "/embed": "POST - Generate embeddings for text",
34
+ "/batch_embed": "POST - Generate embeddings for multiple texts"
35
+ }
36
+ })
37
+
38
+
39
+ @app.route('/embed', methods=['POST'])
40
+ def embed_text():
41
+ """
42
+ Generate embedding for a single text query
43
+
44
+ Request body:
45
+ {
46
+ "text": "Your text here"
47
+ }
48
+
49
+ Response:
50
+ {
51
+ "embedding": [0.123, -0.456, ...],
52
+ "dimensions": 384
53
+ }
54
+ """
55
+ try:
56
+ data = request.get_json()
57
+
58
+ if not data or 'text' not in data:
59
+ return jsonify({
60
+ "error": "Missing 'text' field in request body"
61
+ }), 400
62
+
63
+ text = data['text']
64
+
65
+ if not isinstance(text, str) or len(text.strip()) == 0:
66
+ return jsonify({
67
+ "error": "Text must be a non-empty string"
68
+ }), 400
69
+
70
+ # Generate embedding
71
+ embedding = model.encode(text, convert_to_numpy=True)
72
+
73
+ return jsonify({
74
+ "embedding": embedding.tolist(),
75
+ "dimensions": len(embedding)
76
+ })
77
+
78
+ except Exception as e:
79
+ logger.error(f"Error generating embedding: {str(e)}")
80
+ return jsonify({
81
+ "error": "Internal server error",
82
+ "message": str(e)
83
+ }), 500
84
+
85
+
86
+ @app.route('/batch_embed', methods=['POST'])
87
+ def batch_embed_texts():
88
+ """
89
+ Generate embeddings for multiple texts (batch processing)
90
+
91
+ Request body:
92
+ {
93
+ "texts": ["Text 1", "Text 2", ...]
94
+ }
95
+
96
+ Response:
97
+ {
98
+ "embeddings": [[0.123, ...], [0.456, ...], ...],
99
+ "count": 2,
100
+ "dimensions": 384
101
+ }
102
+ """
103
+ try:
104
+ data = request.get_json()
105
+
106
+ if not data or 'texts' not in data:
107
+ return jsonify({
108
+ "error": "Missing 'texts' field in request body"
109
+ }), 400
110
+
111
+ texts = data['texts']
112
+
113
+ if not isinstance(texts, list) or len(texts) == 0:
114
+ return jsonify({
115
+ "error": "Texts must be a non-empty list"
116
+ }), 400
117
+
118
+ # Limit batch size to prevent abuse
119
+ if len(texts) > 100:
120
+ return jsonify({
121
+ "error": "Batch size too large (max 100 texts)"
122
+ }), 400
123
+
124
+ # Generate embeddings
125
+ embeddings = model.encode(texts, convert_to_numpy=True)
126
+
127
+ return jsonify({
128
+ "embeddings": embeddings.tolist(),
129
+ "count": len(embeddings),
130
+ "dimensions": embeddings.shape[1] if len(embeddings) > 0 else 384
131
+ })
132
+
133
+ except Exception as e:
134
+ logger.error(f"Error generating batch embeddings: {str(e)}")
135
+ return jsonify({
136
+ "error": "Internal server error",
137
+ "message": str(e)
138
+ }), 500
139
+
140
+
141
+ if __name__ == '__main__':
142
+ # HuggingFace Spaces requires the app to listen on port 7860
143
+ port = int(os.environ.get('PORT', 7860))
144
+ logger.info(f"Starting server on port {port}...")
145
+ app.run(host='0.0.0.0', port=port, debug=False)
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ sentence-transformers==2.3.1
2
+ torch==2.1.2
3
+ flask==3.0.0