khoj 1.30.11.dev13__py3-none-any.whl → 1.30.11.dev46__py3-none-any.whl
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.
- khoj/app/settings.py +21 -0
- khoj/database/admin.py +47 -37
- khoj/database/migrations/0075_migrate_generated_assets_and_validate.py +85 -0
- khoj/database/models/__init__.py +164 -31
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1603-f5babe72ba9f6a59.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5538-0ea2d3944ca051e1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-1878cc328ea380bd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/{page-8eead7920b0ff92a.js → page-f5c0801b27a8e95e.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-b5800b5286306140.js → page-8691f6c09a0acd44.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-9219a85f3477e722.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/{page-d7d2ab93e519f0b2.js → page-135d56dd4263e40d.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/layout-6310c57b674dd6f5.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/{page-3c32ad5472f75965.js → page-e79ace822d51557b.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/{page-faa998c71eb7ca8e.js → page-e8b578d155550386.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-cbe7f56b1f87d77a.js → page-b6c835050c970be7.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-6f4879fbbf8b90f7.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-cd5757199539bbf2.js → page-635635e4fb39fe29.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-3a2dfd74acf6e193.js → webpack-5203c3872078c10c.js} +1 -1
- khoj/interface/compiled/_next/static/css/{bedf49fbfc598358.css → 089de1d8526b96e9.css} +1 -1
- khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +1 -0
- khoj/interface/compiled/_next/static/css/edd3abaf11580924.css +1 -0
- khoj/interface/compiled/_next/static/media/1d8a05b60287ae6c-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/6f22fce21a7c433c-s.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/77c207b095007c34-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/82ef96de0e8f4d8c-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/a6ecd16fa044d500-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/bd82c78e5b7b3fe9-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/c32c8052c071fc42-s.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/c4250770ab8708b6-s.p.woff2 +0 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +2 -2
- khoj/interface/compiled/assets/icons/khoj_lantern.svg +100 -0
- khoj/interface/compiled/assets/icons/khoj_lantern_128x128_dark.png +0 -0
- khoj/interface/compiled/automations/index.html +1 -1
- khoj/interface/compiled/automations/index.txt +2 -2
- khoj/interface/compiled/chat/index.html +1 -1
- khoj/interface/compiled/chat/index.txt +2 -2
- khoj/interface/compiled/index.html +1 -1
- khoj/interface/compiled/index.txt +2 -2
- khoj/interface/compiled/search/index.html +1 -1
- khoj/interface/compiled/search/index.txt +2 -2
- khoj/interface/compiled/settings/index.html +1 -1
- khoj/interface/compiled/settings/index.txt +2 -2
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +2 -2
- khoj/processor/conversation/anthropic/anthropic_chat.py +11 -3
- khoj/processor/conversation/google/gemini_chat.py +11 -3
- khoj/processor/conversation/offline/chat_model.py +6 -2
- khoj/processor/conversation/openai/gpt.py +10 -2
- khoj/processor/conversation/prompts.py +18 -0
- khoj/processor/conversation/utils.py +82 -26
- khoj/processor/image/generate.py +10 -13
- khoj/routers/api_chat.py +49 -98
- khoj/routers/helpers.py +41 -1
- {khoj-1.30.11.dev13.dist-info → khoj-1.30.11.dev46.dist-info}/METADATA +2 -1
- {khoj-1.30.11.dev13.dist-info → khoj-1.30.11.dev46.dist-info}/RECORD +62 -59
- khoj/interface/compiled/_next/static/chunks/1603-c68d44bc4ae6039a.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5538-e5f3c9f4d67a64b9.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-f2ea2b26fc0e78b1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-1072c3b0ab136e74.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/layout-72ec1be8afd0b1ab.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-dc97434f0354a74e.js +0 -1
- khoj/interface/compiled/_next/static/css/2d097a35da6bfe8d.css +0 -1
- khoj/interface/compiled/_next/static/css/80bd6301fc657983.css +0 -1
- khoj/interface/compiled/_next/static/media/5455839c73f146e7-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/5984b96ba4822821-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/684adc3dde1b03f1-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/82e3b9a1bdaf0c26-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/8d1ea331386a0db8-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/91475f6526542a4f-s.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/b98b13dbc1c3b59c-s.p.woff2 +0 -0
- khoj/interface/compiled/_next/static/media/c824d7a20139e39d-s.woff2 +0 -0
- /khoj/interface/compiled/_next/static/{K_WyVARSz0loPVvwOW1gg → VhbBwTudxbu82AwZhVKwF}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{K_WyVARSz0loPVvwOW1gg → VhbBwTudxbu82AwZhVKwF}/_ssgManifest.js +0 -0
- {khoj-1.30.11.dev13.dist-info → khoj-1.30.11.dev46.dist-info}/WHEEL +0 -0
- {khoj-1.30.11.dev13.dist-info → khoj-1.30.11.dev46.dist-info}/entry_points.txt +0 -0
- {khoj-1.30.11.dev13.dist-info → khoj-1.30.11.dev46.dist-info}/licenses/LICENSE +0 -0
khoj/app/settings.py
CHANGED
@@ -13,6 +13,8 @@ https://docs.djangoproject.com/en/4.2/ref/settings/
|
|
13
13
|
import os
|
14
14
|
from pathlib import Path
|
15
15
|
|
16
|
+
from django.templatetags.static import static
|
17
|
+
|
16
18
|
from khoj.utils.helpers import in_debug_mode, is_env_var_true
|
17
19
|
|
18
20
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
@@ -72,6 +74,7 @@ INSTALLED_APPS = [
|
|
72
74
|
"django.contrib.auth",
|
73
75
|
"django.contrib.contenttypes",
|
74
76
|
"khoj.database.apps.DatabaseConfig",
|
77
|
+
"unfold",
|
75
78
|
"django.contrib.admin",
|
76
79
|
"django.contrib.sessions",
|
77
80
|
"django.contrib.messages",
|
@@ -195,3 +198,21 @@ APSCHEDULER_DATETIME_FORMAT = "N j, Y, f:s a"
|
|
195
198
|
# that supports multiple background worker processes instead (e.g. Dramatiq, Celery, Django-RQ,
|
196
199
|
# etc. See: https://djangopackages.org/grids/g/workers-queues-tasks/ for popular options).
|
197
200
|
APSCHEDULER_RUN_NOW_TIMEOUT = 240 # Seconds
|
201
|
+
|
202
|
+
UNFOLD = {
|
203
|
+
"SITE_TITLE": "Khoj Admin Panel",
|
204
|
+
"SITE_HEADER": "Khoj Admin Panel",
|
205
|
+
"SITE_URL": "/",
|
206
|
+
"SITE_ICON": {
|
207
|
+
"light": lambda request: static("assets/icons/khoj_lantern_128x128.png"),
|
208
|
+
"dark": lambda request: static("assets/icons/khoj_lantern_128x128_dark.png"),
|
209
|
+
},
|
210
|
+
"SITE_FAVICONS": [
|
211
|
+
{
|
212
|
+
"rel": "icon",
|
213
|
+
"sizes": "32x32",
|
214
|
+
"type": "image/svg+xml",
|
215
|
+
"href": lambda request: static("assets/icons/khoj_lantern.svg"),
|
216
|
+
},
|
217
|
+
],
|
218
|
+
}
|
khoj/database/admin.py
CHANGED
@@ -4,11 +4,14 @@ from datetime import date, datetime, timedelta, timezone
|
|
4
4
|
|
5
5
|
from apscheduler.job import Job
|
6
6
|
from django.contrib import admin, messages
|
7
|
-
from django.contrib.auth.admin import
|
7
|
+
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
|
8
|
+
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
9
|
+
from django.contrib.auth.models import Group
|
8
10
|
from django.http import HttpResponse
|
9
|
-
from django_apscheduler.admin import DjangoJobAdmin
|
11
|
+
from django_apscheduler.admin import DjangoJobAdmin, DjangoJobExecutionAdmin
|
10
12
|
from django_apscheduler.jobstores import DjangoJobStore
|
11
|
-
from django_apscheduler.models import DjangoJob
|
13
|
+
from django_apscheduler.models import DjangoJob, DjangoJobExecution
|
14
|
+
from unfold import admin as unfold_admin
|
12
15
|
|
13
16
|
from khoj.database.models import (
|
14
17
|
Agent,
|
@@ -35,10 +38,8 @@ from khoj.database.models import (
|
|
35
38
|
)
|
36
39
|
from khoj.utils.helpers import ImageIntentType
|
37
40
|
|
38
|
-
admin.site.unregister(DjangoJob)
|
39
|
-
|
40
41
|
|
41
|
-
class KhojDjangoJobAdmin(DjangoJobAdmin):
|
42
|
+
class KhojDjangoJobAdmin(DjangoJobAdmin, unfold_admin.ModelAdmin):
|
42
43
|
list_display = (
|
43
44
|
"id",
|
44
45
|
"next_run_time",
|
@@ -62,10 +63,25 @@ class KhojDjangoJobAdmin(DjangoJobAdmin):
|
|
62
63
|
return queryset, use_distinct
|
63
64
|
|
64
65
|
|
66
|
+
class KhojDjangoJobExecutionAdmin(DjangoJobExecutionAdmin, unfold_admin.ModelAdmin):
|
67
|
+
pass
|
68
|
+
|
69
|
+
|
70
|
+
admin.site.unregister(DjangoJob)
|
65
71
|
admin.site.register(DjangoJob, KhojDjangoJobAdmin)
|
72
|
+
admin.site.unregister(DjangoJobExecution)
|
73
|
+
admin.site.register(DjangoJobExecution, KhojDjangoJobExecutionAdmin)
|
66
74
|
|
67
75
|
|
68
|
-
class
|
76
|
+
class GroupAdmin(BaseGroupAdmin, unfold_admin.ModelAdmin):
|
77
|
+
pass
|
78
|
+
|
79
|
+
|
80
|
+
class UserAdmin(BaseUserAdmin, unfold_admin.ModelAdmin):
|
81
|
+
pass
|
82
|
+
|
83
|
+
|
84
|
+
class KhojUserAdmin(UserAdmin, unfold_admin.ModelAdmin):
|
69
85
|
class DateJoinedAfterFilter(admin.SimpleListFilter):
|
70
86
|
title = "Joined after"
|
71
87
|
parameter_name = "joined_after"
|
@@ -137,21 +153,22 @@ class KhojUserAdmin(UserAdmin):
|
|
137
153
|
get_email_login_url.short_description = "Get email login URL" # type: ignore
|
138
154
|
|
139
155
|
|
156
|
+
admin.site.unregister(Group)
|
140
157
|
admin.site.register(KhojUser, KhojUserAdmin)
|
141
158
|
|
142
|
-
admin.site.register(ProcessLock)
|
143
|
-
admin.site.register(SpeechToTextModelOptions)
|
144
|
-
admin.site.register(ReflectiveQuestion)
|
145
|
-
admin.site.register(ClientApplication)
|
146
|
-
admin.site.register(GithubConfig)
|
147
|
-
admin.site.register(NotionConfig)
|
148
|
-
admin.site.register(UserVoiceModelConfig)
|
149
|
-
admin.site.register(VoiceModelOption)
|
150
|
-
admin.site.register(UserRequests)
|
159
|
+
admin.site.register(ProcessLock, unfold_admin.ModelAdmin)
|
160
|
+
admin.site.register(SpeechToTextModelOptions, unfold_admin.ModelAdmin)
|
161
|
+
admin.site.register(ReflectiveQuestion, unfold_admin.ModelAdmin)
|
162
|
+
admin.site.register(ClientApplication, unfold_admin.ModelAdmin)
|
163
|
+
admin.site.register(GithubConfig, unfold_admin.ModelAdmin)
|
164
|
+
admin.site.register(NotionConfig, unfold_admin.ModelAdmin)
|
165
|
+
admin.site.register(UserVoiceModelConfig, unfold_admin.ModelAdmin)
|
166
|
+
admin.site.register(VoiceModelOption, unfold_admin.ModelAdmin)
|
167
|
+
admin.site.register(UserRequests, unfold_admin.ModelAdmin)
|
151
168
|
|
152
169
|
|
153
170
|
@admin.register(Agent)
|
154
|
-
class AgentAdmin(
|
171
|
+
class AgentAdmin(unfold_admin.ModelAdmin):
|
155
172
|
list_display = (
|
156
173
|
"id",
|
157
174
|
"name",
|
@@ -161,7 +178,7 @@ class AgentAdmin(admin.ModelAdmin):
|
|
161
178
|
|
162
179
|
|
163
180
|
@admin.register(Entry)
|
164
|
-
class EntryAdmin(
|
181
|
+
class EntryAdmin(unfold_admin.ModelAdmin):
|
165
182
|
list_display = (
|
166
183
|
"id",
|
167
184
|
"created_at",
|
@@ -183,7 +200,7 @@ class EntryAdmin(admin.ModelAdmin):
|
|
183
200
|
|
184
201
|
|
185
202
|
@admin.register(Subscription)
|
186
|
-
class KhojUserSubscription(
|
203
|
+
class KhojUserSubscription(unfold_admin.ModelAdmin):
|
187
204
|
list_display = (
|
188
205
|
"id",
|
189
206
|
"user",
|
@@ -195,7 +212,7 @@ class KhojUserSubscription(admin.ModelAdmin):
|
|
195
212
|
|
196
213
|
|
197
214
|
@admin.register(ChatModelOptions)
|
198
|
-
class ChatModelOptionsAdmin(
|
215
|
+
class ChatModelOptionsAdmin(unfold_admin.ModelAdmin):
|
199
216
|
list_display = (
|
200
217
|
"id",
|
201
218
|
"chat_model",
|
@@ -206,7 +223,7 @@ class ChatModelOptionsAdmin(admin.ModelAdmin):
|
|
206
223
|
|
207
224
|
|
208
225
|
@admin.register(TextToImageModelConfig)
|
209
|
-
class TextToImageModelOptionsAdmin(
|
226
|
+
class TextToImageModelOptionsAdmin(unfold_admin.ModelAdmin):
|
210
227
|
list_display = (
|
211
228
|
"id",
|
212
229
|
"model_name",
|
@@ -216,7 +233,7 @@ class TextToImageModelOptionsAdmin(admin.ModelAdmin):
|
|
216
233
|
|
217
234
|
|
218
235
|
@admin.register(OpenAIProcessorConversationConfig)
|
219
|
-
class OpenAIProcessorConversationConfigAdmin(
|
236
|
+
class OpenAIProcessorConversationConfigAdmin(unfold_admin.ModelAdmin):
|
220
237
|
list_display = (
|
221
238
|
"id",
|
222
239
|
"name",
|
@@ -227,7 +244,7 @@ class OpenAIProcessorConversationConfigAdmin(admin.ModelAdmin):
|
|
227
244
|
|
228
245
|
|
229
246
|
@admin.register(SearchModelConfig)
|
230
|
-
class SearchModelConfigAdmin(
|
247
|
+
class SearchModelConfigAdmin(unfold_admin.ModelAdmin):
|
231
248
|
list_display = (
|
232
249
|
"id",
|
233
250
|
"name",
|
@@ -238,7 +255,7 @@ class SearchModelConfigAdmin(admin.ModelAdmin):
|
|
238
255
|
|
239
256
|
|
240
257
|
@admin.register(ServerChatSettings)
|
241
|
-
class ServerChatSettingsAdmin(
|
258
|
+
class ServerChatSettingsAdmin(unfold_admin.ModelAdmin):
|
242
259
|
list_display = (
|
243
260
|
"chat_default",
|
244
261
|
"chat_advanced",
|
@@ -247,7 +264,7 @@ class ServerChatSettingsAdmin(admin.ModelAdmin):
|
|
247
264
|
|
248
265
|
|
249
266
|
@admin.register(WebScraper)
|
250
|
-
class WebScraperAdmin(
|
267
|
+
class WebScraperAdmin(unfold_admin.ModelAdmin):
|
251
268
|
list_display = (
|
252
269
|
"priority",
|
253
270
|
"name",
|
@@ -261,7 +278,7 @@ class WebScraperAdmin(admin.ModelAdmin):
|
|
261
278
|
|
262
279
|
|
263
280
|
@admin.register(Conversation)
|
264
|
-
class ConversationAdmin(
|
281
|
+
class ConversationAdmin(unfold_admin.ModelAdmin):
|
265
282
|
list_display = (
|
266
283
|
"id",
|
267
284
|
"user",
|
@@ -286,17 +303,10 @@ class ConversationAdmin(admin.ModelAdmin):
|
|
286
303
|
modified_log = conversation.conversation_log
|
287
304
|
chat_log = modified_log.get("chat", [])
|
288
305
|
for idx, log in enumerate(chat_log):
|
289
|
-
if
|
290
|
-
log["
|
291
|
-
and log["intent"]
|
292
|
-
and log["intent"]["type"]
|
293
|
-
and (
|
294
|
-
log["intent"]["type"] == ImageIntentType.TEXT_TO_IMAGE.value
|
295
|
-
or log["intent"]["type"] == ImageIntentType.TEXT_TO_IMAGE_V3.value
|
296
|
-
)
|
297
|
-
):
|
298
|
-
log["message"] = "inline image redacted for space"
|
306
|
+
if log["by"] == "khoj" and log["images"]:
|
307
|
+
log["images"] = ["inline image redacted for space"]
|
299
308
|
chat_log[idx] = log
|
309
|
+
|
300
310
|
modified_log["chat"] = chat_log
|
301
311
|
|
302
312
|
writer.writerow(
|
@@ -367,7 +377,7 @@ class ConversationAdmin(admin.ModelAdmin):
|
|
367
377
|
|
368
378
|
|
369
379
|
@admin.register(UserConversationConfig)
|
370
|
-
class UserConversationConfigAdmin(
|
380
|
+
class UserConversationConfigAdmin(unfold_admin.ModelAdmin):
|
371
381
|
list_display = (
|
372
382
|
"id",
|
373
383
|
"get_user_email",
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Made manually by sabaimran for use by Django 5.0.9 on 2024-12-01 16:59
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
# This script was written alongside when Pydantic validation was added to the Conversation conversation_log field.
|
6
|
+
|
7
|
+
|
8
|
+
def migrate_generated_assets(apps, schema_editor):
|
9
|
+
Conversation = apps.get_model("database", "Conversation")
|
10
|
+
|
11
|
+
# Process conversations in chunks
|
12
|
+
for conversation in Conversation.objects.iterator():
|
13
|
+
try:
|
14
|
+
meta_log = conversation.conversation_log
|
15
|
+
modified = False
|
16
|
+
|
17
|
+
for chat in meta_log.get("chat", []):
|
18
|
+
intent_type = chat.get("intent", {}).get("type")
|
19
|
+
|
20
|
+
if intent_type and chat["by"] == "khoj":
|
21
|
+
if intent_type and "text-to-image" in intent_type:
|
22
|
+
# Migrate the generated image to the new format
|
23
|
+
chat["images"] = [chat.get("message")]
|
24
|
+
chat["message"] = chat["intent"]["inferred-queries"][0]
|
25
|
+
modified = True
|
26
|
+
|
27
|
+
if intent_type and "excalidraw" in intent_type:
|
28
|
+
# Migrate the generated excalidraw to the new format
|
29
|
+
chat["excalidrawDiagram"] = chat.get("message")
|
30
|
+
chat["message"] = chat["intent"]["inferred-queries"][0]
|
31
|
+
modified = True
|
32
|
+
|
33
|
+
# Only save if changes were made
|
34
|
+
if modified:
|
35
|
+
conversation.conversation_log = meta_log
|
36
|
+
conversation.save()
|
37
|
+
|
38
|
+
except Exception as e:
|
39
|
+
print(f"Error processing conversation {conversation.id}: {str(e)}")
|
40
|
+
continue
|
41
|
+
|
42
|
+
|
43
|
+
def reverse_migration(apps, schema_editor):
|
44
|
+
Conversation = apps.get_model("database", "Conversation")
|
45
|
+
|
46
|
+
# Process conversations in chunks
|
47
|
+
for conversation in Conversation.objects.iterator():
|
48
|
+
try:
|
49
|
+
meta_log = conversation.conversation_log
|
50
|
+
modified = False
|
51
|
+
|
52
|
+
for chat in meta_log.get("chat", []):
|
53
|
+
intent_type = chat.get("intent", {}).get("type")
|
54
|
+
|
55
|
+
if intent_type and chat["by"] == "khoj":
|
56
|
+
if intent_type and "text-to-image" in intent_type:
|
57
|
+
# Migrate the generated image back to the old format
|
58
|
+
chat["message"] = chat.get("images", [])[0]
|
59
|
+
chat.pop("images", None)
|
60
|
+
modified = True
|
61
|
+
|
62
|
+
if intent_type and "excalidraw" in intent_type:
|
63
|
+
# Migrate the generated excalidraw back to the old format
|
64
|
+
chat["message"] = chat.get("excalidrawDiagram")
|
65
|
+
chat.pop("excalidrawDiagram", None)
|
66
|
+
modified = True
|
67
|
+
|
68
|
+
# Only save if changes were made
|
69
|
+
if modified:
|
70
|
+
conversation.conversation_log = meta_log
|
71
|
+
conversation.save()
|
72
|
+
|
73
|
+
except Exception as e:
|
74
|
+
print(f"Error processing conversation {conversation.id}: {str(e)}")
|
75
|
+
continue
|
76
|
+
|
77
|
+
|
78
|
+
class Migration(migrations.Migration):
|
79
|
+
dependencies = [
|
80
|
+
("database", "0074_alter_conversation_title"),
|
81
|
+
]
|
82
|
+
|
83
|
+
operations = [
|
84
|
+
migrations.RunPython(migrate_generated_assets, reverse_migration),
|
85
|
+
]
|