@mnstry/atelier 0.2.0-alpha.11 → 0.2.0-alpha.13
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/CHANGELOG.md +1005 -0
- package/README.md +149 -9
- package/contracts/atelier-adoption-policy.v2.schema.json +55 -0
- package/contracts/atelier-architecture.v1.schema.json +258 -0
- package/contracts/atelier-boundary-delegations.v1.schema.json +161 -0
- package/contracts/atelier-boundary-owner-keys.v1.schema.json +98 -0
- package/contracts/atelier-build.v1.schema.json +1076 -0
- package/contracts/atelier-capability.v1.schema.json +1252 -0
- package/contracts/atelier-coordination.v1.schema.json +1050 -0
- package/contracts/atelier-evidence-navigation.v1.schema.json +603 -0
- package/contracts/atelier-harness.v1.schema.json +234 -0
- package/contracts/atelier-ingestion.v1.schema.json +806 -0
- package/contracts/atelier-inquiry.v1.schema.json +1772 -0
- package/contracts/atelier-instruction-adoption.v1.schema.json +490 -0
- package/contracts/atelier-interaction.v1.schema.json +553 -0
- package/contracts/atelier-judgment.v1.schema.json +690 -0
- package/contracts/atelier-knowledge.v1.schema.json +1512 -0
- package/contracts/atelier-learning.v1.schema.json +1095 -0
- package/contracts/atelier-migration.v3.schema.json +64 -0
- package/contracts/atelier-obsidian-generation-manifest.v2.schema.json +372 -0
- package/contracts/atelier-personal-workspace-manifest.v1.schema.json +87 -0
- package/contracts/atelier-personal-workspace-overlay.v1.schema.json +299 -0
- package/contracts/atelier-template-adoption.v1.schema.json +223 -0
- package/contracts/atelier-template-profile.v1.schema.json +1168 -0
- package/contracts/atelier-trackable.v1.schema.json +781 -0
- package/contracts/atelier-upgrade-plan.v3.schema.json +532 -0
- package/contracts/compat-baseline.json +1 -1
- package/docs/architecture.md +89 -0
- package/docs/assurance-controls.md +4 -3
- package/docs/blocks/capability-stewardship.md +17 -0
- package/docs/blocks/claims.md +16 -3
- package/docs/blocks/inquiry-harnesses.md +19 -0
- package/docs/blocks/learning-harnesses.md +17 -0
- package/docs/capability-stewardship.md +269 -0
- package/docs/coordination.md +51 -0
- package/docs/decision-practices.md +111 -0
- package/docs/decisions.md +29 -0
- package/docs/evidence-navigation.md +237 -0
- package/docs/exact-upgrades.md +3 -2
- package/docs/ingestion.md +181 -0
- package/docs/inquiry-harnesses.md +211 -0
- package/docs/install.md +80 -3
- package/docs/intake-and-guides.md +5 -0
- package/docs/integration-contract-decisions.md +24 -2
- package/docs/interaction-and-reflection.md +72 -0
- package/docs/knowledge-graph.md +20 -0
- package/docs/knowledge-health/api.md +30 -0
- package/docs/knowledge-health/assessment/api.md +96 -0
- package/docs/knowledge-health/assessment/capabilities.md +40 -0
- package/docs/knowledge-health/assessment/evaluation.md +27 -0
- package/docs/knowledge-health/assessment/facilitator.md +30 -0
- package/docs/knowledge-health/assessment/quickstart.md +61 -0
- package/docs/knowledge-health/assessment/troubleshooting.md +23 -0
- package/docs/knowledge-health/devday/facilitator.md +16 -0
- package/docs/knowledge-health/devday/participant-response.md +22 -0
- package/docs/knowledge-health/devday/public-default.md +107 -0
- package/docs/knowledge-health/devday/quickstart.md +18 -0
- package/docs/knowledge-health/devday/support.md +22 -0
- package/docs/knowledge-health/exercise.md +17 -0
- package/docs/knowledge-health/facilitator.md +20 -0
- package/docs/knowledge-health/quickstart.md +94 -0
- package/docs/knowledge-health/readme.md +15 -0
- package/docs/knowledge-health/support.md +34 -0
- package/docs/knowledge-setup.md +248 -0
- package/docs/knowledge-workspace.md +264 -0
- package/docs/learning-harnesses.md +238 -0
- package/docs/learning.md +215 -0
- package/docs/local-services.md +242 -26
- package/docs/obsidian-contract.md +878 -36
- package/docs/obsidian-plugin.md +619 -0
- package/docs/obsidian.md +809 -64
- package/docs/optional-ingestion.md +83 -0
- package/docs/personal-workspace.md +257 -0
- package/docs/practical-judgment.md +92 -0
- package/docs/progressive-ingestion-acceptance.md +89 -0
- package/docs/progressive-ingestion.md +235 -0
- package/docs/project-command-inventory.md +1 -0
- package/docs/release-engineering.md +56 -1
- package/docs/repo-boundary-guard.md +94 -0
- package/docs/research-starter-quickstart.md +66 -0
- package/docs/research-starter.md +114 -0
- package/docs/semantic-ingestion.md +198 -0
- package/docs/semantic-operation.md +317 -0
- package/docs/skill-steward.md +6 -0
- package/docs/template-profile.md +164 -0
- package/docs/template-upgrades.md +114 -0
- package/docs/trackables.md +111 -0
- package/docs/upgrade.md +68 -3
- package/fixtures/atelier-architecture/binding/invalid/document.json +19 -0
- package/fixtures/atelier-architecture/binding/valid/document.json +19 -0
- package/fixtures/atelier-architecture/catalog/invalid/document.json +290 -0
- package/fixtures/atelier-architecture/catalog/valid/document.json +343 -0
- package/fixtures/atelier-build-contract/attempt/invalid/document.json +20 -0
- package/fixtures/atelier-build-contract/attempt/valid/document.json +19 -0
- package/fixtures/atelier-build-contract/candidate/invalid/document.json +24 -0
- package/fixtures/atelier-build-contract/candidate/valid/document.json +23 -0
- package/fixtures/atelier-build-contract/decision/invalid/document.json +31 -0
- package/fixtures/atelier-build-contract/decision/valid/document.json +30 -0
- package/fixtures/atelier-build-contract/delivery/invalid/document.json +18 -0
- package/fixtures/atelier-build-contract/delivery/valid/document.json +17 -0
- package/fixtures/atelier-build-contract/gate/invalid/document.json +22 -0
- package/fixtures/atelier-build-contract/gate/valid/document.json +21 -0
- package/fixtures/atelier-build-contract/ledger/invalid/document.json +270 -0
- package/fixtures/atelier-build-contract/ledger/valid/document.json +269 -0
- package/fixtures/atelier-build-contract/objective/invalid/document.json +73 -0
- package/fixtures/atelier-build-contract/objective/valid/document.json +72 -0
- package/fixtures/atelier-build-contract/progress/invalid/document.json +18 -0
- package/fixtures/atelier-build-contract/progress/valid/document.json +17 -0
- package/fixtures/atelier-capability-contract/adoption/invalid/document.json +71 -0
- package/fixtures/atelier-capability-contract/adoption/valid/document.json +70 -0
- package/fixtures/atelier-capability-contract/event/invalid/document.json +18 -0
- package/fixtures/atelier-capability-contract/event/valid/document.json +17 -0
- package/fixtures/atelier-capability-contract/journal/invalid/document.json +382 -0
- package/fixtures/atelier-capability-contract/journal/valid/document.json +381 -0
- package/fixtures/atelier-capability-contract/notice/invalid/document.json +8 -0
- package/fixtures/atelier-capability-contract/notice/valid/document.json +7 -0
- package/fixtures/atelier-capability-contract/package/invalid/document.json +59 -0
- package/fixtures/atelier-capability-contract/package/valid/document.json +58 -0
- package/fixtures/atelier-capability-contract/plan/invalid/document.json +107 -0
- package/fixtures/atelier-capability-contract/plan/valid/document.json +106 -0
- package/fixtures/atelier-capability-contract/release/invalid/document.json +85 -0
- package/fixtures/atelier-capability-contract/release/valid/document.json +84 -0
- package/fixtures/atelier-capability-contract/state/invalid/document.json +316 -0
- package/fixtures/atelier-capability-contract/state/valid/document.json +315 -0
- package/fixtures/atelier-coordination/dependency/invalid/document.json +37 -0
- package/fixtures/atelier-coordination/dependency/valid/document.json +36 -0
- package/fixtures/atelier-coordination/directive/invalid/document.json +25 -0
- package/fixtures/atelier-coordination/directive/valid/document.json +24 -0
- package/fixtures/atelier-coordination/disposition/invalid/document.json +34 -0
- package/fixtures/atelier-coordination/disposition/valid/document.json +33 -0
- package/fixtures/atelier-coordination/outcome/invalid/document.json +39 -0
- package/fixtures/atelier-coordination/outcome/valid/document.json +38 -0
- package/fixtures/atelier-coordination/recovery/invalid/document.json +34 -0
- package/fixtures/atelier-coordination/recovery/valid/document.json +33 -0
- package/fixtures/atelier-harness-contract/handoff/invalid/document.json +25 -0
- package/fixtures/atelier-harness-contract/handoff/valid/document.json +24 -0
- package/fixtures/atelier-ingestion/invalid/authority.json +19 -0
- package/fixtures/atelier-ingestion/semantic-baseline.json +26 -0
- package/fixtures/atelier-ingestion/semantic-operation.json +75 -0
- package/fixtures/atelier-ingestion/valid/plan-input.json +18 -0
- package/fixtures/atelier-inquiry-contract/assessment/invalid/document.json +41 -0
- package/fixtures/atelier-inquiry-contract/assessment/valid/document.json +40 -0
- package/fixtures/atelier-inquiry-contract/bundle/invalid/document.json +51 -0
- package/fixtures/atelier-inquiry-contract/bundle/valid/document.json +50 -0
- package/fixtures/atelier-inquiry-contract/campaign/invalid/document.json +17 -0
- package/fixtures/atelier-inquiry-contract/campaign/valid/document.json +16 -0
- package/fixtures/atelier-inquiry-contract/decision/invalid/document.json +22 -0
- package/fixtures/atelier-inquiry-contract/decision/valid/document.json +21 -0
- package/fixtures/atelier-inquiry-contract/feedback/invalid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/feedback/valid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/handoff/invalid/document.json +92 -0
- package/fixtures/atelier-inquiry-contract/handoff/valid/document.json +91 -0
- package/fixtures/atelier-inquiry-contract/hypothesis/invalid/document.json +20 -0
- package/fixtures/atelier-inquiry-contract/hypothesis/valid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/ledger/invalid/document.json +680 -0
- package/fixtures/atelier-inquiry-contract/ledger/valid/document.json +679 -0
- package/fixtures/atelier-inquiry-contract/legacy-assessment/invalid/document.json +19 -0
- package/fixtures/atelier-inquiry-contract/legacy-assessment/valid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/request/invalid/document.json +50 -0
- package/fixtures/atelier-inquiry-contract/request/valid/document.json +49 -0
- package/fixtures/atelier-inquiry-contract/source/invalid/document.json +18 -0
- package/fixtures/atelier-inquiry-contract/source/valid/document.json +17 -0
- package/fixtures/atelier-inquiry-contract/withdrawal/invalid/document.json +16 -0
- package/fixtures/atelier-inquiry-contract/withdrawal/valid/document.json +15 -0
- package/fixtures/atelier-instruction-adoption/context-receipt/invalid/document.json +18 -0
- package/fixtures/atelier-instruction-adoption/context-receipt/valid/document.json +17 -0
- package/fixtures/atelier-instruction-adoption/plan/invalid/document.json +63 -0
- package/fixtures/atelier-instruction-adoption/plan/valid/document.json +62 -0
- package/fixtures/atelier-instruction-adoption/receipt/invalid/document.json +22 -0
- package/fixtures/atelier-instruction-adoption/receipt/valid/document.json +21 -0
- package/fixtures/atelier-instruction-adoption/state/invalid/document.json +30 -0
- package/fixtures/atelier-instruction-adoption/state/valid/document.json +29 -0
- package/fixtures/atelier-interaction/act/invalid/document.json +25 -0
- package/fixtures/atelier-interaction/act/valid/document.json +24 -0
- package/fixtures/atelier-interaction/assessment/invalid/document.json +32 -0
- package/fixtures/atelier-interaction/assessment/valid/document.json +31 -0
- package/fixtures/atelier-interaction/observation/invalid/document.json +22 -0
- package/fixtures/atelier-interaction/observation/valid/document.json +21 -0
- package/fixtures/atelier-interaction/policy/invalid/document.json +20 -0
- package/fixtures/atelier-interaction/policy/valid/document.json +19 -0
- package/fixtures/atelier-judgment/case/invalid/document.json +76 -0
- package/fixtures/atelier-judgment/case/valid/document.json +75 -0
- package/fixtures/atelier-judgment/decision/invalid/document.json +38 -0
- package/fixtures/atelier-judgment/decision/valid/document.json +37 -0
- package/fixtures/atelier-judgment/native-decision.md +3 -0
- package/fixtures/atelier-judgment/practice-reconsideration/definition.json +92 -0
- package/fixtures/atelier-judgment/practice-reconsideration/scenario.json +435 -0
- package/fixtures/atelier-knowledge-contract/activation/invalid/document.json +26 -0
- package/fixtures/atelier-knowledge-contract/activation/valid/document.json +25 -0
- package/fixtures/atelier-knowledge-contract/contribution/invalid/document.json +34 -0
- package/fixtures/atelier-knowledge-contract/contribution/valid/document.json +33 -0
- package/fixtures/atelier-knowledge-contract/domain/invalid/document.json +41 -0
- package/fixtures/atelier-knowledge-contract/domain/valid/document.json +40 -0
- package/fixtures/atelier-knowledge-contract/domain-revision/invalid/document.json +46 -0
- package/fixtures/atelier-knowledge-contract/domain-revision/valid/document.json +45 -0
- package/fixtures/atelier-knowledge-contract/evaluation/invalid/document.json +21 -0
- package/fixtures/atelier-knowledge-contract/evaluation/valid/document.json +20 -0
- package/fixtures/atelier-knowledge-contract/ledger/invalid/document.json +318 -0
- package/fixtures/atelier-knowledge-contract/ledger/valid/document.json +317 -0
- package/fixtures/atelier-knowledge-contract/relation/invalid/document.json +25 -0
- package/fixtures/atelier-knowledge-contract/relation/valid/document.json +24 -0
- package/fixtures/atelier-knowledge-contract/review/invalid/document.json +18 -0
- package/fixtures/atelier-knowledge-contract/review/valid/document.json +17 -0
- package/fixtures/atelier-knowledge-contract/withdrawal/invalid/document.json +16 -0
- package/fixtures/atelier-knowledge-contract/withdrawal/valid/document.json +15 -0
- package/fixtures/atelier-learning/invalid/authority.json +14 -0
- package/fixtures/atelier-learning/valid/capture.json +13 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/authority-key.json +17 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/nonempty-ext.json +25 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/invalid/wrong-schema.json +22 -0
- package/fixtures/atelier-personal-workspace-contract/manifest/valid/manifest.json +22 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/authority-key.json +25 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/unknown-top-level-field.json +27 -0
- package/fixtures/atelier-personal-workspace-contract/overlay/invalid/wrong-schema.json +24 -0
- package/fixtures/atelier-trackable/definition/invalid/document.json +15 -0
- package/fixtures/atelier-trackable/definition/valid/document.json +14 -0
- package/fixtures/atelier-trackable/request/invalid/document.json +23 -0
- package/fixtures/atelier-trackable/request/valid/document.json +22 -0
- package/fixtures/boundary-delegations/invalid/authority-key.v1.json +30 -0
- package/fixtures/boundary-delegations/invalid/push-operation.v1.json +28 -0
- package/fixtures/boundary-delegations/invalid/unsigned.v1.json +25 -0
- package/fixtures/boundary-delegations/valid/empty.v1.json +4 -0
- package/fixtures/boundary-delegations/valid/signed-operator.v1.json +30 -0
- package/fixtures/boundary-owner-keys/invalid/wrong-algorithm.v1.json +15 -0
- package/fixtures/boundary-owner-keys/invalid/wrong-curve.v1.json +15 -0
- package/fixtures/boundary-owner-keys/valid/one-owner.v1.json +17 -0
- package/fixtures/capability-packages/adoption.example.json +70 -0
- package/fixtures/capability-packages/evidence-review/capability-package.json +58 -0
- package/fixtures/capability-packages/evidence-review/capability-release.json +84 -0
- package/fixtures/capability-packages/evidence-review/evaluation.md +15 -0
- package/fixtures/capability-packages/evidence-review/skills/evidence-review/SKILL.md +22 -0
- package/fixtures/capability-packages/evidence-review/skills/evidence-review/references/ledger.md +7 -0
- package/fixtures/capability-packages/research/capability-package.json +88 -0
- package/fixtures/capability-packages/research/capability-release.json +134 -0
- package/fixtures/capability-packages/research/evaluation.md +15 -0
- package/fixtures/capability-packages/research/skills/research-integrate/SKILL.md +29 -0
- package/fixtures/capability-packages/research/skills/research-integrate/references/integration.md +10 -0
- package/fixtures/capability-packages/research/skills/research-plan/SKILL.md +27 -0
- package/fixtures/capability-packages/research/skills/research-plan/references/inquiries.md +26 -0
- package/fixtures/capability-packages/research/skills/research-synthesize/SKILL.md +23 -0
- package/fixtures/capability-packages/research/skills/research-synthesize/references/synthesis.md +12 -0
- package/fixtures/evidence-navigation/invalid/duplicate-operation.json +16 -0
- package/fixtures/evidence-navigation/invalid/instruction-kind.json +36 -0
- package/fixtures/evidence-navigation/invalid/invalid-digest.json +36 -0
- package/fixtures/evidence-navigation/invalid/invalid-time.json +36 -0
- package/fixtures/evidence-navigation/invalid/missing-attribution.json +35 -0
- package/fixtures/evidence-navigation/invalid/reserved-extension.json +19 -0
- package/fixtures/evidence-navigation/invalid/unattributed-acceptance.json +36 -0
- package/fixtures/evidence-navigation/invalid/uncoerced-version.json +16 -0
- package/fixtures/evidence-navigation/invalid/unknown-currency.json +17 -0
- package/fixtures/evidence-navigation/invalid/unknown-selector-field.json +37 -0
- package/fixtures/evidence-navigation/invalid/unsupported-operation.json +15 -0
- package/fixtures/evidence-navigation/valid/claim.json +36 -0
- package/fixtures/evidence-navigation/valid/host.json +16 -0
- package/fixtures/evidence-navigation/valid/profile.json +16 -0
- package/fixtures/evidence-navigation/valid/snapshot.json +17 -0
- package/fixtures/harness-packages/build-harness/capability-package.json +61 -0
- package/fixtures/harness-packages/build-harness/capability-release.json +82 -0
- package/fixtures/harness-packages/build-harness/evaluation.md +3 -0
- package/fixtures/harness-packages/build-harness/skills/atelier-build-harness/SKILL.md +59 -0
- package/fixtures/harness-packages/knowledge-harness/capability-package.json +61 -0
- package/fixtures/harness-packages/knowledge-harness/capability-release.json +82 -0
- package/fixtures/harness-packages/knowledge-harness/evaluation.md +3 -0
- package/fixtures/harness-packages/knowledge-harness/skills/atelier-knowledge-harness/SKILL.md +60 -0
- package/fixtures/harnesses/learning-cycle.json +780 -0
- package/fixtures/inquiry/workshop.json +675 -0
- package/fixtures/inquiry-packages/discovery-harness/capability-package.json +61 -0
- package/fixtures/inquiry-packages/discovery-harness/capability-release.json +82 -0
- package/fixtures/inquiry-packages/discovery-harness/evaluation.md +3 -0
- package/fixtures/inquiry-packages/discovery-harness/skills/atelier-discovery-harness/SKILL.md +48 -0
- package/fixtures/inquiry-packages/research-harness/capability-package.json +61 -0
- package/fixtures/inquiry-packages/research-harness/capability-release.json +82 -0
- package/fixtures/inquiry-packages/research-harness/evaluation.md +3 -0
- package/fixtures/inquiry-packages/research-harness/skills/atelier-research-harness/SKILL.md +43 -0
- package/fixtures/knowledge-health/calibration-suite.json +26 -0
- package/fixtures/knowledge-health/calibration-trial.json +37 -0
- package/fixtures/knowledge-health/corrected/checklist.md +18 -0
- package/fixtures/knowledge-health/devday/participant-record.json +19 -0
- package/fixtures/knowledge-health/expected-summary.json +27 -0
- package/fixtures/knowledge-health/workshop/README.md +20 -0
- package/fixtures/knowledge-health/workshop/atelier.project.json +7 -0
- package/fixtures/knowledge-health/workshop/knowledge-plan.json +65 -0
- package/fixtures/knowledge-health/workshop/records/checklist.md +18 -0
- package/fixtures/knowledge-health/workshop/records/workshop.md +18 -0
- package/fixtures/knowledge-health/workshop/repo-access.v1.json +5 -0
- package/fixtures/knowledge-health/workspace/atelier.project.json +18 -0
- package/fixtures/knowledge-health/workspace/knowledge-plan.json +74 -0
- package/fixtures/knowledge-health/workspace/records/checklist.md +18 -0
- package/fixtures/knowledge-health/workspace/records/pilot.md +16 -0
- package/fixtures/knowledge-health/workspace/repo-access.v1.json +9 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/case-only-collision.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/flat-note-path.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/hidden-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/identity-in-the-body.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/link-breaking-name.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-identity-region.v2.json +156 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-layout-version.v2.json +159 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/note-named-like-a-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/unknown-region-kind.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/v1-contract-version.v2.json +161 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/valid/three-notes.v2.json +160 -0
- package/fixtures/obsidian/edits/workspace.json +1 -1
- package/fixtures/obsidian/materialization/expected-full.json +21 -21
- package/fixtures/obsidian/materialization/expected-full.layout-1.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +7 -7
- package/fixtures/obsidian/materialization/expected-scoped.layout-1.json +39 -0
- package/fixtures/personal-workspace/overlay.json +24 -0
- package/fixtures/templates/local-library.v1.json +75 -0
- package/fixtures/templates/upgrade-contracts/adoption-policy.v2/invalid.json +16 -0
- package/fixtures/templates/upgrade-contracts/adoption-policy.v2/valid.json +13 -0
- package/fixtures/templates/upgrade-contracts/migration.v3/invalid.json +24 -0
- package/fixtures/templates/upgrade-contracts/migration.v3/valid.json +21 -0
- package/fixtures/templates/upgrade-contracts/template-adoption.v1/invalid.json +60 -0
- package/fixtures/templates/upgrade-contracts/template-adoption.v1/valid.json +57 -0
- package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/invalid.json +85 -0
- package/fixtures/templates/upgrade-contracts/upgrade-plan.v3/valid.json +82 -0
- package/package.json +64 -6
- package/plugins/obsidian/main.js +444 -0
- package/plugins/obsidian/manifest.json +9 -0
- package/plugins/obsidian/styles.css +22 -0
- package/skills/claude/atelier-build-harness/SKILL.md +59 -0
- package/skills/claude/atelier-coordination/SKILL.md +32 -0
- package/skills/claude/atelier-discovery-harness/SKILL.md +48 -0
- package/skills/claude/atelier-guided-upgrade/SKILL.md +29 -0
- package/skills/claude/atelier-interaction/SKILL.md +34 -0
- package/skills/claude/atelier-knowledge-harness/SKILL.md +60 -0
- package/skills/claude/atelier-knowledge-setup/SKILL.md +72 -0
- package/skills/claude/atelier-practical-judgment/SKILL.md +64 -0
- package/skills/claude/atelier-research-harness/SKILL.md +43 -0
- package/skills/claude/atelier-skill-steward/SKILL.md +53 -0
- package/skills/claude/atelier-trackables/SKILL.md +40 -0
- package/skills/codex/atelier-build-harness/SKILL.md +59 -0
- package/skills/codex/atelier-coordination/SKILL.md +32 -0
- package/skills/codex/atelier-discovery-harness/SKILL.md +48 -0
- package/skills/codex/atelier-guided-upgrade/SKILL.md +29 -0
- package/skills/codex/atelier-interaction/SKILL.md +34 -0
- package/skills/codex/atelier-knowledge-harness/SKILL.md +60 -0
- package/skills/codex/atelier-knowledge-setup/SKILL.md +72 -0
- package/skills/codex/atelier-practical-judgment/SKILL.md +64 -0
- package/skills/codex/atelier-research-harness/SKILL.md +43 -0
- package/skills/codex/atelier-skill-steward/SKILL.md +53 -0
- package/skills/codex/atelier-trackables/SKILL.md +40 -0
- package/src/architecture/catalog.mjs +355 -0
- package/src/architecture/index.mjs +93 -0
- package/src/boundary/content-rules.mjs +4 -2
- package/src/boundary/delegation-command.mjs +158 -0
- package/src/boundary/delegation.mjs +396 -0
- package/src/boundary/policy.mjs +52 -10
- package/src/build/git.mjs +18 -0
- package/src/build/index.mjs +3 -0
- package/src/build/ledger.mjs +88 -0
- package/src/capabilities/adoption.mjs +381 -0
- package/src/capabilities/evidence.mjs +140 -0
- package/src/capabilities/files.mjs +124 -0
- package/src/capabilities/index.mjs +4 -0
- package/src/capabilities/instructions.mjs +250 -0
- package/src/capabilities/package.mjs +136 -0
- package/src/cli/command-failure.mjs +36 -0
- package/src/cli/execute-command.mjs +16 -2
- package/src/cli/run.mjs +65 -9
- package/src/coauthor/store.mjs +43 -2
- package/src/collaboration/proposals.mjs +189 -0
- package/src/commands/architecture.mjs +27 -0
- package/src/commands/boundary.mjs +3 -0
- package/src/commands/capability.mjs +73 -0
- package/src/commands/enroll.mjs +3 -0
- package/src/commands/harness.mjs +79 -0
- package/src/commands/ingest.mjs +39 -0
- package/src/commands/init.mjs +4 -1
- package/src/commands/inquiry.mjs +66 -0
- package/src/commands/knowledge-semantic.mjs +128 -0
- package/src/commands/knowledge.mjs +119 -0
- package/src/commands/learn.mjs +61 -0
- package/src/commands/obsidian.mjs +832 -62
- package/src/commands/practice.mjs +24 -0
- package/src/commands/setup.mjs +22 -2
- package/src/commands/trackable.mjs +19 -0
- package/src/contracts/corpus.mjs +36 -0
- package/src/coordination/index.mjs +110 -0
- package/src/egress/forbidden-egress.mjs +2 -0
- package/src/evidence-navigation/contracts.mjs +60 -0
- package/src/evidence-navigation/evaluate.mjs +69 -0
- package/src/evidence-navigation/generation.mjs +22 -0
- package/src/evidence-navigation/index.mjs +2 -0
- package/src/evidence-navigation/local.mjs +167 -0
- package/src/evidence-navigation/schema.generated.mjs +604 -0
- package/src/evidence-navigation/types.mjs +12 -0
- package/src/export/atelier-export-contract.mjs +35 -8
- package/src/graph/enroll.mjs +248 -0
- package/src/graph/graph.mjs +17 -1
- package/src/graph/knowledge-graph.mjs +58 -8
- package/src/harnesses/contracts.mjs +41 -0
- package/src/harnesses/exchange.mjs +154 -0
- package/src/harnesses/feedback.mjs +16 -0
- package/src/harnesses/history.mjs +37 -0
- package/src/harnesses/index.mjs +4 -0
- package/src/harnesses/store.mjs +29 -0
- package/src/ingestion/contracts.mjs +41 -0
- package/src/ingestion/evaluation.mjs +119 -0
- package/src/ingestion/optional/composition/index.mjs +171 -0
- package/src/ingestion/optional/evaluation/index.mjs +219 -0
- package/src/ingestion/optional/graphify/composition-binding.mjs +60 -0
- package/src/ingestion/optional/graphify/composition_receiving.py +185 -0
- package/src/ingestion/optional/graphify/graphify_supplier.py +933 -0
- package/src/ingestion/optional/graphify/json_bridge.py +163 -0
- package/src/ingestion/optional/graphify/query_receiving.py +209 -0
- package/src/ingestion/optional/index.mjs +9 -0
- package/src/ingestion/optional/jev/bridge.mjs +166 -0
- package/src/ingestion/optional/jev/composition-binding.mjs +116 -0
- package/src/ingestion/optional/jev/judgments.mjs +174 -0
- package/src/ingestion/optional/jev/native-translation.mjs +32 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/__init__.py +7 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/adapter.py +966 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/artifacts.py +101 -0
- package/src/ingestion/optional/lightrag/atelier_lightrag_supplier/projection.py +146 -0
- package/src/ingestion/optional/lightrag/composition-binding.mjs +67 -0
- package/src/ingestion/optional/vanilla/composition-binding.mjs +102 -0
- package/src/ingestion/optional/vanilla/index.mjs +218 -0
- package/src/ingestion/optional/vanilla/native-normalization.mjs +187 -0
- package/src/ingestion/processors.mjs +218 -0
- package/src/ingestion/semantic.mjs +203 -0
- package/src/ingestion/store.mjs +372 -0
- package/src/inquiry/beliefs.mjs +32 -0
- package/src/inquiry/contracts.mjs +23 -0
- package/src/inquiry/index.mjs +5 -0
- package/src/inquiry/ledger.mjs +135 -0
- package/src/inquiry/lenses.mjs +15 -0
- package/src/inquiry/projection.mjs +77 -0
- package/src/inquiry/store.mjs +35 -0
- package/src/intake/read-scope.mjs +12 -0
- package/src/intake/store.mjs +69 -6
- package/src/interaction/index.mjs +91 -0
- package/src/judgment/index.mjs +82 -0
- package/src/judgment/practice-evaluation.mjs +70 -0
- package/src/judgment/practice.mjs +144 -0
- package/src/knowledge/context.mjs +217 -0
- package/src/knowledge/index.mjs +5 -0
- package/src/knowledge/ingestion.mjs +79 -0
- package/src/knowledge/intake.mjs +33 -0
- package/src/knowledge/ledger.mjs +110 -0
- package/src/knowledge/participatory/guidance.mjs +36 -0
- package/src/knowledge/participatory/guided-contribution.mjs +44 -0
- package/src/knowledge/participatory/index.mjs +2 -0
- package/src/knowledge/participatory/installed-workshop.mjs +108 -0
- package/src/knowledge/plan.mjs +67 -0
- package/src/knowledge/projection.mjs +43 -0
- package/src/knowledge/semantic-operation.mjs +567 -0
- package/src/knowledge/sessions.mjs +467 -0
- package/src/knowledge/workspace.mjs +295 -0
- package/src/knowledge-health/assessment.mjs +165 -0
- package/src/knowledge-health/capabilities.mjs +350 -0
- package/src/knowledge-health/context.mjs +62 -0
- package/src/knowledge-health/example.mjs +47 -0
- package/src/knowledge-health/index.mjs +10 -0
- package/src/knowledge-health/owner-join.mjs +88 -0
- package/src/knowledge-health/proof/common.mjs +24 -0
- package/src/knowledge-health/proof/installed-check.mjs +100 -0
- package/src/knowledge-health/proof/native-readback.mjs +37 -0
- package/src/knowledge-health/proof/rehearse.mjs +68 -0
- package/src/knowledge-health/proof/validate-participant.mjs +17 -0
- package/src/knowledge-health/proof/verify-native-capture.mjs +14 -0
- package/src/knowledge-health/proof/workshop-check.mjs +36 -0
- package/src/knowledge-health/rule-identity.mjs +44 -0
- package/src/knowledge-health/workshop-composition.mjs +19 -0
- package/src/knowledge-health/workshop-profile.mjs +176 -0
- package/src/knowledge-health/workshop.mjs +99 -0
- package/src/learning/adapters.mjs +68 -0
- package/src/learning/assistance.mjs +127 -0
- package/src/learning/contracts.mjs +65 -0
- package/src/learning/store.mjs +272 -0
- package/src/personal-workspace/index.mjs +391 -0
- package/src/personal-workspace/selection.mjs +385 -0
- package/src/project/config.mjs +8 -0
- package/src/project/durable-state.mjs +3 -1
- package/src/project/file-class.mjs +1 -1
- package/src/project/private-state.mjs +10 -1
- package/src/projection/obsidian/contracts.mjs +224 -18
- package/src/projection/obsidian/edits/apply.mjs +63 -19
- package/src/projection/obsidian/edits/regions.mjs +137 -42
- package/src/projection/obsidian/materialize/index.mjs +6 -3
- package/src/projection/obsidian/materialize/path-registry.mjs +443 -57
- package/src/projection/obsidian/materialize/prepare-view.mjs +382 -131
- package/src/projection/obsidian/materialize/redaction.mjs +327 -0
- package/src/projection/obsidian/materialize/settings.mjs +113 -34
- package/src/projection/obsidian/personal-workspace.mjs +436 -0
- package/src/projection/obsidian/plugin-bridge/bundle.mjs +50 -0
- package/src/projection/obsidian/plugin-bridge/channel.mjs +174 -0
- package/src/projection/obsidian/plugin-bridge/index.mjs +10 -0
- package/src/projection/obsidian/proposals/adapter.mjs +47 -8
- package/src/projection/obsidian/proposals/observation.mjs +20 -7
- package/src/projection/obsidian/publication/bridge-script.mjs +11 -5
- package/src/projection/obsidian/publication/index.mjs +2 -1
- package/src/projection/obsidian/publication/publisher.mjs +312 -36
- package/src/projection/obsidian/publication/transport.mjs +63 -8
- package/src/projection/obsidian/publication/vault-list.mjs +200 -0
- package/src/projection/obsidian/recovery/index.mjs +4 -1
- package/src/projection/obsidian/recovery/restart.mjs +3 -1
- package/src/projection/obsidian/recovery/store.mjs +168 -8
- package/src/projection/obsidian/selection-ui/contribution.mjs +6 -5
- package/src/projection/obsidian/selection-ui/focus.mjs +14 -7
- package/src/projection/obsidian/selection-ui/selection-state.mjs +2 -2
- package/src/projection/obsidian/selection-ui/selection.mjs +8 -3
- package/src/readiness/readiness.mjs +10 -3
- package/src/reflection/index.mjs +56 -0
- package/src/runtime/obsidian/app-capability.mjs +104 -12
- package/src/runtime/obsidian/app-production-seams.mjs +161 -42
- package/src/runtime/obsidian/app-registration.mjs +313 -0
- package/src/runtime/obsidian/app-restart.mjs +154 -0
- package/src/runtime/obsidian/engine.mjs +288 -29
- package/src/runtime/obsidian/extension-points.mjs +1 -1
- package/src/runtime/obsidian/index.mjs +15 -8
- package/src/runtime/obsidian/launch-plan.mjs +91 -0
- package/src/runtime/obsidian/lifecycle.mjs +193 -22
- package/src/runtime/obsidian/login-item.mjs +332 -0
- package/src/runtime/obsidian/machine-settings.mjs +119 -12
- package/src/runtime/obsidian/opening.mjs +369 -63
- package/src/runtime/obsidian/pending-edits.mjs +10 -0
- package/src/runtime/obsidian/pipeline.mjs +71 -3
- package/src/runtime/obsidian/plugin-channel.mjs +289 -0
- package/src/runtime/obsidian/plugin-choice.mjs +182 -0
- package/src/runtime/obsidian/plugin-drift.mjs +124 -0
- package/src/runtime/obsidian/plugin-presence.mjs +61 -0
- package/src/runtime/obsidian/process-identity.mjs +20 -10
- package/src/runtime/obsidian/project-views.mjs +282 -0
- package/src/runtime/obsidian/questions.mjs +48 -0
- package/src/runtime/obsidian/release-watch.mjs +56 -0
- package/src/runtime/obsidian/service-main.mjs +113 -17
- package/src/runtime/obsidian/service-manager-production.mjs +61 -0
- package/src/runtime/obsidian/service-managers.mjs +275 -0
- package/src/runtime/obsidian/service-record.mjs +105 -5
- package/src/runtime/obsidian/service-server.mjs +50 -12
- package/src/runtime/obsidian/service.mjs +134 -7
- package/src/runtime/obsidian/startup-adapters.mjs +59 -21
- package/src/runtime/obsidian/state-store.mjs +13 -1
- package/src/runtime/obsidian/unheld-evidence.mjs +84 -0
- package/src/runtime/obsidian/vault-location.mjs +300 -0
- package/src/runtime/obsidian/view-counts.mjs +34 -0
- package/src/server/local-sidecar.mjs +193 -7
- package/src/server/server.mjs +45 -6
- package/src/template-bindings/project.mjs +92 -0
- package/src/templates/profile.mjs +225 -0
- package/src/trackables/domain.mjs +175 -0
- package/src/trackables/store.mjs +114 -0
- package/src/ui/knowledge-page.mjs +1388 -0
- package/src/upgrade/explanation.mjs +1 -1
- package/src/upgrade/template-participant.mjs +171 -0
- package/src/upgrade/transaction.mjs +72 -25
- package/src/upgrade/upgrade.mjs +2 -1
- package/templates/external-project-workspace/pack-lifecycle.json +6 -1
- package/templates/knowledge-workspace/.gitattributes +2 -0
- package/templates/knowledge-workspace/AGENTS.md +27 -0
- package/templates/knowledge-workspace/README.md +44 -0
- package/templates/knowledge-workspace/atelier.project.json +22 -0
- package/templates/knowledge-workspace/gitignore +6 -0
- package/templates/knowledge-workspace/knowledge-plan.json +111 -0
- package/templates/knowledge-workspace/knowledge-plan.schema.json +281 -0
- package/templates/knowledge-workspace/records/forecast.md +22 -0
- package/templates/knowledge-workspace/records/inspection.md +25 -0
- package/templates/knowledge-workspace/records/telescope.md +24 -0
- package/templates/knowledge-workspace/repo-access.v1.json +9 -0
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
"""Host-injected async LightRAG supplier, bound to the inspected Python SDK.
|
|
2
|
+
|
|
3
|
+
No SDK import, model client, network transport, credential lookup or canonical
|
|
4
|
+
write lives here. Use one instance/loop/workspace per derived generation.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import copy
|
|
10
|
+
import inspect
|
|
11
|
+
import re
|
|
12
|
+
import time
|
|
13
|
+
from .artifacts import digest, encoded, portable
|
|
14
|
+
from .projection import build_projection
|
|
15
|
+
|
|
16
|
+
UPSTREAM_COMMIT = "453dce83d6d0354a06e46c8d4029a0895c4e054b"
|
|
17
|
+
QUERY_FIELDS = frozenset({"mode", "only_need_context", "only_need_prompt", "response_type",
|
|
18
|
+
"stream", "top_k", "chunk_top_k", "max_entity_tokens", "max_relation_tokens",
|
|
19
|
+
"max_total_tokens", "hl_keywords", "ll_keywords", "conversation_history", "user_prompt",
|
|
20
|
+
"enable_rerank", "include_references", "disable_user_prompt_prefix"})
|
|
21
|
+
ENQUEUE_FIELDS = frozenset({"input", "ids", "file_paths", "track_id", "docs_format",
|
|
22
|
+
"parse_engine", "process_options", "chunk_options", "admission_token", "from_scan"})
|
|
23
|
+
ROLES = frozenset({"EXTRACT", "KEYWORD", "QUERY", "VLM", "EMBEDDING", "RERANK"})
|
|
24
|
+
CAPABILITIES = {
|
|
25
|
+
"document-pipeline": ["apipeline_enqueue_documents", "apipeline_process_enqueue_documents"],
|
|
26
|
+
"fixed-token-insert": ["ainsert"],
|
|
27
|
+
"accepted-projection": ["ainsert_custom_kg"],
|
|
28
|
+
"retrieval": ["aquery_data"], "native-answer": ["aquery_llm"],
|
|
29
|
+
"exploration": ["get_graph_labels", "get_knowledge_graph", "get_entity_info", "get_relation_info"],
|
|
30
|
+
"draft-edit": ["aedit_entity", "aedit_relation", "acreate_entity", "acreate_relation",
|
|
31
|
+
"amerge_entities", "adelete_by_entity", "adelete_by_relation"],
|
|
32
|
+
"document-maintenance": ["get_processing_status", "aget_docs_by_track_id", "aget_docs_by_ids",
|
|
33
|
+
"adelete_by_doc_id"],
|
|
34
|
+
"native-export": ["aexport_data"],
|
|
35
|
+
"storage-lifecycle": ["initialize_storages", "finalize_storages"],
|
|
36
|
+
"host-bindings": ["graceful-pipeline-cancel", "final-multimodal-readback", "source-eligibility",
|
|
37
|
+
"projection-readback", "generation-cleanup", "vector-rebuild",
|
|
38
|
+
"canonical-citation-hydration", "canonical-citation-eligibility",
|
|
39
|
+
"parser-services", "sdk-construction", "native-server-ui"],
|
|
40
|
+
}
|
|
41
|
+
# Metadata describing native settings, never provider configuration itself.
|
|
42
|
+
SETTINGS_FAMILIES = {
|
|
43
|
+
"storage": ["working_dir", "workspace", "kv_storage", "vector_storage", "graph_storage",
|
|
44
|
+
"doc_status_storage", "vector_db_storage_cls_kwargs", "pipeline_require_strict_storage_reads"],
|
|
45
|
+
"models": ["role_llm_configs", "llm_model_func", "embedding_func", "rerank_model_func",
|
|
46
|
+
"llm_model_kwargs", "llm_model_max_async", "default_llm_timeout"],
|
|
47
|
+
"parse-and-chunk": ["chunking_func", "chunk_token_size", "chunk_overlap_token_size",
|
|
48
|
+
"max_parallel_parse_native", "max_parallel_parse_mineru", "max_parallel_parse_docling",
|
|
49
|
+
"addon_params", "max_parallel_analyze", "queue_size_parse"],
|
|
50
|
+
"extraction": ["kg_extraction_validator", "max_parallel_insert", "force_llm_summary_on_merge",
|
|
51
|
+
"enable_llm_cache_for_entity_extract", "enable_llm_cache"],
|
|
52
|
+
"embedding-and-rerank": ["embedding_batch_num", "embedding_func_max_async", "embedding_cache_config",
|
|
53
|
+
"default_embedding_timeout", "rerank_model_max_async", "default_rerank_timeout",
|
|
54
|
+
"min_rerank_score"],
|
|
55
|
+
"graph": ["max_graph_nodes", "related_chunk_number", "kg_chunk_pick_method"],
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class SupplierError(Exception):
|
|
60
|
+
def __init__(self, code, message, *, receipt=None):
|
|
61
|
+
super().__init__(message)
|
|
62
|
+
self.code, self.receipt = code, receipt
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
async def called(function, *args, **kwargs):
|
|
66
|
+
value = function(*args, **kwargs)
|
|
67
|
+
return await value if inspect.isawaitable(value) else value
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def required(condition, code, message):
|
|
71
|
+
if not condition:
|
|
72
|
+
raise SupplierError(code, message)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def usage_report(usage):
|
|
76
|
+
fields = {"inputTokens", "outputTokens", "cost", "currency", "elapsedMs", "retries"}
|
|
77
|
+
value = {field: None for field in fields}
|
|
78
|
+
if usage is not None:
|
|
79
|
+
required(isinstance(usage, dict) and set(usage) <= fields, "USAGE_INVALID", "Declared usage fields required")
|
|
80
|
+
value.update(usage)
|
|
81
|
+
for field in ["inputTokens", "outputTokens", "elapsedMs", "retries"]:
|
|
82
|
+
required(value[field] is None or (type(value[field]) is int and 0 <= value[field] <= 9007199254740991),
|
|
83
|
+
"USAGE_INVALID", "Counts must be nonnegative safe integers or unknown")
|
|
84
|
+
cost = value["cost"]
|
|
85
|
+
required(cost is None or (type(cost) in {int, float} and cost >= 0), "USAGE_INVALID", "Cost is unknown or nonnegative")
|
|
86
|
+
required(value["currency"] is None or re.fullmatch(r"[A-Z]{3}", value["currency"]) is not None,
|
|
87
|
+
"USAGE_INVALID", "An explicit currency or unknown is required")
|
|
88
|
+
portable(value)
|
|
89
|
+
return value
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class LightRAGSupplier:
|
|
93
|
+
def __init__(self, *, native=None, native_factory=None, query_param_factory, artifacts, generation_id, workspace,
|
|
94
|
+
source_bindings, configuration, eligibility, readback, verify_readback,
|
|
95
|
+
document_mapper=None, final_reconciler=None, assessment=None,
|
|
96
|
+
verify_accepted_projection=None, cancel_pipeline=None, reconcile_native=None,
|
|
97
|
+
cleanup_generation=None, rebuild_vectors=None):
|
|
98
|
+
required(bool(generation_id) and bool(workspace), "BINDING_INVALID", "An owned generation/workspace is required")
|
|
99
|
+
required(bool(source_bindings), "BINDING_INVALID", "Existing source bindings are required")
|
|
100
|
+
required((native is None) != (native_factory is None), "BINDING_INVALID", "Inject one native instance or one host-owned factory")
|
|
101
|
+
if native is not None:
|
|
102
|
+
required(getattr(native, "workspace", None) == workspace, "BINDING_INVALID", "Injected native workspace differs")
|
|
103
|
+
self.native, self.query_param_factory, self.artifacts = native, query_param_factory, artifacts
|
|
104
|
+
self.native_factory = native_factory
|
|
105
|
+
self.eligibility, self.readback, self.verify_readback = eligibility, readback, verify_readback
|
|
106
|
+
self.document_mapper, self.final_reconciler, self.assessment = document_mapper, final_reconciler, assessment
|
|
107
|
+
self.verify_accepted_projection = verify_accepted_projection
|
|
108
|
+
self.cancel_pipeline, self.reconcile_native = cancel_pipeline, reconcile_native
|
|
109
|
+
self.cleanup_generation, self.rebuild_vectors = cleanup_generation, rebuild_vectors
|
|
110
|
+
self.generation = {"generationId": generation_id, "workspace": workspace,
|
|
111
|
+
"sourceBindings": portable(source_bindings), "configuration": portable(configuration),
|
|
112
|
+
"configurationDigest": digest(configuration), "upstreamCommit": UPSTREAM_COMMIT,
|
|
113
|
+
"view": None, "state": "new", "publication": None, "trackIds": [], "nativeDocuments": {},
|
|
114
|
+
"projection": None, "readback": None, "losses": [], "canonicalMutation": False,
|
|
115
|
+
"authority": "none", "nativeQualification": "not-established-by-supplier"}
|
|
116
|
+
self.captures, self.filtered_captures, self.usage, self.receipts = [], [], [], []
|
|
117
|
+
self._loop, self._active, self._lock, self._hook_failure = None, None, asyncio.Lock(), None
|
|
118
|
+
self._manifest_ref = None
|
|
119
|
+
# LightRAG dataclasses.asdict deep-copies bound methods and their owner.
|
|
120
|
+
# A plain closure keeps this callback's live supplier/loop custody intact.
|
|
121
|
+
async def native_quality_hook(chunk_key, chunk_text, maybe_nodes, maybe_edges):
|
|
122
|
+
return await self.quality_hook(chunk_key, chunk_text, maybe_nodes, maybe_edges)
|
|
123
|
+
self._native_quality_hook = native_quality_hook
|
|
124
|
+
|
|
125
|
+
def capabilities(self):
|
|
126
|
+
return {"tool": "lightrag", "upstreamCommit": UPSTREAM_COMMIT,
|
|
127
|
+
"native": {family: {method: callable(getattr(self.native, method, None)) for method in methods}
|
|
128
|
+
for family, methods in CAPABILITIES.items() if family != "host-bindings"},
|
|
129
|
+
"hostRequired": CAPABILITIES["host-bindings"], "settingsFamilies": SETTINGS_FAMILIES,
|
|
130
|
+
"queryFields": sorted(QUERY_FIELDS), "canonicalMutation": False}
|
|
131
|
+
|
|
132
|
+
def snapshot(self):
|
|
133
|
+
return portable({"schema": "atelier.private-lightrag-generation/v0", "generation": self.generation,
|
|
134
|
+
"captures": self.captures, "filteredCaptures": self.filtered_captures,
|
|
135
|
+
"usage": self.usage, "receipts": self.receipts})
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def manifest_ref(self):
|
|
139
|
+
return copy.deepcopy(self._manifest_ref)
|
|
140
|
+
|
|
141
|
+
def capture_bundle(self):
|
|
142
|
+
return copy.deepcopy({"unfiltered": self.captures, "filtered": self.filtered_captures,
|
|
143
|
+
"rawProducerEvents": self.usage})
|
|
144
|
+
|
|
145
|
+
def _persist(self):
|
|
146
|
+
self._manifest_ref = self.artifacts.put_json("generation-manifest", self.snapshot())
|
|
147
|
+
return self._manifest_ref
|
|
148
|
+
|
|
149
|
+
def _receipt(self, operation, outcome, **details):
|
|
150
|
+
receipt = portable({"tool": "lightrag", "generationId": self.generation["generationId"],
|
|
151
|
+
"workspace": self.generation["workspace"], "operation": operation, "outcome": outcome,
|
|
152
|
+
"semanticAcceptance": "pending" if self.generation["view"] != "accepted-projection" else "owner-records-only",
|
|
153
|
+
"canonicalMutation": False, "authority": "none", **details})
|
|
154
|
+
self.receipts.append(receipt)
|
|
155
|
+
receipt["artifact"] = self.artifacts.put_json("operation-receipt", receipt)
|
|
156
|
+
self._persist()
|
|
157
|
+
return copy.deepcopy(receipt)
|
|
158
|
+
|
|
159
|
+
def _assert_loop(self):
|
|
160
|
+
required(self._loop is asyncio.get_running_loop(), "LIFECYCLE_INVALID", "Use the storage's original event loop")
|
|
161
|
+
|
|
162
|
+
async def _current(self):
|
|
163
|
+
result = portable(await called(self.eligibility, copy.deepcopy(self.generation)))
|
|
164
|
+
self.artifacts.put_json("eligibility-readback", result)
|
|
165
|
+
required(result.get("current") is True and result.get("sourceBindings") == self.generation["sourceBindings"],
|
|
166
|
+
"GENERATION_STALE", "Current exact eligible source bindings are required")
|
|
167
|
+
if self.generation["projection"]:
|
|
168
|
+
project = self.artifacts.read_json(self.generation["projection"])["canonicalProjection"]
|
|
169
|
+
required(result.get("historyDigest") == project["historyDigest"] and result.get("activation") == project["activation"],
|
|
170
|
+
"GENERATION_STALE", "Accepted projection history/activation changed")
|
|
171
|
+
return result
|
|
172
|
+
|
|
173
|
+
async def initialize(self):
|
|
174
|
+
required(self.generation["state"] == "new", "LIFECYCLE_INVALID", "Initialize once per native generation")
|
|
175
|
+
self._loop = asyncio.get_running_loop()
|
|
176
|
+
self.generation["state"] = "initializing"
|
|
177
|
+
self._persist() # Write intent before touching native storage.
|
|
178
|
+
try:
|
|
179
|
+
await self._current()
|
|
180
|
+
if self.native is None:
|
|
181
|
+
self.native = await called(self.native_factory, configuration=copy.deepcopy(self.generation["configuration"]),
|
|
182
|
+
quality_hook=self._native_quality_hook, instrument_role=self.instrument_role)
|
|
183
|
+
required(getattr(self.native, "workspace", None) == self.generation["workspace"], "BINDING_INVALID", "Host-created native workspace differs")
|
|
184
|
+
existing = getattr(self.native, "kg_extraction_validator", None)
|
|
185
|
+
required(existing is None or existing in (self.quality_hook, self._native_quality_hook), "HOOK_CUSTODY", "An existing native validator must be composed by its host")
|
|
186
|
+
await called(self.native.initialize_storages)
|
|
187
|
+
# The pinned _build_global_config reads this callable dynamically.
|
|
188
|
+
# Host must inject a dedicated native instance; existing hooks cannot be silently replaced.
|
|
189
|
+
self.native.kg_extraction_validator = self._native_quality_hook
|
|
190
|
+
await self._current()
|
|
191
|
+
self.generation["state"] = "initialized"
|
|
192
|
+
return self._receipt("initialize", "complete")
|
|
193
|
+
except BaseException as error:
|
|
194
|
+
self.generation["state"] = "recovery-required"
|
|
195
|
+
receipt = self._receipt("initialize", "execution-unknown", failureType=type(error).__name__)
|
|
196
|
+
if isinstance(error, asyncio.CancelledError):
|
|
197
|
+
raise
|
|
198
|
+
raise SupplierError("INITIALIZE_FAILED", "Native initialization requires reconciliation", receipt=receipt) from error
|
|
199
|
+
|
|
200
|
+
async def finalize(self):
|
|
201
|
+
self._assert_loop()
|
|
202
|
+
required(self._active is None, "OPERATION_BUSY", "Settle the operation before storage finalization")
|
|
203
|
+
async with self._lock:
|
|
204
|
+
required(self._active is None, "OPERATION_BUSY", "Settle the operation before storage finalization")
|
|
205
|
+
if self.generation["state"] == "finalized":
|
|
206
|
+
return self._receipt("finalize", "already-finalized", deletionVerified=False)
|
|
207
|
+
required(self.native is not None and self.generation["state"] not in {"new", "initializing", "removing", "removed"},
|
|
208
|
+
"LIFECYCLE_INVALID", "Storage custody must exist before finalization")
|
|
209
|
+
self._active = "finalize"
|
|
210
|
+
self.generation.update(state="finalizing", publication=None)
|
|
211
|
+
self._persist()
|
|
212
|
+
try:
|
|
213
|
+
await called(self.native.finalize_storages)
|
|
214
|
+
self.generation["state"] = "finalized"
|
|
215
|
+
return self._receipt("finalize", "complete", deletionVerified=False)
|
|
216
|
+
except BaseException as error:
|
|
217
|
+
self.generation["state"] = "recovery-required"
|
|
218
|
+
receipt = self._receipt("finalize", "execution-unknown", failureType=type(error).__name__, deletionVerified=False)
|
|
219
|
+
if isinstance(error, asyncio.CancelledError):
|
|
220
|
+
raise
|
|
221
|
+
raise SupplierError("FINALIZE_FAILED", "Native finalization did not establish cleanup", receipt=receipt) from error
|
|
222
|
+
finally:
|
|
223
|
+
self._active = None
|
|
224
|
+
|
|
225
|
+
async def reopen(self, manifest_ref):
|
|
226
|
+
"""Reopen retained artifacts, never assume a saved intent did not execute."""
|
|
227
|
+
required(self.generation["state"] == "new", "LIFECYCLE_INVALID", "Reopen into a fresh host-owned native instance")
|
|
228
|
+
saved = self.artifacts.read_json(manifest_ref)
|
|
229
|
+
required(saved.get("schema") == "atelier.private-lightrag-generation/v0", "MANIFEST_INVALID", "Supplier manifest required")
|
|
230
|
+
for key in ["generationId", "workspace", "sourceBindings", "configurationDigest", "upstreamCommit"]:
|
|
231
|
+
required(saved["generation"].get(key) == self.generation[key], "MANIFEST_INVALID", "Exact generation/configuration/source binding required")
|
|
232
|
+
self.generation = copy.deepcopy(saved["generation"])
|
|
233
|
+
self.captures, self.usage, self.receipts = saved["captures"], saved["usage"], saved["receipts"]
|
|
234
|
+
self.filtered_captures = saved.get("filteredCaptures", [])
|
|
235
|
+
self.generation.update(state="recovery-required", publication=None)
|
|
236
|
+
self._loop = asyncio.get_running_loop()
|
|
237
|
+
self._persist()
|
|
238
|
+
try:
|
|
239
|
+
if self.native is None:
|
|
240
|
+
self.native = await called(self.native_factory, configuration=copy.deepcopy(self.generation["configuration"]),
|
|
241
|
+
quality_hook=self._native_quality_hook, instrument_role=self.instrument_role)
|
|
242
|
+
required(getattr(self.native, "workspace", None) == self.generation["workspace"], "BINDING_INVALID", "Host-created native workspace differs")
|
|
243
|
+
existing = getattr(self.native, "kg_extraction_validator", None)
|
|
244
|
+
required(existing is None or existing in (self.quality_hook, self._native_quality_hook), "HOOK_CUSTODY", "Host must compose existing hook")
|
|
245
|
+
await called(self.native.initialize_storages)
|
|
246
|
+
self.native.kg_extraction_validator = self._native_quality_hook
|
|
247
|
+
return self._receipt("reopen", "custody-unknown", previousManifest=manifest_ref,
|
|
248
|
+
nextAction="host-reconcile-before-query-resume-or-rebuild")
|
|
249
|
+
except BaseException as error:
|
|
250
|
+
receipt = self._receipt("reopen", "execution-unknown", failureType=type(error).__name__)
|
|
251
|
+
if isinstance(error, asyncio.CancelledError):
|
|
252
|
+
raise
|
|
253
|
+
raise SupplierError("REOPEN_FAILED", "Native storage reopening requires reconciliation", receipt=receipt) from error
|
|
254
|
+
|
|
255
|
+
async def quality_hook(self, chunk_key, chunk_text, maybe_nodes, maybe_edges):
|
|
256
|
+
"""Capture first, assess copies, prune rejected endpoints including implicit nodes."""
|
|
257
|
+
capture = {"chunk_key": chunk_key, "chunk_text": chunk_text,
|
|
258
|
+
"generationId": self.generation["generationId"], "workspace": self.generation["workspace"],
|
|
259
|
+
"sourceBindings": self.generation["sourceBindings"], "configurationDigest": self.generation["configurationDigest"],
|
|
260
|
+
"nodes": copy.deepcopy(maybe_nodes),
|
|
261
|
+
"edges": [{"endpoints": list(key), "records": copy.deepcopy(records)} for key, records in maybe_edges.items()],
|
|
262
|
+
"authority": "none", "stage": "post-native-normalization-and-gleaning-pre-merge",
|
|
263
|
+
"losses": ["Earlier normalization may already have coalesced distinctions",
|
|
264
|
+
"Multimodal additions occur after this hook and require final reconciliation"]}
|
|
265
|
+
try:
|
|
266
|
+
raw = self.artifacts.put_json("unfiltered-chunk-candidates", capture)
|
|
267
|
+
self.captures.append(raw)
|
|
268
|
+
decision = {"rejected_names": [], "rejected_edges": [], "assessments": []}
|
|
269
|
+
if self.assessment is not None:
|
|
270
|
+
decision = portable(await called(self.assessment, copy.deepcopy(capture)))
|
|
271
|
+
required(isinstance(decision, dict) and set(decision) == {"rejected_names", "rejected_edges", "assessments"},
|
|
272
|
+
"HOOK_POLICY_INVALID", "Explicit rejection/assessment fields are required")
|
|
273
|
+
required(isinstance(decision["rejected_names"], list) and all(isinstance(x, str) for x in decision["rejected_names"]),
|
|
274
|
+
"HOOK_POLICY_INVALID", "Native normalized rejected names must be a list")
|
|
275
|
+
required(isinstance(decision["rejected_edges"], list) and all(isinstance(x, list) and len(x) == 2 and
|
|
276
|
+
all(isinstance(n, str) for n in x) for x in decision["rejected_edges"]),
|
|
277
|
+
"HOOK_POLICY_INVALID", "Native rejected edges must identify exact endpoint pairs")
|
|
278
|
+
rejected, edges = set(decision["rejected_names"]), {tuple(x) for x in decision["rejected_edges"]}
|
|
279
|
+
nodes = {name: copy.deepcopy(records) for name, records in maybe_nodes.items() if name not in rejected}
|
|
280
|
+
kept = {key: copy.deepcopy(records) for key, records in maybe_edges.items()
|
|
281
|
+
if not any(endpoint in rejected for endpoint in key) and key not in edges}
|
|
282
|
+
filtered = {"raw": raw, "decision": decision, "nodes": nodes,
|
|
283
|
+
"edges": [{"endpoints": list(key), "records": records} for key, records in kept.items()],
|
|
284
|
+
"implicitEndpoints": sorted({name for key in kept for name in key if name not in nodes}),
|
|
285
|
+
"semanticAcceptance": "pending", "authority": "none"}
|
|
286
|
+
self.filtered_captures.append(self.artifacts.put_json("filtered-chunk-candidates", filtered))
|
|
287
|
+
self._persist()
|
|
288
|
+
return nodes, kept
|
|
289
|
+
except BaseException as error:
|
|
290
|
+
self._hook_failure = {"failureType": type(error).__name__, "chunk_key": chunk_key,
|
|
291
|
+
"rawRetained": len(self.captures)}
|
|
292
|
+
self.artifacts.put_json("hook-failure", self._hook_failure)
|
|
293
|
+
raise
|
|
294
|
+
|
|
295
|
+
def instrument_role(self, role, function, *, model_id, request_binding, usage_reader=None,
|
|
296
|
+
response_serializer=None):
|
|
297
|
+
"""Host chooses/binds actual native model/embedding/rerank callable; result stays native.
|
|
298
|
+
|
|
299
|
+
request_binding must emit permitted prompt/input/source/cache references and
|
|
300
|
+
must exclude transport kwargs/credentials. Unknown usage remains null.
|
|
301
|
+
response_serializer is a host-owned, explicit artifact serializer for
|
|
302
|
+
native arrays/other non-JSON results. It never changes the native return.
|
|
303
|
+
"""
|
|
304
|
+
required(role in ROLES and bool(model_id), "ROLE_INVALID", "Declare a native role and exact model/embedding identity")
|
|
305
|
+
|
|
306
|
+
async def wrapper(*args, **kwargs):
|
|
307
|
+
self._assert_loop()
|
|
308
|
+
required(self.generation["state"] in {"initializing", "initialized", "indexing", "indexed", "published"},
|
|
309
|
+
"GENERATION_INELIGIBLE", "Role execution requires a live eligible native generation")
|
|
310
|
+
await self._current()
|
|
311
|
+
binding = portable(await called(request_binding, args, kwargs))
|
|
312
|
+
request = self.artifacts.put_json("role-request", {"role": role, "model": model_id,
|
|
313
|
+
"binding": binding, "generationId": self.generation["generationId"]})
|
|
314
|
+
start = time.monotonic()
|
|
315
|
+
raw, recorded = None, False
|
|
316
|
+
try:
|
|
317
|
+
result = await called(function, *args, **kwargs)
|
|
318
|
+
# Do not serialize/consume a provider stream as if it were a complete response.
|
|
319
|
+
if hasattr(result, "__aiter__"):
|
|
320
|
+
return self._capture_role_stream(result, role, model_id, request, start, usage_reader)
|
|
321
|
+
artifact_value = await called(response_serializer, result) if response_serializer else result
|
|
322
|
+
raw = self.artifacts.put_bytes("role-raw-response", artifact_value.encode("utf-8") if isinstance(artifact_value, str)
|
|
323
|
+
else artifact_value if isinstance(artifact_value, bytes) else encoded(artifact_value))
|
|
324
|
+
usage = await called(usage_reader, result) if usage_reader else None
|
|
325
|
+
self._record_usage(role, model_id, request, raw, start, usage, "complete")
|
|
326
|
+
recorded = True
|
|
327
|
+
await self._current()
|
|
328
|
+
return result
|
|
329
|
+
except BaseException as error:
|
|
330
|
+
if not recorded:
|
|
331
|
+
self._record_usage(role, model_id, request, raw, start, None, "failed-or-partial", type(error).__name__)
|
|
332
|
+
raise
|
|
333
|
+
return wrapper
|
|
334
|
+
|
|
335
|
+
def _record_usage(self, role, model_id, request, raw, start, usage, outcome, failure_type=None):
|
|
336
|
+
event = {"role": role, "model": model_id, "request": request, "rawOutput": raw,
|
|
337
|
+
"outcome": outcome, "usage": usage_report(usage), "measurement": "host-reported",
|
|
338
|
+
"wrapperElapsedMs": round((time.monotonic() - start) * 1000), "failureType": failure_type}
|
|
339
|
+
self.usage.append(event)
|
|
340
|
+
self.artifacts.put_json("role-usage", event)
|
|
341
|
+
self._persist()
|
|
342
|
+
|
|
343
|
+
async def _capture_role_stream(self, iterator, role, model_id, request, start, usage_reader):
|
|
344
|
+
fragments, outcome = [], "interrupted-or-partial"
|
|
345
|
+
try:
|
|
346
|
+
async for fragment in iterator:
|
|
347
|
+
raw = self.artifacts.put_bytes("role-stream-fragment", fragment.encode() if isinstance(fragment, str) else encoded(fragment))
|
|
348
|
+
fragments.append(raw)
|
|
349
|
+
await self._current()
|
|
350
|
+
yield fragment
|
|
351
|
+
outcome = "complete"
|
|
352
|
+
finally:
|
|
353
|
+
if hasattr(iterator, "aclose"):
|
|
354
|
+
await iterator.aclose()
|
|
355
|
+
ref = self.artifacts.put_json("role-stream-capture", {"fragments": fragments, "outcome": outcome})
|
|
356
|
+
usage = await called(usage_reader, None) if usage_reader and outcome == "complete" else None
|
|
357
|
+
self._record_usage(role, model_id, request, ref, start, usage, outcome)
|
|
358
|
+
|
|
359
|
+
def record_cache_hit(self, *, role, model_id, binding, raw_output_ref, cache_ref, usage=None):
|
|
360
|
+
required(role in ROLES and raw_output_ref and cache_ref, "CACHE_BINDING_INVALID", "Retained raw/cache references required")
|
|
361
|
+
self.artifacts.read_bytes(raw_output_ref)
|
|
362
|
+
event = {"role": role, "model": model_id, "binding": portable(binding), "rawOutput": raw_output_ref,
|
|
363
|
+
"cacheRef": portable(cache_ref), "cacheHit": True, "usage": usage_report(usage),
|
|
364
|
+
"measurement": "host-reported-cache-event", "outcome": "cache-hit"}
|
|
365
|
+
self.usage.append(event)
|
|
366
|
+
return self._receipt("cache-capture", "retained", event=event)
|
|
367
|
+
|
|
368
|
+
def cost_summary(self, start_index=0):
|
|
369
|
+
events = self.usage[start_index:]
|
|
370
|
+
result = {}
|
|
371
|
+
for role in sorted(ROLES):
|
|
372
|
+
calls = [event for event in events if event["role"] == role]
|
|
373
|
+
currencies = {event["usage"]["currency"] for event in calls if event["usage"]["cost"] is not None}
|
|
374
|
+
result[role] = {"observedEvents": len(calls), "cost": None, "currency": None,
|
|
375
|
+
"inputTokens": None, "outputTokens": None,
|
|
376
|
+
"coverage": "instrumented-events-only; absence does not prove zero provider work"}
|
|
377
|
+
if calls and all(event["usage"]["cost"] is not None for event in calls) and len(currencies) == 1 and None not in currencies:
|
|
378
|
+
result[role].update(cost=sum(event["usage"]["cost"] for event in calls), currency=next(iter(currencies)))
|
|
379
|
+
for field in ["inputTokens", "outputTokens"]:
|
|
380
|
+
if calls and all(event["usage"][field] is not None for event in calls):
|
|
381
|
+
result[role][field] = sum(event["usage"][field] for event in calls)
|
|
382
|
+
return result
|
|
383
|
+
|
|
384
|
+
async def _write(self, operation, action, *, require_current=True):
|
|
385
|
+
self._assert_loop()
|
|
386
|
+
async with self._lock:
|
|
387
|
+
required(self._active is None, "OPERATION_BUSY", "One operation owns this generation")
|
|
388
|
+
required(self.generation["state"] in {"initialized", "indexed", "published"},
|
|
389
|
+
"RECOVERY_REQUIRED", "Reconcile the generation before another write")
|
|
390
|
+
if require_current:
|
|
391
|
+
await self._current()
|
|
392
|
+
self._active, self._hook_failure = operation, None
|
|
393
|
+
self.generation.update(state="indexing", publication=None)
|
|
394
|
+
self._persist()
|
|
395
|
+
try:
|
|
396
|
+
result = await action()
|
|
397
|
+
required(self._hook_failure is None, "HOOK_FAILED", "A hook failure prevents publishing partial native work")
|
|
398
|
+
if require_current:
|
|
399
|
+
await self._current()
|
|
400
|
+
self.generation["state"] = "indexed"
|
|
401
|
+
return self._receipt(operation, "complete", **result)
|
|
402
|
+
except BaseException as error:
|
|
403
|
+
self.generation.update(state="recovery-required", publication=None)
|
|
404
|
+
receipt = self._receipt(operation, "failed-or-partial", failureType=type(error).__name__,
|
|
405
|
+
hookFailure=self._hook_failure, rawCaptures=self.captures, nativeFailure=getattr(error, "receipt", None),
|
|
406
|
+
recovery="host-reconcile-before-resume-or-rebuild")
|
|
407
|
+
if isinstance(error, asyncio.CancelledError):
|
|
408
|
+
raise
|
|
409
|
+
raise SupplierError(getattr(error, "code", "NATIVE_OPERATION_FAILED"),
|
|
410
|
+
"Native work failed or may be partial; retained receipts require reconciliation", receipt=receipt) from error
|
|
411
|
+
finally:
|
|
412
|
+
self._active = None
|
|
413
|
+
|
|
414
|
+
async def ingest(self, **options):
|
|
415
|
+
required(set(options) <= ENQUEUE_FIELDS and "input" in options, "OPTIONS_INVALID", "Pinned enqueue options required")
|
|
416
|
+
required(self.generation["view"] in {None, "native-exploration"}, "VIEW_CONFLICT", "Use a separate native-exploration generation")
|
|
417
|
+
required(self.document_mapper is not None and self.final_reconciler is not None,
|
|
418
|
+
"HOST_BINDING_REQUIRED", "Native document mapping and multimodal reconciliation required")
|
|
419
|
+
# IDs select RAW semantics even when docs_format says pending_parse.
|
|
420
|
+
effective = "raw" if options.get("ids") is not None else options.get("docs_format", "raw")
|
|
421
|
+
self.generation["view"] = "native-exploration"
|
|
422
|
+
async def action():
|
|
423
|
+
request = self.artifacts.put_json("enqueue-request", {k: v for k, v in options.items() if k != "admission_token"})
|
|
424
|
+
track = await called(self.native.apipeline_enqueue_documents, **options)
|
|
425
|
+
required(isinstance(track, str) and track, "NATIVE_RESULT_INVALID", "Enqueue returns a track ID, not document IDs")
|
|
426
|
+
self.generation["trackIds"].append(track)
|
|
427
|
+
self._persist()
|
|
428
|
+
await called(self.native.apipeline_process_enqueue_documents)
|
|
429
|
+
documents = portable(await called(self.native.aget_docs_by_track_id, track_id=track))
|
|
430
|
+
required(bool(documents) and all(doc.get("status") == "processed" for doc in documents.values()),
|
|
431
|
+
"NATIVE_DOCUMENTS_PARTIAL", "Every selected native document must report processed")
|
|
432
|
+
mapping = portable(await called(self.document_mapper, documents, self.generation["sourceBindings"]))
|
|
433
|
+
required(set(mapping) == set(documents), "DOCUMENT_MAPPING_INVALID", "Map actual native document IDs exactly")
|
|
434
|
+
required(all(binding in self.generation["sourceBindings"] for binding in mapping.values()),
|
|
435
|
+
"DOCUMENT_MAPPING_INVALID", "Native documents must resolve to existing exact source bindings")
|
|
436
|
+
self.generation["nativeDocuments"].update(mapping)
|
|
437
|
+
final = portable(await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
438
|
+
final_ref = self.artifacts.put_json("final-native-output", final)
|
|
439
|
+
self.generation["readback"] = final_ref
|
|
440
|
+
reconciliation = portable(await called(self.final_reconciler, final, self.capture_bundle()))
|
|
441
|
+
required(reconciliation.get("reconciled") is True and reconciliation.get("endpointConsistency") == "verified"
|
|
442
|
+
and reconciliation.get("postHookAdditionsReviewed") is True,
|
|
443
|
+
"MULTIMODAL_RECONCILIATION_REQUIRED", "Final post-hook native additions and endpoint consistency require explicit reconciliation")
|
|
444
|
+
self.generation["losses"] = reconciliation.get("losses", [])
|
|
445
|
+
return {"request": request, "trackId": track, "documents": documents, "documentMapping": mapping,
|
|
446
|
+
"effectiveDocsFormat": effective, "nativeOutput": final_ref, "reconciliation": reconciliation,
|
|
447
|
+
"costs": self.cost_summary()}
|
|
448
|
+
return await self._write("native-ingest", action)
|
|
449
|
+
|
|
450
|
+
async def fixed_token_insert(self, **options):
|
|
451
|
+
allowed = {"input", "split_by_character", "split_by_character_only", "ids", "file_paths", "track_id"}
|
|
452
|
+
required(set(options) <= allowed and "input" in options, "OPTIONS_INVALID", "Pinned ainsert options required")
|
|
453
|
+
required(self.generation["view"] in {None, "native-exploration"}, "VIEW_CONFLICT", "Separate native generation required")
|
|
454
|
+
required(self.document_mapper is not None and self.final_reconciler is not None,
|
|
455
|
+
"HOST_BINDING_REQUIRED", "Native document mapping and multimodal reconciliation required")
|
|
456
|
+
self.generation["view"] = "native-exploration"
|
|
457
|
+
async def action():
|
|
458
|
+
track = portable(await called(self.native.ainsert, **options))
|
|
459
|
+
required(isinstance(track, str) and bool(track), "NATIVE_RESULT_INVALID", "ainsert returns an actual track ID")
|
|
460
|
+
self.generation["trackIds"].append(track)
|
|
461
|
+
documents = portable(await called(self.native.aget_docs_by_track_id, track_id=track))
|
|
462
|
+
required(bool(documents) and all(doc.get("status") == "processed" for doc in documents.values()),
|
|
463
|
+
"NATIVE_DOCUMENTS_PARTIAL", "Selected documents must report processed")
|
|
464
|
+
mapping = portable(await called(self.document_mapper, documents, self.generation["sourceBindings"]))
|
|
465
|
+
required(set(mapping) == set(documents) and all(binding in self.generation["sourceBindings"] for binding in mapping.values()),
|
|
466
|
+
"DOCUMENT_MAPPING_INVALID", "Map actual native IDs to exact existing source bindings")
|
|
467
|
+
self.generation["nativeDocuments"].update(mapping)
|
|
468
|
+
final = portable(await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
469
|
+
self.generation["readback"] = self.artifacts.put_json("final-native-output", final)
|
|
470
|
+
reconciliation = portable(await called(self.final_reconciler, final, self.capture_bundle()))
|
|
471
|
+
required(reconciliation.get("reconciled") is True and reconciliation.get("endpointConsistency") == "verified"
|
|
472
|
+
and reconciliation.get("postHookAdditionsReviewed") is True,
|
|
473
|
+
"MULTIMODAL_RECONCILIATION_REQUIRED", "Final post-hook reconciliation required")
|
|
474
|
+
self.generation.update(losses=reconciliation.get("losses", []))
|
|
475
|
+
return {"trackId": track, "documents": documents, "documentMapping": mapping, "chunking": "F",
|
|
476
|
+
"nativeOutput": self.generation["readback"], "reconciliation": reconciliation}
|
|
477
|
+
return await self._write("fixed-token-insert", action)
|
|
478
|
+
|
|
479
|
+
async def import_accepted(self, project, *, profile="assertion-incidence"):
|
|
480
|
+
required(self.generation["view"] is None and self.generation["state"] == "initialized",
|
|
481
|
+
"VIEW_CONFLICT", "Accepted import requires a new isolated generation")
|
|
482
|
+
required(self.verify_accepted_projection is not None, "HOST_BINDING_REQUIRED", "Existing knowledge-owner verification required")
|
|
483
|
+
project = portable(project)
|
|
484
|
+
authority = portable(await called(self.verify_accepted_projection, project))
|
|
485
|
+
required(authority.get("current") is True and authority.get("historyDigest") == project.get("historyDigest")
|
|
486
|
+
and authority.get("activation") == project.get("activation") and authority.get("inputDigest") == digest(project),
|
|
487
|
+
"ACCEPTANCE_BINDING_INVALID", "Owner must verify exact currently accepted active records and endpoint decisions")
|
|
488
|
+
projection = build_projection(project, profile=profile)
|
|
489
|
+
self.generation.update(view="accepted-projection", projection=self.artifacts.put_json("accepted-projection-input", projection),
|
|
490
|
+
losses=projection["losses"])
|
|
491
|
+
async def action():
|
|
492
|
+
await called(self.native.ainsert_custom_kg, custom_kg=projection["custom_kg"],
|
|
493
|
+
full_doc_id="projection:" + self.generation["generationId"])
|
|
494
|
+
native_output = portable(await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
495
|
+
self.generation["readback"] = self.artifacts.put_json("final-native-output", native_output)
|
|
496
|
+
return {"profile": profile, "projection": self.generation["projection"],
|
|
497
|
+
"nativeOutput": self.generation["readback"], "losses": projection["losses"],
|
|
498
|
+
"recoveryGuarantee": "no-document-journal; reconcile-or-rebuild-isolated-generation"}
|
|
499
|
+
return await self._write("accepted-kg-import", action)
|
|
500
|
+
|
|
501
|
+
async def publish(self):
|
|
502
|
+
self._assert_loop()
|
|
503
|
+
async with self._lock:
|
|
504
|
+
required(self.generation["state"] == "indexed" and self.generation["readback"] is not None,
|
|
505
|
+
"GENERATION_INCOMPLETE", "A complete generation and final readback are required")
|
|
506
|
+
await self._current()
|
|
507
|
+
snapshot = self.artifacts.read_json(self.generation["readback"])
|
|
508
|
+
check = portable(await called(self.verify_readback, copy.deepcopy(self.generation), snapshot))
|
|
509
|
+
required(check.get("valid") is True and check.get("generationId") == self.generation["generationId"],
|
|
510
|
+
"READBACK_FAILED", "Host must verify normalization, mapping, completeness and storage readback")
|
|
511
|
+
await self._current()
|
|
512
|
+
self.generation.update(state="published", publication=self.artifacts.put_json("publication-readback", check))
|
|
513
|
+
return self._receipt("publish", "eligible-derived-generation", verification=check)
|
|
514
|
+
|
|
515
|
+
async def _query(self, operation, query, options, generation_id, *, system_prompt=None, progress_callback=None,
|
|
516
|
+
stream_consumer=None, allow_ungrounded=False):
|
|
517
|
+
self._assert_loop()
|
|
518
|
+
async with self._lock:
|
|
519
|
+
required(generation_id == self.generation["generationId"] and self.generation["state"] == "published",
|
|
520
|
+
"GENERATION_INELIGIBLE", "Select the exact completed published generation")
|
|
521
|
+
required(set(options) <= QUERY_FIELDS, "OPTIONS_INVALID", "Pinned QueryParam fields required")
|
|
522
|
+
if options.get("mode") == "bypass":
|
|
523
|
+
required(allow_ungrounded and self.generation["view"] == "native-exploration", "UNGROUNDED_REFUSED", "Explicit ungrounded exploration is required")
|
|
524
|
+
await self._current()
|
|
525
|
+
start = len(self.usage)
|
|
526
|
+
parameter = self.query_param_factory(**portable(options))
|
|
527
|
+
self._active = operation
|
|
528
|
+
self.generation["operationIntent"] = self.artifacts.put_json("native-query-intent",
|
|
529
|
+
{"operation": operation, "query": query, "options": options, "systemPrompt": system_prompt,
|
|
530
|
+
"generationId": generation_id, "sourceBindings": self.generation["sourceBindings"]})
|
|
531
|
+
self._persist()
|
|
532
|
+
raw, partial_artifacts = None, []
|
|
533
|
+
try:
|
|
534
|
+
if operation == "retrieve":
|
|
535
|
+
result = await called(self.native.aquery_data, query=query, param=parameter)
|
|
536
|
+
else:
|
|
537
|
+
# Exactly one native retrieval+answer route. Never prequery aquery_data.
|
|
538
|
+
result = await called(self.native.aquery_llm, query=query, param=parameter,
|
|
539
|
+
system_prompt=system_prompt, progress_callback=progress_callback)
|
|
540
|
+
if operation == "answer" and result.get("llm_response", {}).get("is_streaming"):
|
|
541
|
+
response = dict(result["llm_response"])
|
|
542
|
+
iterator = response.pop("response_iterator")
|
|
543
|
+
if stream_consumer is None:
|
|
544
|
+
if hasattr(iterator, "aclose"):
|
|
545
|
+
await iterator.aclose()
|
|
546
|
+
required(False, "STREAM_CONSUMER_REQUIRED", "A host consumer is required to settle streaming output")
|
|
547
|
+
header = {**result, "llm_response": response}
|
|
548
|
+
partial_artifacts.append(self.artifacts.put_json("native-answer-header", header))
|
|
549
|
+
fragments, completed = [], False
|
|
550
|
+
try:
|
|
551
|
+
async for fragment in iterator:
|
|
552
|
+
ref = self.artifacts.put_bytes("native-answer-fragment", fragment.encode() if isinstance(fragment, str) else encoded(fragment))
|
|
553
|
+
fragments.append(ref)
|
|
554
|
+
await self._current()
|
|
555
|
+
await called(stream_consumer, fragment)
|
|
556
|
+
completed = True
|
|
557
|
+
finally:
|
|
558
|
+
if hasattr(iterator, "aclose"):
|
|
559
|
+
await iterator.aclose()
|
|
560
|
+
partial_artifacts.append(self.artifacts.put_json("native-answer-stream", {"fragments": fragments, "complete": completed}))
|
|
561
|
+
result = {**header, "streamFragments": fragments, "streamComplete": completed}
|
|
562
|
+
result = portable(result)
|
|
563
|
+
raw = self.artifacts.put_json("native-" + operation + "-result", result)
|
|
564
|
+
await self._current() # Withdrawal during retrieval refuses result delivery.
|
|
565
|
+
return self._receipt(operation, "complete" if result.get("status") == "success" else "native-failure",
|
|
566
|
+
nativeResult=result, nativeArtifact=raw, costs=self.cost_summary(start), view=self.generation["view"],
|
|
567
|
+
grounded=options.get("mode") != "bypass", projectionMapping=self.generation["projection"],
|
|
568
|
+
losses=self.generation["losses"], publicationArtifact=self.generation["publication"],
|
|
569
|
+
generationReadback=self.generation["readback"], citationScope={"provider": "lightrag", "generationId": generation_id})
|
|
570
|
+
except BaseException as error:
|
|
571
|
+
receipt = self._receipt(operation, "failed-or-partial", failureType=type(error).__name__, costs=self.cost_summary(start),
|
|
572
|
+
nativeArtifact=raw, partialArtifacts=partial_artifacts)
|
|
573
|
+
if isinstance(error, asyncio.CancelledError):
|
|
574
|
+
raise
|
|
575
|
+
raise SupplierError(getattr(error, "code", "QUERY_FAILED"), "Native query failed or became ineligible", receipt=receipt) from error
|
|
576
|
+
finally:
|
|
577
|
+
self._active = None
|
|
578
|
+
|
|
579
|
+
async def retrieve(self, query, *, generation_id, options=None, allow_ungrounded=False):
|
|
580
|
+
return await self._query("retrieve", query, options or {}, generation_id, allow_ungrounded=allow_ungrounded)
|
|
581
|
+
|
|
582
|
+
async def answer(self, query, *, generation_id, options=None, system_prompt=None,
|
|
583
|
+
progress_callback=None, stream_consumer=None, allow_ungrounded=False):
|
|
584
|
+
return await self._query("answer", query, options or {}, generation_id, system_prompt=system_prompt,
|
|
585
|
+
progress_callback=progress_callback, stream_consumer=stream_consumer, allow_ungrounded=allow_ungrounded)
|
|
586
|
+
|
|
587
|
+
async def _read_native(self, operation, generation_id, action, *, states=frozenset({"initialized", "indexed", "published"})):
|
|
588
|
+
"""Serialize a read against all supplier writes, retaining failed delivery.
|
|
589
|
+
|
|
590
|
+
A busy read refuses immediately; it never observes an unfinished writer or
|
|
591
|
+
implicitly waits for that writer to become a different eligible revision.
|
|
592
|
+
"""
|
|
593
|
+
start, details = len(self.usage), {"requestedGenerationId": generation_id}
|
|
594
|
+
async def preflight():
|
|
595
|
+
self._assert_loop()
|
|
596
|
+
required(generation_id == self.generation["generationId"], "GENERATION_INELIGIBLE", "Select this exact native generation")
|
|
597
|
+
required(self._active is None, "OPERATION_BUSY", "Settle active native work before inspection, export or receiving")
|
|
598
|
+
required(not self._lock.locked(), "OPERATION_BUSY", "Do not wait through an unfinished writer's eligibility/readback window")
|
|
599
|
+
required(self.native is not None and self.generation["state"] in states,
|
|
600
|
+
"GENERATION_INELIGIBLE", "Inspection/export requires initialized, settled native custody")
|
|
601
|
+
try:
|
|
602
|
+
await preflight()
|
|
603
|
+
selection = digest(self.generation)
|
|
604
|
+
async with self._lock:
|
|
605
|
+
# We own the lock here. Recheck the binding/custody, without
|
|
606
|
+
# mistaking this operation's own lock for an active writer.
|
|
607
|
+
required(generation_id == self.generation["generationId"] and self._active is None
|
|
608
|
+
and self.native is not None and self.generation["state"] in states and digest(self.generation) == selection,
|
|
609
|
+
"GENERATION_INELIGIBLE", "Exact settled generation must still hold under the read lock")
|
|
610
|
+
self._active = operation
|
|
611
|
+
revision = digest(self.generation)
|
|
612
|
+
try:
|
|
613
|
+
current = await self._current()
|
|
614
|
+
details["eligibilityBefore"] = self.artifacts.put_json("read-eligibility-before", current)
|
|
615
|
+
await action(details)
|
|
616
|
+
current = await self._current()
|
|
617
|
+
required(digest(self.generation) == revision and generation_id == self.generation["generationId"],
|
|
618
|
+
"GENERATION_INELIGIBLE", "The selected native revision changed during read/export")
|
|
619
|
+
details["eligibilityAfter"] = self.artifacts.put_json("read-eligibility-after", current)
|
|
620
|
+
return self._receipt(operation, details.pop("outcome", "complete"), costs=self.cost_summary(start), **details)
|
|
621
|
+
finally:
|
|
622
|
+
self._active = None
|
|
623
|
+
except BaseException as error:
|
|
624
|
+
receipt = self._receipt(operation, "failed-or-partial", failureType=type(error).__name__,
|
|
625
|
+
failureCode=getattr(error, "code", None), costs=self.cost_summary(start),
|
|
626
|
+
nativeFailure=getattr(error, "receipt", None), **{key: value for key, value in details.items() if key != "outcome"})
|
|
627
|
+
if isinstance(error, asyncio.CancelledError):
|
|
628
|
+
raise
|
|
629
|
+
raise SupplierError(getattr(error, "code", "NATIVE_READ_FAILED"), "Native read/export or receiving refused or failed", receipt=receipt) from error
|
|
630
|
+
|
|
631
|
+
async def inspect_native(self, method, *, generation_id, **options):
|
|
632
|
+
required(method in CAPABILITIES["exploration"] + ["get_processing_status", "aget_docs_by_track_id", "aget_docs_by_ids"],
|
|
633
|
+
"METHOD_INVALID", "Use an inspected native inspection method")
|
|
634
|
+
async def action(details):
|
|
635
|
+
value = portable(await called(getattr(self.native, method), **options))
|
|
636
|
+
details.update(nativeResult=value, nativeArtifact=self.artifacts.put_json("native-inspection", value),
|
|
637
|
+
projectionMapping=self.generation["projection"])
|
|
638
|
+
return await self._read_native(method, generation_id, action)
|
|
639
|
+
|
|
640
|
+
async def receive_citations(self, query_receipt, *, generation_id, hydrate_citations, verify_citations):
|
|
641
|
+
"""Receive existing canonical located evidence through its actual host owner.
|
|
642
|
+
|
|
643
|
+
Raw LightRAG reference numbers/paths never become evidenceRef, identity or
|
|
644
|
+
acceptance. Host callbacks must retain the real mapping/eligibility proof.
|
|
645
|
+
"""
|
|
646
|
+
async def action(details):
|
|
647
|
+
required(query_receipt in self.receipts and query_receipt.get("operation") in {"retrieve", "answer"}
|
|
648
|
+
and query_receipt.get("outcome") == "complete" and query_receipt.get("grounded") is True
|
|
649
|
+
and query_receipt.get("generationId") == generation_id
|
|
650
|
+
and query_receipt.get("publicationArtifact") == self.generation["publication"]
|
|
651
|
+
and query_receipt.get("generationReadback") == self.generation["readback"],
|
|
652
|
+
"CITATION_QUERY_INVALID", "Use this generation's retained successful grounded query receipt")
|
|
653
|
+
result = self.artifacts.read_json(query_receipt["nativeArtifact"])
|
|
654
|
+
required(result == query_receipt["nativeResult"], "CITATION_QUERY_INVALID", "Exact retained query readback required")
|
|
655
|
+
references = result.get("data", {}).get("references", [])
|
|
656
|
+
details.update(queryArtifact=query_receipt["nativeArtifact"], queryReceipt=query_receipt["artifact"],
|
|
657
|
+
nativeReferences=references, projectionMapping=self.generation["projection"],
|
|
658
|
+
publicationArtifact=self.generation["publication"], generationReadback=self.generation["readback"])
|
|
659
|
+
mapping = portable(await called(hydrate_citations, copy.deepcopy(self.generation), copy.deepcopy(query_receipt),
|
|
660
|
+
copy.deepcopy(result), copy.deepcopy(self.generation["projection"])))
|
|
661
|
+
details["mappingArtifact"] = self.artifacts.put_json("host-citation-mapping", mapping)
|
|
662
|
+
required(isinstance(mapping.get("mappingReceipt"), dict) and isinstance(mapping["mappingReceipt"].get("id"), str)
|
|
663
|
+
and bool(mapping["mappingReceipt"]["id"])
|
|
664
|
+
and re.fullmatch(r"sha256:[a-f0-9]{64}", mapping["mappingReceipt"].get("digest", "")),
|
|
665
|
+
"CITATION_MAPPING_REQUIRED", "Retain the existing canonical owner's mapping receipt")
|
|
666
|
+
mappings = mapping.get("mappings")
|
|
667
|
+
required(isinstance(mappings, list) and isinstance(references, list),
|
|
668
|
+
"CITATION_MAPPING_REQUIRED", "Existing located evidence mappings are required")
|
|
669
|
+
hits = []
|
|
670
|
+
for entry in mappings:
|
|
671
|
+
required(isinstance(entry, dict) and entry.get("nativeReference") in references
|
|
672
|
+
and isinstance(entry.get("evidence"), list) and bool(entry["evidence"]),
|
|
673
|
+
"CITATION_MAPPING_INVALID", "Map an exact native reference to existing canonical evidence")
|
|
674
|
+
for evidence in entry["evidence"]:
|
|
675
|
+
required(isinstance(evidence, dict) and all(evidence.get(field) for field in
|
|
676
|
+
["id", "sourceId", "sourceDigest", "attemptId", "locator", "ref", "quote"])
|
|
677
|
+
and isinstance(evidence["locator"], dict) and evidence["locator"].get("kind")
|
|
678
|
+
and evidence["locator"].get("value"), "CITATION_MAPPING_INVALID", "Existing canonical evidence fields and locator required")
|
|
679
|
+
required(any(all(evidence[field] == binding.get(field) for field in ["sourceId", "sourceDigest", "attemptId"])
|
|
680
|
+
for binding in self.generation["sourceBindings"]),
|
|
681
|
+
"CITATION_MAPPING_INVALID", "Citation origin must match a current exact canonical source binding")
|
|
682
|
+
if self.generation["projection"]:
|
|
683
|
+
retained = self.artifacts.read_json(self.generation["projection"])["mapping"]["chunks"]
|
|
684
|
+
required(any(item.get("evidence") == evidence for item in retained.values()),
|
|
685
|
+
"CITATION_MAPPING_INVALID", "Accepted projection citations must reuse its exact retained canonical evidence")
|
|
686
|
+
required(isinstance(entry.get("hits"), list) and bool(entry["hits"]),
|
|
687
|
+
"CITATION_MAPPING_INVALID", "The host must return original canonical evidenceRef hits; supplier cannot derive them")
|
|
688
|
+
for hit in entry["hits"]:
|
|
689
|
+
reference = hit.get("evidenceRef") if isinstance(hit, dict) else None
|
|
690
|
+
required(isinstance(reference, dict) and set(reference) == {"sourceId", "revision", "representationId", "locator"}
|
|
691
|
+
and any(hit.get("quote") == item["quote"] and reference == {
|
|
692
|
+
"sourceId": item["sourceId"], "revision": item["sourceDigest"],
|
|
693
|
+
"representationId": item["attemptId"], "locator": item["locator"]}
|
|
694
|
+
for item in entry["evidence"]),
|
|
695
|
+
"CITATION_MAPPING_INVALID", "Original owner evidenceRef must locate the exact retained canonical quote")
|
|
696
|
+
hits.append(hit)
|
|
697
|
+
required(all(any(hit["evidenceRef"]["sourceId"] == evidence["sourceId"]
|
|
698
|
+
and hit["evidenceRef"]["revision"] == evidence["sourceDigest"]
|
|
699
|
+
and hit["evidenceRef"]["representationId"] == evidence["attemptId"]
|
|
700
|
+
and hit["evidenceRef"]["locator"] == evidence["locator"]
|
|
701
|
+
and hit["quote"] == evidence["quote"] for hit in entry["hits"])
|
|
702
|
+
for evidence in entry["evidence"]),
|
|
703
|
+
"CITATION_MAPPING_INVALID", "Every retained mapped origin needs its original canonical evidenceRef hit")
|
|
704
|
+
unresolved = mapping.get("unresolved", [])
|
|
705
|
+
required(isinstance(unresolved, list) and all(isinstance(item, dict) and item.get("nativeReference") in references
|
|
706
|
+
and isinstance(item.get("reason"), str) and bool(item["reason"]) for item in unresolved),
|
|
707
|
+
"CITATION_MAPPING_INVALID", "Unresolved native references need explicit coverage and reasons")
|
|
708
|
+
located_refs = {digest(item["nativeReference"]) for item in mappings}
|
|
709
|
+
unresolved_refs = {digest(item["nativeReference"]) for item in unresolved}
|
|
710
|
+
required(not located_refs & unresolved_refs and located_refs | unresolved_refs == {digest(item) for item in references},
|
|
711
|
+
"CITATION_MAPPING_INVALID", "Every native reference must be located or explicitly unresolved")
|
|
712
|
+
verification = portable(await called(verify_citations, copy.deepcopy(self.generation), copy.deepcopy(query_receipt), copy.deepcopy(mapping)))
|
|
713
|
+
details["verificationArtifact"] = self.artifacts.put_json("host-citation-verification", verification)
|
|
714
|
+
required(verification.get("current") is True and verification.get("generationId") == generation_id
|
|
715
|
+
and verification.get("sourceBindings") == self.generation["sourceBindings"]
|
|
716
|
+
and verification.get("queryArtifact") == query_receipt["nativeArtifact"]
|
|
717
|
+
and verification.get("mappingDigest") == digest(mapping)
|
|
718
|
+
and verification.get("hitsDigest") == digest(hits)
|
|
719
|
+
and isinstance(verification.get("eligibilityReceipt"), dict)
|
|
720
|
+
and isinstance(verification["eligibilityReceipt"].get("id"), str) and bool(verification["eligibilityReceipt"]["id"])
|
|
721
|
+
and re.fullmatch(r"sha256:[a-f0-9]{64}", verification["eligibilityReceipt"].get("digest", "")),
|
|
722
|
+
"CITATION_ELIGIBILITY_REQUIRED", "Retain concrete existing owner eligibility for the exact canonical mapping/query")
|
|
723
|
+
if self.generation["projection"]:
|
|
724
|
+
project = self.artifacts.read_json(self.generation["projection"])["canonicalProjection"]
|
|
725
|
+
required(verification.get("historyDigest") == project["historyDigest"] and verification.get("activation") == project["activation"],
|
|
726
|
+
"CITATION_ELIGIBILITY_REQUIRED", "Exact current canonical history and activation required")
|
|
727
|
+
details.update(outcome="located-evidence-received" if hits else "no-located-evidence", locatedCitations=mappings, hits=hits,
|
|
728
|
+
unresolvedReferences=unresolved,
|
|
729
|
+
mappingReceipt=mapping["mappingReceipt"], eligibilityReceipt=verification["eligibilityReceipt"],
|
|
730
|
+
evidenceAuthority="existing-canonical-owner", acceptanceGranted=False)
|
|
731
|
+
return await self._read_native("receive-citations", generation_id, action, states=frozenset({"published"}))
|
|
732
|
+
|
|
733
|
+
async def receive_composition(self, received_receipt, *, generation_id, receipt_bridge, verify_receipt_bridge):
|
|
734
|
+
"""Private composer envelope received through the existing host receipt owner.
|
|
735
|
+
|
|
736
|
+
The supplier never promotes an artifact digest to an actor/principal or
|
|
737
|
+
mints receiptRef. Query and receiving usage remain separately attributed.
|
|
738
|
+
"""
|
|
739
|
+
async def action(details):
|
|
740
|
+
required(received_receipt in self.receipts and received_receipt.get("operation") == "receive-citations"
|
|
741
|
+
and received_receipt.get("outcome") in {"located-evidence-received", "no-located-evidence"}
|
|
742
|
+
and received_receipt.get("generationId") == generation_id
|
|
743
|
+
and received_receipt.get("publicationArtifact") == self.generation["publication"]
|
|
744
|
+
and received_receipt.get("generationReadback") == self.generation["readback"],
|
|
745
|
+
"COMPOSITION_RECEIVING_INVALID", "Use the exact current retained citation receiving receipt")
|
|
746
|
+
original = self.artifacts.read_json(received_receipt["artifact"])
|
|
747
|
+
required(original == {key: value for key, value in received_receipt.items() if key != "artifact"},
|
|
748
|
+
"COMPOSITION_RECEIVING_INVALID", "Original receiving receipt artifact readback must agree")
|
|
749
|
+
queries = [item for item in self.receipts if item.get("artifact") == received_receipt["queryReceipt"]
|
|
750
|
+
and item.get("nativeArtifact") == received_receipt["queryArtifact"]
|
|
751
|
+
and item.get("operation") in {"retrieve", "answer"} and item.get("outcome") == "complete"]
|
|
752
|
+
required(len(queries) == 1, "COMPOSITION_RECEIVING_INVALID", "Original supplier query receipt must be retained unambiguously")
|
|
753
|
+
query = queries[0]
|
|
754
|
+
usage = {"query": {"attemptArtifact": query["artifact"], "roles": query["costs"]},
|
|
755
|
+
"receiving": {"attemptArtifact": received_receipt["artifact"], "roles": received_receipt["costs"]}}
|
|
756
|
+
payload = {"provider": "lightrag", "generation": generation_id, "artifactRef": received_receipt["artifact"],
|
|
757
|
+
"sourceBindings": copy.deepcopy(self.generation["sourceBindings"]),
|
|
758
|
+
"hits": received_receipt["hits"], "unresolvedReferences": received_receipt["unresolvedReferences"],
|
|
759
|
+
"usage": usage, "supplierQueryReceipt": query["artifact"], "supplierReceivingReceipt": received_receipt["artifact"],
|
|
760
|
+
"nativeArtifact": query["nativeArtifact"], "nativeResult": query["nativeResult"],
|
|
761
|
+
"nativeOperation": query["operation"],
|
|
762
|
+
"mappingReceipt": received_receipt["mappingReceipt"], "eligibilityReceipt": received_receipt["eligibilityReceipt"],
|
|
763
|
+
"semanticAcceptance": "existing-owner-only", "canonicalMutation": False}
|
|
764
|
+
details.update(sourceBindings=copy.deepcopy(self.generation["sourceBindings"]),
|
|
765
|
+
supplierUsage=usage, payloadArtifact=self.artifacts.put_json("composition-receiving-payload", payload),
|
|
766
|
+
supplierQueryReceipt=query["artifact"], supplierReceivingReceipt=received_receipt["artifact"])
|
|
767
|
+
bridged = portable(await called(receipt_bridge, copy.deepcopy(payload), copy.deepcopy(query), copy.deepcopy(received_receipt)))
|
|
768
|
+
details.update(bridgeResult=bridged, bridgeResultArtifact=self.artifacts.put_json("host-composition-receipt-bridge", bridged))
|
|
769
|
+
required(bool(bridged.get("receiptRef")) and bool(bridged.get("bridgeReceipt"))
|
|
770
|
+
and bridged["receiptRef"] not in [query["artifact"], received_receipt["artifact"],
|
|
771
|
+
query["artifact"]["artifactId"], received_receipt["artifact"]["artifactId"]],
|
|
772
|
+
"COMPOSITION_RECEIPT_REQUIRED", "Actual original host receipt reference and bridge receipt are required")
|
|
773
|
+
check = portable(await called(verify_receipt_bridge, copy.deepcopy(payload), copy.deepcopy(bridged),
|
|
774
|
+
copy.deepcopy(self.generation)))
|
|
775
|
+
details["bridgeVerificationArtifact"] = self.artifacts.put_json("host-composition-receipt-verification", check)
|
|
776
|
+
required(check.get("verified") is True and check.get("current") is True
|
|
777
|
+
and check.get("generation") == generation_id and check.get("envelopeDigest") == digest(payload)
|
|
778
|
+
and check.get("sourceBindings") == self.generation["sourceBindings"]
|
|
779
|
+
and check.get("receiptRef") == bridged["receiptRef"] and check.get("bridgeReceipt") == bridged["bridgeReceipt"]
|
|
780
|
+
and "bridgeUsage" in check and check["bridgeUsage"] == bridged.get("usage")
|
|
781
|
+
and check.get("supplierQueryReceipt") == query["artifact"]
|
|
782
|
+
and check.get("supplierReceivingReceipt") == received_receipt["artifact"],
|
|
783
|
+
"COMPOSITION_RECEIPT_REQUIRED", "Verify the existing host receipt against the exact envelope and original attempts")
|
|
784
|
+
envelope = {**payload, "receiptRef": bridged["receiptRef"], "bridgeReceipt": bridged["bridgeReceipt"],
|
|
785
|
+
"usage": {**usage, "bridge": {"receiptRef": bridged["receiptRef"], "usage": bridged.get("usage")}},
|
|
786
|
+
"receiptAssurance": "explicit-verified-existing-host-bridge"}
|
|
787
|
+
details.update(outcome="composition-envelope-received", envelope=envelope,
|
|
788
|
+
envelopeArtifact=self.artifacts.put_json("composition-received-envelope", envelope))
|
|
789
|
+
return await self._read_native("receive-composition", generation_id, action, states=frozenset({"published"}))
|
|
790
|
+
|
|
791
|
+
async def draft_edit(self, method, **options):
|
|
792
|
+
required(method in CAPABILITIES["draft-edit"], "METHOD_INVALID", "An inspected draft writer is required")
|
|
793
|
+
required(self.generation["view"] == "native-exploration", "ACCEPTED_VIEW_IMMUTABLE", "Edit a separate native draft generation")
|
|
794
|
+
async def action():
|
|
795
|
+
before = self.artifacts.put_json("draft-before", await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
796
|
+
result = portable(await called(getattr(self.native, method), **options))
|
|
797
|
+
native_result = self.artifacts.put_json("draft-native-result", result)
|
|
798
|
+
after = self.artifacts.put_json("draft-after", await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
799
|
+
self.generation["readback"] = after
|
|
800
|
+
operation_status = (result.get("operation_summary") or {}).get("operation_status")
|
|
801
|
+
if result.get("status") not in {None, "success", "not_found"} or operation_status not in {None, "success"}:
|
|
802
|
+
raise SupplierError("NATIVE_EDIT_FAILED", "Native draft writer refused, failed or only partly completed",
|
|
803
|
+
receipt={"nativeResult": result, "nativeArtifact": native_result, "before": before, "after": after,
|
|
804
|
+
"semanticAcceptance": "pending", "reconciliationRequired": True})
|
|
805
|
+
return {"before": before, "after": after, "nativeResult": result, "nativeArtifact": native_result,
|
|
806
|
+
"proposalDelta": {"before": before, "after": after, "method": method, "parameters": portable(options)},
|
|
807
|
+
"identityAcceptance": "pending", "semanticAcceptance": "pending"}
|
|
808
|
+
return await self._write("draft-" + method, action)
|
|
809
|
+
|
|
810
|
+
async def delete_document(self, doc_id, *, delete_llm_cache=False):
|
|
811
|
+
required(self.generation["view"] == "native-exploration", "DELETE_PROFILE_INVALID", "Imported generations require whole-generation cleanup")
|
|
812
|
+
required(doc_id in self.generation["nativeDocuments"], "DOCUMENT_MAPPING_INVALID", "An actual mapped native document ID is required")
|
|
813
|
+
required(self.generation["state"] in {"initialized", "indexed", "published"}, "RECOVERY_REQUIRED", "Settle native custody before selective deletion")
|
|
814
|
+
# Revocation blocks readers immediately, before deletion returns or fails.
|
|
815
|
+
self.generation.update(publication=None, state="indexed")
|
|
816
|
+
async def action():
|
|
817
|
+
result = portable(await called(self.native.adelete_by_doc_id, doc_id=doc_id, delete_llm_cache=delete_llm_cache))
|
|
818
|
+
raw = self.artifacts.put_json("native-deletion-result", result)
|
|
819
|
+
if result.get("status") not in {"success", "not_found"}:
|
|
820
|
+
raise SupplierError("DELETION_NOT_VERIFIED", "Native deletion refused or failed", receipt={"nativeResult": result, "nativeArtifact": raw})
|
|
821
|
+
status = portable(await called(self.native.aget_docs_by_ids, ids=[doc_id]))
|
|
822
|
+
required(not status or all(value is None for value in status.values()), "DELETION_NOT_VERIFIED", "Native document absence must be read back")
|
|
823
|
+
self.generation["nativeDocuments"].pop(doc_id)
|
|
824
|
+
self.generation["readback"] = None
|
|
825
|
+
return {"nativeResult": result, "nativeArtifact": raw, "absenceReadback": status, "sourceAccessRevoked": "host-owned",
|
|
826
|
+
"cacheDeletion": "requested" if delete_llm_cache else "retained", "publication": "requires-rebuild-or-reconcile"}
|
|
827
|
+
return await self._write("delete-document", action, require_current=False)
|
|
828
|
+
|
|
829
|
+
async def cancel(self):
|
|
830
|
+
self._assert_loop()
|
|
831
|
+
required(self.cancel_pipeline is not None, "HOST_BINDING_REQUIRED", "SDK has no public cancel method; bind native workspace flag under its lock")
|
|
832
|
+
result = portable(await called(self.cancel_pipeline, self.native, copy.deepcopy(self.generation)))
|
|
833
|
+
return self._receipt("cancel", "requested", nativeResult=result,
|
|
834
|
+
completionVerified=False, nextAction="await-active-operation-then-reconcile")
|
|
835
|
+
|
|
836
|
+
async def reconcile(self, *, reason):
|
|
837
|
+
self._assert_loop()
|
|
838
|
+
required(self._active is None, "OPERATION_BUSY", "Do not reconcile a running/unknown worker")
|
|
839
|
+
required(bool(reason) and self.reconcile_native is not None, "HOST_BINDING_REQUIRED", "Explicit host reconciliation and reason required")
|
|
840
|
+
async with self._lock:
|
|
841
|
+
required(self._active is None, "OPERATION_BUSY", "Do not reconcile a running/unknown worker")
|
|
842
|
+
required(self.generation["state"] not in {"new", "initializing", "finalizing", "removing", "removed", "finalized"},
|
|
843
|
+
"LIFECYCLE_INVALID", "Reconciliation requires open native custody")
|
|
844
|
+
self._active = "reconcile"
|
|
845
|
+
try:
|
|
846
|
+
evidence = portable(await called(self.reconcile_native, self.native, self.snapshot(), reason))
|
|
847
|
+
required(evidence.get("settled") is True and evidence.get("generationId") == self.generation["generationId"],
|
|
848
|
+
"RECOVERY_UNKNOWN", "Worker/storage custody must be settled")
|
|
849
|
+
required(evidence.get("nextAction") in {"resume-enqueued", "rebuild", "readback"}, "RECOVERY_INVALID", "Declare the actual recovery path")
|
|
850
|
+
if self.generation["view"] == "accepted-projection":
|
|
851
|
+
required(evidence["nextAction"] != "resume-enqueued", "RECOVERY_INVALID", "Custom KG import has no ingestion retry journal")
|
|
852
|
+
if evidence["nextAction"] == "readback":
|
|
853
|
+
await self._current()
|
|
854
|
+
output = portable(await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
855
|
+
if self.generation["view"] == "native-exploration":
|
|
856
|
+
required(self.final_reconciler is not None, "HOST_BINDING_REQUIRED", "Final native reconciliation required")
|
|
857
|
+
final = portable(await called(self.final_reconciler, output, self.capture_bundle()))
|
|
858
|
+
required(final.get("reconciled") is True and final.get("endpointConsistency") == "verified"
|
|
859
|
+
and final.get("postHookAdditionsReviewed") is True,
|
|
860
|
+
"MULTIMODAL_RECONCILIATION_REQUIRED", "Reopened native output requires final reconciliation")
|
|
861
|
+
self.generation["losses"] = final.get("losses", [])
|
|
862
|
+
self.generation.update(state="indexed", readback=self.artifacts.put_json("recovered-native-output", output), publication=None)
|
|
863
|
+
elif evidence["nextAction"] == "resume-enqueued":
|
|
864
|
+
self.generation.update(state="initialized", publication=None)
|
|
865
|
+
else:
|
|
866
|
+
self.generation.update(state="rebuild-required", publication=None)
|
|
867
|
+
return self._receipt("reconcile", "settled", evidence=evidence, reason=reason)
|
|
868
|
+
finally:
|
|
869
|
+
self._active = None
|
|
870
|
+
|
|
871
|
+
async def resume_enqueued(self, *, reconciliation_receipt):
|
|
872
|
+
evidence = reconciliation_receipt.get("evidence", {})
|
|
873
|
+
required(reconciliation_receipt in self.receipts and evidence.get("nextAction") == "resume-enqueued",
|
|
874
|
+
"RECOVERY_INVALID", "Use this generation's retained host reconciliation receipt")
|
|
875
|
+
required(self.generation["view"] == "native-exploration", "RECOVERY_INVALID", "Only the document pipeline is resumable")
|
|
876
|
+
recovery_id = reconciliation_receipt["artifact"]["artifactId"]
|
|
877
|
+
required(recovery_id not in self.generation.get("consumedRecoveryReceipts", []),
|
|
878
|
+
"RECOVERY_INVALID", "This settlement receipt already authorized one resume")
|
|
879
|
+
async def action():
|
|
880
|
+
self.generation.setdefault("consumedRecoveryReceipts", []).append(recovery_id)
|
|
881
|
+
self._persist()
|
|
882
|
+
await called(self.native.apipeline_process_enqueue_documents)
|
|
883
|
+
final = portable(await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
884
|
+
self.generation["readback"] = self.artifacts.put_json("final-native-output", final)
|
|
885
|
+
reconciliation = portable(await called(self.final_reconciler, final, self.capture_bundle()))
|
|
886
|
+
required(reconciliation.get("reconciled") is True and reconciliation.get("endpointConsistency") == "verified"
|
|
887
|
+
and reconciliation.get("postHookAdditionsReviewed") is True,
|
|
888
|
+
"MULTIMODAL_RECONCILIATION_REQUIRED", "Final reconciliation required")
|
|
889
|
+
statuses = {}
|
|
890
|
+
for track in self.generation["trackIds"]:
|
|
891
|
+
docs = portable(await called(self.native.aget_docs_by_track_id, track_id=track))
|
|
892
|
+
required(bool(docs) and all(doc.get("status") == "processed" for doc in docs.values()), "NATIVE_DOCUMENTS_PARTIAL", "Resumed documents are incomplete")
|
|
893
|
+
mapped = portable(await called(self.document_mapper, docs, self.generation["sourceBindings"]))
|
|
894
|
+
required(set(mapped) == set(docs) and all(binding in self.generation["sourceBindings"] for binding in mapped.values()),
|
|
895
|
+
"DOCUMENT_MAPPING_INVALID", "Recovered native IDs need exact source mappings")
|
|
896
|
+
statuses.update(docs)
|
|
897
|
+
self.generation["nativeDocuments"].update(mapped)
|
|
898
|
+
self.generation.update(losses=reconciliation.get("losses", []))
|
|
899
|
+
return {"documents": statuses, "reconciliation": reconciliation, "reenqueued": False}
|
|
900
|
+
return await self._write("resume-enqueued", action)
|
|
901
|
+
|
|
902
|
+
async def export_native(self, *, generation_id, output_path, file_format="csv", include_vector_data=False, export_reader):
|
|
903
|
+
required(file_format in {"csv", "excel", "md", "txt"}, "OPTIONS_INVALID", "Pinned native export format required")
|
|
904
|
+
from pathlib import Path
|
|
905
|
+
root = self.generation["configuration"].get("ownedArtifactRoot")
|
|
906
|
+
required(isinstance(root, str) and Path(root).is_absolute(), "OUTPUT_SCOPE_REQUIRED", "Host must declare its owned artifact root")
|
|
907
|
+
destination = Path(output_path).resolve()
|
|
908
|
+
required(destination.is_relative_to(Path(root).resolve()) and destination != Path(root).resolve(),
|
|
909
|
+
"OUTPUT_SCOPE_REQUIRED", "Export destination must be inside the owned artifact root")
|
|
910
|
+
destination = str(destination)
|
|
911
|
+
async def action(details):
|
|
912
|
+
details.update(outputPath=destination, portableProjection=self.generation["projection"], format=file_format)
|
|
913
|
+
try:
|
|
914
|
+
result = portable(await called(self.native.aexport_data, output_path=destination,
|
|
915
|
+
file_format=file_format, include_vector_data=include_vector_data))
|
|
916
|
+
details.update(nativeResult=result, nativeResultArtifact=self.artifacts.put_json("native-export-result", result))
|
|
917
|
+
except BaseException:
|
|
918
|
+
# Preserve any partial file without replacing the original native
|
|
919
|
+
# failure. The host reader must be read-only and scoped to this path.
|
|
920
|
+
try:
|
|
921
|
+
details["nativeArtifact"] = portable(await called(export_reader, destination))
|
|
922
|
+
except BaseException as read_error:
|
|
923
|
+
details["partialReadbackFailure"] = type(read_error).__name__
|
|
924
|
+
raise
|
|
925
|
+
details["nativeArtifact"] = portable(await called(export_reader, destination))
|
|
926
|
+
details["outcome"] = "derived-artifact"
|
|
927
|
+
return await self._read_native("native-export", generation_id, action)
|
|
928
|
+
|
|
929
|
+
async def rebuild_embedding(self, *, embedding_identity):
|
|
930
|
+
required(self.rebuild_vectors is not None, "HOST_BINDING_REQUIRED", "Bind admitted native vector rebuild tool lifecycle")
|
|
931
|
+
async def action():
|
|
932
|
+
result = portable(await called(self.rebuild_vectors, self.native, copy.deepcopy(self.generation), embedding_identity))
|
|
933
|
+
required(result.get("verified") is True, "VECTOR_REBUILD_FAILED", "Native vector consistency readback required")
|
|
934
|
+
self.generation["configuration"]["embeddingIdentity"] = portable(embedding_identity)
|
|
935
|
+
self.generation["configurationDigest"] = digest(self.generation["configuration"])
|
|
936
|
+
self.generation["readback"] = self.artifacts.put_json("vector-rebuild-readback", await called(self.readback, self.native, copy.deepcopy(self.generation)))
|
|
937
|
+
return {"nativeResult": result, "embeddingIdentity": embedding_identity}
|
|
938
|
+
return await self._write("vector-rebuild", action)
|
|
939
|
+
|
|
940
|
+
async def remove_tool(self):
|
|
941
|
+
self._assert_loop()
|
|
942
|
+
required(self._active is None, "OPERATION_BUSY", "Settle/cancel active native work before removal")
|
|
943
|
+
required(self.cleanup_generation is not None, "HOST_BINDING_REQUIRED", "Host owns workspace/backend/cache destruction")
|
|
944
|
+
async with self._lock:
|
|
945
|
+
required(self._active is None, "OPERATION_BUSY", "Settle active native work before removal")
|
|
946
|
+
required(self.native is not None and self.generation["state"] not in {"new", "initializing", "finalizing", "removing", "removed"},
|
|
947
|
+
"LIFECYCLE_INVALID", "Initialized native custody is required before removal")
|
|
948
|
+
self._active = "remove-tool"
|
|
949
|
+
self.generation.update(publication=None, state="removing")
|
|
950
|
+
self._persist()
|
|
951
|
+
try:
|
|
952
|
+
await called(self.native.finalize_storages)
|
|
953
|
+
cleanup = portable(await called(self.cleanup_generation, self.native, copy.deepcopy(self.generation)))
|
|
954
|
+
required(cleanup.get("deleted") is True and cleanup.get("generationId") == self.generation["generationId"],
|
|
955
|
+
"CLEANUP_NOT_VERIFIED", "Verify all selected native stores/cache/vector artifacts are deleted")
|
|
956
|
+
self.generation["state"] = "removed"
|
|
957
|
+
return self._receipt("remove-tool", "native-generation-deleted", cleanup=cleanup,
|
|
958
|
+
canonicalKnowledge="preserved-by-existing-owner", supplierReceipts="retained")
|
|
959
|
+
except BaseException as error:
|
|
960
|
+
self.generation["state"] = "recovery-required"
|
|
961
|
+
receipt = self._receipt("remove-tool", "cleanup-unknown", failureType=type(error).__name__)
|
|
962
|
+
if isinstance(error, asyncio.CancelledError):
|
|
963
|
+
raise
|
|
964
|
+
raise SupplierError("CLEANUP_NOT_VERIFIED", "Native tool removal needs host cleanup reconciliation", receipt=receipt) from error
|
|
965
|
+
finally:
|
|
966
|
+
self._active = None
|