llms-py 3.0.4__py3-none-any.whl → 3.0.6__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/db.py CHANGED
@@ -5,7 +5,7 @@ import threading
5
5
  from queue import Empty, Queue
6
6
  from threading import Event, Thread
7
7
 
8
- POOL = os.getenv("LLMS_POOL", "1") == "1"
8
+ POOL = os.getenv("LLMS_POOL", "0") == "1"
9
9
 
10
10
 
11
11
  def create_reader_connection(db_path):
llms/llms.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "User-Agent": "llmspy.org/3.0"
17
17
  },
18
18
  "text": {
19
- "model": "kimi-k2",
19
+ "model": "Kimi K2 Instruct",
20
20
  "messages": [
21
21
  {
22
22
  "role": "user",
llms/main.py CHANGED
@@ -41,7 +41,7 @@ try:
41
41
  except ImportError:
42
42
  HAS_PIL = False
43
43
 
44
- VERSION = "3.0.4"
44
+ VERSION = "3.0.6"
45
45
  _ROOT = None
46
46
  DEBUG = os.getenv("DEBUG") == "1"
47
47
  MOCK = os.getenv("MOCK") == "1"
@@ -1829,7 +1829,10 @@ def print_status():
1829
1829
 
1830
1830
 
1831
1831
  def home_llms_path(filename):
1832
- return f"{os.getenv('HOME')}/.llms/{filename}"
1832
+ home_dir = os.getenv("LLMS_HOME", os.path.join(os.getenv("HOME"), ".llms"))
1833
+ relative_path = os.path.join(home_dir, filename)
1834
+ # return resolved full absolute path
1835
+ return os.path.abspath(os.path.normpath(relative_path))
1833
1836
 
1834
1837
 
1835
1838
  def get_cache_path(path=""):
@@ -2703,7 +2706,7 @@ class ExtensionContext:
2703
2706
 
2704
2707
 
2705
2708
  def get_extensions_path():
2706
- return os.getenv("LLMS_EXTENSIONS_DIR", os.path.join(Path.home(), ".llms", "extensions"))
2709
+ return os.getenv("LLMS_EXTENSIONS_DIR", home_llms_path("extensions"))
2707
2710
 
2708
2711
 
2709
2712
  def get_disabled_extensions():
@@ -2731,6 +2734,11 @@ def get_extensions_dirs():
2731
2734
  disabled_extensions = get_disabled_extensions()
2732
2735
 
2733
2736
  builtin_extensions_dir = _ROOT / "extensions"
2737
+ if not os.path.exists(builtin_extensions_dir):
2738
+ # look for local ./extensions dir from script
2739
+ builtin_extensions_dir = os.path.join(os.path.dirname(__file__), "extensions")
2740
+
2741
+ _dbg(f"Loading extensions from {builtin_extensions_dir}")
2734
2742
  if os.path.exists(builtin_extensions_dir):
2735
2743
  for item in os.listdir(builtin_extensions_dir):
2736
2744
  if os.path.isdir(os.path.join(builtin_extensions_dir, item)):
@@ -3463,8 +3471,10 @@ def main():
3463
3471
  cache_root = Path(get_cache_path())
3464
3472
  requested_path = Path(info_path).resolve()
3465
3473
  if not str(requested_path).startswith(str(cache_root)):
3474
+ _dbg(f"Forbidden: {requested_path} is not in {cache_root}")
3466
3475
  return web.Response(text="403: Forbidden", status=403)
3467
- except Exception:
3476
+ except Exception as e:
3477
+ _err(f"Forbidden: {requested_path} is not in {cache_root}", e)
3468
3478
  return web.Response(text="403: Forbidden", status=403)
3469
3479
 
3470
3480
  with open(info_path) as f:
@@ -3479,8 +3489,10 @@ def main():
3479
3489
  cache_root = Path(get_cache_path())
3480
3490
  requested_path = Path(full_path).resolve()
3481
3491
  if not str(requested_path).startswith(str(cache_root)):
3492
+ _dbg(f"Forbidden: {requested_path} is not in {cache_root}")
3482
3493
  return web.Response(text="403: Forbidden", status=403)
3483
3494
  except Exception:
3495
+ _err(f"Forbidden: {requested_path} is not in {cache_root}", e)
3484
3496
  return web.Response(text="403: Forbidden", status=403)
3485
3497
 
3486
3498
  mimetype = get_file_mime_type(full_path)
llms/ui/ai.mjs CHANGED
@@ -6,7 +6,7 @@ const headers = { 'Accept': 'application/json' }
6
6
  const prefsKey = 'llms.prefs'
7
7
 
8
8
  export const o = {
9
- version: '3.0.4',
9
+ version: '3.0.6',
10
10
  base,
11
11
  prefsKey,
12
12
  welcome: 'Welcome to llms.py',
@@ -115,6 +115,7 @@ export function addCopyButtons() {
115
115
 
116
116
  export function useChatPrompt(ctx) {
117
117
  const messageText = ref('')
118
+ const promptHistory = ref([])
118
119
  const attachedFiles = ref([])
119
120
  const hasImage = () => attachedFiles.value.some(f => imageExts.includes(lastRightPart(f.name, '.')))
120
121
  const hasAudio = () => attachedFiles.value.some(f => audioExts.includes(lastRightPart(f.name, '.')))
@@ -363,6 +364,7 @@ export function useChatPrompt(ctx) {
363
364
  createContent,
364
365
  createRequest,
365
366
  applySettings,
367
+ promptHistory,
366
368
  messageText,
367
369
  attachedFiles,
368
370
  editingMessage,
@@ -420,6 +422,7 @@ const ChatPrompt = {
420
422
  <textarea
421
423
  ref="refMessage"
422
424
  v-model="messageText"
425
+ @keydown="onKeyDown"
423
426
  @keydown.enter.exact.prevent="sendMessage"
424
427
  @keydown.enter.shift.exact="addNewLine"
425
428
  @paste="onPaste"
@@ -491,6 +494,7 @@ const ChatPrompt = {
491
494
  const config = ctx.state.config
492
495
  const {
493
496
  messageText,
497
+ promptHistory,
494
498
  hasImage,
495
499
  hasAudio,
496
500
  hasFile,
@@ -500,6 +504,8 @@ const ChatPrompt = {
500
504
  const fileInput = ref(null)
501
505
  const refMessage = ref(null)
502
506
  const showSettings = ref(false)
507
+ const historyIndex = ref(-1)
508
+ const isNavigatingHistory = ref(false)
503
509
 
504
510
  // File attachments (+) handlers
505
511
  const triggerFilePicker = () => {
@@ -639,6 +645,14 @@ const ChatPrompt = {
639
645
  // 1. Construct Structured Content (Text + Attachments)
640
646
  let text = messageText.value.trim()
641
647
 
648
+ if (text) {
649
+ const idx = promptHistory.value.indexOf(text)
650
+ if (idx !== -1) {
651
+ promptHistory.value.splice(idx, 1)
652
+ }
653
+ promptHistory.value.push(text)
654
+ }
655
+
642
656
  messageText.value = ''
643
657
  let content = ctx.chat.createContent({ text, files: ctx.chat.attachedFiles.value })
644
658
 
@@ -742,6 +756,49 @@ const ChatPrompt = {
742
756
  //messageText.value += '\n'
743
757
  }
744
758
 
759
+ const onKeyDown = (e) => {
760
+ if (e.key === 'ArrowUp') {
761
+ if (refMessage.value.selectionStart === 0 && refMessage.value.selectionEnd === 0) {
762
+ if (promptHistory.value.length > 0) {
763
+ e.preventDefault()
764
+ if (historyIndex.value === -1) {
765
+ historyIndex.value = promptHistory.value.length - 1
766
+ } else {
767
+ historyIndex.value = Math.max(0, historyIndex.value - 1)
768
+ }
769
+ isNavigatingHistory.value = true
770
+ messageText.value = promptHistory.value[historyIndex.value]
771
+ nextTick(() => {
772
+ refMessage.value.setSelectionRange(0, 0)
773
+ })
774
+ }
775
+ }
776
+ } else if (e.key === 'ArrowDown') {
777
+ if (historyIndex.value !== -1) {
778
+ e.preventDefault()
779
+ if (historyIndex.value < promptHistory.value.length - 1) {
780
+ historyIndex.value++
781
+ isNavigatingHistory.value = true
782
+ messageText.value = promptHistory.value[historyIndex.value]
783
+ } else {
784
+ historyIndex.value = -1
785
+ isNavigatingHistory.value = true
786
+ messageText.value = ''
787
+ }
788
+ nextTick(() => {
789
+ refMessage.value.setSelectionRange(0, 0)
790
+ })
791
+ }
792
+ }
793
+ }
794
+
795
+ watch(messageText, (newValue) => {
796
+ if (!isNavigatingHistory.value) {
797
+ historyIndex.value = -1
798
+ }
799
+ isNavigatingHistory.value = false
800
+ })
801
+
745
802
  watch(() => ctx.state.selectedAspectRatio, newValue => {
746
803
  ctx.setPrefs({ aspectRatio: newValue })
747
804
  })
@@ -769,6 +826,7 @@ const ChatPrompt = {
769
826
  removeAttachment,
770
827
  sendMessage,
771
828
  addNewLine,
829
+ onKeyDown,
772
830
  imageAspectRatios,
773
831
  }
774
832
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 3.0.4
3
+ Version: 3.0.6
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
@@ -1,9 +1,9 @@
1
1
  llms/__init__.py,sha256=DKwTZDsyYL_wHe7yvLw49Nf8PSgPSyWaeVdotUqSvrQ,84
2
2
  llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
3
- llms/db.py,sha256=BEwBSh7AOc6seylB6Q5k1ru07Zx8WDkSHkto0YWNDSg,11687
3
+ llms/db.py,sha256=oozp5I5lECVO8oZEFwcZl3ES5mARqWeR1BkoqG5kSqM,11687
4
4
  llms/index.html,sha256=nGk1Djtn9p7l6LuKp4Kg0JIB9fCzxtTWXFfmDb4ggpc,1658
5
- llms/llms.json,sha256=gebw9PNWf5UTpU7RDIwo6QcBaJEkU0z3k9EeXR4hQiI,11147
6
- llms/main.py,sha256=5VkdSWkz1kCqR5miHoCufYiNFgMi2maXiXdqRGa6Hao,156278
5
+ llms/llms.json,sha256=esbR3GFEFura3Ajw02HjepGf0AlJPFhQlOPBjT_Lekc,11156
6
+ llms/main.py,sha256=-KhtOKzdJeKj27FjIEaqKuL-dE2ejYAcnPsZrY3j0CE,157027
7
7
  llms/providers-extra.json,sha256=w7_5gB0YUPK0PJNeViM7vRDfNGChXUKMHfGHenVxEkM,10165
8
8
  llms/providers.json,sha256=Hc1STJW9hqM_MWBjeJljs_0I3bTo6qhGlEsL0o-dUuA,285221
9
9
  llms/extensions/analytics/ui/index.mjs,sha256=cr9dPmEJjha2xX6A_7xbJxkOolWFP1p6TgIoO8M8juI,69540
@@ -142,7 +142,7 @@ llms/extensions/system_prompts/ui/prompts.json,sha256=t5DD3bird-87wFa4OlW-bC2wdo
142
142
  llms/extensions/tools/__init__.py,sha256=yfIK7dVqYiZGX5VeJ3x7HQWLPQfuPqeNYJsd0lpZUM4,120
143
143
  llms/extensions/tools/ui/index.mjs,sha256=Nu69U6odCUh8uu1i8d5f8ryO4Lj_OFbPX2LnVyYu1fk,9602
144
144
  llms/ui/App.mjs,sha256=zU-GtbcSMREizjUS9nWiMK6m_oT14MNJ-p_RCz2RVqA,7445
145
- llms/ui/ai.mjs,sha256=5r1oVt7HA8b_ikDpHhA7e6sE8eFaDyxYg3c_Okt4WJw,6540
145
+ llms/ui/ai.mjs,sha256=9bLrX3OSrJBTU3wiOehxwvpInpsyvmDMkiopLiUGclY,6540
146
146
  llms/ui/app.css,sha256=6iQiPeFZ_7pcf6RB1b7tvh6EY22A9lCn9SCCmoJKP2I,176402
147
147
  llms/ui/ctx.mjs,sha256=eFTkWzVBKRU5YDFFVUq3ZDd6eMPa4VRQpE3q0O5Sfgg,12337
148
148
  llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
@@ -167,10 +167,10 @@ llms/ui/modules/layout.mjs,sha256=8pAxs8bedQI3b3eRA9nrfpLZznLmrpp4BZvigYAQjpQ,12
167
167
  llms/ui/modules/model-selector.mjs,sha256=6U4rAZ7vmQELFRQGWk4YEtq02v3lyHdMq6yUOp-ArXg,43184
168
168
  llms/ui/modules/chat/ChatBody.mjs,sha256=YbpIEl6OeBi1RqCPkW5UFbUk6qNi5B_GfRUK5Pmfms8,42875
169
169
  llms/ui/modules/chat/SettingsDialog.mjs,sha256=HMBJTwrapKrRIAstIIqp0QlJL5O-ho4hzgvfagPfsX8,19930
170
- llms/ui/modules/chat/index.mjs,sha256=NA_9R7JEqHKZRnYGqte_J7qoDbG3RY8969TTNXqZa1k,37011
171
- llms_py-3.0.4.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
172
- llms_py-3.0.4.dist-info/METADATA,sha256=TgxpOLuMVr2KoZytO8HUSZh97FzFD37-V026tC246Mo,2191
173
- llms_py-3.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
174
- llms_py-3.0.4.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
175
- llms_py-3.0.4.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
176
- llms_py-3.0.4.dist-info/RECORD,,
170
+ llms/ui/modules/chat/index.mjs,sha256=3arA5PigH6BdBDqi5SbgcNkEIETENYNNxWF3Ro7HFsM,39349
171
+ llms_py-3.0.6.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
172
+ llms_py-3.0.6.dist-info/METADATA,sha256=o8-oUNHj2mr942bU-M_MOWCm3LQOk0IcSl3sYrlxk6g,2191
173
+ llms_py-3.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
174
+ llms_py-3.0.6.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
175
+ llms_py-3.0.6.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
176
+ llms_py-3.0.6.dist-info/RECORD,,