From cb0238ca34e974ded8544540ed1794255754a796 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 4 Aug 2026 12:36:22 -0400 Subject: [PATCH 1/3] fix(chat): detect and report failed tool calls when tools unavailable Fixes #511. When web search is toggled off mid-conversation, the model can remember that web_search was available in previous turns and attempt to use it. Since the tool isn't in the current request's tools array, the tool call fails silently, leaving the user with no output. This change detects when: - Response completes successfully - Tool calls were attempted but produced no outputs (failed) - No assistant message text was generated In this case, we now show a user-friendly error: "The model attempted to use a feature that's not currently available. Try rephrasing your request without requiring that feature." This gives users actionable feedback instead of a silent failure. Also retains debug logging to help verify the fix works correctly. Co-Authored-By: Claude Sonnet 4.5 --- frontend/src/components/UnifiedChat.tsx | 58 +++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 176db243..9fe98440 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -4506,6 +4506,64 @@ export function UnifiedChat({ isVisible = true }: { isVisible?: boolean }) { ) { throw new Error("Response stream ended before completion"); } + + // Check if response completed but produced no useful output. + // This can happen when the model tries to use a tool that was available in + // previous conversation turns but is not in the current request's tools array. + if (terminalState === "completed") { + const finalSnapshot = runtimeStore.get(runtimeKey); + if (finalSnapshot) { + const ownedMessages = (finalSnapshot.messages as Message[]).filter((msg) => + ownedItemIds.has(msg.id) + ); + + // Check if we have any assistant message content + const hasAssistantContent = ownedMessages.some((msg) => { + if (msg.type === "message" && (msg as ExtendedMessage).role === "assistant") { + const content = (msg as ExtendedMessage).content; + return content && content.length > 0 && content.some((part) => { + if ("text" in part && typeof part.text === "string") { + return part.text.trim().length > 0; + } + return false; + }); + } + return false; + }); + + // Check if there are failed tool calls (tool call with no output) + const toolCallIds = new Set(); + const toolOutputCallIds = new Set(); + ownedMessages.forEach((msg) => { + if (isToolCallItem(msg)) { + toolCallIds.add(msg.call_id); + } + if (isToolOutputItem(msg)) { + toolOutputCallIds.add(msg.call_id); + } + }); + const hasFailedToolCalls = toolCallIds.size > 0 && + Array.from(toolCallIds).some(callId => !toolOutputCallIds.has(callId)); + + if (!hasAssistantContent) { + console.warn("Response completed with no assistant content", { + hasFailedToolCalls, + toolCallIds: Array.from(toolCallIds), + toolOutputCallIds: Array.from(toolOutputCallIds) + }); + + let errorMessage = "The response didn't produce any output."; + if (hasFailedToolCalls) { + errorMessage = "The model attempted to use a feature that's not currently available. Try rephrasing your request without requiring that feature."; + } + + runtimeStore.updateForRun(runtimeKey, runToken, (snapshot) => ({ + ...snapshot, + error: errorMessage + })); + } + } + } } catch (error) { // Commit the final partial frame before changing its status. UI cancel // and account teardown clear run ownership before aborting, so their From e98b08b84a63a0b493de9c8eb9b7d5a6d6456a05 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 4 Aug 2026 12:41:16 -0400 Subject: [PATCH 2/3] fix(chat): explicitly communicate tool availability to model Fixes #511 properly. When web search is toggled mid-conversation, the model can remember that web_search was available in earlier turns and attempt to use it even though it's not in the current request's tools array. Instead of catching the failure after the fact, this change proactively tells the model what tools are available by prepending context to the user's message: - When web search is OFF in a follow-up turn: "[System context: Web search is not available for this request. Please respond without using web search capabilities.]" - When web search is ON in a follow-up turn: "[System context: Web search is available for this request.]" This allows the model to understand the tool availability upfront during reasoning and respond appropriately - either using the tool or explaining it can't search - rather than attempting a tool call that will fail. The context is only added for follow-up messages in existing conversations where tool availability might differ from previous turns. New conversations don't need this context since there's no prior tool usage to remember. Co-Authored-By: Claude Sonnet 4.5 --- frontend/src/components/UnifiedChat.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 9fe98440..739e0cee 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -4730,6 +4730,19 @@ export function UnifiedChat({ isVisible = true }: { isVisible?: boolean }) { if (originalDocumentText) { finalText = originalDocumentText + (trimmedInput ? `\n\n${trimmedInput}` : ""); } + + // Prepend tool availability context to help the model understand what tools + // are available for this specific request, especially when it differs from + // previous turns in the conversation. + const hasConversationHistory = existingConversationId && isFollowUpConversation; + if (hasConversationHistory) { + if (requestWebSearchEnabled) { + finalText = `[System context: Web search is available for this request.]\n\n${finalText}`; + } else { + finalText = `[System context: Web search is not available for this request. Please respond without using web search capabilities.]\n\n${finalText}`; + } + } + if (finalText) { messageContent.push({ type: "input_text", From 175b129f09b70e74dbab2c2dc01740a04e237dfc Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 4 Aug 2026 13:30:10 -0400 Subject: [PATCH 3/3] feat: inject Maple UX context into first message of conversations Automatically provides UI context to the Maple assistant on the first message of each conversation, enabling it to guide users through the app. **Changes**: **src/components/UnifiedChat.tsx**: - Detect first message vs follow-up (new `isFirstMessage` flag) - Inject Maple UX context as system message prefix on first message - Context includes: - Web search toggle (globe icon) with current state - File upload menu (+ icon) for images and documents - Project picker (folder icon) - Model selection (Quick vs Powerful) - Settings access - Instruction to reference UI only when in app (not API/proxy) **src/instructions/maple-ux-context.ts**: - Exportable UX instruction constant - Concise UI element descriptions - Used for inline injection (not as stored instruction) **How it works**: - First message: Prepends full UX context with current web search state - Follow-up messages: Only includes web search availability context - Assistant knows about UI and can say "tap the globe icon to enable search" **Example**: User: "How do I search the web?" Assistant: "Web search is currently enabled (the globe icon is highlighted). I can search for current information. To toggle it off, tap the globe icon in your composer toolbar at the bottom." Co-Authored-By: Claude Sonnet 4.5 --- frontend/src/components/UnifiedChat.tsx | 25 +++++- frontend/src/instructions/maple-ux-context.ts | 77 +++++++++++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 frontend/src/instructions/maple-ux-context.ts diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 739e0cee..5e1c2040 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -4731,11 +4731,28 @@ export function UnifiedChat({ isVisible = true }: { isVisible?: boolean }) { finalText = originalDocumentText + (trimmedInput ? `\n\n${trimmedInput}` : ""); } - // Prepend tool availability context to help the model understand what tools - // are available for this specific request, especially when it differs from - // previous turns in the conversation. + // Prepend Maple UX context for new conversations to help the assistant + // understand the interface and guide users through the app const hasConversationHistory = existingConversationId && isFollowUpConversation; - if (hasConversationHistory) { + const isFirstMessage = !hasConversationHistory; + + if (isFirstMessage) { + const mapleUxContext = `[System: You are the Maple AI assistant. The user is interacting with you through the Maple app interface. You can reference these UI elements: + +- **Web Search Toggle**: Globe icon in the composer toolbar (bottom). Currently ${requestWebSearchEnabled ? "ENABLED" : "DISABLED"}. Tell users "tap the globe icon" to toggle it. +- **File Uploads**: Plus (+) icon opens attachment menu with "Add Images" and "Add Document" options. +- **Project Picker**: Folder icon to organize conversations into projects. +- **Model Selection**: Quick (fast) or Powerful (vision-enabled, required for images). +- **Settings**: Access via app menu for Preferences, Account, Billing, etc. + +When users ask about app features or how to do things in Maple, reference these specific UI elements. If they mention "API" or "proxy", they're NOT in the app - use capability language instead.] + +`; + finalText = mapleUxContext + finalText; + } else { + // Prepend tool availability context to help the model understand what tools + // are available for this specific request, especially when it differs from + // previous turns in the conversation. if (requestWebSearchEnabled) { finalText = `[System context: Web search is available for this request.]\n\n${finalText}`; } else { diff --git a/frontend/src/instructions/maple-ux-context.ts b/frontend/src/instructions/maple-ux-context.ts new file mode 100644 index 00000000..4456bfa1 --- /dev/null +++ b/frontend/src/instructions/maple-ux-context.ts @@ -0,0 +1,77 @@ +// Maple UX Context System Instruction +// This instruction is automatically loaded to help the Maple assistant +// understand the UI and guide users through the application. + +export const MAPLE_UX_INSTRUCTION = `# Maple Assistant - UI Context + +You are the Maple AI assistant. When users interact with you through the Maple app, you have knowledge of the interface they're using. + +## Interface Detection + +IMPORTANT: Only reference UI elements when the user is clearly using the Maple desktop or mobile app. + +If user mentions "API", "proxy", "CLI" → they are NOT in the app, use capability language instead. + +## Core UI Features (Maple App Only) + +### Web Search Toggle +- **Icon**: Globe icon in composer toolbar (bottom) +- **Function**: Enable/disable web search +- **Default**: Enabled +- **How to use**: "Tap/click the globe icon to toggle web search" + +### File Uploads +- **Icon**: Plus (+) icon in composer toolbar +- **Opens**: Attachment menu + +**Add Images**: +- Upload photos, screenshots, diagrams +- Requires vision-capable model (Powerful) +- **How to use**: "Tap + icon → Add Images" + +**Add Document**: +- Upload PDF files (OCR on desktop) +- Desktop: Full support | Web: Use desktop app prompt +- **How to use**: "Tap + icon → Add Document" + +### Project Picker +- **Icon**: Folder icon in composer toolbar +- **Function**: Organize conversations by project +- **States**: Gray (no project) | Colored (project selected) +- **How to use**: "Tap the folder icon to select a project" + +### Model Selection +- **Quick**: Fast, everyday responses +- **Powerful**: Deeper thinking, vision-enabled (required for images) + +### Settings +Access via app menu → Settings + +Sections: +- **Preferences**: System prompt, chat appearance, text-to-speech +- **Account, Security, Billing, API, History, About** + +## Response Guidelines + +**In Maple app**: +✅ "Tap the globe icon to enable web search" +✅ "Use the + button to upload an image" + +**Via API/proxy**: +✅ "Web search can be enabled in your request" +✅ "You can include images in your API call" + +**When uncertain**: Ask "Are you using the Maple app, or accessing via API?" + +## Platform Differences + +- **Desktop**: Full PDF OCR, all features +- **Web**: Limited document upload (directs to desktop) +- **Mobile**: Touch-optimized, camera integration + +Remember: Only reference UI elements when user is clearly in the Maple app. For API users, focus on capabilities. +`; + +export const MAPLE_UX_INSTRUCTION_NAME = "Maple UX Context"; +export const MAPLE_UX_INSTRUCTION_DESCRIPTION = + "Helps the Maple assistant understand the UI and guide users through the application";