Pujan Neupane commited on
Commit
5fb4c10
·
0 Parent(s):

Pushed the fastapi code

Browse files
Files changed (5) hide show
  1. .gitattributes +2 -0
  2. .gitignore +2 -0
  3. README.md +132 -0
  4. app.py +97 -0
  5. requirements.txt +49 -0
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *.pth filter=lfs diff=lfs merge=lfs -text
2
+ Ai-Text-Detector/model_weights.pth filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ ./venv/
2
+ ./__pycache__/
README.md ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### **FastAPI AI**
2
+
3
+ This FastAPI app loads a GPT-2 model, tokenizes input text, classifies it, and returns whether the text is AI-generated or human-written.
4
+
5
+ ---
6
+
7
+ ### **install Dependencies**
8
+
9
+ ```bash
10
+ pip install -r requirements.txt
11
+
12
+ ```
13
+
14
+ This command installs all the dependencies listed in the `requirements.txt` file. It ensures that your environment has the required packages to run the project smoothly.
15
+
16
+ **NOTE: IF YOU HAVE DONE ANY CHANGES DON'NT FORGOT TO PUT IT IN THE REQUIREMENTS.TXT USING `bash pip freeze > requirements.txt `**
17
+
18
+ ---
19
+
20
+ ### **Functions**
21
+
22
+ 1. **`load_model()`**
23
+ Loads the GPT-2 model and tokenizer from specified paths.
24
+
25
+ 2. **`lifespan()`**
26
+ Manages the app's lifecycle: loads the model at startup and handles cleanup on shutdown.
27
+
28
+ 3. **`classify_text_sync()`**
29
+ Synchronously tokenizes input text and classifies it using the GPT-2 model. Returns the classification and perplexity.
30
+
31
+ 4. **`classify_text()`**
32
+ Asynchronously executes `classify_text_sync()` in a thread pool to ensure non-blocking processing.
33
+
34
+ 5. **`analyze_text()`**
35
+ **POST** endpoint: accepts text input, classifies it using `classify_text()`, and returns the result with perplexity.
36
+
37
+ 6. **`health_check()`**
38
+ **GET** endpoint: simple health check to confirm the API is running.
39
+
40
+ ---
41
+
42
+ ### **Code Overview**
43
+
44
+ ```python
45
+ executor = ThreadPoolExecutor(max_workers=2)
46
+ ```
47
+
48
+ - **`ThreadPoolExecutor(max_workers=2)`** limits the number of concurrent threads (tasks) per worker process to 2 for text classification. This helps control resource usage and prevent overloading the server.
49
+
50
+ ---
51
+
52
+ ### **Running and Load Balancing:**
53
+
54
+ To run the app in production with load balancing:
55
+
56
+ ```bash
57
+ uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
58
+ ```
59
+
60
+ This command launches the FastAPI app with **4 worker processes**, allowing it to handle multiple requests concurrently.
61
+
62
+ ### **Concurrency Explained:**
63
+
64
+ 1. **`ThreadPoolExecutor(max_workers=20)`**
65
+
66
+ - Controls the **number of threads** within a **single worker** process.
67
+ - Allows up to 20 tasks (text classification requests) to be handled simultaneously per worker, improving responsiveness for I/O-bound tasks.
68
+
69
+ 2. **`--workers 4` in Uvicorn**
70
+ - Spawns **4 independent worker processes** to handle incoming HTTP requests.
71
+ - Each worker can independently handle multiple tasks, increasing the app's ability to process concurrent requests in parallel.
72
+
73
+ ### **How They Relate:**
74
+
75
+ - **Uvicorn’s `--workers`** defines how many worker processes the server will run.
76
+ - **`ThreadPoolExecutor`** limits how many tasks (threads) each worker can process concurrently.
77
+
78
+ For example, with **4 workers** and **20 threads per worker**, the server can handle **80 tasks concurrently**. This provides scalable and efficient processing, balancing the load across multiple workers and threads.
79
+
80
+ ### **Endpoints**
81
+
82
+ #### 1. **`/analyze`**
83
+
84
+ - **Method:** `POST`
85
+ - **Description:** Classifies whether the text is AI-generated or human-written.
86
+ - **Request:**
87
+ ```json
88
+ { "text": "sample text" }
89
+ ```
90
+ - **Response:**
91
+ ```json
92
+ { "result": "AI-generated", "perplexity": 55.67 }
93
+ ```
94
+
95
+ #### 2. **`/health`**
96
+
97
+ - **Method:** `GET`
98
+ - **Description:** Returns the status of the API.
99
+ - **Response:**
100
+ ```json
101
+ { "status": "ok" }
102
+ ```
103
+
104
+ ---
105
+
106
+ ### **Running the API**
107
+
108
+ Start the server with:
109
+
110
+ ```bash
111
+ uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4
112
+ ```
113
+
114
+ ---
115
+
116
+ ### **Testing the API**
117
+
118
+ - Use `curl` for testing:
119
+ ```bash
120
+ curl -X 'POST' 'http://127.0.0.1:8000/analyze' -H 'accept: application/json' -H 'Content-Type: application/json' -d '{"text": "sample text"}'
121
+ ```
122
+
123
+ ---
124
+
125
+ ### **API Documentation**
126
+
127
+ - **Swagger UI:** `http://127.0.0.1:8000/docs` -> `/docs`
128
+ - **ReDoc:** `http://127.0.0.1:8000/redoc` -> `/redoc`
129
+
130
+ ### **Implement it with NEST.js**
131
+
132
+ will be updated
app.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import GPT2LMHeadModel, GPT2TokenizerFast
3
+ from fastapi import FastAPI, HTTPException
4
+ from pydantic import BaseModel
5
+ import asyncio
6
+ from concurrent.futures import ThreadPoolExecutor
7
+ from contextlib import asynccontextmanager
8
+
9
+ # FastAPI instance
10
+ app = FastAPI()
11
+ model, tokenizer = None, None
12
+ executor = ThreadPoolExecutor(max_workers=20)
13
+
14
+ # Function to load model and tokenizer
15
+
16
+
17
+ def load_model():
18
+ model_path = "./Ai-Text-Detector/model"
19
+ weights_path = "./Ai-Text-Detector/model_weights.pth"
20
+ tokenizer = GPT2TokenizerFast.from_pretrained(model_path)
21
+ model = GPT2LMHeadModel.from_pretrained("gpt2")
22
+ model.load_state_dict(torch.load(weights_path, map_location=torch.device("cpu")))
23
+ model.eval() # Set the model to evaluation mode
24
+ return model, tokenizer
25
+
26
+
27
+ @asynccontextmanager
28
+ async def lifespan(app: FastAPI):
29
+ global model, tokenizer
30
+ model, tokenizer = load_model()
31
+ yield
32
+
33
+
34
+ app = FastAPI(lifespan=lifespan)
35
+
36
+
37
+ class TextInput(BaseModel):
38
+ text: str
39
+
40
+
41
+ # Function to classify the text
42
+
43
+
44
+ def classify_text_sync(sentence: str):
45
+ inputs = tokenizer(sentence, return_tensors="pt", truncation=True, padding=True)
46
+ input_ids = inputs["input_ids"]
47
+ attention_mask = inputs["attention_mask"]
48
+
49
+ with torch.no_grad():
50
+ outputs = model(input_ids, attention_mask=attention_mask, labels=input_ids)
51
+ loss = outputs.loss
52
+ perplexity = torch.exp(loss).item()
53
+
54
+ if perplexity < 60:
55
+ result = "AI-generated*"
56
+ elif perplexity < 80:
57
+ result = "Probably AI-generated*"
58
+ else:
59
+ result = "Human-written*"
60
+
61
+ return result, perplexity
62
+
63
+
64
+ async def classify_text(sentence: str):
65
+ loop = asyncio.get_event_loop()
66
+ return await loop.run_in_executor(executor, classify_text_sync, sentence)
67
+
68
+
69
+ @app.post("/analyze")
70
+ async def analyze_text(data: TextInput):
71
+ user_input = data.text.strip()
72
+
73
+ if not user_input:
74
+ raise HTTPException(status_code=400, detail="Text cannot be empty")
75
+
76
+ result, perplexity = await classify_text(user_input)
77
+
78
+ return {
79
+ "result": result,
80
+ "perplexity": round(perplexity, 2),
81
+ }
82
+
83
+
84
+ @app.get("/health")
85
+ async def health_check():
86
+ return {"status": "ok"}
87
+
88
+
89
+ @app.get("/")
90
+ def index():
91
+ return {"Its an API"}
92
+
93
+
94
+ if __name__ == "__main__":
95
+ import uvicorn
96
+
97
+ uvicorn.run("app:app", host="0.0.0.0", port=8000, workers=4) # Specify 4 workers
requirements.txt ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ annotated-types==0.7.0
2
+ anyio==4.9.0
3
+ certifi==2025.1.31
4
+ charset-normalizer==3.4.1
5
+ click==8.1.8
6
+ fastapi==0.115.12
7
+ filelock==3.18.0
8
+ fsspec==2025.3.2
9
+ h11==0.14.0
10
+ huggingface-hub==0.30.2
11
+ idna==3.10
12
+ Jinja2==3.1.6
13
+ MarkupSafe==3.0.2
14
+ mpmath==1.3.0
15
+ networkx==3.4.2
16
+ numpy==2.2.5
17
+ nvidia-cublas-cu12==12.4.5.8
18
+ nvidia-cuda-cupti-cu12==12.4.127
19
+ nvidia-cuda-nvrtc-cu12==12.4.127
20
+ nvidia-cuda-runtime-cu12==12.4.127
21
+ nvidia-cudnn-cu12==9.1.0.70
22
+ nvidia-cufft-cu12==11.2.1.3
23
+ nvidia-curand-cu12==10.3.5.147
24
+ nvidia-cusolver-cu12==11.6.1.9
25
+ nvidia-cusparse-cu12==12.3.1.170
26
+ nvidia-cusparselt-cu12==0.6.2
27
+ nvidia-nccl-cu12==2.21.5
28
+ nvidia-nvjitlink-cu12==12.4.127
29
+ nvidia-nvtx-cu12==12.4.127
30
+ packaging==25.0
31
+ pydantic==2.11.3
32
+ pydantic_core==2.33.1
33
+ PyYAML==6.0.2
34
+ regex==2024.11.6
35
+ requests==2.32.3
36
+ safetensors==0.5.3
37
+ setuptools==79.0.0
38
+ sniffio==1.3.1
39
+ starlette==0.46.2
40
+ sympy==1.13.1
41
+ tokenizers==0.21.1
42
+ torch==2.6.0
43
+ tqdm==4.67.1
44
+ transformers==4.51.3
45
+ triton==3.2.0
46
+ typing-inspection==0.4.0
47
+ typing_extensions==4.13.2
48
+ urllib3==2.4.0
49
+ uvicorn==0.34.2