Skip to content

AI bean import: roast date derivation, rotated OCR, text size fix, logging - #2

Merged
silasg merged 7 commits into
developfrom
feat/ai-bean-import-enhancements
Mar 30, 2026
Merged

AI bean import: roast date derivation, rotated OCR, text size fix, logging#2
silasg merged 7 commits into
developfrom
feat/ai-bean-import-enhancements

Conversation

@silasg

@silasg silasg commented Mar 28, 2026

Copy link
Copy Markdown
Owner

Key improvements

  • Roast date from best-before (cloud based LLMs only): Derive roast date when label states an offset (e.g. "best before 12 months after roasting")
  • Rotated OCR (multi-pass): Detect 90°-rotated text on coffee labels via additional OCR passes
  • Text size classification fix: Use average line height instead of block bounding box height — prevents tall paragraphs from being misclassified as headings
  • Logging cleanup: Add LLM prompt/response logs for debugging, remove noisy log lines

silasg added 6 commits March 28, 2026 19:33
… offset

Cloud AI prompt now instructs the model to calculate the roast date by
subtracting the stated period from a best-before/expiration date when no
explicit roast date is present but the label explicitly describes the
offset (e.g. 'roasted 12 months before best before date'). The hint
notes this may be stated in the local language of the label.
…bels

Run OCR at 0°, 90°, and 270° for each image to detect text printed
sideways on labels. Rotated-pass blocks are classified using the 0°
pass height statistics as baseline and merged into the enriched text
under a '--- Rotated text detected ---' section (only when rotated
passes actually find text).
… remove noise

Log prompt and response for both Apple Intelligence (in sendLLMPrompt)
and Cloud LLM (in cloud-field-extraction) to make debugging easier.

Suppress the deleteChat 'not implemented' error on iOS. Remove 18
verbose log calls (base64 lengths, OCR JSON dumps, photo processing
progress) that cluttered output without aiding diagnosis.
…height

A multi-line paragraph in small font produces a tall bounding box,
causing misclassification as LARGE. Use average line height within
each block as proxy for font size, falling back to block height
when line data is unavailable.
- Remove no-op test asserting block texts on raw string (no layout logic exercised)
- Remove subsumed rotated-section test, move hasUsefulMetadata assertion into classification loop
- Modify shouldUseMetadata test to use line data, proving representative height drives decision
- Extract boundingBoxHeight() to eliminate duplication in getRepresentativeHeight
- Refactor createBlock to use options object instead of 7 positional params
@silasg
silasg force-pushed the feat/ai-bean-import-enhancements branch from eb4eed5 to 7b26edb Compare March 28, 2026 18:33
});

[90, 270].forEach((degrees) => {
it(`should produce a non-empty base64 string without data URL prefix when rotated by ${degrees}°`, async () => {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this test it was rotated? Does it make sense to generate the expected image as well and compare those?

);

const ocrResult = await this.ocrWithRotations(imageData.base64String);
const rawText = ocrResult.primary.text;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean rawtext does not include rotated text, even when found? Why is this not a problem?

img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this check?


// Collect all rotated blocks
const rotatedBlocks = multiPass.rotated.flatMap((r) => r.blocks ?? []);
if (rotatedBlocks.length === 0) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this check the same as L150?

- Strengthen rotation test to verify dimensions are swapped (4×2 → 2×4)
- Include rotated pass text in rawText via extracted collectRawText()
- Replace canvas context null guard with optional chaining
- Collapse duplicate early-return guards in enrichWithLayoutMultiPass
- Extract stripLayoutHeader() to eliminate duplicated regex
@silasg
silasg merged commit e3d4e67 into develop Mar 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant