agentlang 0.10.1 → 0.10.2
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/out/api/http.d.ts.map +1 -1
- package/out/api/http.js +136 -0
- package/out/api/http.js.map +1 -1
- package/out/language/generated/ast.d.ts +27 -2
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +20 -0
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +246 -206
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +263 -206
- package/out/language/main.cjs.map +2 -2
- package/out/language/parser.d.ts.map +1 -1
- package/out/language/parser.js +28 -2
- package/out/language/parser.js.map +1 -1
- package/out/language/syntax.d.ts +14 -0
- package/out/language/syntax.d.ts.map +1 -1
- package/out/language/syntax.js +60 -27
- package/out/language/syntax.js.map +1 -1
- package/out/runtime/api.d.ts +2 -0
- package/out/runtime/api.d.ts.map +1 -1
- package/out/runtime/api.js +3 -0
- package/out/runtime/api.js.map +1 -1
- package/out/runtime/datefns.d.ts +34 -0
- package/out/runtime/datefns.d.ts.map +1 -0
- package/out/runtime/datefns.js +82 -0
- package/out/runtime/datefns.js.map +1 -0
- package/out/runtime/exec-graph.d.ts.map +1 -1
- package/out/runtime/exec-graph.js +22 -3
- package/out/runtime/exec-graph.js.map +1 -1
- package/out/runtime/interpreter.d.ts +9 -0
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +71 -7
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/module.d.ts +8 -0
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +23 -0
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/ai.d.ts +7 -3
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +67 -21
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/modules/core.d.ts.map +1 -1
- package/out/runtime/modules/core.js +5 -1
- package/out/runtime/modules/core.js.map +1 -1
- package/out/runtime/monitor.d.ts +6 -0
- package/out/runtime/monitor.d.ts.map +1 -1
- package/out/runtime/monitor.js +21 -1
- package/out/runtime/monitor.js.map +1 -1
- package/out/runtime/relgraph.d.ts.map +1 -1
- package/out/runtime/relgraph.js +7 -3
- package/out/runtime/relgraph.js.map +1 -1
- package/out/runtime/resolvers/interface.d.ts +3 -2
- package/out/runtime/resolvers/interface.d.ts.map +1 -1
- package/out/runtime/resolvers/interface.js +3 -2
- package/out/runtime/resolvers/interface.js.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.js +17 -4
- package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +17 -7
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/out/runtime/util.d.ts +3 -2
- package/out/runtime/util.d.ts.map +1 -1
- package/out/runtime/util.js +13 -2
- package/out/runtime/util.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +1 -1
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/out/test-harness.d.ts +36 -0
- package/out/test-harness.d.ts.map +1 -0
- package/out/test-harness.js +341 -0
- package/out/test-harness.js.map +1 -0
- package/package.json +4 -1
- package/src/api/http.ts +138 -0
- package/src/language/agentlang.langium +3 -1
- package/src/language/generated/ast.ts +32 -1
- package/src/language/generated/grammar.ts +246 -206
- package/src/language/parser.ts +33 -1
- package/src/language/syntax.ts +71 -24
- package/src/runtime/api.ts +5 -0
- package/src/runtime/datefns.ts +112 -0
- package/src/runtime/exec-graph.ts +23 -2
- package/src/runtime/interpreter.ts +82 -6
- package/src/runtime/module.ts +26 -0
- package/src/runtime/modules/ai.ts +78 -31
- package/src/runtime/modules/core.ts +5 -1
- package/src/runtime/monitor.ts +27 -1
- package/src/runtime/relgraph.ts +7 -3
- package/src/runtime/resolvers/interface.ts +4 -2
- package/src/runtime/resolvers/sqldb/dbutil.ts +20 -6
- package/src/runtime/resolvers/sqldb/impl.ts +17 -7
- package/src/runtime/util.ts +19 -2
- package/src/syntaxes/agentlang.monarch.ts +1 -1
- package/src/test-harness.ts +423 -0
|
@@ -70,7 +70,6 @@ import {
|
|
|
70
70
|
} from '../agents/common.js';
|
|
71
71
|
import { logger } from '../logger.js';
|
|
72
72
|
import { FlowStep } from '../agents/flows.js';
|
|
73
|
-
import Handlebars from 'handlebars';
|
|
74
73
|
import { Statement } from '../../language/generated/ast.js';
|
|
75
74
|
import { isMonitoringEnabled, TtlCache } from '../state.js';
|
|
76
75
|
import { isNodeEnv } from '../../utils/runtime.js';
|
|
@@ -107,6 +106,7 @@ entity ${AgentEntityName} {
|
|
|
107
106
|
flows String @optional,
|
|
108
107
|
validate String @optional,
|
|
109
108
|
retry String @optional,
|
|
109
|
+
saveResponseAs String @optional,
|
|
110
110
|
llm String
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -323,6 +323,7 @@ export class AgentInstance {
|
|
|
323
323
|
flows: string | undefined;
|
|
324
324
|
validate: string | undefined;
|
|
325
325
|
retry: string | undefined;
|
|
326
|
+
saveResponseAs: string | undefined;
|
|
326
327
|
stateless: boolean = false;
|
|
327
328
|
private toolsArray: string[] | undefined = undefined;
|
|
328
329
|
private hasModuleTools = false;
|
|
@@ -645,7 +646,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
|
|
|
645
646
|
const spad = env.getScratchPad();
|
|
646
647
|
if (spad !== undefined && Object.keys(spad).length > 0) {
|
|
647
648
|
if (finalInstruction.indexOf('{{') > 0) {
|
|
648
|
-
return
|
|
649
|
+
return env.maybeRewriteTemplatePatterns(finalInstruction, spad);
|
|
649
650
|
} else {
|
|
650
651
|
const ctx = JSON.stringify(spad);
|
|
651
652
|
return `${finalInstruction}\nSome additional context:\n${ctx}`;
|
|
@@ -657,6 +658,7 @@ Only return a pure JSON object with no extra text, annotations etc.`;
|
|
|
657
658
|
}
|
|
658
659
|
|
|
659
660
|
private static UserTag = 'user';
|
|
661
|
+
private static FileTag = 'file';
|
|
660
662
|
|
|
661
663
|
private async getFullInstructions(
|
|
662
664
|
env: Environment,
|
|
@@ -670,13 +672,21 @@ Only return a pure JSON object with no extra text, annotations etc.`;
|
|
|
670
672
|
}
|
|
671
673
|
}
|
|
672
674
|
|
|
673
|
-
private
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
675
|
+
private async maybeFillInFileContents(instructions: string, env: Environment): Promise<string> {
|
|
676
|
+
const chatId = env.getActiveChatId();
|
|
677
|
+
const FC = '$FC';
|
|
678
|
+
if (chatId && instructions.indexOf(`<${AgentInstance.FileTag}>`) > 0) {
|
|
679
|
+
const ext = extractAndRemoveAllXmlTaggedText(instructions, AgentInstance.FileTag, FC);
|
|
680
|
+
if (ext.extracted) {
|
|
681
|
+
instructions = ext.updatedText;
|
|
682
|
+
for (let i = 0; i < ext.extracted.length; ++i) {
|
|
683
|
+
const fileName = ext.extracted[i];
|
|
684
|
+
const contents = await loadLocalAgentResult(chatId, fileName);
|
|
685
|
+
if (contents) instructions = instructions.replace(FC, contents);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return instructions;
|
|
680
690
|
}
|
|
681
691
|
|
|
682
692
|
maybeValidateJsonResponse(response: string | undefined): object | undefined {
|
|
@@ -875,7 +885,8 @@ Only return a pure JSON object with no extra text, annotations etc.`;
|
|
|
875
885
|
}
|
|
876
886
|
let tmpMsg = message;
|
|
877
887
|
if (extractedText?.extracted) {
|
|
878
|
-
|
|
888
|
+
const s = await this.maybeFillInFileContents(extractedText.extracted.join('\n'), env);
|
|
889
|
+
tmpMsg = `${tmpMsg}\n${s}`;
|
|
879
890
|
}
|
|
880
891
|
const hmsg = await this.maybeAddRelevantDocuments(
|
|
881
892
|
this.maybeAddFlowContext(tmpMsg, env),
|
|
@@ -927,6 +938,13 @@ Only return a pure JSON object with no extra text, annotations etc.`;
|
|
|
927
938
|
await saveAgentChatSession(chatId, msgs, env);
|
|
928
939
|
}
|
|
929
940
|
if (monitoringEnabled) env.setMonitorEntryLlmResponse(response.content);
|
|
941
|
+
if (monitoringEnabled && response.sysMsg.usage_metadata) {
|
|
942
|
+
const u = response.sysMsg.usage_metadata;
|
|
943
|
+
env.setMonitorEntryLlmTokenUsage(u.input_tokens, u.output_tokens, u.total_tokens);
|
|
944
|
+
}
|
|
945
|
+
if (this.saveResponseAs) {
|
|
946
|
+
await saveAgentResponse(this.saveResponseAs, response.content, env);
|
|
947
|
+
}
|
|
930
948
|
env.setLastResult(response.content);
|
|
931
949
|
} catch (err: any) {
|
|
932
950
|
logger.error(`Error while invoking ${agentName} - ${err}`);
|
|
@@ -1448,15 +1466,9 @@ export async function saveFlowStepResultLocally(
|
|
|
1448
1466
|
chatId: string,
|
|
1449
1467
|
step: string,
|
|
1450
1468
|
result: string,
|
|
1451
|
-
suspensionId
|
|
1452
|
-
): Promise<
|
|
1469
|
+
suspensionId?: string | undefined
|
|
1470
|
+
): Promise<string> {
|
|
1453
1471
|
const fs = await getFileSystem();
|
|
1454
|
-
const attrs = newInstanceAttributes()
|
|
1455
|
-
.set('chatId', chatId)
|
|
1456
|
-
.set('step', step)
|
|
1457
|
-
.set('result', result)
|
|
1458
|
-
.set('suspensionId', suspensionId);
|
|
1459
|
-
const inst = makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
|
|
1460
1472
|
const rootDirName = LocalFlowStepsRootDirName;
|
|
1461
1473
|
if (!(await fs.exists(rootDirName))) {
|
|
1462
1474
|
await fs.mkdir(rootDirName);
|
|
@@ -1469,8 +1481,18 @@ export async function saveFlowStepResultLocally(
|
|
|
1469
1481
|
if (await fs.exists(fileName)) {
|
|
1470
1482
|
await fs.unlink(fileName);
|
|
1471
1483
|
}
|
|
1472
|
-
|
|
1473
|
-
|
|
1484
|
+
let s = result;
|
|
1485
|
+
if (suspensionId) {
|
|
1486
|
+
const attrs = newInstanceAttributes()
|
|
1487
|
+
.set('chatId', chatId)
|
|
1488
|
+
.set('step', step)
|
|
1489
|
+
.set('result', result)
|
|
1490
|
+
.set('suspensionId', suspensionId);
|
|
1491
|
+
const inst = makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
|
|
1492
|
+
s = JSON.stringify(inst.attributesAsObject(true));
|
|
1493
|
+
}
|
|
1494
|
+
await fs.writeFile(fileName, s);
|
|
1495
|
+
return s;
|
|
1474
1496
|
}
|
|
1475
1497
|
|
|
1476
1498
|
export async function saveFlowStepResult(
|
|
@@ -1479,9 +1501,10 @@ export async function saveFlowStepResult(
|
|
|
1479
1501
|
result: string,
|
|
1480
1502
|
suspensionId: string,
|
|
1481
1503
|
env: Environment
|
|
1482
|
-
): Promise<
|
|
1504
|
+
): Promise<boolean> {
|
|
1483
1505
|
if (LocalAgentgFlow) {
|
|
1484
|
-
|
|
1506
|
+
await saveFlowStepResultLocally(chatId, step, result, suspensionId);
|
|
1507
|
+
return true;
|
|
1485
1508
|
} else {
|
|
1486
1509
|
const t = `${CoreAIModuleName}/${AgentFlowStep}`;
|
|
1487
1510
|
try {
|
|
@@ -1495,11 +1518,11 @@ export async function saveFlowStepResult(
|
|
|
1495
1518
|
undefined,
|
|
1496
1519
|
env
|
|
1497
1520
|
);
|
|
1498
|
-
if (isInstanceOfType(inst, t)) return
|
|
1499
|
-
else return
|
|
1521
|
+
if (isInstanceOfType(inst, t)) return true;
|
|
1522
|
+
else return false;
|
|
1500
1523
|
} catch (reason: any) {
|
|
1501
1524
|
logger.error(`failed to save flow result for step ${step} - ${reason}`);
|
|
1502
|
-
return
|
|
1525
|
+
return false;
|
|
1503
1526
|
}
|
|
1504
1527
|
}
|
|
1505
1528
|
}
|
|
@@ -1537,10 +1560,10 @@ export async function loadFlowStepResults(chatId: string): Promise<Instance[]> {
|
|
|
1537
1560
|
}
|
|
1538
1561
|
}
|
|
1539
1562
|
|
|
1540
|
-
export async function
|
|
1563
|
+
export async function loadLocalAgentResult(
|
|
1541
1564
|
chatId: string,
|
|
1542
1565
|
step: string
|
|
1543
|
-
): Promise<
|
|
1566
|
+
): Promise<string | undefined> {
|
|
1544
1567
|
const fs = await getFileSystem();
|
|
1545
1568
|
const dirName = `${LocalFlowStepsRootDirName}/${chatId}`;
|
|
1546
1569
|
if (await fs.exists(dirName)) {
|
|
@@ -1548,10 +1571,7 @@ export async function loadLocalFlowStep(
|
|
|
1548
1571
|
for (let i = 0; i < fileNames.length; ++i) {
|
|
1549
1572
|
const fileName = fileNames[i];
|
|
1550
1573
|
if (fileName === step) {
|
|
1551
|
-
|
|
1552
|
-
JSON.parse(await fs.readFile(`${dirName}/${fileName}`))
|
|
1553
|
-
);
|
|
1554
|
-
return makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
|
|
1574
|
+
return await fs.readFile(`${dirName}/${fileName}`);
|
|
1555
1575
|
}
|
|
1556
1576
|
}
|
|
1557
1577
|
return undefined;
|
|
@@ -1560,6 +1580,19 @@ export async function loadLocalFlowStep(
|
|
|
1560
1580
|
}
|
|
1561
1581
|
}
|
|
1562
1582
|
|
|
1583
|
+
export async function loadLocalFlowStep(
|
|
1584
|
+
chatId: string,
|
|
1585
|
+
step: string
|
|
1586
|
+
): Promise<Instance | undefined> {
|
|
1587
|
+
const contents = await loadLocalAgentResult(chatId, step);
|
|
1588
|
+
if (contents) {
|
|
1589
|
+
const attrs = objectToInstanceAttributes(JSON.parse(contents));
|
|
1590
|
+
return makeInstance(CoreAIModuleName, AgentFlowStep, attrs);
|
|
1591
|
+
} else {
|
|
1592
|
+
return undefined;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1563
1596
|
export async function loadFlowStep(chatId: string, step: string): Promise<Instance | undefined> {
|
|
1564
1597
|
if (LocalAgentgFlow) {
|
|
1565
1598
|
return await loadLocalFlowStep(chatId, step);
|
|
@@ -1639,3 +1672,17 @@ export async function fetchAndCreateDocument(
|
|
|
1639
1672
|
throw new Error(`Failed to fetch document: ${title} from ${url}`);
|
|
1640
1673
|
}
|
|
1641
1674
|
}
|
|
1675
|
+
|
|
1676
|
+
export async function saveAgentResponse(
|
|
1677
|
+
fileName: string,
|
|
1678
|
+
response: string,
|
|
1679
|
+
env: Environment
|
|
1680
|
+
): Promise<string> {
|
|
1681
|
+
const chatId = env.getActiveChatId();
|
|
1682
|
+
if (chatId) {
|
|
1683
|
+
await saveFlowStepResultLocally(chatId, fileName, response);
|
|
1684
|
+
} else {
|
|
1685
|
+
logger.warn(`No chatId set, ${fileName} was not saved.`);
|
|
1686
|
+
}
|
|
1687
|
+
return response;
|
|
1688
|
+
}
|
|
@@ -749,5 +749,9 @@ export function migrationDowns(inst: Instance): string[] | undefined {
|
|
|
749
749
|
|
|
750
750
|
export async function doRawQuery(q: any): Promise<any> {
|
|
751
751
|
const qs = objectToQueryPattern(q);
|
|
752
|
-
|
|
752
|
+
const result = await parseAndEvaluateStatement(qs);
|
|
753
|
+
return {
|
|
754
|
+
query: qs,
|
|
755
|
+
result: result.map((res: any) => (res.attributes ? Object.fromEntries(res.attributes) : res)),
|
|
756
|
+
};
|
|
753
757
|
}
|
package/src/runtime/monitor.ts
CHANGED
|
@@ -10,6 +10,9 @@ export class MonitorEntry {
|
|
|
10
10
|
private llm: boolean = false;
|
|
11
11
|
private llmPrompt: string | undefined;
|
|
12
12
|
private llmResponse: string | undefined;
|
|
13
|
+
private llmInputTokens: number | undefined;
|
|
14
|
+
private llmOutputTokens: number | undefined;
|
|
15
|
+
private llmTotalTokens: number | undefined;
|
|
13
16
|
private planner: boolean = false;
|
|
14
17
|
private flowStep: boolean = false;
|
|
15
18
|
private flow: boolean = false;
|
|
@@ -25,6 +28,10 @@ export class MonitorEntry {
|
|
|
25
28
|
return this.input;
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
getTimestamp(): number {
|
|
32
|
+
return this.timestamp;
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
setResult(result: any): MonitorEntry {
|
|
29
36
|
if (this.result === undefined) {
|
|
30
37
|
this.result = result;
|
|
@@ -68,6 +75,13 @@ export class MonitorEntry {
|
|
|
68
75
|
return this;
|
|
69
76
|
}
|
|
70
77
|
|
|
78
|
+
setLlmTokenUsage(input: number, output: number, total: number): MonitorEntry {
|
|
79
|
+
this.llmInputTokens = input;
|
|
80
|
+
this.llmOutputTokens = output;
|
|
81
|
+
this.llmTotalTokens = total;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
71
85
|
flagAsPlanner(): MonitorEntry {
|
|
72
86
|
this.llm = true;
|
|
73
87
|
if (this.flowStep || this.flow || this.decision) {
|
|
@@ -137,6 +151,11 @@ export class MonitorEntry {
|
|
|
137
151
|
llmObj.isFlowStep = this.flowStep;
|
|
138
152
|
llmObj.isDecision = this.decision;
|
|
139
153
|
llmObj.isFlow = this.flow;
|
|
154
|
+
if (this.llmInputTokens !== undefined) {
|
|
155
|
+
llmObj.inputTokens = this.llmInputTokens;
|
|
156
|
+
llmObj.outputTokens = this.llmOutputTokens;
|
|
157
|
+
llmObj.totalTokens = this.llmTotalTokens;
|
|
158
|
+
}
|
|
140
159
|
obj.llm = llmObj;
|
|
141
160
|
}
|
|
142
161
|
obj.label = this.input;
|
|
@@ -269,9 +288,16 @@ export class Monitor {
|
|
|
269
288
|
return this;
|
|
270
289
|
}
|
|
271
290
|
|
|
291
|
+
setEntryLlmTokenUsage(input: number, output: number, total: number): Monitor {
|
|
292
|
+
if (this.lastEntry !== undefined) {
|
|
293
|
+
this.lastEntry.setLlmTokenUsage(input, output, total);
|
|
294
|
+
}
|
|
295
|
+
return this;
|
|
296
|
+
}
|
|
297
|
+
|
|
272
298
|
private finalizeLastEntry(): void {
|
|
273
299
|
if (this.lastEntry) {
|
|
274
|
-
const ms = Date.now() - this.
|
|
300
|
+
const ms = Date.now() - this.lastEntry.getTimestamp();
|
|
275
301
|
this.lastEntry.setLatencyMs(ms);
|
|
276
302
|
if (MonitoringCallback !== undefined) {
|
|
277
303
|
MonitoringCallback(this.lastEntry);
|
package/src/runtime/relgraph.ts
CHANGED
|
@@ -51,8 +51,11 @@ export class RelationshipGraph {
|
|
|
51
51
|
node: RelationshipGraphNode,
|
|
52
52
|
onNode: Function,
|
|
53
53
|
onContainsRelationship: Function,
|
|
54
|
-
onBetweenRelationship: Function
|
|
54
|
+
onBetweenRelationship: Function,
|
|
55
|
+
visited: Set<RelationshipGraphNode>
|
|
55
56
|
) {
|
|
57
|
+
if (visited.has(node)) return;
|
|
58
|
+
visited.add(node);
|
|
56
59
|
const n = node.entity.asFqName();
|
|
57
60
|
onNode(n, node.edges);
|
|
58
61
|
node.edges.forEach((edge: RelationshipGraphEdge) => {
|
|
@@ -60,13 +63,14 @@ export class RelationshipGraph {
|
|
|
60
63
|
? onContainsRelationship
|
|
61
64
|
: onBetweenRelationship;
|
|
62
65
|
rf(n, edge.node.entity.asFqName(), edge.relationship);
|
|
63
|
-
this.walkEdges(edge.node, onNode, onContainsRelationship, onBetweenRelationship);
|
|
66
|
+
this.walkEdges(edge.node, onNode, onContainsRelationship, onBetweenRelationship, visited);
|
|
64
67
|
});
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
walk(onNode: Function, onContainsRelationship: Function, onBetweenRelationship: Function) {
|
|
71
|
+
const visited = new Set<RelationshipGraphNode>();
|
|
68
72
|
this.nodes.forEach((node: RelationshipGraphNode) => {
|
|
69
|
-
this.walkEdges(node, onNode, onContainsRelationship, onBetweenRelationship);
|
|
73
|
+
this.walkEdges(node, onNode, onContainsRelationship, onBetweenRelationship, visited);
|
|
70
74
|
});
|
|
71
75
|
}
|
|
72
76
|
}
|
|
@@ -121,13 +121,15 @@ export class Resolver {
|
|
|
121
121
|
/**
|
|
122
122
|
* Return all instances connected to connectedInstance via the given between-relationship
|
|
123
123
|
* @param relationship Between relationship
|
|
124
|
-
* @param connectedInstance The instance to
|
|
124
|
+
* @param connectedInstance The instance to traverse the relationship from
|
|
125
125
|
* @param inst Target instance with query attributes
|
|
126
|
+
* @param connectedAlias For self-referencing relationships, the alias of the connected instance's role
|
|
126
127
|
*/
|
|
127
128
|
public async queryConnectedInstances(
|
|
128
129
|
relationship: Relationship,
|
|
129
130
|
connectedInstance: Instance,
|
|
130
|
-
inst: Instance
|
|
131
|
+
inst: Instance,
|
|
132
|
+
connectedAlias?: string
|
|
131
133
|
): Promise<any> {
|
|
132
134
|
return this.notImpl(`queryConnectedInstances(${relationship}, ${connectedInstance}, ${inst})`);
|
|
133
135
|
}
|
|
@@ -188,13 +188,27 @@ function ormSchemaFromRecordSchema(
|
|
|
188
188
|
const fqName = makeFqName(moduleName, entityName);
|
|
189
189
|
getAllOneToOneRelationshipsForEntity(moduleName, entityName, allBetRels).forEach(
|
|
190
190
|
(re: Relationship) => {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
191
|
+
if (re.isSelfReferencing()) {
|
|
192
|
+
// Self-referencing one-to-one: add columns for both aliases
|
|
193
|
+
// so each side of the relationship can be stored on the entity.
|
|
194
|
+
// Both must be nullable since they're set after entity creation.
|
|
195
|
+
[re.node1.alias, re.node2.alias].forEach((colName: string) => {
|
|
196
|
+
if (cols.has(colName)) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Cannot establish relationship ${re.name}, ${entityName}.${colName} already exists`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
cols.set(colName, { type: 'varchar', unique: true });
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
const colName = re.getInverseAliasForName(fqName);
|
|
205
|
+
if (cols.has(colName)) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Cannot establish relationship ${re.name}, ${entityName}.${colName} already exists`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
cols.set(colName, { type: 'varchar', unique: true });
|
|
196
211
|
}
|
|
197
|
-
cols.set(colName, { type: 'varchar', unique: true });
|
|
198
212
|
}
|
|
199
213
|
);
|
|
200
214
|
if (relsSpec.size > 0) {
|
|
@@ -480,16 +480,17 @@ export class SqlDbResolver extends Resolver {
|
|
|
480
480
|
public override async queryConnectedInstances(
|
|
481
481
|
relationship: Relationship,
|
|
482
482
|
connectedInstance: Instance,
|
|
483
|
-
inst: Instance
|
|
483
|
+
inst: Instance,
|
|
484
|
+
connectedAlias?: string
|
|
484
485
|
): Promise<Instance[]> {
|
|
485
486
|
let result = SqlDbResolver.EmptyResultSet;
|
|
486
487
|
if (relationship.isOneToOne()) {
|
|
487
|
-
const col = relationship.
|
|
488
|
+
const col = relationship.getAliasForConnected(connectedInstance, connectedAlias);
|
|
488
489
|
inst.addQuery(col, '=', connectedInstance.lookup(PathAttributeName));
|
|
489
490
|
return await this.queryInstances(inst, false);
|
|
490
491
|
} else {
|
|
491
|
-
const from = relationship.
|
|
492
|
-
const to = relationship.
|
|
492
|
+
const from = relationship.getAliasForConnected(connectedInstance, connectedAlias);
|
|
493
|
+
const to = relationship.getInverseAliasForConnected(connectedInstance, connectedAlias);
|
|
493
494
|
await getAllConnected(
|
|
494
495
|
asTableReference(inst.moduleName, inst.name),
|
|
495
496
|
inst.queryAttributesAsObject(),
|
|
@@ -797,9 +798,18 @@ function ensureOneToOneAttributes(inst: Instance) {
|
|
|
797
798
|
const betRels = getAllBetweenRelationships();
|
|
798
799
|
getAllOneToOneRelationshipsForEntity(inst.moduleName, inst.name, betRels).forEach(
|
|
799
800
|
(re: Relationship) => {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
801
|
+
if (re.isSelfReferencing()) {
|
|
802
|
+
// Self-referencing one-to-one: ensure both alias columns have placeholder values
|
|
803
|
+
[re.node1.alias, re.node2.alias].forEach((n: string) => {
|
|
804
|
+
if (!inst.attributes.has(n)) {
|
|
805
|
+
inst.attributes.set(n, crypto.randomUUID());
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
} else {
|
|
809
|
+
const n = re.getInverseAliasFor(inst);
|
|
810
|
+
if (!inst.attributes.has(n)) {
|
|
811
|
+
inst.attributes.set(n, crypto.randomUUID());
|
|
812
|
+
}
|
|
803
813
|
}
|
|
804
814
|
}
|
|
805
815
|
);
|
package/src/runtime/util.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { isNodeEnv, path } from '../utils/runtime.js';
|
|
|
2
2
|
import {
|
|
3
3
|
AliasSpec,
|
|
4
4
|
CatchSpec,
|
|
5
|
+
EmptySpec,
|
|
5
6
|
ExtendsClause,
|
|
6
7
|
isLiteral,
|
|
7
8
|
MapEntry,
|
|
@@ -441,6 +442,18 @@ export function firstCatchSpec(stmt: Statement): CatchSpec | undefined {
|
|
|
441
442
|
return undefined;
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
export function firstEmptySpec(stmt: Statement): EmptySpec | undefined {
|
|
446
|
+
if (stmt.hints) {
|
|
447
|
+
for (let i = 0; i < stmt.hints.length; ++i) {
|
|
448
|
+
const rh = stmt.hints[i];
|
|
449
|
+
if (rh.emptySpec) {
|
|
450
|
+
return rh.emptySpec;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
|
|
444
457
|
function maybeExtractEntryName(n: string): string {
|
|
445
458
|
const i = n.indexOf('$');
|
|
446
459
|
if (i > 0) {
|
|
@@ -671,7 +684,11 @@ export type ExtractedText = {
|
|
|
671
684
|
};
|
|
672
685
|
|
|
673
686
|
// extract all data between a given xml tag from within an arbitray text.
|
|
674
|
-
export function extractAndRemoveAllXmlTaggedText(
|
|
687
|
+
export function extractAndRemoveAllXmlTaggedText(
|
|
688
|
+
text: string,
|
|
689
|
+
tagName: string,
|
|
690
|
+
replaceWith: string = ''
|
|
691
|
+
): ExtractedText {
|
|
675
692
|
const pattern = `<${tagName}\\b[^>]*>([\\s\\S]*?)</${tagName}>`;
|
|
676
693
|
const regex = new RegExp(pattern, 'gi');
|
|
677
694
|
|
|
@@ -683,7 +700,7 @@ export function extractAndRemoveAllXmlTaggedText(text: string, tagName: string):
|
|
|
683
700
|
extracted.push(match[1]);
|
|
684
701
|
}
|
|
685
702
|
|
|
686
|
-
updatedText = text.replace(regex,
|
|
703
|
+
updatedText = text.replace(regex, replaceWith);
|
|
687
704
|
|
|
688
705
|
return { extracted, updatedText };
|
|
689
706
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Monarch syntax highlighting for the agentlang language.
|
|
2
2
|
export default {
|
|
3
3
|
keywords: [
|
|
4
|
-
'@actions','@after','@as','@asc','@async','@before','@catch','@desc','@distinct','@enum','@expr','@from','@full_join','@groupBy','@inner_join','@into','@join','@left_join','@meta','@oneof','@orderBy','@public','@rbac','@ref','@right_join','@then','@upsert','@where','@withRole','@with_unique','agent','agentlang/retry','allow','and','attempts','await','backoff','between','case','commitTransaction','contains','create','decision','delete','directive','else','entity','error','eval','event','extends','false','flow','for','glossaryEntry','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','rollbackTransaction','scenario','startTransaction','subscribe','throw','true','update','upsert','where','workflow'
|
|
4
|
+
'@actions','@after','@as','@asc','@async','@before','@catch','@desc','@distinct','@empty','@enum','@expr','@from','@full_join','@groupBy','@inner_join','@into','@join','@left_join','@meta','@oneof','@orderBy','@public','@rbac','@ref','@right_join','@then','@upsert','@where','@withRole','@with_unique','agent','agentlang/retry','allow','and','attempts','await','backoff','between','case','commitTransaction','contains','create','decision','delete','directive','else','entity','error','eval','event','extends','false','flow','for','glossaryEntry','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','rollbackTransaction','scenario','startTransaction','subscribe','throw','true','update','upsert','where','workflow'
|
|
5
5
|
],
|
|
6
6
|
operators: [
|
|
7
7
|
'!=','*','+',',','-','-->','.','/',':',';','<','<=','<>','=','==','>','>=','?','@'
|