llms-py 2.0.28__py3-none-any.whl → 2.0.30__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/ui/Main.mjs CHANGED
@@ -30,11 +30,13 @@ export default {
30
30
  template: `
31
31
  <div class="flex flex-col h-full w-full">
32
32
  <!-- Header with model and prompt selectors (hidden when auth required and not authenticated) -->
33
- <div v-if="!($ai.requiresAuth && !$ai.auth)" class="border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-2 py-2 w-full min-h-16">
34
- <div class="flex items-center justify-between w-full">
33
+ <div v-if="!($ai.requiresAuth && !$ai.auth)"
34
+ :class="!$ai.isSidebarOpen ? 'pl-6' : ''"
35
+ class="border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 px-2 py-2 w-full min-h-16">
36
+ <div class="flex flex-wrap items-center justify-between w-full">
35
37
  <ModelSelector :models="models" v-model="selectedModel" @updated="configUpdated" />
36
38
 
37
- <div class="flex items-center space-x-2">
39
+ <div class="flex items-center space-x-2 pl-4">
38
40
  <SystemPromptSelector :prompts="prompts" v-model="selectedPrompt"
39
41
  :show="showSystemPrompt" @toggle="showSystemPrompt = !showSystemPrompt" />
40
42
  <Avatar />
@@ -218,7 +220,7 @@ export default {
218
220
  </div>
219
221
 
220
222
  <!-- Loading indicator -->
221
- <div v-if="isGenerating" class="flex items-start space-x-3">
223
+ <div v-if="isGenerating" class="flex items-start space-x-3 group">
222
224
  <!-- Avatar outside the bubble -->
223
225
  <div class="flex-shrink-0">
224
226
  <div class="w-8 h-8 rounded-full bg-gray-600 dark:bg-gray-500 text-white flex items-center justify-center text-sm font-medium">
@@ -234,6 +236,13 @@ export default {
234
236
  <div class="w-2 h-2 bg-gray-400 dark:bg-gray-500 rounded-full animate-bounce" style="animation-delay: 0.2s"></div>
235
237
  </div>
236
238
  </div>
239
+
240
+ <!-- Cancel button -->
241
+ <button type="button" @click="cancelRequest"
242
+ class="px-3 py-1 rounded text-sm text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 border border-transparent hover:border-red-300 dark:hover:border-red-600 transition-all"
243
+ title="Cancel request">
244
+ cancel
245
+ </button>
237
246
  </div>
238
247
 
239
248
  <!-- Error message bubble -->
@@ -741,6 +750,11 @@ export default {
741
750
  editingMessage.value = null
742
751
  }
743
752
 
753
+ // Cancel pending request
754
+ const cancelRequest = () => {
755
+ chatPrompt.cancel()
756
+ }
757
+
744
758
  function tokensTitle(usage) {
745
759
  let title = []
746
760
  if (usage.tokens && usage.price) {
@@ -790,6 +804,7 @@ export default {
790
804
  editMessage,
791
805
  saveEditedMessage,
792
806
  cancelEdit,
807
+ cancelRequest,
793
808
  configUpdated,
794
809
  exportThreads,
795
810
  exportRequests,
llms/ui/ModelSelector.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { ref, onMounted, onUnmounted } from "vue"
1
2
  import ProviderStatus from "./ProviderStatus.mjs"
2
3
  import ProviderIcon from "./ProviderIcon.mjs"
3
4
 
@@ -9,15 +10,16 @@ export default {
9
10
  template:`
10
11
  <!-- Model Selector -->
11
12
  <div class="pl-1 flex space-x-2">
12
- <Autocomplete id="model" :options="models" label=""
13
+ <Autocomplete ref="refSelector" id="model" :options="models" label=""
13
14
  :modelValue="modelValue" @update:modelValue="$emit('update:modelValue', $event)"
14
15
  class="w-72 xl:w-84"
15
16
  :match="(x, value) => x.id.toLowerCase().includes(value.toLowerCase())"
16
17
  placeholder="Select Model...">
17
18
  <template #item="{ id, provider, provider_model, pricing }">
18
- <div :key="id + provider + provider_model" class="group truncate max-w-72 flex justify-between">
19
+ <div :key="id + provider + provider_model"
20
+ class="group truncate max-w-68 xl:max-w-72 flex justify-between">
19
21
  <span :title="id">{{id}}</span>
20
- <span class="flex items-center space-x-1">
22
+ <div class="hidden md:flex items-center space-x-1">
21
23
  <span v-if="pricing && (parseFloat(pricing.input) == 0 && parseFloat(pricing.input) == 0)">
22
24
  <span class="text-xs text-gray-500 dark:text-gray-400" title="Free to use">FREE</span>
23
25
  </span>
@@ -27,10 +29,10 @@ export default {
27
29
  &#183;
28
30
  {{tokenPrice(pricing.output)}} M
29
31
  </span>
30
- <span :title="provider_model + ' from ' + provider">
31
- <ProviderIcon :provider="provider" />
32
+ <span class="min-w-6" :title="provider_model + ' from ' + provider">
33
+ <ProviderIcon class="hidden xl:inline" :provider="provider" />
32
34
  </span>
33
- </span>
35
+ </div>
34
36
  </div>
35
37
  </template>
36
38
  </Autocomplete>
@@ -52,8 +54,25 @@ export default {
52
54
  return ret.endsWith('.00') ? ret.slice(0, -3) : ret
53
55
  }
54
56
 
57
+ const refSelector = ref()
58
+
59
+ function collapse(e) {
60
+ // call toggle when clicking outside of the Autocomplete component
61
+ if (refSelector.value && !refSelector.value.$el.contains(e.target)) {
62
+ refSelector.value.toggle(false)
63
+ }
64
+ }
65
+
66
+ onMounted(() => {
67
+ document.addEventListener('click', collapse)
68
+ })
69
+ onUnmounted(() => {
70
+ document.removeEventListener('click', collapse)
71
+ })
72
+
55
73
  return {
56
- tokenPrice
74
+ refSelector,
75
+ tokenPrice,
57
76
  }
58
77
  }
59
78
  }
llms/ui/Sidebar.mjs CHANGED
@@ -164,7 +164,7 @@ const Sidebar = {
164
164
  },
165
165
  template: `
166
166
  <div class="flex flex-col h-full bg-gray-50 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700">
167
- <Brand @home="goToInitialState" @new="createNewThread" @analytics="goToAnalytics" />
167
+ <Brand @home="goToInitialState" @new="createNewThread" @analytics="goToAnalytics" @toggle-sidebar="$emit('toggle-sidebar')" />
168
168
  <!-- Thread List -->
169
169
  <div class="flex-1 overflow-y-auto">
170
170
  <div v-if="isLoading" class="p-4 text-center text-gray-500 dark:text-gray-400">
@@ -187,7 +187,8 @@ const Sidebar = {
187
187
  </div>
188
188
  </div>
189
189
  `,
190
- setup() {
190
+ emits: ['thread-selected', 'toggle-sidebar'],
191
+ setup(props, { emit }) {
191
192
  const ai = inject('ai')
192
193
  const router = useRouter()
193
194
  const threadStore = useThreadStore()
@@ -208,6 +209,7 @@ const Sidebar = {
208
209
 
209
210
  const selectThread = async (threadId) => {
210
211
  router.push(`${ai.base}/c/${threadId}`)
212
+ emit('thread-selected')
211
213
  }
212
214
 
213
215
  const deleteThread = async (threadId) => {
@@ -223,15 +225,18 @@ const Sidebar = {
223
225
  const createNewThread = async () => {
224
226
  const newThread = await createThread()
225
227
  router.push(`${ai.base}/c/${newThread.id}`)
228
+ emit('thread-selected')
226
229
  }
227
230
 
228
231
  const goToInitialState = () => {
229
232
  clearCurrentThread()
230
233
  router.push(`${ai.base}/`)
234
+ emit('thread-selected')
231
235
  }
232
236
 
233
237
  const goToAnalytics = () => {
234
238
  router.push(`${ai.base}/analytics`)
239
+ emit('thread-selected')
235
240
  }
236
241
 
237
242
  return {
@@ -1,12 +1,13 @@
1
+ import { ref, onMounted, onUnmounted } from "vue"
1
2
  export default {
2
3
  template:`
3
4
  <button v-if="modelValue" type="button" title="Clear System Prompt" @click="$emit('update:modelValue', null)">
4
5
  <svg class="size-4 text-gray-500 dark:text-gray-400" 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
6
  </button>
6
7
 
7
- <Autocomplete id="prompt" :options="prompts" label=""
8
+ <Autocomplete ref="refSelector" id="prompt" :options="prompts" label=""
8
9
  :modelValue="modelValue" @update:modelValue="$emit('update:modelValue', $event)"
9
- class="w-72 xl:w-84"
10
+ class="w-68 xl:w-84"
10
11
  :match="(x, value) => x.name.toLowerCase().includes(value.toLowerCase())"
11
12
  placeholder="Select a System Prompt...">
12
13
  <template #item="{ value }">
@@ -32,5 +33,24 @@ export default {
32
33
  show: Boolean,
33
34
  },
34
35
  setup() {
36
+ const refSelector = ref()
37
+
38
+ function collapse(e) {
39
+ // call toggle when clicking outside of the Autocomplete component
40
+ if (refSelector.value && !refSelector.value.$el.contains(e.target)) {
41
+ refSelector.value.toggle(false)
42
+ }
43
+ }
44
+
45
+ onMounted(() => {
46
+ document.addEventListener('click', collapse)
47
+ })
48
+ onUnmounted(() => {
49
+ document.removeEventListener('click', collapse)
50
+ })
51
+
52
+ return {
53
+ refSelector,
54
+ }
35
55
  }
36
56
  }
llms/ui/ai.mjs CHANGED
@@ -6,7 +6,7 @@ const headers = { 'Accept': 'application/json' }
6
6
  const prefsKey = 'llms.prefs'
7
7
 
8
8
  export const o = {
9
- version: '2.0.28',
9
+ version: '2.0.30',
10
10
  base,
11
11
  prefsKey,
12
12
  welcome: 'Welcome to llms.py',
@@ -14,6 +14,7 @@ export const o = {
14
14
  requiresAuth: false,
15
15
  authType: 'apikey', // 'oauth' or 'apikey' - controls which SignIn component to use
16
16
  headers,
17
+ isSidebarOpen: true, // Shared sidebar state (default open for lg+ screens)
17
18
 
18
19
  resolveUrl(url){
19
20
  return url.startsWith('http') || url.startsWith('/v1') ? url : base + url