llms-py 2.0.9__tar.gz → 2.0.11__tar.gz

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 (51) hide show
  1. {llms_py-2.0.9/llms_py.egg-info → llms_py-2.0.11}/PKG-INFO +33 -25
  2. {llms_py-2.0.9 → llms_py-2.0.11}/README.md +32 -24
  3. llms_py-2.0.11/index.html +80 -0
  4. {llms_py-2.0.9 → llms_py-2.0.11}/llms.json +5 -5
  5. {llms_py-2.0.9 → llms_py-2.0.11}/llms.py +14 -7
  6. {llms_py-2.0.9 → llms_py-2.0.11/llms_py.egg-info}/PKG-INFO +33 -25
  7. {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/SOURCES.txt +12 -2
  8. {llms_py-2.0.9 → llms_py-2.0.11}/pyproject.toml +1 -1
  9. {llms_py-2.0.9 → llms_py-2.0.11}/setup.py +16 -6
  10. llms_py-2.0.11/ui/Avatar.mjs +28 -0
  11. llms_py-2.0.11/ui/Brand.mjs +23 -0
  12. {llms_py-2.0.9 → llms_py-2.0.11}/ui/ChatPrompt.mjs +101 -69
  13. {llms_py-2.0.9 → llms_py-2.0.11}/ui/Main.mjs +43 -183
  14. llms_py-2.0.11/ui/ModelSelector.mjs +29 -0
  15. llms_py-2.0.11/ui/ProviderStatus.mjs +105 -0
  16. {llms_py-2.0.9 → llms_py-2.0.11}/ui/Recents.mjs +2 -1
  17. llms_py-2.0.11/ui/SettingsDialog.mjs +374 -0
  18. {llms_py-2.0.9 → llms_py-2.0.11}/ui/Sidebar.mjs +11 -27
  19. llms_py-2.0.11/ui/SignIn.mjs +64 -0
  20. llms_py-2.0.11/ui/SystemPromptEditor.mjs +31 -0
  21. llms_py-2.0.11/ui/SystemPromptSelector.mjs +36 -0
  22. llms_py-2.0.11/ui/Welcome.mjs +8 -0
  23. llms_py-2.0.11/ui/ai.mjs +80 -0
  24. {llms_py-2.0.9 → llms_py-2.0.11}/ui/app.css +76 -10
  25. llms_py-2.0.11/ui/lib/servicestack-vue.mjs +37 -0
  26. {llms_py-2.0.9 → llms_py-2.0.11}/ui/markdown.mjs +9 -2
  27. {llms_py-2.0.9 → llms_py-2.0.11}/ui/tailwind.input.css +13 -4
  28. {llms_py-2.0.9 → llms_py-2.0.11}/ui/threadStore.mjs +2 -2
  29. {llms_py-2.0.9 → llms_py-2.0.11}/ui/typography.css +109 -1
  30. {llms_py-2.0.9 → llms_py-2.0.11}/ui/utils.mjs +8 -2
  31. llms_py-2.0.9/index.html +0 -64
  32. llms_py-2.0.9/ui/lib/servicestack-vue.min.mjs +0 -37
  33. {llms_py-2.0.9 → llms_py-2.0.11}/LICENSE +0 -0
  34. {llms_py-2.0.9 → llms_py-2.0.11}/MANIFEST.in +0 -0
  35. {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/dependency_links.txt +0 -0
  36. {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/entry_points.txt +0 -0
  37. {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/not-zip-safe +0 -0
  38. {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/requires.txt +0 -0
  39. {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/top_level.txt +0 -0
  40. {llms_py-2.0.9 → llms_py-2.0.11}/requirements.txt +0 -0
  41. {llms_py-2.0.9 → llms_py-2.0.11}/setup.cfg +0 -0
  42. {llms_py-2.0.9 → llms_py-2.0.11}/ui/App.mjs +0 -0
  43. {llms_py-2.0.9 → llms_py-2.0.11}/ui/fav.svg +0 -0
  44. {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/highlight.min.mjs +0 -0
  45. {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/idb.min.mjs +0 -0
  46. {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/marked.min.mjs +0 -0
  47. /llms_py-2.0.9/ui/lib/servicestack-client.min.mjs → /llms_py-2.0.11/ui/lib/servicestack-client.mjs +0 -0
  48. {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/vue-router.min.mjs +0 -0
  49. {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/vue.min.mjs +0 -0
  50. {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/vue.mjs +0 -0
  51. {llms_py-2.0.9 → llms_py-2.0.11}/ui.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 2.0.9
3
+ Version: 2.0.11
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
@@ -97,34 +97,31 @@ pip install aiohttp
97
97
 
98
98
  ## Quick Start
99
99
 
100
- ### 1. Initialize Configuration
101
-
102
- Create a default configuration file:
103
-
104
- ```bash
105
- llms --init
106
- ```
107
-
108
- This saves the latest [llms.json](llms.json) configuration to `~/.llms/llms.json`.
109
-
110
- Modify `~/.llms/llms.json` to enable providers, add required API keys, additional models or any custom
111
- OpenAI-compatible providers.
112
-
113
- ### 2. Set API Keys
100
+ ### 1. Set API Keys
114
101
 
115
102
  Set environment variables for the providers you want to use:
116
103
 
117
104
  ```bash
118
- export OPENROUTER_API_KEY="..."
119
- export GROQ_API_KEY="..."
120
- export GOOGLE_API_KEY="..."
121
- export ANTHROPIC_API_KEY="..."
122
- export GROK_API_KEY="..."
123
- export DASHSCOPE_API_KEY="..."
124
- # ... etc
105
+ export OPENROUTER_FREE_API_KEY="..."
125
106
  ```
126
107
 
127
- ### 3. Enable Providers
108
+ | Provider | Variable | Description | Example |
109
+ |-----------------|---------------------------|---------------------|---------|
110
+ | openrouter_free | `OPENROUTER_FREE_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
111
+ | groq | `GROQ_API_KEY` | Groq API key | `gsk_...` |
112
+ | google_free | `GOOGLE_FREE_API_KEY` | Google FREE API key | `AIza...` |
113
+ | codestral | `CODESTRAL_API_KEY` | Codestral API key | `...` |
114
+ | ollama | N/A | No API key required | |
115
+ | openrouter | `OPENROUTER_API_KEY` | OpenRouter API key | `sk-or-...` |
116
+ | google | `GOOGLE_API_KEY` | Google API key | `AIza...` |
117
+ | anthropic | `ANTHROPIC_API_KEY` | Anthropic API key | `sk-ant-...` |
118
+ | openai | `OPENAI_API_KEY` | OpenAI API key | `sk-...` |
119
+ | grok | `GROK_API_KEY` | Grok (X.AI) API key | `xai-...` |
120
+ | qwen | `DASHSCOPE_API_KEY` | Qwen (Alibaba) API key | `sk-...` |
121
+ | z.ai | `ZAI_API_KEY` | Z.ai API key | `sk-...` |
122
+ | mistral | `MISTRAL_API_KEY` | Mistral API key | `...` |
123
+
124
+ ### 2. Enable Providers
128
125
 
129
126
  Enable the providers you want to use:
130
127
 
@@ -136,7 +133,17 @@ llms --enable openrouter_free google_free groq
136
133
  llms --enable openrouter anthropic google openai mistral grok qwen
137
134
  ```
138
135
 
139
- ### 4. Start Chatting
136
+ ### 3. Run UI
137
+
138
+ Start the UI and an OpenAI compatible API on port **8000**:
139
+
140
+ ```bash
141
+ llms --serve 8000
142
+ ```
143
+
144
+ Launches the UI at `http://localhost:8000` and an OpenAI Endpoint at `http://localhost:8000/v1/chat/completions`.
145
+
146
+ ### 4. Use llms.py CLI
140
147
 
141
148
  ```bash
142
149
  llms "What is the capital of France?"
@@ -144,7 +151,7 @@ llms "What is the capital of France?"
144
151
 
145
152
  ## Configuration
146
153
 
147
- The configuration file (`llms.json`) defines available providers, models, and default settings. Key sections:
154
+ The configuration file [llms.json](llms.json) is saved to `~/.llms/llms.json` and defines available providers, models, and default settings. Key sections:
148
155
 
149
156
  ### Defaults
150
157
  - `headers`: Common HTTP headers for all requests
@@ -159,6 +166,7 @@ Each provider configuration includes:
159
166
  - `base_url`: API endpoint URL
160
167
  - `models`: Model name mappings (local name → provider name)
161
168
 
169
+
162
170
  ## Command Line Usage
163
171
 
164
172
  ### Basic Chat
@@ -57,34 +57,31 @@ pip install aiohttp
57
57
 
58
58
  ## Quick Start
59
59
 
60
- ### 1. Initialize Configuration
61
-
62
- Create a default configuration file:
63
-
64
- ```bash
65
- llms --init
66
- ```
67
-
68
- This saves the latest [llms.json](llms.json) configuration to `~/.llms/llms.json`.
69
-
70
- Modify `~/.llms/llms.json` to enable providers, add required API keys, additional models or any custom
71
- OpenAI-compatible providers.
72
-
73
- ### 2. Set API Keys
60
+ ### 1. Set API Keys
74
61
 
75
62
  Set environment variables for the providers you want to use:
76
63
 
77
64
  ```bash
78
- export OPENROUTER_API_KEY="..."
79
- export GROQ_API_KEY="..."
80
- export GOOGLE_API_KEY="..."
81
- export ANTHROPIC_API_KEY="..."
82
- export GROK_API_KEY="..."
83
- export DASHSCOPE_API_KEY="..."
84
- # ... etc
65
+ export OPENROUTER_FREE_API_KEY="..."
85
66
  ```
86
67
 
87
- ### 3. Enable Providers
68
+ | Provider | Variable | Description | Example |
69
+ |-----------------|---------------------------|---------------------|---------|
70
+ | openrouter_free | `OPENROUTER_FREE_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
71
+ | groq | `GROQ_API_KEY` | Groq API key | `gsk_...` |
72
+ | google_free | `GOOGLE_FREE_API_KEY` | Google FREE API key | `AIza...` |
73
+ | codestral | `CODESTRAL_API_KEY` | Codestral API key | `...` |
74
+ | ollama | N/A | No API key required | |
75
+ | openrouter | `OPENROUTER_API_KEY` | OpenRouter API key | `sk-or-...` |
76
+ | google | `GOOGLE_API_KEY` | Google API key | `AIza...` |
77
+ | anthropic | `ANTHROPIC_API_KEY` | Anthropic API key | `sk-ant-...` |
78
+ | openai | `OPENAI_API_KEY` | OpenAI API key | `sk-...` |
79
+ | grok | `GROK_API_KEY` | Grok (X.AI) API key | `xai-...` |
80
+ | qwen | `DASHSCOPE_API_KEY` | Qwen (Alibaba) API key | `sk-...` |
81
+ | z.ai | `ZAI_API_KEY` | Z.ai API key | `sk-...` |
82
+ | mistral | `MISTRAL_API_KEY` | Mistral API key | `...` |
83
+
84
+ ### 2. Enable Providers
88
85
 
89
86
  Enable the providers you want to use:
90
87
 
@@ -96,7 +93,17 @@ llms --enable openrouter_free google_free groq
96
93
  llms --enable openrouter anthropic google openai mistral grok qwen
97
94
  ```
98
95
 
99
- ### 4. Start Chatting
96
+ ### 3. Run UI
97
+
98
+ Start the UI and an OpenAI compatible API on port **8000**:
99
+
100
+ ```bash
101
+ llms --serve 8000
102
+ ```
103
+
104
+ Launches the UI at `http://localhost:8000` and an OpenAI Endpoint at `http://localhost:8000/v1/chat/completions`.
105
+
106
+ ### 4. Use llms.py CLI
100
107
 
101
108
  ```bash
102
109
  llms "What is the capital of France?"
@@ -104,7 +111,7 @@ llms "What is the capital of France?"
104
111
 
105
112
  ## Configuration
106
113
 
107
- The configuration file (`llms.json`) defines available providers, models, and default settings. Key sections:
114
+ The configuration file [llms.json](llms.json) is saved to `~/.llms/llms.json` and defines available providers, models, and default settings. Key sections:
108
115
 
109
116
  ### Defaults
110
117
  - `headers`: Common HTTP headers for all requests
@@ -119,6 +126,7 @@ Each provider configuration includes:
119
126
  - `base_url`: API endpoint URL
120
127
  - `models`: Model name mappings (local name → provider name)
121
128
 
129
+
122
130
  ## Command Line Usage
123
131
 
124
132
  ### Basic Chat
@@ -0,0 +1,80 @@
1
+ <html>
2
+ <head>
3
+ <title>llms.py</title>
4
+ <link rel="stylesheet" href="/ui/typography.css">
5
+ <link rel="stylesheet" href="/ui/app.css">
6
+ <link rel="icon" type="image/svg" href="/ui/fav.svg">
7
+ <style>
8
+ [type='button'],button[type='submit']{cursor:pointer}
9
+ [type='checkbox'].switch:checked:hover,
10
+ [type='checkbox'].switch:checked:focus,
11
+ [type='checkbox'].switch:checked,
12
+ [type='checkbox'].switch:focus,
13
+ [type='checkbox'].switch
14
+ {
15
+ border: none;
16
+ background: none;
17
+ outline: none;
18
+ box-shadow: none;
19
+ cursor: pointer;
20
+ }
21
+ </style>
22
+ </head>
23
+ <script type="importmap">
24
+ {
25
+ "imports": {
26
+ "vue": "/ui/lib/vue.min.mjs",
27
+ "vue-router": "/ui/lib/vue-router.min.mjs",
28
+ "@servicestack/client": "/ui/lib/servicestack-client.mjs",
29
+ "@servicestack/vue": "/ui/lib/servicestack-vue.mjs",
30
+ "idb": "/ui/lib/idb.min.mjs",
31
+ "marked": "/ui/lib/marked.min.mjs",
32
+ "highlight.js": "/ui/lib/highlight.min.mjs"
33
+ }
34
+ }
35
+ </script>
36
+ <body>
37
+ <div id="app"></div>
38
+ </body>
39
+ <script type="module">
40
+ import { createApp, defineAsyncComponent } from 'vue'
41
+ import { createWebHistory, createRouter } from "vue-router"
42
+ import ServiceStackVue from "@servicestack/vue"
43
+ import App from '/ui/App.mjs'
44
+ import ai from '/ui/ai.mjs'
45
+ import SettingsDialog from '/ui/SettingsDialog.mjs'
46
+
47
+ const { config, models } = await ai.init()
48
+ const MainComponent = defineAsyncComponent(() => import(ai.base + '/ui/Main.mjs'))
49
+ const RecentsComponent = defineAsyncComponent(() => import(ai.base + '/ui/Recents.mjs'))
50
+
51
+ const Components = {
52
+ SettingsDialog,
53
+ }
54
+
55
+ const routes = [
56
+ { path: '/', component: MainComponent },
57
+ { path: '/c/:id', component: MainComponent },
58
+ { path: '/recents', component: RecentsComponent },
59
+ { path: '/:fallback(.*)*', component: MainComponent }
60
+ ]
61
+ routes.forEach(r => r.path = ai.base + r.path)
62
+ const router = createRouter({
63
+ history: createWebHistory(),
64
+ routes,
65
+ })
66
+ const app = createApp(App, { config, models })
67
+ app.use(router)
68
+ app.use(ServiceStackVue)
69
+ app.provide('ai', ai)
70
+ app.provide('config', config)
71
+ app.provide('models', models)
72
+ Object.keys(Components).forEach(name => {
73
+ app.component(name, Components[name])
74
+ })
75
+
76
+ window.ai = app.config.globalProperties.$ai = ai
77
+
78
+ app.mount('#app')
79
+ </script>
80
+ </html>
@@ -86,7 +86,7 @@
86
86
  "enabled": true,
87
87
  "type": "OpenAiProvider",
88
88
  "base_url": "https://openrouter.ai/api",
89
- "api_key": "$OPENROUTER_FREE_API_KEY",
89
+ "api_key": "$OPENROUTER_API_KEY",
90
90
  "models": {
91
91
  "qwen2.5vl": "qwen/qwen2.5-vl-32b-instruct:free",
92
92
  "llama4:109b": "meta-llama/llama-4-scout:free",
@@ -95,10 +95,8 @@
95
95
  "deepseek-r1:671b": "deepseek/deepseek-r1-0528:free",
96
96
  "gemini-2.0-flash": "google/gemini-2.0-flash-exp:free",
97
97
  "glm-4.5-air": "z-ai/glm-4.5-air:free",
98
- "grok-4-fast": "x-ai/grok-4-fast:free",
99
98
  "mai-ds-r1": "microsoft/mai-ds-r1:free",
100
99
  "llama3.3:70b": "meta-llama/llama-3.3-70b-instruct:free",
101
- "kimi-k2": "moonshotai/kimi-k2:free",
102
100
  "nemotron-nano:9b": "nvidia/nemotron-nano-9b-v2:free",
103
101
  "deepseek-r1-distill-llama:70b": "deepseek/deepseek-r1-distill-llama-70b:free",
104
102
  "gpt-oss:20b": "openai/gpt-oss-20b:free",
@@ -107,7 +105,6 @@
107
105
  "devstral-small": "mistralai/devstral-small-2505:free",
108
106
  "venice-uncensored:24b": "cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
109
107
  "llama3.3:8b": "meta-llama/llama-3.3-8b-instruct:free",
110
- "llama3.1:405b": "meta-llama/llama-3.1-405b-instruct:free",
111
108
  "kimi-dev:72b": "moonshotai/kimi-dev-72b:free",
112
109
  "gemma3:27b": "google/gemma-3-27b-it:free",
113
110
  "qwen3-coder": "qwen/qwen3-coder:free",
@@ -176,7 +173,7 @@
176
173
  }
177
174
  },
178
175
  "ollama": {
179
- "enabled": false,
176
+ "enabled": true,
180
177
  "type": "OllamaProvider",
181
178
  "base_url": "http://localhost:11434",
182
179
  "models": {},
@@ -239,6 +236,7 @@
239
236
  "qwen3-max": "qwen/qwen3-max",
240
237
  "qwen3-vl:235b": "qwen/qwen3-vl-235b-a22b-instruct",
241
238
  "qwen3-vl-thinking:235b": "qwen/qwen3-vl-235b-a22b-thinking",
239
+ "ling-1t": "inclusionai/ling-1t",
242
240
  "llama4:109b": "meta-llama/llama-4-scout",
243
241
  "llama4:400b": "meta-llama/llama-4-maverick"
244
242
  }
@@ -286,6 +284,7 @@
286
284
  "base_url": "https://api.openai.com",
287
285
  "api_key": "$OPENAI_API_KEY",
288
286
  "models": {
287
+ "gpt-5-pro": "openai/gpt-5-pro",
289
288
  "gpt-5-codex": "gpt-5-codex",
290
289
  "gpt-audio": "gpt-audio",
291
290
  "gpt-realtime": "gpt-realtime",
@@ -389,6 +388,7 @@
389
388
  "qwen3-coder:30b": "qwen3-coder-30b-a3b-instruct",
390
389
  "qwen3-vl-thinking:235b": "qwen3-vl-235b-a22b-thinking",
391
390
  "qwen3-vl:235b": "qwen3-vl-235b-a22b-instruct",
391
+ "qwen3-vl:30b": "qwen3-vl-30b-a3b-instruct",
392
392
  "qwen2.5-vl:72b": "qwen2.5-vl-72b-instruct",
393
393
  "qwen2.5-vl:32b": "qwen2.5-vl-32b-instruct",
394
394
  "qwen2.5-vl:7b": "qwen2.5-vl-7b-instruct",
@@ -22,7 +22,7 @@ from aiohttp import web
22
22
  from pathlib import Path
23
23
  from importlib import resources # Py≥3.9 (pip install importlib_resources for 3.7/3.8)
24
24
 
25
- VERSION = "2.0.9"
25
+ VERSION = "2.0.11"
26
26
  _ROOT = None
27
27
  g_config_path = None
28
28
  g_ui_path = None
@@ -64,8 +64,8 @@ def chat_summary(chat):
64
64
  elif 'file' in item:
65
65
  if 'file_data' in item['file']:
66
66
  data = item['file']['file_data']
67
- prefix = url.split(',', 1)[0]
68
- item['file']['file_data'] = prefix + f",({len(url) - len(prefix)})"
67
+ prefix = data.split(',', 1)[0]
68
+ item['file']['file_data'] = prefix + f",({len(data) - len(prefix)})"
69
69
  return json.dumps(clone, indent=2)
70
70
 
71
71
  def gemini_chat_summary(gemini_chat):
@@ -444,7 +444,14 @@ class GoogleProvider(OpenAiProvider):
444
444
  async with aiohttp.ClientSession() as session:
445
445
  for message in chat['messages']:
446
446
  if message['role'] == 'system':
447
- system_prompt = message
447
+ content = message['content']
448
+ if isinstance(content, list):
449
+ for item in content:
450
+ if 'text' in item:
451
+ system_prompt = item['text']
452
+ break
453
+ elif isinstance(content, str):
454
+ system_prompt = content
448
455
  elif 'content' in message:
449
456
  if isinstance(message['content'], list):
450
457
  parts = []
@@ -520,7 +527,7 @@ class GoogleProvider(OpenAiProvider):
520
527
  # Add system instruction if present
521
528
  if system_prompt is not None:
522
529
  gemini_chat['systemInstruction'] = {
523
- "parts": [{"text": system_prompt['content']}]
530
+ "parts": [{"text": system_prompt}]
524
531
  }
525
532
 
526
533
  if 'stop' in chat:
@@ -1257,7 +1264,7 @@ def main():
1257
1264
  app.router.add_route('*', '/{tail:.*}', index_handler)
1258
1265
 
1259
1266
  if os.path.exists(g_ui_path):
1260
- async def ui_json_handler(request):
1267
+ async def ui_config_handler(request):
1261
1268
  with open(g_ui_path, "r") as f:
1262
1269
  ui = json.load(f)
1263
1270
  if 'defaults' not in ui:
@@ -1269,7 +1276,7 @@ def main():
1269
1276
  "disabled": disabled
1270
1277
  }
1271
1278
  return web.json_response(ui)
1272
- app.router.add_get('/ui.json', ui_json_handler)
1279
+ app.router.add_get('/config', ui_config_handler)
1273
1280
 
1274
1281
  print(f"Starting server on port {port}...")
1275
1282
  web.run_app(app, host='0.0.0.0', port=port)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 2.0.9
3
+ Version: 2.0.11
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
@@ -97,34 +97,31 @@ pip install aiohttp
97
97
 
98
98
  ## Quick Start
99
99
 
100
- ### 1. Initialize Configuration
101
-
102
- Create a default configuration file:
103
-
104
- ```bash
105
- llms --init
106
- ```
107
-
108
- This saves the latest [llms.json](llms.json) configuration to `~/.llms/llms.json`.
109
-
110
- Modify `~/.llms/llms.json` to enable providers, add required API keys, additional models or any custom
111
- OpenAI-compatible providers.
112
-
113
- ### 2. Set API Keys
100
+ ### 1. Set API Keys
114
101
 
115
102
  Set environment variables for the providers you want to use:
116
103
 
117
104
  ```bash
118
- export OPENROUTER_API_KEY="..."
119
- export GROQ_API_KEY="..."
120
- export GOOGLE_API_KEY="..."
121
- export ANTHROPIC_API_KEY="..."
122
- export GROK_API_KEY="..."
123
- export DASHSCOPE_API_KEY="..."
124
- # ... etc
105
+ export OPENROUTER_FREE_API_KEY="..."
125
106
  ```
126
107
 
127
- ### 3. Enable Providers
108
+ | Provider | Variable | Description | Example |
109
+ |-----------------|---------------------------|---------------------|---------|
110
+ | openrouter_free | `OPENROUTER_FREE_API_KEY` | OpenRouter FREE models API key | `sk-or-...` |
111
+ | groq | `GROQ_API_KEY` | Groq API key | `gsk_...` |
112
+ | google_free | `GOOGLE_FREE_API_KEY` | Google FREE API key | `AIza...` |
113
+ | codestral | `CODESTRAL_API_KEY` | Codestral API key | `...` |
114
+ | ollama | N/A | No API key required | |
115
+ | openrouter | `OPENROUTER_API_KEY` | OpenRouter API key | `sk-or-...` |
116
+ | google | `GOOGLE_API_KEY` | Google API key | `AIza...` |
117
+ | anthropic | `ANTHROPIC_API_KEY` | Anthropic API key | `sk-ant-...` |
118
+ | openai | `OPENAI_API_KEY` | OpenAI API key | `sk-...` |
119
+ | grok | `GROK_API_KEY` | Grok (X.AI) API key | `xai-...` |
120
+ | qwen | `DASHSCOPE_API_KEY` | Qwen (Alibaba) API key | `sk-...` |
121
+ | z.ai | `ZAI_API_KEY` | Z.ai API key | `sk-...` |
122
+ | mistral | `MISTRAL_API_KEY` | Mistral API key | `...` |
123
+
124
+ ### 2. Enable Providers
128
125
 
129
126
  Enable the providers you want to use:
130
127
 
@@ -136,7 +133,17 @@ llms --enable openrouter_free google_free groq
136
133
  llms --enable openrouter anthropic google openai mistral grok qwen
137
134
  ```
138
135
 
139
- ### 4. Start Chatting
136
+ ### 3. Run UI
137
+
138
+ Start the UI and an OpenAI compatible API on port **8000**:
139
+
140
+ ```bash
141
+ llms --serve 8000
142
+ ```
143
+
144
+ Launches the UI at `http://localhost:8000` and an OpenAI Endpoint at `http://localhost:8000/v1/chat/completions`.
145
+
146
+ ### 4. Use llms.py CLI
140
147
 
141
148
  ```bash
142
149
  llms "What is the capital of France?"
@@ -144,7 +151,7 @@ llms "What is the capital of France?"
144
151
 
145
152
  ## Configuration
146
153
 
147
- The configuration file (`llms.json`) defines available providers, models, and default settings. Key sections:
154
+ The configuration file [llms.json](llms.json) is saved to `~/.llms/llms.json` and defines available providers, models, and default settings. Key sections:
148
155
 
149
156
  ### Defaults
150
157
  - `headers`: Common HTTP headers for all requests
@@ -159,6 +166,7 @@ Each provider configuration includes:
159
166
  - `base_url`: API endpoint URL
160
167
  - `models`: Model name mappings (local name → provider name)
161
168
 
169
+
162
170
  ## Command Line Usage
163
171
 
164
172
  ### Basic Chat
@@ -16,10 +16,20 @@ llms_py.egg-info/not-zip-safe
16
16
  llms_py.egg-info/requires.txt
17
17
  llms_py.egg-info/top_level.txt
18
18
  ui/App.mjs
19
+ ui/Avatar.mjs
20
+ ui/Brand.mjs
19
21
  ui/ChatPrompt.mjs
20
22
  ui/Main.mjs
23
+ ui/ModelSelector.mjs
24
+ ui/ProviderStatus.mjs
21
25
  ui/Recents.mjs
26
+ ui/SettingsDialog.mjs
22
27
  ui/Sidebar.mjs
28
+ ui/SignIn.mjs
29
+ ui/SystemPromptEditor.mjs
30
+ ui/SystemPromptSelector.mjs
31
+ ui/Welcome.mjs
32
+ ui/ai.mjs
23
33
  ui/app.css
24
34
  ui/fav.svg
25
35
  ui/markdown.mjs
@@ -30,8 +40,8 @@ ui/utils.mjs
30
40
  ui/lib/highlight.min.mjs
31
41
  ui/lib/idb.min.mjs
32
42
  ui/lib/marked.min.mjs
33
- ui/lib/servicestack-client.min.mjs
34
- ui/lib/servicestack-vue.min.mjs
43
+ ui/lib/servicestack-client.mjs
44
+ ui/lib/servicestack-vue.mjs
35
45
  ui/lib/vue-router.min.mjs
36
46
  ui/lib/vue.min.mjs
37
47
  ui/lib/vue.mjs
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "llms-py"
7
- version = "2.0.9"
7
+ version = "2.0.11"
8
8
  description = "A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers"
9
9
  readme = "README.md"
10
10
  license = "BSD-3-Clause"
@@ -16,7 +16,7 @@ with open(os.path.join(this_directory, "requirements.txt"), encoding="utf-8") as
16
16
 
17
17
  setup(
18
18
  name="llms-py",
19
- version="2.0.9",
19
+ version="2.0.11",
20
20
  author="ServiceStack",
21
21
  author_email="team@servicestack.net",
22
22
  description="A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers",
@@ -63,18 +63,28 @@ setup(
63
63
  (
64
64
  "ui",
65
65
  [
66
+ "ui/ai.mjs",
67
+ "ui/app.css",
66
68
  "ui/App.mjs",
69
+ "ui/Avatar.mjs",
70
+ "ui/Brand.mjs",
67
71
  "ui/ChatPrompt.mjs",
72
+ "ui/fav.svg",
68
73
  "ui/Main.mjs",
74
+ "ui/markdown.mjs",
75
+ "ui/ModelSelector.mjs",
76
+ "ui/ProviderStatus.mjs",
69
77
  "ui/Recents.mjs",
78
+ "ui/SettingsDialog.mjs",
70
79
  "ui/Sidebar.mjs",
71
- "ui/app.css",
72
- "ui/fav.svg",
73
- "ui/markdown.mjs",
80
+ "ui/SignIn.mjs",
81
+ "ui/SystemPromptEditor.mjs",
82
+ "ui/SystemPromptSelector.mjs",
74
83
  "ui/tailwind.input.css",
75
84
  "ui/threadStore.mjs",
76
85
  "ui/typography.css",
77
86
  "ui/utils.mjs",
87
+ "ui/Welcome.mjs",
78
88
  ],
79
89
  ),
80
90
  (
@@ -83,8 +93,8 @@ setup(
83
93
  "ui/lib/highlight.min.mjs",
84
94
  "ui/lib/idb.min.mjs",
85
95
  "ui/lib/marked.min.mjs",
86
- "ui/lib/servicestack-client.min.mjs",
87
- "ui/lib/servicestack-vue.min.mjs",
96
+ "ui/lib/servicestack-client.mjs",
97
+ "ui/lib/servicestack-vue.mjs",
88
98
  "ui/lib/vue-router.min.mjs",
89
99
  "ui/lib/vue.min.mjs",
90
100
  ],
@@ -0,0 +1,28 @@
1
+ import { computed, inject } from "vue"
2
+
3
+ export default {
4
+ template:`
5
+ <div v-if="$ai.auth?.profileUrl" :title="authTitle">
6
+ <img :src="$ai.auth.profileUrl" class="size-8 rounded-full" />
7
+ </div>
8
+ `,
9
+ setup() {
10
+ const ai = inject('ai')
11
+ const authTitle = computed(() => {
12
+ if (!ai.auth) return ''
13
+ const { userId, userName, displayName, bearerToken, roles } = ai.auth
14
+ const name = userName || displayName
15
+ const prefix = roles && roles.includes('Admin') ? 'Admin' : 'Name'
16
+ const sb = [
17
+ name ? `${prefix}: ${name}` : '',
18
+ `API Key: ${bearerToken}`,
19
+ `${userId}`,
20
+ ]
21
+ return sb.filter(x => x).join('\n')
22
+ })
23
+
24
+ return {
25
+ authTitle,
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ export default {
2
+ template:`
3
+ <div class="flex-shrink-0 px-4 py-4 border-b border-gray-200 bg-white min-h-16 select-none">
4
+ <div class="flex items-center justify-between">
5
+ <button type="button"
6
+ @click="$emit('home')"
7
+ class="text-lg font-semibold text-gray-900 hover:text-blue-600 focus:outline-none transition-colors"
8
+ title="Go back to initial state"
9
+ >
10
+ History
11
+ </button>
12
+ <button type="button"
13
+ @click="$emit('new')"
14
+ class="text-gray-900 hover:text-blue-600 focus:outline-none transition-colors"
15
+ title="New Chat"
16
+ >
17
+ <svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z"/></g></svg>
18
+ </button>
19
+ </div>
20
+ </div>
21
+ `,
22
+ emits:['home','new'],
23
+ }