llms-py 3.0.0b7__py3-none-any.whl → 3.0.0b9__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/__pycache__/main.cpython-314.pyc +0 -0
- llms/extensions/analytics/ui/index.mjs +51 -162
- llms/extensions/app/__init__.py +519 -0
- llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms/extensions/app/db.py +643 -0
- llms/extensions/app/db_manager.py +195 -0
- llms/extensions/app/requests.json +9073 -0
- llms/extensions/app/threads.json +15290 -0
- llms/{ui/modules/threads → extensions/app/ui}/Recents.mjs +82 -55
- llms/{ui/modules/threads → extensions/app/ui}/index.mjs +78 -9
- llms/extensions/app/ui/threadStore.mjs +407 -0
- llms/extensions/core_tools/__init__.py +272 -32
- llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
- llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
- llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
- llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
- llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +344 -0
- llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +9884 -0
- llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
- llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
- llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
- llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
- llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
- llms/extensions/core_tools/ui/index.mjs +650 -0
- llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms/extensions/gallery/db.py +4 -4
- llms/extensions/gallery/ui/index.mjs +2 -1
- llms/extensions/katex/__init__.py +6 -0
- llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/katex/ui/README.md +125 -0
- llms/extensions/katex/ui/contrib/auto-render.js +338 -0
- llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
- llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
- llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
- llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
- llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
- llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
- llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
- llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
- llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
- llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms/extensions/katex/ui/index.mjs +92 -0
- llms/extensions/katex/ui/katex-swap.css +1230 -0
- llms/extensions/katex/ui/katex-swap.min.css +1 -0
- llms/extensions/katex/ui/katex.css +1230 -0
- llms/extensions/katex/ui/katex.js +19080 -0
- llms/extensions/katex/ui/katex.min.css +1 -0
- llms/extensions/katex/ui/katex.min.js +1 -0
- llms/extensions/katex/ui/katex.min.mjs +1 -0
- llms/extensions/katex/ui/katex.mjs +18547 -0
- llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms/extensions/providers/anthropic.py +44 -1
- llms/extensions/system_prompts/ui/index.mjs +2 -1
- llms/extensions/tools/__init__.py +5 -0
- llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms/extensions/tools/ui/index.mjs +8 -8
- llms/index.html +26 -38
- llms/llms.json +4 -1
- llms/main.py +492 -103
- llms/ui/App.mjs +2 -3
- llms/ui/ai.mjs +29 -13
- llms/ui/app.css +255 -289
- llms/ui/ctx.mjs +84 -6
- llms/ui/index.mjs +4 -6
- llms/ui/lib/vue.min.mjs +10 -9
- llms/ui/lib/vue.mjs +1796 -1635
- llms/ui/markdown.mjs +4 -2
- llms/ui/modules/chat/ChatBody.mjs +90 -86
- llms/ui/modules/chat/HomeTools.mjs +0 -242
- llms/ui/modules/chat/index.mjs +103 -170
- llms/ui/modules/model-selector.mjs +2 -2
- llms/ui/tailwind.input.css +35 -1
- llms/ui/utils.mjs +12 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b9.dist-info}/METADATA +1 -1
- llms_py-3.0.0b9.dist-info/RECORD +198 -0
- llms/ui/modules/threads/threadStore.mjs +0 -640
- llms_py-3.0.0b7.dist-info/RECORD +0 -80
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b9.dist-info}/WHEEL +0 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b9.dist-info}/entry_points.txt +0 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b9.dist-info}/licenses/LICENSE +0 -0
- {llms_py-3.0.0b7.dist-info → llms_py-3.0.0b9.dist-info}/top_level.txt +0 -0
llms/ui/modules/chat/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { ref, computed, watch, nextTick, inject } from 'vue'
|
|
3
3
|
import { useRouter } from 'vue-router'
|
|
4
|
-
import { $$, createElement, lastRightPart, ApiResult, createErrorStatus } from "@servicestack/client"
|
|
4
|
+
import { $$, createElement, lastRightPart, ApiResult, createErrorStatus, pick } from "@servicestack/client"
|
|
5
5
|
import SettingsDialog, { useSettings } from './SettingsDialog.mjs'
|
|
6
6
|
import ChatBody from './ChatBody.mjs'
|
|
7
7
|
import HomeTools from './HomeTools.mjs'
|
|
@@ -118,33 +118,17 @@ export function addCopyButtons() {
|
|
|
118
118
|
export function useChatPrompt(ctx) {
|
|
119
119
|
const messageText = ref('')
|
|
120
120
|
const attachedFiles = ref([])
|
|
121
|
-
const isGenerating = ref(false)
|
|
122
|
-
const errorStatus = ref(null)
|
|
123
|
-
const abortController = ref(null)
|
|
124
121
|
const hasImage = () => attachedFiles.value.some(f => imageExts.includes(lastRightPart(f.name, '.')))
|
|
125
122
|
const hasAudio = () => attachedFiles.value.some(f => audioExts.includes(lastRightPart(f.name, '.')))
|
|
126
123
|
const hasFile = () => attachedFiles.value.length > 0
|
|
127
|
-
// const hasText = () => !hasImage() && !hasAudio() && !hasFile()
|
|
128
124
|
|
|
129
|
-
const
|
|
125
|
+
const editingMessage = ref(null)
|
|
130
126
|
|
|
131
127
|
function reset() {
|
|
132
128
|
// Ensure initial state is ready to accept input
|
|
133
|
-
isGenerating.value = false
|
|
134
129
|
attachedFiles.value = []
|
|
135
130
|
messageText.value = ''
|
|
136
|
-
|
|
137
|
-
editingMessageId.value = null
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function cancel() {
|
|
141
|
-
// Cancel the pending request
|
|
142
|
-
if (abortController.value) {
|
|
143
|
-
abortController.value.abort()
|
|
144
|
-
}
|
|
145
|
-
// Reset UI state
|
|
146
|
-
isGenerating.value = false
|
|
147
|
-
abortController.value = null
|
|
131
|
+
editingMessage.value = null
|
|
148
132
|
}
|
|
149
133
|
|
|
150
134
|
const settings = useSettings()
|
|
@@ -351,9 +335,6 @@ export function useChatPrompt(ctx) {
|
|
|
351
335
|
if (msg.role === 'assistant') {
|
|
352
336
|
msg.model = model.name // tag with model
|
|
353
337
|
}
|
|
354
|
-
if (store) {
|
|
355
|
-
await ctx.threads.addMessageToThread(threadId, msg)
|
|
356
|
-
}
|
|
357
338
|
}
|
|
358
339
|
}
|
|
359
340
|
|
|
@@ -371,11 +352,6 @@ export function useChatPrompt(ctx) {
|
|
|
371
352
|
usage.price = usage.output
|
|
372
353
|
usage.cost = ctx.fmt.tokenCost(usage.prompt_tokens / 1_000_000 * parseFloat(input) + usage.completion_tokens / 1_000_000 * parseFloat(output))
|
|
373
354
|
}
|
|
374
|
-
await ctx.threads.logRequest(threadId, model, request, response)
|
|
375
|
-
}
|
|
376
|
-
if (store) {
|
|
377
|
-
assistantMessage.model = model.name
|
|
378
|
-
await ctx.threads.addMessageToThread(threadId, assistantMessage, usage)
|
|
379
355
|
}
|
|
380
356
|
|
|
381
357
|
nextTick(addCopyButtons)
|
|
@@ -409,19 +385,11 @@ export function useChatPrompt(ctx) {
|
|
|
409
385
|
applySettings,
|
|
410
386
|
messageText,
|
|
411
387
|
attachedFiles,
|
|
412
|
-
|
|
413
|
-
isGenerating,
|
|
414
|
-
abortController,
|
|
415
|
-
editingMessageId,
|
|
416
|
-
get generating() {
|
|
417
|
-
return isGenerating.value
|
|
418
|
-
},
|
|
388
|
+
editingMessage,
|
|
419
389
|
hasImage,
|
|
420
390
|
hasAudio,
|
|
421
391
|
hasFile,
|
|
422
|
-
// hasText,
|
|
423
392
|
reset,
|
|
424
|
-
cancel,
|
|
425
393
|
settings,
|
|
426
394
|
addCopyButtons,
|
|
427
395
|
getModel,
|
|
@@ -446,7 +414,7 @@ const ChatPrompt = {
|
|
|
446
414
|
<div>
|
|
447
415
|
<button type="button"
|
|
448
416
|
@click="triggerFilePicker"
|
|
449
|
-
:disabled="
|
|
417
|
+
:disabled="$threads.isWatchingThread.value || !model"
|
|
450
418
|
class="size-8 flex items-center justify-center rounded-md border border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:text-gray-400 disabled:cursor-not-allowed"
|
|
451
419
|
title="Attach image or audio">
|
|
452
420
|
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256">
|
|
@@ -460,7 +428,7 @@ const ChatPrompt = {
|
|
|
460
428
|
</div>
|
|
461
429
|
<div>
|
|
462
430
|
<button type="button" title="Settings" @click="showSettings = true"
|
|
463
|
-
:disabled="
|
|
431
|
+
:disabled="$threads.watchingThread || !model"
|
|
464
432
|
class="size-8 flex items-center justify-center rounded-md border border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:text-gray-400 disabled:cursor-not-allowed">
|
|
465
433
|
<svg class="size-4 text-gray-600 dark:text-gray-400 disabled:text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 256 256"><path d="M40,88H73a32,32,0,0,0,62,0h81a8,8,0,0,0,0-16H135a32,32,0,0,0-62,0H40a8,8,0,0,0,0,16Zm64-24A16,16,0,1,1,88,80,16,16,0,0,1,104,64ZM216,168H199a32,32,0,0,0-62,0H40a8,8,0,0,0,0,16h97a32,32,0,0,0,62,0h17a8,8,0,0,0,0-16Zm-48,24a16,16,0,1,1,16-16A16,16,0,0,1,168,192Z"></path></svg>
|
|
466
434
|
</button>
|
|
@@ -486,16 +454,16 @@ const ChatPrompt = {
|
|
|
486
454
|
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30 ring-1 ring-blue-500'
|
|
487
455
|
: 'border-gray-300 dark:border-gray-600 focus:border-blue-500 focus:ring-blue-500'
|
|
488
456
|
]"
|
|
489
|
-
:disabled="
|
|
457
|
+
:disabled="$threads.watchingThread || !model"
|
|
490
458
|
></textarea>
|
|
491
|
-
<button v-if="
|
|
459
|
+
<button v-if="!$threads.watchingThread" title="Send (Enter)" type="button"
|
|
492
460
|
@click="sendMessage"
|
|
493
|
-
:disabled="!messageText.trim() ||
|
|
461
|
+
:disabled="!messageText.trim() || $threads.watchingThread || !model"
|
|
494
462
|
class="absolute bottom-2 right-2 size-8 flex items-center justify-center rounded-md border border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:text-gray-400 disabled:cursor-not-allowed disabled:border-gray-200 dark:disabled:border-gray-700 transition-colors">
|
|
495
463
|
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path stroke-dasharray="20" stroke-dashoffset="20" d="M12 21l0 -17.5"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="20;0"/></path><path stroke-dasharray="12" stroke-dashoffset="12" d="M12 3l7 7M12 3l-7 7"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="12;0"/></path></g></svg>
|
|
496
464
|
</button>
|
|
497
465
|
<button v-else title="Cancel request" type="button"
|
|
498
|
-
@click="
|
|
466
|
+
@click="$threads.cancelThread()"
|
|
499
467
|
class="absolute bottom-2 right-2 size-8 flex items-center justify-center rounded-md border border-red-300 dark:border-red-600 text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 transition-colors">
|
|
500
468
|
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
501
469
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
|
@@ -506,7 +474,7 @@ const ChatPrompt = {
|
|
|
506
474
|
<!-- Attachments & Image Options -->
|
|
507
475
|
<div class="mt-2 flex justify-between items-start gap-2">
|
|
508
476
|
<div class="flex flex-wrap gap-2">
|
|
509
|
-
<div v-for="(f,i) in attachedFiles" :key="i" class="flex items-center gap-2 px-2 py-1 rounded-md border border-gray-300 dark:border-gray-600 text-xs text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-800">
|
|
477
|
+
<div v-for="(f,i) in $chat.attachedFiles.value" :key="i" class="flex items-center gap-2 px-2 py-1 rounded-md border border-gray-300 dark:border-gray-600 text-xs text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-800">
|
|
510
478
|
<span class="truncate max-w-48" :title="f.name">{{ f.name }}</span>
|
|
511
479
|
<button type="button" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200" @click="removeAttachment(i)" title="Remove Attachment">
|
|
512
480
|
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
|
@@ -528,6 +496,7 @@ const ChatPrompt = {
|
|
|
528
496
|
<div v-if="!model" class="mt-2 text-sm text-red-600 dark:text-red-400">
|
|
529
497
|
Please select a model
|
|
530
498
|
</div>
|
|
499
|
+
</div>
|
|
531
500
|
</div>
|
|
532
501
|
</div>
|
|
533
502
|
`,
|
|
@@ -540,23 +509,13 @@ const ChatPrompt = {
|
|
|
540
509
|
setup(props) {
|
|
541
510
|
const ctx = inject('ctx')
|
|
542
511
|
const config = ctx.state.config
|
|
543
|
-
const router = useRouter()
|
|
544
|
-
const chatPrompt = ctx.chat
|
|
545
512
|
const {
|
|
546
513
|
messageText,
|
|
547
|
-
attachedFiles,
|
|
548
|
-
isGenerating,
|
|
549
|
-
errorStatus,
|
|
550
514
|
hasImage,
|
|
551
515
|
hasAudio,
|
|
552
516
|
hasFile,
|
|
553
|
-
editingMessageId,
|
|
554
517
|
getTextContent,
|
|
555
|
-
} =
|
|
556
|
-
const threads = ctx.threads
|
|
557
|
-
const {
|
|
558
|
-
currentThread,
|
|
559
|
-
} = ctx.threads
|
|
518
|
+
} = ctx.chat
|
|
560
519
|
|
|
561
520
|
const fileInput = ref(null)
|
|
562
521
|
const refMessage = ref(null)
|
|
@@ -580,7 +539,7 @@ const ChatPrompt = {
|
|
|
580
539
|
type: f.type,
|
|
581
540
|
width: response.width,
|
|
582
541
|
height: response.height,
|
|
583
|
-
threadId: currentThread.value?.id,
|
|
542
|
+
threadId: ctx.threads.currentThread.value?.id,
|
|
584
543
|
created: Date.now()
|
|
585
544
|
}
|
|
586
545
|
|
|
@@ -589,22 +548,21 @@ const ChatPrompt = {
|
|
|
589
548
|
file: f // Keep original file for preview/fallback if needed
|
|
590
549
|
}
|
|
591
550
|
} catch (error) {
|
|
592
|
-
|
|
593
|
-
errorStatus.value = {
|
|
551
|
+
ctx.setError({
|
|
594
552
|
errorCode: 'Upload Failed',
|
|
595
553
|
message: `Failed to upload ${f.name}: ${error.message}`
|
|
596
|
-
}
|
|
554
|
+
})
|
|
597
555
|
return null
|
|
598
556
|
}
|
|
599
557
|
}))
|
|
600
558
|
|
|
601
|
-
attachedFiles.value.push(...uploadedFiles.filter(f => f))
|
|
559
|
+
ctx.chat.attachedFiles.value.push(...uploadedFiles.filter(f => f))
|
|
602
560
|
}
|
|
603
561
|
|
|
604
562
|
// allow re-selecting the same file
|
|
605
563
|
if (fileInput.value) fileInput.value.value = ''
|
|
606
564
|
|
|
607
|
-
if (!messageText.value
|
|
565
|
+
if (!messageText.value?.trim()) {
|
|
608
566
|
if (hasImage()) {
|
|
609
567
|
messageText.value = getTextContent(config.defaults.image)
|
|
610
568
|
} else if (hasAudio()) {
|
|
@@ -615,7 +573,7 @@ const ChatPrompt = {
|
|
|
615
573
|
}
|
|
616
574
|
}
|
|
617
575
|
const removeAttachment = (i) => {
|
|
618
|
-
attachedFiles.value.splice(i, 1)
|
|
576
|
+
ctx.chat.attachedFiles.value.splice(i, 1)
|
|
619
577
|
}
|
|
620
578
|
|
|
621
579
|
// Handle paste events for clipboard images, audio, and files
|
|
@@ -693,132 +651,110 @@ const ChatPrompt = {
|
|
|
693
651
|
|
|
694
652
|
// Send message
|
|
695
653
|
const sendMessage = async () => {
|
|
696
|
-
if (!messageText.value
|
|
697
|
-
if (
|
|
654
|
+
if (!messageText.value?.trim() && !hasImage() && !hasAudio() && !hasFile()) return
|
|
655
|
+
if (ctx.threads.isWatchingThread.value || !props.model) return
|
|
698
656
|
|
|
699
|
-
|
|
700
|
-
errorStatus.value = null
|
|
657
|
+
ctx.clearError()
|
|
701
658
|
|
|
702
659
|
// 1. Construct Structured Content (Text + Attachments)
|
|
703
660
|
let text = messageText.value.trim()
|
|
704
661
|
|
|
705
|
-
|
|
706
662
|
messageText.value = ''
|
|
707
|
-
let content = ctx.chat.createContent({ text, files: attachedFiles.value })
|
|
708
|
-
|
|
709
|
-
// Create AbortController for this request
|
|
710
|
-
const controller = new AbortController()
|
|
711
|
-
chatPrompt.abortController.value = controller
|
|
712
|
-
const model = props.model.name
|
|
663
|
+
let content = ctx.chat.createContent({ text, files: ctx.chat.attachedFiles.value })
|
|
713
664
|
|
|
714
665
|
let thread
|
|
715
666
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
667
|
+
// Create thread if none exists
|
|
668
|
+
if (!ctx.threads.currentThread.value) {
|
|
669
|
+
thread = await ctx.threads.startNewThread({ model: props.model })
|
|
670
|
+
} else {
|
|
671
|
+
thread = ctx.threads.currentThread.value
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
let threadId = thread.id
|
|
675
|
+
let messages = thread.messages || []
|
|
676
|
+
if (!threadId) {
|
|
677
|
+
console.error('No thread ID found', thread, ctx.threads.currentThread.value)
|
|
678
|
+
return
|
|
679
|
+
}
|
|
727
680
|
|
|
728
|
-
|
|
729
|
-
|
|
681
|
+
// Handle Editing / Redo Logic
|
|
682
|
+
const editingMessage = ctx.chat.editingMessage.value
|
|
683
|
+
if (editingMessage) {
|
|
684
|
+
let messageIndex = messages.findIndex(m => m.timestamp === editingMessage)
|
|
685
|
+
if (messageIndex == -1) {
|
|
686
|
+
messageIndex = messages.findLastIndex(m => m.role === 'user')
|
|
730
687
|
}
|
|
688
|
+
console.log('Editing message', editingMessage, messageIndex, messages)
|
|
731
689
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
// Check if message still exists
|
|
737
|
-
const messageExists = thread.messages.find(m => m.id === editingMessageId.value)
|
|
738
|
-
if (messageExists) {
|
|
739
|
-
// Update the message content
|
|
740
|
-
await threads.updateMessageInThread(threadId, editingMessageId.value, { content })
|
|
741
|
-
// Redo from this message (clears subsequent)
|
|
742
|
-
await threads.redoMessageFromThread(threadId, editingMessageId.value)
|
|
743
|
-
|
|
744
|
-
// Clear editing state
|
|
745
|
-
editingMessageId.value = null
|
|
746
|
-
} else {
|
|
747
|
-
// Fallback if message was deleted
|
|
748
|
-
editingMessageId.value = null
|
|
749
|
-
}
|
|
750
|
-
// Refresh thread state
|
|
751
|
-
thread = await threads.getThread(threadId)
|
|
690
|
+
if (messageIndex >= 0) {
|
|
691
|
+
messages[messageIndex].content = content
|
|
692
|
+
// Truncate messages to only include up to the edited message
|
|
693
|
+
messages.length = messageIndex + 1
|
|
752
694
|
} else {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
if (typeof msgContent === 'string') return msgContent
|
|
759
|
-
if (Array.isArray(msgContent)) return msgContent.find(c => c.type === 'text')?.text || ''
|
|
760
|
-
return ''
|
|
761
|
-
}
|
|
762
|
-
const newText = text // content[0].text
|
|
763
|
-
const lastText = lastMessage && lastMessage.role === 'user' ? getLastText(lastMessage.content) : null
|
|
764
|
-
|
|
765
|
-
const isDuplicate = lastText === newText
|
|
766
|
-
|
|
767
|
-
// Add user message only if it's not a duplicate
|
|
768
|
-
// Note: We are saving the FULL STRUCTURED CONTENT array here
|
|
769
|
-
if (!isDuplicate) {
|
|
770
|
-
await threads.addMessageToThread(threadId, {
|
|
771
|
-
role: 'user',
|
|
772
|
-
content: content,
|
|
773
|
-
model: props.model.name,
|
|
774
|
-
})
|
|
775
|
-
// Reload thread after adding message
|
|
776
|
-
thread = await threads.getThread(threadId)
|
|
777
|
-
}
|
|
695
|
+
messages.push({
|
|
696
|
+
timestamp: new Date().valueOf(),
|
|
697
|
+
role: 'user',
|
|
698
|
+
content,
|
|
699
|
+
})
|
|
778
700
|
}
|
|
701
|
+
} else {
|
|
702
|
+
// Regular Send Logic
|
|
703
|
+
const lastMessage = messages[messages.length - 1]
|
|
704
|
+
|
|
705
|
+
// Check duplicate based on text content extracted from potential array
|
|
706
|
+
const getLastText = (msgContent) => {
|
|
707
|
+
if (typeof msgContent === 'string') return msgContent
|
|
708
|
+
if (Array.isArray(msgContent)) return msgContent.find(c => c.type === 'text')?.text || ''
|
|
709
|
+
return ''
|
|
710
|
+
}
|
|
711
|
+
const newText = text // content[0].text
|
|
712
|
+
const lastText = lastMessage && lastMessage.role === 'user' ? getLastText(lastMessage.content) : null
|
|
713
|
+
const isDuplicate = lastText === newText
|
|
714
|
+
|
|
715
|
+
// Add user message only if it's not a duplicate
|
|
716
|
+
// Note: We are saving the FULL STRUCTURED CONTENT array here
|
|
717
|
+
if (!isDuplicate) {
|
|
718
|
+
messages.push({
|
|
719
|
+
timestamp: new Date().valueOf(),
|
|
720
|
+
role: 'user',
|
|
721
|
+
content,
|
|
722
|
+
})
|
|
723
|
+
}
|
|
724
|
+
}
|
|
779
725
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
const request = ctx.chat.createRequest({ model: props.model })
|
|
726
|
+
const request = ctx.chat.createRequest({ model: props.model })
|
|
783
727
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
content: m.content
|
|
789
|
-
})
|
|
790
|
-
})
|
|
791
|
-
request.metadata.threadId = thread.id
|
|
728
|
+
// Add Thread History
|
|
729
|
+
messages.forEach(m => {
|
|
730
|
+
request.messages.push(m)
|
|
731
|
+
})
|
|
792
732
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
733
|
+
// Update Thread Title if not set or is default
|
|
734
|
+
if (!thread.title || thread.title === 'New Chat' || request.title === 'New Chat') {
|
|
735
|
+
request.title = text.length > 100
|
|
736
|
+
? text.slice(0, 100) + '...'
|
|
737
|
+
: text
|
|
738
|
+
console.debug(`changing thread title from '${thread.title}' to '${request.title}'`)
|
|
739
|
+
} else {
|
|
740
|
+
console.debug(`thread title is '${thread.title}'`, request.title)
|
|
741
|
+
}
|
|
800
742
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
} finally {
|
|
811
|
-
isGenerating.value = false
|
|
812
|
-
chatPrompt.abortController.value = null
|
|
813
|
-
// Restore focus to the textarea
|
|
814
|
-
nextTick(() => {
|
|
815
|
-
refMessage.value?.focus()
|
|
816
|
-
})
|
|
743
|
+
const api = await ctx.threads.queueChat(threadId, request)
|
|
744
|
+
if (api.response) {
|
|
745
|
+
// success
|
|
746
|
+
ctx.chat.editingMessage.value = null
|
|
747
|
+
ctx.chat.attachedFiles.value = []
|
|
748
|
+
thread = api.response
|
|
749
|
+
ctx.threads.replaceThread(thread)
|
|
750
|
+
} else {
|
|
751
|
+
ctx.setError(api.error)
|
|
817
752
|
}
|
|
818
|
-
}
|
|
819
753
|
|
|
820
|
-
|
|
821
|
-
|
|
754
|
+
// Restore focus to the textarea
|
|
755
|
+
nextTick(() => {
|
|
756
|
+
refMessage.value?.focus()
|
|
757
|
+
})
|
|
822
758
|
}
|
|
823
759
|
|
|
824
760
|
const addNewLine = () => {
|
|
@@ -839,8 +775,6 @@ const ChatPrompt = {
|
|
|
839
775
|
})
|
|
840
776
|
|
|
841
777
|
return {
|
|
842
|
-
isGenerating,
|
|
843
|
-
attachedFiles,
|
|
844
778
|
messageText,
|
|
845
779
|
fileInput,
|
|
846
780
|
refMessage,
|
|
@@ -854,7 +788,6 @@ const ChatPrompt = {
|
|
|
854
788
|
onDrop,
|
|
855
789
|
removeAttachment,
|
|
856
790
|
sendMessage,
|
|
857
|
-
cancelRequest,
|
|
858
791
|
addNewLine,
|
|
859
792
|
imageAspectRatios,
|
|
860
793
|
}
|
|
@@ -350,7 +350,7 @@ const ModelSelectorModal = {
|
|
|
350
350
|
@click="selectModel(model)"
|
|
351
351
|
:class="[
|
|
352
352
|
'relative text-left p-4 rounded-lg border transition-all group',
|
|
353
|
-
|
|
353
|
+
$state.selectedModel === model.name
|
|
354
354
|
? 'border-blue-500 bg-blue-50 dark:bg-blue-900/30 ring-2 ring-blue-500/50'
|
|
355
355
|
: 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700/50'
|
|
356
356
|
]">
|
|
@@ -560,7 +560,7 @@ const ModelSelectorModal = {
|
|
|
560
560
|
}
|
|
561
561
|
|
|
562
562
|
// Filter by search query
|
|
563
|
-
if (prefs.value.query
|
|
563
|
+
if (prefs.value.query?.trim()) {
|
|
564
564
|
const query = prefs.value.query.toLowerCase()
|
|
565
565
|
result = result.filter(m =>
|
|
566
566
|
m.name?.toLowerCase().includes(query) ||
|
llms/ui/tailwind.input.css
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
@import "tailwindcss";
|
|
3
3
|
@source "./lib/servicestack-vue.mjs";
|
|
4
4
|
@source "../../extensions";
|
|
5
|
+
@source "../../llms-home/extensions";
|
|
5
6
|
|
|
6
7
|
@custom-variant dark (&:where(.dark, .dark *));
|
|
7
8
|
|
|
@@ -162,6 +163,7 @@
|
|
|
162
163
|
word-break: break-all !important;
|
|
163
164
|
border: none !important;
|
|
164
165
|
border-radius: 0 !important;
|
|
166
|
+
overflow: auto !important;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/* highlight.js - vs.css */
|
|
@@ -319,6 +321,10 @@
|
|
|
319
321
|
background-color: #282c34 !important;
|
|
320
322
|
}
|
|
321
323
|
|
|
324
|
+
.prose :where(h1):not(:where([class~="not-prose"] *)) {
|
|
325
|
+
font-weight: 700 !important;
|
|
326
|
+
}
|
|
327
|
+
|
|
322
328
|
.hljs-comment,
|
|
323
329
|
.hljs-quote {
|
|
324
330
|
color: rgb(148 163 184);
|
|
@@ -336,7 +342,7 @@
|
|
|
336
342
|
padding: .8571429em 1.1428571em;
|
|
337
343
|
max-width: calc(100vw - 1rem);
|
|
338
344
|
min-width: fit-content;
|
|
339
|
-
background-color: #282c34 !important;
|
|
345
|
+
/* background-color: #282c34 !important; overrides CodeMirror */
|
|
340
346
|
}
|
|
341
347
|
|
|
342
348
|
pre code.hljs {
|
|
@@ -651,4 +657,32 @@
|
|
|
651
657
|
left: 0;
|
|
652
658
|
}
|
|
653
659
|
|
|
660
|
+
|
|
661
|
+
@media (min-width: 640px) {
|
|
662
|
+
.message pre {
|
|
663
|
+
max-width: 500px !important;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
@media (min-width: 768px) {
|
|
668
|
+
.message pre {
|
|
669
|
+
max-width: 600px !important;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
@media (min-width: 1024px) {
|
|
674
|
+
|
|
675
|
+
.message pre,
|
|
676
|
+
.message .prose pre {
|
|
677
|
+
max-width: 700px !important;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
@media (min-width: 1280px) {
|
|
682
|
+
|
|
683
|
+
.message pre,
|
|
684
|
+
.message .prose pre {
|
|
685
|
+
max-width: 800px !important;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
654
688
|
}
|
llms/ui/utils.mjs
CHANGED
|
@@ -111,6 +111,17 @@ export function formatCost(cost) {
|
|
|
111
111
|
if (!cost) return ''
|
|
112
112
|
return currFmt2.format(parseFloat(cost))
|
|
113
113
|
}
|
|
114
|
+
export function tokensTitle(usage) {
|
|
115
|
+
let title = []
|
|
116
|
+
if (usage.tokens && usage.price) {
|
|
117
|
+
const msg = parseFloat(usage.price) > 0
|
|
118
|
+
? `${usage.tokens} tokens @ ${usage.price} = ${tokenCostLong(usage.price, usage.tokens)}`
|
|
119
|
+
: `${usage.tokens} tokens`
|
|
120
|
+
const duration = usage.duration ? ` in ${usage.duration}ms` : ''
|
|
121
|
+
title.push(msg + duration)
|
|
122
|
+
}
|
|
123
|
+
return title.join('\n')
|
|
124
|
+
}
|
|
114
125
|
|
|
115
126
|
// Accessible in views via $fmt
|
|
116
127
|
export function utilsFormatters() {
|
|
@@ -172,6 +183,7 @@ export function utilsFormatters() {
|
|
|
172
183
|
currFmt: currFmt2,
|
|
173
184
|
tokenCost,
|
|
174
185
|
tokenCostLong,
|
|
186
|
+
tokensTitle,
|
|
175
187
|
cost: formatCost,
|
|
176
188
|
costLong,
|
|
177
189
|
statsTitle,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b9
|
|
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
|