fix: barcode scan performance + timeout + success message
- Refactor _lookup_in_database to accept a shared httpx.AsyncClient so all three Open*Facts database attempts reuse one TLS connection instead of opening a new one per call; restores pre-fallback scan speed - Increase recipe suggest timeout to 120s (was 30s) to survive cf-orch model cold-start on first request of a session - Include product brand in barcode scan success message so the user can clearly see what was found (e.g. "Added: Cheerios (General Mills) to pantry")
This commit is contained in:
parent
200a6ef87b
commit
9a277f9b42
3 changed files with 33 additions and 25 deletions
|
|
@ -32,22 +32,23 @@ class OpenFoodFactsService:
|
|||
"https://world.openproductsfacts.org/api/v2",
|
||||
]
|
||||
|
||||
async def _lookup_in_database(self, barcode: str, base_url: str) -> Optional[Dict[str, Any]]:
|
||||
"""Try one Open*Facts database. Returns parsed product dict or None."""
|
||||
async def _lookup_in_database(
|
||||
self, barcode: str, base_url: str, client: httpx.AsyncClient
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Try one Open*Facts database using an existing client. Returns parsed product dict or None."""
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
f"{base_url}/product/{barcode}.json",
|
||||
headers={"User-Agent": self.USER_AGENT},
|
||||
timeout=10.0,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
return None
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if data.get("status") != 1:
|
||||
return None
|
||||
return self._parse_product_data(data, barcode)
|
||||
response = await client.get(
|
||||
f"{base_url}/product/{barcode}.json",
|
||||
headers={"User-Agent": self.USER_AGENT},
|
||||
timeout=10.0,
|
||||
)
|
||||
if response.status_code == 404:
|
||||
return None
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
if data.get("status") != 1:
|
||||
return None
|
||||
return self._parse_product_data(data, barcode)
|
||||
except httpx.HTTPError as e:
|
||||
logger.debug("HTTP error for %s at %s: %s", barcode, base_url, e)
|
||||
return None
|
||||
|
|
@ -59,22 +60,26 @@ class OpenFoodFactsService:
|
|||
"""
|
||||
Look up a product by barcode, trying OFFs then fallback databases.
|
||||
|
||||
A single httpx.AsyncClient is created for the whole lookup chain so that
|
||||
connection pooling and TLS session reuse apply across all database attempts.
|
||||
|
||||
Args:
|
||||
barcode: UPC/EAN barcode (8-13 digits)
|
||||
|
||||
Returns:
|
||||
Dictionary with product information, or None if not found in any database.
|
||||
"""
|
||||
result = await self._lookup_in_database(barcode, self.BASE_URL)
|
||||
if result:
|
||||
return result
|
||||
|
||||
for db_url in self._FALLBACK_DATABASES:
|
||||
result = await self._lookup_in_database(barcode, db_url)
|
||||
async with httpx.AsyncClient() as client:
|
||||
result = await self._lookup_in_database(barcode, self.BASE_URL, client)
|
||||
if result:
|
||||
logger.info("Barcode %s found in fallback database: %s", barcode, db_url)
|
||||
return result
|
||||
|
||||
for db_url in self._FALLBACK_DATABASES:
|
||||
result = await self._lookup_in_database(barcode, db_url, client)
|
||||
if result:
|
||||
logger.info("Barcode %s found in fallback database: %s", barcode, db_url)
|
||||
return result
|
||||
|
||||
logger.info("Barcode %s not found in any Open*Facts database", barcode)
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ import { storeToRefs } from 'pinia'
|
|||
import { useInventoryStore } from '../stores/inventory'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
import { inventoryAPI } from '../services/api'
|
||||
import type { InventoryItem, BarcodeScanResponse } from '../services/api'
|
||||
import type { InventoryItem } from '../services/api'
|
||||
import { formatQuantity } from '../utils/units'
|
||||
import EditItemModal from './EditItemModal.vue'
|
||||
import ConfirmDialog from './ConfirmDialog.vue'
|
||||
|
|
@ -716,9 +716,11 @@ async function handleScannerGunInput() {
|
|||
|
||||
const item = result.results[0]
|
||||
if (item?.added_to_inventory) {
|
||||
const productName = item.product?.name || 'item'
|
||||
const productBrand = item.product?.brand ? ` (${item.product.brand})` : ''
|
||||
scannerResults.value.push({
|
||||
type: 'success',
|
||||
message: `Added: ${item.product?.name || 'item'} to ${scannerLocation.value}`,
|
||||
message: `Added: ${productName}${productBrand} to ${scannerLocation.value}`,
|
||||
})
|
||||
await refreshItems()
|
||||
} else if (item?.needs_manual_entry) {
|
||||
|
|
|
|||
|
|
@ -585,7 +585,8 @@ export interface BuildRequest {
|
|||
|
||||
export const recipesAPI = {
|
||||
async suggest(req: RecipeRequest): Promise<RecipeResult> {
|
||||
const response = await api.post('/recipes/suggest', req)
|
||||
// Allow up to 120s — cf-orch model cold-start can take 60+ seconds on first request
|
||||
const response = await api.post('/recipes/suggest', req, { timeout: 120000 })
|
||||
return response.data
|
||||
},
|
||||
async getRecipe(id: number): Promise<RecipeSuggestion> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue