1
1
import OpenAI from "openai"
2
- import { AnyNodeShape , GenericMatchToRelationshipType , GenericNodeShape , GenericNodeType } from "../types/NodeType"
2
+ import { GenericNodeShape , GenericNodeType } from "../types/NodeType"
3
3
import { Driver , EagerResult , Integer , Node } from "neo4j-driver"
4
4
import { Ok , UixErr , UixErrSubtype } from "../types/Result"
5
5
import { openAIAction } from "../clients/openai"
6
6
import { neo4jAction } from "../clients/neo4j"
7
+ import dedent from "dedent"
8
+ import { GenericMatchToRelationshipType } from "../types/MatchToRelationshipType"
7
9
8
10
9
11
10
- export const upsertMatchToRelationship = async (
12
+ export const upsertMatchTypeEmbedding = async (
11
13
neo4jDriver : Driver ,
12
14
openaiClient : OpenAI ,
13
- nodeShape : AnyNodeShape ,
14
- matchToRelationshipType : GenericMatchToRelationshipType ,
15
- matchToNodeType : GenericNodeType
15
+ triggerNode : GenericNodeShape ,
16
+ fromNodeType : GenericNodeType ,
17
+ matchToRelationshipType : GenericMatchToRelationshipType
16
18
) => {
17
19
// Try/catch this because you're not going to handle it with application logic.
18
20
// You'll just log it.
19
21
const result = await neo4jAction ( openAIAction ( async ( ) => {
20
22
// Create Node Type Summary
21
- console . log ( "Creating Node Type Summary" , nodeShape )
23
+ console . log ( "Creating Match Type Summary (nodeTypeEmbedding)" , fromNodeType . type )
24
+ // Collect all nodes in weightedNodeTypeSet
25
+ const {
26
+ targetNode,
27
+ weightedNodeSet
28
+ } = await neo4jDriver . executeQuery < EagerResult < {
29
+ weightedNode : Node < Integer , GenericNodeShape > ,
30
+ targetNode : Node < Integer , GenericNodeShape >
31
+ } > > ( dedent /*cypher*/ `
32
+ // Get the node that initiated the change as a reference point
33
+ match (triggerNode:${ triggerNode . nodeType } {nodeId: $triggerNode.nodeId})
34
+ // Get the parent node that is the target of this update
35
+ match (targetNode: ${ fromNodeType . type } )<-[:CHILD_TO|UNIQUE_TO*]-(triggerNode)
36
+ // Get all nodes in the weightedNodeTypeSet
37
+ match (targetNode)<-[:CHILD_TO|UNIQUE_TO*]-(weightedNode: ${ matchToRelationshipType . weightedNodeTypeSet . map ( ( { NodeType } ) => NodeType . type ) . join ( '|' ) } )
38
+ return weightedNode, targetNode
39
+ ` , {
40
+ triggerNode
41
+ } ) . then ( res => ( {
42
+ targetNode : res . records [ 0 ] . get ( 'targetNode' ) . properties ,
43
+ weightedNodeSet : res . records . map ( record => record . get ( 'weightedNode' ) . properties )
44
+ } ) )
22
45
const { type, description } = matchToRelationshipType
23
46
const nodeTypeSummary = await openaiClient . chat . completions . create ( {
24
47
model : 'gpt-4o' ,
@@ -34,43 +57,45 @@ export const upsertMatchToRelationship = async (
34
57
+ `You should ignore information that is not relevant to the '${ type . toUpperCase ( ) } Type' paragraph as it was defined.\n`
35
58
} , {
36
59
role : 'user' ,
37
- content : `The JSON data to use is: ${ JSON . stringify ( nodeShape ) } `
60
+ content : `The JSON data to use is: ${ JSON . stringify ( Object . fromEntries ( weightedNodeSet . map ( node => ( [ node . nodeType , node ] ) ) ) ) } `
38
61
}
39
62
]
40
63
} ) . then ( res => res . choices [ 0 ] . message . content ?? '' )
64
+ console . log ( "Target Node" , targetNode )
65
+ console . log ( "Weighted Node Set" , weightedNodeSet )
66
+ console . log ( "Node Type Summary" , nodeTypeSummary )
41
67
const nodeTypeEmbedding = await openaiClient . embeddings . create ( {
42
68
model : 'text-embedding-3-large' ,
43
69
input : nodeTypeSummary
44
70
} ) . then ( res => res . data [ 0 ] . embedding )
45
71
// Update Node
46
- const nodeResult = await neo4jDriver . executeQuery < EagerResult < {
47
- toNode : Node < Integer , GenericNodeShape >
72
+ const vectorNode = await neo4jDriver . executeQuery < EagerResult < {
73
+ vectorNode : Node < Integer , GenericNodeShape >
48
74
} > > ( /*cypher*/ `
49
- match (node :${ nodeShape . nodeType } {nodeId: $nodeId})
50
- merge (vectorNode:${ nodeShape . nodeType } Vector:${ matchToRelationshipType . type } {nodeId: $nodeId})-[:VECTOR_TO]->(node )
75
+ match (targetNode :${ targetNode . nodeType } {nodeId: $targetNode. nodeId})
76
+ merge (vectorNode:${ targetNode . nodeType } Vector:${ matchToRelationshipType . type } {nodeId: $targetNode. nodeId})-[:VECTOR_TO]->(targetNode )
51
77
on create
52
78
set vectorNode += $vectorNodeStructure
53
79
on match
54
80
set vectorNode += $vectorNodeStructure
55
- with node
56
- match(toNode: ${ matchToNodeType . type } )<-[:CHILD_TO|UNIQUE_TO*]-(node)
57
- return toNode
81
+ return vectorNode
58
82
` , {
59
- nodeId : nodeShape . nodeId ,
83
+ targetNode ,
60
84
vectorNodeStructure : {
61
85
nodeTypeSummary,
62
86
nodeTypeEmbedding
63
87
}
64
- } ) . then ( res => res . records [ 0 ] . get ( 'toNode' ) . properties )
65
- if ( ! nodeResult ) return UixErr ( {
88
+ } ) . then ( res => res . records [ 0 ] . get ( 'vectorNode' ) . properties )
89
+ console . log ( "Upserted Match Type Embedding" , vectorNode )
90
+ if ( ! vectorNode ) return UixErr ( {
66
91
subtype : UixErrSubtype . UPDATE_NODE_FAILED ,
67
92
message : `Upsert match relationship error` ,
68
93
data : {
69
- nodeShape ,
94
+ targetNode ,
70
95
matchToRelationshipType
71
96
}
72
97
} ) ;
73
- return Ok ( nodeResult )
98
+ return Ok ( targetNode )
74
99
} ) ) ( )
75
100
const { data, error } = result
76
101
if ( data ) return data
0 commit comments