llms-py 2.0.0__py3-none-any.whl → 2.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.
Files changed (31) hide show
  1. llms.py +61 -12
  2. {llms_py-2.0.0.dist-info → llms_py-2.0.2.dist-info}/METADATA +1 -1
  3. llms_py-2.0.2.dist-info/RECORD +30 -0
  4. llms_py-2.0.0.dist-info/RECORD +0 -30
  5. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/index.html +0 -0
  6. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/llms.json +0 -0
  7. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/requirements.txt +0 -0
  8. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/App.mjs +0 -0
  9. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/ChatPrompt.mjs +0 -0
  10. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/Main.mjs +0 -0
  11. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/Recents.mjs +0 -0
  12. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/Sidebar.mjs +0 -0
  13. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/app.css +0 -0
  14. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/fav.svg +0 -0
  15. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/highlight.min.mjs +0 -0
  16. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/idb.min.mjs +0 -0
  17. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/marked.min.mjs +0 -0
  18. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/servicestack-client.min.mjs +0 -0
  19. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/servicestack-vue.min.mjs +0 -0
  20. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/vue-router.min.mjs +0 -0
  21. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/lib/vue.min.mjs +0 -0
  22. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/markdown.mjs +0 -0
  23. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/tailwind.input.css +0 -0
  24. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/threadStore.mjs +0 -0
  25. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/typography.css +0 -0
  26. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui/utils.mjs +0 -0
  27. {llms_py-2.0.0.data → llms_py-2.0.2.data}/data/ui.json +0 -0
  28. {llms_py-2.0.0.dist-info → llms_py-2.0.2.dist-info}/WHEEL +0 -0
  29. {llms_py-2.0.0.dist-info → llms_py-2.0.2.dist-info}/entry_points.txt +0 -0
  30. {llms_py-2.0.0.dist-info → llms_py-2.0.2.dist-info}/licenses/LICENSE +0 -0
  31. {llms_py-2.0.0.dist-info → llms_py-2.0.2.dist-info}/top_level.txt +0 -0
llms.py CHANGED
@@ -16,7 +16,10 @@ import traceback
16
16
  import aiohttp
17
17
  from aiohttp import web
18
18
 
19
- VERSION = "2.0.0"
19
+ from pathlib import Path
20
+ from importlib import resources # Py≥3.9 (pip install importlib_resources for 3.7/3.8)
21
+
22
+ VERSION = "2.0.2"
20
23
  g_config_path = None
21
24
  g_ui_path = None
22
25
  g_config = None
@@ -782,6 +785,7 @@ def disable_provider(provider):
782
785
  save_config(g_config)
783
786
  init_llms(g_config)
784
787
 
788
+
785
789
  def main():
786
790
  global g_verbose, g_default_model, g_logprefix, g_config_path, g_ui_path
787
791
 
@@ -806,6 +810,7 @@ def main():
806
810
 
807
811
  parser.add_argument('--init', action='store_true', help='Create a default llms.json')
808
812
 
813
+ parser.add_argument('--root', default=None, help='Change root directory for UI files', metavar='PATH')
809
814
  parser.add_argument('--logprefix', default="", help='Prefix used in log messages', metavar='PREFIX')
810
815
  parser.add_argument('--verbose', action='store_true', help='Verbose output')
811
816
  parser.add_argument('--update', action='store_true', help='Update to latest version')
@@ -822,18 +827,35 @@ def main():
822
827
  if cli_args.config is not None:
823
828
  g_config_path = os.path.join(os.path.dirname(__file__), cli_args.config)
824
829
 
830
+ try:
831
+ from importlib.resources import files
832
+ _ROOT = files("llms")
833
+ _log(f"RESOURCE ROOT: {_ROOT}")
834
+ except ModuleNotFoundError:
835
+ # package not installed
836
+ # __file__ is *this* module; climb two levels to repo root
837
+ _ROOT = Path(__file__).resolve().parent.parent / "llms"
838
+ _log(f"__file__: {Path(__file__).resolve()}")
839
+ _log(f"PATH ROOT: {_ROOT}")
840
+
841
+ if cli_args.root:
842
+ _ROOT = Path(cli_args.root)
843
+
825
844
  g_config_path = os.path.join(os.path.dirname(__file__), cli_args.config) if cli_args.config else get_config_path()
826
845
  g_ui_path = get_ui_path()
827
846
 
847
+ home_config_path = home_llms_path("llms.json")
848
+ resource_config_path = _ROOT / "llms.json"
849
+ home_ui_path = home_llms_path("ui.json")
850
+ resource_ui_path = _ROOT / "ui.json"
851
+
828
852
  if cli_args.init:
829
- home_config_path = home_llms_path("llms.json")
830
853
  if os.path.exists(home_config_path):
831
854
  print(f"llms.json already exists at {home_config_path}")
832
855
  else:
833
856
  asyncio.run(save_default_config(home_config_path))
834
857
  print(f"Created default config at {home_config_path}")
835
858
 
836
- home_ui_path = home_llms_path("ui.json")
837
859
  if os.path.exists(home_ui_path):
838
860
  print(f"ui.json already exists at {home_ui_path}")
839
861
  else:
@@ -841,9 +863,26 @@ def main():
841
863
  print(f"Created default ui config at {home_ui_path}")
842
864
  exit(0)
843
865
 
844
- if not os.path.exists(g_config_path):
845
- print("Config file not found. Create one with --init or use --config <path>")
846
- exit(1)
866
+ if not g_config_path or not os.path.exists(g_config_path):
867
+ # copy llms.json and ui.json to llms_home
868
+ if not os.path.exists(home_config_path) and os.path.exists(resource_config_path):
869
+ llms_home = os.path.dirname(home_config_path)
870
+ os.makedirs(llms_home, exist_ok=True)
871
+ with open(resource_config_path, "r") as f:
872
+ config_json = f.read()
873
+ with open(home_config_path, "w") as f:
874
+ f.write(config_json)
875
+ _log(f"Created default config at {home_config_path}")
876
+
877
+ if not os.path.exists(home_ui_path) and os.path.exists(resource_ui_path):
878
+ with open(resource_ui_path, "r") as f:
879
+ ui_json = f.read()
880
+ with open(home_ui_path, "w") as f:
881
+ f.write(ui_json)
882
+ _log(f"Created default ui config at {home_ui_path}")
883
+ else:
884
+ print("Config file not found. Create one with --init or use --config <path>")
885
+ exit(1)
847
886
 
848
887
  # read contents
849
888
  with open(g_config_path, "r") as f:
@@ -923,18 +962,28 @@ def main():
923
962
  })
924
963
  app.router.add_post('/providers/{provider}', provider_handler)
925
964
 
926
- # Serve static files from ui/ directory
927
- script_dir = os.path.dirname(os.path.abspath(__file__))
928
- ui_path = os.path.join(script_dir, 'ui')
929
- if os.path.exists(ui_path):
930
- app.router.add_static('/ui/', ui_path, name='ui')
965
+ async def ui_static(request: web.Request) -> web.Response:
966
+ path = Path(request.match_info["path"])
967
+ resource = _ROOT / "ui" / path
968
+ if not resource.is_file():
969
+ raise web.HTTPNotFound
970
+ try:
971
+ resource.relative_to(Path(_ROOT)) # basic directory-traversal guard
972
+ except ValueError:
973
+ raise web.HTTPBadRequest(text="Invalid path")
974
+ content_type, _ = mimetypes.guess_type(resource.name)
975
+ if content_type is None:
976
+ content_type = "application/octet-stream"
977
+ return web.Response(body=resource.read_bytes(), content_type=content_type)
978
+
979
+ app.router.add_get("/ui/{path:.*}", ui_static, name="ui_static")
931
980
 
932
981
  async def not_found_handler(request):
933
982
  return web.Response(text="404: Not Found", status=404)
934
983
  app.router.add_get('/favicon.ico', not_found_handler)
935
984
 
936
985
  # Serve index.html from root
937
- index_path = os.path.join(script_dir, 'index.html')
986
+ index_path = os.path.join(_ROOT, 'index.html')
938
987
  if os.path.exists(index_path):
939
988
  async def index_handler(request):
940
989
  return web.FileResponse(index_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 2.0.0
3
+ Version: 2.0.2
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,30 @@
1
+ llms.py,sha256=idCn0aVSvktEd1PIrvwwfzbUpKuVLAPr705XNZNW0m4,47263
2
+ llms_py-2.0.2.data/data/index.html,sha256=nOgYc7t9QjlbRoy35-W6HpQbEEuD0-O1rkA8j-IYhZ4,2276
3
+ llms_py-2.0.2.data/data/llms.json,sha256=nw9g63Ht15V5h2A9ynx8_1KLLboNrzUN5PTPhPV1lAY,18365
4
+ llms_py-2.0.2.data/data/requirements.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8
5
+ llms_py-2.0.2.data/data/ui.json,sha256=iBOmpNeD5-o8AgUa51ymS-KemovJ7bm9J1fnL0nf8jk,134025
6
+ llms_py-2.0.2.data/data/ui/App.mjs,sha256=hXtUjaL3GrcIHieEK3BzIG72OVzrorBBS4RkE1DOGc4,439
7
+ llms_py-2.0.2.data/data/ui/ChatPrompt.mjs,sha256=S_p2h6CJUDMvuetMgauJzS0MkACDw-bWw0FR-YXre_w,17163
8
+ llms_py-2.0.2.data/data/ui/Main.mjs,sha256=8RdjDDCQSwk4wjRolJIkbWvHZHRf1GB9Qj8zN1lyuHo,26714
9
+ llms_py-2.0.2.data/data/ui/Recents.mjs,sha256=cA9njbuOXeTWWagiArmswwgXuAV36ExhXGPE6sZrffY,7459
10
+ llms_py-2.0.2.data/data/ui/Sidebar.mjs,sha256=TUyicSr8MRpLoUZoQ6neBG0PwhFcz2BhIQVwdaKSuo8,10870
11
+ llms_py-2.0.2.data/data/ui/app.css,sha256=E_UzowGq5lYcvSnHy-KgSsQV_LB6HPr989lpp6YHlug,92441
12
+ llms_py-2.0.2.data/data/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
13
+ llms_py-2.0.2.data/data/ui/markdown.mjs,sha256=YeOnS7bPM0oyXHMVsgwobLYKgqzRNjrmBJ8NIrhU9Z8,6096
14
+ llms_py-2.0.2.data/data/ui/tailwind.input.css,sha256=31O3JLwvf8qPoEmKAGMPXEOgVRNcP7i7Y0DH_xw6qsg,10612
15
+ llms_py-2.0.2.data/data/ui/threadStore.mjs,sha256=bL-yPx1ShU7lBJrzgMHI7K-ZtzcuWDSjtSf--nn0DVk,7199
16
+ llms_py-2.0.2.data/data/ui/typography.css,sha256=XtIOwBvWujwTOSOuF-zs5x3xEJLnmg27q2lmv4JyLm4,16127
17
+ llms_py-2.0.2.data/data/ui/utils.mjs,sha256=exyN-YohPvcGSR-IrjYUj0t3_RDIrnaJgSvkHA2bIRk,4016
18
+ llms_py-2.0.2.data/data/ui/lib/highlight.min.mjs,sha256=sG7wq8bF-IKkfie7S4QSyh5DdHBRf0NqQxMOEH8-MT0,127458
19
+ llms_py-2.0.2.data/data/ui/lib/idb.min.mjs,sha256=CeTXyV4I_pB5vnibvJuyXdMs0iVF2ZL0Z7cdm3w_QaI,3853
20
+ llms_py-2.0.2.data/data/ui/lib/marked.min.mjs,sha256=QRHb_VZugcBJRD2EP6gYlVFEsJw5C2fQ8ImMf_pA2_s,39488
21
+ llms_py-2.0.2.data/data/ui/lib/servicestack-client.min.mjs,sha256=UVafVbzhJ_0N2lzv7rlzIbzwnWpoqXxGk3N3FSKgOOc,54534
22
+ llms_py-2.0.2.data/data/ui/lib/servicestack-vue.min.mjs,sha256=oqeaDTgQxCi4gLt9X0J8UwIJgW9FWCV0d-Iiak6k-qI,218926
23
+ llms_py-2.0.2.data/data/ui/lib/vue-router.min.mjs,sha256=fR30GHoXI1u81zyZ26YEU105pZgbbAKSXbpnzFKIxls,30418
24
+ llms_py-2.0.2.data/data/ui/lib/vue.min.mjs,sha256=iXh97m5hotl0eFllb3aoasQTImvp7mQoRJ_0HoxmZkw,163811
25
+ llms_py-2.0.2.dist-info/licenses/LICENSE,sha256=rRryrddGfVftpde-rmAZpW0R8IJihqJ8t8wpfDXoKiQ,1549
26
+ llms_py-2.0.2.dist-info/METADATA,sha256=MuCK5DkzROO_wVNrMOCorRevw7ap36BCiGGvPlL87sI,23652
27
+ llms_py-2.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ llms_py-2.0.2.dist-info/entry_points.txt,sha256=svUoTt28bIJeo6Mb1ffDBLyzhD90DWtFbfp0ShyRCgU,35
29
+ llms_py-2.0.2.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
30
+ llms_py-2.0.2.dist-info/RECORD,,
@@ -1,30 +0,0 @@
1
- llms.py,sha256=MpIinC5Dd_s6psCUCVY_cOVGAsxmD1Z2zo_cNhJdvR0,45065
2
- llms_py-2.0.0.data/data/index.html,sha256=nOgYc7t9QjlbRoy35-W6HpQbEEuD0-O1rkA8j-IYhZ4,2276
3
- llms_py-2.0.0.data/data/llms.json,sha256=nw9g63Ht15V5h2A9ynx8_1KLLboNrzUN5PTPhPV1lAY,18365
4
- llms_py-2.0.0.data/data/requirements.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8
5
- llms_py-2.0.0.data/data/ui.json,sha256=iBOmpNeD5-o8AgUa51ymS-KemovJ7bm9J1fnL0nf8jk,134025
6
- llms_py-2.0.0.data/data/ui/App.mjs,sha256=hXtUjaL3GrcIHieEK3BzIG72OVzrorBBS4RkE1DOGc4,439
7
- llms_py-2.0.0.data/data/ui/ChatPrompt.mjs,sha256=S_p2h6CJUDMvuetMgauJzS0MkACDw-bWw0FR-YXre_w,17163
8
- llms_py-2.0.0.data/data/ui/Main.mjs,sha256=8RdjDDCQSwk4wjRolJIkbWvHZHRf1GB9Qj8zN1lyuHo,26714
9
- llms_py-2.0.0.data/data/ui/Recents.mjs,sha256=cA9njbuOXeTWWagiArmswwgXuAV36ExhXGPE6sZrffY,7459
10
- llms_py-2.0.0.data/data/ui/Sidebar.mjs,sha256=TUyicSr8MRpLoUZoQ6neBG0PwhFcz2BhIQVwdaKSuo8,10870
11
- llms_py-2.0.0.data/data/ui/app.css,sha256=E_UzowGq5lYcvSnHy-KgSsQV_LB6HPr989lpp6YHlug,92441
12
- llms_py-2.0.0.data/data/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
13
- llms_py-2.0.0.data/data/ui/markdown.mjs,sha256=YeOnS7bPM0oyXHMVsgwobLYKgqzRNjrmBJ8NIrhU9Z8,6096
14
- llms_py-2.0.0.data/data/ui/tailwind.input.css,sha256=31O3JLwvf8qPoEmKAGMPXEOgVRNcP7i7Y0DH_xw6qsg,10612
15
- llms_py-2.0.0.data/data/ui/threadStore.mjs,sha256=bL-yPx1ShU7lBJrzgMHI7K-ZtzcuWDSjtSf--nn0DVk,7199
16
- llms_py-2.0.0.data/data/ui/typography.css,sha256=XtIOwBvWujwTOSOuF-zs5x3xEJLnmg27q2lmv4JyLm4,16127
17
- llms_py-2.0.0.data/data/ui/utils.mjs,sha256=exyN-YohPvcGSR-IrjYUj0t3_RDIrnaJgSvkHA2bIRk,4016
18
- llms_py-2.0.0.data/data/ui/lib/highlight.min.mjs,sha256=sG7wq8bF-IKkfie7S4QSyh5DdHBRf0NqQxMOEH8-MT0,127458
19
- llms_py-2.0.0.data/data/ui/lib/idb.min.mjs,sha256=CeTXyV4I_pB5vnibvJuyXdMs0iVF2ZL0Z7cdm3w_QaI,3853
20
- llms_py-2.0.0.data/data/ui/lib/marked.min.mjs,sha256=QRHb_VZugcBJRD2EP6gYlVFEsJw5C2fQ8ImMf_pA2_s,39488
21
- llms_py-2.0.0.data/data/ui/lib/servicestack-client.min.mjs,sha256=UVafVbzhJ_0N2lzv7rlzIbzwnWpoqXxGk3N3FSKgOOc,54534
22
- llms_py-2.0.0.data/data/ui/lib/servicestack-vue.min.mjs,sha256=oqeaDTgQxCi4gLt9X0J8UwIJgW9FWCV0d-Iiak6k-qI,218926
23
- llms_py-2.0.0.data/data/ui/lib/vue-router.min.mjs,sha256=fR30GHoXI1u81zyZ26YEU105pZgbbAKSXbpnzFKIxls,30418
24
- llms_py-2.0.0.data/data/ui/lib/vue.min.mjs,sha256=iXh97m5hotl0eFllb3aoasQTImvp7mQoRJ_0HoxmZkw,163811
25
- llms_py-2.0.0.dist-info/licenses/LICENSE,sha256=rRryrddGfVftpde-rmAZpW0R8IJihqJ8t8wpfDXoKiQ,1549
26
- llms_py-2.0.0.dist-info/METADATA,sha256=wu4I6AJ1Sq01_YleiZztaYFcqWSfvsare59-ANXMYYI,23652
27
- llms_py-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
- llms_py-2.0.0.dist-info/entry_points.txt,sha256=svUoTt28bIJeo6Mb1ffDBLyzhD90DWtFbfp0ShyRCgU,35
29
- llms_py-2.0.0.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
30
- llms_py-2.0.0.dist-info/RECORD,,
File without changes