llms-py 3.0.0b8__py3-none-any.whl → 3.0.0b10__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.
Files changed (32) hide show
  1. llms/__pycache__/main.cpython-314.pyc +0 -0
  2. llms/extensions/app/README.md +20 -0
  3. llms/extensions/app/__init__.py +16 -5
  4. llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  5. llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  6. llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  7. llms/extensions/app/db.py +12 -9
  8. llms/extensions/app/db_manager.py +1 -1
  9. llms/extensions/app/ui/index.mjs +1 -1
  10. llms/extensions/app/ui/threadStore.mjs +21 -17
  11. llms/extensions/core_tools/CALCULATOR.md +32 -0
  12. llms/extensions/core_tools/__init__.py +1 -1
  13. llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  14. llms/extensions/core_tools/ui/index.mjs +4 -4
  15. llms/extensions/gallery/README.md +61 -0
  16. llms/extensions/gallery/ui/index.mjs +1 -0
  17. llms/extensions/katex/README.md +39 -0
  18. llms/extensions/system_prompts/README.md +22 -0
  19. llms/llms.json +9 -12
  20. llms/main.py +25 -5
  21. llms/ui/ai.mjs +20 -5
  22. llms/ui/app.css +114 -0
  23. llms/ui/ctx.mjs +22 -0
  24. llms/ui/modules/chat/ChatBody.mjs +43 -27
  25. llms/ui/modules/chat/index.mjs +17 -29
  26. {llms_py-3.0.0b8.dist-info → llms_py-3.0.0b10.dist-info}/METADATA +1 -1
  27. {llms_py-3.0.0b8.dist-info → llms_py-3.0.0b10.dist-info}/RECORD +31 -27
  28. llms/ui/modules/chat/HomeTools.mjs +0 -12
  29. {llms_py-3.0.0b8.dist-info → llms_py-3.0.0b10.dist-info}/WHEEL +0 -0
  30. {llms_py-3.0.0b8.dist-info → llms_py-3.0.0b10.dist-info}/entry_points.txt +0 -0
  31. {llms_py-3.0.0b8.dist-info → llms_py-3.0.0b10.dist-info}/licenses/LICENSE +0 -0
  32. {llms_py-3.0.0b8.dist-info → llms_py-3.0.0b10.dist-info}/top_level.txt +0 -0
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: '3.0.0b8',
9
+ version: '3.0.0b10',
10
10
  base,
11
11
  prefsKey,
12
12
  welcome: 'Welcome to llms.py',
@@ -74,12 +74,27 @@ export const o = {
74
74
  return { responseStatus }
75
75
  }
76
76
  },
77
+ createErrorStatus({ message, errorCode, stackTrace, errors, meta }) {
78
+ const ret = {
79
+ errorCode: errorCode || 'Error',
80
+ message: message,
81
+ }
82
+ if (stackTrace) {
83
+ ret.stackTrace = stackTrace
84
+ }
85
+ if (errors && Array.isArray(errors)) {
86
+ ret.errors = errors
87
+ }
88
+ if (meta) {
89
+ ret.meta = meta
90
+ }
91
+ return ret
92
+ },
77
93
  createErrorResult(e) {
78
94
  return new ApiResult({
79
- responseStatus: {
80
- errorCode: 'Error',
81
- message: `${e.message ?? e}`
82
- }
95
+ error: e.errorCode
96
+ ? this.createErrorStatus(e)
97
+ : this.createErrorStatus({ message: `${e.message ?? e}` })
83
98
  })
84
99
  },
85
100
  async getConfig() {
llms/ui/app.css CHANGED
@@ -95,6 +95,7 @@
95
95
  --color-fuchsia-900: oklch(40.1% 0.17 325.612);
96
96
  --color-slate-50: oklch(98.4% 0.003 247.858);
97
97
  --color-slate-200: oklch(92.9% 0.013 255.508);
98
+ --color-slate-300: oklch(86.9% 0.022 252.894);
98
99
  --color-slate-400: oklch(70.4% 0.04 256.788);
99
100
  --color-slate-500: oklch(55.4% 0.046 257.417);
100
101
  --color-slate-700: oklch(37.2% 0.044 257.287);
@@ -186,6 +187,7 @@
186
187
  --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
187
188
  --default-font-family: var(--font-sans);
188
189
  --default-mono-font-family: var(--font-mono);
190
+ --default-ring-color: hsl(var(--ring));
189
191
  }
190
192
  }
191
193
  @layer base {
@@ -393,6 +395,9 @@
393
395
  .top-0 {
394
396
  top: calc(var(--spacing) * 0);
395
397
  }
398
+ .top-1 {
399
+ top: calc(var(--spacing) * 1);
400
+ }
396
401
  .top-1\/2 {
397
402
  top: calc(1/2 * 100%);
398
403
  }
@@ -429,6 +434,9 @@
429
434
  .left-0 {
430
435
  left: calc(var(--spacing) * 0);
431
436
  }
437
+ .left-1 {
438
+ left: calc(var(--spacing) * 1);
439
+ }
432
440
  .left-1\/2 {
433
441
  left: calc(1/2 * 100%);
434
442
  }
@@ -501,9 +509,15 @@
501
509
  max-width: 96rem;
502
510
  }
503
511
  }
512
+ .-m-2 {
513
+ margin: calc(var(--spacing) * -2);
514
+ }
504
515
  .-m-2\.5 {
505
516
  margin: calc(var(--spacing) * -2.5);
506
517
  }
518
+ .-mx-1 {
519
+ margin-inline: calc(var(--spacing) * -1);
520
+ }
507
521
  .-mx-1\.5 {
508
522
  margin-inline: calc(var(--spacing) * -1.5);
509
523
  }
@@ -516,6 +530,9 @@
516
530
  .mx-auto {
517
531
  margin-inline: auto;
518
532
  }
533
+ .-my-1 {
534
+ margin-block: calc(var(--spacing) * -1);
535
+ }
519
536
  .-my-1\.5 {
520
537
  margin-block: calc(var(--spacing) * -1.5);
521
538
  }
@@ -537,6 +554,9 @@
537
554
  .-mt-36 {
538
555
  margin-top: calc(var(--spacing) * -36);
539
556
  }
557
+ .mt-0 {
558
+ margin-top: calc(var(--spacing) * 0);
559
+ }
540
560
  .mt-0\.5 {
541
561
  margin-top: calc(var(--spacing) * 0.5);
542
562
  }
@@ -714,6 +734,9 @@
714
734
  width: calc(var(--spacing) * 20);
715
735
  height: calc(var(--spacing) * 20);
716
736
  }
737
+ .h-1 {
738
+ height: calc(var(--spacing) * 1);
739
+ }
717
740
  .h-1\.5 {
718
741
  height: calc(var(--spacing) * 1.5);
719
742
  }
@@ -979,6 +1002,10 @@
979
1002
  .origin-top-right {
980
1003
  transform-origin: 100% 0;
981
1004
  }
1005
+ .-translate-x-1 {
1006
+ --tw-translate-x: calc(var(--spacing) * -1);
1007
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1008
+ }
982
1009
  .-translate-x-1\/2 {
983
1010
  --tw-translate-x: calc(calc(1/2 * 100%) * -1);
984
1011
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -995,6 +1022,10 @@
995
1022
  --tw-translate-x: calc(var(--spacing) * 0);
996
1023
  translate: var(--tw-translate-x) var(--tw-translate-y);
997
1024
  }
1025
+ .translate-x-1 {
1026
+ --tw-translate-x: calc(var(--spacing) * 1);
1027
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1028
+ }
998
1029
  .translate-x-1\/4 {
999
1030
  --tw-translate-x: calc(1/4 * 100%);
1000
1031
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1007,6 +1038,10 @@
1007
1038
  --tw-translate-x: 100%;
1008
1039
  translate: var(--tw-translate-x) var(--tw-translate-y);
1009
1040
  }
1041
+ .-translate-y-1 {
1042
+ --tw-translate-y: calc(var(--spacing) * -1);
1043
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1044
+ }
1010
1045
  .-translate-y-1\/2 {
1011
1046
  --tw-translate-y: calc(calc(1/2 * 100%) * -1);
1012
1047
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1019,6 +1054,10 @@
1019
1054
  --tw-translate-y: calc(var(--spacing) * 0);
1020
1055
  translate: var(--tw-translate-x) var(--tw-translate-y);
1021
1056
  }
1057
+ .translate-y-1 {
1058
+ --tw-translate-y: calc(var(--spacing) * 1);
1059
+ translate: var(--tw-translate-x) var(--tw-translate-y);
1060
+ }
1022
1061
  .translate-y-1\/4 {
1023
1062
  --tw-translate-y: calc(1/4 * 100%);
1024
1063
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1180,6 +1219,9 @@
1180
1219
  margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)));
1181
1220
  }
1182
1221
  }
1222
+ .gap-x-1 {
1223
+ column-gap: calc(var(--spacing) * 1);
1224
+ }
1183
1225
  .gap-x-1\.5 {
1184
1226
  column-gap: calc(var(--spacing) * 1.5);
1185
1227
  }
@@ -1445,6 +1487,9 @@
1445
1487
  .border-red-400 {
1446
1488
  border-color: var(--color-red-400);
1447
1489
  }
1490
+ .border-red-800 {
1491
+ border-color: var(--color-red-800);
1492
+ }
1448
1493
  .border-red-800\/20 {
1449
1494
  border-color: color-mix(in srgb, oklch(44.4% 0.177 26.899) 20%, transparent);
1450
1495
  @supports (color: color-mix(in lab, red, red)) {
@@ -1454,6 +1499,9 @@
1454
1499
  .border-transparent {
1455
1500
  border-color: transparent;
1456
1501
  }
1502
+ .border-white {
1503
+ border-color: var(--color-white);
1504
+ }
1457
1505
  .border-white\/10 {
1458
1506
  border-color: color-mix(in srgb, #fff 10%, transparent);
1459
1507
  @supports (color: color-mix(in lab, red, red)) {
@@ -1475,12 +1523,18 @@
1475
1523
  .border-yellow-400 {
1476
1524
  border-color: var(--color-yellow-400);
1477
1525
  }
1526
+ .border-yellow-500 {
1527
+ border-color: var(--color-yellow-500);
1528
+ }
1478
1529
  .border-yellow-500\/30 {
1479
1530
  border-color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 30%, transparent);
1480
1531
  @supports (color: color-mix(in lab, red, red)) {
1481
1532
  border-color: color-mix(in oklab, var(--color-yellow-500) 30%, transparent);
1482
1533
  }
1483
1534
  }
1535
+ .border-yellow-600 {
1536
+ border-color: var(--color-yellow-600);
1537
+ }
1484
1538
  .border-yellow-600\/30 {
1485
1539
  border-color: color-mix(in srgb, oklch(68.1% 0.162 75.834) 30%, transparent);
1486
1540
  @supports (color: color-mix(in lab, red, red)) {
@@ -1490,6 +1544,9 @@
1490
1544
  .bg-\[\#fdfbf7\] {
1491
1545
  background-color: #fdfbf7;
1492
1546
  }
1547
+ .bg-black {
1548
+ background-color: var(--color-black);
1549
+ }
1493
1550
  .bg-black\/40 {
1494
1551
  background-color: color-mix(in srgb, #000 40%, transparent);
1495
1552
  @supports (color: color-mix(in lab, red, red)) {
@@ -1556,6 +1613,9 @@
1556
1613
  .bg-gray-400 {
1557
1614
  background-color: var(--color-gray-400);
1558
1615
  }
1616
+ .bg-gray-500 {
1617
+ background-color: var(--color-gray-500);
1618
+ }
1559
1619
  .bg-gray-500\/75 {
1560
1620
  background-color: color-mix(in srgb, oklch(55.1% 0.027 264.364) 75%, transparent);
1561
1621
  @supports (color: color-mix(in lab, red, red)) {
@@ -1619,9 +1679,15 @@
1619
1679
  .bg-sky-600 {
1620
1680
  background-color: var(--color-sky-600);
1621
1681
  }
1682
+ .bg-slate-50 {
1683
+ background-color: var(--color-slate-50);
1684
+ }
1622
1685
  .bg-slate-50\! {
1623
1686
  background-color: var(--color-slate-50) !important;
1624
1687
  }
1688
+ .bg-slate-400 {
1689
+ background-color: var(--color-slate-400);
1690
+ }
1625
1691
  .bg-slate-400\/10 {
1626
1692
  background-color: color-mix(in srgb, oklch(70.4% 0.04 256.788) 10%, transparent);
1627
1693
  @supports (color: color-mix(in lab, red, red)) {
@@ -1686,6 +1752,10 @@
1686
1752
  --tw-gradient-position: to top in oklab;
1687
1753
  background-image: linear-gradient(var(--tw-gradient-stops));
1688
1754
  }
1755
+ .from-black {
1756
+ --tw-gradient-from: var(--color-black);
1757
+ --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
1758
+ }
1689
1759
  .from-black\/80 {
1690
1760
  --tw-gradient-from: color-mix(in srgb, #000 80%, transparent);
1691
1761
  @supports (color: color-mix(in lab, red, red)) {
@@ -1717,6 +1787,11 @@
1717
1787
  --tw-gradient-from: var(--color-yellow-400);
1718
1788
  --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
1719
1789
  }
1790
+ .via-black {
1791
+ --tw-gradient-via: var(--color-black);
1792
+ --tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
1793
+ --tw-gradient-stops: var(--tw-gradient-via-stops);
1794
+ }
1720
1795
  .via-black\/20 {
1721
1796
  --tw-gradient-via: color-mix(in srgb, #000 20%, transparent);
1722
1797
  @supports (color: color-mix(in lab, red, red)) {
@@ -1841,6 +1916,9 @@
1841
1916
  .px-12 {
1842
1917
  padding-inline: calc(var(--spacing) * 12);
1843
1918
  }
1919
+ .py-0 {
1920
+ padding-block: calc(var(--spacing) * 0);
1921
+ }
1844
1922
  .py-0\.5 {
1845
1923
  padding-block: calc(var(--spacing) * 0.5);
1846
1924
  }
@@ -1877,6 +1955,9 @@
1877
1955
  .py-12 {
1878
1956
  padding-block: calc(var(--spacing) * 12);
1879
1957
  }
1958
+ .pt-0 {
1959
+ padding-top: calc(var(--spacing) * 0);
1960
+ }
1880
1961
  .pt-0\.5 {
1881
1962
  padding-top: calc(var(--spacing) * 0.5);
1882
1963
  }
@@ -2243,6 +2324,9 @@
2243
2324
  .text-sky-600 {
2244
2325
  color: var(--color-sky-600);
2245
2326
  }
2327
+ .text-slate-300 {
2328
+ color: var(--color-slate-300);
2329
+ }
2246
2330
  .text-slate-500 {
2247
2331
  color: var(--color-slate-500);
2248
2332
  }
@@ -2270,6 +2354,9 @@
2270
2354
  .text-yellow-400 {
2271
2355
  color: var(--color-yellow-400);
2272
2356
  }
2357
+ .text-yellow-500 {
2358
+ color: var(--color-yellow-500);
2359
+ }
2273
2360
  .text-yellow-500\/60 {
2274
2361
  color: color-mix(in srgb, oklch(79.5% 0.184 86.047) 60%, transparent);
2275
2362
  @supports (color: color-mix(in lab, red, red)) {
@@ -2429,24 +2516,45 @@
2429
2516
  --tw-inset-ring-shadow: inset 0 0 0 1px var(--tw-inset-ring-color, currentcolor);
2430
2517
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2431
2518
  }
2519
+ .shadow-black {
2520
+ --tw-shadow-color: #000;
2521
+ @supports (color: color-mix(in lab, red, red)) {
2522
+ --tw-shadow-color: color-mix(in oklab, var(--color-black) var(--tw-shadow-alpha), transparent);
2523
+ }
2524
+ }
2432
2525
  .shadow-black\/5 {
2433
2526
  --tw-shadow-color: color-mix(in srgb, #000 5%, transparent);
2434
2527
  @supports (color: color-mix(in lab, red, red)) {
2435
2528
  --tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-black) 5%, transparent) var(--tw-shadow-alpha), transparent);
2436
2529
  }
2437
2530
  }
2531
+ .shadow-fuchsia-500 {
2532
+ --tw-shadow-color: oklch(66.7% 0.295 322.15);
2533
+ @supports (color: color-mix(in lab, red, red)) {
2534
+ --tw-shadow-color: color-mix(in oklab, var(--color-fuchsia-500) var(--tw-shadow-alpha), transparent);
2535
+ }
2536
+ }
2438
2537
  .shadow-fuchsia-500\/10 {
2439
2538
  --tw-shadow-color: color-mix(in srgb, oklch(66.7% 0.295 322.15) 10%, transparent);
2440
2539
  @supports (color: color-mix(in lab, red, red)) {
2441
2540
  --tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-fuchsia-500) 10%, transparent) var(--tw-shadow-alpha), transparent);
2442
2541
  }
2443
2542
  }
2543
+ .shadow-gray-200 {
2544
+ --tw-shadow-color: oklch(92.8% 0.006 264.531);
2545
+ @supports (color: color-mix(in lab, red, red)) {
2546
+ --tw-shadow-color: color-mix(in oklab, var(--color-gray-200) var(--tw-shadow-alpha), transparent);
2547
+ }
2548
+ }
2444
2549
  .shadow-gray-200\/50 {
2445
2550
  --tw-shadow-color: color-mix(in srgb, oklch(92.8% 0.006 264.531) 50%, transparent);
2446
2551
  @supports (color: color-mix(in lab, red, red)) {
2447
2552
  --tw-shadow-color: color-mix(in oklab, color-mix(in oklab, var(--color-gray-200) 50%, transparent) var(--tw-shadow-alpha), transparent);
2448
2553
  }
2449
2554
  }
2555
+ .ring-black {
2556
+ --tw-ring-color: var(--color-black);
2557
+ }
2450
2558
  .ring-black\/5 {
2451
2559
  --tw-ring-color: color-mix(in srgb, #000 5%, transparent);
2452
2560
  @supports (color: color-mix(in lab, red, red)) {
@@ -2468,12 +2576,18 @@
2468
2576
  .ring-indigo-500 {
2469
2577
  --tw-ring-color: var(--color-indigo-500);
2470
2578
  }
2579
+ .ring-yellow-400 {
2580
+ --tw-ring-color: var(--color-yellow-400);
2581
+ }
2471
2582
  .ring-yellow-400\/30 {
2472
2583
  --tw-ring-color: color-mix(in srgb, oklch(85.2% 0.199 91.936) 30%, transparent);
2473
2584
  @supports (color: color-mix(in lab, red, red)) {
2474
2585
  --tw-ring-color: color-mix(in oklab, var(--color-yellow-400) 30%, transparent);
2475
2586
  }
2476
2587
  }
2588
+ .inset-ring-gray-900 {
2589
+ --tw-inset-ring-color: var(--color-gray-900);
2590
+ }
2477
2591
  .inset-ring-gray-900\/5 {
2478
2592
  --tw-inset-ring-color: color-mix(in srgb, oklch(21% 0.034 264.665) 5%, transparent);
2479
2593
  @supports (color: color-mix(in lab, red, red)) {
llms/ui/ctx.mjs CHANGED
@@ -43,6 +43,18 @@ export class ExtensionScope {
43
43
  post(url, options) {
44
44
  return this.ctx.ai.post(combinePaths(this.baseUrl, url), options)
45
45
  }
46
+ put(url, options) {
47
+ return this.ctx.ai.post(combinePaths(this.baseUrl, url), {
48
+ ...options,
49
+ method: 'PUT'
50
+ })
51
+ }
52
+ patch(url, options) {
53
+ return this.ctx.ai.post(combinePaths(this.baseUrl, url), {
54
+ ...options,
55
+ method: 'PATCH'
56
+ })
57
+ }
46
58
  async postForm(url, options) {
47
59
  return await this.ctx.ai.postForm(combinePaths(this.baseUrl, url), options)
48
60
  }
@@ -66,6 +78,9 @@ export class ExtensionScope {
66
78
  async createJsonResult(res) {
67
79
  return this.ctx.ai.createJsonResult(res)
68
80
  }
81
+ createErrorStatus(status) {
82
+ return this.ctx.ai.createErrorStatus(status)
83
+ }
69
84
  createErrorResult(e) {
70
85
  return this.ctx.ai.createErrorResult(e)
71
86
  }
@@ -263,6 +278,12 @@ export class AppContext {
263
278
  return toggle
264
279
  }
265
280
 
281
+ createErrorStatus(status) {
282
+ return this.ai.createErrorStatus(status)
283
+ }
284
+ createErrorResult(e) {
285
+ return this.ai.createErrorResult(e)
286
+ }
266
287
  setError(error, msg = null) {
267
288
  this.state.error = error
268
289
  if (error) {
@@ -331,6 +352,7 @@ export class AppContext {
331
352
  if (Array.isArray(content)) {
332
353
  content = content.filter(c => c.type === 'text').map(c => c.text).join('\n')
333
354
  }
355
+ // Handled by katex
334
356
  // if (content) {
335
357
  // content = content
336
358
  // .replaceAll(`\\[ \\boxed{`, '\n<span class="inline-block text-xl text-blue-500 bg-blue-50 dark:text-blue-400 dark:bg-blue-950 px-3 py-1 rounded">')
@@ -20,9 +20,50 @@ const MessageUsage = {
20
20
  }
21
21
  }
22
22
 
23
+ const MessageReasoning = {
24
+ template: `
25
+ <div class="mt-2 mb-2">
26
+ <button type="button" @click="toggleReasoning(message.id)" class="text-xs text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 flex items-center space-x-1">
27
+ <svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" :class="isReasoningExpanded(message.id) ? 'transform rotate-90' : ''"><path fill="currentColor" d="M7 5l6 5l-6 5z"/></svg>
28
+ <span>{{ isReasoningExpanded(message.id) ? 'Hide reasoning' : 'Show reasoning' }}</span>
29
+ </button>
30
+ <div v-if="isReasoningExpanded(message.id)" class="reasoning mt-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 p-2">
31
+ <div v-if="typeof reasoning === 'string'" v-html="$fmt.markdown(reasoning)" class="prose prose-xs max-w-none dark:prose-invert"></div>
32
+ <pre v-else class="text-xs whitespace-pre-wrap overflow-x-auto">{{ formatReasoning(reasoning) }}</pre>
33
+ </div>
34
+ </div>
35
+ `,
36
+ props: {
37
+ reasoning: String,
38
+ message: Object,
39
+ },
40
+ setup(props) {
41
+ const expandedReasoning = ref(new Set())
42
+ const isReasoningExpanded = (id) => expandedReasoning.value.has(id)
43
+ const toggleReasoning = (id) => {
44
+ const s = new Set(expandedReasoning.value)
45
+ if (s.has(id)) {
46
+ s.delete(id)
47
+ } else {
48
+ s.add(id)
49
+ }
50
+ expandedReasoning.value = s
51
+ }
52
+ const formatReasoning = (r) => typeof r === 'string' ? r : JSON.stringify(r, null, 2)
53
+
54
+ return {
55
+ expandedReasoning,
56
+ isReasoningExpanded,
57
+ toggleReasoning,
58
+ formatReasoning,
59
+ }
60
+ }
61
+ }
62
+
23
63
  export default {
24
64
  components: {
25
65
  MessageUsage,
66
+ MessageReasoning,
26
67
  },
27
68
  template: `
28
69
  <div class="flex flex-col h-full">
@@ -110,16 +151,8 @@ export default {
110
151
  ></div>
111
152
 
112
153
  <!-- Collapsible reasoning section -->
113
- <div v-if="message.role === 'assistant' && message.reasoning" class="mt-2 mb-2">
114
- <button type="button" @click="toggleReasoning(message.id)" class="text-xs text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 flex items-center space-x-1">
115
- <svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" :class="isReasoningExpanded(message.id) ? 'transform rotate-90' : ''"><path fill="currentColor" d="M7 5l6 5l-6 5z"/></svg>
116
- <span>{{ isReasoningExpanded(message.id) ? 'Hide reasoning' : 'Show reasoning' }}</span>
117
- </button>
118
- <div v-if="isReasoningExpanded(message.id)" class="reasoning mt-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 p-2">
119
- <div v-if="typeof message.reasoning === 'string'" v-html="$fmt.markdown(message.reasoning)" class="prose prose-xs max-w-none dark:prose-invert"></div>
120
- <pre v-else class="text-xs whitespace-pre-wrap overflow-x-auto">{{ formatReasoning(message.reasoning) }}</pre>
121
- </div>
122
- </div>
154
+ <MessageReasoning v-if="message.role === 'assistant' && (message.reasoning || message.thinking || message.reasoning_content)"
155
+ :reasoning="message.reasoning || message.thinking || message.reasoning_content" :message="message" />
123
156
 
124
157
  <!-- Tool Calls & Outputs -->
125
158
  <div v-if="message.tool_calls && message.tool_calls.length > 0" class="mb-3 space-y-4">
@@ -435,20 +468,6 @@ export default {
435
468
  }
436
469
  }
437
470
 
438
- // Reasoning collapse state and helpers
439
- const expandedReasoning = ref(new Set())
440
- const isReasoningExpanded = (id) => expandedReasoning.value.has(id)
441
- const toggleReasoning = (id) => {
442
- const s = new Set(expandedReasoning.value)
443
- if (s.has(id)) {
444
- s.delete(id)
445
- } else {
446
- s.add(id)
447
- }
448
- expandedReasoning.value = s
449
- }
450
- const formatReasoning = (r) => typeof r === 'string' ? r : JSON.stringify(r, null, 2)
451
-
452
471
  const copyMessageContent = async (message) => {
453
472
  let content = ''
454
473
  if (Array.isArray(message.content)) {
@@ -651,9 +670,6 @@ export default {
651
670
  selectedModelObj,
652
671
  messagesContainer,
653
672
  copying,
654
- isReasoningExpanded,
655
- toggleReasoning,
656
- formatReasoning,
657
673
  copyMessageContent,
658
674
  redoMessage,
659
675
  editMessage,
@@ -1,10 +1,8 @@
1
1
 
2
- import { ref, computed, watch, nextTick, inject } from 'vue'
3
- import { useRouter } from 'vue-router'
2
+ import { ref, watch, nextTick, inject } from 'vue'
4
3
  import { $$, createElement, lastRightPart, ApiResult, createErrorStatus, pick } from "@servicestack/client"
5
4
  import SettingsDialog, { useSettings } from './SettingsDialog.mjs'
6
5
  import ChatBody from './ChatBody.mjs'
7
- import HomeTools from './HomeTools.mjs'
8
6
  import { AppContext } from '../../ctx.mjs'
9
7
 
10
8
  const imageExts = 'png,webp,jpg,jpeg,gif,bmp,svg,tiff,ico'.split(',')
@@ -238,7 +236,7 @@ export function useChatPrompt(ctx) {
238
236
  return request
239
237
  }
240
238
 
241
- async function completion({ request, model, thread, controller, store }) {
239
+ async function completion({ request, thread, model, controller, redirect }) {
242
240
  try {
243
241
  let error
244
242
  if (!model) {
@@ -250,20 +248,18 @@ export function useChatPrompt(ctx) {
250
248
  }
251
249
 
252
250
  if (!model) {
253
- return new ApiResult({
254
- error: createErrorStatus(`Model ${request.model || ''} not found`, 'NotFound')
255
- })
251
+ return ctx.createErrorResult({ message: `Model ${request.model || ''} not found`, errorCode: 'NotFound' })
256
252
  }
257
253
 
258
254
  if (!request.messages) request.messages = []
259
255
  if (!request.metadata) request.metadata = {}
260
256
 
261
- if (store && !thread) {
257
+ if (!thread) {
262
258
  const title = getTextContent(request) || 'New Chat'
263
- thread = await ctx.threads.startNewThread({ title, model })
259
+ thread = await ctx.threads.startNewThread({ title, model, redirect })
264
260
  }
265
261
 
266
- const threadId = thread?.id || ctx.threads.generateThreadId()
262
+ const threadId = thread?.id
267
263
 
268
264
  const ctxRequest = {
269
265
  request,
@@ -282,7 +278,7 @@ export function useChatPrompt(ctx) {
282
278
 
283
279
  let response = null
284
280
  if (!res.ok) {
285
- error = createErrorStatus('', `HTTP ${res.status} ${res.statusText}`)
281
+ error = ctx.createErrorStatus({ message: `HTTP ${res.status} ${res.statusText}` })
286
282
  let errorBody = null
287
283
  try {
288
284
  errorBody = await res.text()
@@ -338,22 +334,6 @@ export function useChatPrompt(ctx) {
338
334
  }
339
335
  }
340
336
 
341
- // Add assistant response (save entire message including reasoning)
342
- const assistantMessage = response.choices?.[0]?.message
343
-
344
- const usage = response.usage
345
- if (usage) {
346
- if (response.metadata?.pricing) {
347
- const [input, output] = response.metadata.pricing.split('/')
348
- usage.duration = response.metadata.duration ?? (Date.now() - startTime)
349
- usage.input = input
350
- usage.output = output
351
- usage.tokens = usage.completion_tokens
352
- usage.price = usage.output
353
- usage.cost = ctx.fmt.tokenCost(usage.prompt_tokens / 1_000_000 * parseFloat(input) + usage.completion_tokens / 1_000_000 * parseFloat(output))
354
- }
355
- }
356
-
357
337
  nextTick(addCopyButtons)
358
338
 
359
339
  return new ApiResult({ response })
@@ -666,7 +646,7 @@ const ChatPrompt = {
666
646
 
667
647
  // Create thread if none exists
668
648
  if (!ctx.threads.currentThread.value) {
669
- thread = await ctx.threads.startNewThread({ model: props.model })
649
+ thread = await ctx.threads.startNewThread({ model: props.model, redirect: true })
670
650
  } else {
671
651
  thread = ctx.threads.currentThread.value
672
652
  }
@@ -740,7 +720,7 @@ const ChatPrompt = {
740
720
  console.debug(`thread title is '${thread.title}'`, request.title)
741
721
  }
742
722
 
743
- const api = await ctx.threads.queueChat(threadId, request)
723
+ const api = await ctx.threads.queueChat({ request, thread })
744
724
  if (api.response) {
745
725
  // success
746
726
  ctx.chat.editingMessage.value = null
@@ -794,6 +774,14 @@ const ChatPrompt = {
794
774
  }
795
775
  }
796
776
 
777
+ const HomeTools = {
778
+ template: `
779
+ <div class="mt-4 flex space-x-3 justify-center items-center">
780
+ <DarkModeToggle />
781
+ </div>
782
+ `,
783
+ }
784
+
797
785
  export default {
798
786
  /**@param {AppContext} ctx */
799
787
  install(ctx) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 3.0.0b8
3
+ Version: 3.0.0b10
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