flowquery 1.0.8 → 1.0.10

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 (38) hide show
  1. package/dist/flowquery.min.js +1 -1
  2. package/dist/parsing/functions/function_factory.d.ts +2 -0
  3. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  4. package/dist/parsing/functions/function_factory.js +2 -0
  5. package/dist/parsing/functions/function_factory.js.map +1 -1
  6. package/dist/parsing/functions/keys.d.ts +7 -0
  7. package/dist/parsing/functions/keys.d.ts.map +1 -0
  8. package/dist/parsing/functions/keys.js +42 -0
  9. package/dist/parsing/functions/keys.js.map +1 -0
  10. package/dist/parsing/functions/type.d.ts +7 -0
  11. package/dist/parsing/functions/type.d.ts.map +1 -0
  12. package/dist/parsing/functions/type.js +49 -0
  13. package/dist/parsing/functions/type.js.map +1 -0
  14. package/docs/flowquery.min.js +1 -1
  15. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  16. package/misc/apps/RAG/package.json +3 -1
  17. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.css +172 -0
  18. package/misc/apps/RAG/src/components/AdaptiveCardRenderer.tsx +312 -0
  19. package/misc/apps/RAG/src/components/ChatContainer.tsx +159 -112
  20. package/misc/apps/RAG/src/components/ChatInput.tsx +58 -44
  21. package/misc/apps/RAG/src/components/ChatMessage.tsx +186 -101
  22. package/misc/apps/RAG/src/components/FlowQueryAgent.ts +50 -6
  23. package/misc/apps/RAG/src/components/FlowQueryRunner.css +9 -0
  24. package/misc/apps/RAG/src/components/FlowQueryRunner.tsx +44 -5
  25. package/misc/apps/RAG/src/components/index.ts +4 -0
  26. package/misc/apps/RAG/src/plugins/index.ts +6 -4
  27. package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +1 -2
  28. package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +1 -2
  29. package/misc/apps/RAG/src/plugins/loaders/Form.ts +578 -0
  30. package/misc/apps/RAG/src/plugins/loaders/Llm.ts +1 -2
  31. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +2 -4
  32. package/misc/apps/RAG/src/plugins/loaders/Table.ts +271 -0
  33. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +12 -0
  34. package/package.json +1 -1
  35. package/src/parsing/functions/function_factory.ts +2 -0
  36. package/src/parsing/functions/keys.ts +31 -0
  37. package/src/parsing/functions/type.ts +39 -0
  38. package/tests/compute/runner.test.ts +8 -0
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Table loader plugin - transforms tabular data into Adaptive Card format.
3
+ *
4
+ * Adaptive Cards are platform-agnostic UI snippets that can be rendered in
5
+ * Microsoft Teams, Outlook, Windows, and other applications.
6
+ *
7
+ * Usage in FlowQuery:
8
+ * // First collect data from an async provider, then pass to table:
9
+ * LOAD JSON FROM mockUsers(5) AS u
10
+ * WITH collect(u) AS users
11
+ * LOAD JSON FROM table(users, 'Users') AS card
12
+ * RETURN card
13
+ *
14
+ * Note: Async providers cannot be nested as function arguments.
15
+ */
16
+
17
+ import { FunctionDef } from 'flowquery/extensibility';
18
+
19
+ /**
20
+ * Interface for Adaptive Card structure
21
+ */
22
+ interface AdaptiveCard {
23
+ type: 'AdaptiveCard';
24
+ $schema: string;
25
+ version: string;
26
+ body: AdaptiveCardElement[];
27
+ }
28
+
29
+ interface AdaptiveCardElement {
30
+ type: string;
31
+ [key: string]: any;
32
+ }
33
+
34
+ interface TableCell {
35
+ type: 'TableCell';
36
+ items: AdaptiveCardElement[];
37
+ }
38
+
39
+ interface TableRow {
40
+ type: 'TableRow';
41
+ cells: TableCell[];
42
+ }
43
+
44
+ /**
45
+ * Table loader - transforms tabular data into an Adaptive Card table format.
46
+ */
47
+ @FunctionDef({
48
+ description: 'Transforms tabular data into an Adaptive Card JSON format with a table layout',
49
+ category: 'async',
50
+ parameters: [
51
+ {
52
+ name: 'data',
53
+ description: 'Array of objects or async generator to display as a table',
54
+ type: 'array',
55
+ required: true
56
+ },
57
+ {
58
+ name: 'title',
59
+ description: 'Optional title for the card',
60
+ type: 'string',
61
+ required: false,
62
+ default: 'Data Table'
63
+ },
64
+ {
65
+ name: 'columns',
66
+ description: 'Optional array of column names to include (defaults to all columns from first row)',
67
+ type: 'array',
68
+ required: false
69
+ },
70
+ {
71
+ name: 'maxRows',
72
+ description: 'Maximum number of rows to display',
73
+ type: 'number',
74
+ required: false,
75
+ default: 100
76
+ }
77
+ ],
78
+ output: {
79
+ description: 'Adaptive Card JSON object',
80
+ type: 'object',
81
+ properties: {
82
+ type: { description: 'Always "AdaptiveCard"', type: 'string' },
83
+ $schema: { description: 'Adaptive Card schema URL', type: 'string' },
84
+ version: { description: 'Adaptive Card version', type: 'string' },
85
+ body: { description: 'Card body elements including table', type: 'array' }
86
+ }
87
+ },
88
+ examples: [
89
+ "LOAD JSON FROM mockUsers(5) AS u WITH collect(u) AS users LOAD JSON FROM table(users, 'User List') AS card RETURN card",
90
+ "LOAD JSON FROM mockProducts(10) AS p WITH collect(p) AS products LOAD JSON FROM table(products, 'Products', ['name', 'price', 'category']) AS card RETURN card"
91
+ ]
92
+ })
93
+ export class TableLoader {
94
+ /**
95
+ * Transforms data into an Adaptive Card with table layout.
96
+ *
97
+ * @param data - Array or async iterable of objects
98
+ * @param title - Card title
99
+ * @param columns - Optional column names to include
100
+ * @param maxRows - Maximum rows to include
101
+ */
102
+ async *fetch(
103
+ data: any[] | AsyncIterable<any>,
104
+ title: string = 'Data Table',
105
+ columns?: string[],
106
+ maxRows: number = 100
107
+ ): AsyncGenerator<AdaptiveCard, void, unknown> {
108
+ // Collect data from array or async iterable
109
+ const rows: any[] = [];
110
+
111
+ if (Symbol.asyncIterator in Object(data)) {
112
+ for await (const item of data as AsyncIterable<any>) {
113
+ rows.push(item);
114
+ if (rows.length >= maxRows) break;
115
+ }
116
+ } else if (Array.isArray(data)) {
117
+ rows.push(...data.slice(0, maxRows));
118
+ } else {
119
+ // Single object
120
+ rows.push(data);
121
+ }
122
+
123
+ if (rows.length === 0) {
124
+ yield this.createEmptyCard(title);
125
+ return;
126
+ }
127
+
128
+ // Determine columns from first row if not specified
129
+ const columnNames = columns || Object.keys(rows[0]);
130
+
131
+ yield this.createTableCard(title, columnNames, rows);
132
+ }
133
+
134
+ /**
135
+ * Creates an Adaptive Card with a table displaying the data.
136
+ * Uses ColumnSet/Column for better compatibility across renderers.
137
+ */
138
+ private createTableCard(title: string, columnNames: string[], rows: any[]): AdaptiveCard {
139
+ const card: AdaptiveCard = {
140
+ type: 'AdaptiveCard',
141
+ $schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
142
+ version: '1.3',
143
+ body: []
144
+ };
145
+
146
+ // Add title
147
+ card.body.push({
148
+ type: 'TextBlock',
149
+ text: title,
150
+ weight: 'Bolder',
151
+ size: 'Large',
152
+ wrap: true
153
+ });
154
+
155
+ // Add separator
156
+ card.body.push({
157
+ type: 'TextBlock',
158
+ text: ' ',
159
+ separator: true
160
+ });
161
+
162
+ // Create header row using ColumnSet
163
+ const headerColumnSet: AdaptiveCardElement = {
164
+ type: 'ColumnSet',
165
+ columns: columnNames.map(col => ({
166
+ type: 'Column',
167
+ width: 'stretch',
168
+ items: [{
169
+ type: 'TextBlock',
170
+ text: this.formatColumnName(col),
171
+ weight: 'Bolder',
172
+ wrap: true
173
+ }]
174
+ })),
175
+ style: 'accent'
176
+ };
177
+ card.body.push(headerColumnSet);
178
+
179
+ // Add data rows using ColumnSets
180
+ for (const row of rows) {
181
+ const dataColumnSet: AdaptiveCardElement = {
182
+ type: 'ColumnSet',
183
+ columns: columnNames.map(col => ({
184
+ type: 'Column',
185
+ width: 'stretch',
186
+ items: [{
187
+ type: 'TextBlock',
188
+ text: this.formatCellValue(row[col]),
189
+ wrap: true
190
+ }]
191
+ })),
192
+ separator: true
193
+ };
194
+ card.body.push(dataColumnSet);
195
+ }
196
+
197
+ // Add row count footer
198
+ card.body.push({
199
+ type: 'TextBlock',
200
+ text: `Showing ${rows.length} row${rows.length !== 1 ? 's' : ''}`,
201
+ size: 'Small',
202
+ isSubtle: true,
203
+ horizontalAlignment: 'Right',
204
+ separator: true
205
+ });
206
+
207
+ return card;
208
+ }
209
+
210
+ /**
211
+ * Creates an empty card when no data is available.
212
+ */
213
+ private createEmptyCard(title: string): AdaptiveCard {
214
+ return {
215
+ type: 'AdaptiveCard',
216
+ $schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
217
+ version: '1.3',
218
+ body: [
219
+ {
220
+ type: 'TextBlock',
221
+ text: title,
222
+ weight: 'Bolder',
223
+ size: 'Large',
224
+ wrap: true
225
+ },
226
+ {
227
+ type: 'TextBlock',
228
+ text: 'No data available',
229
+ isSubtle: true,
230
+ wrap: true
231
+ }
232
+ ]
233
+ };
234
+ }
235
+
236
+ /**
237
+ * Formats a column name for display (converts camelCase/snake_case to Title Case).
238
+ */
239
+ private formatColumnName(name: string): string {
240
+ return name
241
+ .replace(/([A-Z])/g, ' $1') // camelCase
242
+ .replace(/_/g, ' ') // snake_case
243
+ .replace(/^\w/, c => c.toUpperCase())
244
+ .trim();
245
+ }
246
+
247
+ /**
248
+ * Formats a cell value for display in the table.
249
+ */
250
+ private formatCellValue(value: any): string {
251
+ if (value === null || value === undefined) {
252
+ return '-';
253
+ }
254
+ if (typeof value === 'boolean') {
255
+ return value ? '✓' : '✗';
256
+ }
257
+ if (typeof value === 'number') {
258
+ // Format numbers nicely
259
+ if (Number.isInteger(value)) {
260
+ return value.toString();
261
+ }
262
+ return value.toFixed(2);
263
+ }
264
+ if (typeof value === 'object') {
265
+ return JSON.stringify(value);
266
+ }
267
+ return String(value);
268
+ }
269
+ }
270
+
271
+ export { TableLoader as default };
@@ -33,6 +33,18 @@ FlowQuery is a declarative query language for data processing pipelines. It uses
33
33
  LOAD JSON FROM 'https://api.example.com/data' AS item
34
34
  LOAD JSON FROM myFunction(arg1, arg2) AS item
35
35
  \`\`\`
36
+
37
+ **IMPORTANT**: Async data providers (functions used after LOAD JSON FROM) 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:
38
+ \`\`\`
39
+ // WRONG - async providers cannot be nested:
40
+ // LOAD JSON FROM table(mockProducts(5), 'Products') AS card
41
+
42
+ // CORRECT - collect data first, then pass to next provider:
43
+ LOAD JSON FROM mockProducts(5) AS p
44
+ WITH collect(p) AS products
45
+ LOAD JSON FROM table(products, 'Products') AS card
46
+ RETURN card
47
+ \`\`\`
36
48
 
37
49
  3. **LOAD JSON FROM ... HEADERS ... POST** - Make HTTP requests with headers and body
38
50
  \`\`\`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowquery",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "A declarative query language for data processing pipelines.",
5
5
  "main": "dist/index.node.js",
6
6
  "types": "dist/index.node.d.ts",
@@ -9,12 +9,14 @@ import "./rand";
9
9
  import "./round";
10
10
  import "./split";
11
11
  import "./join";
12
+ import "./keys";
12
13
  import "./to_json";
13
14
  import "./replace";
14
15
  import "./stringify";
15
16
  import "./size";
16
17
  import "./functions";
17
18
  import "./predicate_sum";
19
+ import "./type";
18
20
  import {
19
21
  FunctionMetadata,
20
22
  getRegisteredFunctionMetadata,
@@ -0,0 +1,31 @@
1
+ import Function from "./function";
2
+ import { FunctionDef } from "./function_metadata";
3
+
4
+ @FunctionDef({
5
+ description: "Returns the keys of an object (associative array) as an array",
6
+ category: "scalar",
7
+ parameters: [
8
+ { name: "object", description: "Object to extract keys from", type: "object" }
9
+ ],
10
+ output: { description: "Array of keys", type: "array", example: "['name', 'age']" },
11
+ examples: ["WITH { name: 'Alice', age: 30 } AS obj RETURN keys(obj)"]
12
+ })
13
+ class Keys extends Function {
14
+ constructor() {
15
+ super("keys");
16
+ this._expectedParameterCount = 1;
17
+ }
18
+
19
+ public value(): any {
20
+ const obj = this.getChildren()[0].value();
21
+ if (obj === null || obj === undefined) {
22
+ return [];
23
+ }
24
+ if (typeof obj !== "object" || Array.isArray(obj)) {
25
+ throw new Error("keys() expects an object, not an array or primitive");
26
+ }
27
+ return Object.keys(obj);
28
+ }
29
+ }
30
+
31
+ export default Keys;
@@ -0,0 +1,39 @@
1
+ import Function from "./function";
2
+ import { FunctionDef } from "./function_metadata";
3
+
4
+ @FunctionDef({
5
+ description: "Returns the type of a value as a string",
6
+ category: "scalar",
7
+ parameters: [
8
+ { name: "value", description: "Value to check the type of", type: "any" }
9
+ ],
10
+ output: { description: "Type of the input value", type: "string", example: "string" },
11
+ examples: [
12
+ "WITH 'hello' AS val RETURN type(val)",
13
+ "WITH 42 AS val RETURN type(val)",
14
+ "WITH [1, 2, 3] AS val RETURN type(val)"
15
+ ]
16
+ })
17
+ class Type extends Function {
18
+ constructor() {
19
+ super("type");
20
+ this._expectedParameterCount = 1;
21
+ }
22
+
23
+ public value(): any {
24
+ const val = this.getChildren()[0].value();
25
+
26
+ if (val === null) {
27
+ return "null";
28
+ }
29
+ if (val === undefined) {
30
+ return "undefined";
31
+ }
32
+ if (Array.isArray(val)) {
33
+ return "array";
34
+ }
35
+ return typeof val;
36
+ }
37
+ }
38
+
39
+ export default Type;
@@ -495,4 +495,12 @@ test('Test range with size', async () => {
495
495
  const results = runner.results;
496
496
  expect(results.length).toBe(1);
497
497
  expect(results[0]).toEqual({'indices': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]});
498
+ });
499
+
500
+ test('Test keys function', async () => {
501
+ const runner = new Runner('RETURN keys({name: "Alice", age: 30}) as keys');
502
+ await runner.run();
503
+ const results = runner.results;
504
+ expect(results.length).toBe(1);
505
+ expect(results[0]).toEqual({'keys': ['name', 'age']});
498
506
  });