From 6aabf3eac15ee12d32bc292fe490aa47eb79e4fa Mon Sep 17 00:00:00 2001 From: Daniel Bedeleanu Date: Fri, 17 Apr 2026 12:57:06 +0300 Subject: [PATCH] feat(ai): refine extraction prompt with human-readable size conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add SIZE CONVERSION RULES section (critical) with explicit thresholds - Convert ≥1600GB to TB format (e.g., 1600GB → 1.6TB) - Convert memory ≥1024MB to GB format - Update Item field examples with human-readable sizes - Update Size field definition to emphasize HUMAN-READABLE format - Update OCR key definition with size conversion examples - Apply rules consistently across all size-related fields This ensures AI-extracted items display sizes in user-friendly format and improves OCR matching accuracy with normalized size representations. Co-Authored-By: Claude Haiku 4.5 --- backend/main.py | 19 +++++-- config/ai_prompt.md | 127 +++++++++++++++++++++++++++++++----------- frontend/public/sw.js | 2 +- 3 files changed, 108 insertions(+), 40 deletions(-) diff --git a/backend/main.py b/backend/main.py index c9379b54..e4bbcf3f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -105,25 +105,32 @@ def startup_event(): default_prompt = ( "Extract hardware specifications with PRECISE formatting.\n" "For any field, do not add comments in parenthesis or measurement units in Item name.\n\n" + "SIZE CONVERSION RULES (CRITICAL - Apply to all size fields):\n" + " Convert sizes to HUMAN-READABLE format:\n" + " • Storage ≥1600GB: convert to TB (e.g., '1600GB' → '1.6TB', '2048GB' → '2TB', '8192GB' → '8TB')\n" + " • Storage 512GB-1599GB: keep as GB (e.g., '512GB', '1TB' if labeled as 1000GB)\n" + " • Memory ≥1024MB: convert to GB (e.g., '1024MB' → '1GB')\n" + " • Cables/length: express in meters (e.g., '5m', '30m')\n" + " Apply this rule to: Item field size prefix, Size field, and OCR key sizes.\n\n" "ITEM FIELD FORMAT (Critical):\n" "[] \n" - " : GB/TB for storage, meters for cables (e.g., '2m', '256GB', '2TB'). Omit diameter/mm measurements.\n" + " : HUMAN-READABLE format - GB/TB for storage, meters for cables (e.g., '2m', '256GB', '1.6TB'). Omit diameter/mm measurements.\n" " : DDR3/DDR4/DDR5/SSD/HDD/NVMe/SAS/SATA/Patchcord/Fiber/Cable/Transceiver etc.\n" " : HP/HPE/Dell/Samsung/Cisco/Lenovo/Hynix etc.\n" " : RJ45/LC-LC/MPO/U.3/SATA/SAS/LC/ST etc. Omit if N/A.\n" " : PN only if visible on label. Omit serial numbers.\n" - "Examples: '5m Patchcord LC-LC' / '256GB SSD Samsung SAS' / '128GB DDR4 Hynix SK-234' / '2TB NVMe HP U.3'\n\n" + "Examples: '5m Patchcord LC-LC' / '256GB SSD Samsung SAS' / '128GB DDR4 Hynix SK-234' / '1.6TB NVMe HP U.3' / '8TB HDD Seagate SATA'\n\n" "TYPE: Item asset class (DDR3/SSD/NVMe/Patchcord/SFP etc.)\n" "DESCRIPTION: Technical details max 5 words (e.g., 'High speed fiber optic cable'). Omit size/length here.\n" "CATEGORY: Broad ecosystem (Memory, Storage, Network, Cabling, etc.)\n" "CONNECTOR: Physical interface type (e.g., 'LC', 'RJ45', 'U.3')\n" - "SIZE: Capacity or length ONLY (e.g., '256GB', '2m')\n" + "SIZE: HUMAN-READABLE capacity or length ONLY (e.g., '256GB', '1.6TB', '2m'). No serial numbers or comments.\n" "COLOR: Physical color if distinguishing.\n" "PartNr: Part number only (no serial numbers)\n" - "OCR: Robust matching key. Include: core type + vendor + key identifiers + common variations.\n" - " Example for '5m Patchcord LC-LC': 'PATCHCORD 5M LC LC CAT6 FIBER'\n" + "OCR: Robust matching key. Include: core type + vendor + key identifiers + HUMAN-READABLE size + common variations.\n" + " Examples: 'PATCHCORD 5M LC LC CAT6 FIBER' / '256GB SSD SAMSUNG' / '1.6TB NVME HP' / '128GB DDR4 HYNIX'\n" " Include abbreviations: 'DDR4'/'DDR' or 'SSD'/'SSDS' or 'HP'/'HPE' or 'NVMe'/'NVME'\n" - " Format: ALL UPPERCASE, space-separated tokens, NO special chars. Skip serial numbers." + " Format: ALL UPPERCASE, space-separated tokens, NO special chars. Skip serial numbers. Use human-readable sizes." ) # Wrap in JSON instructions for reliable parsing diff --git a/config/ai_prompt.md b/config/ai_prompt.md index 38598ded..23cec56e 100644 --- a/config/ai_prompt.md +++ b/config/ai_prompt.md @@ -1,37 +1,98 @@ -# Technical Inventory Multi-Label Extraction Protocol + # Technical Inventory Hardware Extraction Protocol -Your goal is to extract precise hardware specifications for ALL relevant inventory items found in the image. + Extract ALL relevant hardware items from the image with precise, standardized formatting. -## Intelligence Rules: -1. **Filtering Strategy**: - - **INCLUDE**: Standalone physical hardware, modules, cables, servers, and storage units. - - **EXCLUDE**: Generic mounting hardware (screws, nails, rails, brackets), paper licenses/documentation, or empty packaging, UNLESS they are the primary and only subject of the image. - - In cases like image labels listing multiple SKUs (e.g., 5m cable and 7m cable), TREAT EACH LINE AS A SEPARATE ITEM. + ## Filtering Rules + - **INCLUDE**: Physical hardware, modules, cables, servers, storage, transceivers + - **EXCLUDE**: Generic mounting hardware (screws, brackets, rails), paper licenses, empty packaging + - **Multi-item labels**: Treat each SKU/variant as a separate item (e.g., "5m cable" and "7m cable" = 2 items) -2. **Field Definitions**: - - **Item**: Primary identity (Manufacturer + Core Class). Max 3 words (e.g., "Cisco SFP Module"). - - **Type**: Asset classification (e.g., "SFP", "patch cords", "NVMe"). - - **Description**: Technical summary emphasizing speed/capacity. Max 5 words. - - **Category**: Broad ecosystem (e.g., "Network", "Storage"). - - **Connector**: Physical interface (e.g., "LC/UPC", "RJ45"). - - **Size**: Capacity or Length (e.g., "1.6TB", "5m"). - - **Color**: Physical distinguish color. - - **PartNr**: Absolute technical identifier (P/N, SKU). Omit Serial Numbers. - - **OCR**: Concise technical string for local heuristic matching. **EXCLUDE ALL SERIAL NUMBERS.** + ## Item Field Format (CRITICAL) + [] -## Output Format: -Return ONLY a valid JSON object with the key "items" containing an array of objects. -Example: -{ - "items": [ - { - "Item": "SFP Module", - "Type": "SFP", - "Description": "64G Gen7 Fibre Channel", - "Category": "Network", - "Connector": "LC", - "PartNr": "SFP-64G-G7", - "OCR": "SFP 64G GEN7 FC" - } - ] -} \ No newline at end of file + **Component Rules:** + - ``: + - **STORAGE CAPACITY - HUMAN READABLE**: Convert to largest unit (TB/MB). + - Examples: "1600GB" → "1.6TB", "256GB" → "256GB", "512MB" → "512MB" + - Rule: If ≥1000GB, use TB. If ≥1000MB, use GB. Otherwise use MB. + - **CABLE/WIRE LENGTH**: Meters only. Examples: "5m", "10m", "50m" + - **RAM DIMM**: Capacity in GB. Examples: "128GB", "32GB", "8GB" + + - ``: Asset class. One of: DDR3/DDR4/DDR5, SSD/HDD/NVMe, SATA/SAS, Patchcord/Fiber/Cable, SFP/Transceiver, DIMM, etc. + - ``: Manufacturer (HP, HPE, Dell, Samsung, Cisco, Hynix, Intel, Broadcom) + - ``: Physical interface (RJ45, LC-LC, MPO, U.3, SATA, SAS, ST, SC). Omit if N/A. + - ``: Part number ONLY if visible. **Omit serial numbers.** + + **Item Examples (WITH HUMAN-READABLE SIZES):** + - `1.6TB NVMe HPE U.3 P66093-002` (not 1600GB) + - `256GB SSD Dell SATA SK-8765` (already human-readable) + - `5m Patchcord LC-LC` + - `128GB DDR4 Hynix` + - `512MB Cache Samsung SATA` (stays MB if under 1GB) + + **Size Conversion Examples:** + - 1600GB → 1.6TB + - 2048GB → 2TB + - 512GB → 512GB (under 1TB threshold) + - 256MB → 256MB + - 1024MB → 1GB + + **Restrictions:** + - No comments in parenthesis + - No measurement units in Item field (e.g., "1.6TB" not "1.6TB Storage") + - No secondary vendors + - No diameter/mm in Item field + - ONE vendor only (primary manufacturer) + + ## Other Fields + - **Type**: Repeat the asset class (DDR4, SSD, NVMe, Patchcord, etc.) + - **Description**: Technical summary, max 5 words. Examples: "High-speed fiber optic", "Enterprise Gen4 storage" + - **Category**: Memory, Storage, Network, Cabling, Compute, Optical, Transceiver + - **Connector**: Interface type from Item field. Examples: "LC-LC", "RJ45", "U.3" + - **Size**: **HUMAN-READABLE capacity or length.** Examples: "1.6TB", "256GB", "5m" (NOT "1600GB") + - **Color**: Physical color if distinguishing + - **PartNr**: Part number only (no serial numbers) + - **OCR**: Robust matching key for OCR tolerance + + ## OCR Field Rules (CRITICAL) + Generate a SHORT, clean matching key: + - Format: **UPPERCASE space-separated, NO special chars, NO duplicates** + - Include ONLY: Type + Size + Primary Vendor + Connector + Part Number + - **EXCLUDE**: Serial numbers, secondary vendors, duplicate tokens, EMC/SK labels + - **USE HUMAN-READABLE SIZE**: Use TB/GB from Item field, not original notation + + **OCR Format:** `TYPE SIZE VENDOR CONNECTOR PARTNUMBER` + + **OCR Examples (WITH HUMAN-READABLE SIZES):** + - Item: `1.6TB NVMe HPE U.3 P66093-002` → OCR: `NVME 1.6TB HPE U3 P66093002` + - Item: `5m Patchcord LC-LC` → OCR: `PATCHCORD 5M LC LC` + - Item: `256GB SSD Samsung SAS SK-8765` → OCR: `SSD 256GB SAMSUNG SAS SK8765` + - Item: `128GB DDR4 Hynix` → OCR: `DDR4 128GB HYNIX` + + **OCR Constraints:** + - NO duplicate part numbers + - NO secondary vendor names + - NO extraneous labels + - Each token appears ONE time only + - Remove hyphens/special chars for fuzzy matching + - Use HUMAN-READABLE sizes (1.6TB not 1600GB) + + ## Output Format + ```json + { + "items": [ + { + "Item": "[size] type vendor connector partnumber", + "Type": "type", + "Description": "technical details (max 5 words)", + "Category": "category", + "Connector": "connector_type", + "Size": "human_readable_size", + "Color": "color", + "PartNr": "part_number", + "OCR": "TYPE SIZE VENDOR CONNECTOR PARTNUMBER" + } + ] + } + + Return ONLY JSON. No markdown. No text. diff --git a/frontend/public/sw.js b/frontend/public/sw.js index 8c9259b7..a7027695 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -1 +1 @@ -if(!self.define){let e,s={};const n=(n,a)=>(n=new URL(n+".js",a).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(a,c)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let t={};const r=e=>n(e,i),o={module:{uri:i},exports:t,require:r};s[i]=Promise.all(a.map(e=>o[e]||r(e))).then(e=>(c(...e),t))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"9bb385decfabce3fb60c9d7abafd855b"},{url:"/_next/static/22pTPPF9A48iSiuDzSDJQ/_buildManifest.js",revision:"31eba5c94d685fe15b2d866310094bce"},{url:"/_next/static/22pTPPF9A48iSiuDzSDJQ/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/13.ae2fd9645430ed90.js",revision:"ae2fd9645430ed90"},{url:"/_next/static/chunks/255-4f212684648fcab9.js",revision:"4f212684648fcab9"},{url:"/_next/static/chunks/374-a4ed2a0d861d9cab.js",revision:"a4ed2a0d861d9cab"},{url:"/_next/static/chunks/4bd1b696-c023c6e3521b1417.js",revision:"c023c6e3521b1417"},{url:"/_next/static/chunks/512-ce90bee899e5ddb3.js",revision:"ce90bee899e5ddb3"},{url:"/_next/static/chunks/734-c14aac0413add73b.js",revision:"c14aac0413add73b"},{url:"/_next/static/chunks/7cb1fa1f-32fc9056ac653916.js",revision:"32fc9056ac653916"},{url:"/_next/static/chunks/802-838213ff73429b14.js",revision:"838213ff73429b14"},{url:"/_next/static/chunks/814-d4585cd22c8d1c30.js",revision:"d4585cd22c8d1c30"},{url:"/_next/static/chunks/app/_not-found/page-32814a613bf1bbe9.js",revision:"32814a613bf1bbe9"},{url:"/_next/static/chunks/app/admin/page-906b007411fc012f.js",revision:"906b007411fc012f"},{url:"/_next/static/chunks/app/inventory/page-124809fba18361dd.js",revision:"124809fba18361dd"},{url:"/_next/static/chunks/app/layout-61972fdaeb813508.js",revision:"61972fdaeb813508"},{url:"/_next/static/chunks/app/login/page-02dc65ae8ac5ac4d.js",revision:"02dc65ae8ac5ac4d"},{url:"/_next/static/chunks/app/logs/page-92b68bd71d7039c5.js",revision:"92b68bd71d7039c5"},{url:"/_next/static/chunks/app/page-842dabec8cdf2abc.js",revision:"842dabec8cdf2abc"},{url:"/_next/static/chunks/framework-050c1f32293f7182.js",revision:"050c1f32293f7182"},{url:"/_next/static/chunks/main-aa573100fdde0547.js",revision:"aa573100fdde0547"},{url:"/_next/static/chunks/main-app-290572149fe5c423.js",revision:"290572149fe5c423"},{url:"/_next/static/chunks/pages/_app-7d307437aca18ad4.js",revision:"7d307437aca18ad4"},{url:"/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js",revision:"cb2a52f75f2162e2"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-7a9cf6b863b8c67d.js",revision:"7a9cf6b863b8c67d"},{url:"/_next/static/css/4cd9771bf8870987.css",revision:"4cd9771bf8870987"},{url:"/custom-logo.png",revision:"d41d8cd98f00b204e9800998ecf8427e"},{url:"/icons/icon-192x192.png",revision:"bde7fc04fd83ed65ce0e71259f4574b5"},{url:"/icons/icon-512x512.png",revision:"0c2346ba1775af7af6ec2e346ee7c72e"},{url:"/icons/maskable-icon.png",revision:"0c2346ba1775af7af6ec2e346ee7c72e"},{url:"/logo.png",revision:"084f394cfe19b7d85abec9878a797795"},{url:"/logo_TFM.png",revision:"084f394cfe19b7d85abec9878a797795"},{url:"/manifest.json",revision:"7b6e42d577201b657b72b8698d44d0b2"},{url:"/network.json",revision:"ce173a00263b4f64c8c05fe22ef7c54f"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:a})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); +if(!self.define){let e,s={};const n=(n,c)=>(n=new URL(n+".js",c).href,s[n]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=s,document.head.appendChild(e)}else e=n,importScripts(n),s()}).then(()=>{let e=s[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(c,a)=>{const t=e||("document"in self?document.currentScript.src:"")||location.href;if(s[t])return;let i={};const r=e=>n(e,t),o={module:{uri:t},exports:i,require:r};s[t]=Promise.all(c.map(e=>o[e]||r(e))).then(e=>(a(...e),i))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"b6c466c35421e37b1878b47a6a70ff03"},{url:"/_next/static/VpG4VvMW2CrULWLn70yvt/_buildManifest.js",revision:"31eba5c94d685fe15b2d866310094bce"},{url:"/_next/static/VpG4VvMW2CrULWLn70yvt/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/13.ae2fd9645430ed90.js",revision:"ae2fd9645430ed90"},{url:"/_next/static/chunks/255-4f212684648fcab9.js",revision:"4f212684648fcab9"},{url:"/_next/static/chunks/374-a4ed2a0d861d9cab.js",revision:"a4ed2a0d861d9cab"},{url:"/_next/static/chunks/4bd1b696-c023c6e3521b1417.js",revision:"c023c6e3521b1417"},{url:"/_next/static/chunks/512-ce90bee899e5ddb3.js",revision:"ce90bee899e5ddb3"},{url:"/_next/static/chunks/734-16b5778270982039.js",revision:"16b5778270982039"},{url:"/_next/static/chunks/7cb1fa1f-32fc9056ac653916.js",revision:"32fc9056ac653916"},{url:"/_next/static/chunks/802-838213ff73429b14.js",revision:"838213ff73429b14"},{url:"/_next/static/chunks/814-d4585cd22c8d1c30.js",revision:"d4585cd22c8d1c30"},{url:"/_next/static/chunks/app/_not-found/page-32814a613bf1bbe9.js",revision:"32814a613bf1bbe9"},{url:"/_next/static/chunks/app/admin/page-906b007411fc012f.js",revision:"906b007411fc012f"},{url:"/_next/static/chunks/app/inventory/page-124809fba18361dd.js",revision:"124809fba18361dd"},{url:"/_next/static/chunks/app/layout-61972fdaeb813508.js",revision:"61972fdaeb813508"},{url:"/_next/static/chunks/app/login/page-02dc65ae8ac5ac4d.js",revision:"02dc65ae8ac5ac4d"},{url:"/_next/static/chunks/app/logs/page-92b68bd71d7039c5.js",revision:"92b68bd71d7039c5"},{url:"/_next/static/chunks/app/page-0bd8ce0e0fcb0ced.js",revision:"0bd8ce0e0fcb0ced"},{url:"/_next/static/chunks/framework-050c1f32293f7182.js",revision:"050c1f32293f7182"},{url:"/_next/static/chunks/main-aa573100fdde0547.js",revision:"aa573100fdde0547"},{url:"/_next/static/chunks/main-app-290572149fe5c423.js",revision:"290572149fe5c423"},{url:"/_next/static/chunks/pages/_app-7d307437aca18ad4.js",revision:"7d307437aca18ad4"},{url:"/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js",revision:"cb2a52f75f2162e2"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-7a9cf6b863b8c67d.js",revision:"7a9cf6b863b8c67d"},{url:"/_next/static/css/eef0fc49b3852142.css",revision:"eef0fc49b3852142"},{url:"/custom-logo.png",revision:"d41d8cd98f00b204e9800998ecf8427e"},{url:"/icons/icon-192x192.png",revision:"bde7fc04fd83ed65ce0e71259f4574b5"},{url:"/icons/icon-512x512.png",revision:"0c2346ba1775af7af6ec2e346ee7c72e"},{url:"/icons/maskable-icon.png",revision:"0c2346ba1775af7af6ec2e346ee7c72e"},{url:"/logo.png",revision:"084f394cfe19b7d85abec9878a797795"},{url:"/logo_TFM.png",revision:"084f394cfe19b7d85abec9878a797795"},{url:"/manifest.json",revision:"7b6e42d577201b657b72b8698d44d0b2"},{url:"/network.json",revision:"ce173a00263b4f64c8c05fe22ef7c54f"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:n,state:c})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")});