flowquery 1.0.5 → 1.0.7

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.
Files changed (138) hide show
  1. package/README.md +74 -0
  2. package/dist/compute/runner.d.ts +1 -22
  3. package/dist/compute/runner.d.ts.map +1 -1
  4. package/dist/compute/runner.js.map +1 -1
  5. package/dist/extensibility.d.ts +35 -0
  6. package/dist/extensibility.d.ts.map +1 -0
  7. package/dist/extensibility.js +49 -0
  8. package/dist/extensibility.js.map +1 -0
  9. package/dist/flowquery.min.js +1 -1
  10. package/dist/index.browser.d.ts.map +1 -1
  11. package/dist/index.browser.js +0 -80
  12. package/dist/index.browser.js.map +1 -1
  13. package/dist/index.node.d.ts +3 -3
  14. package/dist/index.node.d.ts.map +1 -1
  15. package/dist/index.node.js +0 -80
  16. package/dist/index.node.js.map +1 -1
  17. package/dist/parsing/functions/avg.d.ts.map +1 -1
  18. package/dist/parsing/functions/avg.js +20 -2
  19. package/dist/parsing/functions/avg.js.map +1 -1
  20. package/dist/parsing/functions/collect.d.ts.map +1 -1
  21. package/dist/parsing/functions/collect.js +20 -2
  22. package/dist/parsing/functions/collect.js.map +1 -1
  23. package/dist/parsing/functions/function_factory.d.ts +26 -80
  24. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  25. package/dist/parsing/functions/function_factory.js +46 -168
  26. package/dist/parsing/functions/function_factory.js.map +1 -1
  27. package/dist/parsing/functions/function_metadata.d.ts +81 -20
  28. package/dist/parsing/functions/function_metadata.d.ts.map +1 -1
  29. package/dist/parsing/functions/function_metadata.js +154 -152
  30. package/dist/parsing/functions/function_metadata.js.map +1 -1
  31. package/dist/parsing/functions/functions.d.ts.map +1 -1
  32. package/dist/parsing/functions/functions.js +37 -2
  33. package/dist/parsing/functions/functions.js.map +1 -1
  34. package/dist/parsing/functions/join.d.ts.map +1 -1
  35. package/dist/parsing/functions/join.js +21 -2
  36. package/dist/parsing/functions/join.js.map +1 -1
  37. package/dist/parsing/functions/predicate_function.d.ts +1 -0
  38. package/dist/parsing/functions/predicate_function.d.ts.map +1 -1
  39. package/dist/parsing/functions/predicate_function.js +3 -0
  40. package/dist/parsing/functions/predicate_function.js.map +1 -1
  41. package/dist/parsing/functions/predicate_sum.d.ts.map +1 -1
  42. package/dist/parsing/functions/predicate_sum.js +23 -2
  43. package/dist/parsing/functions/predicate_sum.js.map +1 -1
  44. package/dist/parsing/functions/rand.d.ts.map +1 -1
  45. package/dist/parsing/functions/rand.js +18 -2
  46. package/dist/parsing/functions/rand.js.map +1 -1
  47. package/dist/parsing/functions/range.d.ts.map +1 -1
  48. package/dist/parsing/functions/range.js +21 -2
  49. package/dist/parsing/functions/range.js.map +1 -1
  50. package/dist/parsing/functions/replace.d.ts.map +1 -1
  51. package/dist/parsing/functions/replace.js +22 -2
  52. package/dist/parsing/functions/replace.js.map +1 -1
  53. package/dist/parsing/functions/round.d.ts.map +1 -1
  54. package/dist/parsing/functions/round.js +20 -2
  55. package/dist/parsing/functions/round.js.map +1 -1
  56. package/dist/parsing/functions/size.d.ts.map +1 -1
  57. package/dist/parsing/functions/size.js +20 -2
  58. package/dist/parsing/functions/size.js.map +1 -1
  59. package/dist/parsing/functions/split.d.ts.map +1 -1
  60. package/dist/parsing/functions/split.js +21 -2
  61. package/dist/parsing/functions/split.js.map +1 -1
  62. package/dist/parsing/functions/stringify.d.ts.map +1 -1
  63. package/dist/parsing/functions/stringify.js +20 -2
  64. package/dist/parsing/functions/stringify.js.map +1 -1
  65. package/dist/parsing/functions/sum.d.ts.map +1 -1
  66. package/dist/parsing/functions/sum.js +20 -2
  67. package/dist/parsing/functions/sum.js.map +1 -1
  68. package/dist/parsing/functions/to_json.d.ts.map +1 -1
  69. package/dist/parsing/functions/to_json.js +20 -2
  70. package/dist/parsing/functions/to_json.js.map +1 -1
  71. package/dist/parsing/parser.d.ts.map +1 -1
  72. package/dist/parsing/parser.js +1 -2
  73. package/dist/parsing/parser.js.map +1 -1
  74. package/docs/flowquery.min.js +1 -1
  75. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  76. package/misc/apps/RAG/.env.example +14 -0
  77. package/misc/apps/RAG/README.md +0 -7
  78. package/misc/apps/RAG/package.json +16 -7
  79. package/misc/apps/RAG/public/index.html +18 -0
  80. package/misc/apps/RAG/src/App.css +42 -0
  81. package/misc/apps/RAG/src/App.tsx +50 -0
  82. package/misc/apps/RAG/src/components/ApiKeySettings.tsx +245 -0
  83. package/misc/apps/RAG/src/components/ChatContainer.css +67 -0
  84. package/misc/apps/RAG/src/components/ChatContainer.tsx +239 -0
  85. package/misc/apps/RAG/src/components/ChatInput.css +23 -0
  86. package/misc/apps/RAG/src/components/ChatInput.tsx +62 -0
  87. package/misc/apps/RAG/src/components/ChatMessage.css +136 -0
  88. package/misc/apps/RAG/src/components/ChatMessage.tsx +152 -0
  89. package/misc/apps/RAG/src/components/FlowQueryAgent.ts +390 -0
  90. package/misc/apps/RAG/src/components/FlowQueryRunner.css +104 -0
  91. package/misc/apps/RAG/src/components/FlowQueryRunner.tsx +332 -0
  92. package/misc/apps/RAG/src/components/index.ts +15 -0
  93. package/misc/apps/RAG/src/index.tsx +17 -0
  94. package/misc/apps/RAG/src/plugins/README.md +139 -0
  95. package/misc/apps/RAG/src/plugins/index.ts +68 -0
  96. package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +75 -0
  97. package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +67 -0
  98. package/misc/apps/RAG/src/plugins/loaders/Llm.ts +437 -0
  99. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +151 -0
  100. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +385 -0
  101. package/misc/apps/RAG/src/prompts/index.ts +10 -0
  102. package/misc/apps/RAG/src/utils/FlowQueryExecutor.ts +131 -0
  103. package/misc/apps/RAG/src/utils/FlowQueryExtractor.ts +203 -0
  104. package/misc/apps/RAG/src/utils/index.ts +9 -0
  105. package/misc/apps/RAG/tsconfig.json +4 -2
  106. package/misc/apps/RAG/webpack.config.js +23 -12
  107. package/package.json +7 -1
  108. package/src/compute/runner.ts +1 -26
  109. package/src/extensibility.ts +45 -0
  110. package/src/index.browser.ts +2 -88
  111. package/src/index.node.ts +3 -92
  112. package/src/parsing/functions/avg.ts +10 -0
  113. package/src/parsing/functions/collect.ts +10 -0
  114. package/src/parsing/functions/function_factory.ts +56 -194
  115. package/src/parsing/functions/function_metadata.ts +187 -168
  116. package/src/parsing/functions/functions.ts +27 -0
  117. package/src/parsing/functions/join.ts +11 -0
  118. package/src/parsing/functions/predicate_function.ts +4 -0
  119. package/src/parsing/functions/predicate_sum.ts +13 -0
  120. package/src/parsing/functions/rand.ts +8 -0
  121. package/src/parsing/functions/range.ts +11 -0
  122. package/src/parsing/functions/replace.ts +12 -0
  123. package/src/parsing/functions/round.ts +10 -0
  124. package/src/parsing/functions/size.ts +10 -0
  125. package/src/parsing/functions/split.ts +11 -0
  126. package/src/parsing/functions/stringify.ts +10 -0
  127. package/src/parsing/functions/sum.ts +10 -0
  128. package/src/parsing/functions/to_json.ts +10 -0
  129. package/src/parsing/parser.ts +1 -2
  130. package/tests/extensibility.test.ts +563 -0
  131. package/tsconfig.json +1 -0
  132. package/dist/parsing/functions/predicate_function_factory.d.ts +0 -6
  133. package/dist/parsing/functions/predicate_function_factory.d.ts.map +0 -1
  134. package/dist/parsing/functions/predicate_function_factory.js +0 -19
  135. package/dist/parsing/functions/predicate_function_factory.js.map +0 -1
  136. package/misc/apps/RAG/src/index.ts +0 -20
  137. package/src/parsing/functions/predicate_function_factory.ts +0 -15
  138. package/tests/parsing/function_plugins.test.ts +0 -369
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Example plugin: Generate mock data for testing.
3
+ *
4
+ * Usage in FlowQuery:
5
+ * LOAD JSON FROM mockUsers(10) AS user
6
+ * RETURN user.name, user.email
7
+ */
8
+
9
+ import { FunctionDef } from 'flowquery/extensibility';
10
+
11
+ /**
12
+ * MockUsers loader class - generates mock user data for testing.
13
+ */
14
+ @FunctionDef({
15
+ isAsyncProvider: true,
16
+ description: 'Generates mock user data for testing purposes',
17
+ category: 'testing',
18
+ parameters: [
19
+ {
20
+ name: 'count',
21
+ description: 'Number of mock users to generate',
22
+ type: 'number',
23
+ required: false,
24
+ default: 5
25
+ }
26
+ ],
27
+ output: {
28
+ description: 'Mock user object',
29
+ type: 'object',
30
+ properties: {
31
+ id: { description: 'User ID', type: 'number' },
32
+ name: { description: 'Full name', type: 'string' },
33
+ email: { description: 'Email address', type: 'string' },
34
+ age: { description: 'Age in years', type: 'number' },
35
+ active: { description: 'Whether user is active', type: 'boolean' }
36
+ }
37
+ },
38
+ examples: [
39
+ "LOAD JSON FROM mockUsers(10) AS user RETURN user.name, user.email",
40
+ "LOAD JSON FROM mockUsers(20) AS user RETURN user WHERE user.active = true"
41
+ ]
42
+ })
43
+ export class MockUsersLoader {
44
+ private readonly firstNames: string[];
45
+ private readonly lastNames: string[];
46
+ private readonly domains: string[];
47
+
48
+ constructor(
49
+ firstNames: string[] = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack'],
50
+ lastNames: string[] = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez'],
51
+ domains: string[] = ['example.com', 'test.org', 'demo.net']
52
+ ) {
53
+ this.firstNames = firstNames;
54
+ this.lastNames = lastNames;
55
+ this.domains = domains;
56
+ }
57
+
58
+ /**
59
+ * Generates mock user data.
60
+ *
61
+ * @param count - Number of mock users to generate
62
+ */
63
+ async *fetch(count: number = 5): AsyncGenerator<any, void, unknown> {
64
+ for (let i = 0; i < count; i++) {
65
+ const firstName = this.firstNames[Math.floor(Math.random() * this.firstNames.length)];
66
+ const lastName = this.lastNames[Math.floor(Math.random() * this.lastNames.length)];
67
+ const domain = this.domains[Math.floor(Math.random() * this.domains.length)];
68
+
69
+ yield {
70
+ id: i + 1,
71
+ name: `${firstName} ${lastName}`,
72
+ email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@${domain}`,
73
+ age: Math.floor(Math.random() * 50) + 18,
74
+ active: Math.random() > 0.3
75
+ };
76
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * MockProducts loader class - generates mock product data for testing.
82
+ */
83
+ @FunctionDef({
84
+ isAsyncProvider: true,
85
+ description: 'Generates mock product data for testing purposes',
86
+ category: 'testing',
87
+ parameters: [
88
+ {
89
+ name: 'count',
90
+ description: 'Number of mock products to generate',
91
+ type: 'number',
92
+ required: false,
93
+ default: 5
94
+ }
95
+ ],
96
+ output: {
97
+ description: 'Mock product object',
98
+ type: 'object',
99
+ properties: {
100
+ id: { description: 'Product ID', type: 'number' },
101
+ name: { description: 'Product name', type: 'string' },
102
+ category: { description: 'Product category', type: 'string' },
103
+ price: { description: 'Price in dollars', type: 'number' },
104
+ inStock: { description: 'Whether product is in stock', type: 'boolean' },
105
+ rating: { description: 'Customer rating (0-5)', type: 'number' }
106
+ }
107
+ },
108
+ examples: [
109
+ "LOAD JSON FROM mockProducts(10) AS p RETURN p.name, p.price",
110
+ "LOAD JSON FROM mockProducts(50) AS p RETURN p WHERE p.category = 'Electronics'"
111
+ ]
112
+ })
113
+ export class MockProductsLoader {
114
+ private readonly categories: string[];
115
+ private readonly adjectives: string[];
116
+ private readonly nouns: string[];
117
+
118
+ constructor(
119
+ categories: string[] = ['Electronics', 'Clothing', 'Books', 'Home', 'Sports'],
120
+ adjectives: string[] = ['Premium', 'Basic', 'Pro', 'Ultra', 'Classic'],
121
+ nouns: string[] = ['Widget', 'Gadget', 'Item', 'Product', 'Thing']
122
+ ) {
123
+ this.categories = categories;
124
+ this.adjectives = adjectives;
125
+ this.nouns = nouns;
126
+ }
127
+
128
+ /**
129
+ * Generates mock product data.
130
+ *
131
+ * @param count - Number of mock products to generate
132
+ */
133
+ async *fetch(count: number = 5): AsyncGenerator<any, void, unknown> {
134
+ for (let i = 0; i < count; i++) {
135
+ const adj = this.adjectives[Math.floor(Math.random() * this.adjectives.length)];
136
+ const noun = this.nouns[Math.floor(Math.random() * this.nouns.length)];
137
+ const category = this.categories[Math.floor(Math.random() * this.categories.length)];
138
+
139
+ yield {
140
+ id: i + 1,
141
+ name: `${adj} ${noun} ${i + 1}`,
142
+ category,
143
+ price: Math.round(Math.random() * 1000 * 100) / 100,
144
+ inStock: Math.random() > 0.2,
145
+ rating: Math.round(Math.random() * 50) / 10
146
+ };
147
+ }
148
+ }
149
+ }
150
+
151
+ export { MockUsersLoader as default };
@@ -0,0 +1,385 @@
1
+ /**
2
+ * FlowQuery System Prompt Generator
3
+ *
4
+ * Generates a system prompt that instructs the LLM to create FlowQuery statements
5
+ * based on natural language queries, with awareness of available loader plugins.
6
+ *
7
+ * Uses FlowQuery's built-in functions() introspection to dynamically discover
8
+ * available async data loaders and their metadata.
9
+ */
10
+
11
+ import { FunctionMetadata, ParameterSchema, OutputSchema } from 'flowquery/extensibility';
12
+ import { getAllPluginMetadata, getAvailableLoaders } from '../plugins';
13
+
14
+ /**
15
+ * FlowQuery language reference documentation.
16
+ */
17
+ const FLOWQUERY_LANGUAGE_REFERENCE = `
18
+ ## FlowQuery Language Reference
19
+
20
+ FlowQuery is a declarative query language for data processing pipelines. It uses SQL-like syntax with additional constructs for working with APIs and data transformations.
21
+
22
+ ### Core Clauses
23
+
24
+ 1. **WITH** - Define variables and expressions
25
+ \`\`\`
26
+ WITH 'value' AS myVariable
27
+ WITH 1 + 2 AS result
28
+ WITH expression1 AS var1, expression2 AS var2
29
+ \`\`\`
30
+
31
+ 2. **LOAD JSON FROM** - Load data from a URL or async data provider
32
+ \`\`\`
33
+ LOAD JSON FROM 'https://api.example.com/data' AS item
34
+ LOAD JSON FROM myFunction(arg1, arg2) AS item
35
+ \`\`\`
36
+
37
+ 3. **LOAD JSON FROM ... HEADERS ... POST** - Make HTTP requests with headers and body
38
+ \`\`\`
39
+ LOAD JSON FROM 'https://api.example.com/data'
40
+ HEADERS {
41
+ \`Content-Type\`: 'application/json',
42
+ Authorization: f'Bearer {apiKey}'
43
+ }
44
+ POST {
45
+ field1: 'value1',
46
+ field2: variable
47
+ } AS response
48
+ \`\`\`
49
+
50
+ 4. **UNWIND** - Expand arrays into individual rows
51
+ \`\`\`
52
+ UNWIND [1, 2, 3] AS number
53
+ UNWIND myArray AS item
54
+ UNWIND range(0, 10) AS index
55
+ \`\`\`
56
+
57
+ 5. **WHERE** - Filter results
58
+ \`\`\`
59
+ WHERE item.active = true
60
+ WHERE user.age > 18 AND user.name CONTAINS 'John'
61
+ \`\`\`
62
+
63
+ 6. **RETURN** - Specify output columns
64
+ \`\`\`
65
+ RETURN item.name, item.value
66
+ RETURN item.name AS Name, item.price AS Price
67
+ RETURN * -- Return all fields
68
+ \`\`\`
69
+
70
+ 7. **ORDER BY** - Sort results
71
+ \`\`\`
72
+ ORDER BY item.name ASC
73
+ ORDER BY item.price DESC, item.name ASC
74
+ \`\`\`
75
+
76
+ 8. **LIMIT** - Limit number of results
77
+ \`\`\`
78
+ LIMIT 10
79
+ \`\`\`
80
+
81
+ 9. **SKIP** - Skip a number of results
82
+ \`\`\`
83
+ SKIP 5
84
+ \`\`\`
85
+
86
+ ### Built-in Functions
87
+
88
+ - **String Functions**: \`size()\`, \`substring()\`, \`trim()\`, \`toLower()\`, \`toUpper()\`, \`split()\`, \`join()\`, \`replace()\`, \`startsWith()\`, \`endsWith()\`, \`contains()\`
89
+ - **Math Functions**: \`abs()\`, \`ceil()\`, \`floor()\`, \`round()\`, \`sqrt()\`, \`pow()\`, \`min()\`, \`max()\`
90
+ - **Aggregate Functions**: \`sum()\`, \`avg()\`, \`count()\`, \`collect()\`, \`min()\`, \`max()\`
91
+ - **List Functions**: \`range()\`, \`head()\`, \`tail()\`, \`last()\`, \`size()\`, \`reverse()\`
92
+ - **Type Functions**: \`type()\`, \`toInteger()\`, \`toFloat()\`, \`toString()\`, \`toBoolean()\`
93
+ - **Utility Functions**: \`coalesce()\`, \`keys()\`, \`properties()\`, \`stringify()\`
94
+
95
+ ### F-Strings (Template Literals)
96
+
97
+ Use \`f"..."\` for string interpolation:
98
+ \`\`\`
99
+ WITH f"Hello, {name}!" AS greeting
100
+ WITH f"The result is {value * 2}" AS message
101
+ \`\`\`
102
+
103
+ ### Object and Array Literals
104
+
105
+ \`\`\`
106
+ WITH { name: 'John', age: 30 } AS person
107
+ WITH [1, 2, 3] AS numbers
108
+ WITH { items: [{ id: 1 }, { id: 2 }] } AS data
109
+ \`\`\`
110
+
111
+ ### Property Access
112
+
113
+ \`\`\`
114
+ item.propertyName
115
+ item['property-with-dashes']
116
+ item.nested.property
117
+ array[0]
118
+ \`\`\`
119
+
120
+ ### Comparison Operators
121
+
122
+ - \`=\`, \`<>\` (not equal), \`<\`, \`>\`, \`<=\`, \`>=\`
123
+ - \`AND\`, \`OR\`, \`NOT\`
124
+ - \`IN\`, \`CONTAINS\`, \`STARTS WITH\`, \`ENDS WITH\`
125
+ - \`IS NULL\`, \`IS NOT NULL\`
126
+
127
+ ### Comments
128
+
129
+ \`\`\`
130
+ // Single line comment
131
+ /* Multi-line
132
+ comment */
133
+ \`\`\`
134
+ `;
135
+
136
+ /**
137
+ * FlowQuery System Prompt Generator class.
138
+ * Provides methods to generate various system prompts for LLM interactions.
139
+ */
140
+ export class FlowQuerySystemPrompt {
141
+ /**
142
+ * Format a parameter schema into a readable string.
143
+ */
144
+ private static formatParameter(param: ParameterSchema): string {
145
+ const required = param.required ? ' (required)' : ' (optional)';
146
+ const defaultVal = param.default !== undefined ? `, default: ${JSON.stringify(param.default)}` : '';
147
+ const enumVals = param.enum ? `, values: [${param.enum.map(v => JSON.stringify(v)).join(', ')}]` : '';
148
+ return ` - \`${param.name}\`: ${param.type}${required}${defaultVal}${enumVals} - ${param.description}`;
149
+ }
150
+
151
+ /**
152
+ * Format output schema into a readable string.
153
+ */
154
+ private static formatOutput(output: OutputSchema): string {
155
+ let result = ` Returns: ${output.type} - ${output.description}`;
156
+
157
+ if (output.properties) {
158
+ result += '\n Output properties:';
159
+ for (const [key, prop] of Object.entries(output.properties)) {
160
+ result += `\n - \`${key}\`: ${prop.type} - ${prop.description}`;
161
+ }
162
+ }
163
+
164
+ if (output.example) {
165
+ result += `\n Example output: ${JSON.stringify(output.example, null, 2)}`;
166
+ }
167
+
168
+ return result;
169
+ }
170
+
171
+ /**
172
+ * Format a plugin metadata into a readable documentation block.
173
+ */
174
+ private static formatPluginDocumentation(plugin: FunctionMetadata): string {
175
+ const lines: string[] = [];
176
+
177
+ lines.push(`### \`${plugin.name}\``);
178
+ lines.push(`**Description**: ${plugin.description}`);
179
+
180
+ if (plugin.category) {
181
+ lines.push(`**Category**: ${plugin.category}`);
182
+ }
183
+
184
+ if (plugin.parameters.length > 0) {
185
+ lines.push('\n**Parameters**:');
186
+ for (const param of plugin.parameters) {
187
+ lines.push(this.formatParameter(param));
188
+ }
189
+ } else {
190
+ lines.push('\n**Parameters**: None');
191
+ }
192
+
193
+ lines.push('\n**Output**:');
194
+ lines.push(this.formatOutput(plugin.output));
195
+
196
+ if (plugin.examples && plugin.examples.length > 0) {
197
+ lines.push('\n**Usage Examples**:');
198
+ for (const example of plugin.examples) {
199
+ lines.push(`\`\`\`\n${example}\n\`\`\``);
200
+ }
201
+ }
202
+
203
+ if (plugin.notes) {
204
+ lines.push(`\n**Notes**: ${plugin.notes}`);
205
+ }
206
+
207
+ return lines.join('\n');
208
+ }
209
+
210
+ /**
211
+ * Generate documentation for all available plugins.
212
+ */
213
+ private static generatePluginDocumentation(plugins: FunctionMetadata[]): string {
214
+ if (plugins.length === 0) {
215
+ return 'No data loader plugins are currently available.';
216
+ }
217
+
218
+ const sections: string[] = [];
219
+
220
+ // Group plugins by category
221
+ const byCategory = new Map<string, FunctionMetadata[]>();
222
+ for (const plugin of plugins) {
223
+ const category = plugin.category || 'general';
224
+ if (!byCategory.has(category)) {
225
+ byCategory.set(category, []);
226
+ }
227
+ byCategory.get(category)!.push(plugin);
228
+ }
229
+
230
+ sections.push('## Available Data Loader Plugins\n');
231
+ sections.push('The following async data loader functions are available for use with `LOAD JSON FROM`:\n');
232
+
233
+ for (const [category, categoryPlugins] of byCategory) {
234
+ sections.push(`\n### Category: ${category.charAt(0).toUpperCase() + category.slice(1)}\n`);
235
+ for (const plugin of categoryPlugins) {
236
+ sections.push(this.formatPluginDocumentation(plugin));
237
+ sections.push('---');
238
+ }
239
+ }
240
+
241
+ return sections.join('\n');
242
+ }
243
+
244
+ /**
245
+ * Internal helper to build the system prompt from plugin documentation.
246
+ */
247
+ private static buildSystemPrompt(pluginDocs: string, additionalContext?: string): string {
248
+ 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.
249
+
250
+ ## How You Work
251
+
252
+ You operate in a multi-step process:
253
+ 1. **Analyze** the user's natural language request
254
+ 2. **Generate** a FlowQuery statement that fulfills the request using available plugins
255
+ 3. The system will **execute** your FlowQuery and provide you with the results
256
+ 4. You will then **interpret** the results and present them to the user in a helpful way
257
+
258
+ ## Response Format for Query Generation
259
+
260
+ When the user makes a request that requires fetching or processing data:
261
+ 1. Generate a FlowQuery statement wrapped in a code block with \`\`\`flowquery language tag
262
+ 2. Keep any explanation brief - the main focus should be the query
263
+ 3. The query will be automatically executed and you'll receive the results
264
+
265
+ When the user asks a question that doesn't require data fetching (e.g., asking about FlowQuery syntax or general questions):
266
+ 1. Start your response with [NO_QUERY_NEEDED]
267
+ 2. Then provide your direct answer
268
+
269
+ ## Important Guidelines
270
+
271
+ - Only use the available data loader plugins documented below
272
+ - Use proper FlowQuery syntax as documented in the language reference
273
+ - For API calls, prefer using the registered loader plugins over raw HTTP calls when possible
274
+ - Always alias loaded items with \`AS\` for clarity
275
+ - Use meaningful aliases in RETURN statements for better readability
276
+ - Generate the simplest query that fulfills the user's request
277
+ - If you cannot determine what the user needs, ask clarifying questions (with [NO_QUERY_NEEDED])
278
+
279
+ ${FLOWQUERY_LANGUAGE_REFERENCE}
280
+
281
+ ${pluginDocs}
282
+
283
+ ${additionalContext ? `## Additional Context\n\n${additionalContext}` : ''}
284
+
285
+ ## Example Response Format
286
+
287
+ **When a query is needed**:
288
+ \`\`\`flowquery
289
+ LOAD JSON FROM pluginName(args) AS item
290
+ WHERE item.field = 'value'
291
+ RETURN item.name AS Name, item.value AS Value
292
+ \`\`\`
293
+
294
+ **When no query is needed** (e.g., general questions about FlowQuery):
295
+ [NO_QUERY_NEEDED]
296
+ Your direct answer here...
297
+
298
+ Now help the user with their request.`;
299
+ }
300
+
301
+ /**
302
+ * Generate the complete FlowQuery system prompt.
303
+ * Uses FlowQuery's introspection via functions() as the single source of truth.
304
+ *
305
+ * @param additionalContext - Optional additional context to include in the prompt
306
+ * @returns The complete system prompt string
307
+ */
308
+ public static generate(additionalContext?: string): string {
309
+ // Uses FlowQuery's introspection to get available async providers
310
+ const plugins = getAllPluginMetadata();
311
+ const pluginDocs = this.generatePluginDocumentation(plugins);
312
+
313
+ return this.buildSystemPrompt(pluginDocs, additionalContext);
314
+ }
315
+
316
+ /**
317
+ * Generate a system prompt for the interpretation phase.
318
+ * Used after FlowQuery execution to interpret results.
319
+ *
320
+ * @returns The interpretation system prompt string
321
+ */
322
+ public static generateInterpretationPrompt(): string {
323
+ return `You are a helpful assistant interpreting FlowQuery execution results.
324
+
325
+ ## Your Role
326
+
327
+ The user made a natural language request, which was converted to a FlowQuery statement and executed.
328
+ You are now receiving the execution results. Your job is to:
329
+
330
+ 1. **Summarize** the results in a clear, user-friendly way
331
+ 2. **Highlight** key insights or patterns in the data
332
+ 3. **Format** the data appropriately (tables, lists, or prose depending on the data)
333
+ 4. **Answer** the user's original question using the data
334
+
335
+ ## Guidelines
336
+
337
+ - Be concise but thorough
338
+ - If the results contain many items, summarize rather than listing all
339
+ - If there's an error, explain what went wrong in user-friendly terms
340
+ - Use markdown formatting for better readability
341
+ - If the data doesn't fully answer the user's question, note what's missing`;
342
+ }
343
+
344
+ /**
345
+ * Get a minimal system prompt without full documentation.
346
+ * Useful for contexts where token count is a concern.
347
+ */
348
+ public static getMinimalPrompt(): string {
349
+ const plugins = getAllPluginMetadata();
350
+ const pluginList = plugins.map(p => `- \`${p.name}\`: ${p.description}`).join('\n');
351
+
352
+ return `You are a FlowQuery assistant. Generate FlowQuery statements based on user requests.
353
+
354
+ Available data loader plugins:
355
+ ${pluginList}
356
+
357
+ FlowQuery uses SQL-like syntax: WITH, LOAD JSON FROM, UNWIND, WHERE, RETURN, ORDER BY, LIMIT, SKIP.
358
+ Use f"..." for string interpolation. Access properties with dot notation or brackets.
359
+
360
+ Always wrap FlowQuery code in \`\`\`flowquery code blocks.`;
361
+ }
362
+
363
+ /**
364
+ * Generate the FlowQuery system prompt asynchronously using functions() introspection.
365
+ * This is the preferred method that uses FlowQuery's built-in introspection.
366
+ *
367
+ * @param additionalContext - Optional additional context to include in the prompt
368
+ * @returns Promise resolving to the complete system prompt string
369
+ */
370
+ public static async generateAsync(additionalContext?: string): Promise<string> {
371
+ // Use FlowQuery's functions() introspection to discover available loaders
372
+ const plugins = await getAvailableLoaders();
373
+ const pluginDocs = this.generatePluginDocumentation(plugins);
374
+
375
+ return this.buildSystemPrompt(pluginDocs, additionalContext);
376
+ }
377
+ }
378
+
379
+ // Export functions for backward compatibility
380
+ export const generateFlowQuerySystemPrompt = FlowQuerySystemPrompt.generate.bind(FlowQuerySystemPrompt);
381
+ export const generateInterpretationPrompt = FlowQuerySystemPrompt.generateInterpretationPrompt.bind(FlowQuerySystemPrompt);
382
+ export const getMinimalFlowQueryPrompt = FlowQuerySystemPrompt.getMinimalPrompt.bind(FlowQuerySystemPrompt);
383
+ export const generateFlowQuerySystemPromptAsync = FlowQuerySystemPrompt.generateAsync.bind(FlowQuerySystemPrompt);
384
+
385
+ export default FlowQuerySystemPrompt;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Prompts module - exports system prompt generators.
3
+ */
4
+
5
+ export {
6
+ FlowQuerySystemPrompt,
7
+ generateFlowQuerySystemPrompt,
8
+ generateInterpretationPrompt,
9
+ getMinimalFlowQueryPrompt
10
+ } from './FlowQuerySystemPrompt';
@@ -0,0 +1,131 @@
1
+ /**
2
+ * FlowQuery Executor Utility
3
+ *
4
+ * Executes FlowQuery statements and handles errors gracefully.
5
+ */
6
+
7
+ import { FlowQuery } from 'flowquery';
8
+
9
+ /**
10
+ * Result of executing a FlowQuery statement.
11
+ */
12
+ export interface FlowQueryExecutionResult {
13
+ /** Whether the execution was successful */
14
+ success: boolean;
15
+ /** The query that was executed */
16
+ query: string;
17
+ /** The results from the query, if successful */
18
+ results?: any[];
19
+ /** Error message, if execution failed */
20
+ error?: string;
21
+ /** Execution time in milliseconds */
22
+ executionTime: number;
23
+ }
24
+
25
+ /**
26
+ * FlowQuery Executor class for executing FlowQuery statements.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const executor = new FlowQueryExecutor();
31
+ * const result = await executor.execute("LOAD JSON FROM somePlugin(5) AS item RETURN item.text");
32
+ * if (result.success) {
33
+ * console.log(result.results);
34
+ * } else {
35
+ * console.error(result.error);
36
+ * }
37
+ * ```
38
+ */
39
+ export class FlowQueryExecutor {
40
+ private defaultMaxItems: number;
41
+
42
+ /**
43
+ * Creates a new FlowQueryExecutor instance.
44
+ * @param defaultMaxItems - Default maximum items to display when formatting results (default: 20)
45
+ */
46
+ constructor(defaultMaxItems: number = 20) {
47
+ this.defaultMaxItems = defaultMaxItems;
48
+ }
49
+
50
+ /**
51
+ * Execute a FlowQuery statement and return the results.
52
+ *
53
+ * @param query - The FlowQuery statement to execute
54
+ * @returns The execution result including success status, results or error
55
+ */
56
+ async execute(query: string): Promise<FlowQueryExecutionResult> {
57
+ const startTime = performance.now();
58
+
59
+ try {
60
+ // Validate the query is not empty
61
+ if (!query || query.trim() === '') {
62
+ return {
63
+ success: false,
64
+ query,
65
+ error: 'Query cannot be empty',
66
+ executionTime: performance.now() - startTime
67
+ };
68
+ }
69
+
70
+ // Create a runner and execute the query
71
+ const runner = new FlowQuery(query);
72
+ await runner.run();
73
+
74
+ // Get the results
75
+ const results = runner.results;
76
+
77
+ return {
78
+ success: true,
79
+ query,
80
+ results: Array.isArray(results) ? results : [results],
81
+ executionTime: performance.now() - startTime
82
+ };
83
+ } catch (error) {
84
+ const errorMessage = error instanceof Error ? error.message : String(error);
85
+
86
+ return {
87
+ success: false,
88
+ query,
89
+ error: errorMessage,
90
+ executionTime: performance.now() - startTime
91
+ };
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Format execution results for display or LLM consumption.
97
+ *
98
+ * @param result - The execution result to format
99
+ * @param maxItems - Maximum number of items to include (uses default if not specified)
100
+ * @returns A formatted string representation of the results
101
+ */
102
+ formatResult(result: FlowQueryExecutionResult, maxItems?: number): string {
103
+ const limit = maxItems ?? this.defaultMaxItems;
104
+
105
+ if (!result.success) {
106
+ return `Execution Error: ${result.error}`;
107
+ }
108
+
109
+ const results = result.results || [];
110
+ const totalCount = results.length;
111
+ const displayResults = results.slice(0, limit);
112
+
113
+ let output = `Execution successful (${result.executionTime.toFixed(2)}ms)\n`;
114
+ output += `Total results: ${totalCount}\n\n`;
115
+
116
+ if (totalCount === 0) {
117
+ output += 'No results returned.';
118
+ } else {
119
+ output += 'Results:\n';
120
+ output += JSON.stringify(displayResults, null, 2);
121
+
122
+ if (totalCount > limit) {
123
+ output += `\n\n... and ${totalCount - limit} more results`;
124
+ }
125
+ }
126
+
127
+ return output;
128
+ }
129
+ }
130
+
131
+ export default FlowQueryExecutor;