Repository navigation
fix: improve Kitchen AI reliability and conversation scrolling - #33
Draft
1395490241lhj-code wants to merge 2 commits into
Draft
1395490241lhj-code wants to merge 2 commits into
1395490241lhj-code wants to merge 2 commits into
Conversation
/api/ai-conversation logged every provider failure as provider_unavailable and dropped the upstream error, so 429, 413, timeouts and 5xx could not be told apart in production logs. - ai_conversation_failed and ai_conversation_completed now carry the safe classification: primaryProvider, timeoutMs, requestSizeBytes, and, on failure, upstream status, reason, timeoutSource and upstreamRequestId; a provider switch records the first provider's fallbackReason. - Field values are validated, not just field names: status must be an integer 100-599, codes must be short safe identifiers (otherwise "unrecognized"), request ids must be 8-128 safe characters (otherwise dropped). Upstream error text, keys and conversation content are never logged. This adds diagnostics only. It does not change provider routing, timeouts, fallback or retries, and it does not identify the production failure; that needs the redacted logs after deploy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On device, a meal follow-up got prose instead of recipe cards, failed retries stacked identical error rows, and the transcript pulled a reader back to the bottom on every streamed delta. - Meal follow-ups: a whole short follow-up (换一个 / 今晚做什么 / 还有别的吗 / 推荐点简单的 / Anything else?) inherits the meal intent when one of the last three user questions was an explicit meal question. Home only. Excluding inventory still withholds the shortlist. Sentences that only share a word (我想换手机 / 推荐电影院 / 今晚做什么运动) read only tonight's plan. - Instructions: dish recommendations use present_recipe_card, cite recipeID and keep prose to one or two sentences, without claiming freshness or quantity. The length rule applies only to recommendations; plan changes and other questions are answered in full. - Transcript: an older attempt of a retried turn that carried only an error (or was stopped empty) is hidden once a newer attempt exists. Presentation only; history is unchanged and partial content always stays. - Auto-scroll (AIConversationAutoScroll): one trigger, at most one scroll per update. A drag up to reread stops following; a drag that dismisses the keyboard keeps the previous state. A new user message, a running retry or a different conversation resumes following, without relying on observing .preparingContext. No animation added. - Tests: assembler privacy negatives assert the actual reads; test47 covers scrolling up mid-stream and sending with the keyboard up, with a DEBUG-only incremental stream script. The structured-blocks AXXXL helper now scrolls both ways (that Light AXXXL red also reproduces on bcca20a). No change to the six content blocks, five proposals, coordinator, persistence, confirmation or undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 21, 2026
1395490241lhj-code
added a commit
that referenced
this pull request
Oct 22, 2026
…s end to end - ConversationContextAssembler.isMealQuestion now reads a question clause by clause. A clause about food someone else cooks (食堂, 餐厅, 外卖, 老板, 附近 ...) or about a medicine (药, not 药膳) does not read inventory or the shortlist, unless the same clause names the family's own kitchen (家里, 在家, 冰箱 ...). Other clauses still count, so 「不想点外卖,在家 做什么菜」 stays a cooking question. New pattern (f) covers 家里有鸡蛋能 做什么 / 冰箱里的东西能做点什么. The inventory exclusion, the Home tonightPlan read and the context budget are unchanged. - Tests check the domain reads and the final request context: eating-out and medicine questions read only tonightPlan and carry no inventory facts or missing counts; home-cooking questions read inventory, tonightPlan and the shortlist; excluding inventory still withholds both. - ConversationOrchestratorTests: four end-to-end cases from a provider tool call through the interpreter, coordinator and store: tonight for four, no or null count keeps the existing rule, 0/13/2.5/"4" is refused with nothing written, and a replacement for four previews "4 份" and writes it on confirm. The helper keeps the orchestrator alive for the whole stream, since its turn task holds it weakly. - AIConversationWorkspaceUITests.bringIntoView: when the target sits above the visible band (at AX sizes 继续调整 pushes the last card up), scroll back in small steps; otherwise scroll down as before. Same direction check as PR #33's 329cc50; nothing else from that PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Kitchen AI Reliability & Motion is stacked on PR #32: base
claude/hopeful-volhard-9272e9, headclaude/kitchen-ai-reliability. It fixes problems found during the first real use on an iPhone 17 Pro. It does not fix the production "AI 服务暂时不可用" outage. That outage has no proven root cause yet.Commits:
20a0de2fix(server): improve Kitchen AI upstream failure diagnostics329cc50fix(ios): stabilize Kitchen AI conversation and scrolling11 files, +694/−11. There is no change to the six content blocks, the five proposals, the coordinator, the interpreter, the orchestrator, persistence, confirmation or undo.
main:/api/ai-diagnostics/configreportsrenderCommit=5b6d2b09a4ca. The cause can only be established after this patch is deployed and Render's redactedai_conversation_failedlogs are read for a reproduced failure. Possible causes, none confirmed:requestSizeBytescounts onlymessages. It does not include the ~6,000 characters of tool definitions sent with every request, so it is not the full request size.bcca20a + de14ffd. This PR is not installed on any device.de14ffd, "run legacy data migration once so emptied data stays empty") is not inmainand not in this branch. A read-only merge check shows this branch + iOS: fix bugs and logic issues found in the UX and functional reviews #31 and Kitchen AI Phase 1a: tonight recipe recommendations with deterministic kitchen facts #32 + iOS: fix bugs and logic issues found in the UX and functional reviews #31 both merge cleanly. Final integration must keepde14ffd, and any device build must bebcca20a + de14ffd + this PR.Server: upstream failure diagnostics (
20a0de2)Problem.
/api/ai-conversationcaught every provider exception, logged onlyresultCode: provider_unavailable, and dropped the upstream error. 429, 413, timeouts and 5xx were indistinguishable in production logs.Change.
server.jsandtest/ai-conversation-stream.test.mjsonly.ai_conversation_failed/ai_conversation_completednow also record:primaryProvider,timeoutMsandrequestSizeBytes(messages only, see above);status,reason(the upstream error code),timeoutSourceandupstreamRequestId;fallbackReason(the first provider's code).Field values are validated, not only field names. The logger allowlist does not vet values, so:
statusmust be an integer from 100 to 599;[A-Za-z0-9_.-]), otherwiseunrecognized;[A-Za-z0-9_-], otherwise they are dropped.Upstream error text, keys and conversation content are never logged. Tests assert this with hostile upstream values, for example a code that echoes the prompt and a request id containing spaces and a key fragment.
Unchanged: provider routing, timeouts (45 s primary, 20 s fallback), the fallback rule, retries (
maxRetries: 0, no client auto-retry), rate limiting, and/api/ai-chat.How to read the logs after deploy:
durationMs≈ 45000attempt=2status,reasonandfallbackReasonthen name the upstream cause.iOS: conversation reliability and scrolling (
329cc50)Fixes for what the device run showed:
A meal follow-up got prose instead of cards. 「今晚做什么」, sent after 「用快过期的食材做饭」, read only tonight's plan.
System instructions. When recommending dishes, the model uses
present_recipe_card, cites shortlistrecipeIDs, keeps prose to one or two sentences, and does not claim freshness or quantity. When nothing fits, it says why briefly and gives one next step. The length rule applies only to dish recommendations; plan changes and other questions are answered in full.Failed retries stacked identical error rows. An older attempt of a retried turn is hidden once a newer attempt exists, if it carried only an error or was stopped empty (
AIConversationTranscriptPresentation). This is presentation only: persisted history is unchanged, and a failed attempt with partial text, a card or an action outcome always stays.The transcript yanked readers back to the bottom on every streamed delta. Three
onChangehandlers could scroll in one update. Now (AIConversationAutoScroll):.preparingContext, so it still works if SwiftUI coalesces the send with.requestingor.streaming.No animation was added, and the existing scroll animation and Reduce Motion handling are unchanged (D-043).
Privacy regression checks
These are assembler tests that assert the actual domain reads, not just the intent function:
Validation
All iOS runs used xcodebuild through
dev-sim-sloton the reserved simulator, iOS 27.2, with parallel testing off. Xcode MCP was held by another worktree. The tested tree is byte-identical to329cc50; nothing changed after the final runs.ai-conversation-stream,-lifecycle,-turn-limit,ai-server-routes)No new compiler warnings in the changed files.
How the UI result was reached:
bcca20afailed Light AXXXL with the same error. So this is a pre-existing PR Kitchen AI Phase 1a: tonight recipe recommendations with deterministic kitchen facts #32 red: at AXXXL the 继续调整 chips push the last card above the view, and the test helper only scrolled down.About
test47:test47_ScrollingUpDuringStreamingIsNotPulledBackAndASendFollowsAgainuses a DEBUG-only incremental stream of 40 pieces over about 10 s. It covers: dismiss the keyboard, scroll up mid-stream, position held after the stream ends, then send with the keyboard up and follow the new reply.latestUserMessageIDresume is a defensive fix.Screenshots. test47 captured three screens, kept as local review evidence and not attached here:
AutoScroll-1-ScrolledUpMidStreamAutoScroll-2-StreamFinishedPositionHeldAutoScroll-3-SendFollowsWithKeyboardCI's
iOS testsworkflow runs the whole unit target plus its default UI selection. That selection does not includeAIConversationWorkspaceUITests. To recapture these screens in CI, dispatch it withui_tests: KitchenManagerUITests/AIConversationWorkspaceUITests.Not verified
/api/ai-chat's existing log values are not value-validated. That is pre-existing and out of scope.Proposed, not implemented (needs decisions)
Merge order
de14ffd) and Kitchen AI Phase 1a: tonight recipe recommendations with deterministic kitchen facts #32 intomainin whatever order the owner picks. Keepde14ffd.mainand merge it.🤖 Generated with Claude Code