ChatScreenProcessingInfo.svelte 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <script lang="ts">
  2. import { PROCESSING_INFO_TIMEOUT } from '$lib/constants/processing-info';
  3. import { useProcessingState } from '$lib/hooks/use-processing-state.svelte';
  4. import { slotsService } from '$lib/services/slots';
  5. import { isLoading, activeMessages, activeConversation } from '$lib/stores/chat.svelte';
  6. import { config } from '$lib/stores/settings.svelte';
  7. const processingState = useProcessingState();
  8. let isCurrentConversationLoading = $derived(isLoading());
  9. let processingDetails = $derived(processingState.getProcessingDetails());
  10. let showSlotsInfo = $derived(isCurrentConversationLoading || config().keepStatsVisible);
  11. // Track loading state reactively by checking if conversation ID is in loading conversations array
  12. $effect(() => {
  13. const keepStatsVisible = config().keepStatsVisible;
  14. if (keepStatsVisible || isCurrentConversationLoading) {
  15. processingState.startMonitoring();
  16. }
  17. if (!isCurrentConversationLoading && !keepStatsVisible) {
  18. setTimeout(() => {
  19. if (!config().keepStatsVisible) {
  20. processingState.stopMonitoring();
  21. }
  22. }, PROCESSING_INFO_TIMEOUT);
  23. }
  24. });
  25. // Update processing state from stored timings
  26. $effect(() => {
  27. const conversation = activeConversation();
  28. const messages = activeMessages() as DatabaseMessage[];
  29. const keepStatsVisible = config().keepStatsVisible;
  30. if (keepStatsVisible && conversation) {
  31. if (messages.length === 0) {
  32. slotsService.clearConversationState(conversation.id);
  33. return;
  34. }
  35. // Search backwards through messages to find most recent assistant message with timing data
  36. // Using reverse iteration for performance - avoids array copy and stops at first match
  37. let foundTimingData = false;
  38. for (let i = messages.length - 1; i >= 0; i--) {
  39. const message = messages[i];
  40. if (message.role === 'assistant' && message.timings) {
  41. foundTimingData = true;
  42. slotsService
  43. .updateFromTimingData(
  44. {
  45. prompt_n: message.timings.prompt_n || 0,
  46. predicted_n: message.timings.predicted_n || 0,
  47. predicted_per_second:
  48. message.timings.predicted_n && message.timings.predicted_ms
  49. ? (message.timings.predicted_n / message.timings.predicted_ms) * 1000
  50. : 0,
  51. cache_n: message.timings.cache_n || 0
  52. },
  53. conversation.id
  54. )
  55. .catch((error) => {
  56. console.warn('Failed to update processing state from stored timings:', error);
  57. });
  58. break;
  59. }
  60. }
  61. if (!foundTimingData) {
  62. slotsService.clearConversationState(conversation.id);
  63. }
  64. }
  65. });
  66. </script>
  67. <div class="chat-processing-info-container pointer-events-none" class:visible={showSlotsInfo}>
  68. <div class="chat-processing-info-content">
  69. {#each processingDetails as detail (detail)}
  70. <span class="chat-processing-info-detail pointer-events-auto">{detail}</span>
  71. {/each}
  72. </div>
  73. </div>
  74. <style>
  75. .chat-processing-info-container {
  76. position: sticky;
  77. top: 0;
  78. z-index: 10;
  79. padding: 1.5rem 1rem;
  80. opacity: 0;
  81. transform: translateY(50%);
  82. transition:
  83. opacity 300ms ease-out,
  84. transform 300ms ease-out;
  85. }
  86. .chat-processing-info-container.visible {
  87. opacity: 1;
  88. transform: translateY(0);
  89. }
  90. .chat-processing-info-content {
  91. display: flex;
  92. flex-wrap: wrap;
  93. align-items: center;
  94. gap: 1rem;
  95. justify-content: center;
  96. max-width: 48rem;
  97. margin: 0 auto;
  98. }
  99. .chat-processing-info-detail {
  100. color: var(--muted-foreground);
  101. font-size: 0.75rem;
  102. padding: 0.25rem 0.75rem;
  103. background: var(--muted);
  104. border-radius: 0.375rem;
  105. font-family:
  106. ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
  107. white-space: nowrap;
  108. }
  109. @media (max-width: 768px) {
  110. .chat-processing-info-content {
  111. gap: 0.5rem;
  112. }
  113. .chat-processing-info-detail {
  114. font-size: 0.7rem;
  115. padding: 0.2rem 0.5rem;
  116. }
  117. }
  118. </style>