llms-py 2.0.9__py3-none-any.whl → 2.0.10__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.py +14 -7
- llms_py-2.0.10.data/data/index.html +80 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/llms.json +1 -4
- llms_py-2.0.10.data/data/ui/Avatar.mjs +28 -0
- llms_py-2.0.10.data/data/ui/Brand.mjs +23 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/ChatPrompt.mjs +101 -69
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/Main.mjs +43 -183
- llms_py-2.0.10.data/data/ui/ModelSelector.mjs +29 -0
- llms_py-2.0.10.data/data/ui/ProviderStatus.mjs +105 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/Recents.mjs +2 -1
- llms_py-2.0.10.data/data/ui/SettingsDialog.mjs +374 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/Sidebar.mjs +11 -27
- llms_py-2.0.10.data/data/ui/SignIn.mjs +64 -0
- llms_py-2.0.10.data/data/ui/SystemPromptEditor.mjs +31 -0
- llms_py-2.0.10.data/data/ui/SystemPromptSelector.mjs +36 -0
- llms_py-2.0.10.data/data/ui/Welcome.mjs +8 -0
- llms_py-2.0.10.data/data/ui/ai.mjs +80 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/app.css +76 -10
- llms_py-2.0.10.data/data/ui/lib/servicestack-vue.mjs +37 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/markdown.mjs +9 -2
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/tailwind.input.css +13 -4
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/threadStore.mjs +2 -2
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/typography.css +109 -1
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/utils.mjs +8 -2
- {llms_py-2.0.9.dist-info → llms_py-2.0.10.dist-info}/METADATA +1 -1
- llms_py-2.0.10.dist-info/RECORD +40 -0
- llms_py-2.0.9.data/data/index.html +0 -64
- llms_py-2.0.9.data/data/ui/lib/servicestack-vue.min.mjs +0 -37
- llms_py-2.0.9.dist-info/RECORD +0 -30
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/requirements.txt +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/App.mjs +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/fav.svg +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/lib/highlight.min.mjs +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/lib/idb.min.mjs +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/lib/marked.min.mjs +0 -0
- /llms_py-2.0.9.data/data/ui/lib/servicestack-client.min.mjs → /llms_py-2.0.10.data/data/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui/lib/vue.min.mjs +0 -0
- {llms_py-2.0.9.data → llms_py-2.0.10.data}/data/ui.json +0 -0
- {llms_py-2.0.9.dist-info → llms_py-2.0.10.dist-info}/WHEEL +0 -0
- {llms_py-2.0.9.dist-info → llms_py-2.0.10.dist-info}/entry_points.txt +0 -0
- {llms_py-2.0.9.dist-info → llms_py-2.0.10.dist-info}/licenses/LICENSE +0 -0
- {llms_py-2.0.9.dist-info → llms_py-2.0.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { ref, computed, watch, inject } from 'vue'
|
|
2
|
+
import { storageObject } from './utils.mjs'
|
|
3
|
+
|
|
4
|
+
const settingsKey = 'llms.settings'
|
|
5
|
+
|
|
6
|
+
export function useSettings() {
|
|
7
|
+
const intFields = [
|
|
8
|
+
'max_completion_tokens',
|
|
9
|
+
'n',
|
|
10
|
+
'seed',
|
|
11
|
+
'top_logprobs',
|
|
12
|
+
]
|
|
13
|
+
const floatFields = [
|
|
14
|
+
'frequency_penalty',
|
|
15
|
+
'presence_penalty',
|
|
16
|
+
'temperature',
|
|
17
|
+
'top_p',
|
|
18
|
+
]
|
|
19
|
+
const boolFields = [
|
|
20
|
+
'enable_thinking',
|
|
21
|
+
'parallel_tool_calls',
|
|
22
|
+
'store',
|
|
23
|
+
]
|
|
24
|
+
const strFields = [
|
|
25
|
+
'prompt_cache_key',
|
|
26
|
+
'reasoning_effort',
|
|
27
|
+
'safety_identifier',
|
|
28
|
+
'service_tier',
|
|
29
|
+
'verbosity',
|
|
30
|
+
]
|
|
31
|
+
const listFields = [
|
|
32
|
+
'stop',
|
|
33
|
+
]
|
|
34
|
+
const allFields = [
|
|
35
|
+
...intFields,
|
|
36
|
+
...floatFields,
|
|
37
|
+
...boolFields,
|
|
38
|
+
...strFields,
|
|
39
|
+
...listFields,
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
let settings = ref(storageObject(settingsKey))
|
|
43
|
+
|
|
44
|
+
function validSettings(localSettings) {
|
|
45
|
+
const to = {}
|
|
46
|
+
intFields.forEach(f => {
|
|
47
|
+
if (localSettings[f] != null && localSettings[f] !== '' && !isNaN(parseInt(localSettings[f]))) {
|
|
48
|
+
to[f] = parseInt(localSettings[f])
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
floatFields.forEach(f => {
|
|
52
|
+
if (localSettings[f] != null && localSettings[f] !== '' && !isNaN(parseFloat(localSettings[f]))) {
|
|
53
|
+
to[f] = parseFloat(localSettings[f])
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
boolFields.forEach(f => {
|
|
57
|
+
if (localSettings[f] != null && localSettings[f] !== '' && !!localSettings[f]) {
|
|
58
|
+
to[f] = !!localSettings[f]
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
strFields.forEach(f => {
|
|
62
|
+
if (localSettings[f] != null && localSettings[f] !== '') {
|
|
63
|
+
to[f] = localSettings[f]
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
listFields.forEach(f => {
|
|
67
|
+
if (localSettings[f] != null && localSettings[f] !== '') {
|
|
68
|
+
to[f] = Array.isArray(localSettings[f])
|
|
69
|
+
? localSettings[f]
|
|
70
|
+
: typeof localSettings[f] == 'string'
|
|
71
|
+
? localSettings[f].split(',').map(x => x.trim())
|
|
72
|
+
: []
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
return to
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function applySettings(chatRequest) {
|
|
79
|
+
console.log('applySettings', JSON.stringify(settings.value, undefined, 2))
|
|
80
|
+
const removeFields = allFields.filter(f => !(f in settings.value))
|
|
81
|
+
removeFields.forEach(f => delete chatRequest[f])
|
|
82
|
+
Object.keys(settings.value).forEach(k => {
|
|
83
|
+
chatRequest[k] = settings.value[k]
|
|
84
|
+
})
|
|
85
|
+
console.log('applySettings.chatRequest', JSON.stringify(chatRequest, undefined, 2))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function resetSettings() {
|
|
89
|
+
return saveSettings({})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function saveSettings(localSettings) {
|
|
93
|
+
// console.log('saveSettings', JSON.stringify(localSettings, undefined, 2))
|
|
94
|
+
settings.value = validSettings(localSettings)
|
|
95
|
+
console.log('saveSettings.settings', JSON.stringify(settings.value, undefined, 2))
|
|
96
|
+
return storageObject(settingsKey, settings.value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
allFields,
|
|
101
|
+
settings,
|
|
102
|
+
applySettings,
|
|
103
|
+
saveSettings,
|
|
104
|
+
resetSettings,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default {
|
|
109
|
+
template: `
|
|
110
|
+
<div v-if="isOpen" class="fixed inset-0 z-50 overflow-y-auto" @click.self="close">
|
|
111
|
+
<div class="flex min-h-screen items-center justify-center p-4">
|
|
112
|
+
<!-- Backdrop -->
|
|
113
|
+
<div class="fixed inset-0 bg-black/40 transition-opacity" @click="close"></div>
|
|
114
|
+
|
|
115
|
+
<!-- Dialog -->
|
|
116
|
+
<div class="relative bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden">
|
|
117
|
+
<!-- Header -->
|
|
118
|
+
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-200">
|
|
119
|
+
<h2 class="text-xl font-semibold text-gray-900">Chat Request Settings</h2>
|
|
120
|
+
<button type="button" @click="close" class="text-gray-400 hover:text-gray-600">
|
|
121
|
+
<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
122
|
+
<path fill="currentColor" d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z"/>
|
|
123
|
+
</svg>
|
|
124
|
+
</button>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<!-- Content -->
|
|
128
|
+
<form class="px-6 py-4 overflow-y-auto max-h-[calc(90vh-140px)]" @submit.prevent="save">
|
|
129
|
+
<p class="text-sm text-gray-600 mb-4">
|
|
130
|
+
Configure default values for chat request options. Leave empty to use model defaults.
|
|
131
|
+
</p>
|
|
132
|
+
|
|
133
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
134
|
+
<!-- Temperature -->
|
|
135
|
+
<div>
|
|
136
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
137
|
+
Temperature
|
|
138
|
+
<span class="text-gray-500 font-normal">(0-2)</span>
|
|
139
|
+
</label>
|
|
140
|
+
<input type="number" v-model="localSettings.temperature"
|
|
141
|
+
step="0.1" min="0" max="2"
|
|
142
|
+
placeholder="e.g., 0.7"
|
|
143
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
144
|
+
<p class="mt-1 text-xs text-gray-500">Higher values more random, lower for more focus</p>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- Max Completion Tokens -->
|
|
148
|
+
<div>
|
|
149
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
150
|
+
Max Completion Tokens
|
|
151
|
+
</label>
|
|
152
|
+
<input type="number" v-model="localSettings.max_completion_tokens"
|
|
153
|
+
step="1" min="1"
|
|
154
|
+
placeholder="e.g., 2048"
|
|
155
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
156
|
+
<p class="mt-1 text-xs text-gray-500">Max tokens for completion (inc. reasoning tokens)</p>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- Seed -->
|
|
160
|
+
<div>
|
|
161
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
162
|
+
Seed
|
|
163
|
+
</label>
|
|
164
|
+
<input type="number" v-model="localSettings.seed"
|
|
165
|
+
step="1"
|
|
166
|
+
placeholder="e.g., 42"
|
|
167
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
168
|
+
<p class="mt-1 text-xs text-gray-500">For deterministic sampling (Beta feature)</p>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<!-- Top P -->
|
|
172
|
+
<div>
|
|
173
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
174
|
+
Top P
|
|
175
|
+
<span class="text-gray-500 font-normal">(0-1)</span>
|
|
176
|
+
</label>
|
|
177
|
+
<input type="number" v-model="localSettings.top_p"
|
|
178
|
+
step="0.1" min="0" max="1"
|
|
179
|
+
placeholder="e.g., 0.9"
|
|
180
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
181
|
+
<p class="mt-1 text-xs text-gray-500">Nucleus sampling - alternative to temperature</p>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<!-- Frequency Penalty -->
|
|
185
|
+
<div>
|
|
186
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
187
|
+
Frequency Penalty
|
|
188
|
+
<span class="text-gray-500 font-normal">(-2.0 to 2.0)</span>
|
|
189
|
+
</label>
|
|
190
|
+
<input type="number" v-model="localSettings.frequency_penalty"
|
|
191
|
+
step="0.1" min="-2" max="2"
|
|
192
|
+
placeholder="e.g., 0.5"
|
|
193
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
194
|
+
<p class="mt-1 text-xs text-gray-500">Penalize tokens based on frequency in text</p>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<!-- Presence Penalty -->
|
|
198
|
+
<div>
|
|
199
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
200
|
+
Presence Penalty
|
|
201
|
+
<span class="text-gray-500 font-normal">(-2.0 to 2.0)</span>
|
|
202
|
+
</label>
|
|
203
|
+
<input type="number" v-model="localSettings.presence_penalty"
|
|
204
|
+
step="0.1" min="-2" max="2"
|
|
205
|
+
placeholder="e.g., 0.5"
|
|
206
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
207
|
+
<p class="mt-1 text-xs text-gray-500">Penalize tokens based on presence in text</p>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<!-- Stop Sequences -->
|
|
211
|
+
<div>
|
|
212
|
+
<label for="stop" class="block text-sm font-medium text-gray-700 mb-1">
|
|
213
|
+
Stop Sequences
|
|
214
|
+
</label>
|
|
215
|
+
<TagInput id="stop" inputClass="h-[37px] !shadow-none"
|
|
216
|
+
v-model="localSettings.stop"
|
|
217
|
+
placeholder=""
|
|
218
|
+
label=""
|
|
219
|
+
/>
|
|
220
|
+
<p class="mt-1 text-xs text-gray-500">Up to 4 sequences where API stops generating</p>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<!-- Reasoning Effort -->
|
|
224
|
+
<div>
|
|
225
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
226
|
+
Reasoning Effort
|
|
227
|
+
</label>
|
|
228
|
+
<select v-model="localSettings.reasoning_effort"
|
|
229
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
230
|
+
<option value="">Default</option>
|
|
231
|
+
<option value="minimal">Minimal</option>
|
|
232
|
+
<option value="low">Low</option>
|
|
233
|
+
<option value="medium">Medium</option>
|
|
234
|
+
<option value="high">High</option>
|
|
235
|
+
</select>
|
|
236
|
+
<p class="mt-1 text-xs text-gray-500">Constrains effort on reasoning for reasoning models</p>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<!-- Verbosity -->
|
|
240
|
+
<div>
|
|
241
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
242
|
+
Verbosity
|
|
243
|
+
</label>
|
|
244
|
+
<select v-model="localSettings.verbosity"
|
|
245
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
246
|
+
<option value="">Default</option>
|
|
247
|
+
<option value="low">Low</option>
|
|
248
|
+
<option value="medium">Medium</option>
|
|
249
|
+
<option value="high">High</option>
|
|
250
|
+
</select>
|
|
251
|
+
<p class="mt-1 text-xs text-gray-500">Constrains verbosity of model's response</p>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<!-- Service Tier -->
|
|
255
|
+
<div>
|
|
256
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
257
|
+
Service Tier
|
|
258
|
+
</label>
|
|
259
|
+
<input type="text" v-model="localSettings.service_tier"
|
|
260
|
+
placeholder="e.g., auto, default"
|
|
261
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
262
|
+
<p class="mt-1 text-xs text-gray-500">Processing type for serving the request</p>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<!-- Top Logprobs -->
|
|
266
|
+
<div>
|
|
267
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
268
|
+
Top Logprobs
|
|
269
|
+
<span class="text-gray-500 font-normal">(0-20)</span>
|
|
270
|
+
</label>
|
|
271
|
+
<input type="number" v-model="localSettings.top_logprobs"
|
|
272
|
+
step="1" min="0" max="20"
|
|
273
|
+
placeholder="e.g., 5"
|
|
274
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
275
|
+
<p class="mt-1 text-xs text-gray-500">Number of most likely tokens to return with log probs</p>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<!-- Safety Identifier -->
|
|
279
|
+
<div>
|
|
280
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">
|
|
281
|
+
Safety Identifier
|
|
282
|
+
</label>
|
|
283
|
+
<input type="text" v-model="localSettings.safety_identifier"
|
|
284
|
+
placeholder="Unique user identifier"
|
|
285
|
+
class="block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" />
|
|
286
|
+
<p class="mt-1 text-xs text-gray-500">Identifier to help detect policy violations</p>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<!-- Store -->
|
|
290
|
+
<div>
|
|
291
|
+
<label class="flex items-center">
|
|
292
|
+
<input type="checkbox" v-model="localSettings.store"
|
|
293
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" />
|
|
294
|
+
<span class="ml-2 text-sm font-medium text-gray-700">Store Output</span>
|
|
295
|
+
</label>
|
|
296
|
+
<p class="mt-1 text-xs text-gray-500">Store output for model distillation or evals</p>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<!-- Enable Thinking -->
|
|
300
|
+
<div>
|
|
301
|
+
<label class="flex items-center">
|
|
302
|
+
<input type="checkbox" v-model="localSettings.enable_thinking"
|
|
303
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" />
|
|
304
|
+
<span class="ml-2 text-sm font-medium text-gray-700">Enable Thinking</span>
|
|
305
|
+
</label>
|
|
306
|
+
<p class="mt-1 text-xs text-gray-500">Enable thinking mode for supported models (Qwen)</p>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</form>
|
|
310
|
+
|
|
311
|
+
<!-- Footer -->
|
|
312
|
+
<div class="flex items-center justify-between px-6 py-4 border-t border-gray-200 bg-gray-50">
|
|
313
|
+
<button type="button" @click="reset"
|
|
314
|
+
class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-900">
|
|
315
|
+
Reset to Defaults
|
|
316
|
+
</button>
|
|
317
|
+
<div class="flex space-x-3">
|
|
318
|
+
<button type="button" @click="close"
|
|
319
|
+
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50">
|
|
320
|
+
Cancel
|
|
321
|
+
</button>
|
|
322
|
+
<button type="submit" @click="save"
|
|
323
|
+
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700">
|
|
324
|
+
Save Settings
|
|
325
|
+
</button>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
`,
|
|
332
|
+
props: {
|
|
333
|
+
isOpen: {
|
|
334
|
+
type: Boolean,
|
|
335
|
+
default: false
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
emits: ['close'],
|
|
339
|
+
setup(props, { emit }) {
|
|
340
|
+
const chatSettings = inject('chatSettings')
|
|
341
|
+
const { settings, saveSettings, resetSettings } = chatSettings
|
|
342
|
+
|
|
343
|
+
// Local copy for editing
|
|
344
|
+
const localSettings = ref(Object.assign({}, settings.value))
|
|
345
|
+
|
|
346
|
+
// Watch for dialog open to sync local settings
|
|
347
|
+
watch(() => props.isOpen, (isOpen) => {
|
|
348
|
+
if (isOpen) {
|
|
349
|
+
localSettings.value = Object.assign({}, settings.value)
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
function close() {
|
|
354
|
+
emit('close')
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function save() {
|
|
358
|
+
saveSettings(localSettings.value)
|
|
359
|
+
close()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function reset() {
|
|
363
|
+
localSettings.value = resetSettings()
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return {
|
|
367
|
+
localSettings,
|
|
368
|
+
close,
|
|
369
|
+
save,
|
|
370
|
+
reset,
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { onMounted } from 'vue'
|
|
1
|
+
import { onMounted, inject } from 'vue'
|
|
2
2
|
import { useRouter } from 'vue-router'
|
|
3
3
|
import { useThreadStore } from './threadStore.mjs'
|
|
4
|
+
import Brand from './Brand.mjs'
|
|
4
5
|
|
|
5
6
|
// Thread Item Component
|
|
6
7
|
const ThreadItem = {
|
|
@@ -127,7 +128,7 @@ const GroupedThreads = {
|
|
|
127
128
|
/>
|
|
128
129
|
</div>
|
|
129
130
|
<div class="mb-4 flex w-full justify-center">
|
|
130
|
-
<button @click="$router.push('/recents')" type="button"
|
|
131
|
+
<button @click="$router.push($ai.base + '/recents')" type="button"
|
|
131
132
|
class="flex text-sm space-x-1 font-semibold text-gray-900 hover:text-blue-600 focus:outline-none transition-colors">
|
|
132
133
|
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M8 2.19c3.13 0 5.68 2.25 5.68 5s-2.55 5-5.68 5a5.7 5.7 0 0 1-1.89-.29l-.75-.26l-.56.56a14 14 0 0 1-2 1.55a.13.13 0 0 1-.07 0v-.06a6.58 6.58 0 0 0 .15-4.29a5.25 5.25 0 0 1-.55-2.16c0-2.77 2.55-5 5.68-5M8 .94c-3.83 0-6.93 2.81-6.93 6.27a6.4 6.4 0 0 0 .64 2.64a5.53 5.53 0 0 1-.18 3.48a1.32 1.32 0 0 0 2 1.5a15 15 0 0 0 2.16-1.71a6.8 6.8 0 0 0 2.31.36c3.83 0 6.93-2.81 6.93-6.27S11.83.94 8 .94"></path><ellipse cx="5.2" cy="7.7" fill="currentColor" rx=".8" ry=".75"></ellipse><ellipse cx="8" cy="7.7" fill="currentColor" rx=".8" ry=".75"></ellipse><ellipse cx="10.8" cy="7.7" fill="currentColor" rx=".8" ry=".75"></ellipse></svg>
|
|
133
134
|
<span>All Chats</span>
|
|
@@ -146,31 +147,13 @@ const GroupedThreads = {
|
|
|
146
147
|
|
|
147
148
|
const Sidebar = {
|
|
148
149
|
components: {
|
|
150
|
+
Brand,
|
|
149
151
|
GroupedThreads,
|
|
150
152
|
ThreadItem,
|
|
151
153
|
},
|
|
152
154
|
template: `
|
|
153
155
|
<div class="flex flex-col h-full bg-gray-50 border-r border-gray-200">
|
|
154
|
-
|
|
155
|
-
<div class="flex-shrink-0 px-4 py-4 border-b border-gray-200 bg-white min-h-16 select-none">
|
|
156
|
-
<div class="flex items-center justify-between">
|
|
157
|
-
<button type="button"
|
|
158
|
-
@click="goToInitialState"
|
|
159
|
-
class="text-lg font-semibold text-gray-900 hover:text-blue-600 focus:outline-none transition-colors"
|
|
160
|
-
title="Go back to initial state"
|
|
161
|
-
>
|
|
162
|
-
History
|
|
163
|
-
</button>
|
|
164
|
-
<button type="button"
|
|
165
|
-
@click="createNewThread"
|
|
166
|
-
class="text-gray-900 hover:text-blue-600 focus:outline-none transition-colors"
|
|
167
|
-
title="New Chat"
|
|
168
|
-
>
|
|
169
|
-
<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>
|
|
170
|
-
</button>
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
|
|
156
|
+
<Brand @home="goToInitialState" @new="createNewThread" />
|
|
174
157
|
<!-- Thread List -->
|
|
175
158
|
<div class="flex-1 overflow-y-auto">
|
|
176
159
|
<div v-if="isLoading" class="p-4 text-center text-gray-500">
|
|
@@ -187,13 +170,14 @@ const Sidebar = {
|
|
|
187
170
|
</div>
|
|
188
171
|
|
|
189
172
|
<div v-else class="py-2">
|
|
190
|
-
<GroupedThreads :currentThread="currentThread" :groupedThreads="threadStore.getGroupedThreads(
|
|
173
|
+
<GroupedThreads :currentThread="currentThread" :groupedThreads="threadStore.getGroupedThreads(18)"
|
|
191
174
|
@select="selectThread" @delete="deleteThread" />
|
|
192
175
|
</div>
|
|
193
176
|
</div>
|
|
194
177
|
</div>
|
|
195
178
|
`,
|
|
196
179
|
setup() {
|
|
180
|
+
const ai = inject('ai')
|
|
197
181
|
const router = useRouter()
|
|
198
182
|
const threadStore = useThreadStore()
|
|
199
183
|
const {
|
|
@@ -212,7 +196,7 @@ const Sidebar = {
|
|
|
212
196
|
})
|
|
213
197
|
|
|
214
198
|
const selectThread = async (threadId) => {
|
|
215
|
-
router.push(
|
|
199
|
+
router.push(`${ai.base}/c/${threadId}`)
|
|
216
200
|
}
|
|
217
201
|
|
|
218
202
|
const deleteThread = async (threadId) => {
|
|
@@ -220,19 +204,19 @@ const Sidebar = {
|
|
|
220
204
|
const wasCurrent = currentThread?.value?.id === threadId
|
|
221
205
|
await deleteThreadFromStore(threadId)
|
|
222
206
|
if (wasCurrent) {
|
|
223
|
-
router.push(
|
|
207
|
+
router.push(`${ai.base}/`)
|
|
224
208
|
}
|
|
225
209
|
}
|
|
226
210
|
}
|
|
227
211
|
|
|
228
212
|
const createNewThread = async () => {
|
|
229
213
|
const newThread = await createThread()
|
|
230
|
-
router.push(
|
|
214
|
+
router.push(`${ai.base}/c/${newThread.id}`)
|
|
231
215
|
}
|
|
232
216
|
|
|
233
217
|
const goToInitialState = () => {
|
|
234
218
|
clearCurrentThread()
|
|
235
|
-
router.push(
|
|
219
|
+
router.push(`${ai.base}/`)
|
|
236
220
|
}
|
|
237
221
|
|
|
238
222
|
return {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { inject, ref } from "vue"
|
|
2
|
+
import { toJsonObject } from "./utils.mjs"
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
template: `
|
|
6
|
+
<div class="min-h-full -mt-12 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
|
7
|
+
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
|
8
|
+
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-gray-50">
|
|
9
|
+
Sign In
|
|
10
|
+
</h2>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
13
|
+
<ErrorSummary v-if="errorSummary" class="mb-3" :status="errorSummary" />
|
|
14
|
+
<div class="bg-white dark:bg-black py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
|
15
|
+
<form @submit.prevent="submit">
|
|
16
|
+
<div class="flex flex-1 flex-col justify-between">
|
|
17
|
+
<div class="space-y-6">
|
|
18
|
+
<fieldset class="grid grid-cols-12 gap-6">
|
|
19
|
+
<div class="w-full col-span-12">
|
|
20
|
+
<TextInput id="apiKey" name="apiKey" label="API Key" v-model="apiKey" />
|
|
21
|
+
</div>
|
|
22
|
+
</fieldset>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="mt-8">
|
|
26
|
+
<PrimaryButton class="w-full">Sign In</PrimaryButton>
|
|
27
|
+
</div>
|
|
28
|
+
</form>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
`,
|
|
33
|
+
emits: ['done'],
|
|
34
|
+
setup(props, { emit }) {
|
|
35
|
+
const ai = inject('ai')
|
|
36
|
+
const apiKey = ref('')
|
|
37
|
+
const errorSummary = ref()
|
|
38
|
+
async function submit() {
|
|
39
|
+
const r = await ai.get('/auth', {
|
|
40
|
+
headers: {
|
|
41
|
+
'Authorization': `Bearer ${apiKey.value}`
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
const txt = await r.text()
|
|
45
|
+
const json = toJsonObject(txt)
|
|
46
|
+
// console.log('json', json)
|
|
47
|
+
if (r.ok) {
|
|
48
|
+
json.apiKey = apiKey.value
|
|
49
|
+
emit('done', json)
|
|
50
|
+
} else {
|
|
51
|
+
errorSummary.value = json.responseStatus || {
|
|
52
|
+
errorCode: "Unauthorized",
|
|
53
|
+
message: 'Invalid API Key'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
apiKey,
|
|
60
|
+
submit,
|
|
61
|
+
errorSummary,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
template:`
|
|
3
|
+
<div class="border-b border-gray-200 bg-gray-50 px-6 py-4">
|
|
4
|
+
<div class="max-w-6xl mx-auto">
|
|
5
|
+
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
6
|
+
System Prompt
|
|
7
|
+
<span v-if="selected" class="text-gray-500 font-normal">
|
|
8
|
+
({{ prompts.find(p => p.id === selected.id)?.name || 'Custom' }})
|
|
9
|
+
</span>
|
|
10
|
+
</label>
|
|
11
|
+
<textarea
|
|
12
|
+
:value="modelValue" @input="$emit('update:modelValue', $event.target.value)"
|
|
13
|
+
placeholder="Enter a system prompt to guide AI's behavior..."
|
|
14
|
+
rows="6"
|
|
15
|
+
class="block w-full resize-vertical rounded-md border border-gray-300 px-3 py-2 text-sm placeholder-gray-500 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
|
16
|
+
></textarea>
|
|
17
|
+
<div class="mt-2 text-xs text-gray-500">
|
|
18
|
+
You can modify this system prompt before sending messages. Changes will only apply to new conversations.
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
`,
|
|
23
|
+
emits: ['update:modelValue'],
|
|
24
|
+
props: {
|
|
25
|
+
prompts: Array,
|
|
26
|
+
selected: Object,
|
|
27
|
+
modelValue: String,
|
|
28
|
+
},
|
|
29
|
+
setup() {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
template:`
|
|
3
|
+
<button v-if="modelValue" type="button" title="Clear System Prompt" @click="$emit('update:modelValue', null)">
|
|
4
|
+
<svg class="size-4 text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z"/></svg>
|
|
5
|
+
</button>
|
|
6
|
+
|
|
7
|
+
<Autocomplete id="prompt" :options="prompts" label=""
|
|
8
|
+
:modelValue="modelValue" @update:modelValue="$emit('update:modelValue', $event)"
|
|
9
|
+
class="w-72 xl:w-84"
|
|
10
|
+
:match="(x, value) => x.name.toLowerCase().includes(value.toLowerCase())"
|
|
11
|
+
placeholder="Select a System Prompt...">
|
|
12
|
+
<template #item="{ value }">
|
|
13
|
+
<div class="truncate max-w-72" :title="value">{{value}}</div>
|
|
14
|
+
</template>
|
|
15
|
+
</Autocomplete>
|
|
16
|
+
|
|
17
|
+
<!-- Toggle System Prompt Visibility -->
|
|
18
|
+
<button type="button"
|
|
19
|
+
@click="$emit('toggle')"
|
|
20
|
+
:class="show ? 'text-blue-700' : 'text-gray-600'"
|
|
21
|
+
class="p-1 rounded-md hover:bg-blue-100 hover:text-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
|
|
22
|
+
:title="show ? 'Hide system prompt' : 'Show system prompt'"
|
|
23
|
+
>
|
|
24
|
+
<svg v-if="!show" class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="currentColor" d="M33.62 17.53c-3.37-6.23-9.28-10-15.82-10S5.34 11.3 2 17.53l-.28.47l.26.48c3.37 6.23 9.28 10 15.82 10s12.46-3.72 15.82-10l.26-.48Zm-15.82 8.9C12.17 26.43 7 23.29 4 18c3-5.29 8.17-8.43 13.8-8.43S28.54 12.72 31.59 18c-3.05 5.29-8.17 8.43-13.79 8.43"/><path fill="currentColor" d="M18.09 11.17A6.86 6.86 0 1 0 25 18a6.86 6.86 0 0 0-6.91-6.83m0 11.72A4.86 4.86 0 1 1 23 18a4.87 4.87 0 0 1-4.91 4.89"/><path fill="none" d="M0 0h36v36H0z"/></svg>
|
|
25
|
+
<svg v-else class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="currentColor" d="M25.19 20.4a6.8 6.8 0 0 0 .43-2.4a6.86 6.86 0 0 0-6.86-6.86a6.8 6.8 0 0 0-2.37.43L18 13.23a5 5 0 0 1 .74-.06A4.87 4.87 0 0 1 23.62 18a5 5 0 0 1-.06.74Z" class="clr-i-outline clr-i-outline-path-1"/><path fill="currentColor" d="M34.29 17.53c-3.37-6.23-9.28-10-15.82-10a16.8 16.8 0 0 0-5.24.85L14.84 10a14.8 14.8 0 0 1 3.63-.47c5.63 0 10.75 3.14 13.8 8.43a17.8 17.8 0 0 1-4.37 5.1l1.42 1.42a19.9 19.9 0 0 0 5-6l.26-.48Z"/><path fill="currentColor" d="m4.87 5.78l4.46 4.46a19.5 19.5 0 0 0-6.69 7.29l-.26.47l.26.48c3.37 6.23 9.28 10 15.82 10a16.9 16.9 0 0 0 7.37-1.69l5 5l1.75-1.5l-26-26Zm9.75 9.75l6.65 6.65a4.8 4.8 0 0 1-2.5.72A4.87 4.87 0 0 1 13.9 18a4.8 4.8 0 0 1 .72-2.47m-1.45-1.45a6.85 6.85 0 0 0 9.55 9.55l1.6 1.6a14.9 14.9 0 0 1-5.86 1.2c-5.63 0-10.75-3.14-13.8-8.43a17.3 17.3 0 0 1 6.12-6.3Z"/><path fill="none" d="M0 0h36v36H0z"/></svg>
|
|
26
|
+
</button>
|
|
27
|
+
`,
|
|
28
|
+
emits: ['updated', 'update:modelValue', 'toggle'],
|
|
29
|
+
props: {
|
|
30
|
+
prompts: Array,
|
|
31
|
+
modelValue: Object,
|
|
32
|
+
show: Boolean,
|
|
33
|
+
},
|
|
34
|
+
setup() {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
template: `
|
|
3
|
+
<div class="mb-2 flex justify-center">
|
|
4
|
+
<svg class="size-20 text-gray-700" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M8 2.19c3.13 0 5.68 2.25 5.68 5s-2.55 5-5.68 5a5.7 5.7 0 0 1-1.89-.29l-.75-.26l-.56.56a14 14 0 0 1-2 1.55a.13.13 0 0 1-.07 0v-.06a6.58 6.58 0 0 0 .15-4.29a5.25 5.25 0 0 1-.55-2.16c0-2.77 2.55-5 5.68-5M8 .94c-3.83 0-6.93 2.81-6.93 6.27a6.4 6.4 0 0 0 .64 2.64a5.53 5.53 0 0 1-.18 3.48a1.32 1.32 0 0 0 2 1.5a15 15 0 0 0 2.16-1.71a6.8 6.8 0 0 0 2.31.36c3.83 0 6.93-2.81 6.93-6.27S11.83.94 8 .94"/><ellipse cx="5.2" cy="7.7" fill="currentColor" rx=".8" ry=".75"/><ellipse cx="8" cy="7.7" fill="currentColor" rx=".8" ry=".75"/><ellipse cx="10.8" cy="7.7" fill="currentColor" rx=".8" ry=".75"/></svg>
|
|
5
|
+
</div>
|
|
6
|
+
<h2 class="text-2xl font-semibold text-gray-900 mb-2">{{ $ai.welcome }}</h2>
|
|
7
|
+
`
|
|
8
|
+
}
|