@remnic/core 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/access-cli.d.ts +13 -3
- package/dist/access-cli.js +90 -75
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +10 -3
- package/dist/access-http.js +25 -18
- package/dist/access-mcp.d.ts +30 -3
- package/dist/access-mcp.js +16 -1
- package/dist/access-schema.d.ts +46 -46
- package/dist/access-schema.js +1 -1
- package/dist/access-service.d.ts +65 -4
- package/dist/access-service.js +21 -15
- package/dist/active-memory-bridge.d.ts +66 -0
- package/dist/active-memory-bridge.js +11 -0
- package/dist/active-recall.d.ts +96 -0
- package/dist/active-recall.js +308 -0
- package/dist/active-recall.js.map +1 -0
- package/dist/behavior-learner.js +1 -1
- package/dist/bootstrap.d.ts +6 -3
- package/dist/bootstrap.js +2 -2
- package/dist/boxes.js +2 -2
- package/dist/briefing.d.ts +169 -0
- package/dist/briefing.js +52 -0
- package/dist/briefing.js.map +1 -0
- package/dist/buffer.d.ts +19 -5
- package/dist/buffer.js +2 -2
- package/dist/calibration.js +6 -6
- package/dist/causal-behavior.js +5 -5
- package/dist/causal-chain.js +3 -3
- package/dist/causal-consolidation.d.ts +22 -2
- package/dist/causal-consolidation.js +36 -9
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +6 -6
- package/dist/causal-trajectory-graph.js +1 -1
- package/dist/causal-trajectory.d.ts +14 -1
- package/dist/causal-trajectory.js +5 -1
- package/dist/{chunk-KWBU5S5U.js → chunk-2ODBA7MQ.js} +9 -3
- package/dist/chunk-2ODBA7MQ.js.map +1 -0
- package/dist/{chunk-6UJQNRIO.js → chunk-2VFW5K5U.js} +93 -36
- package/dist/chunk-2VFW5K5U.js.map +1 -0
- package/dist/chunk-3PG3H5TD.js +7 -0
- package/dist/chunk-3PG3H5TD.js.map +1 -0
- package/dist/{chunk-NTTLPF7F.js → chunk-3QFQGRHO.js} +5 -5
- package/dist/chunk-4DJQYKMN.js +187 -0
- package/dist/chunk-4DJQYKMN.js.map +1 -0
- package/dist/chunk-4KAN3GZ3.js +225 -0
- package/dist/chunk-4KAN3GZ3.js.map +1 -0
- package/dist/{chunk-ORZMT74A.js → chunk-4NRAJUDS.js} +11 -1
- package/dist/chunk-4NRAJUDS.js.map +1 -0
- package/dist/{chunk-B7LOFDVE.js → chunk-4WMCPJWX.js} +8 -3
- package/dist/chunk-4WMCPJWX.js.map +1 -0
- package/dist/{chunk-G3AG3KZN.js → chunk-5IZL4DCV.js} +2 -2
- package/dist/{chunk-BRK4ODMI.js → chunk-5NPGSAVB.js} +2 -2
- package/dist/chunk-6MKAMLQL.js +16 -0
- package/dist/chunk-6MKAMLQL.js.map +1 -0
- package/dist/{chunk-ESSMF2FR.js → chunk-6PFRXT4K.js} +15 -6
- package/dist/chunk-6PFRXT4K.js.map +1 -0
- package/dist/chunk-6ZH4TU6I.js +245 -0
- package/dist/chunk-6ZH4TU6I.js.map +1 -0
- package/dist/{chunk-V4YC4LUK.js → chunk-74JR4N5J.js} +175 -63
- package/dist/chunk-74JR4N5J.js.map +1 -0
- package/dist/{chunk-L5RPWGFK.js → chunk-7DHTMOND.js} +2 -2
- package/dist/{chunk-TVVVQQAK.js → chunk-7PA4OZEU.js} +53 -11
- package/dist/chunk-7PA4OZEU.js.map +1 -0
- package/dist/{chunk-Q6FETXJA.js → chunk-7SEAZFFB.js} +2 -2
- package/dist/chunk-ALXMCZEU.js +332 -0
- package/dist/chunk-ALXMCZEU.js.map +1 -0
- package/dist/{chunk-QANCTXQF.js → chunk-AYPYCLR7.js} +3 -3
- package/dist/{chunk-WWIQTB2Y.js → chunk-BKQJBXXX.js} +9 -2
- package/dist/chunk-BKQJBXXX.js.map +1 -0
- package/dist/{chunk-LP47L3ZX.js → chunk-BTY5RRRF.js} +7 -7
- package/dist/{chunk-SCHEKPYH.js → chunk-C2EFFULQ.js} +1 -1
- package/dist/{chunk-GJR6D6KC.js → chunk-D654IBA6.js} +2 -2
- package/dist/{chunk-UV2FO7J4.js → chunk-E6K4NIEU.js} +2 -2
- package/dist/{chunk-T4WRIV2C.js → chunk-EABGC2TL.js} +2 -2
- package/dist/chunk-ECKDIK5F.js +813 -0
- package/dist/chunk-ECKDIK5F.js.map +1 -0
- package/dist/chunk-EJI5XIBB.js +232 -0
- package/dist/chunk-EJI5XIBB.js.map +1 -0
- package/dist/{chunk-ONRU4L2N.js → chunk-FEMOX5AD.js} +2 -2
- package/dist/{chunk-IFFFR3MR.js → chunk-FSFEQI74.js} +3 -3
- package/dist/chunk-G4SK7DSQ.js +121 -0
- package/dist/chunk-G4SK7DSQ.js.map +1 -0
- package/dist/{chunk-UIYZ5T3I.js → chunk-GJQPH5G3.js} +8 -8
- package/dist/{chunk-2PO5ZRKV.js → chunk-GZCUW5IC.js} +16 -3
- package/dist/chunk-GZCUW5IC.js.map +1 -0
- package/dist/{chunk-IZME7KW2.js → chunk-HITJFT7E.js} +24 -10
- package/dist/{chunk-IZME7KW2.js.map → chunk-HITJFT7E.js.map} +1 -1
- package/dist/chunk-IQT3XTKW.js +121 -0
- package/dist/chunk-IQT3XTKW.js.map +1 -0
- package/dist/{chunk-BDFZXRSO.js → chunk-J4IYOZZ5.js} +15 -2
- package/dist/chunk-J4IYOZZ5.js.map +1 -0
- package/dist/{chunk-ZKYI7UVO.js → chunk-JR4ZC3G4.js} +2 -2
- package/dist/{chunk-UCYSTFZR.js → chunk-JRNQ3RNA.js} +2 -2
- package/dist/{chunk-UYSKNO6E.js → chunk-JROGC36Y.js} +15 -4
- package/dist/chunk-JROGC36Y.js.map +1 -0
- package/dist/{chunk-GPGBSNKM.js → chunk-K4FLSOR5.js} +2 -2
- package/dist/{chunk-M5ZBBBJI.js → chunk-KEG4GNGI.js} +2 -2
- package/dist/chunk-KVE7R4CG.js +320 -0
- package/dist/chunk-KVE7R4CG.js.map +1 -0
- package/dist/{chunk-L7WO3MZ4.js → chunk-KWP7T3DP.js} +2 -2
- package/dist/chunk-LAYN4LDC.js +267 -0
- package/dist/chunk-LAYN4LDC.js.map +1 -0
- package/dist/{chunk-PGK3VUHN.js → chunk-MTLYEMJB.js} +3 -2
- package/dist/chunk-MTLYEMJB.js.map +1 -0
- package/dist/{chunk-J47FNDR7.js → chunk-MYQWXITD.js} +7 -7
- package/dist/{chunk-YNI4S5WT.js → chunk-N53K2EXC.js} +2 -2
- package/dist/{chunk-763GUIOU.js → chunk-NBNN5GOB.js} +2 -2
- package/dist/{chunk-CXWFUJR2.js → chunk-NSB3WSYS.js} +125 -6
- package/dist/chunk-NSB3WSYS.js.map +1 -0
- package/dist/{chunk-KL4CP4SB.js → chunk-O5ETUNBT.js} +17 -5
- package/dist/chunk-O5ETUNBT.js.map +1 -0
- package/dist/{chunk-OOSWAUYB.js → chunk-ODWDQNRE.js} +2 -2
- package/dist/{chunk-ISY75RLM.js → chunk-OJFGVJS6.js} +288 -7
- package/dist/chunk-OJFGVJS6.js.map +1 -0
- package/dist/{chunk-HLBYLYRD.js → chunk-PAORGQRI.js} +70 -13
- package/dist/chunk-PAORGQRI.js.map +1 -0
- package/dist/{chunk-ZJLY4QSU.js → chunk-PMB3WGDL.js} +69 -6
- package/dist/chunk-PMB3WGDL.js.map +1 -0
- package/dist/{chunk-J3BT33K7.js → chunk-POBPGDWI.js} +5 -5
- package/dist/{chunk-QWUUMMIK.js → chunk-POMSFKTB.js} +1351 -76
- package/dist/chunk-POMSFKTB.js.map +1 -0
- package/dist/{chunk-OTAVQCSF.js → chunk-PYXS46O7.js} +2 -2
- package/dist/chunk-QDW3E4RD.js +108 -0
- package/dist/chunk-QDW3E4RD.js.map +1 -0
- package/dist/{chunk-YNCQ7E4M.js → chunk-QDYXG4CS.js} +4 -3
- package/dist/chunk-QDYXG4CS.js.map +1 -0
- package/dist/{chunk-XUHI52HK.js → chunk-QKAH5B6E.js} +4 -4
- package/dist/{chunk-HLXVTBF3.js → chunk-QNJMBKFK.js} +3 -2
- package/dist/chunk-QNJMBKFK.js.map +1 -0
- package/dist/chunk-RCICHSHL.js +789 -0
- package/dist/chunk-RCICHSHL.js.map +1 -0
- package/dist/{chunk-HG2NKWR2.js → chunk-S4LX5EBI.js} +2 -2
- package/dist/{chunk-4A24LIM2.js → chunk-S75M5ZRK.js} +2 -2
- package/dist/{chunk-QCCCQT3O.js → chunk-TBBDFYXW.js} +2 -2
- package/dist/chunk-TBBDFYXW.js.map +1 -0
- package/dist/{chunk-U4PV25RD.js → chunk-U2IQTSBY.js} +1 -1
- package/dist/chunk-U2IQTSBY.js.map +1 -0
- package/dist/chunk-U66YHYC7.js +31 -0
- package/dist/chunk-U66YHYC7.js.map +1 -0
- package/dist/{chunk-MWGVGUIS.js → chunk-UEYA6UC7.js} +36 -4
- package/dist/chunk-UEYA6UC7.js.map +1 -0
- package/dist/{chunk-MDDAA2AO.js → chunk-UPMD5XND.js} +2 -2
- package/dist/{chunk-M5KEYE5E.js → chunk-URB2WSKZ.js} +2 -2
- package/dist/chunk-UVJFDP7P.js +202 -0
- package/dist/chunk-UVJFDP7P.js.map +1 -0
- package/dist/{chunk-QY2BHY5O.js → chunk-V7XCAHIB.js} +265 -25
- package/dist/chunk-V7XCAHIB.js.map +1 -0
- package/dist/chunk-W6SL7OFG.js +180 -0
- package/dist/chunk-W6SL7OFG.js.map +1 -0
- package/dist/{chunk-QDOSNLB4.js → chunk-X4WESCKA.js} +17 -15
- package/dist/chunk-X4WESCKA.js.map +1 -0
- package/dist/{chunk-OTFNI3OO.js → chunk-XMGSSBFX.js} +1738 -383
- package/dist/chunk-XMGSSBFX.js.map +1 -0
- package/dist/chunk-YDBIWGNI.js +298 -0
- package/dist/chunk-YDBIWGNI.js.map +1 -0
- package/dist/chunk-YFYL2SIJ.js +7857 -0
- package/dist/chunk-YFYL2SIJ.js.map +1 -0
- package/dist/chunking.js +1 -1
- package/dist/citations.d.ts +67 -0
- package/dist/citations.js +13 -0
- package/dist/citations.js.map +1 -0
- package/dist/cli-DwIBnp2g.d.ts +1240 -0
- package/dist/cli.d.ts +31 -1147
- package/dist/cli.js +149 -7092
- package/dist/cli.js.map +1 -1
- package/dist/codex-materialize-CQlLTzke.d.ts +139 -0
- package/dist/codex-thread-key.d.ts +3 -0
- package/dist/codex-thread-key.js +7 -0
- package/dist/codex-thread-key.js.map +1 -0
- package/dist/config.js +3 -2
- package/dist/connectors/codex/instructions.md +160 -0
- package/dist/connectors/codex/resources/namespace-cheatsheet.md +48 -0
- package/dist/day-summary.d.ts +7 -2
- package/dist/day-summary.js +5 -2
- package/dist/embedding-fallback.d.ts +96 -2
- package/dist/embedding-fallback.js +6 -4
- package/dist/{engine-2A6J4XEX.js → engine-X7X3AAG3.js} +10 -7
- package/dist/engine-X7X3AAG3.js.map +1 -0
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +10 -7
- package/dist/entity-schema.d.ts +11 -0
- package/dist/entity-schema.js +19 -0
- package/dist/entity-schema.js.map +1 -0
- package/dist/explicit-capture.d.ts +6 -3
- package/dist/explicit-capture.js +2 -2
- package/dist/extraction-judge.d.ts +66 -0
- package/dist/extraction-judge.js +18 -0
- package/dist/extraction-judge.js.map +1 -0
- package/dist/extraction.d.ts +1 -0
- package/dist/extraction.js +12 -10
- package/dist/fallback-llm.js +4 -4
- package/dist/graph.js +1 -1
- package/dist/importance.d.ts +11 -1
- package/dist/importance.js +3 -1
- package/dist/index.d.ts +1140 -8
- package/dist/index.js +3350 -333
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +2 -1
- package/dist/intent.js +3 -1
- package/dist/lifecycle.js +1 -1
- package/dist/local-llm.js +2 -2
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +1 -1
- package/dist/memory-cache.d.ts +2 -2
- package/dist/memory-cache.js +1 -1
- package/dist/{memory-projection-store-NxMkbocT.d.ts → memory-projection-store-DeSXPh1j.d.ts} +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/model-registry.js +2 -2
- package/dist/models-json.js +2 -2
- package/dist/native-knowledge.js +2 -2
- package/dist/negative.js +2 -2
- package/dist/operator-toolkit.js +20 -16
- package/dist/{orchestrator-CIvLFHx3.d.ts → orchestrator-B9kwlCep.d.ts} +254 -9
- package/dist/orchestrator.d.ts +6 -3
- package/dist/orchestrator.js +70 -58
- package/dist/page-versioning.d.ts +77 -0
- package/dist/page-versioning.js +15 -0
- package/dist/page-versioning.js.map +1 -0
- package/dist/plugin-id.d.ts +37 -0
- package/dist/plugin-id.js +11 -0
- package/dist/plugin-id.js.map +1 -0
- package/dist/policy-runtime.js +2 -2
- package/dist/profiling.js +2 -2
- package/dist/qmd.d.ts +5 -2
- package/dist/qmd.js +3 -3
- package/dist/recall-audit.d.ts +20 -0
- package/dist/recall-audit.js +50 -0
- package/dist/recall-audit.js.map +1 -0
- package/dist/recall-mmr.d.ts +152 -0
- package/dist/recall-mmr.js +17 -0
- package/dist/recall-mmr.js.map +1 -0
- package/dist/recall-qos.js +2 -2
- package/dist/recall-state.js +2 -2
- package/dist/relevance.js +2 -2
- package/dist/resolve-provider-secret.js +2 -2
- package/dist/resume-bundles.js +5 -4
- package/dist/retrieval-agents.js +2 -2
- package/dist/retrieval.js +2 -2
- package/dist/schemas.d.ts +422 -64
- package/dist/schemas.js +3 -1
- package/dist/sdk-compat.d.ts +2 -0
- package/dist/sdk-compat.js +6 -3
- package/dist/sdk-compat.js.map +1 -1
- package/dist/semantic-chunking.d.ts +87 -0
- package/dist/semantic-chunking.js +20 -0
- package/dist/semantic-chunking.js.map +1 -0
- package/dist/semantic-consolidation-DrvSYRdB.d.ts +119 -0
- package/dist/semantic-consolidation.d.ts +4 -42
- package/dist/semantic-consolidation.js +23 -2
- package/dist/semantic-rule-promotion.js +9 -6
- package/dist/semantic-rule-verifier.js +10 -7
- package/dist/session-observer-state.js +2 -2
- package/dist/session-toggles.d.ts +22 -0
- package/dist/session-toggles.js +116 -0
- package/dist/session-toggles.js.map +1 -0
- package/dist/skills-registry.d.ts +47 -0
- package/dist/skills-registry.js +48 -0
- package/dist/skills-registry.js.map +1 -0
- package/dist/source-attribution.d.ts +169 -0
- package/dist/source-attribution.js +27 -0
- package/dist/source-attribution.js.map +1 -0
- package/dist/storage.d.ts +171 -10
- package/dist/storage.js +16 -5
- package/dist/summarizer.js +7 -7
- package/dist/temporal-supersession.d.ts +127 -0
- package/dist/temporal-supersession.js +20 -0
- package/dist/temporal-supersession.js.map +1 -0
- package/dist/threading.js +2 -2
- package/dist/tier-migration.d.ts +2 -1
- package/dist/tier-routing.js +2 -2
- package/dist/tokens.d.ts +21 -1
- package/dist/tokens.js +5 -1
- package/dist/transcript.js +2 -2
- package/dist/types.d.ts +497 -3
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +2 -2
- package/dist/utility-runtime.js +3 -3
- package/dist/verified-recall.js +11 -8
- package/dist/whitespace.d.ts +4 -0
- package/dist/whitespace.js +9 -0
- package/dist/whitespace.js.map +1 -0
- package/package.json +14 -8
- package/dist/chunk-2CJCWDMR.js +0 -87
- package/dist/chunk-2CJCWDMR.js.map +0 -1
- package/dist/chunk-2PO5ZRKV.js.map +0 -1
- package/dist/chunk-6UJQNRIO.js.map +0 -1
- package/dist/chunk-B7LOFDVE.js.map +0 -1
- package/dist/chunk-BDFZXRSO.js.map +0 -1
- package/dist/chunk-CXWFUJR2.js.map +0 -1
- package/dist/chunk-DORBM6OB.js +0 -81
- package/dist/chunk-DORBM6OB.js.map +0 -1
- package/dist/chunk-ESSMF2FR.js.map +0 -1
- package/dist/chunk-HLBYLYRD.js.map +0 -1
- package/dist/chunk-HLXVTBF3.js.map +0 -1
- package/dist/chunk-ISY75RLM.js.map +0 -1
- package/dist/chunk-KL4CP4SB.js.map +0 -1
- package/dist/chunk-KWBU5S5U.js.map +0 -1
- package/dist/chunk-MWGVGUIS.js.map +0 -1
- package/dist/chunk-ORZMT74A.js.map +0 -1
- package/dist/chunk-OTFNI3OO.js.map +0 -1
- package/dist/chunk-PGK3VUHN.js.map +0 -1
- package/dist/chunk-QCCCQT3O.js.map +0 -1
- package/dist/chunk-QDOSNLB4.js.map +0 -1
- package/dist/chunk-QPKFPHOO.js +0 -178
- package/dist/chunk-QPKFPHOO.js.map +0 -1
- package/dist/chunk-QWUUMMIK.js.map +0 -1
- package/dist/chunk-QY2BHY5O.js.map +0 -1
- package/dist/chunk-TVVVQQAK.js.map +0 -1
- package/dist/chunk-U4PV25RD.js.map +0 -1
- package/dist/chunk-UYSKNO6E.js.map +0 -1
- package/dist/chunk-V4YC4LUK.js.map +0 -1
- package/dist/chunk-WWIQTB2Y.js.map +0 -1
- package/dist/chunk-YNCQ7E4M.js.map +0 -1
- package/dist/chunk-ZJLY4QSU.js.map +0 -1
- /package/dist/{engine-2A6J4XEX.js.map → active-memory-bridge.js.map} +0 -0
- /package/dist/{chunk-NTTLPF7F.js.map → chunk-3QFQGRHO.js.map} +0 -0
- /package/dist/{chunk-G3AG3KZN.js.map → chunk-5IZL4DCV.js.map} +0 -0
- /package/dist/{chunk-BRK4ODMI.js.map → chunk-5NPGSAVB.js.map} +0 -0
- /package/dist/{chunk-L5RPWGFK.js.map → chunk-7DHTMOND.js.map} +0 -0
- /package/dist/{chunk-Q6FETXJA.js.map → chunk-7SEAZFFB.js.map} +0 -0
- /package/dist/{chunk-QANCTXQF.js.map → chunk-AYPYCLR7.js.map} +0 -0
- /package/dist/{chunk-LP47L3ZX.js.map → chunk-BTY5RRRF.js.map} +0 -0
- /package/dist/{chunk-SCHEKPYH.js.map → chunk-C2EFFULQ.js.map} +0 -0
- /package/dist/{chunk-GJR6D6KC.js.map → chunk-D654IBA6.js.map} +0 -0
- /package/dist/{chunk-UV2FO7J4.js.map → chunk-E6K4NIEU.js.map} +0 -0
- /package/dist/{chunk-T4WRIV2C.js.map → chunk-EABGC2TL.js.map} +0 -0
- /package/dist/{chunk-ONRU4L2N.js.map → chunk-FEMOX5AD.js.map} +0 -0
- /package/dist/{chunk-IFFFR3MR.js.map → chunk-FSFEQI74.js.map} +0 -0
- /package/dist/{chunk-UIYZ5T3I.js.map → chunk-GJQPH5G3.js.map} +0 -0
- /package/dist/{chunk-ZKYI7UVO.js.map → chunk-JR4ZC3G4.js.map} +0 -0
- /package/dist/{chunk-UCYSTFZR.js.map → chunk-JRNQ3RNA.js.map} +0 -0
- /package/dist/{chunk-GPGBSNKM.js.map → chunk-K4FLSOR5.js.map} +0 -0
- /package/dist/{chunk-M5ZBBBJI.js.map → chunk-KEG4GNGI.js.map} +0 -0
- /package/dist/{chunk-L7WO3MZ4.js.map → chunk-KWP7T3DP.js.map} +0 -0
- /package/dist/{chunk-J47FNDR7.js.map → chunk-MYQWXITD.js.map} +0 -0
- /package/dist/{chunk-YNI4S5WT.js.map → chunk-N53K2EXC.js.map} +0 -0
- /package/dist/{chunk-763GUIOU.js.map → chunk-NBNN5GOB.js.map} +0 -0
- /package/dist/{chunk-OOSWAUYB.js.map → chunk-ODWDQNRE.js.map} +0 -0
- /package/dist/{chunk-J3BT33K7.js.map → chunk-POBPGDWI.js.map} +0 -0
- /package/dist/{chunk-OTAVQCSF.js.map → chunk-PYXS46O7.js.map} +0 -0
- /package/dist/{chunk-XUHI52HK.js.map → chunk-QKAH5B6E.js.map} +0 -0
- /package/dist/{chunk-HG2NKWR2.js.map → chunk-S4LX5EBI.js.map} +0 -0
- /package/dist/{chunk-4A24LIM2.js.map → chunk-S75M5ZRK.js.map} +0 -0
- /package/dist/{chunk-MDDAA2AO.js.map → chunk-UPMD5XND.js.map} +0 -0
- /package/dist/{chunk-M5KEYE5E.js.map → chunk-URB2WSKZ.js.map} +0 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// src/recall-mmr.ts
|
|
2
|
+
var DEFAULT_LAMBDA = 0.7;
|
|
3
|
+
var DEFAULT_TOP_N = 40;
|
|
4
|
+
var DEFAULT_DIVERSITY_SAMPLE_SIZE = 10;
|
|
5
|
+
function applyMmrToCandidates(opts) {
|
|
6
|
+
const candidates = Array.isArray(opts.candidates) ? opts.candidates : [];
|
|
7
|
+
if (candidates.length === 0) return [];
|
|
8
|
+
const lambda = clampLambda(opts.lambda);
|
|
9
|
+
const topN = clampPositiveInt(opts.topN, DEFAULT_TOP_N);
|
|
10
|
+
const budget = clampPositiveInt(opts.budget, candidates.length);
|
|
11
|
+
if (budget <= 0) return [];
|
|
12
|
+
if (candidates.length === 1) return [candidates[0]];
|
|
13
|
+
const pool = candidates.slice(0, topN);
|
|
14
|
+
const tail = candidates.slice(topN);
|
|
15
|
+
const relevance = computeRelevanceScores(pool, opts.queryEmbedding);
|
|
16
|
+
const pairSim = /* @__PURE__ */ new Map();
|
|
17
|
+
const pairKey = (i, j) => i < j ? `${i}:${j}` : `${j}:${i}`;
|
|
18
|
+
const sim = (i, j) => {
|
|
19
|
+
if (i === j) return 1;
|
|
20
|
+
const key = pairKey(i, j);
|
|
21
|
+
const cached = pairSim.get(key);
|
|
22
|
+
if (cached !== void 0) return cached;
|
|
23
|
+
const a = pool[i];
|
|
24
|
+
const b = pool[j];
|
|
25
|
+
const s = similarity(a, b);
|
|
26
|
+
pairSim.set(key, s);
|
|
27
|
+
return s;
|
|
28
|
+
};
|
|
29
|
+
const selectedIdx = [];
|
|
30
|
+
const remaining = /* @__PURE__ */ new Set();
|
|
31
|
+
for (let i = 0; i < pool.length; i += 1) remaining.add(i);
|
|
32
|
+
while (selectedIdx.length < budget && remaining.size > 0) {
|
|
33
|
+
let bestIdx = -1;
|
|
34
|
+
let bestMmr = Number.NEGATIVE_INFINITY;
|
|
35
|
+
for (const idx of remaining) {
|
|
36
|
+
const rel = relevance[idx] ?? 0;
|
|
37
|
+
let maxSimToSelected = 0;
|
|
38
|
+
if (selectedIdx.length > 0) {
|
|
39
|
+
for (const s of selectedIdx) {
|
|
40
|
+
const pairwise = sim(idx, s);
|
|
41
|
+
if (pairwise > maxSimToSelected) maxSimToSelected = pairwise;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const mmr = lambda * rel - (1 - lambda) * maxSimToSelected;
|
|
45
|
+
if (mmr > bestMmr || // Stable tie-breaker: prefer the earlier original position.
|
|
46
|
+
mmr === bestMmr && (bestIdx < 0 || idx < bestIdx)) {
|
|
47
|
+
bestMmr = mmr;
|
|
48
|
+
bestIdx = idx;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (bestIdx < 0) break;
|
|
52
|
+
selectedIdx.push(bestIdx);
|
|
53
|
+
remaining.delete(bestIdx);
|
|
54
|
+
}
|
|
55
|
+
const selected = selectedIdx.map((i) => pool[i]);
|
|
56
|
+
if (selected.length < budget && tail.length > 0) {
|
|
57
|
+
for (const c of tail) {
|
|
58
|
+
if (selected.length >= budget) break;
|
|
59
|
+
selected.push(c);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return selected;
|
|
63
|
+
}
|
|
64
|
+
function summarizeMmrDiversity(before, after, sampleSize = DEFAULT_DIVERSITY_SAMPLE_SIZE) {
|
|
65
|
+
const n = clampPositiveInt(sampleSize, DEFAULT_DIVERSITY_SAMPLE_SIZE);
|
|
66
|
+
const beforeSlice = before.slice(0, n);
|
|
67
|
+
const afterSlice = after.slice(0, n);
|
|
68
|
+
let headReorderCount = 0;
|
|
69
|
+
const compareLength = Math.min(beforeSlice.length, afterSlice.length);
|
|
70
|
+
for (let i = 0; i < compareLength; i += 1) {
|
|
71
|
+
if (beforeSlice[i].id !== afterSlice[i].id) headReorderCount += 1;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
considered: before.length,
|
|
75
|
+
kept: after.length,
|
|
76
|
+
headReorderCount,
|
|
77
|
+
avgPairwiseSimBefore: averagePairwiseSimilarity(beforeSlice),
|
|
78
|
+
avgPairwiseSimAfter: averagePairwiseSimilarity(afterSlice)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function clampLambda(value) {
|
|
82
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_LAMBDA;
|
|
83
|
+
if (value < 0) return 0;
|
|
84
|
+
if (value > 1) return 1;
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
function clampPositiveInt(value, fallback) {
|
|
88
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
89
|
+
if (value <= 0) return 0;
|
|
90
|
+
return Math.floor(value);
|
|
91
|
+
}
|
|
92
|
+
function computeRelevanceScores(pool, queryEmbedding) {
|
|
93
|
+
const canUseQueryEmbedding = Array.isArray(queryEmbedding) && queryEmbedding.length > 0;
|
|
94
|
+
if (canUseQueryEmbedding) {
|
|
95
|
+
const scores = [];
|
|
96
|
+
for (const c of pool) {
|
|
97
|
+
if (Array.isArray(c.embedding) && c.embedding.length > 0) {
|
|
98
|
+
scores.push(cosineSimilarity(queryEmbedding, c.embedding));
|
|
99
|
+
} else {
|
|
100
|
+
scores.push(normalizeFinite(c.score));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return normalizeVector(scores);
|
|
104
|
+
}
|
|
105
|
+
const raw = pool.map((c) => normalizeFinite(c.score));
|
|
106
|
+
return normalizeVector(raw);
|
|
107
|
+
}
|
|
108
|
+
function similarity(a, b) {
|
|
109
|
+
if (Array.isArray(a.embedding) && a.embedding.length > 0 && Array.isArray(b.embedding) && b.embedding.length > 0 && a.embedding.length === b.embedding.length) {
|
|
110
|
+
return cosineSimilarity(a.embedding, b.embedding);
|
|
111
|
+
}
|
|
112
|
+
return jaccardSimilarity(a.content ?? "", b.content ?? "");
|
|
113
|
+
}
|
|
114
|
+
function cosineSimilarity(a, b) {
|
|
115
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
116
|
+
const len = Math.min(a.length, b.length);
|
|
117
|
+
let dot = 0;
|
|
118
|
+
let na = 0;
|
|
119
|
+
let nb = 0;
|
|
120
|
+
for (let i = 0; i < len; i += 1) {
|
|
121
|
+
const av = a[i] ?? 0;
|
|
122
|
+
const bv = b[i] ?? 0;
|
|
123
|
+
dot += av * bv;
|
|
124
|
+
na += av * av;
|
|
125
|
+
nb += bv * bv;
|
|
126
|
+
}
|
|
127
|
+
if (na === 0 || nb === 0) return 0;
|
|
128
|
+
const cos = dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
129
|
+
if (cos < 0) return 0;
|
|
130
|
+
if (cos > 1) return 1;
|
|
131
|
+
return cos;
|
|
132
|
+
}
|
|
133
|
+
function normalizeTokens(text) {
|
|
134
|
+
if (!text) return /* @__PURE__ */ new Set();
|
|
135
|
+
const cleaned = text.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
|
|
136
|
+
if (cleaned.length === 0) {
|
|
137
|
+
const chars = /* @__PURE__ */ new Set();
|
|
138
|
+
for (const ch of text.toLowerCase()) {
|
|
139
|
+
if (/\s/.test(ch)) continue;
|
|
140
|
+
chars.add(ch);
|
|
141
|
+
}
|
|
142
|
+
return chars;
|
|
143
|
+
}
|
|
144
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
145
|
+
for (const token of cleaned.split(/\s+/)) {
|
|
146
|
+
if (!token) continue;
|
|
147
|
+
if (token.length >= 2 && hasUnsegmentableScript(token)) {
|
|
148
|
+
for (const ch of token) tokens.add(ch);
|
|
149
|
+
} else {
|
|
150
|
+
tokens.add(token);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return tokens;
|
|
154
|
+
}
|
|
155
|
+
function hasUnsegmentableScript(token) {
|
|
156
|
+
return /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u.test(
|
|
157
|
+
token
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function jaccardSimilarity(a, b) {
|
|
161
|
+
const ta = normalizeTokens(a);
|
|
162
|
+
const tb = normalizeTokens(b);
|
|
163
|
+
if (ta.size === 0 && tb.size === 0) return 0;
|
|
164
|
+
let intersection = 0;
|
|
165
|
+
for (const t of ta) if (tb.has(t)) intersection += 1;
|
|
166
|
+
const union = ta.size + tb.size - intersection;
|
|
167
|
+
if (union === 0) return 0;
|
|
168
|
+
return intersection / union;
|
|
169
|
+
}
|
|
170
|
+
function normalizeFinite(value) {
|
|
171
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
function normalizeVector(values) {
|
|
175
|
+
if (values.length === 0) return values;
|
|
176
|
+
let max = 0;
|
|
177
|
+
let min = Number.POSITIVE_INFINITY;
|
|
178
|
+
for (const v of values) {
|
|
179
|
+
if (!Number.isFinite(v)) continue;
|
|
180
|
+
const a = Math.abs(v);
|
|
181
|
+
if (a > max) max = a;
|
|
182
|
+
if (v < min) min = v;
|
|
183
|
+
}
|
|
184
|
+
if (!Number.isFinite(min)) {
|
|
185
|
+
return values.map(() => 0);
|
|
186
|
+
}
|
|
187
|
+
if (max === 0) {
|
|
188
|
+
return values.map(() => 1);
|
|
189
|
+
}
|
|
190
|
+
if (min >= 0 && max <= 1) {
|
|
191
|
+
return values.map(
|
|
192
|
+
(v) => Number.isFinite(v) ? v < 0 ? 0 : v > 1 ? 1 : v : 0
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return values.map((v) => {
|
|
196
|
+
if (!Number.isFinite(v)) return 0;
|
|
197
|
+
const scaled = v / max;
|
|
198
|
+
if (scaled < 0) return 0;
|
|
199
|
+
if (scaled > 1) return 1;
|
|
200
|
+
return scaled;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function averagePairwiseSimilarity(candidates) {
|
|
204
|
+
if (candidates.length < 2) return 0;
|
|
205
|
+
let sum = 0;
|
|
206
|
+
let count = 0;
|
|
207
|
+
for (let i = 0; i < candidates.length; i += 1) {
|
|
208
|
+
for (let j = i + 1; j < candidates.length; j += 1) {
|
|
209
|
+
sum += similarity(candidates[i], candidates[j]);
|
|
210
|
+
count += 1;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (count === 0) return 0;
|
|
214
|
+
return sum / count;
|
|
215
|
+
}
|
|
216
|
+
function reorderRecallResultsWithMmr(results, options = {}) {
|
|
217
|
+
const emptyReport = {
|
|
218
|
+
considered: 0,
|
|
219
|
+
kept: 0,
|
|
220
|
+
headReorderCount: 0,
|
|
221
|
+
avgPairwiseSimBefore: 0,
|
|
222
|
+
avgPairwiseSimAfter: 0
|
|
223
|
+
};
|
|
224
|
+
const lambda = clampLambda(options.lambda);
|
|
225
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
226
|
+
return { reordered: [], diversity: emptyReport, lambda };
|
|
227
|
+
}
|
|
228
|
+
if (results.length < 2) {
|
|
229
|
+
return {
|
|
230
|
+
reordered: [results[0]],
|
|
231
|
+
diversity: {
|
|
232
|
+
considered: 1,
|
|
233
|
+
kept: 1,
|
|
234
|
+
headReorderCount: 0,
|
|
235
|
+
avgPairwiseSimBefore: 0,
|
|
236
|
+
avgPairwiseSimAfter: 0
|
|
237
|
+
},
|
|
238
|
+
lambda
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const topN = clampPositiveInt(options.topN, DEFAULT_TOP_N);
|
|
242
|
+
const candidates = results.map((r, index) => ({
|
|
243
|
+
id: makeRecallKey(r, index),
|
|
244
|
+
content: r.snippet ?? "",
|
|
245
|
+
score: typeof r.score === "number" ? r.score : 0,
|
|
246
|
+
embedding: null
|
|
247
|
+
}));
|
|
248
|
+
const indexById = /* @__PURE__ */ new Map();
|
|
249
|
+
for (let i = 0; i < candidates.length; i += 1) {
|
|
250
|
+
indexById.set(candidates[i].id, i);
|
|
251
|
+
}
|
|
252
|
+
const selectedMmr = applyMmrToCandidates({
|
|
253
|
+
candidates,
|
|
254
|
+
lambda,
|
|
255
|
+
topN,
|
|
256
|
+
budget: results.length
|
|
257
|
+
});
|
|
258
|
+
const reordered = [];
|
|
259
|
+
const reorderedCandidates = [];
|
|
260
|
+
const seen = /* @__PURE__ */ new Set();
|
|
261
|
+
for (const c of selectedMmr) {
|
|
262
|
+
if (seen.has(c.id)) continue;
|
|
263
|
+
const origIndex = indexById.get(c.id);
|
|
264
|
+
if (origIndex === void 0) continue;
|
|
265
|
+
seen.add(c.id);
|
|
266
|
+
reordered.push(results[origIndex]);
|
|
267
|
+
reorderedCandidates.push(candidates[origIndex]);
|
|
268
|
+
}
|
|
269
|
+
if (reordered.length < results.length) {
|
|
270
|
+
for (let i = 0; i < results.length; i += 1) {
|
|
271
|
+
const origCandidate = candidates[i];
|
|
272
|
+
if (seen.has(origCandidate.id)) continue;
|
|
273
|
+
seen.add(origCandidate.id);
|
|
274
|
+
reordered.push(results[i]);
|
|
275
|
+
reorderedCandidates.push(origCandidate);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const diversity = summarizeMmrDiversity(
|
|
279
|
+
candidates,
|
|
280
|
+
reorderedCandidates,
|
|
281
|
+
options.diversitySampleSize
|
|
282
|
+
);
|
|
283
|
+
return { reordered, diversity, lambda };
|
|
284
|
+
}
|
|
285
|
+
function makeRecallKey(r, index) {
|
|
286
|
+
const baseKey = r.path || r.docid || "";
|
|
287
|
+
return `${baseKey}::${index}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export {
|
|
291
|
+
DEFAULT_LAMBDA,
|
|
292
|
+
DEFAULT_TOP_N,
|
|
293
|
+
applyMmrToCandidates,
|
|
294
|
+
summarizeMmrDiversity,
|
|
295
|
+
normalizeTokens,
|
|
296
|
+
reorderRecallResultsWithMmr
|
|
297
|
+
};
|
|
298
|
+
//# sourceMappingURL=chunk-YDBIWGNI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recall-mmr.ts"],"sourcesContent":["/**\n * Maximal Marginal Relevance (MMR) re-selection for recall candidates.\n *\n * After the reranker produces its ordered candidate list, we run an MMR pass\n * over the top N candidates (per-section) so that a cluster of near-duplicate\n * high-scoring facts cannot dominate the injected context.\n *\n * MMR(d) = λ * sim(d, query) − (1 − λ) * max_{d' ∈ selected} sim(d, d')\n *\n * - λ defaults to 0.7 (tilted toward relevance, with meaningful diversity).\n * - Similarity uses cosine over pre-computed embeddings when available, and\n * falls back to Jaccard over normalized tokens (lowercased alphanumerics)\n * when embeddings are missing.\n * - Per-section application is the caller's responsibility: pass each\n * section's ordered candidate slice independently so one cluster in one\n * section cannot starve another section.\n *\n * Pure, deterministic, no IO. Input arrays are never mutated.\n */\n\n/** Minimal candidate shape used by MMR. Callers may wrap their own records. */\nexport interface MmrCandidate {\n /** Stable identifier for the candidate. Only used for tie-breaking. */\n id: string;\n /** Text content used for the Jaccard similarity fallback. */\n content: string;\n /**\n * Relevance score from the upstream ranker (e.g. rerank score or RRF).\n * Used as the `sim(d, query)` term when no query embedding is available.\n */\n score: number;\n /** Optional pre-computed embedding vector for cosine similarity. */\n embedding?: readonly number[] | null;\n}\n\nexport interface ApplyMmrOptions<C extends MmrCandidate> {\n /** Ordered candidate list (most relevant first). */\n candidates: readonly C[];\n /** Optional query embedding. If provided and candidates carry embeddings,\n * relevance is measured by cosine similarity to the query. Otherwise\n * candidate `score` is normalized and used. */\n queryEmbedding?: readonly number[] | null;\n /** λ ∈ [0, 1]. 1 = pure relevance, 0 = pure diversity. Default 0.7. */\n lambda?: number;\n /** Apply MMR only over the top N candidates. Default 40. */\n topN?: number;\n /** Maximum number of candidates to select. Default = candidates length. */\n budget?: number;\n}\n\nexport interface MmrDiversityReport {\n /**\n * Total number of candidates MMR considered (the full input pool, pre-MMR).\n * Previously this mirrored `kept` and was therefore uninformative; it now\n * reflects the true pool size so `kept/considered` logs carry signal even\n * though MMR is reorder-only.\n */\n considered: number;\n /**\n * Number of candidates present in the MMR output. With the current\n * orchestrator pipeline MMR is reorder-only (no drops), so this is the\n * same as `considered`. It's still reported separately so a future\n * drop-mode MMR can distinguish them without another schema change.\n */\n kept: number;\n /**\n * Head-of-list positions whose candidate identity changed between the\n * pre-MMR and post-MMR slices. This is the *actionable* diversity signal:\n * it tells the caller how many of the top `sampleSize` results MMR\n * promoted or demoted. Zero means MMR had no head-of-list effect; a value\n * greater than zero means at least one diverse candidate was swapped in.\n */\n headReorderCount: number;\n /** Average pairwise similarity of the head-of-list input slice (pre-MMR). */\n avgPairwiseSimBefore: number;\n /** Average pairwise similarity of the head-of-list MMR output slice. */\n avgPairwiseSimAfter: number;\n}\n\nexport const DEFAULT_LAMBDA = 0.7;\nexport const DEFAULT_TOP_N = 40;\n/**\n * Default number of head-of-list candidates compared by\n * {@link summarizeMmrDiversity}. Small on purpose: we want to measure whether\n * MMR actually *changed* the head of the list. Comparing the full top-N slice\n * is meaningless when `budget >= candidates.length` because both slices\n * contain the same set (just reordered), making pairwise-similarity\n * order-independent and identical before vs after.\n */\nconst DEFAULT_DIVERSITY_SAMPLE_SIZE = 10;\n\n/**\n * Pure MMR re-selection over an ordered candidate list.\n *\n * Returns a new array — the input is never mutated. When `candidates.length`\n * is `<= 1` or `budget <= 0`, a defensive copy is returned as a no-op.\n */\nexport function applyMmrToCandidates<C extends MmrCandidate>(\n opts: ApplyMmrOptions<C>,\n): C[] {\n const candidates = Array.isArray(opts.candidates) ? opts.candidates : [];\n if (candidates.length === 0) return [];\n\n const lambda = clampLambda(opts.lambda);\n const topN = clampPositiveInt(opts.topN, DEFAULT_TOP_N);\n const budget = clampPositiveInt(opts.budget, candidates.length);\n\n if (budget <= 0) return [];\n if (candidates.length === 1) return [candidates[0]!];\n\n // Only reorder the top-N slice; anything past it keeps its original position\n // appended at the end (but will not be selected unless budget > topN).\n const pool = candidates.slice(0, topN);\n const tail = candidates.slice(topN);\n\n // Relevance scores as `sim(d, query)`. Prefer cosine to the query embedding\n // when we can compute it; otherwise fall back to the candidate's own score\n // normalized into [0, 1] across the pool.\n const relevance = computeRelevanceScores(pool, opts.queryEmbedding);\n\n // Pre-compute pairwise candidate-candidate similarity lazily as needed.\n const pairSim = new Map<string, number>();\n const pairKey = (i: number, j: number): string =>\n i < j ? `${i}:${j}` : `${j}:${i}`;\n const sim = (i: number, j: number): number => {\n if (i === j) return 1;\n const key = pairKey(i, j);\n const cached = pairSim.get(key);\n if (cached !== undefined) return cached;\n const a = pool[i]!;\n const b = pool[j]!;\n const s = similarity(a, b);\n pairSim.set(key, s);\n return s;\n };\n\n const selectedIdx: number[] = [];\n const remaining = new Set<number>();\n for (let i = 0; i < pool.length; i += 1) remaining.add(i);\n\n while (selectedIdx.length < budget && remaining.size > 0) {\n let bestIdx = -1;\n let bestMmr = Number.NEGATIVE_INFINITY;\n for (const idx of remaining) {\n const rel = relevance[idx] ?? 0;\n let maxSimToSelected = 0;\n if (selectedIdx.length > 0) {\n for (const s of selectedIdx) {\n const pairwise = sim(idx, s);\n if (pairwise > maxSimToSelected) maxSimToSelected = pairwise;\n }\n }\n const mmr = lambda * rel - (1 - lambda) * maxSimToSelected;\n if (\n mmr > bestMmr ||\n // Stable tie-breaker: prefer the earlier original position.\n (mmr === bestMmr && (bestIdx < 0 || idx < bestIdx))\n ) {\n bestMmr = mmr;\n bestIdx = idx;\n }\n }\n if (bestIdx < 0) break;\n selectedIdx.push(bestIdx);\n remaining.delete(bestIdx);\n }\n\n const selected: C[] = selectedIdx.map((i) => pool[i]!);\n\n // If the caller set a budget larger than topN, append tail candidates in\n // their original order until budget is filled. This keeps MMR scoped to the\n // top-N slice while still respecting the caller's requested size.\n if (selected.length < budget && tail.length > 0) {\n for (const c of tail) {\n if (selected.length >= budget) break;\n selected.push(c);\n }\n }\n\n return selected;\n}\n\n/**\n * Summarize how much MMR reshuffled the head of the candidate list, for\n * logging. Optional — callers can skip this if they don't care about metrics.\n *\n * IMPORTANT: `before` should be the score-ordered input (the same ordering the\n * upstream reranker emitted, *not* the MMR output), and `after` should be the\n * MMR-reordered list. Both are truncated to `sampleSize` before their pairwise\n * similarity is averaged.\n *\n * The sample size intentionally defaults to a small number\n * ({@link DEFAULT_DIVERSITY_SAMPLE_SIZE}) so that when the caller uses\n * `budget >= candidates.length` (i.e. MMR only reorders without dropping), the\n * head-of-list comparison still reflects whether MMR promoted diverse\n * candidates. Passing a sample size `>= candidates.length` in that situation\n * makes `avgPairwiseSimBefore` and `avgPairwiseSimAfter` trivially equal\n * because pairwise similarity is order-independent.\n */\nexport function summarizeMmrDiversity<C extends MmrCandidate>(\n before: readonly C[],\n after: readonly C[],\n sampleSize: number = DEFAULT_DIVERSITY_SAMPLE_SIZE,\n): MmrDiversityReport {\n const n = clampPositiveInt(sampleSize, DEFAULT_DIVERSITY_SAMPLE_SIZE);\n const beforeSlice = before.slice(0, n);\n const afterSlice = after.slice(0, n);\n // Count how many head-of-list positions MMR actually changed. A zero value\n // means MMR left the head untouched; a positive value is the actionable\n // \"MMR promoted N diverse candidates\" signal the previous metric lacked.\n let headReorderCount = 0;\n const compareLength = Math.min(beforeSlice.length, afterSlice.length);\n for (let i = 0; i < compareLength; i += 1) {\n if (beforeSlice[i]!.id !== afterSlice[i]!.id) headReorderCount += 1;\n }\n return {\n considered: before.length,\n kept: after.length,\n headReorderCount,\n avgPairwiseSimBefore: averagePairwiseSimilarity(beforeSlice),\n avgPairwiseSimAfter: averagePairwiseSimilarity(afterSlice),\n };\n}\n\n// -----------------------------------------------------------------------------\n// Internals\n// -----------------------------------------------------------------------------\n\nfunction clampLambda(value: number | undefined): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return DEFAULT_LAMBDA;\n if (value < 0) return 0;\n if (value > 1) return 1;\n return value;\n}\n\nfunction clampPositiveInt(\n value: number | undefined,\n fallback: number,\n): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return fallback;\n if (value <= 0) return 0;\n return Math.floor(value);\n}\n\nfunction computeRelevanceScores<C extends MmrCandidate>(\n pool: readonly C[],\n queryEmbedding: readonly number[] | null | undefined,\n): number[] {\n // If we have a query embedding, use cosine against each candidate embedding\n // where possible. Candidates without embeddings fall back to normalized score.\n const canUseQueryEmbedding =\n Array.isArray(queryEmbedding) && queryEmbedding.length > 0;\n\n if (canUseQueryEmbedding) {\n const scores: number[] = [];\n for (const c of pool) {\n if (Array.isArray(c.embedding) && c.embedding.length > 0) {\n scores.push(cosineSimilarity(queryEmbedding!, c.embedding));\n } else {\n // Fall back to the upstream relevance score for this one candidate.\n scores.push(normalizeFinite(c.score));\n }\n }\n return normalizeVector(scores);\n }\n\n // No query embedding — use normalized candidate scores.\n const raw = pool.map((c) => normalizeFinite(c.score));\n return normalizeVector(raw);\n}\n\nfunction similarity<C extends MmrCandidate>(a: C, b: C): number {\n if (\n Array.isArray(a.embedding) &&\n a.embedding.length > 0 &&\n Array.isArray(b.embedding) &&\n b.embedding.length > 0 &&\n a.embedding.length === b.embedding.length\n ) {\n return cosineSimilarity(a.embedding, b.embedding);\n }\n return jaccardSimilarity(a.content ?? \"\", b.content ?? \"\");\n}\n\nfunction cosineSimilarity(\n a: readonly number[],\n b: readonly number[],\n): number {\n if (a.length === 0 || b.length === 0) return 0;\n const len = Math.min(a.length, b.length);\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < len; i += 1) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dot += av * bv;\n na += av * av;\n nb += bv * bv;\n }\n if (na === 0 || nb === 0) return 0;\n const cos = dot / (Math.sqrt(na) * Math.sqrt(nb));\n // Clamp to [0, 1]. Negative cosines (opposite directions) are treated as 0\n // for MMR purposes — two embeddings pointing opposite ways should be\n // \"maximally diverse\", not discouraged further.\n if (cos < 0) return 0;\n if (cos > 1) return 1;\n return cos;\n}\n\nexport function normalizeTokens(text: string): Set<string> {\n if (!text) return new Set();\n // Unicode-aware normalization. We lowercase and replace any character that\n // is NOT a Unicode letter or number with a space, then split on whitespace.\n // This preserves non-Latin scripts (Cyrillic, Greek, Hebrew, Arabic, etc.)\n // so the Jaccard fallback still detects near-duplicates in multilingual\n // snippets. Without this fix, `[^a-z0-9]+` strips all non-ASCII and two\n // identical Chinese/Japanese/Cyrillic snippets both collapse to the empty\n // set, returning similarity 0 and letting duplicates dominate recall.\n const cleaned = text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim();\n if (cleaned.length === 0) {\n // CJK fallback: scripts like Chinese/Japanese/Korean do not use word\n // breaks, so after the Unicode strip the above split-on-whitespace yields\n // one big token. The Latin-style split is a bad match for these scripts;\n // use per-codepoint character tokens instead, which approximates a\n // unigram shingle and is accurate enough for near-duplicate detection.\n const chars = new Set<string>();\n for (const ch of text.toLowerCase()) {\n if (/\\s/.test(ch)) continue;\n chars.add(ch);\n }\n return chars;\n }\n const tokens = new Set<string>();\n for (const token of cleaned.split(/\\s+/)) {\n if (!token) continue;\n // A single \"token\" that is actually a run of CJK codepoints (no spaces in\n // the source) should still be split into per-character tokens so the\n // Jaccard overlap works. Latin/Cyrillic/etc. keep their word-level tokens.\n if (token.length >= 2 && hasUnsegmentableScript(token)) {\n for (const ch of token) tokens.add(ch);\n } else {\n tokens.add(token);\n }\n }\n return tokens;\n}\n\n/**\n * Returns true when `token` contains at least one codepoint in a script that\n * does not use whitespace for word segmentation (CJK Unified Ideographs,\n * Hiragana, Katakana, Hangul). These are tokenized per-character so Jaccard\n * similarity can still detect near-duplicate snippets.\n */\nfunction hasUnsegmentableScript(token: string): boolean {\n return /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u.test(\n token,\n );\n}\n\nfunction jaccardSimilarity(a: string, b: string): number {\n const ta = normalizeTokens(a);\n const tb = normalizeTokens(b);\n if (ta.size === 0 && tb.size === 0) return 0;\n let intersection = 0;\n for (const t of ta) if (tb.has(t)) intersection += 1;\n const union = ta.size + tb.size - intersection;\n if (union === 0) return 0;\n return intersection / union;\n}\n\nfunction normalizeFinite(value: number): number {\n if (typeof value !== \"number\" || !Number.isFinite(value)) return 0;\n return value;\n}\n\nfunction normalizeVector(values: number[]): number[] {\n if (values.length === 0) return values;\n // Intentionally *not* min-max normalized. Min-max maps the lowest score to\n // 0 and the highest to 1, which amplifies tiny relevance gaps (e.g. a tight\n // reranker cluster like [0.93, 0.94, 0.95]) into the full [0, 1] range.\n // That lets a near-duplicate at the top permanently beat a diverse\n // candidate at the bottom because `lambda * 0 - (1 - lambda) * 0 = 0` is\n // always worse than `lambda * 1 - (1 - lambda) * maxSim` for any\n // maxSim < lambda / (1 - lambda). MMR is supposed to escape exactly that\n // scenario; min-max defeats it.\n //\n // Instead we preserve the relative score gaps by scaling only if the\n // scores leave the MMR-friendly `[0, 1]` range. Negative or out-of-range\n // scores are clamped to `[0, 1]` by dividing by the max absolute value; if\n // everything is already inside `[0, 1]`, we pass through untouched so\n // tight clusters stay tight and MMR can promote a diverse candidate from\n // the bottom of the cluster.\n let max = 0;\n let min = Number.POSITIVE_INFINITY;\n for (const v of values) {\n if (!Number.isFinite(v)) continue;\n const a = Math.abs(v);\n if (a > max) max = a;\n if (v < min) min = v;\n }\n if (!Number.isFinite(min)) {\n return values.map(() => 0);\n }\n if (max === 0) {\n // All scores are zero — give everyone 1 so diversity fully drives\n // selection (otherwise every candidate has `lambda * 0 = 0` and the\n // first-found wins trivially, hiding any diversity benefit).\n return values.map(() => 1);\n }\n // If scores already live in [0, 1], pass them through so tight clusters\n // (e.g. [0.93, 0.94, 0.95]) stay tight and the (1 - lambda) * maxSim term\n // can actually outweigh a 0.01 relevance gap, which is the whole point of\n // MMR. Otherwise divide by `max` to bring the top score to 1 while\n // preserving *relative* gaps (e.g. [100, 200, 300] -> [0.33, 0.67, 1.0]).\n if (min >= 0 && max <= 1) {\n return values.map((v) =>\n Number.isFinite(v) ? (v < 0 ? 0 : v > 1 ? 1 : v) : 0,\n );\n }\n return values.map((v) => {\n if (!Number.isFinite(v)) return 0;\n const scaled = v / max;\n if (scaled < 0) return 0;\n if (scaled > 1) return 1;\n return scaled;\n });\n}\n\nfunction averagePairwiseSimilarity<C extends MmrCandidate>(\n candidates: readonly C[],\n): number {\n if (candidates.length < 2) return 0;\n let sum = 0;\n let count = 0;\n for (let i = 0; i < candidates.length; i += 1) {\n for (let j = i + 1; j < candidates.length; j += 1) {\n sum += similarity(candidates[i]!, candidates[j]!);\n count += 1;\n }\n }\n if (count === 0) return 0;\n return sum / count;\n}\n\n// -----------------------------------------------------------------------------\n// Orchestration helper: MMR for recall results keyed by path-first\n// -----------------------------------------------------------------------------\n\n/**\n * Minimum shape the recall-MMR orchestration helper expects from a result.\n * Any richer result type (e.g. {@link QmdSearchResult}) satisfies this.\n */\nexport interface MmrRecallResult {\n readonly docid?: string;\n readonly path?: string;\n readonly snippet?: string;\n readonly score?: number;\n}\n\nexport interface ReorderRecallResultsOptions {\n readonly lambda?: number;\n readonly topN?: number;\n /**\n * Head-of-list sample size used by the diversity metric. Defaults to\n * {@link DEFAULT_DIVERSITY_SAMPLE_SIZE}. Intentionally small so the metric\n * reflects head-of-list changes even when `budget >= results.length`.\n */\n readonly diversitySampleSize?: number;\n}\n\nexport interface ReorderRecallResultsOutcome<R extends MmrRecallResult> {\n readonly reordered: R[];\n readonly diversity: MmrDiversityReport;\n readonly lambda: number;\n}\n\n/**\n * Apply MMR to an ordered list of recall results and return the reordered\n * list plus a head-of-list diversity report.\n *\n * This helper is the single source of truth for the orchestrator's\n * per-section MMR pass. It is pure and deterministic so it can be unit\n * tested without constructing an Orchestrator.\n *\n * Key invariants:\n * 1. **No silent drops.** Candidates are keyed by a stable unique key derived\n * from `path` first, falling back to `docid`, and always suffixed with the\n * candidate's original index. Two results that share a basename-style\n * docid but differ in path are treated as distinct candidates and both\n * survive the reorder.\n * 2. **No mutation.** The input array is never mutated; a new array is\n * returned.\n * 3. **Diversity metric is meaningful.** The report compares the *head of\n * list* before and after MMR using a small sample size, so it reflects\n * whether MMR promoted diverse candidates even when\n * `budget >= results.length`.\n */\nexport function reorderRecallResultsWithMmr<R extends MmrRecallResult>(\n results: readonly R[],\n options: ReorderRecallResultsOptions = {},\n): ReorderRecallResultsOutcome<R> {\n const emptyReport: MmrDiversityReport = {\n considered: 0,\n kept: 0,\n headReorderCount: 0,\n avgPairwiseSimBefore: 0,\n avgPairwiseSimAfter: 0,\n };\n const lambda = clampLambda(options.lambda);\n\n if (!Array.isArray(results) || results.length === 0) {\n return { reordered: [], diversity: emptyReport, lambda };\n }\n if (results.length < 2) {\n // Single-element input: MMR is a trivial no-op but the diversity report\n // must still reflect the real pool size so external callers see\n // `considered=1 kept=1` rather than the `0/0` sentinel used for the\n // empty case. This is also the exact path the orchestrator short-circuits\n // past, but the helper is a public export and external callers cannot\n // rely on that invariant.\n return {\n reordered: [results[0]!],\n diversity: {\n considered: 1,\n kept: 1,\n headReorderCount: 0,\n avgPairwiseSimBefore: 0,\n avgPairwiseSimAfter: 0,\n },\n lambda,\n };\n }\n\n const topN = clampPositiveInt(options.topN, DEFAULT_TOP_N);\n\n // Build a per-result *unique* key so distinct results with colliding\n // docids or paths are never silently collapsed by id-based lookups.\n // Keys are always suffixed with the original input index so two results\n // that share the same `path` (or share the same `docid` when `path` is\n // empty) still get distinct identities.\n const candidates: MmrCandidate[] = results.map((r, index) => ({\n id: makeRecallKey(r, index),\n content: r.snippet ?? \"\",\n score: typeof r.score === \"number\" ? r.score : 0,\n embedding: null,\n }));\n // Index lookup by id so we can map MMR-selected candidates back to their\n // *original* position (and therefore their original `candidates[i]` object,\n // which preserves its `id` across the reorder). Reusing those original\n // candidate objects in the diversity comparison means\n // `headReorderCount` works correctly even when two results in the input\n // share the same base key: a swap at the same head position shows up\n // because their original-index-suffixed ids still differ.\n const indexById = new Map<string, number>();\n for (let i = 0; i < candidates.length; i += 1) {\n indexById.set(candidates[i]!.id, i);\n }\n\n const selectedMmr = applyMmrToCandidates({\n candidates,\n lambda,\n topN,\n budget: results.length,\n });\n\n const reordered: R[] = [];\n // Parallel array of the *original* candidate objects in the new (post-MMR)\n // order. Used for a faithful diversity comparison that preserves each\n // candidate's original identity.\n const reorderedCandidates: MmrCandidate[] = [];\n const seen = new Set<string>();\n for (const c of selectedMmr) {\n if (seen.has(c.id)) continue;\n const origIndex = indexById.get(c.id);\n if (origIndex === undefined) continue;\n seen.add(c.id);\n reordered.push(results[origIndex]!);\n reorderedCandidates.push(candidates[origIndex]!);\n }\n // Safety: append any candidates MMR did not select so nothing is dropped.\n if (reordered.length < results.length) {\n for (let i = 0; i < results.length; i += 1) {\n const origCandidate = candidates[i]!;\n if (seen.has(origCandidate.id)) continue;\n seen.add(origCandidate.id);\n reordered.push(results[i]!);\n reorderedCandidates.push(origCandidate);\n }\n }\n\n const diversity = summarizeMmrDiversity(\n candidates,\n reorderedCandidates,\n options.diversitySampleSize,\n );\n\n return { reordered, diversity, lambda };\n}\n\nfunction makeRecallKey(r: MmrRecallResult, index: number): string {\n const baseKey = r.path || r.docid || \"\";\n return `${baseKey}::${index}`;\n}\n"],"mappings":";AA+EO,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AAS7B,IAAM,gCAAgC;AAQ/B,SAAS,qBACd,MACK;AACL,QAAM,aAAa,MAAM,QAAQ,KAAK,UAAU,IAAI,KAAK,aAAa,CAAC;AACvE,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC;AAErC,QAAM,SAAS,YAAY,KAAK,MAAM;AACtC,QAAM,OAAO,iBAAiB,KAAK,MAAM,aAAa;AACtD,QAAM,SAAS,iBAAiB,KAAK,QAAQ,WAAW,MAAM;AAE9D,MAAI,UAAU,EAAG,QAAO,CAAC;AACzB,MAAI,WAAW,WAAW,EAAG,QAAO,CAAC,WAAW,CAAC,CAAE;AAInD,QAAM,OAAO,WAAW,MAAM,GAAG,IAAI;AACrC,QAAM,OAAO,WAAW,MAAM,IAAI;AAKlC,QAAM,YAAY,uBAAuB,MAAM,KAAK,cAAc;AAGlE,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,UAAU,CAAC,GAAW,MAC1B,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC;AACjC,QAAM,MAAM,CAAC,GAAW,MAAsB;AAC5C,QAAI,MAAM,EAAG,QAAO;AACpB,UAAM,MAAM,QAAQ,GAAG,CAAC;AACxB,UAAM,SAAS,QAAQ,IAAI,GAAG;AAC9B,QAAI,WAAW,OAAW,QAAO;AACjC,UAAM,IAAI,KAAK,CAAC;AAChB,UAAM,IAAI,KAAK,CAAC;AAChB,UAAM,IAAI,WAAW,GAAG,CAAC;AACzB,YAAQ,IAAI,KAAK,CAAC;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,cAAwB,CAAC;AAC/B,QAAM,YAAY,oBAAI,IAAY;AAClC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,EAAG,WAAU,IAAI,CAAC;AAExD,SAAO,YAAY,SAAS,UAAU,UAAU,OAAO,GAAG;AACxD,QAAI,UAAU;AACd,QAAI,UAAU,OAAO;AACrB,eAAW,OAAO,WAAW;AAC3B,YAAM,MAAM,UAAU,GAAG,KAAK;AAC9B,UAAI,mBAAmB;AACvB,UAAI,YAAY,SAAS,GAAG;AAC1B,mBAAW,KAAK,aAAa;AAC3B,gBAAM,WAAW,IAAI,KAAK,CAAC;AAC3B,cAAI,WAAW,iBAAkB,oBAAmB;AAAA,QACtD;AAAA,MACF;AACA,YAAM,MAAM,SAAS,OAAO,IAAI,UAAU;AAC1C,UACE,MAAM;AAAA,MAEL,QAAQ,YAAY,UAAU,KAAK,MAAM,UAC1C;AACA,kBAAU;AACV,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,QAAI,UAAU,EAAG;AACjB,gBAAY,KAAK,OAAO;AACxB,cAAU,OAAO,OAAO;AAAA,EAC1B;AAEA,QAAM,WAAgB,YAAY,IAAI,CAAC,MAAM,KAAK,CAAC,CAAE;AAKrD,MAAI,SAAS,SAAS,UAAU,KAAK,SAAS,GAAG;AAC/C,eAAW,KAAK,MAAM;AACpB,UAAI,SAAS,UAAU,OAAQ;AAC/B,eAAS,KAAK,CAAC;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;AAmBO,SAAS,sBACd,QACA,OACA,aAAqB,+BACD;AACpB,QAAM,IAAI,iBAAiB,YAAY,6BAA6B;AACpE,QAAM,cAAc,OAAO,MAAM,GAAG,CAAC;AACrC,QAAM,aAAa,MAAM,MAAM,GAAG,CAAC;AAInC,MAAI,mBAAmB;AACvB,QAAM,gBAAgB,KAAK,IAAI,YAAY,QAAQ,WAAW,MAAM;AACpE,WAAS,IAAI,GAAG,IAAI,eAAe,KAAK,GAAG;AACzC,QAAI,YAAY,CAAC,EAAG,OAAO,WAAW,CAAC,EAAG,GAAI,qBAAoB;AAAA,EACpE;AACA,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,sBAAsB,0BAA0B,WAAW;AAAA,IAC3D,qBAAqB,0BAA0B,UAAU;AAAA,EAC3D;AACF;AAMA,SAAS,YAAY,OAAmC;AACtD,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,EAAG,QAAO;AACtB,SAAO;AACT;AAEA,SAAS,iBACP,OACA,UACQ;AACR,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO,KAAK,MAAM,KAAK;AACzB;AAEA,SAAS,uBACP,MACA,gBACU;AAGV,QAAM,uBACJ,MAAM,QAAQ,cAAc,KAAK,eAAe,SAAS;AAE3D,MAAI,sBAAsB;AACxB,UAAM,SAAmB,CAAC;AAC1B,eAAW,KAAK,MAAM;AACpB,UAAI,MAAM,QAAQ,EAAE,SAAS,KAAK,EAAE,UAAU,SAAS,GAAG;AACxD,eAAO,KAAK,iBAAiB,gBAAiB,EAAE,SAAS,CAAC;AAAA,MAC5D,OAAO;AAEL,eAAO,KAAK,gBAAgB,EAAE,KAAK,CAAC;AAAA,MACtC;AAAA,IACF;AACA,WAAO,gBAAgB,MAAM;AAAA,EAC/B;AAGA,QAAM,MAAM,KAAK,IAAI,CAAC,MAAM,gBAAgB,EAAE,KAAK,CAAC;AACpD,SAAO,gBAAgB,GAAG;AAC5B;AAEA,SAAS,WAAmC,GAAM,GAAc;AAC9D,MACE,MAAM,QAAQ,EAAE,SAAS,KACzB,EAAE,UAAU,SAAS,KACrB,MAAM,QAAQ,EAAE,SAAS,KACzB,EAAE,UAAU,SAAS,KACrB,EAAE,UAAU,WAAW,EAAE,UAAU,QACnC;AACA,WAAO,iBAAiB,EAAE,WAAW,EAAE,SAAS;AAAA,EAClD;AACA,SAAO,kBAAkB,EAAE,WAAW,IAAI,EAAE,WAAW,EAAE;AAC3D;AAEA,SAAS,iBACP,GACA,GACQ;AACR,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO;AAC7C,QAAM,MAAM,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AACvC,MAAI,MAAM;AACV,MAAI,KAAK;AACT,MAAI,KAAK;AACT,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK,GAAG;AAC/B,UAAM,KAAK,EAAE,CAAC,KAAK;AACnB,UAAM,KAAK,EAAE,CAAC,KAAK;AACnB,WAAO,KAAK;AACZ,UAAM,KAAK;AACX,UAAM,KAAK;AAAA,EACb;AACA,MAAI,OAAO,KAAK,OAAO,EAAG,QAAO;AACjC,QAAM,MAAM,OAAO,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,EAAE;AAI/C,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO;AACT;AAEO,SAAS,gBAAgB,MAA2B;AACzD,MAAI,CAAC,KAAM,QAAO,oBAAI,IAAI;AAQ1B,QAAM,UAAU,KACb,YAAY,EACZ,QAAQ,oBAAoB,GAAG,EAC/B,KAAK;AACR,MAAI,QAAQ,WAAW,GAAG;AAMxB,UAAM,QAAQ,oBAAI,IAAY;AAC9B,eAAW,MAAM,KAAK,YAAY,GAAG;AACnC,UAAI,KAAK,KAAK,EAAE,EAAG;AACnB,YAAM,IAAI,EAAE;AAAA,IACd;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,SAAS,QAAQ,MAAM,KAAK,GAAG;AACxC,QAAI,CAAC,MAAO;AAIZ,QAAI,MAAM,UAAU,KAAK,uBAAuB,KAAK,GAAG;AACtD,iBAAW,MAAM,MAAO,QAAO,IAAI,EAAE;AAAA,IACvC,OAAO;AACL,aAAO,IAAI,KAAK;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAQA,SAAS,uBAAuB,OAAwB;AACtD,SAAO,2EAA2E;AAAA,IAChF;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,GAAW,GAAmB;AACvD,QAAM,KAAK,gBAAgB,CAAC;AAC5B,QAAM,KAAK,gBAAgB,CAAC;AAC5B,MAAI,GAAG,SAAS,KAAK,GAAG,SAAS,EAAG,QAAO;AAC3C,MAAI,eAAe;AACnB,aAAW,KAAK,GAAI,KAAI,GAAG,IAAI,CAAC,EAAG,iBAAgB;AACnD,QAAM,QAAQ,GAAG,OAAO,GAAG,OAAO;AAClC,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,eAAe;AACxB;AAEA,SAAS,gBAAgB,OAAuB;AAC9C,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,SAAO;AACT;AAEA,SAAS,gBAAgB,QAA4B;AACnD,MAAI,OAAO,WAAW,EAAG,QAAO;AAgBhC,MAAI,MAAM;AACV,MAAI,MAAM,OAAO;AACjB,aAAW,KAAK,QAAQ;AACtB,QAAI,CAAC,OAAO,SAAS,CAAC,EAAG;AACzB,UAAM,IAAI,KAAK,IAAI,CAAC;AACpB,QAAI,IAAI,IAAK,OAAM;AACnB,QAAI,IAAI,IAAK,OAAM;AAAA,EACrB;AACA,MAAI,CAAC,OAAO,SAAS,GAAG,GAAG;AACzB,WAAO,OAAO,IAAI,MAAM,CAAC;AAAA,EAC3B;AACA,MAAI,QAAQ,GAAG;AAIb,WAAO,OAAO,IAAI,MAAM,CAAC;AAAA,EAC3B;AAMA,MAAI,OAAO,KAAK,OAAO,GAAG;AACxB,WAAO,OAAO;AAAA,MAAI,CAAC,MACjB,OAAO,SAAS,CAAC,IAAK,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAK;AAAA,IACrD;AAAA,EACF;AACA,SAAO,OAAO,IAAI,CAAC,MAAM;AACvB,QAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,UAAM,SAAS,IAAI;AACnB,QAAI,SAAS,EAAG,QAAO;AACvB,QAAI,SAAS,EAAG,QAAO;AACvB,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,0BACP,YACQ;AACR,MAAI,WAAW,SAAS,EAAG,QAAO;AAClC,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,aAAS,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AACjD,aAAO,WAAW,WAAW,CAAC,GAAI,WAAW,CAAC,CAAE;AAChD,eAAS;AAAA,IACX;AAAA,EACF;AACA,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,MAAM;AACf;AAuDO,SAAS,4BACd,SACA,UAAuC,CAAC,GACR;AAChC,QAAM,cAAkC;AAAA,IACtC,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,EACvB;AACA,QAAM,SAAS,YAAY,QAAQ,MAAM;AAEzC,MAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAAG;AACnD,WAAO,EAAE,WAAW,CAAC,GAAG,WAAW,aAAa,OAAO;AAAA,EACzD;AACA,MAAI,QAAQ,SAAS,GAAG;AAOtB,WAAO;AAAA,MACL,WAAW,CAAC,QAAQ,CAAC,CAAE;AAAA,MACvB,WAAW;AAAA,QACT,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,kBAAkB;AAAA,QAClB,sBAAsB;AAAA,QACtB,qBAAqB;AAAA,MACvB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAO,iBAAiB,QAAQ,MAAM,aAAa;AAOzD,QAAM,aAA6B,QAAQ,IAAI,CAAC,GAAG,WAAW;AAAA,IAC5D,IAAI,cAAc,GAAG,KAAK;AAAA,IAC1B,SAAS,EAAE,WAAW;AAAA,IACtB,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;AAAA,IAC/C,WAAW;AAAA,EACb,EAAE;AAQF,QAAM,YAAY,oBAAI,IAAoB;AAC1C,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,cAAU,IAAI,WAAW,CAAC,EAAG,IAAI,CAAC;AAAA,EACpC;AAEA,QAAM,cAAc,qBAAqB;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,QAAM,YAAiB,CAAC;AAIxB,QAAM,sBAAsC,CAAC;AAC7C,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,KAAK,aAAa;AAC3B,QAAI,KAAK,IAAI,EAAE,EAAE,EAAG;AACpB,UAAM,YAAY,UAAU,IAAI,EAAE,EAAE;AACpC,QAAI,cAAc,OAAW;AAC7B,SAAK,IAAI,EAAE,EAAE;AACb,cAAU,KAAK,QAAQ,SAAS,CAAE;AAClC,wBAAoB,KAAK,WAAW,SAAS,CAAE;AAAA,EACjD;AAEA,MAAI,UAAU,SAAS,QAAQ,QAAQ;AACrC,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,YAAM,gBAAgB,WAAW,CAAC;AAClC,UAAI,KAAK,IAAI,cAAc,EAAE,EAAG;AAChC,WAAK,IAAI,cAAc,EAAE;AACzB,gBAAU,KAAK,QAAQ,CAAC,CAAE;AAC1B,0BAAoB,KAAK,aAAa;AAAA,IACxC;AAAA,EACF;AAEA,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV;AAEA,SAAO,EAAE,WAAW,WAAW,OAAO;AACxC;AAEA,SAAS,cAAc,GAAoB,OAAuB;AAChE,QAAM,UAAU,EAAE,QAAQ,EAAE,SAAS;AACrC,SAAO,GAAG,OAAO,KAAK,KAAK;AAC7B;","names":[]}
|