gcharanteja commited on
Commit
1b7ffc7
·
1 Parent(s): c181d67
Files changed (4) hide show
  1. Dockerfile +13 -17
  2. app.py +37 -93
  3. requirements.txt +3 -3
  4. start.sh +19 -8
Dockerfile CHANGED
@@ -1,24 +1,20 @@
1
- FROM python:3.9
2
-
3
- # Create user
4
- RUN useradd -m -u 1000 user
5
-
6
- # Create /data/chroma as root and give ownership to user BEFORE switching
7
- RUN mkdir -p /data/chroma && chown -R user:user /data
8
-
9
- USER user
10
- ENV PATH="/home/user/.local/bin:$PATH"
11
 
12
  WORKDIR /app
13
 
14
- COPY --chown=user ./requirements.txt requirements.txt
15
- RUN pip install --no-cache-dir --upgrade -r requirements.txt
 
 
 
16
 
17
- COPY --chown=user . /app
18
 
19
- # Make start.sh executable
20
- RUN chmod +x /app/start.sh
21
 
22
- VOLUME ["/data/chroma"]
 
23
 
24
- CMD ["/app/start.sh"]
 
 
 
1
+ FROM chromadb/chroma:latest
 
 
 
 
 
 
 
 
 
2
 
3
  WORKDIR /app
4
 
5
+ # Install FastAPI and Uvicorn for the health check app
6
+ RUN pip install fastapi uvicorn requests
7
+
8
+ COPY requirements.txt .
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
 
11
+ COPY . .
12
 
13
+ RUN chmod +x start.sh
 
14
 
15
+ EXPOSE 8000
16
+ EXPOSE 7860
17
 
18
+ # Start both ChromaDB and the Health Check App
19
+ # We use a simple script to run both in background
20
+ CMD ["sh", "-c", "./start.sh & python3 app.py"]
app.py CHANGED
@@ -1,94 +1,38 @@
1
- import json
2
  import os
3
- import chromadb
4
- from fastapi import FastAPI, HTTPException
5
- from pydantic import BaseModel
6
- from typing import Optional
7
-
8
- app = FastAPI()
9
-
10
- # ── Config ────────────────────────────────────────────────────────────────────
11
- CONFIG_PATH = "/data/config.json"
12
- DEFAULT_CONFIG = {"text": "Hello, World!"}
13
-
14
- CHROMA_PATH = "/data/chroma" # same /data persistent volume, survives restarts
15
-
16
- # ── ChromaDB client ───────────────────────────────────────────────────────────
17
- os.makedirs(CHROMA_PATH, exist_ok=True)
18
- chroma_client = chromadb.PersistentClient(path=CHROMA_PATH)
19
- collection = chroma_client.get_or_create_collection(name="my_collection")
20
-
21
- # ── Pydantic models ───────────────────────────────────────────────────────────
22
- class Document(BaseModel):
23
- id: str
24
- text: str
25
- metadata: Optional[dict] = {}
26
-
27
- class QueryRequest(BaseModel):
28
- query_text: str
29
- n_results: int = 3
30
-
31
- # ── Config helpers ────────────────────────────────────────────────────────────
32
- def create_default_config():
33
- os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
34
- with open(CONFIG_PATH, "w") as f:
35
- json.dump(DEFAULT_CONFIG, f, indent=2)
36
-
37
- def load_config():
38
- if not os.path.exists(CONFIG_PATH):
39
- create_default_config()
40
- with open(CONFIG_PATH, "r") as f:
41
- return json.load(f)
42
-
43
- # ── Routes ────────────────────────────────────────────────────────────────────
44
- @app.get("/")
45
- def greet_json():
46
- config = load_config()
47
- return {"message": config.get("text")}
48
-
49
- @app.post("/documents/add")
50
- def add_document(doc: Document):
51
- existing_ids = collection.get(ids=[doc.id])["ids"]
52
- if existing_ids:
53
- raise HTTPException(status_code=409, detail=f"Document '{doc.id}' already exists.")
54
- collection.add(
55
- documents=[doc.text],
56
- ids=[doc.id],
57
- metadatas=[doc.metadata],
58
- )
59
- return {"message": f"Document '{doc.id}' added successfully."}
60
-
61
- @app.post("/documents/query")
62
- def query_documents(req: QueryRequest):
63
- count = collection.count()
64
- if count == 0:
65
- return {"results": [], "message": "Collection is empty."}
66
- n = min(req.n_results, count)
67
- results = collection.query(query_texts=[req.query_text], n_results=n)
68
- hits = [
69
- {
70
- "id": results["ids"][0][i],
71
- "text": results["documents"][0][i],
72
- "metadata": results["metadatas"][0][i],
73
- "distance": results["distances"][0][i],
74
- }
75
- for i in range(len(results["ids"][0]))
76
- ]
77
- return {"results": hits}
78
-
79
- @app.get("/documents/list")
80
- def list_documents():
81
- data = collection.get()
82
- docs = [
83
- {"id": data["ids"][i], "text": data["documents"][i], "metadata": data["metadatas"][i]}
84
- for i in range(len(data["ids"]))
85
- ]
86
- return {"count": len(docs), "documents": docs}
87
-
88
- @app.delete("/documents/{doc_id}")
89
- def delete_document(doc_id: str):
90
- existing = collection.get(ids=[doc_id])["ids"]
91
- if not existing:
92
- raise HTTPException(status_code=404, detail=f"Document '{doc_id}' not found.")
93
- collection.delete(ids=[doc_id])
94
- return {"message": f"Document '{doc_id}' deleted."}
 
 
1
  import os
2
+ import requests
3
+ from fastapi import FastAPI
4
+ from fastapi.responses import HTMLResponse
5
+
6
+ app = FastAPI(title="ChromaDB on HF Space")
7
+
8
+ CHROMA_URL = "http://localhost:8000"
9
+
10
+ @app.get("/", response_class=HTMLResponse)
11
+ def read_root():
12
+ return """
13
+ <html>
14
+ <head><title>ChromaDB HF Space</title></head>
15
+ <body>
16
+ <h1>✅ ChromaDB is Running!</h1>
17
+ <p>Persistent Storage: /data/chroma_db</p>
18
+ <p>API Endpoint: <a href="/docs">Swagger UI</a> (if enabled)</p>
19
+ <p>Direct ChromaDB API: <code>{}/api/v1/heartbeat</code></p>
20
+ </body>
21
+ </html>
22
+ """.format(CHROMA_URL)
23
+
24
+ @app.get("/health")
25
+ def health_check():
26
+ try:
27
+ response = requests.get(f"{CHROMA_URL}/api/v1/heartbeat")
28
+ if response.status_code == 200:
29
+ return {"status": "healthy", "chromadb": "up"}
30
+ else:
31
+ return {"status": "unhealthy", "chromadb": "error"}
32
+ except Exception as e:
33
+ return {"status": "error", "detail": str(e)}
34
+
35
+ if __name__ == "__main__":
36
+ import uvicorn
37
+ # Run on port 7860 for HF Spaces UI
38
+ uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", 7860)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,4 +1,4 @@
 
1
  fastapi
2
- uvicorn[standard]
3
- chromadb
4
- pydantic
 
1
+ chromadb==0.5.0
2
  fastapi
3
+ uvicorn
4
+ requests
 
start.sh CHANGED
@@ -1,13 +1,24 @@
1
  #!/bin/bash
2
- set -e
3
 
4
- echo "===== Application Startup at $(date '+%Y-%m-%d %H:%M:%S') ====="
 
5
 
6
- mkdir -p /data/chroma
7
- mkdir -p /data
8
 
9
- echo "[startup] Directories ready."
10
- echo "[startup] HF_DATASET_REPO=$HF_DATASET_REPO"
11
- echo "[startup] Starting uvicorn..."
12
 
13
- exec uvicorn app:app --host 0.0.0.0 --port 7860 --log-level info
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  #!/bin/bash
 
2
 
3
+ echo "=== [HF Space] Starting ChromaDB with Persistent Storage ==="
4
+ echo "Time: $(date)"
5
 
6
+ # Define the persistent directory inside the mounted bucket
7
+ PERSIST_DIR="/data/chroma_db"
8
 
9
+ # Create the directory if it doesn't exist
10
+ mkdir -p $PERSIST_DIR
 
11
 
12
+ echo "📂 Persistence directory: $PERSIST_DIR"
13
+ echo "✅ Checking permissions..."
14
+ ls -ld $PERSIST_DIR
15
+
16
+ # Set environment variables for ChromaDB
17
+ export CHROMA_SERVER_HOST=0.0.0.0
18
+ export CHROMA_SERVER_HTTP_PORT=8000
19
+ export CHROMA_DB_IMPL=chromadb.db.impl.sqlite
20
+ export PERSIST_DIRECTORY=$PERSIST_DIR
21
+
22
+ # If you are using the newer ChromaDB version that uses the 'chroma run' command:
23
+ echo "🚀 Starting ChromaDB Server..."
24
+ exec chroma run --path $PERSIST_DIR --host 0.0.0.0 --port 8000