flowquery 1.0.11 → 1.0.13

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 (59) hide show
  1. package/README.md +142 -38
  2. package/dist/flowquery.min.js +1 -1
  3. package/dist/parsing/functions/aggregate_function.d.ts +1 -1
  4. package/dist/parsing/functions/aggregate_function.d.ts.map +1 -1
  5. package/dist/parsing/functions/aggregate_function.js.map +1 -1
  6. package/dist/parsing/functions/async_function.d.ts +4 -15
  7. package/dist/parsing/functions/async_function.d.ts.map +1 -1
  8. package/dist/parsing/functions/async_function.js +6 -59
  9. package/dist/parsing/functions/async_function.js.map +1 -1
  10. package/dist/parsing/functions/function.d.ts +1 -1
  11. package/dist/parsing/functions/function.d.ts.map +1 -1
  12. package/dist/parsing/functions/function.js +1 -1
  13. package/dist/parsing/functions/function.js.map +1 -1
  14. package/dist/parsing/functions/function_factory.d.ts +2 -0
  15. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  16. package/dist/parsing/functions/function_factory.js +12 -11
  17. package/dist/parsing/functions/function_factory.js.map +1 -1
  18. package/dist/parsing/functions/function_metadata.d.ts +53 -24
  19. package/dist/parsing/functions/function_metadata.d.ts.map +1 -1
  20. package/dist/parsing/functions/function_metadata.js +57 -53
  21. package/dist/parsing/functions/function_metadata.js.map +1 -1
  22. package/dist/parsing/functions/predicate_function.d.ts +1 -1
  23. package/dist/parsing/functions/predicate_function.d.ts.map +1 -1
  24. package/dist/parsing/functions/predicate_function.js +1 -1
  25. package/dist/parsing/functions/predicate_function.js.map +1 -1
  26. package/dist/parsing/operations/load.js +1 -1
  27. package/dist/parsing/operations/load.js.map +1 -1
  28. package/dist/parsing/parser.d.ts.map +1 -1
  29. package/dist/parsing/parser.js +1 -2
  30. package/dist/parsing/parser.js.map +1 -1
  31. package/docs/flowquery.min.js +1 -1
  32. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  33. package/misc/apps/RAG/README.md +0 -12
  34. package/misc/apps/RAG/package.json +1 -2
  35. package/misc/apps/RAG/src/components/ChatContainer.tsx +33 -6
  36. package/misc/apps/RAG/src/components/ChatMessage.css +24 -0
  37. package/misc/apps/RAG/src/components/ChatMessage.tsx +51 -2
  38. package/misc/apps/RAG/src/components/FlowQueryAgent.ts +566 -286
  39. package/misc/apps/RAG/src/plugins/index.ts +3 -1
  40. package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +6 -5
  41. package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +3 -3
  42. package/misc/apps/RAG/src/plugins/loaders/Form.ts +5 -5
  43. package/misc/apps/RAG/src/plugins/loaders/Llm.ts +14 -14
  44. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +5 -5
  45. package/misc/apps/RAG/src/plugins/loaders/Table.ts +4 -4
  46. package/misc/apps/RAG/src/plugins/loaders/Weather.ts +126 -0
  47. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +4 -0
  48. package/package.json +1 -1
  49. package/src/parsing/functions/aggregate_function.ts +1 -1
  50. package/src/parsing/functions/async_function.ts +5 -50
  51. package/src/parsing/functions/function.ts +2 -2
  52. package/src/parsing/functions/function_factory.ts +18 -9
  53. package/src/parsing/functions/function_metadata.ts +57 -56
  54. package/src/parsing/functions/predicate_function.ts +2 -2
  55. package/src/parsing/operations/load.ts +1 -1
  56. package/src/parsing/parser.ts +2 -1
  57. package/tests/extensibility.test.ts +50 -12
  58. package/tests/parsing/parser.test.ts +2 -2
  59. package/misc/apps/RAG/.env.example +0 -14
@@ -78,28 +78,11 @@ export type AsyncDataProvider = (...args: any[]) => AsyncGenerator<any, void, un
78
78
  class FunctionRegistry {
79
79
  private static metadata: Map<string, FunctionMetadata> = new Map<string, FunctionMetadata>();
80
80
  private static factories: Map<string, () => any> = new Map<string, () => any>();
81
- private static asyncProviders: Map<string, AsyncDataProvider> = new Map<string, AsyncDataProvider>();
82
-
83
- /** Derives a camelCase display name from a class name, removing 'Loader' suffix. */
84
- private static deriveDisplayName(className: string): string {
85
- const baseName: string = className.endsWith('Loader') ? className.slice(0, -6) : className;
86
- return baseName.charAt(0).toLowerCase() + baseName.slice(1);
87
- }
88
-
89
- /** Registers an async data provider class. */
90
- static registerAsync<T extends new (...args: any[]) => any>(constructor: T, options: FunctionDefOptions): void {
91
- const displayName: string = this.deriveDisplayName(constructor.name);
92
- const registryKey: string = displayName.toLowerCase();
93
-
94
- this.metadata.set(registryKey, { name: displayName, ...options });
95
- this.asyncProviders.set(registryKey, (...args: any[]) => new constructor().fetch(...args));
96
- }
97
81
 
98
82
  /** Registers a regular function class. */
99
- static registerFunction<T extends new (...args: any[]) => any>(constructor: T, options: FunctionDefOptions): void {
83
+ static register<T extends new (...args: any[]) => any>(constructor: T, options: FunctionDefOptions): void {
100
84
  const instance: any = new constructor();
101
- const baseName: string = (instance.name?.toLowerCase() || constructor.name.toLowerCase());
102
- const displayName: string = baseName.includes(':') ? baseName.split(':')[0] : baseName;
85
+ const displayName: string = (instance.name?.toLowerCase() || constructor.name.toLowerCase());
103
86
  const registryKey: string = options.category ? `${displayName}:${options.category}` : displayName;
104
87
 
105
88
  this.metadata.set(registryKey, { name: displayName, ...options });
@@ -118,7 +101,7 @@ class FunctionRegistry {
118
101
  const lowerName: string = name.toLowerCase();
119
102
  if (category) return this.metadata.get(`${lowerName}:${category}`);
120
103
  for (const meta of this.metadata.values()) {
121
- if (meta.name === lowerName) return meta;
104
+ if (meta.name.toLowerCase() === lowerName) return meta;
122
105
  }
123
106
  return undefined;
124
107
  }
@@ -128,10 +111,6 @@ class FunctionRegistry {
128
111
  if (category) return this.factories.get(`${lowerName}:${category}`);
129
112
  return this.factories.get(lowerName);
130
113
  }
131
-
132
- static getAsyncProvider(name: string): AsyncDataProvider | undefined {
133
- return this.asyncProviders.get(name.toLowerCase());
134
- }
135
114
  }
136
115
 
137
116
  /**
@@ -144,45 +123,74 @@ export type FunctionDefOptions = Omit<FunctionMetadata, 'name'>;
144
123
  * The function name is derived from the class's constructor call to super() for regular functions,
145
124
  * or from the class name for async providers.
146
125
  *
147
- * For async providers (category: "async"), the class must have a `fetch` method that returns
148
- * an AsyncGenerator. The function name is derived from the class name (removing 'Loader' suffix
149
- * if present) and converted to camelCase.
126
+ * For async providers (category: "async"), the class must have a `generate` method that returns
127
+ * an AsyncGenerator. This allows the function to be used as a data source in LOAD operations.
150
128
  *
151
129
  * @param options - Function metadata (excluding name)
152
130
  * @returns Class decorator
153
131
  *
154
132
  * @example
155
133
  * ```typescript
156
- * // Regular function
134
+ * // Scalar function example
157
135
  * @FunctionDef({
158
- * description: "Calculates the sum of numeric values",
159
- * category: "aggregate",
160
- * parameters: [{ name: "value", description: "Numeric value to sum", type: "number" }],
161
- * output: { description: "Sum of all values", type: "number", example: 150 },
162
- * examples: ["WITH [1, 2, 3] AS nums UNWIND nums AS n RETURN sum(n)"]
136
+ * description: "Adds two numbers",
137
+ * category: "scalar",
138
+ * parameters: [
139
+ * { name: "a", description: "First number", type: "number" },
140
+ * { name: "b", description: "Second number", type: "number" }
141
+ * ],
142
+ * output: { description: "Sum of a and b", type: "number" },
143
+ * examples: ["ADD(2, 3) // returns 5"]
163
144
  * })
164
- * class Sum extends AggregateFunction { ... }
165
- *
166
- * // Async data provider
145
+ * class AddFunction extends Function {
146
+ * constructor() {
147
+ * super("add");
148
+ * }
149
+ * public execute(a: number, b: number): number {
150
+ * return a + b;
151
+ * }
152
+ * }
153
+ * // Aggregate function example
167
154
  * @FunctionDef({
168
- * description: "Fetches random cat facts from the Cat Facts API",
169
- * category: "async",
170
- * parameters: [{ name: "count", description: "Number of facts", type: "number", required: false, default: 1 }],
171
- * output: { description: "Cat fact object", type: "object" },
172
- * examples: ["LOAD JSON FROM catFacts(5) AS fact RETURN fact.text"]
155
+ * description: "Calculates the average of a list of numbers",
156
+ * category: "aggregate",
157
+ * parameters: [
158
+ * { name: "values", description: "Array of numbers", type: "array", items: { type: "number" } }
159
+ * ],
160
+ * output: { description: "Average value", type: "number" },
161
+ * examples: ["AVERAGE([1, 2, 3, 4, 5]) // returns 3"]
173
162
  * })
174
- * class CatFactsLoader {
175
- * async *fetch(count: number = 1): AsyncGenerator<any> { ... }
163
+ * class AverageFunction extends AggregateFunction {
164
+ * constructor() {
165
+ * super("average");
166
+ * }
167
+ * public execute(values: number[]): number {
168
+ * const sum = values.reduce((acc, val) => acc + val, 0);
169
+ * return sum / values.length;
170
+ * }
171
+ * }
172
+ * // Async data provider example
173
+ * @FunctionDef({
174
+ * description: "Fetches data from an external API",
175
+ * category: "async",
176
+ * parameters: [
177
+ * { name: "endpoint", description: "API endpoint URL", type: "string" }
178
+ * ],
179
+ * output: { description: "Data object", type: "object" },
180
+ * examples: ["MyAsyncDataProvider('https://api.example.com/data')"]
181
+ * })
182
+ * class MyAsyncDataProvider extends AsyncFunction {
183
+ * public async *generate(endpoint: string): AsyncGenerator<any> {
184
+ * const response = await fetch(endpoint);
185
+ * const data = await response.json();
186
+ * for (const item of data) {
187
+ * yield item;
188
+ * }
176
189
  * }
177
- * ```
178
190
  */
179
191
  export function FunctionDef(options: FunctionDefOptions) {
180
192
  return function <T extends new (...args: any[]) => any>(constructor: T): T {
181
- if (options.category === 'async') {
182
- FunctionRegistry.registerAsync(constructor, options);
183
- } else {
184
- FunctionRegistry.registerFunction(constructor, options);
185
- }
193
+ FunctionRegistry.register(constructor, options);
186
194
  return constructor;
187
195
  };
188
196
  }
@@ -207,10 +215,3 @@ export function getRegisteredFunctionFactory(name: string, category?: string): (
207
215
  export function getFunctionMetadata(name: string, category?: string): FunctionMetadata | undefined {
208
216
  return FunctionRegistry.getMetadata(name, category);
209
217
  }
210
-
211
- /**
212
- * Gets a registered async data provider by name.
213
- */
214
- export function getRegisteredAsyncProvider(name: string): AsyncDataProvider | undefined {
215
- return FunctionRegistry.getAsyncProvider(name);
216
- }
@@ -8,9 +8,9 @@ class PredicateFunction extends ASTNode {
8
8
  private _name: string;
9
9
  protected _valueHolder: ValueHolder = new ValueHolder();
10
10
 
11
- constructor(name: string) {
11
+ constructor(name?: string) {
12
12
  super();
13
- this._name = name;
13
+ this._name = name || this.constructor.name;
14
14
  }
15
15
 
16
16
  public get name(): string {
@@ -87,7 +87,7 @@ class Load extends Operation {
87
87
  */
88
88
  private async loadFromFunction(): Promise<void> {
89
89
  const asyncFunc = this.asyncFunction!;
90
- for await (const item of asyncFunc.execute()) {
90
+ for await (const item of asyncFunc.generate()) {
91
91
  this._value = item;
92
92
  await this.next?.run();
93
93
  }
@@ -597,7 +597,7 @@ class Parser extends BaseParser {
597
597
  if(!this.peek()?.isLeftParenthesis()) {
598
598
  return null;
599
599
  }
600
- const asyncFunc = new AsyncFunction(this.token.value);
600
+ const asyncFunc = FunctionFactory.createAsync(this.token.value);
601
601
  this.setNextToken(); // skip function name
602
602
  this.setNextToken(); // skip left parenthesis
603
603
  this.skipWhitespaceAndComments();
@@ -609,6 +609,7 @@ class Parser extends BaseParser {
609
609
  this.setNextToken();
610
610
  return asyncFunc;
611
611
  }
612
+
612
613
  private parsePredicateFunction(): PredicateFunction | null {
613
614
  if(!this.ahead([Token.IDENTIFIER(""), Token.LEFT_PARENTHESIS, Token.IDENTIFIER(""), Token.IN])) {
614
615
  return null;
@@ -12,8 +12,8 @@ import {
12
12
  FunctionCategory
13
13
  } from "../src/extensibility";
14
14
  import {
15
- getRegisteredAsyncProvider,
16
- getFunctionMetadata
15
+ getFunctionMetadata,
16
+ getRegisteredFunctionFactory
17
17
  } from "../src/parsing/functions/function_metadata";
18
18
 
19
19
  describe("Extensibility API Exports", () => {
@@ -216,8 +216,8 @@ describe("FunctionDef Decorator", () => {
216
216
  ],
217
217
  output: { description: "Data object", type: "object" }
218
218
  })
219
- class SimpleLoader {
220
- async *fetch(count: number = 1): AsyncGenerator<any> {
219
+ class Simple extends AsyncFunction {
220
+ public async *generate(count: number = 1): AsyncGenerator<any> {
221
221
  for (let i = 0; i < count; i++) {
222
222
  yield { id: i, data: `item${i}` };
223
223
  }
@@ -225,9 +225,9 @@ describe("FunctionDef Decorator", () => {
225
225
  }
226
226
 
227
227
  // Verify the decorated class still works correctly
228
- const loader = new SimpleLoader();
228
+ const loader = new Simple("simple");
229
229
  const results: any[] = [];
230
- for await (const item of loader.fetch(2)) {
230
+ for await (const item of loader.generate(2)) {
231
231
  results.push(item);
232
232
  }
233
233
  expect(results.length).toBe(2);
@@ -235,12 +235,12 @@ describe("FunctionDef Decorator", () => {
235
235
  expect(results[1]).toEqual({ id: 1, data: "item1" });
236
236
 
237
237
  // Verify the async provider was registered
238
- const provider = getRegisteredAsyncProvider("simple");
238
+ const provider = getRegisteredFunctionFactory("simple", "async");
239
239
  expect(provider).toBeDefined();
240
240
  expect(typeof provider).toBe("function");
241
241
 
242
242
  // Verify the metadata was registered
243
- const metadata = getFunctionMetadata("simple");
243
+ const metadata = getFunctionMetadata("simple", "async");
244
244
  expect(metadata).toBeDefined();
245
245
  expect(metadata?.name).toBe("simple");
246
246
  expect(metadata?.category).toBe("async");
@@ -540,17 +540,17 @@ describe("Plugin Functions Integration with FlowQuery", () => {
540
540
  description: "Provides example data for testing",
541
541
  category: "async",
542
542
  parameters: [],
543
- output: { description: "Example data object", type: "object" }
543
+ output: { description: "Example data o.bject", type: "object" }
544
544
  })
545
- class GetExampleDataLoader {
546
- async *fetch(): AsyncGenerator<any> {
545
+ class GetExampleData extends AsyncFunction {
546
+ public async *generate(): AsyncGenerator<any> {
547
547
  yield { id: 1, name: "Alice" };
548
548
  yield { id: 2, name: "Bob" };
549
549
  }
550
550
  }
551
551
 
552
552
  // Verify registration worked
553
- expect(getRegisteredAsyncProvider("getExampleData")).toBeDefined();
553
+ expect(getRegisteredFunctionFactory("getExampleData", "async")).toBeDefined();
554
554
 
555
555
  // Use the async provider in a FlowQuery statement
556
556
  const runner = new Runner("LOAD JSON FROM getExampleData() AS data RETURN data.id AS id, data.name AS name");
@@ -561,6 +561,44 @@ describe("Plugin Functions Integration with FlowQuery", () => {
561
561
  expect(runner.results[1]).toEqual({ id: 2, name: "Bob" });
562
562
  });
563
563
 
564
+ test("Function names are case-insensitive", async () => {
565
+ @FunctionDef({
566
+ description: "Test function for case insensitivity",
567
+ category: "async",
568
+ parameters: [],
569
+ output: { description: "Test data", type: "object" }
570
+ })
571
+ class MixedCaseFunc extends AsyncFunction {
572
+ public async *generate(): AsyncGenerator<any> {
573
+ yield { value: 42 };
574
+ }
575
+ }
576
+
577
+ // Verify registration works with different casings
578
+ expect(getRegisteredFunctionFactory("MixedCaseFunc", "async")).toBeDefined();
579
+ expect(getRegisteredFunctionFactory("mixedcasefunc", "async")).toBeDefined();
580
+ expect(getRegisteredFunctionFactory("MIXEDCASEFUNC", "async")).toBeDefined();
581
+ expect(getRegisteredFunctionFactory("mIxEdCaSeFuNc", "async")).toBeDefined();
582
+
583
+ // Verify metadata lookup is case-insensitive
584
+ expect(getFunctionMetadata("MixedCaseFunc", "async")).toBeDefined();
585
+ expect(getFunctionMetadata("mixedcasefunc", "async")).toBeDefined();
586
+ expect(getFunctionMetadata("MIXEDCASEFUNC", "async")).toBeDefined();
587
+
588
+ // Test using different casings in FlowQuery statements
589
+ const runner1 = new Runner("LOAD JSON FROM mixedcasefunc() AS d RETURN d.value AS v");
590
+ await runner1.run();
591
+ expect(runner1.results[0]).toEqual({ v: 42 });
592
+
593
+ const runner2 = new Runner("LOAD JSON FROM MIXEDCASEFUNC() AS d RETURN d.value AS v");
594
+ await runner2.run();
595
+ expect(runner2.results[0]).toEqual({ v: 42 });
596
+
597
+ const runner3 = new Runner("LOAD JSON FROM MixedCaseFunc() AS d RETURN d.value AS v");
598
+ await runner3.run();
599
+ expect(runner3.results[0]).toEqual({ v: 42 });
600
+ });
601
+
564
602
  test("Custom function can be retrieved via functions() in a FlowQuery statement", async () => {
565
603
  @FunctionDef({
566
604
  description: "A unique test function for introspection",
@@ -17,12 +17,12 @@ test('Test Parser', () => {
17
17
 
18
18
  test('Test Parser with function', () => {
19
19
  const parser = new Parser();
20
- const ast = parser.parse('RETURN f()');
20
+ const ast = parser.parse('RETURN rand()');
21
21
  expect(ast.print()).toBe(
22
22
  'ASTNode\n' +
23
23
  '- Return\n' +
24
24
  '-- Expression\n' +
25
- '--- Function (f)'
25
+ '--- Function (rand)'
26
26
  );
27
27
  });
28
28
 
@@ -1,14 +0,0 @@
1
- # OpenAI API Configuration
2
- # Copy this file to .env and fill in your values
3
-
4
- # Your OpenAI API key (required)
5
- OPENAI_API_KEY=sk-your-api-key-here
6
-
7
- # Optional: OpenAI Organization ID
8
- OPENAI_ORGANIZATION_ID=org-your-org-id
9
-
10
- # Optional: Default model
11
- OPENAI_MODEL=gpt-4o-mini
12
-
13
- # Note: These values are read at build time and used as defaults
14
- # when localStorage is empty. Changes require rebuilding the app.