django-neural-feed 1.0.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.
Files changed (24) hide show
  1. django_neural_feed-1.0.0/LICENSE +21 -0
  2. django_neural_feed-1.0.0/PKG-INFO +323 -0
  3. django_neural_feed-1.0.0/README.md +284 -0
  4. django_neural_feed-1.0.0/pyproject.toml +60 -0
  5. django_neural_feed-1.0.0/setup.cfg +4 -0
  6. django_neural_feed-1.0.0/src/django_neural_feed/__init__.py +1 -0
  7. django_neural_feed-1.0.0/src/django_neural_feed/apps.py +27 -0
  8. django_neural_feed-1.0.0/src/django_neural_feed/conf.py +122 -0
  9. django_neural_feed-1.0.0/src/django_neural_feed/encoders.py +75 -0
  10. django_neural_feed-1.0.0/src/django_neural_feed/feeds.py +153 -0
  11. django_neural_feed-1.0.0/src/django_neural_feed/migrations/0001_initial.py +48 -0
  12. django_neural_feed-1.0.0/src/django_neural_feed/migrations/__init__.py +0 -0
  13. django_neural_feed-1.0.0/src/django_neural_feed/mixins.py +30 -0
  14. django_neural_feed-1.0.0/src/django_neural_feed/models.py +23 -0
  15. django_neural_feed-1.0.0/src/django_neural_feed/signals.py +253 -0
  16. django_neural_feed-1.0.0/src/django_neural_feed/tasks.py +99 -0
  17. django_neural_feed-1.0.0/src/django_neural_feed.egg-info/PKG-INFO +323 -0
  18. django_neural_feed-1.0.0/src/django_neural_feed.egg-info/SOURCES.txt +22 -0
  19. django_neural_feed-1.0.0/src/django_neural_feed.egg-info/dependency_links.txt +1 -0
  20. django_neural_feed-1.0.0/src/django_neural_feed.egg-info/requires.txt +17 -0
  21. django_neural_feed-1.0.0/src/django_neural_feed.egg-info/top_level.txt +1 -0
  22. django_neural_feed-1.0.0/tests/test_feeds_coverage.py +176 -0
  23. django_neural_feed-1.0.0/tests/test_neural_architecture.py +774 -0
  24. django_neural_feed-1.0.0/tests/test_signals_coverage.py +657 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dersty
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,323 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-neural-feed
3
+ Version: 1.0.0
4
+ Summary: Personalized content feeds using vector embeddings, pgvector, and hybrid database-level scoring.
5
+ Author: itsDersty
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Web Environment
9
+ Classifier: Framework :: Django
10
+ Classifier: Framework :: Django :: 4.2
11
+ Classifier: Framework :: Django :: 5.0
12
+ Classifier: Framework :: Django :: 6.0
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Internet :: WWW/HTTP
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: Django>=4.2
24
+ Requires-Dist: numpy>=2.0.0
25
+ Requires-Dist: pgvector>=0.4.0
26
+ Requires-Dist: sentence-transformers>=3.0.0
27
+ Provides-Extra: celery
28
+ Requires-Dist: celery>=5.3.0; extra == "celery"
29
+ Provides-Extra: dev
30
+ Requires-Dist: black>=24.0.0; extra == "dev"
31
+ Requires-Dist: flake8>=7.0.0; extra == "dev"
32
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
33
+ Requires-Dist: pytest-django>=4.8.0; extra == "dev"
34
+ Requires-Dist: pytest-mock>=3.14.0; extra == "dev"
35
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
36
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "dev"
37
+ Requires-Dist: celery>=5.3.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # Django Neural Feed (DNF)
41
+
42
+ <p align="center">
43
+ <a href="https://github.com/ItsDersty/django-neural-feed/actions/workflows/main.yml">
44
+ <img src="https://img.shields.io/github/actions/workflow/status/ItsDersty/django-neural-feed/main.yml?branch=feature/oop-architecture&style=flat-square&label=tests" alt="Build Status">
45
+ </a>
46
+ <a href="https://github.com/ItsDersty/django-neural-feed">
47
+ <img src="https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square" alt="Coverage">
48
+ </a>
49
+ <a href="https://pypi.org/project/django-neural-feed/">
50
+ <img src="https://img.shields.io/pypi/v/django-neural-feed?style=flat-square&color=blue" alt="PyPI Version">
51
+ </a>
52
+ <a href="https://github.com/ItsDersty/django-neural-feed/blob/main/LICENSE">
53
+ <img src="https://img.shields.io/github/license/ItsDersty/django-neural-feed?style=flat-square&color=green" alt="License">
54
+ </a>
55
+ <a href="https://github.com/ItsDersty/django-neural-feed">
56
+ <img src="https://img.shields.io/badge/python-3.10+-blue?style=flat-square" alt="Python Version">
57
+ </a>
58
+ <a href="https://github.com/ItsDersty/django-neural-feed">
59
+ <img src="https://img.shields.io/badge/django-4.2%2B-darkgreen?style=flat-square" alt="Django Version">
60
+ </a>
61
+ </p>
62
+
63
+ ## Overview
64
+
65
+ **Django Neural Feed (DNF)** is a production-ready Django application designed to build intelligent, personalized content feeds powered by semantic vector embeddings. It leverages PostgreSQL's `pgvector` extension to compute vector similarity at the database level, combined with customizable content freshness and popularity metrics—all evaluated in a single optimized SQL query.
66
+
67
+ With its object-oriented architecture, DNF decouples your configuration logic into dedicated Feed classes. It tracks user interactions non-intrusively via Django signals and supports flexible deployment execution blocks, easily falling back from Celery asynchronous queues to synchronous background threads if the broker is offline.
68
+
69
+ ## Core Features
70
+
71
+ - **🧠 Object-Oriented Feed Configuration**: Define isolated, multi-tenant recommendation feeds by subclassing a unified `BaseNeuralFeed` class.
72
+ - **⚡ Bulletproof Asynchronous Pipeline**: Offload embedding generation and vector aggregation to Celery. Features an automated synchronous thread fallback system.
73
+ - **📊 Dedicated Multi-Feed User Profiles**: Stores vector profiles in an isolated `UserFeedProfile` model partitioned by `feed_id`, keeping your core Auth User table clean.
74
+ - **🎯 Hybrid Multi-Criteria Scoring**: Merges semantic similarity (pgvector cosine distance), content recency, and custom popularity expressions into a single database-level annotation.
75
+ - **🚀 Non-Invasive Integration**: Attach recommendation behavior to existing content models with minimal migrations, leaving your interaction tables (Likes/Dislikes) completely untouched.
76
+
77
+ ## Requirements
78
+
79
+ - **Python**: 3.10+
80
+ - **Django**: 4.2, 5.0, 6.0+
81
+ - **PostgreSQL**: 12+ (with `pgvector` extension installed)
82
+ - **NumPy**: 2.0.0+
83
+ - **pgvector**: 0.4.0+
84
+ - **SentenceTransformers**: 3.0.0+
85
+
86
+ ## Installation
87
+
88
+ ### 1. Install the Package
89
+
90
+ ```bash
91
+ pip install django-neural-feed
92
+ ```
93
+ ### **2. Add to Django Settings**
94
+
95
+ ```python
96
+ INSTALLED_APPS = [
97
+ # ... other apps
98
+ 'django_neural_feed',
99
+ ]
100
+ ```
101
+ ### **3. Initialize PostgreSQL Extension**
102
+
103
+ Ensure pgvector is enabled in your database instance:
104
+
105
+ ```sql
106
+ CREATE EXTENSION IF NOT EXISTS vector;
107
+ ```
108
+
109
+ ## **Quick Start**
110
+
111
+ ### **Step 1: Configure Your Content Model**
112
+
113
+ Inherit from NeuralRecommendMixin to inject a vector embedding column into your target content table.
114
+
115
+ ```python
116
+ from django.conf import settings
117
+ from django.db import models
118
+ from django_neural_feed.mixins import NeuralRecommendMixin
119
+
120
+ class Post(NeuralRecommendMixin, models.Model): # NOTE: NeuralRecommendMixin must be BEFORE models.Model!
121
+ title = models.CharField(max_length=255)
122
+ content = models.TextField()
123
+ created_at = models.DateTimeField(auto_now_add=True)
124
+ likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="liked_posts")
125
+
126
+ def get_ready_text(self) -> str:
127
+ return f"{self.title} {self.content}"
128
+ ```
129
+
130
+ Prepare and apply your migrations:
131
+
132
+ ```bash
133
+ python manage.py makemigrations
134
+ python manage.py migrate
135
+ ```
136
+
137
+ ### **Step 2: Define a Custom Feed Class**
138
+
139
+ Create a dedicated `feeds.py` configuration to encapsulate tracking thresholds, model fields, math scoring expressions, and hybrid weights.
140
+
141
+ ```python
142
+ from django.db.models import Count, F, FloatField, ExpressionWrapper, Value
143
+ from django.db.models.functions import Cast, Ln, Extract, Now
144
+ from django_neural_feed.feeds import BaseNeuralFeed
145
+ from your_app.models import Post
146
+
147
+ class PostFeed(BaseNeuralFeed):
148
+ # 1. Core Feed Identity
149
+ feed_id = "posts_main"
150
+ parent_feed = None # Optional: Reference to a parent feed class for inheritance hierarchy
151
+
152
+ # 2. Target Django Models Configuration
153
+ content_django_model = Post
154
+ interaction_django_model = Post.likes.through
155
+
156
+ # 3. Interaction Tracking Pipelines
157
+ mode = "m2m" # Use "m2m" for ManyToMany fields, or "model" for explicit through models
158
+ user_field_name = "user" # Field pointing to User model (not needed if mode is "m2m")
159
+ content_field_name = "post" # Field pointing to Content model (not needed if mode is "m2m")
160
+
161
+ # 4. Model & Pipeline Thresholds
162
+ embedding_model_name = "paraphrase-multilingual-MiniLM-L12-v2" # Overrides global setting
163
+ user_likes_limit = 20 # Max target sample size slice for vector profile aggregation
164
+
165
+ # 5. Hybrid Scoring Global Weights (Should ideally sum up to 1.0)
166
+ weight_similarity = 0.6
167
+ weight_freshness = 0.2
168
+ weight_popularity = 0.2
169
+
170
+ # 6. Popularity: Logarithmic scaling using natural logarithm to keep viral jumps balanced
171
+ # Ln(Value(1000.0)) scales the metric dynamically, hitting a 1.0 score modifier at 1000 likes.
172
+ popularity_expression = ExpressionWrapper(
173
+ Ln(Cast(Count("likes"), FloatField()) + Value(1.0)) / Ln(Value(1000.0)),
174
+ output_field=FloatField()
175
+ )
176
+
177
+ # 7. Freshness: Time-decay function based on post age in hours
178
+ # Safely subtracts timestamps inside the database, converting the interval to hours.
179
+ freshness_expression = ExpressionWrapper(
180
+ Value(1.0) / (
181
+ Value(1.0) + (
182
+ Extract(Now() - F("created_at"), "epoch") / 3600.0
183
+ )
184
+ ),
185
+ output_field=FloatField()
186
+ )
187
+ ```
188
+
189
+ ### **Step 3: Register Feed in Settings**
190
+
191
+ Register the string path to your custom feed configuration within the `DJANGO_NEURAL_FEED["FEEDS"]` list inside your `settings.py`.
192
+
193
+ ```python
194
+ DJANGO_NEURAL_FEED = {
195
+ "FEEDS": [
196
+ "your_app.feeds.PostFeed", # DNF hooks up all model and M2M signals automatically
197
+ ],
198
+ }
199
+ ```
200
+
201
+ ### **Step 4: Fetch Personalized Feed Results**
202
+
203
+ Use your feed's `.get_feed()` function to obtain optimized querysets sorted by hybrid weights.
204
+
205
+ ```python
206
+ from your_app.feeds import PostFeed
207
+ from your_app.models import Post
208
+
209
+ def user_feed_view(request):
210
+ # Gather IDs of posts the user has already liked to exclude them from the feed
211
+ excluded_ids = Post.objects.filter(
212
+ likes=request.user
213
+ ).values_list('id', flat=True)
214
+
215
+ # Generate personalized recommendations directly via your Feed class
216
+ feed_queryset = PostFeed.get_feed(
217
+ user=request.user,
218
+ queryset=Post.objects.all(),
219
+ excluded_ids=excluded_ids,
220
+ limit=20
221
+ )
222
+ return feed_queryset
223
+ ```
224
+
225
+ ### **Configuration Reference**
226
+
227
+ You can pass default global limits and model engine backends via standard DJANGO_NEURAL_FEED dictionary keys in your settings.py:
228
+
229
+ ```python
230
+ DJANGO_NEURAL_FEED = {
231
+ "MODEL_NAME": "paraphrase-multilingual-MiniLM-L12-v2",
232
+ "VECTOR_DIMENSION": 384,
233
+ "CELERY_ENABLED": True,
234
+ "WEIGHT_SIMILARITY": 0.6,
235
+ "WEIGHT_FRESHNESS": 0.2,
236
+ "WEIGHT_POPULARITY": 0.2,
237
+ }
238
+ ```
239
+
240
+ | Global Config Key | Type | Default | Purpose |
241
+ | :---- | :---- | :---- | :---- |
242
+ | MODEL_NAME | str | paraphrase-multilingual-MiniLM-L12-v2 | Target HuggingFace SentenceTransformer engine. |
243
+ | VECTOR_DIMENSION | int | 384 | Embedding dense matrix array dimension sizes. |
244
+ | ENCODER_CLASS | str/type | django_neural_feed.encoders.DefaultVectorEncoder | Path to the vectorization engine class interface. |
245
+ | WEIGHT_SIMILARITY | float | 0.6 | Default proportional weight of cosine similarity scoring. |
246
+ | WEIGHT_FRESHNESS | float | 0.2 | Default proportional weight of item creation recency. |
247
+ | WEIGHT_POPULARITY | float | 0.2 | Default proportional weight of user interaction counts. |
248
+ | USER_LIKES_LIMIT | int | 20 | Max target sample size slice for vector aggregation. |
249
+ | CELERY_ENABLED | bool | False | Toggles routing tasks to background Celery workers. |
250
+
251
+ ### **Advanced Settings Overriding**
252
+
253
+ Every specific attribute can be declared dynamically within your custom BaseNeuralFeed class implementation to build separate configurations for multiple models (e.g., separate metrics weights for ArticlesFeed vs VideoFeed).
254
+
255
+ | Feed Class Attribute | Type | Default Value / Fallback | Purpose |
256
+ | :---- | :---- | :---- | :---- |
257
+ | feed_id | str | "default_feed" | Unique identifier for partitioning user vector profiles. |
258
+ | mode | str | *Required* ("m2m" | "model") | Toggles the internal signal tracking pipeline architecture. |
259
+ | embedding_model_name | str | settings.MODEL_NAME | Overrides the text-embedding engine for this specific feed. |
260
+ | user_likes_limit | int | settings.USER_LIKES_LIMIT | Overrides the history interaction slice size for this feed. |
261
+ | weight_similarity | float | settings.WEIGHT_SIMILARITY | Fine-tunes semantic similarity importance for this feed. |
262
+ | weight_freshness | float | settings.WEIGHT_FRESHNESS | Fine-tunes time-decay metric importance for this feed. |
263
+ | weight_popularity | float | settings.WEIGHT_POPULARITY | Fine-tunes interaction count importance for this feed. |
264
+ | popularity_expression | Expression | Value(1.0) | Custom Django ORM expression for parsing popularity scoring. |
265
+ | freshness_expression | Expression | Value(1.0) | Custom Django ORM expression for parsing time-decay scoring. |
266
+
267
+ ## **Architecture Mechanics**
268
+
269
+ 1. **Content Structuring**: When an entity subclassing NeuralRecommendMixin fires a post_save execution block, DNF reads get_ready_text() to calculate a dense float vector.
270
+ 2. **Preference Profiling**: On target connection updates, an isolated worker fetches the latest interaction history rows, calculates an averaged, L2-normalized mean representation vector, and updates UserFeedProfile.
271
+ 3. **Query Engine Generation**: Invoking Feed.get_feed() applies pgvector operations combined with standard math normalization, avoiding redundant lookups.
272
+
273
+ ## **Custom Vector Encoders (Advanced)**
274
+
275
+ By default, DNF uses local `sentence-transformers` via `DefaultVectorEncoder`. If you prefer to use external cloud APIs (like OpenAI, Cohere, or custom microservices) for generating embeddings, you can implement a custom encoder.
276
+
277
+ ### 1. Subclass BaseVectorEncoder
278
+
279
+ Create a custom encoder class anywhere in your Django project and implement the text_to_vector method. You can optionally override average_vectors if you want to replace NumPy-based processing.
280
+
281
+ ```python
282
+ import requests
283
+ from django_neural_feed.encoders import BaseVectorEncoder
284
+
285
+ class OpenAIAppEncoder(BaseVectorEncoder):
286
+ @classmethod
287
+ def text_to_vector(cls, text: str, model_name: str) -> list[float]:
288
+ """Fetch embeddings via custom third-party cloud API endpoint."""
289
+ if not text.strip():
290
+ return []
291
+
292
+ # Example API execution blueprint
293
+ response = requests.post(
294
+ "https://api.openai.com/v1/embeddings",
295
+ headers={"Authorization": "Bearer YOUR_API_KEY"},
296
+ json={"input": text, "model": model_name}
297
+ )
298
+ return response.json()["data"][0]["embedding"]
299
+ ```
300
+
301
+ ### 2. Register Custom Encoder in Settings
302
+
303
+ Pass the absolute string path pointing to your class implementation inside the global dictionary configuration:
304
+
305
+ ```python
306
+ DJANGO_NEURAL_FEED = {
307
+ "ENCODER_CLASS": "your_app.encoders.OpenAIAppEncoder",
308
+ "MODEL_NAME": "text-embedding-3-small", # Passed down as model_name argument
309
+ "VECTOR_DIMENSION": 1536, # Adjust to match your custom API provider
310
+ }
311
+ ```
312
+
313
+ ## **Testing**
314
+
315
+ DNF maintains full code coverage execution metrics. Run the suite natively using:
316
+
317
+ ```bash
318
+ pytest --cov=src/django_neural_feed
319
+ ```
320
+
321
+ ## **License**
322
+
323
+ Distributed under the terms of the MIT License.
@@ -0,0 +1,284 @@
1
+ # Django Neural Feed (DNF)
2
+
3
+ <p align="center">
4
+ <a href="https://github.com/ItsDersty/django-neural-feed/actions/workflows/main.yml">
5
+ <img src="https://img.shields.io/github/actions/workflow/status/ItsDersty/django-neural-feed/main.yml?branch=feature/oop-architecture&style=flat-square&label=tests" alt="Build Status">
6
+ </a>
7
+ <a href="https://github.com/ItsDersty/django-neural-feed">
8
+ <img src="https://img.shields.io/badge/coverage-100%25-brightgreen?style=flat-square" alt="Coverage">
9
+ </a>
10
+ <a href="https://pypi.org/project/django-neural-feed/">
11
+ <img src="https://img.shields.io/pypi/v/django-neural-feed?style=flat-square&color=blue" alt="PyPI Version">
12
+ </a>
13
+ <a href="https://github.com/ItsDersty/django-neural-feed/blob/main/LICENSE">
14
+ <img src="https://img.shields.io/github/license/ItsDersty/django-neural-feed?style=flat-square&color=green" alt="License">
15
+ </a>
16
+ <a href="https://github.com/ItsDersty/django-neural-feed">
17
+ <img src="https://img.shields.io/badge/python-3.10+-blue?style=flat-square" alt="Python Version">
18
+ </a>
19
+ <a href="https://github.com/ItsDersty/django-neural-feed">
20
+ <img src="https://img.shields.io/badge/django-4.2%2B-darkgreen?style=flat-square" alt="Django Version">
21
+ </a>
22
+ </p>
23
+
24
+ ## Overview
25
+
26
+ **Django Neural Feed (DNF)** is a production-ready Django application designed to build intelligent, personalized content feeds powered by semantic vector embeddings. It leverages PostgreSQL's `pgvector` extension to compute vector similarity at the database level, combined with customizable content freshness and popularity metrics—all evaluated in a single optimized SQL query.
27
+
28
+ With its object-oriented architecture, DNF decouples your configuration logic into dedicated Feed classes. It tracks user interactions non-intrusively via Django signals and supports flexible deployment execution blocks, easily falling back from Celery asynchronous queues to synchronous background threads if the broker is offline.
29
+
30
+ ## Core Features
31
+
32
+ - **🧠 Object-Oriented Feed Configuration**: Define isolated, multi-tenant recommendation feeds by subclassing a unified `BaseNeuralFeed` class.
33
+ - **⚡ Bulletproof Asynchronous Pipeline**: Offload embedding generation and vector aggregation to Celery. Features an automated synchronous thread fallback system.
34
+ - **📊 Dedicated Multi-Feed User Profiles**: Stores vector profiles in an isolated `UserFeedProfile` model partitioned by `feed_id`, keeping your core Auth User table clean.
35
+ - **🎯 Hybrid Multi-Criteria Scoring**: Merges semantic similarity (pgvector cosine distance), content recency, and custom popularity expressions into a single database-level annotation.
36
+ - **🚀 Non-Invasive Integration**: Attach recommendation behavior to existing content models with minimal migrations, leaving your interaction tables (Likes/Dislikes) completely untouched.
37
+
38
+ ## Requirements
39
+
40
+ - **Python**: 3.10+
41
+ - **Django**: 4.2, 5.0, 6.0+
42
+ - **PostgreSQL**: 12+ (with `pgvector` extension installed)
43
+ - **NumPy**: 2.0.0+
44
+ - **pgvector**: 0.4.0+
45
+ - **SentenceTransformers**: 3.0.0+
46
+
47
+ ## Installation
48
+
49
+ ### 1. Install the Package
50
+
51
+ ```bash
52
+ pip install django-neural-feed
53
+ ```
54
+ ### **2. Add to Django Settings**
55
+
56
+ ```python
57
+ INSTALLED_APPS = [
58
+ # ... other apps
59
+ 'django_neural_feed',
60
+ ]
61
+ ```
62
+ ### **3. Initialize PostgreSQL Extension**
63
+
64
+ Ensure pgvector is enabled in your database instance:
65
+
66
+ ```sql
67
+ CREATE EXTENSION IF NOT EXISTS vector;
68
+ ```
69
+
70
+ ## **Quick Start**
71
+
72
+ ### **Step 1: Configure Your Content Model**
73
+
74
+ Inherit from NeuralRecommendMixin to inject a vector embedding column into your target content table.
75
+
76
+ ```python
77
+ from django.conf import settings
78
+ from django.db import models
79
+ from django_neural_feed.mixins import NeuralRecommendMixin
80
+
81
+ class Post(NeuralRecommendMixin, models.Model): # NOTE: NeuralRecommendMixin must be BEFORE models.Model!
82
+ title = models.CharField(max_length=255)
83
+ content = models.TextField()
84
+ created_at = models.DateTimeField(auto_now_add=True)
85
+ likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="liked_posts")
86
+
87
+ def get_ready_text(self) -> str:
88
+ return f"{self.title} {self.content}"
89
+ ```
90
+
91
+ Prepare and apply your migrations:
92
+
93
+ ```bash
94
+ python manage.py makemigrations
95
+ python manage.py migrate
96
+ ```
97
+
98
+ ### **Step 2: Define a Custom Feed Class**
99
+
100
+ Create a dedicated `feeds.py` configuration to encapsulate tracking thresholds, model fields, math scoring expressions, and hybrid weights.
101
+
102
+ ```python
103
+ from django.db.models import Count, F, FloatField, ExpressionWrapper, Value
104
+ from django.db.models.functions import Cast, Ln, Extract, Now
105
+ from django_neural_feed.feeds import BaseNeuralFeed
106
+ from your_app.models import Post
107
+
108
+ class PostFeed(BaseNeuralFeed):
109
+ # 1. Core Feed Identity
110
+ feed_id = "posts_main"
111
+ parent_feed = None # Optional: Reference to a parent feed class for inheritance hierarchy
112
+
113
+ # 2. Target Django Models Configuration
114
+ content_django_model = Post
115
+ interaction_django_model = Post.likes.through
116
+
117
+ # 3. Interaction Tracking Pipelines
118
+ mode = "m2m" # Use "m2m" for ManyToMany fields, or "model" for explicit through models
119
+ user_field_name = "user" # Field pointing to User model (not needed if mode is "m2m")
120
+ content_field_name = "post" # Field pointing to Content model (not needed if mode is "m2m")
121
+
122
+ # 4. Model & Pipeline Thresholds
123
+ embedding_model_name = "paraphrase-multilingual-MiniLM-L12-v2" # Overrides global setting
124
+ user_likes_limit = 20 # Max target sample size slice for vector profile aggregation
125
+
126
+ # 5. Hybrid Scoring Global Weights (Should ideally sum up to 1.0)
127
+ weight_similarity = 0.6
128
+ weight_freshness = 0.2
129
+ weight_popularity = 0.2
130
+
131
+ # 6. Popularity: Logarithmic scaling using natural logarithm to keep viral jumps balanced
132
+ # Ln(Value(1000.0)) scales the metric dynamically, hitting a 1.0 score modifier at 1000 likes.
133
+ popularity_expression = ExpressionWrapper(
134
+ Ln(Cast(Count("likes"), FloatField()) + Value(1.0)) / Ln(Value(1000.0)),
135
+ output_field=FloatField()
136
+ )
137
+
138
+ # 7. Freshness: Time-decay function based on post age in hours
139
+ # Safely subtracts timestamps inside the database, converting the interval to hours.
140
+ freshness_expression = ExpressionWrapper(
141
+ Value(1.0) / (
142
+ Value(1.0) + (
143
+ Extract(Now() - F("created_at"), "epoch") / 3600.0
144
+ )
145
+ ),
146
+ output_field=FloatField()
147
+ )
148
+ ```
149
+
150
+ ### **Step 3: Register Feed in Settings**
151
+
152
+ Register the string path to your custom feed configuration within the `DJANGO_NEURAL_FEED["FEEDS"]` list inside your `settings.py`.
153
+
154
+ ```python
155
+ DJANGO_NEURAL_FEED = {
156
+ "FEEDS": [
157
+ "your_app.feeds.PostFeed", # DNF hooks up all model and M2M signals automatically
158
+ ],
159
+ }
160
+ ```
161
+
162
+ ### **Step 4: Fetch Personalized Feed Results**
163
+
164
+ Use your feed's `.get_feed()` function to obtain optimized querysets sorted by hybrid weights.
165
+
166
+ ```python
167
+ from your_app.feeds import PostFeed
168
+ from your_app.models import Post
169
+
170
+ def user_feed_view(request):
171
+ # Gather IDs of posts the user has already liked to exclude them from the feed
172
+ excluded_ids = Post.objects.filter(
173
+ likes=request.user
174
+ ).values_list('id', flat=True)
175
+
176
+ # Generate personalized recommendations directly via your Feed class
177
+ feed_queryset = PostFeed.get_feed(
178
+ user=request.user,
179
+ queryset=Post.objects.all(),
180
+ excluded_ids=excluded_ids,
181
+ limit=20
182
+ )
183
+ return feed_queryset
184
+ ```
185
+
186
+ ### **Configuration Reference**
187
+
188
+ You can pass default global limits and model engine backends via standard DJANGO_NEURAL_FEED dictionary keys in your settings.py:
189
+
190
+ ```python
191
+ DJANGO_NEURAL_FEED = {
192
+ "MODEL_NAME": "paraphrase-multilingual-MiniLM-L12-v2",
193
+ "VECTOR_DIMENSION": 384,
194
+ "CELERY_ENABLED": True,
195
+ "WEIGHT_SIMILARITY": 0.6,
196
+ "WEIGHT_FRESHNESS": 0.2,
197
+ "WEIGHT_POPULARITY": 0.2,
198
+ }
199
+ ```
200
+
201
+ | Global Config Key | Type | Default | Purpose |
202
+ | :---- | :---- | :---- | :---- |
203
+ | MODEL_NAME | str | paraphrase-multilingual-MiniLM-L12-v2 | Target HuggingFace SentenceTransformer engine. |
204
+ | VECTOR_DIMENSION | int | 384 | Embedding dense matrix array dimension sizes. |
205
+ | ENCODER_CLASS | str/type | django_neural_feed.encoders.DefaultVectorEncoder | Path to the vectorization engine class interface. |
206
+ | WEIGHT_SIMILARITY | float | 0.6 | Default proportional weight of cosine similarity scoring. |
207
+ | WEIGHT_FRESHNESS | float | 0.2 | Default proportional weight of item creation recency. |
208
+ | WEIGHT_POPULARITY | float | 0.2 | Default proportional weight of user interaction counts. |
209
+ | USER_LIKES_LIMIT | int | 20 | Max target sample size slice for vector aggregation. |
210
+ | CELERY_ENABLED | bool | False | Toggles routing tasks to background Celery workers. |
211
+
212
+ ### **Advanced Settings Overriding**
213
+
214
+ Every specific attribute can be declared dynamically within your custom BaseNeuralFeed class implementation to build separate configurations for multiple models (e.g., separate metrics weights for ArticlesFeed vs VideoFeed).
215
+
216
+ | Feed Class Attribute | Type | Default Value / Fallback | Purpose |
217
+ | :---- | :---- | :---- | :---- |
218
+ | feed_id | str | "default_feed" | Unique identifier for partitioning user vector profiles. |
219
+ | mode | str | *Required* ("m2m" | "model") | Toggles the internal signal tracking pipeline architecture. |
220
+ | embedding_model_name | str | settings.MODEL_NAME | Overrides the text-embedding engine for this specific feed. |
221
+ | user_likes_limit | int | settings.USER_LIKES_LIMIT | Overrides the history interaction slice size for this feed. |
222
+ | weight_similarity | float | settings.WEIGHT_SIMILARITY | Fine-tunes semantic similarity importance for this feed. |
223
+ | weight_freshness | float | settings.WEIGHT_FRESHNESS | Fine-tunes time-decay metric importance for this feed. |
224
+ | weight_popularity | float | settings.WEIGHT_POPULARITY | Fine-tunes interaction count importance for this feed. |
225
+ | popularity_expression | Expression | Value(1.0) | Custom Django ORM expression for parsing popularity scoring. |
226
+ | freshness_expression | Expression | Value(1.0) | Custom Django ORM expression for parsing time-decay scoring. |
227
+
228
+ ## **Architecture Mechanics**
229
+
230
+ 1. **Content Structuring**: When an entity subclassing NeuralRecommendMixin fires a post_save execution block, DNF reads get_ready_text() to calculate a dense float vector.
231
+ 2. **Preference Profiling**: On target connection updates, an isolated worker fetches the latest interaction history rows, calculates an averaged, L2-normalized mean representation vector, and updates UserFeedProfile.
232
+ 3. **Query Engine Generation**: Invoking Feed.get_feed() applies pgvector operations combined with standard math normalization, avoiding redundant lookups.
233
+
234
+ ## **Custom Vector Encoders (Advanced)**
235
+
236
+ By default, DNF uses local `sentence-transformers` via `DefaultVectorEncoder`. If you prefer to use external cloud APIs (like OpenAI, Cohere, or custom microservices) for generating embeddings, you can implement a custom encoder.
237
+
238
+ ### 1. Subclass BaseVectorEncoder
239
+
240
+ Create a custom encoder class anywhere in your Django project and implement the text_to_vector method. You can optionally override average_vectors if you want to replace NumPy-based processing.
241
+
242
+ ```python
243
+ import requests
244
+ from django_neural_feed.encoders import BaseVectorEncoder
245
+
246
+ class OpenAIAppEncoder(BaseVectorEncoder):
247
+ @classmethod
248
+ def text_to_vector(cls, text: str, model_name: str) -> list[float]:
249
+ """Fetch embeddings via custom third-party cloud API endpoint."""
250
+ if not text.strip():
251
+ return []
252
+
253
+ # Example API execution blueprint
254
+ response = requests.post(
255
+ "https://api.openai.com/v1/embeddings",
256
+ headers={"Authorization": "Bearer YOUR_API_KEY"},
257
+ json={"input": text, "model": model_name}
258
+ )
259
+ return response.json()["data"][0]["embedding"]
260
+ ```
261
+
262
+ ### 2. Register Custom Encoder in Settings
263
+
264
+ Pass the absolute string path pointing to your class implementation inside the global dictionary configuration:
265
+
266
+ ```python
267
+ DJANGO_NEURAL_FEED = {
268
+ "ENCODER_CLASS": "your_app.encoders.OpenAIAppEncoder",
269
+ "MODEL_NAME": "text-embedding-3-small", # Passed down as model_name argument
270
+ "VECTOR_DIMENSION": 1536, # Adjust to match your custom API provider
271
+ }
272
+ ```
273
+
274
+ ## **Testing**
275
+
276
+ DNF maintains full code coverage execution metrics. Run the suite natively using:
277
+
278
+ ```bash
279
+ pytest --cov=src/django_neural_feed
280
+ ```
281
+
282
+ ## **License**
283
+
284
+ Distributed under the terms of the MIT License.
@@ -0,0 +1,60 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "django-neural-feed"
7
+ version = "1.0.0"
8
+ authors = [
9
+ { name = "itsDersty" }
10
+ ]
11
+ description = "Personalized content feeds using vector embeddings, pgvector, and hybrid database-level scoring."
12
+ readme = "README.md"
13
+ requires-python = ">=3.10"
14
+ license = { text = "MIT" }
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Web Environment",
18
+ "Framework :: Django",
19
+ "Framework :: Django :: 4.2",
20
+ "Framework :: Django :: 5.0",
21
+ "Framework :: Django :: 6.0",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Topic :: Internet :: WWW/HTTP",
29
+ ]
30
+
31
+ dependencies = [
32
+ "Django>=4.2",
33
+ "numpy>=2.0.0",
34
+ "pgvector>=0.4.0",
35
+ "sentence-transformers>=3.0.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ celery = [
40
+ "celery>=5.3.0",
41
+ ]
42
+ dev = [
43
+ "black>=24.0.0",
44
+ "flake8>=7.0.0",
45
+ "pytest>=8.0.0",
46
+ "pytest-django>=4.8.0",
47
+ "pytest-mock>=3.14.0",
48
+ "pytest-cov>=5.0.0",
49
+ "psycopg2-binary>=2.9.0",
50
+ "celery>=5.3.0",
51
+ ]
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+ include = ["django_neural_feed*"]
56
+
57
+ [tool.coverage.run]
58
+ omit = [
59
+ "*/migrations/*",
60
+ ]