From 824b23adfee2fae66bcdd95f1c87fd79d3f997a8 Mon Sep 17 00:00:00 2001 From: Matteo Marsala Date: Wed, 26 Nov 2025 12:40:01 +0100 Subject: [PATCH] Add error management, update scrollbar --- examples/bedtime-story-teller/assets/app.js | 99 ++++++++++++------- .../bedtime-story-teller/assets/index.html | 1 + .../bedtime-story-teller/assets/style.css | 15 ++- examples/bedtime-story-teller/python/main.py | 79 ++++++++------- 4 files changed, 121 insertions(+), 73 deletions(-) diff --git a/examples/bedtime-story-teller/assets/app.js b/examples/bedtime-story-teller/assets/app.js index 506c8c4..bf3d785 100644 --- a/examples/bedtime-story-teller/assets/app.js +++ b/examples/bedtime-story-teller/assets/app.js @@ -7,55 +7,83 @@ const socket = io(`http://${window.location.host}`); let generateStoryButtonOriginalHTML = ''; // To store the original content of the generate story button let storyBuffer = ''; +// Error container elements +const errorContainer = document.getElementById('error-container'); +function showError(message) { + errorContainer.textContent = message; + errorContainer.style.display = 'block'; +} -function initSocketIO() { - socket.on('prompt', (data) => { - const promptContainer = document.getElementById('prompt-container'); - const promptDisplay = document.getElementById('prompt-display'); - promptDisplay.innerHTML = data; - promptContainer.style.display = 'block'; - }); - - socket.on('response', (data) => { - - document.getElementById('story-container').style.display = 'flex'; - - storyBuffer += data; - - }); - - - - socket.on('stream_end', () => { +function hideError() { + errorContainer.style.display = 'none'; + errorContainer.textContent = ''; +} - const storyResponse = document.getElementById('story-response'); - storyResponse.innerHTML = storyBuffer; +function handlePrompt(data) { + const promptContainer = document.getElementById('prompt-container'); + const promptDisplay = document.getElementById('prompt-display'); + promptDisplay.innerHTML = data; + promptContainer.style.display = 'block'; +} - +function handleResponse(data) { + document.getElementById('story-container').style.display = 'flex'; + storyBuffer += data; +} - document.getElementById('loading-spinner').style.display = 'none'; +function handleStreamEnd() { + hideError(); // Hide any errors on successful stream end - const clearStoryButton = document.getElementById('clear-story-button'); + const storyResponse = document.getElementById('story-response'); + storyResponse.innerHTML = storyBuffer; - clearStoryButton.style.display = 'block'; + document.getElementById('loading-spinner').style.display = 'none'; + const clearStoryButton = document.getElementById('clear-story-button'); + clearStoryButton.style.display = 'block'; + clearStoryButton.disabled = false; - clearStoryButton.disabled = false; + const generateStoryButton = document.querySelector('.generate-story-button'); + if (generateStoryButton) { + generateStoryButton.disabled = false; + generateStoryButton.innerHTML = generateStoryButtonOriginalHTML; // Restore original content + } +} - +function handleStoryError(data) { + // Hide the loading spinner + document.getElementById('loading-spinner').style.display = 'none'; - const generateStoryButton = document.querySelector('.generate-story-button'); + // Restore the generate story button + const generateStoryButton = document.querySelector('.generate-story-button'); + if (generateStoryButton) { + generateStoryButton.disabled = false; + generateStoryButton.innerHTML = generateStoryButtonOriginalHTML; + } - if (generateStoryButton) { + // Display the error message in the dedicated error container + showError(`An error occurred while generating the story: ${data.error}`); - generateStoryButton.disabled = false; + // Also show the "New story" button to allow the user to restart + const clearStoryButton = document.getElementById('clear-story-button'); + clearStoryButton.style.display = 'block'; + clearStoryButton.disabled = false; +} - generateStoryButton.innerHTML = generateStoryButtonOriginalHTML; // Restore original content +function initSocketIO() { + socket.on('prompt', handlePrompt); + socket.on('response', handleResponse); + socket.on('stream_end', handleStreamEnd); + socket.on('story_error', handleStoryError); - } + socket.on('connect', () => { + hideError(); // Clear any previous errors on successful connection + }); - }); + socket.on('disconnect', () => { + showError("Connection to backend lost. Please refresh the page or check the backend server."); + }); } function unlockAndOpenNext(currentContainer) { @@ -229,6 +257,7 @@ function gatherDataAndGenerateStory() { } function generateStory(data) { + hideError(); // Hide any errors when starting a new generation document.querySelector('.story-output-placeholder').style.display = 'none'; const responseArea = document.getElementById('story-response-area'); responseArea.style.display = 'flex'; @@ -251,6 +280,7 @@ function generateStory(data) { } function resetStoryView() { + hideError(); // Hide any errors when resetting view document.querySelector('.story-output-placeholder').style.display = 'flex'; const responseArea = document.getElementById('story-response-area'); responseArea.style.display = 'none'; @@ -469,6 +499,7 @@ document.addEventListener('DOMContentLoaded', () => { }); document.getElementById('generate-randomly-button').addEventListener('click', () => { + hideError(); // Hide any errors when starting a new generation // Age const ageChips = document.querySelectorAll('.parameter-container:nth-child(1) .chip'); const randomAgeChip = getRandomElement(ageChips); @@ -519,4 +550,4 @@ document.addEventListener('DOMContentLoaded', () => { generateStory(storyData); }); -}); +}); \ No newline at end of file diff --git a/examples/bedtime-story-teller/assets/index.html b/examples/bedtime-story-teller/assets/index.html index f4e0e21..f10b8f8 100644 --- a/examples/bedtime-story-teller/assets/index.html +++ b/examples/bedtime-story-teller/assets/index.html @@ -201,6 +201,7 @@

Story

+ diff --git a/examples/bedtime-story-teller/assets/style.css b/examples/bedtime-story-teller/assets/style.css index 26bf5da..bc8ac0a 100644 --- a/examples/bedtime-story-teller/assets/style.css +++ b/examples/bedtime-story-teller/assets/style.css @@ -687,6 +687,14 @@ body { 100% { transform: rotate(360deg); } } +.error-message { + background-color: #f8d7da; + color: #721c24; + padding: 10px; + margin-top: 20px; + border-radius: 5px; + text-align: center; +} /* @@ -791,5 +799,10 @@ body { } #story-response::-webkit-scrollbar-track { - background-color: transparent; + background-color: #ECF1F1; } + +#story-response::-webkit-scrollbar-thumb { + background-color: #C9D2D2; + border-radius: 4px; +} \ No newline at end of file diff --git a/examples/bedtime-story-teller/python/main.py b/examples/bedtime-story-teller/python/main.py index 4dfd7ae..b8f1a02 100644 --- a/examples/bedtime-story-teller/python/main.py +++ b/examples/bedtime-story-teller/python/main.py @@ -18,44 +18,47 @@ def generate_story(_, data): - age = data.get('age', 'any') - theme = data.get('theme', 'any') - tone = data.get('tone', 'any') - ending_type = data.get('endingType', 'any') - narrative_structure = data.get('narrativeStructure', 'any') - duration = data.get('duration', 'any') - characters = data.get('characters', []) - other = data.get('other', '') - - # Create a prompt with HTML for display - prompt_for_display = f"As a parent who loves to read bedtime stories to my {age} year old child, I need a delightful and age-appropriate story." - - if characters: - prompt_for_display += " Characters of the story: " - char_prompts = [] - for i, char in enumerate(characters): - ch = f"Character {i+1} ({char.get('name')}, {char.get('role')}" - ch += f", {char.get('description')})" if char.get('description') else ")" - char_prompts.append(ch) - prompt_for_display += ", ".join(char_prompts) - prompt_for_display += "." - - prompt_for_display += f" The story type is {theme}. The tone should be {tone}. The format should be a narrative-style story with a clear beginning, middle, and end, allowing for a smooth and engaging reading experience. The objective is to entertain and soothe the child before bedtime. Provide a brief introduction to set the scene and introduce the main character. The scope should revolve around the topic: managing emotions and conflicts. The length should be approximately {duration}. Please ensure the story has a {narrative_structure} narrative structure, leaving the child with a sense of {ending_type}. The language should be easy to understand and suitable for my child's age comprehension." - if other: - prompt_for_display += f"\n\nOther on optional stuff for the story: {other}" - - # Create a plain text prompt for the LLM by stripping HTML tags - prompt_for_llm = re.sub('<[^>]*>', '', prompt_for_display) - - # Send the display prompt to the UI - ui.send_message("prompt", prompt_for_display) - - # Use the plain text prompt for the LLM and stream the response - for resp in llm.chat_stream(prompt_for_llm): - ui.send_message("response", resp) - - # Signal the end of the stream - ui.send_message("stream_end", {}) + try: + age = data.get('age', 'any') + theme = data.get('theme', 'any') + tone = data.get('tone', 'any') + ending_type = data.get('endingType', 'any') + narrative_structure = data.get('narrativeStructure', 'any') + duration = data.get('duration', 'any') + characters = data.get('characters', []) + other = data.get('other', '') + + # Create a prompt with HTML for display + prompt_for_display = f"As a parent who loves to read bedtime stories to my {age} year old child, I need a delightful and age-appropriate story." + + if characters: + prompt_for_display += " Characters of the story: " + char_prompts = [] + for i, char in enumerate(characters): + ch = f"Character {i+1} ({char.get('name')}, {char.get('role')}" + ch += f", {char.get('description')})" if char.get('description') else ")" + char_prompts.append(ch) + prompt_for_display += ", ".join(char_prompts) + prompt_for_display += "." + + prompt_for_display += f" The story type is {theme}. The tone should be {tone}. The format should be a narrative-style story with a clear beginning, middle, and end, allowing for a smooth and engaging reading experience. The objective is to entertain and soothe the child before bedtime. Provide a brief introduction to set the scene and introduce the main character. The scope should revolve around the topic: managing emotions and conflicts. The length should be approximately {duration}. Please ensure the story has a {narrative_structure} narrative structure, leaving the child with a sense of {ending_type}. The language should be easy to understand and suitable for my child's age comprehension." + if other: + prompt_for_display += f"\n\nOther on optional stuff for the story: {other}" + + # Create a plain text prompt for the LLM by stripping HTML tags + prompt_for_llm = re.sub('<[^>]*>', '', prompt_for_display) + + # Send the display prompt to the UI + ui.send_message("prompt", prompt_for_display) + + # Use the plain text prompt for the LLM and stream the response + for resp in llm.chat_stream(prompt_for_llm): + ui.send_message("response", resp) + + # Signal the end of the stream + ui.send_message("stream_end", {}) + except Exception as e: + ui.send_message("story_error", {"error": str(e)}) ui.on_message("generate_story", generate_story)