llms-py 3.0.0b10__py3-none-any.whl → 3.0.2__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.
- llms/{extensions/app/db_manager.py → db.py} +170 -15
- llms/extensions/app/__init__.py +95 -39
- llms/extensions/app/db.py +16 -124
- llms/extensions/app/ui/threadStore.mjs +20 -2
- llms/extensions/core_tools/__init__.py +37 -0
- llms/extensions/gallery/__init__.py +15 -13
- llms/extensions/gallery/db.py +117 -172
- llms/extensions/gallery/ui/index.mjs +1 -1
- llms/extensions/providers/__init__.py +3 -1
- llms/extensions/providers/anthropic.py +7 -3
- llms/extensions/providers/cerebras.py +37 -0
- llms/extensions/providers/chutes.py +1 -1
- llms/extensions/providers/google.py +131 -28
- llms/extensions/providers/nvidia.py +2 -2
- llms/extensions/providers/openai.py +2 -2
- llms/extensions/providers/openrouter.py +4 -2
- llms/extensions/system_prompts/ui/index.mjs +21 -26
- llms/extensions/system_prompts/ui/prompts.json +5 -5
- llms/llms.json +3 -0
- llms/main.py +81 -34
- llms/providers.json +1 -1
- llms/ui/ai.mjs +1 -1
- llms/ui/app.css +96 -3
- llms/ui/ctx.mjs +24 -1
- llms/ui/index.mjs +2 -0
- llms/ui/modules/chat/ChatBody.mjs +1 -0
- llms/ui/modules/chat/index.mjs +19 -1
- llms/ui/modules/icons.mjs +46 -0
- llms/ui/modules/layout.mjs +28 -0
- llms/ui/modules/model-selector.mjs +0 -40
- llms/ui/utils.mjs +9 -1
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/METADATA +1 -1
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/RECORD +37 -65
- llms/__pycache__/__init__.cpython-312.pyc +0 -0
- llms/__pycache__/__init__.cpython-313.pyc +0 -0
- llms/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/__pycache__/__main__.cpython-312.pyc +0 -0
- llms/__pycache__/__main__.cpython-314.pyc +0 -0
- llms/__pycache__/llms.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-312.pyc +0 -0
- llms/__pycache__/main.cpython-313.pyc +0 -0
- llms/__pycache__/main.cpython-314.pyc +0 -0
- llms/__pycache__/plugins.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms/extensions/app/requests.json +0 -9073
- llms/extensions/app/threads.json +0 -15290
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
- llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
- llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
- llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/WHEEL +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.0b10.dist-info → llms_py-3.0.2.dist-info}/top_level.txt +0 -0
llms/extensions/app/db.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
-
import threading
|
|
4
3
|
from datetime import datetime, timedelta
|
|
5
4
|
from typing import Any, Dict
|
|
6
5
|
|
|
7
|
-
from .
|
|
6
|
+
from llms.db import DbManager, order_by, select_columns, to_dto, valid_columns
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
def with_user(data, user):
|
|
@@ -17,44 +16,6 @@ def with_user(data, user):
|
|
|
17
16
|
return data
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
def valid_columns(all_columns, fields):
|
|
21
|
-
if fields:
|
|
22
|
-
if not isinstance(fields, list):
|
|
23
|
-
fields = fields.split(",")
|
|
24
|
-
cols = []
|
|
25
|
-
for k in fields:
|
|
26
|
-
k = k.strip()
|
|
27
|
-
if k in all_columns:
|
|
28
|
-
cols.append(k)
|
|
29
|
-
return cols
|
|
30
|
-
return []
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def table_columns(all_columns, fields):
|
|
34
|
-
cols = valid_columns(all_columns, fields)
|
|
35
|
-
return ", ".join(cols) if len(cols) > 0 else ", ".join(all_columns)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def select_columns(all_columns, fields, select=None):
|
|
39
|
-
columns = table_columns(all_columns, fields)
|
|
40
|
-
if select == "distinct":
|
|
41
|
-
return f"SELECT DISTINCT {columns}"
|
|
42
|
-
return f"SELECT {columns}"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def order_by(all_columns, sort):
|
|
46
|
-
cols = []
|
|
47
|
-
for k in sort.split(","):
|
|
48
|
-
k = k.strip()
|
|
49
|
-
by = ""
|
|
50
|
-
if k[0] == "-":
|
|
51
|
-
by = " DESC"
|
|
52
|
-
k = k[1:]
|
|
53
|
-
if k in all_columns:
|
|
54
|
-
cols.append(f"{k}{by}")
|
|
55
|
-
return f"ORDER BY {', '.join(cols)} " if len(cols) > 0 else ""
|
|
56
|
-
|
|
57
|
-
|
|
58
19
|
class AppDB:
|
|
59
20
|
def __init__(self, ctx, db_path):
|
|
60
21
|
if db_path is None:
|
|
@@ -81,6 +42,7 @@ class AppDB:
|
|
|
81
42
|
"modalities": "JSON",
|
|
82
43
|
"messages": "JSON",
|
|
83
44
|
"args": "JSON",
|
|
45
|
+
"tools": "JSON",
|
|
84
46
|
"toolHistory": "JSON",
|
|
85
47
|
"cost": "REAL",
|
|
86
48
|
"inputTokens": "INTEGER",
|
|
@@ -94,6 +56,7 @@ class AppDB:
|
|
|
94
56
|
"metadata": "JSON",
|
|
95
57
|
"error": "TEXT",
|
|
96
58
|
"ref": "TEXT",
|
|
59
|
+
"providerResponse": "JSON",
|
|
97
60
|
},
|
|
98
61
|
"request": {
|
|
99
62
|
"id": "INTEGER",
|
|
@@ -300,6 +263,9 @@ class AppDB:
|
|
|
300
263
|
sql_params,
|
|
301
264
|
).lastrowid
|
|
302
265
|
|
|
266
|
+
def to_dto(self, row, json_columns):
|
|
267
|
+
return to_dto(self.ctx, row, json_columns)
|
|
268
|
+
|
|
303
269
|
def get_user_filter(self, user=None, params=None):
|
|
304
270
|
if user is None:
|
|
305
271
|
return "WHERE user IS NULL", params or {}
|
|
@@ -309,12 +275,8 @@ class AppDB:
|
|
|
309
275
|
return "WHERE user = :user", args
|
|
310
276
|
|
|
311
277
|
def get_thread(self, id, user=None):
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return self.db.one(f"SELECT * FROM thread {sql_where} AND id = :id", params)
|
|
315
|
-
except Exception as e:
|
|
316
|
-
self.ctx.err(f"get_thread ({id}, {user})", e)
|
|
317
|
-
return None
|
|
278
|
+
sql_where, params = self.get_user_filter(user, {"id": id})
|
|
279
|
+
return self.db.one(f"SELECT * FROM thread {sql_where} AND id = :id", params)
|
|
318
280
|
|
|
319
281
|
def get_thread_column(self, id, column, user=None):
|
|
320
282
|
if column not in self.columns["thread"]:
|
|
@@ -375,76 +337,6 @@ class AppDB:
|
|
|
375
337
|
self.ctx.err(f"query_threads ({take}, {skip})", e)
|
|
376
338
|
return []
|
|
377
339
|
|
|
378
|
-
def insert(self, table, info, callback=None):
|
|
379
|
-
if not info:
|
|
380
|
-
raise Exception("info is required")
|
|
381
|
-
|
|
382
|
-
columns = self.columns[table]
|
|
383
|
-
args = {}
|
|
384
|
-
known_columns = columns.keys()
|
|
385
|
-
for k, val in info.items():
|
|
386
|
-
if k in known_columns and k != "id":
|
|
387
|
-
args[k] = self.db.value(val)
|
|
388
|
-
|
|
389
|
-
insert_keys = list(args.keys())
|
|
390
|
-
insert_body = ", ".join(insert_keys)
|
|
391
|
-
insert_values = ", ".join(["?" for _ in insert_keys])
|
|
392
|
-
|
|
393
|
-
sql = f"INSERT INTO {table} ({insert_body}) VALUES ({insert_values})"
|
|
394
|
-
|
|
395
|
-
self.db.write(sql, tuple(args[k] for k in insert_keys), callback)
|
|
396
|
-
|
|
397
|
-
async def insert_async(self, table, info):
|
|
398
|
-
event = threading.Event()
|
|
399
|
-
|
|
400
|
-
ret = [None]
|
|
401
|
-
|
|
402
|
-
def cb(lastrowid, rowcount, error=None):
|
|
403
|
-
nonlocal ret
|
|
404
|
-
if error:
|
|
405
|
-
raise error
|
|
406
|
-
ret[0] = lastrowid
|
|
407
|
-
event.set()
|
|
408
|
-
|
|
409
|
-
self.insert(table, info, cb)
|
|
410
|
-
event.wait()
|
|
411
|
-
return ret[0]
|
|
412
|
-
|
|
413
|
-
def update(self, table, info, callback=None):
|
|
414
|
-
if not info:
|
|
415
|
-
raise Exception("info is required")
|
|
416
|
-
|
|
417
|
-
columns = self.columns[table]
|
|
418
|
-
args = {}
|
|
419
|
-
known_columns = columns.keys()
|
|
420
|
-
for k, val in info.items():
|
|
421
|
-
if k in known_columns and k != "id":
|
|
422
|
-
args[k] = self.db.value(val)
|
|
423
|
-
|
|
424
|
-
update_keys = list(args.keys())
|
|
425
|
-
update_body = ", ".join([f"{k} = :{k}" for k in update_keys])
|
|
426
|
-
|
|
427
|
-
args["id"] = info["id"]
|
|
428
|
-
sql = f"UPDATE {table} SET {update_body} WHERE id = :id"
|
|
429
|
-
|
|
430
|
-
self.db.write(sql, args, callback)
|
|
431
|
-
|
|
432
|
-
async def update_async(self, table, info):
|
|
433
|
-
event = threading.Event()
|
|
434
|
-
|
|
435
|
-
ret = [None]
|
|
436
|
-
|
|
437
|
-
def cb(lastrowid, rowcount, error=None):
|
|
438
|
-
nonlocal ret
|
|
439
|
-
if error:
|
|
440
|
-
raise error
|
|
441
|
-
ret[0] = rowcount
|
|
442
|
-
event.set()
|
|
443
|
-
|
|
444
|
-
self.update(table, info, cb)
|
|
445
|
-
event.wait()
|
|
446
|
-
return ret[0]
|
|
447
|
-
|
|
448
340
|
def prepare_thread(self, thread, id=None, user=None):
|
|
449
341
|
now = datetime.now()
|
|
450
342
|
if id:
|
|
@@ -458,16 +350,16 @@ class AppDB:
|
|
|
458
350
|
return with_user(thread, user=user)
|
|
459
351
|
|
|
460
352
|
def create_thread(self, thread: Dict[str, Any], user=None):
|
|
461
|
-
return self.insert("thread", self.prepare_thread(thread, user=user))
|
|
353
|
+
return self.db.insert("thread", self.columns["thread"], self.prepare_thread(thread, user=user))
|
|
462
354
|
|
|
463
355
|
async def create_thread_async(self, thread: Dict[str, Any], user=None):
|
|
464
|
-
return await self.insert_async("thread", self.prepare_thread(thread, user=user))
|
|
356
|
+
return await self.db.insert_async("thread", self.columns["thread"], self.prepare_thread(thread, user=user))
|
|
465
357
|
|
|
466
358
|
def update_thread(self, id, thread: Dict[str, Any], user=None):
|
|
467
|
-
return self.update("thread", self.prepare_thread(thread, id, user=user))
|
|
359
|
+
return self.db.update("thread", self.columns["thread"], self.prepare_thread(thread, id, user=user))
|
|
468
360
|
|
|
469
361
|
async def update_thread_async(self, id, thread: Dict[str, Any], user=None):
|
|
470
|
-
return await self.update_async("thread", self.prepare_thread(thread, id, user=user))
|
|
362
|
+
return await self.db.update_async("thread", self.columns["thread"], self.prepare_thread(thread, id, user=user))
|
|
471
363
|
|
|
472
364
|
def delete_thread(self, id, user=None, callback=None):
|
|
473
365
|
sql_where, params = self.get_user_filter(user, {"id": id})
|
|
@@ -609,21 +501,21 @@ class AppDB:
|
|
|
609
501
|
|
|
610
502
|
def create_request(self, request: Dict[str, Any], user=None):
|
|
611
503
|
request["createdAt"] = request["updatedAt"] = datetime.now()
|
|
612
|
-
return self.insert("request", with_user(request, user=user))
|
|
504
|
+
return self.db.insert("request", self.columns["request"], with_user(request, user=user))
|
|
613
505
|
|
|
614
506
|
async def create_request_async(self, request: Dict[str, Any], user=None):
|
|
615
507
|
request["createdAt"] = request["updatedAt"] = datetime.now()
|
|
616
|
-
return await self.insert_async("request", with_user(request, user=user))
|
|
508
|
+
return await self.db.insert_async("request", self.columns["request"], with_user(request, user=user))
|
|
617
509
|
|
|
618
510
|
def update_request(self, id, request: Dict[str, Any], user=None):
|
|
619
511
|
request["id"] = id
|
|
620
512
|
request["updatedAt"] = datetime.now()
|
|
621
|
-
return self.update("request", with_user(request, user=user))
|
|
513
|
+
return self.db.update("request", self.columns["request"], with_user(request, user=user))
|
|
622
514
|
|
|
623
515
|
async def update_request_async(self, id, request: Dict[str, Any], user=None):
|
|
624
516
|
request["id"] = id
|
|
625
517
|
request["updatedAt"] = datetime.now()
|
|
626
|
-
return await self.update_async("request", with_user(request, user=user))
|
|
518
|
+
return await self.db.update_async("request", self.columns["request"], with_user(request, user=user))
|
|
627
519
|
|
|
628
520
|
def delete_request(self, id, user=None, callback=None):
|
|
629
521
|
sql_where, params = self.get_user_filter(user, {"id": id})
|
|
@@ -15,6 +15,7 @@ export const nextId = (() => {
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
const threads = ref([])
|
|
18
|
+
const threadDetails = ref({})
|
|
18
19
|
const currentThread = ref(null)
|
|
19
20
|
const isLoading = ref(false)
|
|
20
21
|
|
|
@@ -196,7 +197,7 @@ async function loadThreads() {
|
|
|
196
197
|
isLoading.value = true
|
|
197
198
|
|
|
198
199
|
try {
|
|
199
|
-
const api = await ext.getJson('/threads?take=
|
|
200
|
+
const api = await ext.getJson('/threads?take=30')
|
|
200
201
|
threads.value = api.response || []
|
|
201
202
|
return threads.value
|
|
202
203
|
} finally {
|
|
@@ -239,6 +240,7 @@ async function setCurrentThreadFromRoute(threadId, router) {
|
|
|
239
240
|
return null
|
|
240
241
|
}
|
|
241
242
|
|
|
243
|
+
loadThreadDetails(threadId)
|
|
242
244
|
const thread = setCurrentThread(threadId)
|
|
243
245
|
if (thread) {
|
|
244
246
|
return thread
|
|
@@ -307,7 +309,7 @@ function getLatestCachedThread() {
|
|
|
307
309
|
return threads.value[0]
|
|
308
310
|
}
|
|
309
311
|
|
|
310
|
-
async function startNewThread({ title, model, redirect }) {
|
|
312
|
+
async function startNewThread({ title, model, tools, redirect }) {
|
|
311
313
|
if (!model) {
|
|
312
314
|
console.error('No model selected')
|
|
313
315
|
return
|
|
@@ -330,6 +332,7 @@ async function startNewThread({ title, model, redirect }) {
|
|
|
330
332
|
title,
|
|
331
333
|
model: model.name,
|
|
332
334
|
info: ctx.utils.toModelInfo(model),
|
|
335
|
+
tools,
|
|
333
336
|
})
|
|
334
337
|
|
|
335
338
|
console.log('newThread', newThread, model)
|
|
@@ -361,6 +364,19 @@ async function queueChat(ctxRequest, options = {}) {
|
|
|
361
364
|
return api
|
|
362
365
|
}
|
|
363
366
|
|
|
367
|
+
async function loadThreadDetails(id, opt = null) {
|
|
368
|
+
if (!threadDetails.value[id] || opt?.force) {
|
|
369
|
+
const api = await ctx.getJson(`/ext/app/threads/${id}`)
|
|
370
|
+
if (api.response) {
|
|
371
|
+
threadDetails.value[id] = api.response
|
|
372
|
+
}
|
|
373
|
+
if (api.error) {
|
|
374
|
+
console.error(api.error)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return threadDetails.value[id]
|
|
378
|
+
}
|
|
379
|
+
|
|
364
380
|
// Export the store
|
|
365
381
|
export function useThreadStore() {
|
|
366
382
|
return {
|
|
@@ -388,6 +404,8 @@ export function useThreadStore() {
|
|
|
388
404
|
startNewThread,
|
|
389
405
|
replaceThread,
|
|
390
406
|
queueChat,
|
|
407
|
+
threadDetails,
|
|
408
|
+
loadThreadDetails,
|
|
391
409
|
isWatchingThread,
|
|
392
410
|
startWatchingThread,
|
|
393
411
|
stopWatchingThread,
|
|
@@ -102,6 +102,24 @@ def write_file(path: str, content: str) -> bool:
|
|
|
102
102
|
return True
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
def edit_file(path: str, old_str: str, new_str: str) -> Dict[str, Any]:
|
|
106
|
+
"""
|
|
107
|
+
Replaces first occurrence of old_str with new_str in file. If old_str is empty,
|
|
108
|
+
create/overwrite file with new_str.
|
|
109
|
+
:return: A dictionary with the path to the file and the action taken.
|
|
110
|
+
"""
|
|
111
|
+
safe_path = _resolve_safe_path(path)
|
|
112
|
+
if old_str == "":
|
|
113
|
+
safe_path.write_text(new_str, encoding="utf-8")
|
|
114
|
+
return {"path": str(safe_path), "action": "created_file"}
|
|
115
|
+
original = safe_path.read_text(encoding="utf-8")
|
|
116
|
+
if original.find(old_str) == -1:
|
|
117
|
+
return {"path": str(safe_path), "action": "old_str not found"}
|
|
118
|
+
edited = original.replace(old_str, new_str, 1)
|
|
119
|
+
safe_path.write_text(edited, encoding="utf-8")
|
|
120
|
+
return {"path": str(safe_path), "action": "edited"}
|
|
121
|
+
|
|
122
|
+
|
|
105
123
|
def list_directory(path: str) -> str:
|
|
106
124
|
"""List directory contents"""
|
|
107
125
|
safe_path = _resolve_safe_path(path)
|
|
@@ -526,6 +544,25 @@ def install(ctx):
|
|
|
526
544
|
# ctx.register_tool(semantic_search) # TODO: implement
|
|
527
545
|
ctx.register_tool(read_file)
|
|
528
546
|
ctx.register_tool(write_file)
|
|
547
|
+
ctx.register_tool(
|
|
548
|
+
edit_file,
|
|
549
|
+
{
|
|
550
|
+
"type": "function",
|
|
551
|
+
"function": {
|
|
552
|
+
"name": "edit_file",
|
|
553
|
+
"description": "Replaces first occurrence of old_str with new_str in file. If old_str is empty, create/overwrite file with new_str.",
|
|
554
|
+
"parameters": {
|
|
555
|
+
"type": "object",
|
|
556
|
+
"properties": {
|
|
557
|
+
"path": {"type": "string", "description": "Path to the file to edit."},
|
|
558
|
+
"old_str": {"type": "string", "description": "String to replace."},
|
|
559
|
+
"new_str": {"type": "string", "description": "String to replace with."},
|
|
560
|
+
},
|
|
561
|
+
"required": ["path", "old_str", "new_str"],
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
)
|
|
529
566
|
ctx.register_tool(list_directory)
|
|
530
567
|
ctx.register_tool(glob_paths)
|
|
531
568
|
ctx.register_tool(calc)
|
|
@@ -3,44 +3,46 @@ import os
|
|
|
3
3
|
|
|
4
4
|
from aiohttp import web
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
from .db import GalleryDB
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
from llms.extensions.gallery.db import GalleryDB
|
|
10
|
-
except ImportError as e:
|
|
11
|
-
print(f"Failed to import GalleryDB: {e}")
|
|
12
|
-
GalleryDB = None
|
|
8
|
+
g_db = None
|
|
13
9
|
|
|
14
10
|
|
|
15
11
|
def install(ctx):
|
|
16
|
-
def
|
|
12
|
+
def get_db():
|
|
17
13
|
global g_db
|
|
18
14
|
if g_db is None and GalleryDB:
|
|
19
15
|
try:
|
|
20
16
|
db_path = os.path.join(ctx.get_user_path(), "gallery", "gallery.sqlite")
|
|
21
17
|
g_db = GalleryDB(ctx, db_path)
|
|
18
|
+
ctx.register_shutdown_handler(g_db.db.close)
|
|
22
19
|
except Exception as e:
|
|
23
20
|
ctx.err("Failed to init GalleryDB", e)
|
|
24
21
|
return g_db
|
|
25
22
|
|
|
26
|
-
if not
|
|
23
|
+
if not get_db():
|
|
27
24
|
return
|
|
28
25
|
|
|
26
|
+
def media_dto(row):
|
|
27
|
+
return row and g_db.to_dto(row, ["reactions", "category", "tags", "ratings", "objects", "metadata"])
|
|
28
|
+
|
|
29
29
|
def on_cache_save(context):
|
|
30
|
-
url = context
|
|
31
|
-
info = context
|
|
30
|
+
url = context.get("url", None)
|
|
31
|
+
info = context.get("info", {})
|
|
32
|
+
user = context.get("user", None)
|
|
32
33
|
ctx.log(f"cache saved: {url}")
|
|
33
|
-
ctx.
|
|
34
|
+
ctx.dbg(json.dumps(info, indent=2))
|
|
34
35
|
|
|
35
36
|
if "url" not in info:
|
|
36
37
|
info["url"] = url
|
|
37
|
-
g_db.insert_media(info)
|
|
38
|
+
g_db.insert_media(info, user=user)
|
|
38
39
|
|
|
39
40
|
ctx.register_cache_saved_filter(on_cache_save)
|
|
40
41
|
|
|
41
42
|
async def query_media(request):
|
|
42
43
|
rows = g_db.query_media(request.query, user=ctx.get_username(request))
|
|
43
|
-
|
|
44
|
+
dtos = [media_dto(row) for row in rows]
|
|
45
|
+
return web.json_response(dtos)
|
|
44
46
|
|
|
45
47
|
ctx.add_get("media", query_media)
|
|
46
48
|
|