ChatMessage.stories.svelte 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <script module lang="ts">
  2. import { defineMeta } from '@storybook/addon-svelte-csf';
  3. import ChatMessage from '$lib/components/app/chat/ChatMessages/ChatMessage.svelte';
  4. const { Story } = defineMeta({
  5. title: 'Components/ChatScreen/ChatMessage',
  6. component: ChatMessage,
  7. parameters: {
  8. layout: 'centered'
  9. }
  10. });
  11. // Mock messages for different scenarios
  12. const userMessage: DatabaseMessage = {
  13. id: '1',
  14. convId: 'conv-1',
  15. type: 'message',
  16. timestamp: Date.now() - 1000 * 60 * 5,
  17. role: 'user',
  18. content: 'What is the meaning of life, the universe, and everything?',
  19. parent: '',
  20. thinking: '',
  21. children: []
  22. };
  23. const assistantMessage: DatabaseMessage = {
  24. id: '2',
  25. convId: 'conv-1',
  26. type: 'message',
  27. timestamp: Date.now() - 1000 * 60 * 3,
  28. role: 'assistant',
  29. content:
  30. 'The answer to the ultimate question of life, the universe, and everything is **42**.\n\nThis comes from Douglas Adams\' "The Hitchhiker\'s Guide to the Galaxy," where a supercomputer named Deep Thought calculated this answer over 7.5 million years. However, the question itself was never properly formulated, which is why the answer seems meaningless without context.',
  31. parent: '1',
  32. thinking: '',
  33. children: []
  34. };
  35. const assistantWithReasoning: DatabaseMessage = {
  36. id: '3',
  37. convId: 'conv-1',
  38. type: 'message',
  39. timestamp: Date.now() - 1000 * 60 * 2,
  40. role: 'assistant',
  41. content: "Here's the concise answer, now that I've thought it through carefully for you.",
  42. parent: '1',
  43. thinking:
  44. "Let's consider the user's question step by step:\\n\\n1. Identify the core problem\\n2. Evaluate relevant information\\n3. Formulate a clear answer\\n\\nFollowing this process ensures the final response stays focused and accurate.",
  45. children: []
  46. };
  47. const rawOutputMessage: DatabaseMessage = {
  48. id: '6',
  49. convId: 'conv-1',
  50. type: 'message',
  51. timestamp: Date.now() - 1000 * 60,
  52. role: 'assistant',
  53. content:
  54. '<|channel|>analysis<|message|>User greeted me. Initiating overcomplicated analysis: Is this a trap? No, just a normal hello. Respond calmly, act like a helpful assistant, and do not start explaining quantum physics again. Confidence 0.73. Engaging socially acceptable greeting protocol...<|end|>Hello there! How can I help you today?',
  55. parent: '1',
  56. thinking: '',
  57. children: []
  58. };
  59. let processingMessage = $state({
  60. id: '4',
  61. convId: 'conv-1',
  62. type: 'message',
  63. timestamp: 0, // No timestamp = processing
  64. role: 'assistant',
  65. content: '',
  66. parent: '1',
  67. thinking: '',
  68. children: []
  69. });
  70. let streamingMessage = $state({
  71. id: '5',
  72. convId: 'conv-1',
  73. type: 'message',
  74. timestamp: 0, // No timestamp = streaming
  75. role: 'assistant',
  76. content: '',
  77. parent: '1',
  78. thinking: '',
  79. children: []
  80. });
  81. </script>
  82. <Story
  83. name="User"
  84. args={{
  85. message: userMessage
  86. }}
  87. play={async () => {
  88. const { settingsStore } = await import('$lib/stores/settings.svelte');
  89. settingsStore.updateConfig('disableReasoningFormat', false);
  90. }}
  91. />
  92. <Story
  93. name="Assistant"
  94. args={{
  95. class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
  96. message: assistantMessage
  97. }}
  98. play={async () => {
  99. const { settingsStore } = await import('$lib/stores/settings.svelte');
  100. settingsStore.updateConfig('disableReasoningFormat', false);
  101. }}
  102. />
  103. <Story
  104. name="AssistantWithReasoning"
  105. args={{
  106. class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
  107. message: assistantWithReasoning
  108. }}
  109. play={async () => {
  110. const { settingsStore } = await import('$lib/stores/settings.svelte');
  111. settingsStore.updateConfig('disableReasoningFormat', false);
  112. }}
  113. />
  114. <Story
  115. name="RawLlmOutput"
  116. args={{
  117. class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
  118. message: rawOutputMessage
  119. }}
  120. play={async () => {
  121. const { settingsStore } = await import('$lib/stores/settings.svelte');
  122. settingsStore.updateConfig('disableReasoningFormat', true);
  123. }}
  124. />
  125. <Story
  126. name="WithReasoningContent"
  127. args={{
  128. message: streamingMessage
  129. }}
  130. asChild
  131. play={async () => {
  132. const { settingsStore } = await import('$lib/stores/settings.svelte');
  133. settingsStore.updateConfig('disableReasoningFormat', false);
  134. // Phase 1: Stream reasoning content in chunks
  135. let reasoningText =
  136. 'I need to think about this carefully. Let me break down the problem:\n\n1. The user is asking for help with something complex\n2. I should provide a thorough and helpful response\n3. I need to consider multiple approaches\n4. The best solution would be to explain step by step\n\nThis approach will ensure clarity and understanding.';
  137. let reasoningChunk = 'I';
  138. let i = 0;
  139. while (i < reasoningText.length) {
  140. const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
  141. const chunk = reasoningText.slice(i, i + chunkSize);
  142. reasoningChunk += chunk;
  143. // Update the reactive state directly
  144. streamingMessage.thinking = reasoningChunk;
  145. i += chunkSize;
  146. await new Promise((resolve) => setTimeout(resolve, 50));
  147. }
  148. const regularText =
  149. "Based on my analysis, here's the solution:\n\n**Step 1:** First, we need to understand the requirements clearly.\n\n**Step 2:** Then we can implement the solution systematically.\n\n**Step 3:** Finally, we test and validate the results.\n\nThis approach ensures we cover all aspects of the problem effectively.";
  150. let contentChunk = '';
  151. i = 0;
  152. while (i < regularText.length) {
  153. const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
  154. const chunk = regularText.slice(i, i + chunkSize);
  155. contentChunk += chunk;
  156. // Update the reactive state directly
  157. streamingMessage.content = contentChunk;
  158. i += chunkSize;
  159. await new Promise((resolve) => setTimeout(resolve, 50));
  160. }
  161. streamingMessage.timestamp = Date.now();
  162. }}
  163. >
  164. <div class="w-[56rem]">
  165. <ChatMessage message={streamingMessage} />
  166. </div>
  167. </Story>
  168. <Story
  169. name="Processing"
  170. args={{
  171. message: processingMessage
  172. }}
  173. play={async () => {
  174. const { settingsStore } = await import('$lib/stores/settings.svelte');
  175. settingsStore.updateConfig('disableReasoningFormat', false);
  176. // Import the chat store to simulate loading state
  177. const { chatStore } = await import('$lib/stores/chat.svelte');
  178. // Set loading state to true to trigger the processing UI
  179. chatStore.isLoading = true;
  180. // Simulate the processing state hook behavior
  181. // This will show the "Generating..." text and parameter details
  182. await new Promise((resolve) => setTimeout(resolve, 100));
  183. }}
  184. />