aegis-detect 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Anthony Qin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: aegis-detect
3
+ Version: 0.1.0
4
+ Summary: AI Code Detection Tool
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: accelerate>=0.20.0
9
+ Requires-Dist: datasets>=2.0.0
10
+ Requires-Dist: huggingface-hub>=0.15.0
11
+ Requires-Dist: matplotlib>=3.5.0
12
+ Requires-Dist: numpy>=1.21.0
13
+ Requires-Dist: openai>=1.0.0
14
+ Requires-Dist: pandas>=1.5.0
15
+ Requires-Dist: peft>=0.4.0
16
+ Requires-Dist: python-dotenv>=0.19.0
17
+ Requires-Dist: safetensors>=0.3.0
18
+ Requires-Dist: scikit-learn>=1.0.0
19
+ Requires-Dist: seaborn>=0.13.2
20
+ Requires-Dist: torch>=2.0.0
21
+ Requires-Dist: tqdm>=4.60.0
22
+ Requires-Dist: transformers>=4.30.0
23
+ Dynamic: license-file
24
+
25
+ # Aegis: AI Python Code Detection Model
26
+
27
+ ## Overview
28
+ Aegis is a fine-tuned CodeBERT model that classifies AI-generated and human code. CodeBERT has 125 million parameters, but using LoRA (Low-Rank Adaptation), Aegis was efficiently trained locally with only a subset of the original parameters being updated. This project sprouted out of my curiosity of classifying AI and human code off semantic differences. Hence, the dataset (20K samples: 10K AI + 10K Human) was aggressively cleaned to ensure standard formatting and the removal of comments and docstrings. The threshold was set to 0.7 to suggest that were is enough evidence to "pass" the code sample as AI-generated. Predictions by Aegis are not perfect: it's not an end all be all judge. Additionally, tasks where semantic convergence between humans and AI is observed (think LeetCode) are inherently hard to classify.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install aegis-detect
34
+ ```
35
+
36
+ ### CLI Usage
37
+
38
+ **Supported commands**:
39
+ ```bash
40
+ # Predicting using a file
41
+ aegis --file path/to/code.py
42
+
43
+ # Predicting using text
44
+ aegis --text "def add(a, b):\n return a + b"
45
+
46
+ # JSON output
47
+ aegis --file path/to/code.py --json > result.json
48
+
49
+ # Setting a threshold for AI classification
50
+ aegis --file path/to/code.py --threshold 0.7
51
+
52
+ # Help
53
+ aegis --help
54
+ ```
55
+
56
+ **Notes**:
57
+ - On first run, the model adapter is downloaded from the Hugging Face repo [anthonyq7/aegis](https://huggingface.co/anthonyq7/aegis) and cached under `~/.aegis/models`.
58
+ - Internet access is required on the first run; subsequent runs use local cache.
59
+ - The CLI prints the predicted label and probabilities for human and AI.
60
+
61
+ ## Key Results
62
+
63
+ ### Model Performance
64
+ - **Accuracy**: 85.10%
65
+ - **Precision**: 83.37%
66
+ - **Recall**: 87.70%
67
+ - **F1-Score**: 85.48%
68
+
69
+ ### Confusion Matrix
70
+ ![Alt text](model/results/confusion_matrix.png)
71
+
72
+ ### Attention Heatmap
73
+ ![Alt text](model/results/attention_weights.png)
74
+
75
+ ## Contact
76
+ **Email**: a.j.qin@wustl.edu
77
+
78
+ ## License
79
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,55 @@
1
+ # Aegis: AI Python Code Detection Model
2
+
3
+ ## Overview
4
+ Aegis is a fine-tuned CodeBERT model that classifies AI-generated and human code. CodeBERT has 125 million parameters, but using LoRA (Low-Rank Adaptation), Aegis was efficiently trained locally with only a subset of the original parameters being updated. This project sprouted out of my curiosity of classifying AI and human code off semantic differences. Hence, the dataset (20K samples: 10K AI + 10K Human) was aggressively cleaned to ensure standard formatting and the removal of comments and docstrings. The threshold was set to 0.7 to suggest that were is enough evidence to "pass" the code sample as AI-generated. Predictions by Aegis are not perfect: it's not an end all be all judge. Additionally, tasks where semantic convergence between humans and AI is observed (think LeetCode) are inherently hard to classify.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pip install aegis-detect
10
+ ```
11
+
12
+ ### CLI Usage
13
+
14
+ **Supported commands**:
15
+ ```bash
16
+ # Predicting using a file
17
+ aegis --file path/to/code.py
18
+
19
+ # Predicting using text
20
+ aegis --text "def add(a, b):\n return a + b"
21
+
22
+ # JSON output
23
+ aegis --file path/to/code.py --json > result.json
24
+
25
+ # Setting a threshold for AI classification
26
+ aegis --file path/to/code.py --threshold 0.7
27
+
28
+ # Help
29
+ aegis --help
30
+ ```
31
+
32
+ **Notes**:
33
+ - On first run, the model adapter is downloaded from the Hugging Face repo [anthonyq7/aegis](https://huggingface.co/anthonyq7/aegis) and cached under `~/.aegis/models`.
34
+ - Internet access is required on the first run; subsequent runs use local cache.
35
+ - The CLI prints the predicted label and probabilities for human and AI.
36
+
37
+ ## Key Results
38
+
39
+ ### Model Performance
40
+ - **Accuracy**: 85.10%
41
+ - **Precision**: 83.37%
42
+ - **Recall**: 87.70%
43
+ - **F1-Score**: 85.48%
44
+
45
+ ### Confusion Matrix
46
+ ![Alt text](model/results/confusion_matrix.png)
47
+
48
+ ### Attention Heatmap
49
+ ![Alt text](model/results/attention_weights.png)
50
+
51
+ ## Contact
52
+ **Email**: a.j.qin@wustl.edu
53
+
54
+ ## License
55
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,3 @@
1
+ __version__ = "0.1.0"
2
+
3
+ from aegis.predictor import Predictor
@@ -0,0 +1,76 @@
1
+ """CLI for Aegis"""
2
+
3
+ import argparse
4
+ import json
5
+ from argparse import RawDescriptionHelpFormatter
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ from transformers import logging as hf_logging
10
+
11
+ from aegis.predictor import Predictor
12
+
13
+ hf_logging.set_verbosity_error()
14
+
15
+ def get_code_input(text: Optional[str], file_path: Optional[str]) -> str:
16
+ if text:
17
+ return text
18
+
19
+ file = Path(file_path).expanduser()
20
+ if not file.exists():
21
+ raise SystemExit(f"Error: File not found: {file_path}")
22
+ return file.read_text(encoding="utf-8")
23
+
24
+
25
+ def main() -> None:
26
+ parser = argparse.ArgumentParser(
27
+ prog="aegis",
28
+ description="Detect whether code is human-written or AI-generated",
29
+ formatter_class=RawDescriptionHelpFormatter,
30
+ epilog=(
31
+ "Examples:\n"
32
+ " aegis --file path/to/code.py\n"
33
+ " aegis --text 'def add(a, b): return a + b'\n"
34
+ " aegis --file script.py --json > result.json\n"
35
+ " aegis --threshold 0.7\n"
36
+ )
37
+ )
38
+
39
+ #creates a mutually exclusive input group where only either --text or --file is allowed
40
+ input_group = parser.add_mutually_exclusive_group(required=True)
41
+ input_group.add_argument("--text", type=str, help="Code snippet as text (quote if it contains spaces or special characters).")
42
+ input_group.add_argument("--file", type=str, help="Path to code file")
43
+
44
+ #output format
45
+ parser.add_argument("--json", action="store_true", help="Output results as JSON")
46
+
47
+ #allows users to adjust a threshold
48
+ parser.add_argument("--threshold", type=float, help="Custom threshold to classify as AI")
49
+
50
+ #parses args
51
+ args = parser.parse_args()
52
+
53
+ #gets the code to analyze
54
+ code = get_code_input(args.text, args.file)
55
+
56
+ #loads model and makes a prediction
57
+ if args.threshold is None:
58
+ predictor = Predictor()
59
+ elif args.threshold:
60
+ predictor = Predictor(threshold=args.threshold)
61
+ elif args.threshold == 0:
62
+ predictor = Predictor(threshold=0)
63
+ else:
64
+ raise ValueError("Threshold must be a float between 0 and 1")
65
+
66
+
67
+ result = predictor.predict(code)
68
+
69
+ if args.json:
70
+ print(json.dumps(result, ensure_ascii=False))
71
+ else:
72
+ print(f"Prediction: {result['prediction']} | human={result['human']} | ai={result['ai']}")
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
@@ -0,0 +1,91 @@
1
+ """Core logic for the CLI interface"""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ import torch
7
+ from huggingface_hub import snapshot_download
8
+ from peft import PeftModel
9
+ from transformers import AutoModelForSequenceClassification, AutoTokenizer
10
+
11
+
12
+ class Predictor:
13
+
14
+ def __init__(self, threshold: Optional[float] = None, model_name="anthonyq7/aegis"):
15
+
16
+ if threshold:
17
+ if not (0.0 <= threshold <= 1.0):
18
+ raise ValueError("Threshold must be a float between 0 and 1")
19
+
20
+ if threshold is None:
21
+ self.threshold = 0.7
22
+ else:
23
+ self.threshold = float(threshold)
24
+
25
+ self.model_path = self._get_or_download_model(model_name)
26
+ self._load_model()
27
+
28
+ def _get_or_download_model(self, model_name):
29
+ cached_dir = Path.home() / ".aegis" / "models"
30
+ cached_dir.mkdir(parents=True, exist_ok=True)
31
+
32
+ if not any(cached_dir.iterdir()):
33
+ try:
34
+ snapshot_download(
35
+ repo_id=model_name,
36
+ local_dir=cached_dir
37
+ )
38
+ print("\u2713 Model downloaded successfully!")
39
+ except Exception as e:
40
+ print(f"\u2717 Error downloading mode: {e}")
41
+ print(f"Make sure the model exists at huggingface.co/{model_name}")
42
+ raise
43
+
44
+ return cached_dir
45
+
46
+ def _load_model(self):
47
+
48
+ print("Loading model...")
49
+
50
+ self.tokenizer = AutoTokenizer.from_pretrained(str(self.model_path))
51
+ base_model = AutoModelForSequenceClassification.from_pretrained("microsoft/codebert-base", num_labels=2)
52
+ self.model = PeftModel.from_pretrained(base_model, str(self.model_path))
53
+ self.model.eval()
54
+
55
+ print("\u2713 Model loaded!")
56
+
57
+ def predict(self, code):
58
+
59
+ if not code or not code.strip():
60
+ raise ValueError("Code cannot be empty")
61
+
62
+ inputs = self.tokenizer(
63
+ code,
64
+ truncation=True,
65
+ padding="max_length",
66
+ max_length=512,
67
+ return_tensors="pt"
68
+ )
69
+
70
+ with torch.no_grad():
71
+ outputs = self.model(**inputs)
72
+ probs = torch.nn.functional.softmax(outputs.logits, dim=-1).cpu().numpy().tolist()[0]
73
+
74
+ human_prob = f"{float(probs[0]):.4f}"
75
+ ai_prob = f"{float(probs[1]):.4f}"
76
+
77
+ return {
78
+ "human": human_prob,
79
+ "ai": ai_prob,
80
+ "prediction": "ai-generated" if float(ai_prob) >= self.threshold else "human"
81
+ }
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: aegis-detect
3
+ Version: 0.1.0
4
+ Summary: AI Code Detection Tool
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: accelerate>=0.20.0
9
+ Requires-Dist: datasets>=2.0.0
10
+ Requires-Dist: huggingface-hub>=0.15.0
11
+ Requires-Dist: matplotlib>=3.5.0
12
+ Requires-Dist: numpy>=1.21.0
13
+ Requires-Dist: openai>=1.0.0
14
+ Requires-Dist: pandas>=1.5.0
15
+ Requires-Dist: peft>=0.4.0
16
+ Requires-Dist: python-dotenv>=0.19.0
17
+ Requires-Dist: safetensors>=0.3.0
18
+ Requires-Dist: scikit-learn>=1.0.0
19
+ Requires-Dist: seaborn>=0.13.2
20
+ Requires-Dist: torch>=2.0.0
21
+ Requires-Dist: tqdm>=4.60.0
22
+ Requires-Dist: transformers>=4.30.0
23
+ Dynamic: license-file
24
+
25
+ # Aegis: AI Python Code Detection Model
26
+
27
+ ## Overview
28
+ Aegis is a fine-tuned CodeBERT model that classifies AI-generated and human code. CodeBERT has 125 million parameters, but using LoRA (Low-Rank Adaptation), Aegis was efficiently trained locally with only a subset of the original parameters being updated. This project sprouted out of my curiosity of classifying AI and human code off semantic differences. Hence, the dataset (20K samples: 10K AI + 10K Human) was aggressively cleaned to ensure standard formatting and the removal of comments and docstrings. The threshold was set to 0.7 to suggest that were is enough evidence to "pass" the code sample as AI-generated. Predictions by Aegis are not perfect: it's not an end all be all judge. Additionally, tasks where semantic convergence between humans and AI is observed (think LeetCode) are inherently hard to classify.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install aegis-detect
34
+ ```
35
+
36
+ ### CLI Usage
37
+
38
+ **Supported commands**:
39
+ ```bash
40
+ # Predicting using a file
41
+ aegis --file path/to/code.py
42
+
43
+ # Predicting using text
44
+ aegis --text "def add(a, b):\n return a + b"
45
+
46
+ # JSON output
47
+ aegis --file path/to/code.py --json > result.json
48
+
49
+ # Setting a threshold for AI classification
50
+ aegis --file path/to/code.py --threshold 0.7
51
+
52
+ # Help
53
+ aegis --help
54
+ ```
55
+
56
+ **Notes**:
57
+ - On first run, the model adapter is downloaded from the Hugging Face repo [anthonyq7/aegis](https://huggingface.co/anthonyq7/aegis) and cached under `~/.aegis/models`.
58
+ - Internet access is required on the first run; subsequent runs use local cache.
59
+ - The CLI prints the predicted label and probabilities for human and AI.
60
+
61
+ ## Key Results
62
+
63
+ ### Model Performance
64
+ - **Accuracy**: 85.10%
65
+ - **Precision**: 83.37%
66
+ - **Recall**: 87.70%
67
+ - **F1-Score**: 85.48%
68
+
69
+ ### Confusion Matrix
70
+ ![Alt text](model/results/confusion_matrix.png)
71
+
72
+ ### Attention Heatmap
73
+ ![Alt text](model/results/attention_weights.png)
74
+
75
+ ## Contact
76
+ **Email**: a.j.qin@wustl.edu
77
+
78
+ ## License
79
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ aegis/__init__.py
5
+ aegis/cli.py
6
+ aegis/predictor.py
7
+ aegis_detect.egg-info/PKG-INFO
8
+ aegis_detect.egg-info/SOURCES.txt
9
+ aegis_detect.egg-info/dependency_links.txt
10
+ aegis_detect.egg-info/entry_points.txt
11
+ aegis_detect.egg-info/requires.txt
12
+ aegis_detect.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ aegis = aegis.cli:main
@@ -0,0 +1,15 @@
1
+ accelerate>=0.20.0
2
+ datasets>=2.0.0
3
+ huggingface-hub>=0.15.0
4
+ matplotlib>=3.5.0
5
+ numpy>=1.21.0
6
+ openai>=1.0.0
7
+ pandas>=1.5.0
8
+ peft>=0.4.0
9
+ python-dotenv>=0.19.0
10
+ safetensors>=0.3.0
11
+ scikit-learn>=1.0.0
12
+ seaborn>=0.13.2
13
+ torch>=2.0.0
14
+ tqdm>=4.60.0
15
+ transformers>=4.30.0
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "aegis-detect"
3
+ version = "0.1.0"
4
+ description = "AI Code Detection Tool"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "accelerate>=0.20.0",
9
+ "datasets>=2.0.0",
10
+ "huggingface-hub>=0.15.0",
11
+ "matplotlib>=3.5.0",
12
+ "numpy>=1.21.0",
13
+ "openai>=1.0.0",
14
+ "pandas>=1.5.0",
15
+ "peft>=0.4.0",
16
+ "python-dotenv>=0.19.0",
17
+ "safetensors>=0.3.0",
18
+ "scikit-learn>=1.0.0",
19
+ "seaborn>=0.13.2",
20
+ "torch>=2.0.0",
21
+ "tqdm>=4.60.0",
22
+ "transformers>=4.30.0",
23
+ ]
24
+
25
+ [project.scripts]
26
+ aegis = "aegis.cli:main"
27
+
28
+ [build-system]
29
+ requires = ["setuptools>=68", "wheel"]
30
+ build-backend = "setuptools.build_meta"
31
+
32
+ [tool.setuptools]
33
+ packages = ["aegis"]
34
+
35
+ [tool.uv]
36
+ dev-dependencies = [
37
+ "ruff>=0.6.0"
38
+ ]
39
+
40
+ [tool.ruff]
41
+ exclude = [
42
+ ".git",
43
+ "__pycache__",
44
+ ".venv",
45
+ "venv",
46
+ "node_modules",
47
+ "build",
48
+ "dist",
49
+ "*.egg-info",
50
+ "saved_models",
51
+ "data/raw",
52
+ ]
53
+
54
+ [tool.ruff.lint]
55
+ select = ["E", "W", "F", "I", "N"]
56
+ ignore = ["E501"]
57
+
58
+ [tool.ruff.lint.per-file-ignores]
59
+ "__init__.py" = ["F401"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+