archgraph-argo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +115 -0
- package/argo/package.json +8 -0
- package/argo/rules/intent-architecture-global-rule.md +45 -0
- package/argo/schema/ImplementationToCodingHandoff.schema.json +252 -0
- package/argo/schema/ImplementationToIntentTraceProposal.schema.json +180 -0
- package/argo/schema/IntentToImplementationHandoff.schema.json +75 -0
- package/argo/schema/SystemArchitecture.schema.json +378 -0
- package/argo/schema/archimate3.2.pdf +0 -0
- package/argo/scripts/ARCHITECTURE.md +57 -0
- package/argo/scripts/archimate32-rules.js +12301 -0
- package/argo/scripts/argo-mcp-server.js +629 -0
- package/argo/scripts/argo-paths.js +77 -0
- package/argo/scripts/ensureArgoHarnessEnvironment.js +340 -0
- package/argo/scripts/generateArchitectureDiffPlantuml.js +466 -0
- package/argo/scripts/graph-rag/ARCHITECTURE.md +192 -0
- package/argo/scripts/graph-rag/canonicalProjectionAuthority.js +45 -0
- package/argo/scripts/graph-rag/defaultSemanticRetrieval.js +969 -0
- package/argo/scripts/graph-rag/embeddingQualificationGate.js +59 -0
- package/argo/scripts/graph-rag/externalProductionConfig.js +74 -0
- package/argo/scripts/graph-rag/liveEmbeddingIndexGate.js +129 -0
- package/argo/scripts/graph-rag/liveEmbeddingNeo4jBoundary.js +137 -0
- package/argo/scripts/graph-rag/liveEmbeddingProviderClient.js +49 -0
- package/argo/scripts/graph-rag/liveEmbeddingProviderConfig.js +481 -0
- package/argo/scripts/graph-rag/mutationEmbeddingVectorLifecycle.js +1261 -0
- package/argo/scripts/graph-rag/neo4jNativeRetrieval.js +37 -0
- package/argo/scripts/graph-rag/productionGraphRagRuntime.js +1624 -0
- package/argo/scripts/graph-rag/semantic-persistence/ARCHITECTURE.md +51 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticBackfill.js +241 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticCheckpointStore.js +99 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticNeo4jAdapter.js +149 -0
- package/argo/scripts/graph-rag/semantic-persistence/productionSemanticProjectionStore.js +171 -0
- package/argo/scripts/graph-rag/semanticOperatorError.js +38 -0
- package/argo/scripts/graph-rag/semanticOperatorJourney.js +459 -0
- package/argo/scripts/graph-rag/semanticReadinessAttestationStore.js +398 -0
- package/argo/scripts/graph-rag/systemMetadataCommandAdapter.js +269 -0
- package/argo/scripts/graph-semantics.js +220 -0
- package/argo/scripts/neo4j-system-architecture-store.js +777 -0
- package/argo/scripts/repositoryArgoEnvironment.js +101 -0
- package/argo/scripts/runArchitectureTests.js +583 -0
- package/argo/scripts/semanticOperatorJourneyCli.js +91 -0
- package/argo/scripts/syncSystemArchitectureToNeo4j.js +67 -0
- package/argo/scripts/systemarchitecture-mcp-server.js +2965 -0
- package/argo/scripts/test-executors/_template.js +58 -0
- package/argo/scripts/test-executors/default.js +199 -0
- package/argo/scripts/validateStageHandoff.js +459 -0
- package/argo/scripts/validateSystemArchitecture.js +254 -0
- package/argo/scripts/validateTraceProposal.js +181 -0
- package/argo/scripts/validator-mcp-server.js +377 -0
- package/argo/skills/argo-init/SKILL.md +110 -0
- package/bin/argo-deploy.js +12 -0
- package/install-argo.ps1 +112 -0
- package/package.json +28 -0
- package/vendor/neo4j-driver-6.2.0.tgz +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
function evaluateEmbeddingQualification(qualification) {
|
|
2
|
+
const supplied = qualification && typeof qualification === 'object'
|
|
3
|
+
? qualification
|
|
4
|
+
: {};
|
|
5
|
+
|
|
6
|
+
if (supplied.approvedByHuman !== true) {
|
|
7
|
+
throw blockingError(
|
|
8
|
+
'EMBEDDING_QUALIFICATION_REQUIRED',
|
|
9
|
+
'Embedding configuration requires explicit human approval',
|
|
10
|
+
'approvedByHuman',
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (supplied.source === 'implicit-default') {
|
|
15
|
+
throw blockingError(
|
|
16
|
+
'IMPLICIT_EMBEDDING_DEFAULT_PROHIBITED',
|
|
17
|
+
'Implicit embedding defaults cannot qualify index delivery',
|
|
18
|
+
'source',
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const field of ['provider', 'model', 'version']) {
|
|
23
|
+
if (typeof supplied[field] !== 'string' || supplied[field].trim().length === 0) {
|
|
24
|
+
throw blockingError(
|
|
25
|
+
'EMBEDDING_CONFIGURATION_REQUIRED',
|
|
26
|
+
`${field} must be explicitly supplied`,
|
|
27
|
+
field,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!Number.isInteger(supplied.dimensions) || supplied.dimensions <= 0) {
|
|
33
|
+
throw blockingError(
|
|
34
|
+
'EMBEDDING_CONFIGURATION_REQUIRED',
|
|
35
|
+
'dimensions must be an explicitly supplied positive integer',
|
|
36
|
+
'dimensions',
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
status: 'approved',
|
|
42
|
+
approvedByHuman: true,
|
|
43
|
+
provider: supplied.provider.trim(),
|
|
44
|
+
model: supplied.model.trim(),
|
|
45
|
+
version: supplied.version.trim(),
|
|
46
|
+
dimensions: supplied.dimensions,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function blockingError(category, message, field) {
|
|
51
|
+
const error = new Error(message);
|
|
52
|
+
error.category = category;
|
|
53
|
+
error.field = field;
|
|
54
|
+
return error;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
evaluateEmbeddingQualification,
|
|
59
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const REQUIRED_FIELDS = [
|
|
2
|
+
'neo4jUri',
|
|
3
|
+
'neo4jUsername',
|
|
4
|
+
'neo4jPassword',
|
|
5
|
+
'embeddingCredential',
|
|
6
|
+
];
|
|
7
|
+
const APPROVED_SOURCE_KEYS = new Map([
|
|
8
|
+
['neo4jUri', 'ARGO_NEO4J_DATABASE_URL'],
|
|
9
|
+
['neo4jUsername', 'ARGO_NEO4J_DATABASE_USERNAME'],
|
|
10
|
+
['neo4jPassword', 'ARGO_NEO4J_DATABASE_PASSWORD'],
|
|
11
|
+
['embeddingCredential', 'QWEN_KEY'],
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
function resolveExternalProductionConfig(configuration, context = {}) {
|
|
15
|
+
const supplied = configuration && typeof configuration === 'object'
|
|
16
|
+
? configuration
|
|
17
|
+
: {};
|
|
18
|
+
validateApprovedSourceKeys(context.sourceKeys);
|
|
19
|
+
|
|
20
|
+
for (const field of REQUIRED_FIELDS) {
|
|
21
|
+
const value = supplied[field];
|
|
22
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
23
|
+
throw blockingError(
|
|
24
|
+
'EXTERNAL_CREDENTIALS_REQUIRED',
|
|
25
|
+
`${field} is required for ${context.operation || 'production operation'}`,
|
|
26
|
+
field,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
neo4jUri: supplied.neo4jUri.trim(),
|
|
33
|
+
neo4jUsername: supplied.neo4jUsername.trim(),
|
|
34
|
+
neo4jPassword: supplied.neo4jPassword,
|
|
35
|
+
embeddingCredential: supplied.embeddingCredential,
|
|
36
|
+
...(supplied.neo4jDatabase === undefined
|
|
37
|
+
? {}
|
|
38
|
+
: { neo4jDatabase: supplied.neo4jDatabase }),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validateApprovedSourceKeys(sourceKeys) {
|
|
43
|
+
if (sourceKeys === undefined) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (!(sourceKeys instanceof Map)) {
|
|
47
|
+
throw blockingError(
|
|
48
|
+
'UNAPPROVED_RUNTIME_CONFIG_SOURCE',
|
|
49
|
+
'runtime configuration source provenance is invalid',
|
|
50
|
+
'sourceKeys',
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const field of REQUIRED_FIELDS) {
|
|
55
|
+
if (sourceKeys.get(field) !== APPROVED_SOURCE_KEYS.get(field)) {
|
|
56
|
+
throw blockingError(
|
|
57
|
+
'UNAPPROVED_RUNTIME_CONFIG_SOURCE',
|
|
58
|
+
`${field} is not resolved from its approved source`,
|
|
59
|
+
field,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function blockingError(category, message, field) {
|
|
66
|
+
const error = new Error(message);
|
|
67
|
+
error.category = category;
|
|
68
|
+
error.field = field;
|
|
69
|
+
return error;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
resolveExternalProductionConfig,
|
|
74
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const { evaluateEmbeddingQualification } = require('./embeddingQualificationGate.js');
|
|
2
|
+
const { createLiveEmbeddingProviderClient } = require('./liveEmbeddingProviderClient.js');
|
|
3
|
+
|
|
4
|
+
const IDENTITY_FIELDS = Object.freeze([
|
|
5
|
+
'canonicalIdentity',
|
|
6
|
+
'canonicalVersion',
|
|
7
|
+
'contentIdentity',
|
|
8
|
+
'contentVersion',
|
|
9
|
+
'indexIdentity',
|
|
10
|
+
'indexVersion',
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
function buildSemanticIndexEvidenceRecord(input = {}) {
|
|
14
|
+
const qualification = input.qualification && typeof input.qualification === 'object'
|
|
15
|
+
? input.qualification
|
|
16
|
+
: {};
|
|
17
|
+
const objectType = input.objectType || 'Element';
|
|
18
|
+
const objectId = input.objectId || input.id || `${objectType.toLowerCase()}-record`;
|
|
19
|
+
const indexVersion = input.indexVersion || incrementVersion(input.contentVersion || input.canonicalVersion);
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
objectType,
|
|
22
|
+
objectId,
|
|
23
|
+
channel: input.channel || channelForObjectType(objectType),
|
|
24
|
+
canonicalVersion: input.canonicalVersion || 'canonical-v1',
|
|
25
|
+
contentVersion: input.contentVersion || 'content-v1',
|
|
26
|
+
indexVersion,
|
|
27
|
+
provider: input.provider || qualification.provider || 'approved-test-provider',
|
|
28
|
+
model: input.model || qualification.model || 'approved-test-model',
|
|
29
|
+
version: input.version || input.modelVersion || qualification.version || '2026-07-24',
|
|
30
|
+
modelVersion: input.modelVersion || qualification.version || '2026-07-24',
|
|
31
|
+
dimensions: input.dimensions || qualification.dimensions || 1536,
|
|
32
|
+
...(Array.isArray(input.vector) ? { vector: [...input.vector] } : {}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function createLiveEmbeddingIndexGate(dependencies = {}) {
|
|
37
|
+
const { configuration, transport, indexBoundary } = dependencies;
|
|
38
|
+
if (!configuration || !indexBoundary || typeof indexBoundary.writeEvidence !== 'function') {
|
|
39
|
+
throw safeError('LIVE_PROVIDER_E2E_BOUNDARY_MISSING');
|
|
40
|
+
}
|
|
41
|
+
const client = createLiveEmbeddingProviderClient({ configuration, transport });
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
async executeApprovedEmbedding(input) {
|
|
44
|
+
const qualification = evaluateEmbeddingQualification(input && input.qualification);
|
|
45
|
+
requireApprovedConfiguration(configuration, qualification);
|
|
46
|
+
requireInput(input);
|
|
47
|
+
const vector = await client.embed(input.input);
|
|
48
|
+
if (
|
|
49
|
+
vector.length !== 1024
|
|
50
|
+
|| vector.some(value => typeof value !== 'number' || !Number.isFinite(value))
|
|
51
|
+
) {
|
|
52
|
+
throw safeError('LIVE_PROVIDER_RESPONSE_INVALID');
|
|
53
|
+
}
|
|
54
|
+
const evidence = {
|
|
55
|
+
provider: qualification.provider,
|
|
56
|
+
model: qualification.model,
|
|
57
|
+
qualificationVersion: qualification.version,
|
|
58
|
+
dimensions: qualification.dimensions,
|
|
59
|
+
...Object.fromEntries(IDENTITY_FIELDS.map(field => [field, input[field]])),
|
|
60
|
+
vector,
|
|
61
|
+
};
|
|
62
|
+
try {
|
|
63
|
+
await indexBoundary.writeEvidence(evidence);
|
|
64
|
+
} catch {
|
|
65
|
+
throw safeError('LIVE_PROVIDER_INDEX_WRITE_PROHIBITED');
|
|
66
|
+
}
|
|
67
|
+
return Object.freeze({
|
|
68
|
+
qualification: Object.freeze({ ...qualification }),
|
|
69
|
+
vector,
|
|
70
|
+
evidence: Object.freeze(evidence),
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function requireApprovedConfiguration(configuration, qualification) {
|
|
77
|
+
if (
|
|
78
|
+
qualification.provider !== configuration.embeddingProvider
|
|
79
|
+
|| qualification.model !== configuration.embeddingModel
|
|
80
|
+
|| qualification.version !== configuration.embeddingModelVersion
|
|
81
|
+
|| qualification.dimensions !== configuration.embeddingDimensions
|
|
82
|
+
|| qualification.dimensions !== 1024
|
|
83
|
+
) {
|
|
84
|
+
throw safeError('LIVE_PROVIDER_INDEX_WRITE_PROHIBITED');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function requireInput(input) {
|
|
89
|
+
if (!input || typeof input.input !== 'string' || input.input.trim() === '') {
|
|
90
|
+
throw safeError('LIVE_PROVIDER_INDEX_WRITE_PROHIBITED');
|
|
91
|
+
}
|
|
92
|
+
for (const field of IDENTITY_FIELDS) {
|
|
93
|
+
if (typeof input[field] !== 'string' || input[field].trim() === '') {
|
|
94
|
+
throw safeError('LIVE_PROVIDER_INDEX_WRITE_PROHIBITED');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function safeError(category) {
|
|
100
|
+
const error = new Error(category);
|
|
101
|
+
error.category = category;
|
|
102
|
+
return error;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function channelForObjectType(objectType) {
|
|
106
|
+
if (objectType === 'ArchitectureRelationship') {
|
|
107
|
+
return 'relationships';
|
|
108
|
+
}
|
|
109
|
+
if (objectType === 'View') {
|
|
110
|
+
return 'views';
|
|
111
|
+
}
|
|
112
|
+
return 'elements';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function incrementVersion(value) {
|
|
116
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
117
|
+
return 'index-v1';
|
|
118
|
+
}
|
|
119
|
+
const numericSuffix = value.match(/^(.*?)(\d+)$/);
|
|
120
|
+
if (!numericSuffix) {
|
|
121
|
+
return `${value}-indexed`;
|
|
122
|
+
}
|
|
123
|
+
return `${numericSuffix[1]}${Number(numericSuffix[2]) + 1}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
buildSemanticIndexEvidenceRecord,
|
|
128
|
+
createLiveEmbeddingIndexGate,
|
|
129
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
async function createApprovedNeo4jBoundary({ configuration, neo4j, logger }) {
|
|
2
|
+
if (
|
|
3
|
+
!configuration
|
|
4
|
+
|| !neo4j
|
|
5
|
+
|| !neo4j.auth
|
|
6
|
+
|| typeof neo4j.auth.basic !== 'function'
|
|
7
|
+
|| typeof neo4j.driver !== 'function'
|
|
8
|
+
) {
|
|
9
|
+
throw safeError('LIVE_PROVIDER_NEO4J_BOUNDARY_MISSING');
|
|
10
|
+
}
|
|
11
|
+
const uri = requireValue(configuration, 'neo4jDatabaseUrl');
|
|
12
|
+
const username = requireValue(configuration, 'neo4jDatabaseUsername');
|
|
13
|
+
const password = requireValue(configuration, 'neo4jDatabasePassword');
|
|
14
|
+
let driver;
|
|
15
|
+
try {
|
|
16
|
+
const authentication = neo4j.auth.basic(username, password);
|
|
17
|
+
driver = neo4j.driver(uri, authentication);
|
|
18
|
+
await driver.verifyConnectivity();
|
|
19
|
+
} catch {
|
|
20
|
+
if (driver && typeof driver.close === 'function') {
|
|
21
|
+
try { await driver.close(); } catch {}
|
|
22
|
+
}
|
|
23
|
+
throw safeError('LIVE_PROVIDER_OPERATION_FAILED');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let closed = false;
|
|
27
|
+
async function query(cypher, parameters) {
|
|
28
|
+
if (closed) throw safeError('LIVE_PROVIDER_OPERATION_FAILED');
|
|
29
|
+
const session = driver.session();
|
|
30
|
+
try {
|
|
31
|
+
return await session.run(cypher, parameters);
|
|
32
|
+
} catch {
|
|
33
|
+
throw safeError('LIVE_PROVIDER_OPERATION_FAILED');
|
|
34
|
+
} finally {
|
|
35
|
+
try { await session.close(); } catch {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
async countWrites(runId) {
|
|
41
|
+
const result = await query(
|
|
42
|
+
'MATCH (e:ArgoLiveEmbeddingEvidence { runId: $runId }) RETURN count(e) AS count',
|
|
43
|
+
{ runId },
|
|
44
|
+
);
|
|
45
|
+
const count = result.records[0].get('count');
|
|
46
|
+
return count && typeof count.toNumber === 'function' ? count.toNumber() : Number(count);
|
|
47
|
+
},
|
|
48
|
+
async writeEvidence(runId, evidence) {
|
|
49
|
+
if (evidence === undefined) {
|
|
50
|
+
evidence = runId;
|
|
51
|
+
runId = evidence.runId;
|
|
52
|
+
}
|
|
53
|
+
const normalized = normalizeEvidence(evidence);
|
|
54
|
+
await query(
|
|
55
|
+
'CREATE (e:ArgoLiveEmbeddingEvidence { runId: $runId, vector: $vector, provider: $provider, model: $model, qualificationVersion: $qualificationVersion, dimensions: $dimensions, canonicalIdentity: $canonicalIdentity, canonicalVersion: $canonicalVersion, contentIdentity: $contentIdentity, contentVersion: $contentVersion, indexIdentity: $indexIdentity, indexVersion: $indexVersion })',
|
|
56
|
+
{ runId, ...normalized },
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
async readEvidence(runId) {
|
|
60
|
+
const result = await query(
|
|
61
|
+
'MATCH (e:ArgoLiveEmbeddingEvidence { runId: $runId }) RETURN e { .runId, .provider, .model, .qualificationVersion, .dimensions, .canonicalIdentity, .canonicalVersion, .contentIdentity, .contentVersion, .indexIdentity, .indexVersion, .vector } AS evidence',
|
|
62
|
+
{ runId },
|
|
63
|
+
);
|
|
64
|
+
return result.records.map(record => normalizeNeo4jValue(record.get('evidence')));
|
|
65
|
+
},
|
|
66
|
+
async queryVectorEvidence(runId, vector, canonicalIdentities) {
|
|
67
|
+
if (!Array.isArray(vector) || vector.length !== 1024 || !Array.isArray(canonicalIdentities)) {
|
|
68
|
+
throw safeError('LIVE_PROVIDER_OPERATION_FAILED');
|
|
69
|
+
}
|
|
70
|
+
await query(
|
|
71
|
+
'CREATE VECTOR INDEX argo_live_embedding_vector IF NOT EXISTS FOR (e:ArgoLiveEmbeddingEvidence) ON (e.vector) OPTIONS { indexConfig: { `vector.dimensions`: 1024, `vector.similarity_function`: "cosine" } }',
|
|
72
|
+
{},
|
|
73
|
+
);
|
|
74
|
+
const result = await query(
|
|
75
|
+
'CALL db.index.vector.queryNodes("argo_live_embedding_vector", $limit, $vector) YIELD node, score WHERE node.runId = $runId AND node.canonicalIdentity IN $canonicalIdentities RETURN node { .canonicalIdentity, .canonicalVersion, .contentVersion, .indexVersion, .provider, .model, .qualificationVersion, .dimensions, .vector } AS evidence, score',
|
|
76
|
+
{
|
|
77
|
+
runId,
|
|
78
|
+
vector,
|
|
79
|
+
canonicalIdentities,
|
|
80
|
+
limit: Math.max(1, canonicalIdentities.length * 4),
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
return result.records.map(record => normalizeNeo4jValue(record.get('evidence')));
|
|
84
|
+
},
|
|
85
|
+
async cleanup(runId) {
|
|
86
|
+
await query('MATCH (e:ArgoLiveEmbeddingEvidence { runId: $runId }) DELETE e', { runId });
|
|
87
|
+
return this.countWrites(runId);
|
|
88
|
+
},
|
|
89
|
+
async close() {
|
|
90
|
+
if (closed) return;
|
|
91
|
+
closed = true;
|
|
92
|
+
try {
|
|
93
|
+
await driver.close();
|
|
94
|
+
} catch {
|
|
95
|
+
throw safeError('LIVE_PROVIDER_OPERATION_FAILED');
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeEvidence(evidence) {
|
|
102
|
+
const fields = [
|
|
103
|
+
'provider', 'model', 'qualificationVersion', 'dimensions', 'canonicalIdentity',
|
|
104
|
+
'canonicalVersion', 'contentIdentity', 'contentVersion', 'indexIdentity', 'indexVersion', 'vector',
|
|
105
|
+
];
|
|
106
|
+
if (!evidence || fields.some(field => evidence[field] === undefined)) {
|
|
107
|
+
throw safeError('LIVE_PROVIDER_OPERATION_FAILED');
|
|
108
|
+
}
|
|
109
|
+
return Object.fromEntries(fields.map(field => [field, evidence[field]]));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function normalizeNeo4jValue(evidence) {
|
|
113
|
+
if (!evidence) return evidence;
|
|
114
|
+
const dimensions = evidence.dimensions;
|
|
115
|
+
return {
|
|
116
|
+
...evidence,
|
|
117
|
+
dimensions: dimensions && typeof dimensions.toNumber === 'function'
|
|
118
|
+
? dimensions.toNumber()
|
|
119
|
+
: dimensions,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function requireValue(configuration, field) {
|
|
124
|
+
const value = configuration[field];
|
|
125
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
126
|
+
throw safeError('LIVE_PROVIDER_NEO4J_BOUNDARY_MISSING');
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function safeError(category) {
|
|
132
|
+
const error = new Error(category);
|
|
133
|
+
error.category = category;
|
|
134
|
+
return error;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = { createApprovedNeo4jBoundary };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
function createLiveEmbeddingProviderClient({ configuration, transport }) {
|
|
2
|
+
if (!configuration || !transport || typeof transport.request !== 'function') {
|
|
3
|
+
throw safeError('LIVE_PROVIDER_CONFIGURATION_REQUIRED');
|
|
4
|
+
}
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
async embed(input) {
|
|
7
|
+
let response;
|
|
8
|
+
try {
|
|
9
|
+
response = await transport.request(
|
|
10
|
+
`${configuration.embeddingBaseUrl}/embeddings`,
|
|
11
|
+
{
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
Authorization: `Bearer ${configuration.qwenKey}`,
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
input,
|
|
19
|
+
model: configuration.embeddingModel,
|
|
20
|
+
dimensions: configuration.embeddingDimensions,
|
|
21
|
+
}),
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
} catch {
|
|
25
|
+
throw safeError('LIVE_PROVIDER_REQUEST_FAILED');
|
|
26
|
+
}
|
|
27
|
+
if (!response || response.ok !== true || typeof response.json !== 'function') {
|
|
28
|
+
throw safeError('LIVE_PROVIDER_REQUEST_FAILED');
|
|
29
|
+
}
|
|
30
|
+
let payload;
|
|
31
|
+
try {
|
|
32
|
+
payload = await response.json();
|
|
33
|
+
} catch {
|
|
34
|
+
throw safeError('LIVE_PROVIDER_RESPONSE_INVALID');
|
|
35
|
+
}
|
|
36
|
+
const vector = payload && payload.data && payload.data[0] && payload.data[0].embedding;
|
|
37
|
+
if (!Array.isArray(vector)) throw safeError('LIVE_PROVIDER_RESPONSE_INVALID');
|
|
38
|
+
return vector;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function safeError(category) {
|
|
44
|
+
const error = new Error(category);
|
|
45
|
+
error.category = category;
|
|
46
|
+
return error;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { createLiveEmbeddingProviderClient };
|