fastapi-struct 0.2.1__tar.gz → 0.3.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.
- fastapi_struct-0.3.0/PKG-INFO +240 -0
- fastapi_struct-0.3.0/README.md +226 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/pyproject.toml +9 -7
- fastapi_struct-0.3.0/src/create_fastapi_app/__init__.py +1 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/cli.py +4 -4
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/generator.py +34 -3
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/.env +4 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/.env.example +5 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/.gitignore +32 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/README.md +226 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/ai/llm.py +28 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/ai/prompt.py +2 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/ai/rag.py +19 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/api/v1/api.py +8 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/api/v1/routes/chat.py +21 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/api/v1/routes/health.py +3 -2
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/api/v1/routes/predict.py +2 -1
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/api/v1/routes/rag.py +11 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/core/config.py +10 -4
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/main.py +42 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/ml/model.py +5 -6
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/ml/pipeline.py +14 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/schemas/chat.py +20 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/schemas/predict.py +4 -3
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/schemas/rag.py +20 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/services/ai_service.py +42 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/services/inference.py +0 -5
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/pyproject.toml +2 -2
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/tests/__init__.py +0 -0
- fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/tests/test_chat.py +18 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/tests/test_predict.py +1 -1
- fastapi_struct-0.3.0/src/fastapi_struct.egg-info/PKG-INFO +240 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/fastapi_struct.egg-info/SOURCES.txt +10 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/tests/test_generator.py +16 -11
- fastapi_struct-0.2.1/PKG-INFO +0 -231
- fastapi_struct-0.2.1/README.md +0 -218
- fastapi_struct-0.2.1/src/create_fastapi_app/__init__.py +0 -1
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/.env +0 -4
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/.env.example +0 -4
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/.gitignore +0 -9
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/README.md +0 -31
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/api/v1/api.py +0 -6
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/main.py +0 -37
- fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/ml/pipeline.py +0 -17
- fastapi_struct-0.2.1/src/fastapi_struct.egg-info/PKG-INFO +0 -231
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/MANIFEST.in +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/setup.cfg +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/.env +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/.env.example +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/.gitignore +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/README.md +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/api/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/api/v1/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/api/v1/api.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/api/v1/routes/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/api/v1/routes/items.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/api/v1/routes/users.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/core/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/core/config.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/core/database.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/core/security.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/main.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/models/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/models/item.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/models/user.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/repositories/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/repositories/item_repository.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/repositories/user_repository.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/schemas/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/schemas/item.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/schemas/user.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/services/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/services/item_service.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/services/user_service.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/utils/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/app/utils/email.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/pyproject.toml +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/requirements.txt +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/tests/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/tests/conftest.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/tests/test_items.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/basic/tests/test_users.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/app/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/api → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/ai}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/api/v1 → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/api}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/api/v1/routes → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/api/v1}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/core → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/api/v1/routes}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/ml → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/core}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/schemas → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/ml}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/app/services → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/schemas}/__init__.py +0 -0
- {fastapi_struct-0.2.1/src/create_fastapi_app/templates/ml/tests → fastapi_struct-0.3.0/src/create_fastapi_app/templates/ml/app/services}/__init__.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/requirements.txt +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/create_fastapi_app/templates/ml/tests/conftest.py +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/fastapi_struct.egg-info/dependency_links.txt +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/fastapi_struct.egg-info/entry_points.txt +0 -0
- {fastapi_struct-0.2.1 → fastapi_struct-0.3.0}/src/fastapi_struct.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-struct
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: CLI tool to generate standard, production-ready FastAPI architectures for AI, GenAI & ML services.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: fastapi,ai,machine-learning,genai,llm,rag,inference,project-structure,boilerplate,scaffold,cli
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Framework :: FastAPI
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
10
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="https://pypi.org/project/fastapi-struct/"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" width="320" alt="fastapi-struct"></a>
|
|
17
|
+
</p>
|
|
18
|
+
<p align="center">
|
|
19
|
+
<em>Production-ready FastAPI architecture generator tailor-made for <strong>AI, GenAI, LLM, and Machine Learning</strong> services.</em>
|
|
20
|
+
</p>
|
|
21
|
+
<p align="center">
|
|
22
|
+
<a href="https://pypi.org/project/fastapi-struct" target="_blank">
|
|
23
|
+
<img src="https://img.shields.io/pypi/v/fastapi-struct.svg?color=%2334D058" alt="Package version">
|
|
24
|
+
</a>
|
|
25
|
+
<a href="https://pypi.org/project/fastapi-struct" target="_blank">
|
|
26
|
+
<img src="https://img.shields.io/pypi/pyversions/fastapi-struct.svg?color=%2334D058" alt="Supported Python versions">
|
|
27
|
+
</a>
|
|
28
|
+
<a href="https://github.com/fastapi/fastapi" target="_blank">
|
|
29
|
+
<img src="https://img.shields.io/badge/FastAPI-0.115+-009688.svg?logo=fastapi&logoColor=white" alt="FastAPI">
|
|
30
|
+
</a>
|
|
31
|
+
<a href="https://github.com/pydantic/pydantic" target="_blank">
|
|
32
|
+
<img src="https://img.shields.io/badge/Pydantic-v2-E92063.svg?logo=pydantic&logoColor=white" alt="Pydantic v2">
|
|
33
|
+
</a>
|
|
34
|
+
<a href="https://opensource.org/licenses/MIT" target="_blank">
|
|
35
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT">
|
|
36
|
+
</a>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
**FastAPI Documentation:** [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com)
|
|
42
|
+
**PyPI Package:** [https://pypi.org/project/fastapi-struct/](https://pypi.org/project/fastapi-struct/)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## ⚡ Why `fastapi-struct` for AI & ML Engineers?
|
|
47
|
+
|
|
48
|
+
Traditional web generator templates force relational database CRUD patterns (`repositories/`, `models/`, `users/`, `email.py`), cluttering modern AI microservices.
|
|
49
|
+
|
|
50
|
+
**`fastapi-struct`** defaults to a lean, enterprise-grade **AI & Machine Learning architecture**:
|
|
51
|
+
|
|
52
|
+
- 🤖 **GenAI Token Streaming:** Real-time token-by-token streaming via `StreamingResponse` (Server-Sent Events) for LLM chat completions.
|
|
53
|
+
- 📚 **RAG & Vector Search:** Modular vector retrieval layer ready for Pinecone, Qdrant, ChromaDB, or pgvector.
|
|
54
|
+
- 🧠 **Lifespan Model Caching:** Heavy model weights (PyTorch, ONNX, HuggingFace) load into GPU/CPU memory **once at startup** via FastAPI's `@asynccontextmanager lifespan`.
|
|
55
|
+
- 🩺 **GPU & Readiness Probes:** Built-in `/health` endpoint reporting GPU/device readiness (`cpu` or `cuda`).
|
|
56
|
+
- 🧹 **Zero `__pycache__` Clutter:** Automatically excludes byte-compiled files and stray caches during project scaffolding.
|
|
57
|
+
- 🧪 **Preconfigured Pytest Suite:** 4 automated integration tests for health, prediction, chat, and RAG.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 📦 Quick Start & CLI Usage
|
|
62
|
+
|
|
63
|
+
### Generate in the Current Directory (`.`)
|
|
64
|
+
```bash
|
|
65
|
+
fastapi-struct .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Or Create a Named Project Folder
|
|
69
|
+
```bash
|
|
70
|
+
fastapi-struct my_ai_service
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
*(You can also run it instantly with `uvx` without installing: `uvx fastapi-struct .`)*
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 📂 The AI & ML Architecture
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
my_ai_service/
|
|
81
|
+
│
|
|
82
|
+
├── app/
|
|
83
|
+
│ ├── __init__.py
|
|
84
|
+
│ ├── main.py # FastAPI initialization + Lifespan AI/ML model loader
|
|
85
|
+
│ │
|
|
86
|
+
│ ├── api/
|
|
87
|
+
│ │ ├── __init__.py
|
|
88
|
+
│ │ └── v1/
|
|
89
|
+
│ │ ├── __init__.py
|
|
90
|
+
│ │ ├── api.py # Central router aggregating AI, RAG, and ML routes
|
|
91
|
+
│ │ └── routes/
|
|
92
|
+
│ │ ├── __init__.py
|
|
93
|
+
│ │ ├── health.py # /health probe (GPU / device readiness check)
|
|
94
|
+
│ │ ├── chat.py # /chat & /stream (GenAI real-time token streaming)
|
|
95
|
+
│ │ ├── rag.py # /rag/query (Retrieval-Augmented Generation)
|
|
96
|
+
│ │ └── predict.py # /predict (Classical ML / Deep Learning inference)
|
|
97
|
+
│ │
|
|
98
|
+
│ ├── core/
|
|
99
|
+
│ │ ├── __init__.py
|
|
100
|
+
│ │ └── config.py # LLM_MODEL, EMBEDDING_MODEL, DEVICE, API_KEYS
|
|
101
|
+
│ │
|
|
102
|
+
│ ├── ai/ # GenAI, LLMs & Retrieval Engine
|
|
103
|
+
│ │ ├── __init__.py
|
|
104
|
+
│ │ ├── llm.py # LLM client & async token streaming generator
|
|
105
|
+
│ │ ├── prompt.py # System prompts & prompt engineering templates
|
|
106
|
+
│ │ └── rag.py # Vector retriever & RAG context builder
|
|
107
|
+
│ │
|
|
108
|
+
│ ├── ml/ # Machine Learning & Deep Learning Engine
|
|
109
|
+
│ │ ├── __init__.py
|
|
110
|
+
│ │ ├── model.py # Model wrapper (PyTorch / ONNX / Transformers)
|
|
111
|
+
│ │ └── pipeline.py # Tensor preprocessing & postprocessing
|
|
112
|
+
│ │
|
|
113
|
+
│ ├── schemas/ # Pydantic request & response models
|
|
114
|
+
│ │ ├── __init__.py
|
|
115
|
+
│ │ ├── chat.py # ChatRequest, ChatResponse, ChatMessage
|
|
116
|
+
│ │ ├── rag.py # RAGRequest, RAGResponse, DocumentChunk
|
|
117
|
+
│ │ └── predict.py # PredictionRequest, PredictionResponse, HealthResponse
|
|
118
|
+
│ │
|
|
119
|
+
│ └── services/ # Business logic & latency tracking
|
|
120
|
+
│ ├── __init__.py
|
|
121
|
+
│ ├── ai_service.py # GenAI orchestration (Chat, Stream, RAG)
|
|
122
|
+
│ └── inference.py # ML inference orchestration & latency benchmarking
|
|
123
|
+
│
|
|
124
|
+
├── tests/ # Automated test suite
|
|
125
|
+
│ ├── __init__.py
|
|
126
|
+
│ ├── conftest.py # TestClient fixtures + mock AI/ML providers
|
|
127
|
+
│ ├── test_chat.py # Pytest test cases for chat & RAG
|
|
128
|
+
│ └── test_predict.py # Pytest test cases for /health & /predict
|
|
129
|
+
│
|
|
130
|
+
├── .gitignore # Ignores __pycache__, .env, and large model weights (*.pt, *.bin)
|
|
131
|
+
├── .env # Local model configuration
|
|
132
|
+
├── .env.example
|
|
133
|
+
├── requirements.txt # Ready for pip install -r requirements.txt
|
|
134
|
+
├── pyproject.toml # Build & pytest configuration
|
|
135
|
+
└── README.md
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 🚀 Running Your Service
|
|
141
|
+
|
|
142
|
+
### 1. Create and activate a virtual environment
|
|
143
|
+
|
|
144
|
+
**Windows PowerShell:**
|
|
145
|
+
```powershell
|
|
146
|
+
python -m venv .venv
|
|
147
|
+
.\.venv\Scripts\Activate.ps1
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Windows CMD:**
|
|
151
|
+
```cmd
|
|
152
|
+
python -m venv .venv
|
|
153
|
+
call .venv\Scripts\activate.bat
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**macOS / Linux:**
|
|
157
|
+
```bash
|
|
158
|
+
python3 -m venv .venv
|
|
159
|
+
source .venv/bin/activate
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 2. Install dependencies
|
|
163
|
+
```bash
|
|
164
|
+
pip install -r requirements.txt
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 3. Start the development server
|
|
168
|
+
```bash
|
|
169
|
+
python -m uvicorn app.main:app --reload
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 🔍 Interactive Endpoints
|
|
175
|
+
|
|
176
|
+
Visit **[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)** for interactive Swagger UI:
|
|
177
|
+
|
|
178
|
+
| Method | Endpoint | Description |
|
|
179
|
+
| :--- | :--- | :--- |
|
|
180
|
+
| `GET` | `/` | API status and capabilities overview |
|
|
181
|
+
| `GET` | `/api/v1/health` | Health & device readiness probe (`cpu` or `cuda`) |
|
|
182
|
+
| `POST` | `/api/v1/chat/` | Standard GenAI chat completion |
|
|
183
|
+
| `POST` | `/api/v1/chat/stream` | Real-time token-by-token streaming (SSE) |
|
|
184
|
+
| `POST` | `/api/v1/rag/query` | Retrieval-Augmented Generation (Vector Search + LLM) |
|
|
185
|
+
| `POST` | `/api/v1/predict/` | ML model inference with execution latency tracking |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 🧪 Running Automated Tests
|
|
190
|
+
|
|
191
|
+
Run the included Pytest suite:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
python -m pytest
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Output:
|
|
198
|
+
```text
|
|
199
|
+
tests\test_chat.py .. [ 50%]
|
|
200
|
+
tests\test_predict.py .. [100%]
|
|
201
|
+
============================== 4 passed in 0.15s ==============================
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 🛠 Customizing Your AI & ML Models
|
|
207
|
+
|
|
208
|
+
### Connecting Real LLMs (OpenAI, Anthropic, Gemini, Ollama)
|
|
209
|
+
In `app/ai/llm.py`, plug your provider call directly into `generate_response()` and `stream_tokens()`:
|
|
210
|
+
```python
|
|
211
|
+
from openai import AsyncOpenAI
|
|
212
|
+
client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
|
|
213
|
+
|
|
214
|
+
async def stream_tokens(self, messages):
|
|
215
|
+
response = await client.chat.completions.create(
|
|
216
|
+
model=self.model_name,
|
|
217
|
+
messages=[m.model_dump() for m in messages],
|
|
218
|
+
stream=True,
|
|
219
|
+
)
|
|
220
|
+
async for chunk in response:
|
|
221
|
+
content = chunk.choices[0].delta.content or ""
|
|
222
|
+
yield f"data: {content}\n\n"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Loading PyTorch / ONNX Weights
|
|
226
|
+
In `app/ml/model.py`, initialize your local weights inside `load()`:
|
|
227
|
+
```python
|
|
228
|
+
import torch
|
|
229
|
+
|
|
230
|
+
def load(self):
|
|
231
|
+
self.model = torch.load(settings.ML_MODEL_PATH, map_location=settings.DEVICE)
|
|
232
|
+
self.model.eval()
|
|
233
|
+
self.is_loaded = True
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 📜 License
|
|
239
|
+
|
|
240
|
+
This project is open-source software licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://pypi.org/project/fastapi-struct/"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" width="320" alt="fastapi-struct"></a>
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<em>Production-ready FastAPI architecture generator tailor-made for <strong>AI, GenAI, LLM, and Machine Learning</strong> services.</em>
|
|
6
|
+
</p>
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://pypi.org/project/fastapi-struct" target="_blank">
|
|
9
|
+
<img src="https://img.shields.io/pypi/v/fastapi-struct.svg?color=%2334D058" alt="Package version">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://pypi.org/project/fastapi-struct" target="_blank">
|
|
12
|
+
<img src="https://img.shields.io/pypi/pyversions/fastapi-struct.svg?color=%2334D058" alt="Supported Python versions">
|
|
13
|
+
</a>
|
|
14
|
+
<a href="https://github.com/fastapi/fastapi" target="_blank">
|
|
15
|
+
<img src="https://img.shields.io/badge/FastAPI-0.115+-009688.svg?logo=fastapi&logoColor=white" alt="FastAPI">
|
|
16
|
+
</a>
|
|
17
|
+
<a href="https://github.com/pydantic/pydantic" target="_blank">
|
|
18
|
+
<img src="https://img.shields.io/badge/Pydantic-v2-E92063.svg?logo=pydantic&logoColor=white" alt="Pydantic v2">
|
|
19
|
+
</a>
|
|
20
|
+
<a href="https://opensource.org/licenses/MIT" target="_blank">
|
|
21
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT">
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
**FastAPI Documentation:** [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com)
|
|
28
|
+
**PyPI Package:** [https://pypi.org/project/fastapi-struct/](https://pypi.org/project/fastapi-struct/)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## ⚡ Why `fastapi-struct` for AI & ML Engineers?
|
|
33
|
+
|
|
34
|
+
Traditional web generator templates force relational database CRUD patterns (`repositories/`, `models/`, `users/`, `email.py`), cluttering modern AI microservices.
|
|
35
|
+
|
|
36
|
+
**`fastapi-struct`** defaults to a lean, enterprise-grade **AI & Machine Learning architecture**:
|
|
37
|
+
|
|
38
|
+
- 🤖 **GenAI Token Streaming:** Real-time token-by-token streaming via `StreamingResponse` (Server-Sent Events) for LLM chat completions.
|
|
39
|
+
- 📚 **RAG & Vector Search:** Modular vector retrieval layer ready for Pinecone, Qdrant, ChromaDB, or pgvector.
|
|
40
|
+
- 🧠 **Lifespan Model Caching:** Heavy model weights (PyTorch, ONNX, HuggingFace) load into GPU/CPU memory **once at startup** via FastAPI's `@asynccontextmanager lifespan`.
|
|
41
|
+
- 🩺 **GPU & Readiness Probes:** Built-in `/health` endpoint reporting GPU/device readiness (`cpu` or `cuda`).
|
|
42
|
+
- 🧹 **Zero `__pycache__` Clutter:** Automatically excludes byte-compiled files and stray caches during project scaffolding.
|
|
43
|
+
- 🧪 **Preconfigured Pytest Suite:** 4 automated integration tests for health, prediction, chat, and RAG.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 📦 Quick Start & CLI Usage
|
|
48
|
+
|
|
49
|
+
### Generate in the Current Directory (`.`)
|
|
50
|
+
```bash
|
|
51
|
+
fastapi-struct .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Or Create a Named Project Folder
|
|
55
|
+
```bash
|
|
56
|
+
fastapi-struct my_ai_service
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
*(You can also run it instantly with `uvx` without installing: `uvx fastapi-struct .`)*
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 📂 The AI & ML Architecture
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
my_ai_service/
|
|
67
|
+
│
|
|
68
|
+
├── app/
|
|
69
|
+
│ ├── __init__.py
|
|
70
|
+
│ ├── main.py # FastAPI initialization + Lifespan AI/ML model loader
|
|
71
|
+
│ │
|
|
72
|
+
│ ├── api/
|
|
73
|
+
│ │ ├── __init__.py
|
|
74
|
+
│ │ └── v1/
|
|
75
|
+
│ │ ├── __init__.py
|
|
76
|
+
│ │ ├── api.py # Central router aggregating AI, RAG, and ML routes
|
|
77
|
+
│ │ └── routes/
|
|
78
|
+
│ │ ├── __init__.py
|
|
79
|
+
│ │ ├── health.py # /health probe (GPU / device readiness check)
|
|
80
|
+
│ │ ├── chat.py # /chat & /stream (GenAI real-time token streaming)
|
|
81
|
+
│ │ ├── rag.py # /rag/query (Retrieval-Augmented Generation)
|
|
82
|
+
│ │ └── predict.py # /predict (Classical ML / Deep Learning inference)
|
|
83
|
+
│ │
|
|
84
|
+
│ ├── core/
|
|
85
|
+
│ │ ├── __init__.py
|
|
86
|
+
│ │ └── config.py # LLM_MODEL, EMBEDDING_MODEL, DEVICE, API_KEYS
|
|
87
|
+
│ │
|
|
88
|
+
│ ├── ai/ # GenAI, LLMs & Retrieval Engine
|
|
89
|
+
│ │ ├── __init__.py
|
|
90
|
+
│ │ ├── llm.py # LLM client & async token streaming generator
|
|
91
|
+
│ │ ├── prompt.py # System prompts & prompt engineering templates
|
|
92
|
+
│ │ └── rag.py # Vector retriever & RAG context builder
|
|
93
|
+
│ │
|
|
94
|
+
│ ├── ml/ # Machine Learning & Deep Learning Engine
|
|
95
|
+
│ │ ├── __init__.py
|
|
96
|
+
│ │ ├── model.py # Model wrapper (PyTorch / ONNX / Transformers)
|
|
97
|
+
│ │ └── pipeline.py # Tensor preprocessing & postprocessing
|
|
98
|
+
│ │
|
|
99
|
+
│ ├── schemas/ # Pydantic request & response models
|
|
100
|
+
│ │ ├── __init__.py
|
|
101
|
+
│ │ ├── chat.py # ChatRequest, ChatResponse, ChatMessage
|
|
102
|
+
│ │ ├── rag.py # RAGRequest, RAGResponse, DocumentChunk
|
|
103
|
+
│ │ └── predict.py # PredictionRequest, PredictionResponse, HealthResponse
|
|
104
|
+
│ │
|
|
105
|
+
│ └── services/ # Business logic & latency tracking
|
|
106
|
+
│ ├── __init__.py
|
|
107
|
+
│ ├── ai_service.py # GenAI orchestration (Chat, Stream, RAG)
|
|
108
|
+
│ └── inference.py # ML inference orchestration & latency benchmarking
|
|
109
|
+
│
|
|
110
|
+
├── tests/ # Automated test suite
|
|
111
|
+
│ ├── __init__.py
|
|
112
|
+
│ ├── conftest.py # TestClient fixtures + mock AI/ML providers
|
|
113
|
+
│ ├── test_chat.py # Pytest test cases for chat & RAG
|
|
114
|
+
│ └── test_predict.py # Pytest test cases for /health & /predict
|
|
115
|
+
│
|
|
116
|
+
├── .gitignore # Ignores __pycache__, .env, and large model weights (*.pt, *.bin)
|
|
117
|
+
├── .env # Local model configuration
|
|
118
|
+
├── .env.example
|
|
119
|
+
├── requirements.txt # Ready for pip install -r requirements.txt
|
|
120
|
+
├── pyproject.toml # Build & pytest configuration
|
|
121
|
+
└── README.md
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🚀 Running Your Service
|
|
127
|
+
|
|
128
|
+
### 1. Create and activate a virtual environment
|
|
129
|
+
|
|
130
|
+
**Windows PowerShell:**
|
|
131
|
+
```powershell
|
|
132
|
+
python -m venv .venv
|
|
133
|
+
.\.venv\Scripts\Activate.ps1
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Windows CMD:**
|
|
137
|
+
```cmd
|
|
138
|
+
python -m venv .venv
|
|
139
|
+
call .venv\Scripts\activate.bat
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**macOS / Linux:**
|
|
143
|
+
```bash
|
|
144
|
+
python3 -m venv .venv
|
|
145
|
+
source .venv/bin/activate
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 2. Install dependencies
|
|
149
|
+
```bash
|
|
150
|
+
pip install -r requirements.txt
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 3. Start the development server
|
|
154
|
+
```bash
|
|
155
|
+
python -m uvicorn app.main:app --reload
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 🔍 Interactive Endpoints
|
|
161
|
+
|
|
162
|
+
Visit **[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)** for interactive Swagger UI:
|
|
163
|
+
|
|
164
|
+
| Method | Endpoint | Description |
|
|
165
|
+
| :--- | :--- | :--- |
|
|
166
|
+
| `GET` | `/` | API status and capabilities overview |
|
|
167
|
+
| `GET` | `/api/v1/health` | Health & device readiness probe (`cpu` or `cuda`) |
|
|
168
|
+
| `POST` | `/api/v1/chat/` | Standard GenAI chat completion |
|
|
169
|
+
| `POST` | `/api/v1/chat/stream` | Real-time token-by-token streaming (SSE) |
|
|
170
|
+
| `POST` | `/api/v1/rag/query` | Retrieval-Augmented Generation (Vector Search + LLM) |
|
|
171
|
+
| `POST` | `/api/v1/predict/` | ML model inference with execution latency tracking |
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🧪 Running Automated Tests
|
|
176
|
+
|
|
177
|
+
Run the included Pytest suite:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
python -m pytest
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Output:
|
|
184
|
+
```text
|
|
185
|
+
tests\test_chat.py .. [ 50%]
|
|
186
|
+
tests\test_predict.py .. [100%]
|
|
187
|
+
============================== 4 passed in 0.15s ==============================
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 🛠 Customizing Your AI & ML Models
|
|
193
|
+
|
|
194
|
+
### Connecting Real LLMs (OpenAI, Anthropic, Gemini, Ollama)
|
|
195
|
+
In `app/ai/llm.py`, plug your provider call directly into `generate_response()` and `stream_tokens()`:
|
|
196
|
+
```python
|
|
197
|
+
from openai import AsyncOpenAI
|
|
198
|
+
client = AsyncOpenAI(api_key=settings.OPENAI_API_KEY)
|
|
199
|
+
|
|
200
|
+
async def stream_tokens(self, messages):
|
|
201
|
+
response = await client.chat.completions.create(
|
|
202
|
+
model=self.model_name,
|
|
203
|
+
messages=[m.model_dump() for m in messages],
|
|
204
|
+
stream=True,
|
|
205
|
+
)
|
|
206
|
+
async for chunk in response:
|
|
207
|
+
content = chunk.choices[0].delta.content or ""
|
|
208
|
+
yield f"data: {content}\n\n"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Loading PyTorch / ONNX Weights
|
|
212
|
+
In `app/ml/model.py`, initialize your local weights inside `load()`:
|
|
213
|
+
```python
|
|
214
|
+
import torch
|
|
215
|
+
|
|
216
|
+
def load(self):
|
|
217
|
+
self.model = torch.load(settings.ML_MODEL_PATH, map_location=settings.DEVICE)
|
|
218
|
+
self.model.eval()
|
|
219
|
+
self.is_loaded = True
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 📜 License
|
|
225
|
+
|
|
226
|
+
This project is open-source software licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -4,26 +4,28 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-struct"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "CLI tool to generate standard,
|
|
7
|
+
version = "0.3.0"
|
|
8
|
+
description = "CLI tool to generate standard, production-ready FastAPI architectures for AI, GenAI & ML services."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
license = "MIT"
|
|
12
12
|
keywords = [
|
|
13
13
|
"fastapi",
|
|
14
|
+
"ai",
|
|
15
|
+
"machine-learning",
|
|
16
|
+
"genai",
|
|
17
|
+
"llm",
|
|
18
|
+
"rag",
|
|
19
|
+
"inference",
|
|
14
20
|
"project-structure",
|
|
15
21
|
"boilerplate",
|
|
16
22
|
"scaffold",
|
|
17
23
|
"cli",
|
|
18
|
-
"template",
|
|
19
|
-
"folder-structure",
|
|
20
|
-
"architecture",
|
|
21
|
-
"repository-pattern",
|
|
22
|
-
"rest-api",
|
|
23
24
|
]
|
|
24
25
|
classifiers = [
|
|
25
26
|
"Programming Language :: Python :: 3",
|
|
26
27
|
"Framework :: FastAPI",
|
|
28
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
29
|
"Topic :: Software Development :: Code Generators",
|
|
28
30
|
"Operating System :: OS Independent",
|
|
29
31
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -8,7 +8,7 @@ from create_fastapi_app.generator import create_project
|
|
|
8
8
|
def main():
|
|
9
9
|
parser = argparse.ArgumentParser(
|
|
10
10
|
prog="fastapi-struct",
|
|
11
|
-
description="Create
|
|
11
|
+
description="Create production-ready FastAPI projects for AI/ML inference or Web APIs.",
|
|
12
12
|
)
|
|
13
13
|
parser.add_argument(
|
|
14
14
|
"name",
|
|
@@ -18,9 +18,9 @@ def main():
|
|
|
18
18
|
parser.add_argument(
|
|
19
19
|
"-t",
|
|
20
20
|
"--template",
|
|
21
|
-
choices=["
|
|
22
|
-
default="
|
|
23
|
-
help="Architecture template
|
|
21
|
+
choices=["ml", "basic"],
|
|
22
|
+
default="ml",
|
|
23
|
+
help="Architecture template: 'ml' (AI/ML inference service, default) or 'basic' (enterprise layered web API).",
|
|
24
24
|
)
|
|
25
25
|
parser.add_argument(
|
|
26
26
|
"--version",
|
|
@@ -6,7 +6,7 @@ PACKAGE_DIR = Path(__file__).parent
|
|
|
6
6
|
TEMPLATES_DIR = PACKAGE_DIR / "templates"
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def create_project(name: str, template: str = "
|
|
9
|
+
def create_project(name: str, template: str = "ml") -> Path:
|
|
10
10
|
if name == ".":
|
|
11
11
|
project_path = Path.cwd()
|
|
12
12
|
project_name = project_path.name
|
|
@@ -31,8 +31,36 @@ def create_project(name: str, template: str = "basic") -> Path:
|
|
|
31
31
|
f"Template '{template}' not found. Available templates: {', '.join(available)}"
|
|
32
32
|
)
|
|
33
33
|
|
|
34
|
-
#
|
|
35
|
-
|
|
34
|
+
# When scaffolding in current directory ('.'), clean up old conflicting app subfolders if present
|
|
35
|
+
if name == ".":
|
|
36
|
+
old_folders = ["repositories", "models", "utils"]
|
|
37
|
+
for old in old_folders:
|
|
38
|
+
old_path = project_path / "app" / old
|
|
39
|
+
if old_path.exists() and old_path.is_dir() and template == "ml":
|
|
40
|
+
shutil.rmtree(old_path, ignore_errors=True)
|
|
41
|
+
|
|
42
|
+
# Ignore __pycache__ and byte-compiled files during copy
|
|
43
|
+
ignore_patterns = shutil.ignore_patterns(
|
|
44
|
+
"__pycache__",
|
|
45
|
+
"*.pyc",
|
|
46
|
+
"*.pyo",
|
|
47
|
+
".pytest_cache",
|
|
48
|
+
"*.egg-info",
|
|
49
|
+
".DS_Store",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Copy template files cleanly
|
|
53
|
+
shutil.copytree(
|
|
54
|
+
selected_template_dir,
|
|
55
|
+
project_path,
|
|
56
|
+
ignore=ignore_patterns,
|
|
57
|
+
dirs_exist_ok=True,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Clean up any stray __pycache__ in project_path if it existed prior to copy
|
|
61
|
+
for pycache in project_path.rglob("__pycache__"):
|
|
62
|
+
if pycache.is_dir() and ".venv" not in pycache.parts:
|
|
63
|
+
shutil.rmtree(pycache, ignore_errors=True)
|
|
36
64
|
|
|
37
65
|
config_path = project_path / "pyproject.toml"
|
|
38
66
|
if config_path.exists():
|
|
@@ -43,6 +71,9 @@ def create_project(name: str, template: str = "basic") -> Path:
|
|
|
43
71
|
).replace(
|
|
44
72
|
'name = "my-ml-api"',
|
|
45
73
|
f'name = "{project_name.lower().replace("_", "-")}"',
|
|
74
|
+
).replace(
|
|
75
|
+
'name = "my-ai-ml-service"',
|
|
76
|
+
f'name = "{project_name.lower().replace("_", "-")}"',
|
|
46
77
|
)
|
|
47
78
|
config_path.write_text(config, encoding="utf-8")
|
|
48
79
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Byte-compiled / optimized files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Caches & Environments
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.mypy_cache/
|
|
10
|
+
.ruff_cache/
|
|
11
|
+
.venv/
|
|
12
|
+
env/
|
|
13
|
+
venv/
|
|
14
|
+
ENV/
|
|
15
|
+
|
|
16
|
+
# Large Model weights
|
|
17
|
+
*.pt
|
|
18
|
+
*.pth
|
|
19
|
+
*.onnx
|
|
20
|
+
*.bin
|
|
21
|
+
*.safetensors
|
|
22
|
+
weights/
|
|
23
|
+
checkpoints/
|
|
24
|
+
|
|
25
|
+
# Secrets & Environment variables
|
|
26
|
+
.env
|
|
27
|
+
.env.local
|
|
28
|
+
|
|
29
|
+
# Distribution & Build
|
|
30
|
+
dist/
|
|
31
|
+
build/
|
|
32
|
+
*.egg-info/
|