enigma-memory 0.1.18 → 0.1.22
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/README.md +76 -24
- package/apps/cli/bin/enigma-desktop.mjs +140 -0
- package/apps/cli/bin/enigma-terminal.mjs +78 -0
- package/apps/cli/bin/enigma.mjs +1923 -285
- package/apps/desktop/electron-main.cjs +217 -0
- package/apps/desktop/package.json +12 -0
- package/apps/desktop/src/app.js +264 -7
- package/apps/desktop/src/index.html +3514 -1373
- package/apps/desktop/src/launch-electron.mjs +51 -0
- package/apps/desktop/src/server.mjs +2914 -0
- package/apps/desktop/src/styles.css +2972 -260
- package/apps/desktop/src/zk-browser-prove.mjs +53 -0
- package/apps/desktop/src/zk-state.mjs +1789 -0
- package/apps/gateway/bin/enigma-gateway.mjs +102 -5
- package/apps/gateway/src/server.mjs +271 -8
- package/apps/ios/EnigmaCore/Package.swift +12 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/EnigmaAPIClient.swift +227 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/Models.swift +278 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PKCE.swift +96 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/PrivacyMinimizer.swift +187 -0
- package/apps/ios/EnigmaCore/Sources/EnigmaCore/ToolModels.swift +129 -0
- package/apps/ios/EnigmaCore/Tests/EnigmaCoreTests/EnigmaCoreTests.swift +42 -0
- package/apps/ios/EnigmaIOS/Enigma/AppModel.swift +346 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AccentColor.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/Contents.json +11 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/AppIcon.appiconset/EnigmaAppIcon.png +0 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/Contents.json +3 -0
- package/apps/ios/EnigmaIOS/Enigma/Assets.xcassets/LaunchBackground.colorset/Contents.json +12 -0
- package/apps/ios/EnigmaIOS/Enigma/ChatView.swift +181 -0
- package/apps/ios/EnigmaIOS/Enigma/CouncilView.swift +78 -0
- package/apps/ios/EnigmaIOS/Enigma/CreateView.swift +152 -0
- package/apps/ios/EnigmaIOS/Enigma/EnigmaApp.swift +52 -0
- package/apps/ios/EnigmaIOS/Enigma/Info.plist +52 -0
- package/apps/ios/EnigmaIOS/Enigma/NaturalLanguagePrivacyTagger.swift +26 -0
- package/apps/ios/EnigmaIOS/Enigma/OAuthClient.swift +321 -0
- package/apps/ios/EnigmaIOS/Enigma/OnboardingView.swift +105 -0
- package/apps/ios/EnigmaIOS/Enigma/PrivateVaultView.swift +275 -0
- package/apps/ios/EnigmaIOS/Enigma/SecureStore.swift +76 -0
- package/apps/ios/EnigmaIOS/Enigma/SettingsView.swift +60 -0
- package/apps/ios/EnigmaIOS/Enigma/Theme.swift +80 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/project.pbxproj +211 -0
- package/apps/ios/EnigmaIOS/EnigmaIOS.xcodeproj/xcshareddata/xcschemes/Enigma.xcscheme +23 -0
- package/apps/native-host/README.md +19 -8
- package/apps/native-host/bin/enigma-native-host.mjs +229 -13
- package/apps/relay/bin/enigma-relay.mjs +103 -5
- package/apps/relay/src/federation-runtime.mjs +618 -0
- package/apps/relay/src/server.mjs +310 -9
- package/apps/verifier/bin/enigma-verify.mjs +327 -11
- package/cortex-v3/circuits/build/intent_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/sale_vk_bytes.json +35 -0
- package/cortex-v3/circuits/build/vk_bytes.json +32 -0
- package/cortex-v3/proving-assets.json +64 -0
- package/cortex-v3/zk/BUILD-CONTRACT.md +87 -0
- package/cortex-v3/zk/action-transition-vk.json +119 -0
- package/cortex-v3/zk/alias-adversarial.test.mjs +220 -0
- package/cortex-v3/zk/groth16-verify-child.mjs +17 -0
- package/cortex-v3/zk/intent-witness.mjs +365 -0
- package/cortex-v3/zk/intent-witness.test.mjs +485 -0
- package/cortex-v3/zk/proving-assets.mjs +203 -0
- package/cortex-v3/zk/sale-witness.mjs +783 -0
- package/cortex-v3/zk/sale-witness.test.mjs +784 -0
- package/cortex-v3/zk/sealed-sale-release-vk.json +119 -0
- package/cortex-v3/zk/settlement-evidence.mjs +722 -0
- package/cortex-v3/zk/setup-intent.mjs +688 -0
- package/cortex-v3/zk/setup-sale.mjs +666 -0
- package/cortex-v3/zk/setup.mjs +594 -0
- package/cortex-v3/zk/witness.mjs +184 -0
- package/cortex-v3/zk/zk-codec.mjs +232 -0
- package/cortex-v3/zk/zk-codec.test.mjs +293 -0
- package/cortex-v3/zk/zk-settle.mjs +370 -0
- package/cortex-v3/zk/zk-tree.mjs +256 -0
- package/cortex-v3/zk/zk-tree.test.mjs +419 -0
- package/deploy/docker-compose.local-production-simulation.yml +36 -0
- package/docs/browser-extension-install.md +8 -6
- package/docs/client-connectors.md +15 -11
- package/docs/developer-ecosystem.md +15 -13
- package/docs/enigma-memory-ready-conformance.md +11 -9
- package/docs/install-anywhere.md +61 -28
- package/docs/installers-and-desktop.md +8 -7
- package/docs/novelty-invention-candidates.md +161 -161
- package/docs/proof-network-claim-boundaries.md +320 -318
- package/examples/01-quickstart-agent/index.mjs +49 -0
- package/examples/01_agent_memory_quickstart.mjs +57 -0
- package/examples/02-multi-agent-swarm/index.mjs +57 -0
- package/examples/02_cross_model_passport.mjs +64 -0
- package/examples/03-langchain-memory/index.mjs +41 -0
- package/examples/03_poseidon_commitment_verification.mjs +71 -0
- package/examples/04-python-trading-agent/trader.py +49 -0
- package/examples/README.md +27 -0
- package/examples/ci/github-actions.yml +7 -2
- package/package.json +142 -11
- package/packages/adapters/PACKAGE_CONTRACT.md +1 -1
- package/packages/connectors/src/index.js +196 -4
- package/packages/connectors/swarm-router.mjs +168 -0
- package/packages/core/src/index.js +248 -1
- package/packages/core/src/version.mjs +7 -0
- package/packages/dev-tools/package.json +19 -0
- package/packages/dev-tools/src/index.js +4 -0
- package/packages/dev-tools/src/memory-benchmark-suite.js +112 -0
- package/packages/dev-tools/src/swarm-simulator.js +101 -0
- package/packages/dev-tools/src/vault-inspector.js +114 -0
- package/packages/dev-tools/src/vector-benchmark.js +100 -0
- package/packages/developer-platform/src/access-credentials.js +341 -0
- package/packages/developer-platform/src/http.js +132 -0
- package/packages/developer-platform/src/index.js +4 -0
- package/packages/developer-platform/src/usage-http.js +60 -0
- package/packages/developer-platform/src/usage.js +295 -0
- package/packages/enclave-runtime/attestation.mjs +159 -0
- package/packages/enclave-runtime/index.mjs +47 -0
- package/packages/enclave-runtime/session-manager.mjs +253 -0
- package/packages/enclave-runtime/zeroization-proof.mjs +227 -0
- package/packages/enigma-reflex/package.json +14 -0
- package/packages/enigma-reflex/src/index.js +204 -0
- package/packages/enigma-reflex/training/generate-dataset.mjs +40 -0
- package/packages/enigma-reflex/training/requirements.txt +8 -0
- package/packages/enigma-reflex/training/train.py +314 -0
- package/packages/enigma-weave/LICENSE +22 -0
- package/packages/enigma-weave/UPSTREAM.json +21 -0
- package/packages/enigma-weave/package.json +14 -0
- package/packages/enigma-weave/src/index.js +286 -0
- package/packages/hosted-cloud/src/index.js +80 -5
- package/packages/importers/src/index.js +432 -0
- package/packages/inference-runtime/src/browser.js +401 -0
- package/packages/inference-runtime/src/chat.js +265 -0
- package/packages/inference-runtime/src/code.js +407 -0
- package/packages/inference-runtime/src/contracts.js +162 -0
- package/packages/inference-runtime/src/http.js +232 -0
- package/packages/inference-runtime/src/image.js +186 -0
- package/packages/inference-runtime/src/index.js +10 -0
- package/packages/inference-runtime/src/model-router.js +320 -0
- package/packages/inference-runtime/src/platform.js +125 -0
- package/packages/inference-runtime/src/privacy.js +400 -0
- package/packages/inference-runtime/src/video.js +253 -0
- package/packages/mcp-server/README.md +22 -6
- package/packages/mcp-server/bin/enigma-mcp.mjs +2 -1
- package/packages/mcp-server/src/index.js +1418 -105
- package/packages/mcp-server/src/oauth.js +561 -0
- package/packages/mcp-server/src/private-handoff.js +84 -0
- package/packages/mcp-server/src/remote-http.js +273 -0
- package/packages/mcp-server/src/remote-policy.js +72 -0
- package/packages/mcp-server/swarm-bridge.mjs +361 -0
- package/packages/mesh/index.d.ts +283 -0
- package/packages/mesh/package.json +23 -0
- package/packages/mesh/src/crypto.js +189 -0
- package/packages/mesh/src/federation-packets.js +353 -0
- package/packages/mesh/src/gossip.js +311 -0
- package/packages/mesh/src/index.js +6 -0
- package/packages/mesh/src/protocol.js +255 -0
- package/packages/mesh/src/router.js +279 -0
- package/packages/mesh/src/transport.js +306 -0
- package/packages/passport/src/index.js +426 -1
- package/packages/private-economy/src/credits-http.js +100 -0
- package/packages/private-economy/src/credits.js +447 -0
- package/packages/private-economy/src/index.js +5 -0
- package/packages/private-economy/src/payments-http.js +120 -0
- package/packages/private-economy/src/payments.js +509 -0
- package/packages/private-economy/src/x402.js +346 -0
- package/packages/proof-network/PACKAGE_CONTRACT.md +21 -0
- package/packages/rag/index.d.ts +182 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/THIRD_PARTY_LICENSES.txt +207 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/config.json +25 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/onnx/model_quantized.onnx +0 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/sha256-manifest.json +28 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer.json +30686 -0
- package/packages/rag/models/Xenova/all-MiniLM-L6-v2/tokenizer_config.json +15 -0
- package/packages/rag/package.json +27 -0
- package/packages/rag/src/blinded-search.js +109 -0
- package/packages/rag/src/bm25.js +169 -0
- package/packages/rag/src/embeddings.js +459 -0
- package/packages/rag/src/hybrid.js +76 -0
- package/packages/rag/src/index.js +38 -0
- package/packages/rag/src/reranker.js +61 -0
- package/packages/rag/src/research.js +107 -0
- package/packages/rag/src/vector-store.js +430 -0
- package/packages/rag/src/verify-model-artifacts.mjs +4 -0
- package/packages/sdk/index.d.ts +760 -0
- package/packages/sdk/package.json +33 -0
- package/packages/sdk/python/README.md +24 -0
- package/packages/sdk/python/enigma_sdk.py +250 -0
- package/packages/sdk/python/pyproject.toml +34 -0
- package/packages/sdk/python/requirements.txt +1 -0
- package/packages/sdk/python/setup.py +20 -0
- package/packages/sdk/src/federation/capability-grant.js +389 -0
- package/packages/sdk/src/federation/federation-router.js +360 -0
- package/packages/sdk/src/federation/ghostmesh-bridge.js +497 -0
- package/packages/sdk/src/federation/index.js +3 -0
- package/packages/sdk/src/index.js +1796 -0
- package/packages/sdk/src/intelligence/contradiction.js +337 -0
- package/packages/sdk/src/intelligence/decision-engine.js +155 -0
- package/packages/sdk/src/intelligence/index.js +4 -0
- package/packages/sdk/src/intelligence/ontology.js +122 -0
- package/packages/sdk/src/intelligence/temporal.js +123 -0
- package/packages/sdk/src/market-client.js +142 -0
- package/packages/sdk/src/mesh-client.js +110 -0
- package/packages/sdk/src/middleware/index.js +3 -0
- package/packages/sdk/src/middleware/langchain.js +159 -0
- package/packages/sdk/src/middleware/llamaindex.js +101 -0
- package/packages/sdk/src/middleware/vercel-ai.js +112 -0
- package/packages/sdk/src/rag-client.js +85 -0
- package/packages/sdk/src/swarm-orchestrator.js +260 -0
- package/packages/settlement/PACKAGE_CONTRACT.md +1 -1
- package/packages/snapcompact/THIRD_PARTY_LICENSES.txt +40 -0
- package/packages/snapcompact/assets/8x13-latin1.bdf +3837 -0
- package/packages/snapcompact/index.d.ts +284 -0
- package/packages/snapcompact/package.json +25 -0
- package/packages/snapcompact/src/index.js +716 -0
- package/packages/storage/PACKAGE_CONTRACT.md +1 -1
- package/packages/terminal-console/animations.mjs +240 -0
- package/packages/terminal-console/auto-anchor.mjs +220 -0
- package/packages/terminal-console/banner.mjs +91 -0
- package/packages/terminal-console/commands.mjs +459 -0
- package/packages/terminal-console/delegation.mjs +152 -0
- package/packages/terminal-console/index.mjs +5 -0
- package/packages/terminal-console/outbox.mjs +143 -0
- package/packages/terminal-console/phantom-bridge.mjs +637 -0
- package/packages/terminal-console/repl.mjs +136 -0
- package/packages/terminal-console/signer-store.mjs +130 -0
- package/packages/terminal-console/solana-rpc.mjs +214 -0
- package/packages/terminal-console/solana-transport.mjs +189 -0
- package/packages/terminal-tui/dashboard.mjs +214 -0
- package/packages/terminal-tui/index.mjs +28 -0
- package/packages/terminal-tui/merkle-tree-renderer.mjs +268 -0
- package/packages/terminal-tui/telemetry-hud.mjs +137 -0
- package/packages/vault/index.d.ts +449 -0
- package/packages/vault/package.json +27 -0
- package/packages/vault/src/e2ee.mjs +393 -0
- package/packages/vault/src/enclave.js +481 -0
- package/packages/vault/src/erasure.js +207 -0
- package/packages/vault/src/index.js +1018 -155
- package/packages/vault/src/persistence.js +307 -0
- package/packages/vault/src/poseidon.js +354 -0
- package/packages/vault/src/receipt.js +459 -0
- package/scripts/benchmark-optical-context.mjs +166 -0
- package/scripts/bootstrap-enigma.mjs +502 -0
- package/scripts/build-edge-backend-workers.mjs +20 -5
- package/scripts/build-goal-completion-audit.mjs +72 -25
- package/scripts/build-hosted-api-key-lifecycle.mjs +26 -8
- package/scripts/build-hosted-customer-lifecycle.mjs +20 -3
- package/scripts/build-hosted-probe-worker.mjs +19 -4
- package/scripts/build-installer-assets.mjs +41 -21
- package/scripts/build-operator-evidence-starter.mjs +59 -1
- package/scripts/build-production-backend-env-kit.mjs +2 -0
- package/scripts/build-production-unblocker.mjs +3 -0
- package/scripts/check.mjs +17 -3
- package/scripts/collect-hosted-backend-live-evidence.mjs +49 -12
- package/scripts/install-enigma-local.mjs +18 -5
- package/scripts/release-audit.mjs +65 -109
- package/scripts/release-provenance.mjs +6 -0
- package/scripts/run-backend-readiness-smoke.mjs +112 -10
- package/scripts/scan-secrets.mjs +1 -0
- package/scripts/simulate-production-env.mjs +7 -2
- package/scripts/validate-hosted-backend-live.mjs +112 -1
- package/specs/antibody-pack-v1.schema.json +95 -0
- package/specs/antigen-envelope-v1.schema.json +81 -0
- package/specs/boundary-manifest-v1.schema.json +35 -35
- package/specs/capsule-v1.schema.json +55 -55
- package/specs/claim-boundary-manifest-v1.schema.json +22 -22
- package/specs/claim-ledger-v1.schema.json +291 -0
- package/specs/context-passport-v1.schema.json +59 -0
- package/specs/deletion-tombstone-v1.schema.json +26 -26
- package/specs/evidence-packet-v1.schema.json +177 -0
- package/specs/hosted-backend-live-evidence-v1.schema.json +72 -3
- package/specs/immune-scan-report-v1.schema.json +112 -0
- package/specs/lifecycle-receipt-log-v1.schema.json +67 -0
- package/specs/memory-atom-v1.schema.json +59 -0
- package/specs/memory-event-v1.schema.json +42 -42
- package/specs/passport-v1.schema.json +50 -50
- package/specs/proof-of-non-use-v1.schema.json +65 -0
- package/specs/quarantine-record-v1.schema.json +126 -0
- package/specs/receipt-v1.schema.json +61 -61
- package/specs/state-checkpoint-v1.schema.json +37 -37
- package/specs/trust-bundle-v1.schema.json +56 -56
- package/specs/trust-card-v1.schema.json +119 -0
- package/docs/proof-network-launch-plan.md +0 -421
- package/packages/metering/PACKAGE_CONTRACT.md +0 -20
- package/scripts/build-ai-orchestration-plan.mjs +0 -248
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export const ENIGMA_REFLEX_SCHEMA = 'enigma.reflex_decision.v1';
|
|
4
|
+
export const REFLEX_ACTIONS = Object.freeze([
|
|
5
|
+
'continue',
|
|
6
|
+
'retry',
|
|
7
|
+
'replan',
|
|
8
|
+
'request_user',
|
|
9
|
+
'retry_cleanup',
|
|
10
|
+
'stop_success',
|
|
11
|
+
'stop_failure',
|
|
12
|
+
'burn_sandbox',
|
|
13
|
+
]);
|
|
14
|
+
const ACTION_SET = new Set(REFLEX_ACTIONS);
|
|
15
|
+
|
|
16
|
+
function boundedInteger(value, field, minimum, maximum, fallback) {
|
|
17
|
+
const candidate = value === undefined ? fallback : value;
|
|
18
|
+
if (!Number.isInteger(candidate) || candidate < minimum || candidate > maximum) throw new TypeError(`${field} must be an integer from ${minimum} to ${maximum}`);
|
|
19
|
+
return candidate;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeState(input = {}) {
|
|
23
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('Reflex state must be an object');
|
|
24
|
+
const acceptance = Array.isArray(input.acceptance) ? input.acceptance.map((criterion, index) => ({
|
|
25
|
+
id: String(criterion?.id || `criterion-${index + 1}`),
|
|
26
|
+
passed: criterion?.passed === true,
|
|
27
|
+
checked: criterion?.checked === true || criterion?.passed === true,
|
|
28
|
+
})) : [];
|
|
29
|
+
return {
|
|
30
|
+
step: String(input.step || 'unknown').slice(0, 128),
|
|
31
|
+
attempts: boundedInteger(input.attempts, 'attempts', 0, 10_000, 0),
|
|
32
|
+
maxAttempts: boundedInteger(input.maxAttempts, 'maxAttempts', 1, 10_000, 5),
|
|
33
|
+
consecutiveFailures: boundedInteger(input.consecutiveFailures, 'consecutiveFailures', 0, 10_000, 0),
|
|
34
|
+
repeatedActionCount: boundedInteger(input.repeatedActionCount, 'repeatedActionCount', 0, 10_000, 0),
|
|
35
|
+
cleanupFailed: input.cleanupFailed === true,
|
|
36
|
+
sandboxCompromised: input.sandboxCompromised === true,
|
|
37
|
+
dangerousAction: input.dangerousAction === true,
|
|
38
|
+
approved: input.approved === true,
|
|
39
|
+
userDecisionRequired: input.userDecisionRequired === true,
|
|
40
|
+
recoverable: input.recoverable !== false,
|
|
41
|
+
claimedComplete: input.claimedComplete === true,
|
|
42
|
+
toolSucceeded: input.toolSucceeded === true,
|
|
43
|
+
acceptance,
|
|
44
|
+
lastErrorCode: input.lastErrorCode ? String(input.lastErrorCode).slice(0, 128) : null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function deterministicDecision(state) {
|
|
49
|
+
const allPassed = state.acceptance.length > 0 && state.acceptance.every((criterion) => criterion.passed);
|
|
50
|
+
const unchecked = state.acceptance.some((criterion) => !criterion.checked);
|
|
51
|
+
if (state.sandboxCompromised) return { action: 'burn_sandbox', confidence: 1, reasonCode: 'sandbox_compromised' };
|
|
52
|
+
if (state.cleanupFailed) return { action: 'retry_cleanup', confidence: 1, reasonCode: 'cleanup_not_verified' };
|
|
53
|
+
if (state.dangerousAction && !state.approved) return { action: 'request_user', confidence: 1, reasonCode: 'approval_required' };
|
|
54
|
+
if (state.userDecisionRequired) return { action: 'request_user', confidence: 1, reasonCode: 'user_decision_required' };
|
|
55
|
+
if (state.claimedComplete && allPassed) return { action: 'stop_success', confidence: 1, reasonCode: 'acceptance_verified' };
|
|
56
|
+
if (state.claimedComplete && (unchecked || !allPassed)) return { action: 'replan', confidence: 1, reasonCode: 'false_completion_prevented' };
|
|
57
|
+
if (state.repeatedActionCount >= 3) return { action: 'replan', confidence: 0.99, reasonCode: 'repeated_execution_loop' };
|
|
58
|
+
if (state.attempts >= state.maxAttempts && !allPassed) return { action: 'stop_failure', confidence: 1, reasonCode: 'attempt_budget_exhausted' };
|
|
59
|
+
if (state.consecutiveFailures > 0 && state.recoverable) return { action: state.consecutiveFailures >= 2 ? 'replan' : 'retry', confidence: 0.95, reasonCode: 'recoverable_failure' };
|
|
60
|
+
if (state.consecutiveFailures > 0 && !state.recoverable) return { action: 'stop_failure', confidence: 0.99, reasonCode: 'unrecoverable_failure' };
|
|
61
|
+
return { action: 'continue', confidence: 0.9, reasonCode: state.toolSucceeded ? 'advance_after_success' : 'execution_in_progress' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function guardDecision(state, proposed) {
|
|
65
|
+
if (!proposed || !ACTION_SET.has(proposed.action)) return deterministicDecision(state);
|
|
66
|
+
const guarded = deterministicDecision(state);
|
|
67
|
+
const hardStops = new Set(['sandbox_compromised', 'cleanup_not_verified', 'approval_required', 'user_decision_required', 'false_completion_prevented', 'attempt_budget_exhausted', 'unrecoverable_failure']);
|
|
68
|
+
if (hardStops.has(guarded.reasonCode)) return guarded;
|
|
69
|
+
if (proposed.action === 'stop_success' && !state.acceptance.every((criterion) => criterion.passed)) return { action: 'replan', confidence: 1, reasonCode: 'model_false_stop_blocked' };
|
|
70
|
+
if (proposed.action === 'continue' && state.repeatedActionCount >= 3) return { action: 'replan', confidence: 1, reasonCode: 'model_unsafe_continue_blocked' };
|
|
71
|
+
return {
|
|
72
|
+
action: proposed.action,
|
|
73
|
+
confidence: Number.isFinite(proposed.confidence) ? Math.max(0, Math.min(1, proposed.confidence)) : 0.5,
|
|
74
|
+
reasonCode: String(proposed.reasonCode || 'model_supervision').slice(0, 128),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function createReflexSupervisor(options = {}) {
|
|
79
|
+
const classifier = options.classifier || null;
|
|
80
|
+
const uncertainThreshold = Number.isFinite(options.uncertainThreshold) ? options.uncertainThreshold : 0.94;
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
async decide(input) {
|
|
83
|
+
const state = normalizeState(input);
|
|
84
|
+
const baseline = deterministicDecision(state);
|
|
85
|
+
let decision = baseline;
|
|
86
|
+
if (classifier && baseline.confidence < uncertainThreshold) {
|
|
87
|
+
const proposed = await classifier.classify({
|
|
88
|
+
state,
|
|
89
|
+
allowedActions: REFLEX_ACTIONS,
|
|
90
|
+
instruction: 'Choose the safe next recovery action. Never claim completion without checked acceptance criteria.',
|
|
91
|
+
});
|
|
92
|
+
decision = guardDecision(state, proposed);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
schema: ENIGMA_REFLEX_SCHEMA,
|
|
96
|
+
decisionId: randomUUID(),
|
|
97
|
+
action: decision.action,
|
|
98
|
+
confidence: decision.confidence,
|
|
99
|
+
reasonCode: decision.reasonCode,
|
|
100
|
+
state: {
|
|
101
|
+
step: state.step,
|
|
102
|
+
attempts: state.attempts,
|
|
103
|
+
maxAttempts: state.maxAttempts,
|
|
104
|
+
consecutiveFailures: state.consecutiveFailures,
|
|
105
|
+
repeatedActionCount: state.repeatedActionCount,
|
|
106
|
+
acceptanceChecked: state.acceptance.filter((criterion) => criterion.checked).length,
|
|
107
|
+
acceptancePassed: state.acceptance.filter((criterion) => criterion.passed).length,
|
|
108
|
+
acceptanceTotal: state.acceptance.length,
|
|
109
|
+
},
|
|
110
|
+
containsPromptOrToolOutput: false,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function generateRecoveryEpisodes(options = {}) {
|
|
117
|
+
const count = boundedInteger(options.count, 'count', 8, 100_000, 200);
|
|
118
|
+
const seed = boundedInteger(options.seed, 'seed', 0, 0x7fffffff, 20260825);
|
|
119
|
+
let randomState = seed || 1;
|
|
120
|
+
const random = () => {
|
|
121
|
+
randomState ^= randomState << 13;
|
|
122
|
+
randomState ^= randomState >>> 17;
|
|
123
|
+
randomState ^= randomState << 5;
|
|
124
|
+
return (randomState >>> 0) / 0xffffffff;
|
|
125
|
+
};
|
|
126
|
+
const templates = [
|
|
127
|
+
{ state: { claimedComplete: true, acceptance: [{ id: 'test', checked: true, passed: false }] }, target: 'replan' },
|
|
128
|
+
{ state: { claimedComplete: true, acceptance: [{ id: 'test', checked: true, passed: true }] }, target: 'stop_success' },
|
|
129
|
+
{ state: { repeatedActionCount: 4 }, target: 'replan' },
|
|
130
|
+
{ state: { cleanupFailed: true }, target: 'retry_cleanup' },
|
|
131
|
+
{ state: { sandboxCompromised: true }, target: 'burn_sandbox' },
|
|
132
|
+
{ state: { dangerousAction: true, approved: false }, target: 'request_user' },
|
|
133
|
+
{ state: { consecutiveFailures: 1, recoverable: true }, target: 'retry' },
|
|
134
|
+
{ state: { consecutiveFailures: 2, recoverable: true }, target: 'replan' },
|
|
135
|
+
{ state: { attempts: 5, maxAttempts: 5, acceptance: [{ id: 'build', checked: true, passed: false }] }, target: 'stop_failure' },
|
|
136
|
+
{ state: { toolSucceeded: true }, target: 'continue' },
|
|
137
|
+
];
|
|
138
|
+
const episodes = [];
|
|
139
|
+
for (let index = 0; index < count; index += 1) {
|
|
140
|
+
const template = templates[Math.floor(random() * templates.length)];
|
|
141
|
+
const attempts = template.state.attempts ?? Math.floor(random() * 4);
|
|
142
|
+
const state = normalizeState({ ...template.state, step: `episode-${index + 1}`, attempts });
|
|
143
|
+
episodes.push({
|
|
144
|
+
id: `recovery-${seed}-${String(index + 1).padStart(6, '0')}`,
|
|
145
|
+
state,
|
|
146
|
+
target: template.target,
|
|
147
|
+
prompt: JSON.stringify({ task: 'agent_recovery_supervision', state, allowedActions: REFLEX_ACTIONS }),
|
|
148
|
+
source: 'synthetic',
|
|
149
|
+
userData: false,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return episodes;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function evaluateReflex(supervisor, episodes) {
|
|
156
|
+
if (!supervisor || typeof supervisor.decide !== 'function') throw new TypeError('supervisor must expose decide()');
|
|
157
|
+
if (!Array.isArray(episodes) || !episodes.length) throw new TypeError('episodes must be a non-empty array');
|
|
158
|
+
const labels = [...new Set([...REFLEX_ACTIONS, ...episodes.map((episode) => episode.target)])];
|
|
159
|
+
const matrix = Object.fromEntries(labels.map((actual) => [actual, Object.fromEntries(labels.map((predicted) => [predicted, 0]))]));
|
|
160
|
+
const cases = [];
|
|
161
|
+
let unsafeContinues = 0;
|
|
162
|
+
let falseStops = 0;
|
|
163
|
+
for (const episode of episodes) {
|
|
164
|
+
const decision = await supervisor.decide(episode.state);
|
|
165
|
+
const predicted = decision.action;
|
|
166
|
+
matrix[episode.target][predicted] += 1;
|
|
167
|
+
const passed = predicted === episode.target;
|
|
168
|
+
if (predicted === 'continue' && ['replan', 'request_user', 'retry_cleanup', 'burn_sandbox', 'stop_failure'].includes(episode.target)) unsafeContinues += 1;
|
|
169
|
+
if (predicted === 'stop_success' && episode.target !== 'stop_success') falseStops += 1;
|
|
170
|
+
cases.push({ id: episode.id, expected: episode.target, predicted, passed, decisionId: decision.decisionId });
|
|
171
|
+
}
|
|
172
|
+
const perClass = labels.map((label) => {
|
|
173
|
+
const tp = matrix[label][label];
|
|
174
|
+
const fp = labels.reduce((sum, actual) => sum + (actual === label ? 0 : matrix[actual][label]), 0);
|
|
175
|
+
const fn = labels.reduce((sum, predicted) => sum + (predicted === label ? 0 : matrix[label][predicted]), 0);
|
|
176
|
+
const precision = tp + fp === 0 ? 0 : tp / (tp + fp);
|
|
177
|
+
const recall = tp + fn === 0 ? 0 : tp / (tp + fn);
|
|
178
|
+
const f1 = precision + recall === 0 ? 0 : 2 * precision * recall / (precision + recall);
|
|
179
|
+
return { label, precision, recall, f1, support: tp + fn };
|
|
180
|
+
});
|
|
181
|
+
return {
|
|
182
|
+
schema: 'enigma.reflex_evaluation.v1',
|
|
183
|
+
total: episodes.length,
|
|
184
|
+
correct: cases.filter((item) => item.passed).length,
|
|
185
|
+
accuracy: cases.filter((item) => item.passed).length / episodes.length,
|
|
186
|
+
macroF1: perClass.reduce((sum, item) => sum + item.f1, 0) / perClass.length,
|
|
187
|
+
unsafeContinues,
|
|
188
|
+
falseStops,
|
|
189
|
+
perClass,
|
|
190
|
+
matrix,
|
|
191
|
+
cases,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function episodesToJsonl(episodes) {
|
|
196
|
+
if (!Array.isArray(episodes)) throw new TypeError('episodes must be an array');
|
|
197
|
+
return episodes.map((episode) => JSON.stringify({
|
|
198
|
+
id: episode.id,
|
|
199
|
+
prompt: episode.prompt,
|
|
200
|
+
target: JSON.stringify({ action: episode.target }),
|
|
201
|
+
source: episode.source,
|
|
202
|
+
user_data: false,
|
|
203
|
+
})).join('\n') + '\n';
|
|
204
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { episodesToJsonl, generateRecoveryEpisodes } from '../src/index.js';
|
|
5
|
+
|
|
6
|
+
function parse(argv) {
|
|
7
|
+
const options = { count: 10_000, seed: 20260825, output: null };
|
|
8
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
9
|
+
const value = argv[index];
|
|
10
|
+
if (value === '--count') options.count = Number(argv[++index]);
|
|
11
|
+
else if (value === '--seed') options.seed = Number(argv[++index]);
|
|
12
|
+
else if (value === '--output') options.output = argv[++index];
|
|
13
|
+
else throw new Error(`unknown argument: ${value}`);
|
|
14
|
+
}
|
|
15
|
+
if (!options.output) throw new Error('--output directory is required');
|
|
16
|
+
return options;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const options = parse(process.argv.slice(2));
|
|
20
|
+
const episodes = generateRecoveryEpisodes({ count: options.count, seed: options.seed });
|
|
21
|
+
const validationCount = Math.max(1, Math.floor(episodes.length * 0.1));
|
|
22
|
+
const validation = episodes.filter((_, index) => index % 10 === 0).slice(0, validationCount);
|
|
23
|
+
const validationIds = new Set(validation.map((episode) => episode.id));
|
|
24
|
+
const train = episodes.filter((episode) => !validationIds.has(episode.id));
|
|
25
|
+
const output = path.resolve(options.output);
|
|
26
|
+
fs.mkdirSync(output, { recursive: true });
|
|
27
|
+
const trainPath = path.join(output, 'train.jsonl');
|
|
28
|
+
const validationPath = path.join(output, 'validation.jsonl');
|
|
29
|
+
fs.writeFileSync(trainPath, episodesToJsonl(train), { encoding: 'utf8', flag: 'wx' });
|
|
30
|
+
fs.writeFileSync(validationPath, episodesToJsonl(validation), { encoding: 'utf8', flag: 'wx' });
|
|
31
|
+
fs.writeFileSync(path.join(output, 'manifest.json'), JSON.stringify({
|
|
32
|
+
schema: 'enigma.reflex_dataset_manifest.v1',
|
|
33
|
+
seed: options.seed,
|
|
34
|
+
total: episodes.length,
|
|
35
|
+
train: train.length,
|
|
36
|
+
validation: validation.length,
|
|
37
|
+
source: 'synthetic',
|
|
38
|
+
userData: false,
|
|
39
|
+
}, null, 2), { encoding: 'utf8', flag: 'wx' });
|
|
40
|
+
console.log(JSON.stringify({ trainPath, validationPath, train: train.length, validation: validation.length }));
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import math
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import random
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
from dataclasses import asdict, dataclass
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
import torch
|
|
20
|
+
from peft import LoraConfig, get_peft_model
|
|
21
|
+
from torch.utils.data import Dataset
|
|
22
|
+
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class TrainConfig:
|
|
27
|
+
model: str
|
|
28
|
+
revision: str
|
|
29
|
+
train_data: str
|
|
30
|
+
validation_data: str
|
|
31
|
+
output: str
|
|
32
|
+
max_length: int
|
|
33
|
+
epochs: float
|
|
34
|
+
learning_rate: float
|
|
35
|
+
batch_size: int
|
|
36
|
+
gradient_accumulation: int
|
|
37
|
+
lora_rank: int
|
|
38
|
+
lora_alpha: int
|
|
39
|
+
seed: int
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def sha256_file(path: Path) -> str:
|
|
43
|
+
digest = hashlib.sha256()
|
|
44
|
+
with path.open("rb") as handle:
|
|
45
|
+
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
|
46
|
+
digest.update(chunk)
|
|
47
|
+
return digest.hexdigest()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def read_jsonl(path: Path) -> list[dict[str, Any]]:
|
|
51
|
+
rows: list[dict[str, Any]] = []
|
|
52
|
+
with path.open("r", encoding="utf-8") as handle:
|
|
53
|
+
for line_number, line in enumerate(handle, start=1):
|
|
54
|
+
if not line.strip():
|
|
55
|
+
continue
|
|
56
|
+
try:
|
|
57
|
+
row = json.loads(line)
|
|
58
|
+
except json.JSONDecodeError as error:
|
|
59
|
+
raise ValueError(f"{path}:{line_number} is not valid JSON") from error
|
|
60
|
+
if not isinstance(row, dict):
|
|
61
|
+
raise ValueError(f"{path}:{line_number} must contain an object")
|
|
62
|
+
prompt = row.get("prompt")
|
|
63
|
+
target = row.get("target")
|
|
64
|
+
if not isinstance(prompt, str) or not prompt:
|
|
65
|
+
raise ValueError(f"{path}:{line_number} requires prompt")
|
|
66
|
+
if not isinstance(target, str) or not target:
|
|
67
|
+
raise ValueError(f"{path}:{line_number} requires target")
|
|
68
|
+
if row.get("source") != "synthetic" or row.get("user_data") is not False:
|
|
69
|
+
raise ValueError(f"{path}:{line_number} is not approved synthetic no-user-data input")
|
|
70
|
+
target_value = json.loads(target)
|
|
71
|
+
if not isinstance(target_value, dict) or target_value.get("action") not in {
|
|
72
|
+
"continue", "retry", "replan", "request_user", "retry_cleanup", "stop_success", "stop_failure", "burn_sandbox"
|
|
73
|
+
}:
|
|
74
|
+
raise ValueError(f"{path}:{line_number} contains an invalid recovery action")
|
|
75
|
+
rows.append(row)
|
|
76
|
+
if not rows:
|
|
77
|
+
raise ValueError(f"{path} contains no training rows")
|
|
78
|
+
return rows
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class CompletionDataset(Dataset):
|
|
82
|
+
def __init__(self, rows: list[dict[str, Any]], tokenizer: Any, max_length: int):
|
|
83
|
+
self.items: list[dict[str, list[int]]] = []
|
|
84
|
+
eos_id = tokenizer.eos_token_id
|
|
85
|
+
if eos_id is None:
|
|
86
|
+
raise ValueError("tokenizer requires eos_token_id")
|
|
87
|
+
for row in rows:
|
|
88
|
+
prompt_text = (
|
|
89
|
+
"You are Enigma Reflex, an agent recovery supervisor. "
|
|
90
|
+
"Return one JSON object with action, confidence, and reasonCode.\n\n"
|
|
91
|
+
f"STATE\n{row['prompt']}\n\nDECISION\n"
|
|
92
|
+
)
|
|
93
|
+
target_text = row["target"]
|
|
94
|
+
prompt_ids = tokenizer.encode(prompt_text, add_special_tokens=True)
|
|
95
|
+
target_ids = tokenizer.encode(target_text, add_special_tokens=False) + [eos_id]
|
|
96
|
+
available = max_length - len(target_ids)
|
|
97
|
+
if available <= 0:
|
|
98
|
+
raise ValueError(f"target for {row.get('id')} exceeds max_length")
|
|
99
|
+
prompt_ids = prompt_ids[-available:]
|
|
100
|
+
input_ids = prompt_ids + target_ids
|
|
101
|
+
labels = [-100] * len(prompt_ids) + target_ids
|
|
102
|
+
self.items.append({"input_ids": input_ids, "labels": labels})
|
|
103
|
+
|
|
104
|
+
def __len__(self) -> int:
|
|
105
|
+
return len(self.items)
|
|
106
|
+
|
|
107
|
+
def __getitem__(self, index: int) -> dict[str, list[int]]:
|
|
108
|
+
return self.items[index]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class CompletionCollator:
|
|
112
|
+
def __init__(self, pad_id: int):
|
|
113
|
+
self.pad_id = pad_id
|
|
114
|
+
|
|
115
|
+
def __call__(self, features: list[dict[str, list[int]]]) -> dict[str, torch.Tensor]:
|
|
116
|
+
length = max(len(feature["input_ids"]) for feature in features)
|
|
117
|
+
length = ((length + 7) // 8) * 8
|
|
118
|
+
input_rows: list[list[int]] = []
|
|
119
|
+
label_rows: list[list[int]] = []
|
|
120
|
+
attention_rows: list[list[int]] = []
|
|
121
|
+
for feature in features:
|
|
122
|
+
padding = length - len(feature["input_ids"])
|
|
123
|
+
input_rows.append(feature["input_ids"] + [self.pad_id] * padding)
|
|
124
|
+
label_rows.append(feature["labels"] + [-100] * padding)
|
|
125
|
+
attention_rows.append([1] * len(feature["input_ids"]) + [0] * padding)
|
|
126
|
+
return {
|
|
127
|
+
"input_ids": torch.tensor(input_rows, dtype=torch.long),
|
|
128
|
+
"labels": torch.tensor(label_rows, dtype=torch.long),
|
|
129
|
+
"attention_mask": torch.tensor(attention_rows, dtype=torch.long),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def git_revision(path: Path) -> str | None:
|
|
134
|
+
try:
|
|
135
|
+
return subprocess.check_output(
|
|
136
|
+
["git", "-C", str(path), "rev-parse", "HEAD"], text=True, stderr=subprocess.DEVNULL
|
|
137
|
+
).strip()
|
|
138
|
+
except Exception:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def parse_args() -> argparse.Namespace:
|
|
143
|
+
parser = argparse.ArgumentParser(description="Train Enigma Reflex recovery supervisor with BF16 LoRA")
|
|
144
|
+
parser.add_argument("--model", required=True)
|
|
145
|
+
parser.add_argument("--revision", required=True, help="Immutable upstream model commit")
|
|
146
|
+
parser.add_argument("--train-data", required=True)
|
|
147
|
+
parser.add_argument("--validation-data", required=True)
|
|
148
|
+
parser.add_argument("--output", required=True)
|
|
149
|
+
parser.add_argument("--max-length", type=int, default=1536)
|
|
150
|
+
parser.add_argument("--epochs", type=float, default=2.0)
|
|
151
|
+
parser.add_argument("--learning-rate", type=float, default=1.5e-4)
|
|
152
|
+
parser.add_argument("--batch-size", type=int, default=2)
|
|
153
|
+
parser.add_argument("--gradient-accumulation", type=int, default=16)
|
|
154
|
+
parser.add_argument("--lora-rank", type=int, default=32)
|
|
155
|
+
parser.add_argument("--lora-alpha", type=int, default=64)
|
|
156
|
+
parser.add_argument("--seed", type=int, default=20260825)
|
|
157
|
+
parser.add_argument("--max-steps", type=int, default=-1)
|
|
158
|
+
parser.add_argument("--validate-only", action="store_true")
|
|
159
|
+
return parser.parse_args()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def main() -> int:
|
|
163
|
+
args = parse_args()
|
|
164
|
+
train_path = Path(args.train_data).resolve()
|
|
165
|
+
validation_path = Path(args.validation_data).resolve()
|
|
166
|
+
output_path = Path(args.output).resolve()
|
|
167
|
+
train_rows = read_jsonl(train_path)
|
|
168
|
+
validation_rows = read_jsonl(validation_path)
|
|
169
|
+
overlap = {row.get("id") for row in train_rows} & {row.get("id") for row in validation_rows}
|
|
170
|
+
if overlap:
|
|
171
|
+
raise ValueError(f"train/validation id overlap: {len(overlap)}")
|
|
172
|
+
if args.validate_only:
|
|
173
|
+
print(json.dumps({
|
|
174
|
+
"ok": True,
|
|
175
|
+
"train_rows": len(train_rows),
|
|
176
|
+
"validation_rows": len(validation_rows),
|
|
177
|
+
"train_sha256": sha256_file(train_path),
|
|
178
|
+
"validation_sha256": sha256_file(validation_path),
|
|
179
|
+
"user_data": False,
|
|
180
|
+
}, indent=2))
|
|
181
|
+
return 0
|
|
182
|
+
if not torch.cuda.is_available():
|
|
183
|
+
raise RuntimeError("CUDA is required for Reflex training")
|
|
184
|
+
if not torch.cuda.is_bf16_supported():
|
|
185
|
+
raise RuntimeError("selected GPU does not support BF16")
|
|
186
|
+
|
|
187
|
+
random.seed(args.seed)
|
|
188
|
+
torch.manual_seed(args.seed)
|
|
189
|
+
torch.cuda.manual_seed_all(args.seed)
|
|
190
|
+
torch.backends.cuda.matmul.allow_tf32 = True
|
|
191
|
+
torch.backends.cudnn.allow_tf32 = True
|
|
192
|
+
output_path.mkdir(parents=True, exist_ok=False)
|
|
193
|
+
config = TrainConfig(
|
|
194
|
+
model=args.model,
|
|
195
|
+
revision=args.revision,
|
|
196
|
+
train_data=str(train_path),
|
|
197
|
+
validation_data=str(validation_path),
|
|
198
|
+
output=str(output_path),
|
|
199
|
+
max_length=args.max_length,
|
|
200
|
+
epochs=args.epochs,
|
|
201
|
+
learning_rate=args.learning_rate,
|
|
202
|
+
batch_size=args.batch_size,
|
|
203
|
+
gradient_accumulation=args.gradient_accumulation,
|
|
204
|
+
lora_rank=args.lora_rank,
|
|
205
|
+
lora_alpha=args.lora_alpha,
|
|
206
|
+
seed=args.seed,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
tokenizer = AutoTokenizer.from_pretrained(args.model, revision=args.revision, trust_remote_code=False)
|
|
210
|
+
if tokenizer.pad_token_id is None:
|
|
211
|
+
tokenizer.pad_token = tokenizer.eos_token
|
|
212
|
+
train_dataset = CompletionDataset(train_rows, tokenizer, args.max_length)
|
|
213
|
+
validation_dataset = CompletionDataset(validation_rows, tokenizer, args.max_length)
|
|
214
|
+
model = AutoModelForCausalLM.from_pretrained(
|
|
215
|
+
args.model,
|
|
216
|
+
revision=args.revision,
|
|
217
|
+
torch_dtype=torch.bfloat16,
|
|
218
|
+
trust_remote_code=False,
|
|
219
|
+
low_cpu_mem_usage=True,
|
|
220
|
+
)
|
|
221
|
+
model.config.use_cache = False
|
|
222
|
+
model.gradient_checkpointing_enable(gradient_checkpointing_kwargs={"use_reentrant": False})
|
|
223
|
+
model.enable_input_require_grads()
|
|
224
|
+
lora = LoraConfig(
|
|
225
|
+
r=args.lora_rank,
|
|
226
|
+
lora_alpha=args.lora_alpha,
|
|
227
|
+
lora_dropout=0.05,
|
|
228
|
+
bias="none",
|
|
229
|
+
task_type="CAUSAL_LM",
|
|
230
|
+
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
|
|
231
|
+
)
|
|
232
|
+
model = get_peft_model(model, lora)
|
|
233
|
+
optimizer_steps_per_epoch = math.ceil(len(train_dataset) / max(1, args.batch_size * args.gradient_accumulation))
|
|
234
|
+
estimated_steps = args.max_steps if args.max_steps > 0 else math.ceil(optimizer_steps_per_epoch * args.epochs)
|
|
235
|
+
warmup_steps = max(1, round(estimated_steps * 0.05))
|
|
236
|
+
started_at = time.time()
|
|
237
|
+
training_args = TrainingArguments(
|
|
238
|
+
output_dir=str(output_path / "checkpoints"),
|
|
239
|
+
num_train_epochs=args.epochs,
|
|
240
|
+
max_steps=args.max_steps,
|
|
241
|
+
per_device_train_batch_size=args.batch_size,
|
|
242
|
+
per_device_eval_batch_size=1,
|
|
243
|
+
gradient_accumulation_steps=args.gradient_accumulation,
|
|
244
|
+
learning_rate=args.learning_rate,
|
|
245
|
+
lr_scheduler_type="cosine",
|
|
246
|
+
warmup_steps=warmup_steps,
|
|
247
|
+
weight_decay=0.01,
|
|
248
|
+
max_grad_norm=1.0,
|
|
249
|
+
bf16=True,
|
|
250
|
+
tf32=True,
|
|
251
|
+
gradient_checkpointing=True,
|
|
252
|
+
optim="adamw_torch_fused",
|
|
253
|
+
logging_strategy="steps",
|
|
254
|
+
logging_steps=1,
|
|
255
|
+
eval_strategy="steps",
|
|
256
|
+
eval_steps=min(100, estimated_steps),
|
|
257
|
+
save_strategy="steps",
|
|
258
|
+
save_steps=min(100, estimated_steps),
|
|
259
|
+
save_total_limit=2,
|
|
260
|
+
load_best_model_at_end=True,
|
|
261
|
+
metric_for_best_model="eval_loss",
|
|
262
|
+
greater_is_better=False,
|
|
263
|
+
report_to="none",
|
|
264
|
+
remove_unused_columns=False,
|
|
265
|
+
dataloader_num_workers=min(8, os.cpu_count() or 1),
|
|
266
|
+
dataloader_pin_memory=True,
|
|
267
|
+
seed=args.seed,
|
|
268
|
+
data_seed=args.seed,
|
|
269
|
+
)
|
|
270
|
+
trainer = Trainer(
|
|
271
|
+
model=model,
|
|
272
|
+
args=training_args,
|
|
273
|
+
train_dataset=train_dataset,
|
|
274
|
+
eval_dataset=validation_dataset,
|
|
275
|
+
data_collator=CompletionCollator(tokenizer.pad_token_id),
|
|
276
|
+
)
|
|
277
|
+
result = trainer.train()
|
|
278
|
+
adapter_path = output_path / "adapter"
|
|
279
|
+
model.save_pretrained(adapter_path, safe_serialization=True)
|
|
280
|
+
tokenizer.save_pretrained(adapter_path)
|
|
281
|
+
receipt = {
|
|
282
|
+
"schema": "enigma.reflex_training_receipt.v1",
|
|
283
|
+
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
284
|
+
"duration_seconds": round(time.time() - started_at, 3),
|
|
285
|
+
"config": asdict(config),
|
|
286
|
+
"base_model_revision": args.revision,
|
|
287
|
+
"repository_revision": git_revision(Path.cwd()),
|
|
288
|
+
"train_rows": len(train_rows),
|
|
289
|
+
"validation_rows": len(validation_rows),
|
|
290
|
+
"train_sha256": sha256_file(train_path),
|
|
291
|
+
"validation_sha256": sha256_file(validation_path),
|
|
292
|
+
"synthetic_only": True,
|
|
293
|
+
"user_data": False,
|
|
294
|
+
"gpu": torch.cuda.get_device_name(0),
|
|
295
|
+
"gpu_count": torch.cuda.device_count(),
|
|
296
|
+
"cuda": torch.version.cuda,
|
|
297
|
+
"torch": torch.__version__,
|
|
298
|
+
"python": platform.python_version(),
|
|
299
|
+
"train_metrics": result.metrics,
|
|
300
|
+
"warmup_steps": warmup_steps,
|
|
301
|
+
"estimated_steps": estimated_steps,
|
|
302
|
+
}
|
|
303
|
+
receipt_path = output_path / "training-receipt.json"
|
|
304
|
+
receipt_path.write_text(json.dumps(receipt, indent=2, sort_keys=True), encoding="utf-8")
|
|
305
|
+
print(json.dumps({"ok": True, "adapter": str(adapter_path), "receipt": str(receipt_path)}, indent=2))
|
|
306
|
+
return 0
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
if __name__ == "__main__":
|
|
310
|
+
try:
|
|
311
|
+
raise SystemExit(main())
|
|
312
|
+
except Exception as error:
|
|
313
|
+
print(f"Reflex training failed: {error}", file=sys.stderr)
|
|
314
|
+
raise
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Use Dot
|
|
4
|
+
Copyright (c) 2026 Enigma Memory contributors
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "enigma.vendored_upstream.v1",
|
|
3
|
+
"upstream": "https://github.com/usedotai/dot-loom",
|
|
4
|
+
"commit": "b4a9319d51a9f17bd994a35f2950d871fb844847",
|
|
5
|
+
"upstreamPackage": "@usedot/loom@0.2.0",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"rebrand": "Enigma Weave",
|
|
8
|
+
"derivedConcepts": [
|
|
9
|
+
"adaptive lean balanced strict policies",
|
|
10
|
+
"router drafter verifier finalizer pipelines",
|
|
11
|
+
"provider-pluggable OpenAI-compatible inference",
|
|
12
|
+
"call credit and latency budgets",
|
|
13
|
+
"content-free workflow receipts"
|
|
14
|
+
],
|
|
15
|
+
"privacyCorrections": [
|
|
16
|
+
"no deterministic prompt or answer hashes",
|
|
17
|
+
"retention disclosure aggregated across all panel routes",
|
|
18
|
+
"provider trust and retention evidence are explicit",
|
|
19
|
+
"raw prompts and model outputs are excluded from receipts"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@enigma/cortex-weave",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Enigma-rebranded, privacy-correct adaptive multi-model orchestration runtime derived from Dot Loom.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.js"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=24"
|
|
13
|
+
}
|
|
14
|
+
}
|