llms-py 3.0.0__py3-none-any.whl → 3.0.0b2__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/__pycache__/main.cpython-314.pyc +0 -0
- llms/index.html +37 -26
- llms/llms.json +21 -70
- llms/main.py +731 -1426
- llms/providers.json +1 -1
- llms/{extensions/analytics/ui/index.mjs → ui/Analytics.mjs} +238 -154
- llms/ui/App.mjs +63 -133
- llms/ui/Avatar.mjs +86 -0
- llms/ui/Brand.mjs +52 -0
- llms/ui/ChatPrompt.mjs +597 -0
- llms/ui/Main.mjs +862 -0
- llms/ui/OAuthSignIn.mjs +61 -0
- llms/ui/ProviderIcon.mjs +36 -0
- llms/ui/ProviderStatus.mjs +104 -0
- llms/{extensions/app/ui → ui}/Recents.mjs +57 -82
- llms/ui/{modules/chat/SettingsDialog.mjs → SettingsDialog.mjs} +9 -9
- llms/{extensions/app/ui/index.mjs → ui/Sidebar.mjs} +57 -122
- llms/ui/SignIn.mjs +65 -0
- llms/ui/Welcome.mjs +8 -0
- llms/ui/ai.mjs +13 -117
- llms/ui/app.css +49 -1776
- llms/ui/index.mjs +171 -87
- llms/ui/lib/charts.mjs +13 -9
- llms/ui/lib/servicestack-vue.mjs +3 -3
- llms/ui/lib/vue.min.mjs +9 -10
- llms/ui/lib/vue.mjs +1602 -1763
- llms/ui/markdown.mjs +2 -10
- llms/ui/model-selector.mjs +686 -0
- llms/ui/tailwind.input.css +1 -55
- llms/ui/threadStore.mjs +583 -0
- llms/ui/utils.mjs +118 -113
- llms/ui.json +1069 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/METADATA +1 -1
- llms_py-3.0.0b2.dist-info/RECORD +58 -0
- llms/extensions/app/README.md +0 -20
- llms/extensions/app/__init__.py +0 -530
- 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/db.py +0 -644
- llms/extensions/app/db_manager.py +0 -195
- llms/extensions/app/requests.json +0 -9073
- llms/extensions/app/threads.json +0 -15290
- llms/extensions/app/ui/threadStore.mjs +0 -411
- llms/extensions/core_tools/CALCULATOR.md +0 -32
- llms/extensions/core_tools/__init__.py +0 -598
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -201
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -185
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -101
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -160
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -66
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -27
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -72
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -119
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -98
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -225
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +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/core_tools/ui/codemirror/mode/clike/clike.js +0 -942
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -118
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -962
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -62
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -402
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -40
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -135
- llms/extensions/core_tools/ui/index.mjs +0 -650
- llms/extensions/gallery/README.md +0 -61
- llms/extensions/gallery/__init__.py +0 -61
- llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/gallery/db.py +0 -298
- llms/extensions/gallery/ui/index.mjs +0 -482
- llms/extensions/katex/README.md +0 -39
- llms/extensions/katex/__init__.py +0 -6
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/katex/ui/README.md +0 -125
- llms/extensions/katex/ui/contrib/auto-render.js +0 -338
- llms/extensions/katex/ui/contrib/auto-render.min.js +0 -1
- llms/extensions/katex/ui/contrib/auto-render.mjs +0 -244
- llms/extensions/katex/ui/contrib/copy-tex.js +0 -127
- llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -1
- llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -105
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -109
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -1
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -24
- llms/extensions/katex/ui/contrib/mhchem.js +0 -3213
- llms/extensions/katex/ui/contrib/mhchem.min.js +0 -1
- llms/extensions/katex/ui/contrib/mhchem.mjs +0 -3109
- llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -887
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -1
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -800
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms/extensions/katex/ui/index.mjs +0 -92
- llms/extensions/katex/ui/katex-swap.css +0 -1230
- llms/extensions/katex/ui/katex-swap.min.css +0 -1
- llms/extensions/katex/ui/katex.css +0 -1230
- llms/extensions/katex/ui/katex.js +0 -19080
- llms/extensions/katex/ui/katex.min.css +0 -1
- llms/extensions/katex/ui/katex.min.js +0 -1
- llms/extensions/katex/ui/katex.min.mjs +0 -1
- llms/extensions/katex/ui/katex.mjs +0 -18547
- llms/extensions/providers/__init__.py +0 -18
- 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/providers/anthropic.py +0 -229
- llms/extensions/providers/chutes.py +0 -155
- llms/extensions/providers/google.py +0 -378
- llms/extensions/providers/nvidia.py +0 -105
- llms/extensions/providers/openai.py +0 -156
- llms/extensions/providers/openrouter.py +0 -72
- llms/extensions/system_prompts/README.md +0 -22
- llms/extensions/system_prompts/__init__.py +0 -45
- llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/system_prompts/ui/index.mjs +0 -280
- llms/extensions/system_prompts/ui/prompts.json +0 -1067
- llms/extensions/tools/__init__.py +0 -5
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/ui/index.mjs +0 -204
- llms/providers-extra.json +0 -356
- llms/ui/ctx.mjs +0 -365
- llms/ui/modules/chat/ChatBody.mjs +0 -691
- llms/ui/modules/chat/index.mjs +0 -828
- llms/ui/modules/layout.mjs +0 -243
- llms/ui/modules/model-selector.mjs +0 -851
- llms_py-3.0.0.dist-info/RECORD +0 -202
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/WHEEL +0 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b2
|
|
4
4
|
Summary: A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers
|
|
5
5
|
Home-page: https://github.com/ServiceStack/llms
|
|
6
6
|
Author: ServiceStack
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
llms/__init__.py,sha256=DKwTZDsyYL_wHe7yvLw49Nf8PSgPSyWaeVdotUqSvrQ,84
|
|
2
|
+
llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
|
|
3
|
+
llms/index.html,sha256=K-kQuV8Qk-ZdD0iyWbLBGxuo7jY8dXLlSY-cXFHv84c,2079
|
|
4
|
+
llms/llms.json,sha256=e9CDUaABFuNxcsF6lOlKBTJcX3MKAGY3jED7CgCUZF0,9699
|
|
5
|
+
llms/main.py,sha256=Gl69g6oNo7tRohJxZgCK27qtelhNxnmj1Li_uEoadrE,130839
|
|
6
|
+
llms/providers.json,sha256=_0YEqStJhnXKjQuXssayj8CH12VFAuQWuvyrBQtCWr4,226852
|
|
7
|
+
llms/ui.json,sha256=iBOmpNeD5-o8AgUa51ymS-KemovJ7bm9J1fnL0nf8jk,134025
|
|
8
|
+
llms/__pycache__/__init__.cpython-312.pyc,sha256=lg2oFc0aKgj536NOJxcIpbCpEWi47ptF8NufPphgUUk,204
|
|
9
|
+
llms/__pycache__/__init__.cpython-313.pyc,sha256=DvmykIYAjV4Xjv5NeC70BA0JIX8qtwEWP2dfzmOBrps,173
|
|
10
|
+
llms/__pycache__/__init__.cpython-314.pyc,sha256=9CxeBz41D_-ezno6pIOsnoMAFzdRX2uZjKkBLUm25RQ,206
|
|
11
|
+
llms/__pycache__/__main__.cpython-312.pyc,sha256=fcu9LVl5nfdSzg9HqoKyDGoDvENeIXKkjygN7FENEGE,321
|
|
12
|
+
llms/__pycache__/__main__.cpython-314.pyc,sha256=IFxtGVpJq_3whKuM5Ln7YMweKFNbHVp8M7VSrLK5h2A,324
|
|
13
|
+
llms/__pycache__/llms.cpython-312.pyc,sha256=S5dFI79JdUe2dQW4ogdB-CCNhudQeFaFGcfKxgJGBms,72080
|
|
14
|
+
llms/__pycache__/main.cpython-312.pyc,sha256=HrqApYAiiBmYN73HIx_Hl2-Xm1Gy7I_0zuR5j86qoRM,104940
|
|
15
|
+
llms/__pycache__/main.cpython-313.pyc,sha256=6NQ__SJ2rC9ItFLKLHL5ewb5RqxLzZabwgczA9wZd-w,74814
|
|
16
|
+
llms/__pycache__/main.cpython-314.pyc,sha256=EZZmS1VitUA1zdiwfIYW-b4cpEsbrFESNdGJRcv3vHU,164343
|
|
17
|
+
llms/__pycache__/plugins.cpython-314.pyc,sha256=fer8nTkidG_vQSx80tL2bAvMS0opDom93bewjseFcyg,3560
|
|
18
|
+
llms/ui/Analytics.mjs,sha256=LfWbUlpb__0EEYtHu6e4r8AeyhsNQeAxrg44RuNSR0M,73261
|
|
19
|
+
llms/ui/App.mjs,sha256=JMxFtsJ03sZZYmHseCOG3mei0TyB5WSCsqlLlnGCLDU,4478
|
|
20
|
+
llms/ui/Avatar.mjs,sha256=VdTFpzD1ZpYW6WhcLjmOkEtMGJGgG4RaeLd8GUKpBcU,3416
|
|
21
|
+
llms/ui/Brand.mjs,sha256=JLN_lPirNXqS332g0B_WVOlFRVg3lNe1Q56TRnpj0zQ,3411
|
|
22
|
+
llms/ui/ChatPrompt.mjs,sha256=Bpvp9ANq_nnZBpmzMbOnYp_rPATla1Xa9qMZzMegXFk,26588
|
|
23
|
+
llms/ui/Main.mjs,sha256=KlIUA-tj7yBKl2rTyzbLn2xp2MLlowMWI3XtSM2UR6I,45258
|
|
24
|
+
llms/ui/OAuthSignIn.mjs,sha256=3WzJ4mhwHgN2ymkb5cmYHpJi961KgctwAbM_uJ7Cq8k,3504
|
|
25
|
+
llms/ui/ProviderIcon.mjs,sha256=XdEJRm90Zaeu7AlcxfLTXqh_jj2F5JvFkug0XexZeQc,22900
|
|
26
|
+
llms/ui/ProviderStatus.mjs,sha256=aeYN0Rr2oYpEKGAAkqmdp618tY504rRgTSPbOaLCSzU,6081
|
|
27
|
+
llms/ui/Recents.mjs,sha256=asQgduWj1eP0o1ft5CnlS85jDpP60mkUD4xtmQpTxmo,8902
|
|
28
|
+
llms/ui/SettingsDialog.mjs,sha256=vyqLOrACBICwT8qQ10oJAMOYeA1phrAyz93mZygn-9Y,19956
|
|
29
|
+
llms/ui/Sidebar.mjs,sha256=bIRafgZRpNzxZwTxElJ1hM-zry2oz3DMp_aX8VRK1rU,11211
|
|
30
|
+
llms/ui/SignIn.mjs,sha256=hJ-lCZtt4GfVBv8ylGsZ7CB1yJLcZVhHqEdfGLjKKN4,2340
|
|
31
|
+
llms/ui/Welcome.mjs,sha256=r9j7unF9CF3k7gEQBMRMVsa2oSjgHGNn46Oa5l5BwlY,950
|
|
32
|
+
llms/ui/ai.mjs,sha256=k4kqD33Rx-lHVOqmVlFmAyqV3pKA5SyCjnD5Pmr69yU,3346
|
|
33
|
+
llms/ui/app.css,sha256=Qavq-7xEfzKZ67-LDIsmVpNbgaRkmiBP0HOrMhXpA6M,118798
|
|
34
|
+
llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
|
|
35
|
+
llms/ui/index.mjs,sha256=O7DCCFQ0ozCkDIeTkp_Jk1lbwO_lxYm_jx_NClKA1zU,6353
|
|
36
|
+
llms/ui/markdown.mjs,sha256=0LUF2Tyvpv88ic-MjdUjAAdd1Rt8LYA_d7VRA-kBFuM,6518
|
|
37
|
+
llms/ui/model-selector.mjs,sha256=wREYMzM0ItfEVhNyKS16BA08jJCfo8MXWNoPY4mHrUI,35619
|
|
38
|
+
llms/ui/tailwind.input.css,sha256=5PiDdc5nyPE9Fheg_PgLnz3RR3C0o5x6M3M-KWVFo-4,14403
|
|
39
|
+
llms/ui/threadStore.mjs,sha256=r5KWZnQ3yf06cseFfwaIHFUrm190GhIrVEZ_vo8VHbM,16821
|
|
40
|
+
llms/ui/typography.css,sha256=6o7pbMIamRVlm2GfzSStpcOG4T5eFCK_WcQ3RIHKAsU,19587
|
|
41
|
+
llms/ui/utils.mjs,sha256=Lw2MSuN4qYSJsf0HwBOCgSI2i5o-hi_nEgFycd2DI0g,7340
|
|
42
|
+
llms/ui/lib/chart.js,sha256=dx8FdDX0Rv6OZtZjr9FQh5h-twFsKjfnb-FvFlQ--cU,196176
|
|
43
|
+
llms/ui/lib/charts.mjs,sha256=MNym9qE_2eoH6M7_8Gj9i6e6-Y3b7zw9UQWCUHRF6x0,1088
|
|
44
|
+
llms/ui/lib/color.js,sha256=DDG7Pr-qzJHTPISZNSqP_qJR8UflKHEc_56n6xrBugQ,8273
|
|
45
|
+
llms/ui/lib/highlight.min.mjs,sha256=sG7wq8bF-IKkfie7S4QSyh5DdHBRf0NqQxMOEH8-MT0,127458
|
|
46
|
+
llms/ui/lib/idb.min.mjs,sha256=CeTXyV4I_pB5vnibvJuyXdMs0iVF2ZL0Z7cdm3w_QaI,3853
|
|
47
|
+
llms/ui/lib/marked.min.mjs,sha256=QRHb_VZugcBJRD2EP6gYlVFEsJw5C2fQ8ImMf_pA2_s,39488
|
|
48
|
+
llms/ui/lib/servicestack-client.mjs,sha256=UVafVbzhJ_0N2lzv7rlzIbzwnWpoqXxGk3N3FSKgOOc,54534
|
|
49
|
+
llms/ui/lib/servicestack-vue.mjs,sha256=unTA8lM0tKy2PwZiJ8UEvrTuGmei8jNZnmmuQ5MKyV4,216753
|
|
50
|
+
llms/ui/lib/vue-router.min.mjs,sha256=fR30GHoXI1u81zyZ26YEU105pZgbbAKSXbpnzFKIxls,30418
|
|
51
|
+
llms/ui/lib/vue.min.mjs,sha256=iXh97m5hotl0eFllb3aoasQTImvp7mQoRJ_0HoxmZkw,163811
|
|
52
|
+
llms/ui/lib/vue.mjs,sha256=dS8LKOG01t9CvZ04i0tbFXHqFXOO_Ha4NmM3BytjQAs,537071
|
|
53
|
+
llms_py-3.0.0b2.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
|
|
54
|
+
llms_py-3.0.0b2.dist-info/METADATA,sha256=D8vEf8jkSvsAu3jOgJ-SIj8j3HsFqXjpJy5d2s_dT-o,2193
|
|
55
|
+
llms_py-3.0.0b2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
56
|
+
llms_py-3.0.0b2.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
|
|
57
|
+
llms_py-3.0.0b2.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
|
|
58
|
+
llms_py-3.0.0b2.dist-info/RECORD,,
|
llms/extensions/app/README.md
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# App Extension
|
|
2
|
-
|
|
3
|
-
This extension provides the core application logic and data persistence for the LLMs platform.
|
|
4
|
-
|
|
5
|
-
## Data Storage & Architecture
|
|
6
|
-
|
|
7
|
-
### Server-Side SQLite Migration
|
|
8
|
-
The application has migrated from client-side IndexedDB storage to a robust server-side SQLite solution. This architectural shift ensures better data consistency, improved performance, and enables multi-device access to your chat history.
|
|
9
|
-
|
|
10
|
-
### Asset Management
|
|
11
|
-
To keep the database efficient and portable, binary assets (images, audio, etc.) are not stored directly in the SQLite database. Instead:
|
|
12
|
-
- All generated assets are stored in the local file system cache at `~/.llms/cache`.
|
|
13
|
-
- The database stores only **relative URLs** pointing to these assets.
|
|
14
|
-
- This approach allows for efficient caching and serving of static media.
|
|
15
|
-
|
|
16
|
-
### Concurrency Model
|
|
17
|
-
To ensure data integrity and high performance without complex locking mechanisms, the system utilizes a **single background thread** for managing all write operations to the database. This design improves concurrency handling and eliminates database locking issues during high-load scenarios.
|
|
18
|
-
|
|
19
|
-
### Multi-Tenancy & Security
|
|
20
|
-
When authentication is enabled, data isolation is automatically enforced. All core tables, including `threads` and `requests`, are scoped to the authenticated user, ensuring that users can only access their own data.
|
llms/extensions/app/__init__.py
DELETED
|
@@ -1,530 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
import time
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from aiohttp import web
|
|
9
|
-
|
|
10
|
-
g_db = None
|
|
11
|
-
|
|
12
|
-
try:
|
|
13
|
-
from llms.extensions.app.db import AppDB
|
|
14
|
-
except ImportError as e:
|
|
15
|
-
print(f"Failed to import AppDB: {e}")
|
|
16
|
-
AppDB = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def install(ctx):
|
|
20
|
-
def get_db():
|
|
21
|
-
global g_db
|
|
22
|
-
if g_db is None and AppDB:
|
|
23
|
-
try:
|
|
24
|
-
db_path = os.path.join(ctx.get_user_path(), "app", "app.sqlite")
|
|
25
|
-
g_db = AppDB(ctx, db_path)
|
|
26
|
-
ctx.register_shutdown_handler(g_db.close)
|
|
27
|
-
|
|
28
|
-
threads_json = "/home/mythz/src/ServiceStack/llms/llms/extensions/app/threads.json"
|
|
29
|
-
requests_json = "/home/mythz/src/ServiceStack/llms/llms/extensions/app/requests.json"
|
|
30
|
-
with open(threads_json) as f:
|
|
31
|
-
threads = json.load(f)["threads"]
|
|
32
|
-
threads.reverse()
|
|
33
|
-
with open(requests_json) as f:
|
|
34
|
-
requests = json.load(f)["requests"]
|
|
35
|
-
requests.reverse()
|
|
36
|
-
# g_db.import_db(threads, requests)
|
|
37
|
-
|
|
38
|
-
except Exception as e:
|
|
39
|
-
ctx.err("Failed to init AppDB", e)
|
|
40
|
-
return g_db
|
|
41
|
-
|
|
42
|
-
if not get_db():
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
def to_dto(row, json_columns):
|
|
46
|
-
# as=column -> [0,1,2]
|
|
47
|
-
if not isinstance(row, dict):
|
|
48
|
-
return row
|
|
49
|
-
|
|
50
|
-
to = {}
|
|
51
|
-
for k, v in row.items():
|
|
52
|
-
if k in json_columns and v is not None and isinstance(v, str):
|
|
53
|
-
try:
|
|
54
|
-
to[k] = json.loads(v)
|
|
55
|
-
except Exception as e:
|
|
56
|
-
ctx.err(f"Failed to parse JSON for {k}: {v} ({type(v)})", e)
|
|
57
|
-
to[k] = v
|
|
58
|
-
else:
|
|
59
|
-
to[k] = v
|
|
60
|
-
return to
|
|
61
|
-
|
|
62
|
-
def thread_dto(row):
|
|
63
|
-
return row and to_dto(row, ["messages", "modalities", "args", "modelInfo", "stats", "metadata"])
|
|
64
|
-
|
|
65
|
-
def request_dto(row):
|
|
66
|
-
return row and to_dto(row, ["usage"])
|
|
67
|
-
|
|
68
|
-
def prompt_to_title(prompt):
|
|
69
|
-
return prompt[:100] + ("..." if len(prompt) > 100 else "") if prompt else None
|
|
70
|
-
|
|
71
|
-
def timestamp_messages(messages):
|
|
72
|
-
timestamp = int(time.time() * 1000)
|
|
73
|
-
for message in messages:
|
|
74
|
-
if "timestamp" not in message:
|
|
75
|
-
message["timestamp"] = timestamp
|
|
76
|
-
timestamp += 1 # make unique
|
|
77
|
-
return messages
|
|
78
|
-
|
|
79
|
-
async def query_threads(request):
|
|
80
|
-
rows = g_db.query_threads(request.query, user=ctx.get_username(request))
|
|
81
|
-
dtos = [thread_dto(row) for row in rows]
|
|
82
|
-
return web.json_response(dtos)
|
|
83
|
-
|
|
84
|
-
ctx.add_get("threads", query_threads)
|
|
85
|
-
|
|
86
|
-
async def create_thread(request):
|
|
87
|
-
thread = await request.json()
|
|
88
|
-
id = await g_db.create_thread_async(thread, user=ctx.get_username(request))
|
|
89
|
-
row = g_db.get_thread(id, user=ctx.get_username(request))
|
|
90
|
-
return web.json_response(thread_dto(row) if row else "")
|
|
91
|
-
|
|
92
|
-
ctx.add_post("threads", create_thread)
|
|
93
|
-
|
|
94
|
-
async def update_thread(request):
|
|
95
|
-
thread = await request.json()
|
|
96
|
-
id = request.match_info["id"]
|
|
97
|
-
update_count = await g_db.update_thread_async(id, thread, user=ctx.get_username(request))
|
|
98
|
-
if update_count == 0:
|
|
99
|
-
raise Exception("Thread not found")
|
|
100
|
-
row = g_db.get_thread(id, user=ctx.get_username(request))
|
|
101
|
-
return web.json_response(thread_dto(row) if row else "")
|
|
102
|
-
|
|
103
|
-
ctx.add_patch("threads/{id}", update_thread)
|
|
104
|
-
|
|
105
|
-
async def delete_thread(request):
|
|
106
|
-
id = request.match_info["id"]
|
|
107
|
-
g_db.delete_thread(id, user=ctx.get_username(request))
|
|
108
|
-
return web.json_response({})
|
|
109
|
-
|
|
110
|
-
ctx.add_delete("threads/{id}", delete_thread)
|
|
111
|
-
|
|
112
|
-
async def queue_chat_handler(request):
|
|
113
|
-
# Check authentication if enabled
|
|
114
|
-
is_authenticated, user_data = ctx.check_auth(request)
|
|
115
|
-
if not is_authenticated:
|
|
116
|
-
return web.json_response(ctx.error_auth_required, status=401)
|
|
117
|
-
|
|
118
|
-
if not request.body_exists:
|
|
119
|
-
raise Exception("messages required")
|
|
120
|
-
|
|
121
|
-
chat = await request.json()
|
|
122
|
-
|
|
123
|
-
messages = timestamp_messages(chat.get("messages", []))
|
|
124
|
-
if len(messages) == 0:
|
|
125
|
-
raise Exception("messages required")
|
|
126
|
-
|
|
127
|
-
id = request.match_info["id"]
|
|
128
|
-
thread = thread_dto(g_db.get_thread(id, user=ctx.get_username(request)))
|
|
129
|
-
if not thread:
|
|
130
|
-
raise Exception("Thread not found")
|
|
131
|
-
|
|
132
|
-
update_thread = {
|
|
133
|
-
"messages": messages,
|
|
134
|
-
"startedAt": datetime.now(),
|
|
135
|
-
"completedAt": None,
|
|
136
|
-
"error": None,
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
model = chat.get("model", None)
|
|
140
|
-
if model:
|
|
141
|
-
update_thread["model"] = model
|
|
142
|
-
metadata = chat.get("metadata", {})
|
|
143
|
-
if len(metadata) > 0:
|
|
144
|
-
update_thread["metadata"] = metadata
|
|
145
|
-
if chat.get("modalities") or not thread.get("modalities"):
|
|
146
|
-
update_thread["modalities"] = chat.get("modalities", ["text"])
|
|
147
|
-
system_prompt = ctx.chat_to_system_prompt(chat)
|
|
148
|
-
if system_prompt:
|
|
149
|
-
update_thread["systemPrompt"] = system_prompt
|
|
150
|
-
|
|
151
|
-
args = thread.get("args") or {}
|
|
152
|
-
for k, v in chat.items():
|
|
153
|
-
if k in ctx.request_args:
|
|
154
|
-
args[k] = v
|
|
155
|
-
update_thread["args"] = args
|
|
156
|
-
|
|
157
|
-
# allow chat to override thread title
|
|
158
|
-
title = chat.get("title")
|
|
159
|
-
if title:
|
|
160
|
-
update_thread["title"] = title
|
|
161
|
-
else:
|
|
162
|
-
# only update thread title if it's not already set
|
|
163
|
-
title = thread.get("title")
|
|
164
|
-
if not title:
|
|
165
|
-
update_thread["title"] = title = prompt_to_title(ctx.last_user_prompt(chat))
|
|
166
|
-
|
|
167
|
-
user = ctx.get_username(request)
|
|
168
|
-
await g_db.update_thread_async(
|
|
169
|
-
id,
|
|
170
|
-
update_thread,
|
|
171
|
-
user=user,
|
|
172
|
-
)
|
|
173
|
-
thread = thread_dto(g_db.get_thread(id, user=user))
|
|
174
|
-
if not thread:
|
|
175
|
-
raise Exception("Thread not found")
|
|
176
|
-
|
|
177
|
-
metadata = thread.get("metadata", {})
|
|
178
|
-
chat = {
|
|
179
|
-
"model": thread.get("model"),
|
|
180
|
-
"messages": thread.get("messages"),
|
|
181
|
-
"modalities": thread.get("modalities"),
|
|
182
|
-
"systemPrompt": thread.get("systemPrompt"),
|
|
183
|
-
"metadata": metadata,
|
|
184
|
-
}
|
|
185
|
-
for k, v in thread.get("args", {}).items():
|
|
186
|
-
if k in ctx.request_args:
|
|
187
|
-
chat[k] = v
|
|
188
|
-
|
|
189
|
-
context = {
|
|
190
|
-
"chat": chat,
|
|
191
|
-
"user": user,
|
|
192
|
-
"threadId": id,
|
|
193
|
-
"metadata": metadata,
|
|
194
|
-
"tools": metadata.get("tools", "all"),
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
# execute chat in background thread
|
|
198
|
-
async def run_chat(chat_req, context_req):
|
|
199
|
-
try:
|
|
200
|
-
await ctx.chat_completion(chat_req, context=context_req)
|
|
201
|
-
except Exception as ex:
|
|
202
|
-
ctx.err("run_chat", ex)
|
|
203
|
-
# not necessary to update thread in db with error as it's done in chat_error filter
|
|
204
|
-
|
|
205
|
-
asyncio.create_task(run_chat(chat, context))
|
|
206
|
-
|
|
207
|
-
return web.json_response(thread_dto(thread))
|
|
208
|
-
|
|
209
|
-
ctx.add_post("threads/{id}/chat", queue_chat_handler)
|
|
210
|
-
|
|
211
|
-
async def get_thread_updates(request):
|
|
212
|
-
id = request.match_info["id"]
|
|
213
|
-
after = request.query.get("after", None)
|
|
214
|
-
user = ctx.get_username(request)
|
|
215
|
-
thread = g_db.get_thread(id, user=user)
|
|
216
|
-
if not thread:
|
|
217
|
-
raise Exception("Thread not found")
|
|
218
|
-
if after:
|
|
219
|
-
started = time.time()
|
|
220
|
-
thread_id = thread.get("id")
|
|
221
|
-
thread_updated_at = thread.get("updatedAt")
|
|
222
|
-
|
|
223
|
-
while thread_updated_at <= after:
|
|
224
|
-
thread_updated_at = g_db.get_thread_column(thread_id, "updatedAt", user=user)
|
|
225
|
-
# if thread is not updated in 30 seconds, break
|
|
226
|
-
if time.time() - started > 10:
|
|
227
|
-
break
|
|
228
|
-
await asyncio.sleep(1)
|
|
229
|
-
ctx.dbg(f"get_thread_updates: {thread_id} / {thread_updated_at} < {after} / {thread_updated_at < after}")
|
|
230
|
-
thread = g_db.get_thread(thread_id, user=user)
|
|
231
|
-
return web.json_response(thread_dto(thread))
|
|
232
|
-
|
|
233
|
-
ctx.add_get("threads/{id}/updates", get_thread_updates)
|
|
234
|
-
|
|
235
|
-
async def cancel_thread(request):
|
|
236
|
-
id = request.match_info["id"]
|
|
237
|
-
await g_db.update_thread_async(
|
|
238
|
-
id, {"completedAt": datetime.now(), "error": "Request was canceled"}, user=ctx.get_username(request)
|
|
239
|
-
)
|
|
240
|
-
thread = g_db.get_thread(id, user=ctx.get_username(request))
|
|
241
|
-
ctx.dbg(f"cancel_thread: {id} / {thread.get('error')} / {thread.get('completedAt')}")
|
|
242
|
-
return web.json_response(thread_dto(thread))
|
|
243
|
-
|
|
244
|
-
ctx.add_post("threads/{id}/cancel", cancel_thread)
|
|
245
|
-
|
|
246
|
-
async def query_requests(request):
|
|
247
|
-
rows = g_db.query_requests(request.query, user=ctx.get_username(request))
|
|
248
|
-
dtos = [request_dto(row) for row in rows]
|
|
249
|
-
return web.json_response(dtos)
|
|
250
|
-
|
|
251
|
-
ctx.add_get("requests", query_requests)
|
|
252
|
-
|
|
253
|
-
async def delete_request(request):
|
|
254
|
-
id = request.match_info["id"]
|
|
255
|
-
g_db.delete_request(id, user=ctx.get_username(request))
|
|
256
|
-
return web.json_response({})
|
|
257
|
-
|
|
258
|
-
ctx.add_delete("requests/{id}", delete_request)
|
|
259
|
-
|
|
260
|
-
async def requests_summary(request):
|
|
261
|
-
rows = g_db.get_request_summary(user=ctx.get_username(request))
|
|
262
|
-
stats = {
|
|
263
|
-
"dailyData": {},
|
|
264
|
-
"years": [],
|
|
265
|
-
"totalCost": 0,
|
|
266
|
-
"totalRequests": 0,
|
|
267
|
-
"totalInputTokens": 0,
|
|
268
|
-
"totalOutputTokens": 0,
|
|
269
|
-
}
|
|
270
|
-
years = set()
|
|
271
|
-
for row in rows:
|
|
272
|
-
date = row["date"]
|
|
273
|
-
year = int(date[:4])
|
|
274
|
-
years.add(year)
|
|
275
|
-
stats["dailyData"][date] = {
|
|
276
|
-
"cost": row["cost"],
|
|
277
|
-
"requests": row["requests"],
|
|
278
|
-
"inputTokens": row["inputTokens"],
|
|
279
|
-
"outputTokens": row["outputTokens"],
|
|
280
|
-
}
|
|
281
|
-
stats["totalCost"] += row["cost"] or 0
|
|
282
|
-
stats["totalRequests"] += row["requests"] or 0
|
|
283
|
-
stats["totalInputTokens"] += row["inputTokens"] or 0
|
|
284
|
-
stats["totalOutputTokens"] += row["outputTokens"] or 0
|
|
285
|
-
|
|
286
|
-
stats["years"] = sorted(years)
|
|
287
|
-
return web.json_response(stats)
|
|
288
|
-
|
|
289
|
-
ctx.add_get("requests/summary", requests_summary)
|
|
290
|
-
|
|
291
|
-
async def daily_requests_summary(request):
|
|
292
|
-
day = request.match_info["day"]
|
|
293
|
-
summary = g_db.get_daily_request_summary(day, user=ctx.get_username(request))
|
|
294
|
-
return web.json_response(summary)
|
|
295
|
-
|
|
296
|
-
ctx.add_get("requests/summary/{day}", daily_requests_summary)
|
|
297
|
-
|
|
298
|
-
async def chat_request(openai_request, context):
|
|
299
|
-
chat = openai_request
|
|
300
|
-
user = context.get("user", None)
|
|
301
|
-
provider = context.get("provider", None)
|
|
302
|
-
thread_id = context.get("threadId", None)
|
|
303
|
-
model_info = context.get("modelInfo", None)
|
|
304
|
-
|
|
305
|
-
metadata = chat.get("metadata", {})
|
|
306
|
-
model = chat.get("model", None)
|
|
307
|
-
messages = timestamp_messages(chat.get("messages", []))
|
|
308
|
-
title = context.get("title") or prompt_to_title(ctx.last_user_prompt(chat) if chat else None)
|
|
309
|
-
started_at = context.get("startedAt")
|
|
310
|
-
if not started_at:
|
|
311
|
-
context["startedAt"] = started_at = datetime.now()
|
|
312
|
-
if thread_id is None:
|
|
313
|
-
thread = {
|
|
314
|
-
"user": user,
|
|
315
|
-
"model": model,
|
|
316
|
-
"provider": provider,
|
|
317
|
-
"modelInfo": model_info,
|
|
318
|
-
"title": title,
|
|
319
|
-
"messages": messages,
|
|
320
|
-
"systemPrompt": ctx.chat_to_system_prompt(chat),
|
|
321
|
-
"modalities": chat.get("modalities", ["text"]),
|
|
322
|
-
"startedAt": started_at,
|
|
323
|
-
"metadata": metadata,
|
|
324
|
-
}
|
|
325
|
-
thread_id = await g_db.create_thread_async(thread, user=user)
|
|
326
|
-
context["threadId"] = thread_id
|
|
327
|
-
else:
|
|
328
|
-
update_thread = {
|
|
329
|
-
"model": model,
|
|
330
|
-
"provider": provider,
|
|
331
|
-
"modelInfo": model_info,
|
|
332
|
-
"startedAt": started_at,
|
|
333
|
-
"messages": messages,
|
|
334
|
-
"completedAt": None,
|
|
335
|
-
"error": None,
|
|
336
|
-
"metadata": metadata,
|
|
337
|
-
}
|
|
338
|
-
await g_db.update_thread_async(thread_id, update_thread, user=user)
|
|
339
|
-
|
|
340
|
-
completed_at = g_db.get_thread_column(thread_id, "completedAt", user=user)
|
|
341
|
-
if completed_at:
|
|
342
|
-
context["completed"] = True
|
|
343
|
-
|
|
344
|
-
ctx.register_chat_request_filter(chat_request)
|
|
345
|
-
|
|
346
|
-
async def tool_request(chat_request, context):
|
|
347
|
-
messages = chat_request.get("messages", [])
|
|
348
|
-
ctx.dbg(f"tool_request: messages {len(messages)}")
|
|
349
|
-
thread_id = context.get("threadId", None)
|
|
350
|
-
if not thread_id:
|
|
351
|
-
ctx.dbg("Missing threadId")
|
|
352
|
-
return
|
|
353
|
-
user = context.get("user", None)
|
|
354
|
-
await g_db.update_thread_async(
|
|
355
|
-
thread_id,
|
|
356
|
-
{
|
|
357
|
-
"messages": messages,
|
|
358
|
-
},
|
|
359
|
-
user=user,
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
completed_at = g_db.get_thread_column(thread_id, "completedAt", user=user)
|
|
363
|
-
if completed_at:
|
|
364
|
-
context["completed"] = True
|
|
365
|
-
|
|
366
|
-
ctx.register_chat_tool_filter(tool_request)
|
|
367
|
-
|
|
368
|
-
async def chat_response(openai_response, context):
|
|
369
|
-
ctx.dbg("create_response")
|
|
370
|
-
o = openai_response
|
|
371
|
-
chat = context.get("chat")
|
|
372
|
-
usage = o.get("usage", None)
|
|
373
|
-
if not usage and not chat:
|
|
374
|
-
ctx.dbg("Missing chat and usage")
|
|
375
|
-
return
|
|
376
|
-
|
|
377
|
-
user = context.get("user", None)
|
|
378
|
-
thread_id = context.get("threadId", None)
|
|
379
|
-
provider = context.get("provider", None)
|
|
380
|
-
model_info = context.get("modelInfo", None)
|
|
381
|
-
model_cost = context.get("modelCost", model_info.get("cost", None)) or {"input": 0, "output": 0}
|
|
382
|
-
duration = context.get("duration", 0)
|
|
383
|
-
|
|
384
|
-
metadata = o.get("metadata", {})
|
|
385
|
-
choices = o.get("choices", [])
|
|
386
|
-
tasks = []
|
|
387
|
-
title = context.get("title") or prompt_to_title(ctx.last_user_prompt(chat) if chat else None)
|
|
388
|
-
completed_at = datetime.now()
|
|
389
|
-
|
|
390
|
-
model = model_info.get("name") or model_info.get("id")
|
|
391
|
-
finish_reason = choices[0].get("finish_reason", None) if len(choices) > 0 else None
|
|
392
|
-
input_price = model_cost.get("input", 0)
|
|
393
|
-
output_price = model_cost.get("output", 0)
|
|
394
|
-
input_tokens = usage.get("prompt_tokens", 0)
|
|
395
|
-
output_tokens = usage.get("completion_tokens", 0)
|
|
396
|
-
total_tokens = usage.get("total_tokens", input_tokens + output_tokens)
|
|
397
|
-
cost = o.get("cost", ((input_price * input_tokens) + (output_price * output_tokens)) / 1000000)
|
|
398
|
-
|
|
399
|
-
request = {
|
|
400
|
-
"user": user,
|
|
401
|
-
"model": model,
|
|
402
|
-
"duration": duration,
|
|
403
|
-
"cost": cost,
|
|
404
|
-
"inputPrice": input_price,
|
|
405
|
-
"inputTokens": input_tokens,
|
|
406
|
-
"inputCachedTokens": usage.get("inputCachedTokens", 0),
|
|
407
|
-
"outputPrice": output_price,
|
|
408
|
-
"outputTokens": output_tokens,
|
|
409
|
-
"finishReason": finish_reason,
|
|
410
|
-
"provider": provider,
|
|
411
|
-
"providerModel": o.get("model", None),
|
|
412
|
-
"providerRef": o.get("provider", None),
|
|
413
|
-
"threadId": thread_id,
|
|
414
|
-
"title": title,
|
|
415
|
-
"startedAt": context.get("startedAt"),
|
|
416
|
-
"totalTokens": total_tokens,
|
|
417
|
-
"usage": usage,
|
|
418
|
-
"completedAt": completed_at,
|
|
419
|
-
"toolHistory": o.get("tool_history", None),
|
|
420
|
-
"ref": o.get("id", None),
|
|
421
|
-
}
|
|
422
|
-
tasks.append(g_db.create_request_async(request, user=user))
|
|
423
|
-
|
|
424
|
-
if thread_id:
|
|
425
|
-
messages = chat.get("messages", [])
|
|
426
|
-
last_role = messages[-1].get("role", None) if len(messages) > 0 else None
|
|
427
|
-
if last_role == "user" or last_role == "tool":
|
|
428
|
-
user_message = messages[-1]
|
|
429
|
-
user_message["model"] = model
|
|
430
|
-
user_message["usage"] = {
|
|
431
|
-
"tokens": input_tokens,
|
|
432
|
-
"price": input_price,
|
|
433
|
-
"cost": (input_price * input_tokens) / 1000000,
|
|
434
|
-
}
|
|
435
|
-
else:
|
|
436
|
-
ctx.dbg(
|
|
437
|
-
f"Missing user message for thread {thread_id}, {len(messages)} messages, last role: {last_role}"
|
|
438
|
-
)
|
|
439
|
-
assistant_message = ctx.chat_response_to_message(o)
|
|
440
|
-
assistant_message["model"] = model
|
|
441
|
-
assistant_message["usage"] = {
|
|
442
|
-
"tokens": output_tokens,
|
|
443
|
-
"price": output_price,
|
|
444
|
-
"cost": (output_price * output_tokens) / 1000000,
|
|
445
|
-
"duration": duration,
|
|
446
|
-
}
|
|
447
|
-
messages.append(assistant_message)
|
|
448
|
-
|
|
449
|
-
update_thread = {
|
|
450
|
-
"model": model,
|
|
451
|
-
"providerModel": o.get("model"),
|
|
452
|
-
"modelInfo": model_info,
|
|
453
|
-
"messages": messages,
|
|
454
|
-
"completedAt": completed_at,
|
|
455
|
-
}
|
|
456
|
-
if "error" in metadata:
|
|
457
|
-
update_thread["error"] = metadata["error"]
|
|
458
|
-
tasks.append(g_db.update_thread_async(thread_id, update_thread, user=user))
|
|
459
|
-
else:
|
|
460
|
-
ctx.dbg("Missing thread_id")
|
|
461
|
-
|
|
462
|
-
await asyncio.gather(*tasks)
|
|
463
|
-
|
|
464
|
-
# Update thread costs from all thread requests
|
|
465
|
-
thread_requests = g_db.query_requests({"threadId": thread_id}, user=user)
|
|
466
|
-
total_costs = 0
|
|
467
|
-
total_input = 0
|
|
468
|
-
total_output = 0
|
|
469
|
-
for request in thread_requests:
|
|
470
|
-
total_costs += request.get("cost", 0) or 0
|
|
471
|
-
total_input += request.get("inputTokens", 0) or 0
|
|
472
|
-
total_output += request.get("outputTokens", 0) or 0
|
|
473
|
-
stats = {
|
|
474
|
-
"inputTokens": total_input,
|
|
475
|
-
"outputTokens": total_output,
|
|
476
|
-
"cost": total_costs,
|
|
477
|
-
"duration": duration,
|
|
478
|
-
"requests": len(thread_requests),
|
|
479
|
-
}
|
|
480
|
-
g_db.update_thread(
|
|
481
|
-
thread_id,
|
|
482
|
-
{
|
|
483
|
-
"inputTokens": total_input,
|
|
484
|
-
"outputTokens": total_output,
|
|
485
|
-
"cost": total_costs,
|
|
486
|
-
"stats": stats,
|
|
487
|
-
},
|
|
488
|
-
user=user,
|
|
489
|
-
)
|
|
490
|
-
|
|
491
|
-
ctx.register_chat_response_filter(chat_response)
|
|
492
|
-
|
|
493
|
-
async def chat_error(e: Exception, context: Any):
|
|
494
|
-
error = ctx.error_message(e)
|
|
495
|
-
ctx.dbg(f"Chat error: {error}")
|
|
496
|
-
chat = context.get("chat")
|
|
497
|
-
if not chat:
|
|
498
|
-
ctx.dbg("Missing chat")
|
|
499
|
-
return
|
|
500
|
-
|
|
501
|
-
title = context.get("title") or prompt_to_title(ctx.last_user_prompt(chat) if chat else None)
|
|
502
|
-
completed_at = datetime.now()
|
|
503
|
-
user = context.get("user", None)
|
|
504
|
-
|
|
505
|
-
thread_id = context.get("threadId", None)
|
|
506
|
-
tasks = []
|
|
507
|
-
if thread_id:
|
|
508
|
-
tasks.append(g_db.update_thread_async(thread_id, {"completedAt": completed_at, "error": error}, user=user))
|
|
509
|
-
else:
|
|
510
|
-
ctx.dbg("Missing threadId")
|
|
511
|
-
|
|
512
|
-
request = {
|
|
513
|
-
"user": user,
|
|
514
|
-
"model": chat.get("model", None),
|
|
515
|
-
"title": title,
|
|
516
|
-
"threadId": thread_id,
|
|
517
|
-
"startedAt": context.get("startedAt"),
|
|
518
|
-
"completedAt": completed_at,
|
|
519
|
-
"error": error,
|
|
520
|
-
"stackTrace": context.get("stackTrace", None),
|
|
521
|
-
}
|
|
522
|
-
tasks.append(g_db.create_request_async(request, user=user))
|
|
523
|
-
|
|
524
|
-
if len(tasks) > 0:
|
|
525
|
-
await asyncio.gather(*tasks)
|
|
526
|
-
|
|
527
|
-
ctx.register_chat_error_filter(chat_error)
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
__install__ = install
|
|
Binary file
|
|
Binary file
|
|
Binary file
|