cloudunity commited on
Commit
e58161b
·
verified ·
1 Parent(s): 88bbebc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -38
app.py CHANGED
@@ -1,10 +1,11 @@
 
1
  from fastapi import FastAPI, HTTPException
2
  from fastapi.responses import JSONResponse
3
  import numpy as np
4
  from sentence_transformers import SentenceTransformer
5
  import asyncio
6
  import logging
7
- from typing import List
8
  from pydantic import BaseModel
9
  import time
10
 
@@ -14,17 +15,21 @@ logger = logging.getLogger(__name__)
14
 
15
  app = FastAPI(title="Embedding API")
16
 
 
 
17
  # Load model on startup
18
  try:
19
- model = SentenceTransformer("all-MiniLM-L12-v2")
20
  logger.info("Model loaded successfully")
21
  except Exception as e:
22
  logger.error(f"Failed to load model: {e}")
23
  model = None
24
 
25
  # Request batching queue
26
- class EmbedRequest(BaseModel):
27
- texts: List[str]
 
 
28
 
29
  class BatchItem:
30
  def __init__(self, texts: List[str]):
@@ -104,38 +109,47 @@ async def startup():
104
  logger.info("Batch processor started")
105
 
106
 
107
- @app.get("/health")
108
- async def health():
109
- """Health check endpoint"""
110
  if model is None:
111
- return JSONResponse({"status": "unhealthy", "reason": "model not loaded"}, status_code=503)
112
- return {"status": "healthy"}
113
-
114
-
115
- @app.post("/embed")
116
- async def embed(request: EmbedRequest):
117
- """
118
- Embed texts. Automatically batches with concurrent requests.
119
 
120
- Example:
121
- ```
122
- POST /embed
123
- {
124
- "texts": ["hello world", "foo bar"]
 
 
 
 
 
 
 
 
125
  }
126
- ```
127
- """
 
 
128
  if model is None:
129
  raise HTTPException(status_code=503, detail="Model not ready")
130
 
131
- if not request.texts:
132
- raise HTTPException(status_code=400, detail="texts cannot be empty")
 
 
 
 
 
 
133
 
134
- if len(request.texts) > 512:
135
  raise HTTPException(status_code=400, detail="Cannot embed more than 512 texts at once")
136
 
137
- # Create request item
138
- batch_item = BatchItem(request.texts)
139
 
140
  async with batch_lock:
141
  pending_batch.append(batch_item)
@@ -144,29 +158,52 @@ async def embed(request: EmbedRequest):
144
  if len(pending_batch) >= MAX_BATCH_SIZE:
145
  batch_event.set()
146
 
147
- # Signal processor that there's work (in case it's idle)
148
  batch_event.set()
149
 
150
  # Wait for result with timeout
151
  try:
152
- embeddings = await asyncio.wait_for(batch_item.future, timeout=30.0)
153
- return {
154
- "embeddings": embeddings,
155
- "model": "all-MiniLM-L12-v2",
156
- "count": len(request.texts)
157
- }
158
  except asyncio.TimeoutError:
159
  raise HTTPException(status_code=504, detail="Embedding request timed out")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
 
162
  @app.get("/")
163
  async def root():
164
  """API info"""
165
  return {
166
- "name": "Embedding API",
167
- "model": "all-MiniLM-L12-v2",
168
  "endpoints": {
169
- "POST /embed": "Embed texts",
 
170
  "GET /health": "Health check"
171
  }
172
  }
@@ -174,4 +211,4 @@ async def root():
174
 
175
  if __name__ == "__main__":
176
  import uvicorn
177
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+
2
  from fastapi import FastAPI, HTTPException
3
  from fastapi.responses import JSONResponse
4
  import numpy as np
5
  from sentence_transformers import SentenceTransformer
6
  import asyncio
7
  import logging
8
+ from typing import List, Optional, Union
9
  from pydantic import BaseModel
10
  import time
11
 
 
15
 
16
  app = FastAPI(title="Embedding API")
17
 
18
+ MODEL_NAME = "all-MiniLM-L12-v2"
19
+
20
  # Load model on startup
21
  try:
22
+ model = SentenceTransformer(MODEL_NAME)
23
  logger.info("Model loaded successfully")
24
  except Exception as e:
25
  logger.error(f"Failed to load model: {e}")
26
  model = None
27
 
28
  # Request batching queue
29
+ class EmbeddingRequest(BaseModel):
30
+ input: Union[str, List[str]]
31
+ model: str = MODEL_NAME
32
+ encoding_format: Optional[str] = None
33
 
34
  class BatchItem:
35
  def __init__(self, texts: List[str]):
 
109
  logger.info("Batch processor started")
110
 
111
 
112
+ @app.get("/v1/models")
113
+ async def list_models():
114
+ """OpenAI-compatible models endpoint"""
115
  if model is None:
116
+ raise HTTPException(status_code=503, detail="Model not ready")
 
 
 
 
 
 
 
117
 
118
+ return {
119
+ "object": "list",
120
+ "data": [
121
+ {
122
+ "id": MODEL_NAME,
123
+ "object": "model",
124
+ "created": 0,
125
+ "owned_by": "huggingface",
126
+ "permission": [],
127
+ "root": MODEL_NAME,
128
+ "parent": None
129
+ }
130
+ ]
131
  }
132
+
133
+
134
+ @app.post("/v1/embeddings")
135
+ async def create_embeddings(request: EmbeddingRequest):
136
  if model is None:
137
  raise HTTPException(status_code=503, detail="Model not ready")
138
 
139
+ # Normalize input to list
140
+ if isinstance(request.input, str):
141
+ texts = [request.input]
142
+ else:
143
+ texts = request.input
144
+
145
+ if not texts:
146
+ raise HTTPException(status_code=400, detail="input cannot be empty")
147
 
148
+ if len(texts) > 512:
149
  raise HTTPException(status_code=400, detail="Cannot embed more than 512 texts at once")
150
 
151
+ # Create batch item
152
+ batch_item = BatchItem(texts)
153
 
154
  async with batch_lock:
155
  pending_batch.append(batch_item)
 
158
  if len(pending_batch) >= MAX_BATCH_SIZE:
159
  batch_event.set()
160
 
161
+ # Signal processor that there's work
162
  batch_event.set()
163
 
164
  # Wait for result with timeout
165
  try:
166
+ embeddings_list = await asyncio.wait_for(batch_item.future, timeout=30.0)
 
 
 
 
 
167
  except asyncio.TimeoutError:
168
  raise HTTPException(status_code=504, detail="Embedding request timed out")
169
+
170
+ # Format response as OpenAI-compatible
171
+ data = []
172
+ for idx, embedding in enumerate(embeddings_list):
173
+ data.append({
174
+ "object": "embedding",
175
+ "embedding": embedding,
176
+ "index": idx
177
+ })
178
+
179
+ return {
180
+ "object": "list",
181
+ "data": data,
182
+ "model": MODEL_NAME,
183
+ "usage": {
184
+ "prompt_tokens": sum(len(text.split()) for text in texts),
185
+ "total_tokens": sum(len(text.split()) for text in texts)
186
+ }
187
+ }
188
+
189
+
190
+ @app.get("/health")
191
+ async def health():
192
+ """Health check endpoint"""
193
+ if model is None:
194
+ return JSONResponse({"status": "unhealthy", "reason": "model not loaded"}, status_code=503)
195
+ return {"status": "healthy"}
196
 
197
 
198
  @app.get("/")
199
  async def root():
200
  """API info"""
201
  return {
202
+ "name": "OpenAI-Compatible Embedding API",
203
+ "model": MODEL_NAME,
204
  "endpoints": {
205
+ "GET /v1/models": "List available models",
206
+ "POST /v1/embeddings": "Create embeddings (OpenAI-compatible)",
207
  "GET /health": "Health check"
208
  }
209
  }
 
211
 
212
  if __name__ == "__main__":
213
  import uvicorn
214
+ uvicorn.run(app, host="0.0.0.0", port=7860)