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.
- {llms_py-2.0.9/llms_py.egg-info → llms_py-2.0.11}/PKG-INFO +33 -25
- {llms_py-2.0.9 → llms_py-2.0.11}/README.md +32 -24
- llms_py-2.0.11/index.html +80 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/llms.json +5 -5
- {llms_py-2.0.9 → llms_py-2.0.11}/llms.py +14 -7
- {llms_py-2.0.9 → llms_py-2.0.11/llms_py.egg-info}/PKG-INFO +33 -25
- {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/SOURCES.txt +12 -2
- {llms_py-2.0.9 → llms_py-2.0.11}/pyproject.toml +1 -1
- {llms_py-2.0.9 → llms_py-2.0.11}/setup.py +16 -6
- llms_py-2.0.11/ui/Avatar.mjs +28 -0
- llms_py-2.0.11/ui/Brand.mjs +23 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/ChatPrompt.mjs +101 -69
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/Main.mjs +43 -183
- llms_py-2.0.11/ui/ModelSelector.mjs +29 -0
- llms_py-2.0.11/ui/ProviderStatus.mjs +105 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/Recents.mjs +2 -1
- llms_py-2.0.11/ui/SettingsDialog.mjs +374 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/Sidebar.mjs +11 -27
- llms_py-2.0.11/ui/SignIn.mjs +64 -0
- llms_py-2.0.11/ui/SystemPromptEditor.mjs +31 -0
- llms_py-2.0.11/ui/SystemPromptSelector.mjs +36 -0
- llms_py-2.0.11/ui/Welcome.mjs +8 -0
- llms_py-2.0.11/ui/ai.mjs +80 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/app.css +76 -10
- llms_py-2.0.11/ui/lib/servicestack-vue.mjs +37 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/markdown.mjs +9 -2
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/tailwind.input.css +13 -4
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/threadStore.mjs +2 -2
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/typography.css +109 -1
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/utils.mjs +8 -2
- llms_py-2.0.9/index.html +0 -64
- llms_py-2.0.9/ui/lib/servicestack-vue.min.mjs +0 -37
- {llms_py-2.0.9 → llms_py-2.0.11}/LICENSE +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/MANIFEST.in +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/dependency_links.txt +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/entry_points.txt +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/not-zip-safe +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/requires.txt +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/llms_py.egg-info/top_level.txt +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/requirements.txt +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/setup.cfg +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/App.mjs +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/fav.svg +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/highlight.min.mjs +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/idb.min.mjs +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/marked.min.mjs +0 -0
- /llms_py-2.0.9/ui/lib/servicestack-client.min.mjs → /llms_py-2.0.11/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/vue.min.mjs +0 -0
- {llms_py-2.0.9 → llms_py-2.0.11}/ui/lib/vue.mjs +0 -0
- {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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
###
|
|
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 (
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
###
|
|
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 (
|
|
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": "$
|
|
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":
|
|
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.
|
|
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 =
|
|
68
|
-
item['file']['file_data'] = prefix + f",({len(
|
|
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
|
-
|
|
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
|
|
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
|
|
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('/
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
###
|
|
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 (
|
|
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.
|
|
34
|
-
ui/lib/servicestack-vue.
|
|
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.
|
|
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.
|
|
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/
|
|
72
|
-
"ui/
|
|
73
|
-
"ui/
|
|
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.
|
|
87
|
-
"ui/lib/servicestack-vue.
|
|
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
|
+
}
|