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.
- package/README.md +142 -38
- package/dist/flowquery.min.js +1 -1
- package/dist/parsing/functions/aggregate_function.d.ts +1 -1
- package/dist/parsing/functions/aggregate_function.d.ts.map +1 -1
- package/dist/parsing/functions/aggregate_function.js.map +1 -1
- package/dist/parsing/functions/async_function.d.ts +4 -15
- package/dist/parsing/functions/async_function.d.ts.map +1 -1
- package/dist/parsing/functions/async_function.js +6 -59
- package/dist/parsing/functions/async_function.js.map +1 -1
- package/dist/parsing/functions/function.d.ts +1 -1
- package/dist/parsing/functions/function.d.ts.map +1 -1
- package/dist/parsing/functions/function.js +1 -1
- package/dist/parsing/functions/function.js.map +1 -1
- package/dist/parsing/functions/function_factory.d.ts +2 -0
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +12 -11
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/functions/function_metadata.d.ts +53 -24
- package/dist/parsing/functions/function_metadata.d.ts.map +1 -1
- package/dist/parsing/functions/function_metadata.js +57 -53
- package/dist/parsing/functions/function_metadata.js.map +1 -1
- package/dist/parsing/functions/predicate_function.d.ts +1 -1
- package/dist/parsing/functions/predicate_function.d.ts.map +1 -1
- package/dist/parsing/functions/predicate_function.js +1 -1
- package/dist/parsing/functions/predicate_function.js.map +1 -1
- package/dist/parsing/operations/load.js +1 -1
- package/dist/parsing/operations/load.js.map +1 -1
- package/dist/parsing/parser.d.ts.map +1 -1
- package/dist/parsing/parser.js +1 -2
- package/dist/parsing/parser.js.map +1 -1
- package/docs/flowquery.min.js +1 -1
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/misc/apps/RAG/README.md +0 -12
- package/misc/apps/RAG/package.json +1 -2
- package/misc/apps/RAG/src/components/ChatContainer.tsx +33 -6
- package/misc/apps/RAG/src/components/ChatMessage.css +24 -0
- package/misc/apps/RAG/src/components/ChatMessage.tsx +51 -2
- package/misc/apps/RAG/src/components/FlowQueryAgent.ts +566 -286
- package/misc/apps/RAG/src/plugins/index.ts +3 -1
- package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +6 -5
- package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +3 -3
- package/misc/apps/RAG/src/plugins/loaders/Form.ts +5 -5
- package/misc/apps/RAG/src/plugins/loaders/Llm.ts +14 -14
- package/misc/apps/RAG/src/plugins/loaders/MockData.ts +5 -5
- package/misc/apps/RAG/src/plugins/loaders/Table.ts +4 -4
- package/misc/apps/RAG/src/plugins/loaders/Weather.ts +126 -0
- package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +4 -0
- package/package.json +1 -1
- package/src/parsing/functions/aggregate_function.ts +1 -1
- package/src/parsing/functions/async_function.ts +5 -50
- package/src/parsing/functions/function.ts +2 -2
- package/src/parsing/functions/function_factory.ts +18 -9
- package/src/parsing/functions/function_metadata.ts +57 -56
- package/src/parsing/functions/predicate_function.ts +2 -2
- package/src/parsing/operations/load.ts +1 -1
- package/src/parsing/parser.ts +2 -1
- package/tests/extensibility.test.ts +50 -12
- package/tests/parsing/parser.test.ts +2 -2
- 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
|
|
83
|
+
static register<T extends new (...args: any[]) => any>(constructor: T, options: FunctionDefOptions): void {
|
|
100
84
|
const instance: any = new constructor();
|
|
101
|
-
const
|
|
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 `
|
|
148
|
-
* an AsyncGenerator.
|
|
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
|
-
* //
|
|
134
|
+
* // Scalar function example
|
|
157
135
|
* @FunctionDef({
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
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
|
|
165
|
-
*
|
|
166
|
-
*
|
|
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
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
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
|
|
175
|
-
*
|
|
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
|
-
|
|
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
|
|
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.
|
|
90
|
+
for await (const item of asyncFunc.generate()) {
|
|
91
91
|
this._value = item;
|
|
92
92
|
await this.next?.run();
|
|
93
93
|
}
|
package/src/parsing/parser.ts
CHANGED
|
@@ -597,7 +597,7 @@ class Parser extends BaseParser {
|
|
|
597
597
|
if(!this.peek()?.isLeftParenthesis()) {
|
|
598
598
|
return null;
|
|
599
599
|
}
|
|
600
|
-
const asyncFunc =
|
|
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
|
-
|
|
16
|
-
|
|
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
|
|
220
|
-
async *
|
|
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
|
|
228
|
+
const loader = new Simple("simple");
|
|
229
229
|
const results: any[] = [];
|
|
230
|
-
for await (const item of loader.
|
|
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 =
|
|
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
|
|
543
|
+
output: { description: "Example data o.bject", type: "object" }
|
|
544
544
|
})
|
|
545
|
-
class
|
|
546
|
-
async *
|
|
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(
|
|
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
|
|
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 (
|
|
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.
|