flowquery 1.0.25 → 1.0.26
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/.github/workflows/release.yml +1 -0
- package/.husky/pre-commit +3 -2
- package/dist/flowquery.min.js +1 -1
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +1 -0
- package/dist/parsing/parser.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/pyproject.toml +1 -1
- package/flowquery-py/src/parsing/parser.py +1 -0
- package/flowquery-py/tests/compute/test_runner.py +26 -0
- package/flowquery-py/tests/parsing/test_parser.py +18 -0
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/jest.config.js +6 -9
- package/misc/apps/RAG/data/chats.json +302 -0
- package/misc/apps/RAG/data/emails.json +182 -0
- package/misc/apps/RAG/data/events.json +226 -0
- package/misc/apps/RAG/data/files.json +172 -0
- package/misc/apps/RAG/data/users.json +158 -0
- package/misc/apps/RAG/jest.config.js +21 -0
- package/misc/apps/RAG/package.json +9 -2
- package/misc/apps/RAG/src/App.tsx +5 -5
- package/misc/apps/RAG/src/components/ChatContainer.tsx +53 -124
- package/misc/apps/RAG/src/components/FlowQueryAgent.ts +151 -157
- package/misc/apps/RAG/src/components/index.ts +1 -1
- package/misc/apps/RAG/src/graph/index.ts +19 -0
- package/misc/apps/RAG/src/graph/initializeGraph.ts +254 -0
- package/misc/apps/RAG/src/index.tsx +25 -13
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +146 -231
- package/misc/apps/RAG/src/prompts/index.ts +4 -4
- package/misc/apps/RAG/src/tests/graph.test.ts +35 -0
- package/misc/apps/RAG/src/utils/FlowQueryExecutor.ts +20 -21
- package/misc/apps/RAG/src/utils/FlowQueryExtractor.ts +35 -30
- package/misc/apps/RAG/src/utils/Llm.ts +248 -0
- package/misc/apps/RAG/src/utils/index.ts +7 -4
- package/misc/apps/RAG/tsconfig.json +4 -3
- package/misc/apps/RAG/webpack.config.js +40 -40
- package/package.json +1 -1
- package/src/parsing/parser.ts +1 -0
- package/tests/compute/runner.test.ts +1 -1
- package/tests/parsing/parser.test.ts +16 -0
- package/misc/apps/RAG/src/plugins/README.md +0 -139
- package/misc/apps/RAG/src/plugins/index.ts +0 -72
- package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +0 -70
- package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +0 -65
- package/misc/apps/RAG/src/plugins/loaders/Form.ts +0 -594
- package/misc/apps/RAG/src/plugins/loaders/Llm.ts +0 -450
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +0 -101
- package/misc/apps/RAG/src/plugins/loaders/Table.ts +0 -274
- package/misc/apps/RAG/src/plugins/loaders/Weather.ts +0 -138
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* FlowQuery System Prompt Generator
|
|
3
3
|
*
|
|
4
4
|
* Generates a system prompt that instructs the LLM to create FlowQuery statements
|
|
5
|
-
* based on natural language queries, with awareness of
|
|
5
|
+
* based on natural language queries, with awareness of the graph schema.
|
|
6
6
|
*
|
|
7
|
-
* Uses FlowQuery's built-in
|
|
8
|
-
* available
|
|
7
|
+
* Uses FlowQuery's built-in schema() introspection to dynamically discover
|
|
8
|
+
* available nodes and relationships in the graph.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
import { getAllPluginMetadata, getAvailableLoaders } from "../plugins";
|
|
10
|
+
import { getGraphSchema } from "../graph";
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
13
|
* FlowQuery language reference documentation.
|
|
@@ -17,116 +15,92 @@ import { getAllPluginMetadata, getAvailableLoaders } from "../plugins";
|
|
|
17
15
|
const FLOWQUERY_LANGUAGE_REFERENCE = `
|
|
18
16
|
## FlowQuery Language Reference
|
|
19
17
|
|
|
20
|
-
FlowQuery is a declarative query language for
|
|
18
|
+
FlowQuery is a Cypher-inspired declarative query language for querying graph data. It uses pattern matching to traverse nodes and relationships.
|
|
21
19
|
|
|
22
|
-
###
|
|
20
|
+
### Graph Query Clauses
|
|
23
21
|
|
|
24
|
-
1. **
|
|
22
|
+
1. **MATCH** - Match patterns in the graph
|
|
25
23
|
\`\`\`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
MATCH (n:Person)
|
|
25
|
+
MATCH (a:User)-[:KNOWS]-(b:User)
|
|
26
|
+
MATCH (user:User)-[:SENT]->(email:Email)
|
|
27
|
+
MATCH p=(a:Person)-[:KNOWS*1..3]-(b:Person)
|
|
29
28
|
\`\`\`
|
|
30
29
|
|
|
31
|
-
2. **
|
|
30
|
+
2. **WHERE** - Filter matched patterns
|
|
32
31
|
\`\`\`
|
|
33
|
-
|
|
32
|
+
MATCH (n:Person)
|
|
33
|
+
WHERE n.age > 30
|
|
34
|
+
|
|
35
|
+
MATCH (a:User)-[:SENT]-(email:Email)
|
|
36
|
+
WHERE email.subject CONTAINS 'urgent'
|
|
34
37
|
\`\`\`
|
|
35
38
|
|
|
36
|
-
3. **
|
|
37
|
-
\`\`\`
|
|
38
|
-
CALL myFunction(arg1, arg2) YIELD field1, field2, field3
|
|
39
|
-
\`\`\`
|
|
40
|
-
|
|
41
|
-
**IMPORTANT**: Async data providers (functions used with CALL) cannot be nested inside other function calls. If you need to pass data from one async provider to another, first load the data into a variable using collect(), then pass that variable:
|
|
39
|
+
3. **RETURN** - Specify output columns
|
|
42
40
|
\`\`\`
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
CALL mockProducts(5) YIELD id, name, price
|
|
48
|
-
WITH collect({ id: id, name: name, price: price }) AS products
|
|
49
|
-
CALL table(products, 'Products') YIELD html
|
|
50
|
-
RETURN html
|
|
41
|
+
RETURN n.name, n.email
|
|
42
|
+
RETURN n.name AS Name, n.email AS Email
|
|
43
|
+
RETURN n -- Return full node
|
|
44
|
+
RETURN * -- Return all matched variables
|
|
51
45
|
\`\`\`
|
|
52
46
|
|
|
53
|
-
4. **
|
|
47
|
+
4. **WITH** - Define intermediate variables or chain queries
|
|
54
48
|
\`\`\`
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
POST {
|
|
61
|
-
field1: 'value1',
|
|
62
|
-
field2: variable
|
|
63
|
-
} AS response
|
|
49
|
+
MATCH (n:User)
|
|
50
|
+
WITH n, size((n)-[:SENT]->()) AS emailCount
|
|
51
|
+
WHERE emailCount > 5
|
|
52
|
+
RETURN n.name, emailCount
|
|
64
53
|
\`\`\`
|
|
65
54
|
|
|
66
55
|
5. **UNWIND** - Expand arrays into individual rows
|
|
67
56
|
\`\`\`
|
|
68
57
|
UNWIND [1, 2, 3] AS number
|
|
69
|
-
UNWIND
|
|
70
|
-
UNWIND range(0, 10) AS index
|
|
71
|
-
\`\`\`
|
|
72
|
-
|
|
73
|
-
**IMPORTANT**: An UNWIND statement cannot be followed directly by a WHERE statement. If you need to filter after unwinding, use a WITH clause in between:
|
|
74
|
-
\`\`\`
|
|
75
|
-
// WRONG - UNWIND cannot be directly followed by WHERE:
|
|
76
|
-
// UNWIND items AS item
|
|
77
|
-
// WHERE item.active = true
|
|
78
|
-
|
|
79
|
-
// CORRECT - use WITH between UNWIND and WHERE:
|
|
80
|
-
UNWIND items AS item
|
|
81
|
-
WITH item
|
|
82
|
-
WHERE item.active = true
|
|
58
|
+
UNWIND n.tags AS tag
|
|
83
59
|
\`\`\`
|
|
84
60
|
|
|
85
|
-
|
|
86
|
-
\`\`\`
|
|
87
|
-
WHERE item.active = true
|
|
88
|
-
WHERE user.age > 18 AND user.name CONTAINS 'John'
|
|
89
|
-
\`\`\`
|
|
90
|
-
|
|
91
|
-
7. **RETURN** - Specify output columns
|
|
92
|
-
\`\`\`
|
|
93
|
-
RETURN item.name, item.value
|
|
94
|
-
RETURN item.name AS Name, item.price AS Price
|
|
95
|
-
RETURN * -- Return all fields
|
|
96
|
-
\`\`\`
|
|
61
|
+
### Pattern Syntax
|
|
97
62
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
- **
|
|
101
|
-
- **
|
|
102
|
-
- **Aggregate Functions**: \`sum()\`, \`avg()\`, \`count()\`, \`collect()\`, \`min()\`, \`max()\`
|
|
103
|
-
- **List Functions**: \`range()\`, \`head()\`, \`tail()\`, \`last()\`, \`size()\`, \`reverse()\`
|
|
104
|
-
- **Type Functions**: \`type()\`, \`toInteger()\`, \`toFloat()\`, \`toString()\`, \`toBoolean()\`
|
|
105
|
-
- **Utility Functions**: \`coalesce()\`, \`keys()\`, \`properties()\`, \`stringify()\`
|
|
63
|
+
- **Nodes**: \`(variable:Label)\` or \`(variable)\` or \`(:Label)\`
|
|
64
|
+
- **Relationships**: \`-[:TYPE]-\` (undirected), \`-[:TYPE]->\` (outgoing), \`<-[:TYPE]-\` (incoming)
|
|
65
|
+
- **Variable-length paths**: \`-[:TYPE*1..3]-\` (1 to 3 hops), \`-[:TYPE*]-\` (any number)
|
|
66
|
+
- **Named paths**: \`p=(a)-[:KNOWS]-(b)\`
|
|
106
67
|
|
|
107
|
-
###
|
|
68
|
+
### Examples
|
|
108
69
|
|
|
109
|
-
Use \`f"..."\` for string interpolation:
|
|
110
70
|
\`\`\`
|
|
111
|
-
|
|
112
|
-
|
|
71
|
+
// Find all users
|
|
72
|
+
MATCH (u:User)
|
|
73
|
+
RETURN u.name, u.email
|
|
74
|
+
|
|
75
|
+
// Find users and their managers
|
|
76
|
+
MATCH (user:User)-[:MANAGES]-(manager:User)
|
|
77
|
+
RETURN user.name AS Employee, manager.name AS Manager
|
|
78
|
+
|
|
79
|
+
// Find emails sent by a specific user
|
|
80
|
+
MATCH (u:User)-[:SENT]->(e:Email)
|
|
81
|
+
WHERE u.name = 'Alice'
|
|
82
|
+
RETURN e.subject, e.sentDate
|
|
83
|
+
|
|
84
|
+
// Find chain of relationships
|
|
85
|
+
MATCH (a:User)-[:KNOWS*1..2]-(b:User)
|
|
86
|
+
WHERE a.name = 'Bob'
|
|
87
|
+
RETURN b.name AS Connection
|
|
113
88
|
\`\`\`
|
|
114
89
|
|
|
115
|
-
###
|
|
90
|
+
### Built-in Functions
|
|
116
91
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
92
|
+
- **Aggregation**: \`count()\`, \`sum()\`, \`avg()\`, \`min()\`, \`max()\`, \`collect()\`
|
|
93
|
+
- **String**: \`size()\`, \`trim()\`, \`toLower()\`, \`toUpper()\`, \`split()\`, \`join()\`, \`replace()\`
|
|
94
|
+
- **List**: \`range()\`, \`head()\`, \`tail()\`, \`last()\`, \`size()\`, \`reverse()\`
|
|
95
|
+
- **Type**: \`type()\`, \`toInteger()\`, \`toFloat()\`, \`toString()\`
|
|
96
|
+
- **Utility**: \`keys()\`, \`properties()\`
|
|
122
97
|
|
|
123
|
-
###
|
|
98
|
+
### F-Strings (Template Literals)
|
|
124
99
|
|
|
100
|
+
Use \`f"..."\` for string interpolation:
|
|
125
101
|
\`\`\`
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
item.nested.property
|
|
129
|
-
array[0]
|
|
102
|
+
MATCH (u:User)
|
|
103
|
+
RETURN f"Name: {u.name}, Email: {u.email}" AS info
|
|
130
104
|
\`\`\`
|
|
131
105
|
|
|
132
106
|
### Comparison Operators
|
|
@@ -135,14 +109,6 @@ array[0]
|
|
|
135
109
|
- \`AND\`, \`OR\`, \`NOT\`
|
|
136
110
|
- \`IN\`, \`CONTAINS\`, \`STARTS WITH\`, \`ENDS WITH\`
|
|
137
111
|
- \`IS NULL\`, \`IS NOT NULL\`
|
|
138
|
-
|
|
139
|
-
### Comments
|
|
140
|
-
|
|
141
|
-
\`\`\`
|
|
142
|
-
// Single line comment
|
|
143
|
-
/* Multi-line
|
|
144
|
-
comment */
|
|
145
|
-
\`\`\`
|
|
146
112
|
`;
|
|
147
113
|
|
|
148
114
|
/**
|
|
@@ -151,126 +117,80 @@ array[0]
|
|
|
151
117
|
*/
|
|
152
118
|
export class FlowQuerySystemPrompt {
|
|
153
119
|
/**
|
|
154
|
-
* Format
|
|
155
|
-
|
|
156
|
-
private static formatParameter(param: ParameterSchema): string {
|
|
157
|
-
const required = param.required ? " (required)" : " (optional)";
|
|
158
|
-
const defaultVal =
|
|
159
|
-
param.default !== undefined ? `, default: ${JSON.stringify(param.default)}` : "";
|
|
160
|
-
const enumVals = param.enum
|
|
161
|
-
? `, values: [${param.enum.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
162
|
-
: "";
|
|
163
|
-
return ` - \`${param.name}\`: ${param.type}${required}${defaultVal}${enumVals} - ${param.description}`;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Format output schema into a readable string.
|
|
120
|
+
* Format the graph schema into readable documentation.
|
|
121
|
+
* Accepts raw schema() results: array of { kind, label, type, sample }
|
|
168
122
|
*/
|
|
169
|
-
private static
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (output.properties) {
|
|
173
|
-
result += "\n Output properties:";
|
|
174
|
-
for (const [key, prop] of Object.entries(output.properties)) {
|
|
175
|
-
result += `\n - \`${key}\`: ${prop.type} - ${prop.description}`;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (output.example) {
|
|
180
|
-
result += `\n Example output: ${JSON.stringify(output.example, null, 2)}`;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Format a plugin metadata into a readable documentation block.
|
|
188
|
-
*/
|
|
189
|
-
private static formatPluginDocumentation(plugin: FunctionMetadata): string {
|
|
190
|
-
const lines: string[] = [];
|
|
191
|
-
|
|
192
|
-
lines.push(`### \`${plugin.name}\``);
|
|
193
|
-
lines.push(`**Description**: ${plugin.description}`);
|
|
123
|
+
private static formatSchemaDocumentation(schema: any[]): string {
|
|
124
|
+
const sections: string[] = [];
|
|
194
125
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
126
|
+
sections.push("## Graph Schema\n");
|
|
127
|
+
sections.push(
|
|
128
|
+
"The following nodes and relationships are available in the graph for use with `MATCH` queries:\n"
|
|
129
|
+
);
|
|
198
130
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
131
|
+
// Filter nodes and relationships from raw schema results
|
|
132
|
+
const nodes = schema.filter((r) => r.kind === "node");
|
|
133
|
+
const relationships = schema.filter((r) => r.kind === "relationship");
|
|
134
|
+
|
|
135
|
+
// Document nodes
|
|
136
|
+
sections.push("### Node Labels\n");
|
|
137
|
+
if (nodes.length > 0) {
|
|
138
|
+
for (const node of nodes) {
|
|
139
|
+
sections.push(`#### \`${node.label}\``);
|
|
140
|
+
// Extract properties from sample data
|
|
141
|
+
if (node.sample && typeof node.sample === "object") {
|
|
142
|
+
const props = Object.entries(node.sample);
|
|
143
|
+
if (props.length > 0) {
|
|
144
|
+
sections.push("**Properties**:");
|
|
145
|
+
for (const [name, value] of props) {
|
|
146
|
+
const propType = Array.isArray(value) ? "array" : typeof value;
|
|
147
|
+
sections.push(` - \`${name}\`: ${propType}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
sections.push(`**Sample**: \`${JSON.stringify(node.sample)}\``);
|
|
151
|
+
}
|
|
152
|
+
sections.push("");
|
|
203
153
|
}
|
|
204
154
|
} else {
|
|
205
|
-
|
|
155
|
+
sections.push("No nodes defined.\n");
|
|
206
156
|
}
|
|
207
157
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Generate documentation for all available plugins.
|
|
227
|
-
*/
|
|
228
|
-
private static generatePluginDocumentation(plugins: FunctionMetadata[]): string {
|
|
229
|
-
if (plugins.length === 0) {
|
|
230
|
-
return "No data loader plugins are currently available.";
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const sections: string[] = [];
|
|
234
|
-
|
|
235
|
-
// Group plugins by category
|
|
236
|
-
const byCategory = new Map<string, FunctionMetadata[]>();
|
|
237
|
-
for (const plugin of plugins) {
|
|
238
|
-
const category = plugin.category || "general";
|
|
239
|
-
if (!byCategory.has(category)) {
|
|
240
|
-
byCategory.set(category, []);
|
|
241
|
-
}
|
|
242
|
-
byCategory.get(category)!.push(plugin);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
sections.push("## Available Data Loader Plugins\n");
|
|
246
|
-
sections.push(
|
|
247
|
-
"The following async data loader functions are available for use with `CALL ... YIELD`:\n"
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
for (const [category, categoryPlugins] of byCategory) {
|
|
251
|
-
sections.push(
|
|
252
|
-
`\n### Category: ${category.charAt(0).toUpperCase() + category.slice(1)}\n`
|
|
253
|
-
);
|
|
254
|
-
for (const plugin of categoryPlugins) {
|
|
255
|
-
sections.push(this.formatPluginDocumentation(plugin));
|
|
256
|
-
sections.push("---");
|
|
158
|
+
// Document relationships
|
|
159
|
+
sections.push("### Relationship Types\n");
|
|
160
|
+
if (relationships.length > 0) {
|
|
161
|
+
for (const rel of relationships) {
|
|
162
|
+
sections.push(`#### \`[:${rel.type}]\``);
|
|
163
|
+
// Extract properties from sample data
|
|
164
|
+
if (rel.sample && typeof rel.sample === "object") {
|
|
165
|
+
const props = Object.entries(rel.sample);
|
|
166
|
+
if (props.length > 0) {
|
|
167
|
+
sections.push("**Properties**:");
|
|
168
|
+
for (const [name, value] of props) {
|
|
169
|
+
const propType = Array.isArray(value) ? "array" : typeof value;
|
|
170
|
+
sections.push(` - \`${name}\`: ${propType}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
sections.push("");
|
|
257
175
|
}
|
|
176
|
+
} else {
|
|
177
|
+
sections.push("No relationships defined.\n");
|
|
258
178
|
}
|
|
259
179
|
|
|
260
180
|
return sections.join("\n");
|
|
261
181
|
}
|
|
262
182
|
|
|
263
183
|
/**
|
|
264
|
-
* Internal helper to build the system prompt from
|
|
184
|
+
* Internal helper to build the system prompt from schema documentation.
|
|
265
185
|
*/
|
|
266
|
-
private static buildSystemPrompt(
|
|
186
|
+
private static buildSystemPrompt(schemaDocs: string, additionalContext?: string): string {
|
|
267
187
|
return `You are a FlowQuery assistant. Your primary role is to help users by creating and executing FlowQuery statements based on their natural language requests.
|
|
268
188
|
|
|
269
189
|
## How You Work
|
|
270
190
|
|
|
271
191
|
You operate in a multi-step process:
|
|
272
192
|
1. **Analyze** the user's natural language request
|
|
273
|
-
2. **Generate** a FlowQuery statement that fulfills the request using
|
|
193
|
+
2. **Generate** a FlowQuery statement that fulfills the request using the graph schema
|
|
274
194
|
3. The system will **execute** your FlowQuery and provide you with the results
|
|
275
195
|
4. You will then **interpret** the results and present them to the user in a helpful way
|
|
276
196
|
|
|
@@ -287,9 +207,9 @@ When the user asks a question that doesn't require data fetching (e.g., asking a
|
|
|
287
207
|
|
|
288
208
|
## Important Guidelines
|
|
289
209
|
|
|
290
|
-
- Only use the
|
|
210
|
+
- Only use the nodes and relationships documented in the graph schema below
|
|
291
211
|
- Use proper FlowQuery syntax as documented in the language reference
|
|
292
|
-
-
|
|
212
|
+
- Use MATCH patterns to query the graph
|
|
293
213
|
- Always alias loaded items with \`AS\` for clarity
|
|
294
214
|
- Use meaningful aliases in RETURN statements for better readability
|
|
295
215
|
- Generate the simplest query that fulfills the user's request
|
|
@@ -301,7 +221,7 @@ When the user asks a question that doesn't require data fetching (e.g., asking a
|
|
|
301
221
|
|
|
302
222
|
${FLOWQUERY_LANGUAGE_REFERENCE}
|
|
303
223
|
|
|
304
|
-
${
|
|
224
|
+
${schemaDocs}
|
|
305
225
|
|
|
306
226
|
${additionalContext ? `## Additional Context\n\n${additionalContext}` : ""}
|
|
307
227
|
|
|
@@ -309,9 +229,9 @@ ${additionalContext ? `## Additional Context\n\n${additionalContext}` : ""}
|
|
|
309
229
|
|
|
310
230
|
**When a query is needed**:
|
|
311
231
|
\`\`\`flowquery
|
|
312
|
-
|
|
313
|
-
WHERE
|
|
314
|
-
RETURN
|
|
232
|
+
MATCH (n:NodeLabel)-[:RELATIONSHIP]-(m:OtherLabel)
|
|
233
|
+
WHERE n.property = 'value'
|
|
234
|
+
RETURN n.name AS Name, m.value AS Value
|
|
315
235
|
\`\`\`
|
|
316
236
|
|
|
317
237
|
**When no query is needed** (e.g., general questions about FlowQuery):
|
|
@@ -323,17 +243,16 @@ Now help the user with their request.`;
|
|
|
323
243
|
|
|
324
244
|
/**
|
|
325
245
|
* Generate the complete FlowQuery system prompt.
|
|
326
|
-
* Uses FlowQuery's
|
|
246
|
+
* Uses FlowQuery's schema() introspection to discover the graph structure.
|
|
327
247
|
*
|
|
328
248
|
* @param additionalContext - Optional additional context to include in the prompt
|
|
329
|
-
* @returns
|
|
249
|
+
* @returns Promise resolving to the complete system prompt string
|
|
330
250
|
*/
|
|
331
|
-
public static generate(additionalContext?: string): string {
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
const pluginDocs = this.generatePluginDocumentation(plugins);
|
|
251
|
+
public static async generate(additionalContext?: string): Promise<string> {
|
|
252
|
+
const schema = await getGraphSchema();
|
|
253
|
+
const schemaDocs = this.formatSchemaDocumentation(schema);
|
|
335
254
|
|
|
336
|
-
return this.buildSystemPrompt(
|
|
255
|
+
return this.buildSystemPrompt(schemaDocs, additionalContext);
|
|
337
256
|
}
|
|
338
257
|
|
|
339
258
|
/**
|
|
@@ -367,46 +286,42 @@ You are now receiving the execution results. Your job is to:
|
|
|
367
286
|
/**
|
|
368
287
|
* Get a minimal system prompt without full documentation.
|
|
369
288
|
* Useful for contexts where token count is a concern.
|
|
289
|
+
*
|
|
290
|
+
* @returns Promise resolving to minimal prompt string
|
|
370
291
|
*/
|
|
371
|
-
public static getMinimalPrompt(): string {
|
|
372
|
-
const
|
|
373
|
-
const
|
|
292
|
+
public static async getMinimalPrompt(): Promise<string> {
|
|
293
|
+
const schema = await getGraphSchema();
|
|
294
|
+
const nodes = schema.filter((r: any) => r.kind === "node");
|
|
295
|
+
const relationships = schema.filter((r: any) => r.kind === "relationship");
|
|
296
|
+
|
|
297
|
+
const nodeList =
|
|
298
|
+
nodes.length > 0 ? nodes.map((n: any) => `- \`${n.label}\``).join("\n") : "None";
|
|
299
|
+
const relList =
|
|
300
|
+
relationships.length > 0
|
|
301
|
+
? relationships.map((r: any) => `- \`[:${r.type}]\``).join("\n")
|
|
302
|
+
: "None";
|
|
374
303
|
|
|
375
304
|
return `You are a FlowQuery assistant. Generate FlowQuery statements based on user requests.
|
|
376
305
|
|
|
377
|
-
Available
|
|
378
|
-
${
|
|
306
|
+
Available node labels:
|
|
307
|
+
${nodeList}
|
|
379
308
|
|
|
380
|
-
|
|
309
|
+
Available relationships:
|
|
310
|
+
${relList}
|
|
311
|
+
|
|
312
|
+
FlowQuery uses Cypher-like syntax: MATCH, WITH, UNWIND, WHERE, RETURN.
|
|
381
313
|
Use f"..." for string interpolation. Access properties with dot notation or brackets.
|
|
382
314
|
|
|
383
315
|
Always wrap FlowQuery code in \`\`\`flowquery code blocks.`;
|
|
384
316
|
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Generate the FlowQuery system prompt asynchronously using functions() introspection.
|
|
388
|
-
* This is the preferred method that uses FlowQuery's built-in introspection.
|
|
389
|
-
*
|
|
390
|
-
* @param additionalContext - Optional additional context to include in the prompt
|
|
391
|
-
* @returns Promise resolving to the complete system prompt string
|
|
392
|
-
*/
|
|
393
|
-
public static async generateAsync(additionalContext?: string): Promise<string> {
|
|
394
|
-
// Use FlowQuery's functions() introspection to discover available loaders
|
|
395
|
-
const plugins = await getAvailableLoaders();
|
|
396
|
-
const pluginDocs = this.generatePluginDocumentation(plugins);
|
|
397
|
-
|
|
398
|
-
return this.buildSystemPrompt(pluginDocs, additionalContext);
|
|
399
|
-
}
|
|
400
317
|
}
|
|
401
318
|
|
|
402
|
-
//
|
|
319
|
+
// Convenience function exports
|
|
403
320
|
export const generateFlowQuerySystemPrompt =
|
|
404
321
|
FlowQuerySystemPrompt.generate.bind(FlowQuerySystemPrompt);
|
|
405
322
|
export const generateInterpretationPrompt =
|
|
406
323
|
FlowQuerySystemPrompt.generateInterpretationPrompt.bind(FlowQuerySystemPrompt);
|
|
407
324
|
export const getMinimalFlowQueryPrompt =
|
|
408
325
|
FlowQuerySystemPrompt.getMinimalPrompt.bind(FlowQuerySystemPrompt);
|
|
409
|
-
export const generateFlowQuerySystemPromptAsync =
|
|
410
|
-
FlowQuerySystemPrompt.generateAsync.bind(FlowQuerySystemPrompt);
|
|
411
326
|
|
|
412
327
|
export default FlowQuerySystemPrompt;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Prompts module - exports system prompt generators.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export {
|
|
6
6
|
FlowQuerySystemPrompt,
|
|
7
|
-
generateFlowQuerySystemPrompt,
|
|
7
|
+
generateFlowQuerySystemPrompt,
|
|
8
8
|
generateInterpretationPrompt,
|
|
9
|
-
getMinimalFlowQueryPrompt
|
|
10
|
-
} from
|
|
9
|
+
getMinimalFlowQueryPrompt,
|
|
10
|
+
} from "./FlowQuerySystemPrompt";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the FlowQuery graph initialization
|
|
3
|
+
*
|
|
4
|
+
* Note: The tests that depend on graph initialization
|
|
5
|
+
* are skipped due to Jest module isolation issues with the flowquery package.
|
|
6
|
+
* These tests work correctly when the app runs directly.
|
|
7
|
+
*/
|
|
8
|
+
import FlowQuery from "flowquery";
|
|
9
|
+
|
|
10
|
+
describe("Graph - Basic FlowQuery Operations", () => {
|
|
11
|
+
test("should execute simple WITH/RETURN query", async () => {
|
|
12
|
+
const runner = new FlowQuery("WITH 42 AS value RETURN value");
|
|
13
|
+
await runner.run();
|
|
14
|
+
expect(runner.results).toEqual([{ value: 42 }]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("should execute UNWIND query", async () => {
|
|
18
|
+
const runner = new FlowQuery("UNWIND [1, 2, 3] AS num RETURN num");
|
|
19
|
+
await runner.run();
|
|
20
|
+
expect(runner.results.length).toBe(3);
|
|
21
|
+
expect(runner.results).toEqual([{ num: 1 }, { num: 2 }, { num: 3 }]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("should execute expression with functions", async () => {
|
|
25
|
+
const runner = new FlowQuery("WITH size([1, 2, 3, 4, 5]) AS len RETURN len");
|
|
26
|
+
await runner.run();
|
|
27
|
+
expect(runner.results).toEqual([{ len: 5 }]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("should execute aggregation query", async () => {
|
|
31
|
+
const runner = new FlowQuery("UNWIND [10, 20, 30] AS n RETURN sum(n) AS total");
|
|
32
|
+
await runner.run();
|
|
33
|
+
expect(runner.results).toEqual([{ total: 60 }]);
|
|
34
|
+
});
|
|
35
|
+
});
|