agentlang 0.1.6 → 0.1.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.
- package/package.json +1 -1
- package/src/runtime/auth/cognito.ts +152 -0
- package/src/runtime/auth/interface.ts +24 -0
- package/src/runtime/exec-graph.ts +10 -4
- package/src/runtime/interpreter.ts +65 -31
- package/src/runtime/loader.ts +37 -0
- package/src/runtime/module.ts +20 -0
- package/src/runtime/modules/ai.ts +103 -1
- package/src/runtime/modules/auth.ts +73 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlang",
|
|
3
3
|
"description": "The easiest way to build the most reliable AI agents - enterprise-grade teams of AI agents that collaborate with each other and humans",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7",
|
|
5
5
|
"license": "Sustainable Use License",
|
|
6
6
|
"author": "agentlang-ai",
|
|
7
7
|
"homepage": "https://github.com/agentlang-ai/agentlang#readme",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentlangAuth,
|
|
3
|
+
InviteUserCallback,
|
|
4
|
+
InvitationInfo,
|
|
3
5
|
LoginCallback,
|
|
4
6
|
LogoutCallback,
|
|
5
7
|
SessionInfo,
|
|
@@ -40,6 +42,8 @@ let ForgotPasswordCommand: any = undefined;
|
|
|
40
42
|
let ConfirmForgotPasswordCommand: any = undefined;
|
|
41
43
|
let AdminGetUserCommand: any = undefined;
|
|
42
44
|
let InitiateAuthCommand: any = undefined;
|
|
45
|
+
let AdminCreateUserCommand: any = undefined;
|
|
46
|
+
let RespondToAuthChallengeCommand: any = undefined;
|
|
43
47
|
let AuthenticationDetails: any = undefined;
|
|
44
48
|
let CognitoUser: any = undefined;
|
|
45
49
|
let CognitoUserPool: any = undefined;
|
|
@@ -61,6 +65,8 @@ if (isNodeEnv) {
|
|
|
61
65
|
ConfirmForgotPasswordCommand = cip.ConfirmForgotPasswordCommand;
|
|
62
66
|
AdminGetUserCommand = cip.AdminGetUserCommand;
|
|
63
67
|
InitiateAuthCommand = cip.InitiateAuthCommand;
|
|
68
|
+
AdminCreateUserCommand = cip.AdminCreateUserCommand;
|
|
69
|
+
RespondToAuthChallengeCommand = cip.RespondToAuthChallengeCommand;
|
|
64
70
|
|
|
65
71
|
const ci = await import('amazon-cognito-identity-js');
|
|
66
72
|
AuthenticationDetails = ci.AuthenticationDetails;
|
|
@@ -1013,4 +1019,150 @@ export class CognitoAuth implements AgentlangAuth {
|
|
|
1013
1019
|
throw err; // This line won't be reached due to handleCognitoError throwing
|
|
1014
1020
|
}
|
|
1015
1021
|
}
|
|
1022
|
+
|
|
1023
|
+
async inviteUser(
|
|
1024
|
+
email: string,
|
|
1025
|
+
firstName: string,
|
|
1026
|
+
lastName: string,
|
|
1027
|
+
userData: Map<string, any> | undefined,
|
|
1028
|
+
env: Environment,
|
|
1029
|
+
cb: InviteUserCallback
|
|
1030
|
+
): Promise<void> {
|
|
1031
|
+
try {
|
|
1032
|
+
const client = new CognitoIdentityProviderClient({
|
|
1033
|
+
region: process.env.AWS_REGION || 'us-west-2',
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
const userAttrs = [
|
|
1037
|
+
{
|
|
1038
|
+
Name: 'email',
|
|
1039
|
+
Value: email,
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
Name: 'email_verified',
|
|
1043
|
+
Value: 'true',
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
Name: 'given_name',
|
|
1047
|
+
Value: firstName,
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
Name: 'family_name',
|
|
1051
|
+
Value: lastName,
|
|
1052
|
+
},
|
|
1053
|
+
];
|
|
1054
|
+
|
|
1055
|
+
if (userData) {
|
|
1056
|
+
userData.forEach((v: any, k: string) => {
|
|
1057
|
+
userAttrs.push({ Name: k, Value: String(v) });
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
let userExists = false;
|
|
1062
|
+
try {
|
|
1063
|
+
const getUserCommand = new AdminGetUserCommand({
|
|
1064
|
+
UserPoolId: this.fetchUserPoolId(),
|
|
1065
|
+
Username: email,
|
|
1066
|
+
});
|
|
1067
|
+
await client.send(getUserCommand);
|
|
1068
|
+
userExists = true;
|
|
1069
|
+
logger.debug(`User ${email} already exists, will resend invitation`);
|
|
1070
|
+
} catch (err: any) {
|
|
1071
|
+
if (err.name !== 'UserNotFoundException') {
|
|
1072
|
+
throw err;
|
|
1073
|
+
}
|
|
1074
|
+
logger.debug(`User ${email} does not exist, will create new user`);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const command = new AdminCreateUserCommand({
|
|
1078
|
+
UserPoolId: this.fetchUserPoolId(),
|
|
1079
|
+
Username: email,
|
|
1080
|
+
UserAttributes: userAttrs,
|
|
1081
|
+
DesiredDeliveryMediums: ['EMAIL'],
|
|
1082
|
+
...(userExists ? { MessageAction: 'RESEND' } : {}),
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
logger.debug(`Attempting to invite user: ${email}`);
|
|
1086
|
+
const response = await client.send(command);
|
|
1087
|
+
|
|
1088
|
+
if (response.$metadata.httpStatusCode === 200) {
|
|
1089
|
+
logger.info(`User invitation successful for: ${email}`);
|
|
1090
|
+
|
|
1091
|
+
await ensureUser(email, firstName, lastName, env);
|
|
1092
|
+
|
|
1093
|
+
const invitationInfo: InvitationInfo = {
|
|
1094
|
+
email: email,
|
|
1095
|
+
firstName: firstName,
|
|
1096
|
+
lastName: lastName,
|
|
1097
|
+
invitationId: response.User?.Username,
|
|
1098
|
+
systemInvitationInfo: response,
|
|
1099
|
+
};
|
|
1100
|
+
|
|
1101
|
+
cb(invitationInfo);
|
|
1102
|
+
} else {
|
|
1103
|
+
logger.error(
|
|
1104
|
+
`User invitation failed with HTTP status ${response.$metadata.httpStatusCode}`,
|
|
1105
|
+
{
|
|
1106
|
+
email: email,
|
|
1107
|
+
statusCode: response.$metadata.httpStatusCode,
|
|
1108
|
+
}
|
|
1109
|
+
);
|
|
1110
|
+
throw new BadRequestError(
|
|
1111
|
+
`User invitation failed with status ${response.$metadata.httpStatusCode}`
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
} catch (err: any) {
|
|
1115
|
+
if (err instanceof BadRequestError) throw err;
|
|
1116
|
+
logger.error(`User invitation error for ${email}:`, {
|
|
1117
|
+
errorName: err.name,
|
|
1118
|
+
errorMessage: sanitizeErrorMessage(err.message),
|
|
1119
|
+
});
|
|
1120
|
+
handleCognitoError(err, 'inviteUser');
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
async acceptInvitation(
|
|
1125
|
+
email: string,
|
|
1126
|
+
tempPassword: string,
|
|
1127
|
+
newPassword: string,
|
|
1128
|
+
_env: Environment
|
|
1129
|
+
): Promise<void> {
|
|
1130
|
+
try {
|
|
1131
|
+
const client = new CognitoIdentityProviderClient({
|
|
1132
|
+
region: process.env.AWS_REGION || 'us-west-2',
|
|
1133
|
+
credentials: fromEnv(),
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
const initAuth = new InitiateAuthCommand({
|
|
1137
|
+
AuthFlow: 'USER_PASSWORD_AUTH',
|
|
1138
|
+
ClientId: this.fetchClientId(),
|
|
1139
|
+
AuthParameters: {
|
|
1140
|
+
USERNAME: email,
|
|
1141
|
+
PASSWORD: tempPassword,
|
|
1142
|
+
},
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
const initResponse = await client.send(initAuth);
|
|
1146
|
+
|
|
1147
|
+
if (initResponse.ChallengeName === 'NEW_PASSWORD_REQUIRED') {
|
|
1148
|
+
const respond = new RespondToAuthChallengeCommand({
|
|
1149
|
+
ClientId: this.fetchClientId(),
|
|
1150
|
+
ChallengeName: 'NEW_PASSWORD_REQUIRED',
|
|
1151
|
+
Session: initResponse.Session,
|
|
1152
|
+
ChallengeResponses: {
|
|
1153
|
+
USERNAME: email,
|
|
1154
|
+
NEW_PASSWORD: newPassword,
|
|
1155
|
+
},
|
|
1156
|
+
});
|
|
1157
|
+
|
|
1158
|
+
await client.send(respond);
|
|
1159
|
+
logger.info(`User invitation accepted successfully for: ${email}`);
|
|
1160
|
+
} else {
|
|
1161
|
+
throw new Error(`Unexpected challenge: ${initResponse.ChallengeName}`);
|
|
1162
|
+
}
|
|
1163
|
+
} catch (err: any) {
|
|
1164
|
+
logger.error(`Accept invitation failed for ${email}: ${sanitizeErrorMessage(err.message)}`);
|
|
1165
|
+
handleCognitoError(err, 'acceptInvitation');
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1016
1168
|
}
|
|
@@ -21,6 +21,16 @@ export type SessionInfo = {
|
|
|
21
21
|
export type SignUpCallback = (userInfo: UserInfo) => void;
|
|
22
22
|
export type LoginCallback = (sessionInfo: SessionInfo) => void;
|
|
23
23
|
export type LogoutCallback = (status: boolean) => void;
|
|
24
|
+
export type InviteUserCallback = (invitationInfo: InvitationInfo) => void;
|
|
25
|
+
|
|
26
|
+
export type InvitationInfo = {
|
|
27
|
+
email: string;
|
|
28
|
+
firstName: string;
|
|
29
|
+
lastName: string;
|
|
30
|
+
tempPassword?: string;
|
|
31
|
+
invitationId?: string;
|
|
32
|
+
systemInvitationInfo?: any;
|
|
33
|
+
};
|
|
24
34
|
|
|
25
35
|
export interface AgentlangAuth {
|
|
26
36
|
signUp(
|
|
@@ -53,4 +63,18 @@ export interface AgentlangAuth {
|
|
|
53
63
|
env: Environment
|
|
54
64
|
): Promise<boolean>;
|
|
55
65
|
refreshToken(refreshToken: string, env: Environment): Promise<SessionInfo>;
|
|
66
|
+
inviteUser(
|
|
67
|
+
email: string,
|
|
68
|
+
firstName: string,
|
|
69
|
+
lastName: string,
|
|
70
|
+
userData: Map<string, any> | undefined,
|
|
71
|
+
env: Environment,
|
|
72
|
+
cb: InviteUserCallback
|
|
73
|
+
): Promise<void>;
|
|
74
|
+
acceptInvitation(
|
|
75
|
+
email: string,
|
|
76
|
+
tempPassword: string,
|
|
77
|
+
newPassword: string,
|
|
78
|
+
env: Environment
|
|
79
|
+
): Promise<void>;
|
|
56
80
|
}
|
|
@@ -218,12 +218,18 @@ export async function executeGraph(execGraph: ExecGraph, env: Environment): Prom
|
|
|
218
218
|
case SubGraphType.EVENT:
|
|
219
219
|
await evaluateStatement(node.code as Statement, env);
|
|
220
220
|
break;
|
|
221
|
-
case SubGraphType.IF:
|
|
222
|
-
|
|
221
|
+
case SubGraphType.IF: {
|
|
222
|
+
const newEnv = new Environment(`${env.name}-if`, env);
|
|
223
|
+
await executeIfSubGraph(subg, newEnv);
|
|
224
|
+
env.setLastResult(newEnv.getLastResult());
|
|
223
225
|
break;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
+
}
|
|
227
|
+
case SubGraphType.FOR_EACH: {
|
|
228
|
+
const newEnv = new Environment(`${env.name}-forEach`, env);
|
|
229
|
+
await executeForEachSubGraph(subg, node, newEnv);
|
|
230
|
+
env.setLastResult(newEnv.getLastResult());
|
|
226
231
|
break;
|
|
232
|
+
}
|
|
227
233
|
case SubGraphType.DELETE:
|
|
228
234
|
await executeDeleteSubGraph(subg, node, env);
|
|
229
235
|
break;
|
|
@@ -140,6 +140,7 @@ export class Environment extends Instance {
|
|
|
140
140
|
private activeCatchHandlers: Array<CatchHandlers>;
|
|
141
141
|
private eventExecutor: Function | undefined = undefined;
|
|
142
142
|
private statementsExecutor: Function | undefined = undefined;
|
|
143
|
+
private scratchPad: any = undefined;
|
|
143
144
|
|
|
144
145
|
private activeUserData: any = undefined;
|
|
145
146
|
|
|
@@ -270,6 +271,28 @@ export class Environment extends Instance {
|
|
|
270
271
|
return this.attributes.get(Environment.FlowContextTag);
|
|
271
272
|
}
|
|
272
273
|
|
|
274
|
+
addToScratchPad(k: string, data: any): Environment {
|
|
275
|
+
if (this.scratchPad == undefined) {
|
|
276
|
+
this.scratchPad = {};
|
|
277
|
+
}
|
|
278
|
+
this.scratchPad[k] = data;
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
getScratchPad(): any {
|
|
283
|
+
return this.scratchPad;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
resetScratchPad(): Environment {
|
|
287
|
+
this.scratchPad = undefined;
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
setScratchPad(obj: any): Environment {
|
|
292
|
+
this.scratchPad = obj;
|
|
293
|
+
return this;
|
|
294
|
+
}
|
|
295
|
+
|
|
273
296
|
static SuspensionUserData = '^';
|
|
274
297
|
|
|
275
298
|
bindSuspensionUserData(userData: string): Environment {
|
|
@@ -1365,10 +1388,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
|
|
|
1365
1388
|
else if (isDocEventInstance(inst)) await handleDocEvent(inst, env);
|
|
1366
1389
|
else {
|
|
1367
1390
|
const eventExec = env.getEventExecutor();
|
|
1391
|
+
const newEnv = new Environment(`${inst.name}.env`, env);
|
|
1368
1392
|
if (eventExec) {
|
|
1369
|
-
await eventExec(inst,
|
|
1393
|
+
await eventExec(inst, newEnv);
|
|
1394
|
+
env.setLastResult(newEnv.getLastResult());
|
|
1370
1395
|
} else {
|
|
1371
|
-
await evaluate(inst, (result: Result) => env.setLastResult(result),
|
|
1396
|
+
await evaluate(inst, (result: Result) => env.setLastResult(result), newEnv);
|
|
1372
1397
|
}
|
|
1373
1398
|
env.resetReturnFlag();
|
|
1374
1399
|
}
|
|
@@ -1480,31 +1505,7 @@ async function evaluateJoinQuery(
|
|
|
1480
1505
|
}
|
|
1481
1506
|
const resolver = await getResolverForPath(inst.name, moduleName, env);
|
|
1482
1507
|
const result: Result = await resolver.queryByJoin(inst, joinsSpec, normIntoSpec, distinct);
|
|
1483
|
-
|
|
1484
|
-
const transformedResult = transformDateFieldsInJoinResult(result);
|
|
1485
|
-
|
|
1486
|
-
env.setLastResult(transformedResult);
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
function transformDateFieldsInJoinResult(result: any): any {
|
|
1490
|
-
if (!result || !Array.isArray(result)) {
|
|
1491
|
-
return result;
|
|
1492
|
-
}
|
|
1493
|
-
return result.map((row: any) => {
|
|
1494
|
-
if (typeof row !== 'object' || row === null) {
|
|
1495
|
-
return row;
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
for (const [key, value] of Object.entries(row)) {
|
|
1499
|
-
if (value && value instanceof Date) {
|
|
1500
|
-
if (value instanceof Date) {
|
|
1501
|
-
row[key] = value.toLocaleDateString('en-CA');
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
return row;
|
|
1507
|
-
});
|
|
1508
|
+
env.setLastResult(result);
|
|
1508
1509
|
}
|
|
1509
1510
|
|
|
1510
1511
|
async function walkJoinQueryPattern(
|
|
@@ -1539,6 +1540,11 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
|
|
|
1539
1540
|
const flowContext = env.getFlowContext();
|
|
1540
1541
|
msg = flowContext ? `context: ${flowContext}\n${msg}` : msg;
|
|
1541
1542
|
|
|
1543
|
+
const scratchPad = env.getScratchPad();
|
|
1544
|
+
if (scratchPad) {
|
|
1545
|
+
const spad = JSON.stringify(scratchPad);
|
|
1546
|
+
msg = `${msg}\nScratchpad:\n${spad}`;
|
|
1547
|
+
}
|
|
1542
1548
|
// log invocation details
|
|
1543
1549
|
let invokeDebugMsg = `\nInvoking agent ${agent.name}:`;
|
|
1544
1550
|
if (agent.role) {
|
|
@@ -1589,6 +1595,7 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
|
|
|
1589
1595
|
env.setLastResult(await parseAndEvaluateStatement(rs, undefined, env));
|
|
1590
1596
|
}
|
|
1591
1597
|
}
|
|
1598
|
+
agent.maybeAddScratchData(env);
|
|
1592
1599
|
break;
|
|
1593
1600
|
} catch (err: any) {
|
|
1594
1601
|
if (retries < MAX_PLANNER_RETRIES) {
|
|
@@ -1604,6 +1611,30 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
|
|
|
1604
1611
|
}
|
|
1605
1612
|
}
|
|
1606
1613
|
}
|
|
1614
|
+
} else {
|
|
1615
|
+
let retries = 0;
|
|
1616
|
+
while (true) {
|
|
1617
|
+
try {
|
|
1618
|
+
const obj = agent.maybeValidateJsonResponse(result);
|
|
1619
|
+
if (obj != undefined) {
|
|
1620
|
+
env.setLastResult(obj);
|
|
1621
|
+
env.addToScratchPad(agent.getFqName(), obj);
|
|
1622
|
+
}
|
|
1623
|
+
break;
|
|
1624
|
+
} catch (err: any) {
|
|
1625
|
+
if (retries < MAX_PLANNER_RETRIES) {
|
|
1626
|
+
await agent.invoke(`Please fix these errors:\n ${err}`, env);
|
|
1627
|
+
const r: string | undefined = env.getLastResult();
|
|
1628
|
+
result = r;
|
|
1629
|
+
++retries;
|
|
1630
|
+
} else {
|
|
1631
|
+
logger.error(
|
|
1632
|
+
`Failed to validate JSON response generated by agent ${agent.name} - ${result}, ${err}`
|
|
1633
|
+
);
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1607
1638
|
}
|
|
1608
1639
|
} else {
|
|
1609
1640
|
throw new Error(`Agent ${agent.name} failed to generate a response`);
|
|
@@ -1635,6 +1666,7 @@ async function handleAgentInvocationWithFlow(
|
|
|
1635
1666
|
): Promise<void> {
|
|
1636
1667
|
rootAgent.markAsFlowExecutor();
|
|
1637
1668
|
await iterateOnFlow(flow, rootAgent, msg, env);
|
|
1669
|
+
env.resetScratchPad();
|
|
1638
1670
|
}
|
|
1639
1671
|
|
|
1640
1672
|
async function saveFlowSuspension(
|
|
@@ -1643,9 +1675,10 @@ async function saveFlowSuspension(
|
|
|
1643
1675
|
step: FlowStep,
|
|
1644
1676
|
env: Environment
|
|
1645
1677
|
): Promise<void> {
|
|
1678
|
+
const spad = env.getScratchPad() || {};
|
|
1646
1679
|
const suspId = await createSuspension(
|
|
1647
1680
|
env.getSuspensionId(),
|
|
1648
|
-
[FlowSuspensionTag, agent.name, step, context],
|
|
1681
|
+
[FlowSuspensionTag, agent.name, step, context, JSON.stringify(spad)],
|
|
1649
1682
|
env
|
|
1650
1683
|
);
|
|
1651
1684
|
env.setLastResult({ suspension: suspId || 'null' });
|
|
@@ -1656,11 +1689,12 @@ export async function restartFlow(
|
|
|
1656
1689
|
userData: string,
|
|
1657
1690
|
env: Environment
|
|
1658
1691
|
): Promise<void> {
|
|
1659
|
-
const [_, agentName, step, ctx] = flowContext;
|
|
1692
|
+
const [_, agentName, step, ctx, spad] = flowContext;
|
|
1660
1693
|
const rootAgent: AgentInstance = await findAgentByName(agentName, env);
|
|
1661
1694
|
const flow = getAgentFlow(agentName, rootAgent.moduleName);
|
|
1662
1695
|
if (flow) {
|
|
1663
1696
|
const newCtx = `${ctx}\n${step} --> ${userData}\n`;
|
|
1697
|
+
env.setScratchPad(JSON.parse(spad));
|
|
1664
1698
|
await iterateOnFlow(flow, rootAgent, newCtx, env);
|
|
1665
1699
|
}
|
|
1666
1700
|
}
|
|
@@ -1674,7 +1708,7 @@ async function iterateOnFlow(
|
|
|
1674
1708
|
env: Environment
|
|
1675
1709
|
): Promise<void> {
|
|
1676
1710
|
rootAgent.disableSession();
|
|
1677
|
-
const s = `Now consider the following flowchart and context:\n${flow}\n\
|
|
1711
|
+
const s = `Now consider the following flowchart and context:\n${flow}\n\nInitial context: ${msg}\n\n
|
|
1678
1712
|
If you understand from the context that a step with no further possible steps has been evaluated,
|
|
1679
1713
|
terminate the flowchart by returning DONE. Never return to the top or root step of the flowchart, instead return DONE.\n`;
|
|
1680
1714
|
await agentInvoke(rootAgent, s, env);
|
|
@@ -1710,7 +1744,7 @@ async function iterateOnFlow(
|
|
|
1710
1744
|
console.debug(
|
|
1711
1745
|
`\n----> Completed execution of step ${step}, iteration id ${iterId} with result:\n${rs}`
|
|
1712
1746
|
);
|
|
1713
|
-
context = `${context}\nExecuted steps: ${[...executedSteps].join(', ')}
|
|
1747
|
+
context = `${context}\nExecuted steps: ${[...executedSteps].join(', ')}`;
|
|
1714
1748
|
await agentInvoke(rootAgent, `${s}\n${context}`, env);
|
|
1715
1749
|
step = env.getLastResult().trim();
|
|
1716
1750
|
}
|
package/src/runtime/loader.ts
CHANGED
|
@@ -78,7 +78,9 @@ import {
|
|
|
78
78
|
LlmEntityName,
|
|
79
79
|
registerAgentDirectives,
|
|
80
80
|
registerAgentGlossary,
|
|
81
|
+
registerAgentResponseSchema,
|
|
81
82
|
registerAgentScenarios,
|
|
83
|
+
registerAgentScratchNames,
|
|
82
84
|
} from './modules/ai.js';
|
|
83
85
|
import { getDefaultLLMService } from './agents/registry.js';
|
|
84
86
|
import { GenericResolver, GenericResolverMethods } from './resolvers/interface.js';
|
|
@@ -566,6 +568,20 @@ function processAgentGlossary(agentName: string, value: Literal): AgentGlossaryE
|
|
|
566
568
|
return undefined;
|
|
567
569
|
}
|
|
568
570
|
|
|
571
|
+
function processAgentScratchNames(agentName: string, value: Literal): string[] | undefined {
|
|
572
|
+
if (value.array) {
|
|
573
|
+
const scratch = new Array<string>();
|
|
574
|
+
value.array.vals.forEach((stmt: Statement) => {
|
|
575
|
+
const expr = stmt.pattern.expr;
|
|
576
|
+
if (expr && isLiteral(expr) && (expr.id || expr.str)) {
|
|
577
|
+
scratch.push(expr.id || expr.str || '');
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
return scratch;
|
|
581
|
+
}
|
|
582
|
+
return undefined;
|
|
583
|
+
}
|
|
584
|
+
|
|
569
585
|
async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
|
|
570
586
|
let llmName: string | undefined = undefined;
|
|
571
587
|
const name = def.name;
|
|
@@ -577,6 +593,8 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
|
|
|
577
593
|
let conds: AgentCondition[] | undefined = undefined;
|
|
578
594
|
let scenarios: AgentScenario[] | undefined = undefined;
|
|
579
595
|
let glossary: AgentGlossaryEntry[] | undefined = undefined;
|
|
596
|
+
let responseSchema: string | undefined = undefined;
|
|
597
|
+
let scratchNames: string[] | undefined = undefined;
|
|
580
598
|
def.body?.attributes.forEach((apdef: GenericPropertyDef) => {
|
|
581
599
|
if (apdef.name == 'flows') {
|
|
582
600
|
let fnames: string | undefined = undefined;
|
|
@@ -604,6 +622,19 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
|
|
|
604
622
|
scenarios = processAgentScenarios(name, apdef.value);
|
|
605
623
|
} else if (apdef.name == 'glossary') {
|
|
606
624
|
glossary = processAgentGlossary(name, apdef.value);
|
|
625
|
+
} else if (apdef.name == 'responseSchema') {
|
|
626
|
+
const s = apdef.value.id || apdef.value.str || apdef.value.id;
|
|
627
|
+
if (s) {
|
|
628
|
+
if (isFqName(s)) {
|
|
629
|
+
responseSchema = s;
|
|
630
|
+
} else {
|
|
631
|
+
responseSchema = makeFqName(moduleName, s);
|
|
632
|
+
}
|
|
633
|
+
} else {
|
|
634
|
+
throw new Error(`responseSchema must be a valid name in agent ${name}`);
|
|
635
|
+
}
|
|
636
|
+
} else if (apdef.name == 'scratch') {
|
|
637
|
+
scratchNames = processAgentScratchNames(name, apdef.value);
|
|
607
638
|
} else {
|
|
608
639
|
let v: any = undefined;
|
|
609
640
|
if (apdef.value.array) {
|
|
@@ -666,6 +697,12 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
|
|
|
666
697
|
if (glossary) {
|
|
667
698
|
registerAgentGlossary(moduleName, name, glossary);
|
|
668
699
|
}
|
|
700
|
+
if (responseSchema) {
|
|
701
|
+
registerAgentResponseSchema(moduleName, name, responseSchema);
|
|
702
|
+
}
|
|
703
|
+
if (scratchNames) {
|
|
704
|
+
registerAgentScratchNames(moduleName, name, scratchNames);
|
|
705
|
+
}
|
|
669
706
|
// Don't add llm to module attrs if it wasn't originally specified
|
|
670
707
|
addAgent(def.name, attrs, moduleName);
|
|
671
708
|
}
|
package/src/runtime/module.ts
CHANGED
|
@@ -3139,3 +3139,23 @@ export function getEntityRbacRules(entityFqName: string): RbacSpecification[] |
|
|
|
3139
3139
|
}
|
|
3140
3140
|
return undefined;
|
|
3141
3141
|
}
|
|
3142
|
+
|
|
3143
|
+
export function asJSONSchema(fqName: string): string {
|
|
3144
|
+
const parts = splitFqName(fqName);
|
|
3145
|
+
if (parts.hasModule()) {
|
|
3146
|
+
const mod = fetchModule(parts.getModuleName());
|
|
3147
|
+
const record = mod.getRecord(parts.getEntryName());
|
|
3148
|
+
const s = new Array<string>();
|
|
3149
|
+
record.schema.forEach((attr: AttributeSpec, n: string) => {
|
|
3150
|
+
let t = attr.type;
|
|
3151
|
+
const enums = getEnumValues(attr);
|
|
3152
|
+
if (enums) {
|
|
3153
|
+
t = `one of the strings: ${[...enums].join(',')}`;
|
|
3154
|
+
}
|
|
3155
|
+
s.push(`"${n}": <${t}>`);
|
|
3156
|
+
});
|
|
3157
|
+
return `{${s.join(',\n')}}`;
|
|
3158
|
+
} else {
|
|
3159
|
+
throw new Error(`Failed to find module for ${fqName}`);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
parseAndEvaluateStatement,
|
|
7
7
|
} from '../interpreter.js';
|
|
8
8
|
import {
|
|
9
|
+
asJSONSchema,
|
|
9
10
|
fetchModule,
|
|
10
11
|
Instance,
|
|
11
12
|
instanceToObject,
|
|
@@ -129,6 +130,26 @@ export function registerAgentGlossary(
|
|
|
129
130
|
AgentGlossary.set(makeFqName(moduleName, agentName), glossary);
|
|
130
131
|
}
|
|
131
132
|
|
|
133
|
+
const AgentResponseSchema = new Map<string, string>();
|
|
134
|
+
|
|
135
|
+
export function registerAgentResponseSchema(
|
|
136
|
+
moduleName: string,
|
|
137
|
+
agentName: string,
|
|
138
|
+
responseSchema: string
|
|
139
|
+
) {
|
|
140
|
+
AgentResponseSchema.set(makeFqName(moduleName, agentName), responseSchema);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const AgentScratchNames = new Map<string, Set<string>>();
|
|
144
|
+
|
|
145
|
+
export function registerAgentScratchNames(
|
|
146
|
+
moduleName: string,
|
|
147
|
+
agentName: string,
|
|
148
|
+
scratch: string[]
|
|
149
|
+
) {
|
|
150
|
+
AgentScratchNames.set(makeFqName(moduleName, agentName), new Set(scratch));
|
|
151
|
+
}
|
|
152
|
+
|
|
132
153
|
export class AgentInstance {
|
|
133
154
|
llm: string = '';
|
|
134
155
|
name: string = '';
|
|
@@ -145,6 +166,7 @@ export class AgentInstance {
|
|
|
145
166
|
private toolsArray: string[] | undefined = undefined;
|
|
146
167
|
private hasModuleTools = false;
|
|
147
168
|
private withSession = true;
|
|
169
|
+
private fqName: string | undefined;
|
|
148
170
|
|
|
149
171
|
private constructor() {}
|
|
150
172
|
|
|
@@ -241,7 +263,7 @@ export class AgentInstance {
|
|
|
241
263
|
if (this.cachedInstruction) {
|
|
242
264
|
return this.cachedInstruction;
|
|
243
265
|
}
|
|
244
|
-
const fqName =
|
|
266
|
+
const fqName = this.getFqName();
|
|
245
267
|
this.cachedInstruction = `${this.instruction || ''} ${this.directivesAsString(fqName)}`;
|
|
246
268
|
const gls = AgentGlossary.get(fqName);
|
|
247
269
|
if (gls) {
|
|
@@ -262,14 +284,80 @@ export class AgentInstance {
|
|
|
262
284
|
});
|
|
263
285
|
this.cachedInstruction = `${this.cachedInstruction}\nHere are some example user requests and the corresponding responses you are supposed to produce:\n${scs.join('\n')}`;
|
|
264
286
|
}
|
|
287
|
+
const responseSchema = AgentResponseSchema.get(fqName);
|
|
288
|
+
if (responseSchema) {
|
|
289
|
+
this.cachedInstruction = `${this.cachedInstruction}\nReturn your response in the following JSON schema:\n${asJSONSchema(responseSchema)}
|
|
290
|
+
Only return a pure JSON object with no extra text, annotations etc.`;
|
|
291
|
+
}
|
|
265
292
|
return this.cachedInstruction;
|
|
266
293
|
}
|
|
267
294
|
|
|
295
|
+
maybeValidateJsonResponse(response: string | undefined): object | undefined {
|
|
296
|
+
if (response) {
|
|
297
|
+
const responseSchema = AgentResponseSchema.get(this.getFqName());
|
|
298
|
+
if (responseSchema) {
|
|
299
|
+
const attrs = JSON.parse(response);
|
|
300
|
+
const parts = splitFqName(responseSchema);
|
|
301
|
+
const moduleName = parts.getModuleName();
|
|
302
|
+
const entryName = parts.getEntryName();
|
|
303
|
+
const attrsMap = new Map(Object.entries(attrs));
|
|
304
|
+
const scm = fetchModule(moduleName).getRecord(entryName).schema;
|
|
305
|
+
const recAttrs = new Map<string, any>();
|
|
306
|
+
attrsMap.forEach((v: any, k: string) => {
|
|
307
|
+
if (scm.has(k)) {
|
|
308
|
+
recAttrs.set(k, v);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
makeInstance(moduleName, entryName, recAttrs);
|
|
312
|
+
return attrs;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
getFqName(): string {
|
|
319
|
+
if (this.fqName == undefined) {
|
|
320
|
+
this.fqName = makeFqName(this.moduleName, this.name);
|
|
321
|
+
}
|
|
322
|
+
return this.fqName;
|
|
323
|
+
}
|
|
324
|
+
|
|
268
325
|
markAsFlowExecutor(): AgentInstance {
|
|
269
326
|
this.type = 'flow-exec';
|
|
270
327
|
return this;
|
|
271
328
|
}
|
|
272
329
|
|
|
330
|
+
getScratchNames(): Set<string> | undefined {
|
|
331
|
+
return AgentScratchNames.get(this.getFqName());
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
maybeAddScratchData(env: Environment): AgentInstance {
|
|
335
|
+
const obj: any = env.getLastResult();
|
|
336
|
+
let r: Instance | Instance[] | undefined = undefined;
|
|
337
|
+
if (
|
|
338
|
+
obj instanceof Instance ||
|
|
339
|
+
(obj instanceof Array && obj.length > 0 && obj[0] instanceof Instance)
|
|
340
|
+
) {
|
|
341
|
+
r = obj;
|
|
342
|
+
} else {
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
const scratchNames = this.getScratchNames();
|
|
346
|
+
let data: any = undefined;
|
|
347
|
+
let n = '';
|
|
348
|
+
if (r instanceof Array) {
|
|
349
|
+
data = r.map((inst: Instance) => {
|
|
350
|
+
return extractScratchData(scratchNames, inst);
|
|
351
|
+
});
|
|
352
|
+
n = r[0].getFqName();
|
|
353
|
+
} else {
|
|
354
|
+
data = extractScratchData(scratchNames, r);
|
|
355
|
+
n = r.getFqName();
|
|
356
|
+
}
|
|
357
|
+
if (data) env.addToScratchPad(n, data);
|
|
358
|
+
return this;
|
|
359
|
+
}
|
|
360
|
+
|
|
273
361
|
async invoke(message: string, env: Environment) {
|
|
274
362
|
const p = await findProviderForLLM(this.llm, env);
|
|
275
363
|
const agentName = this.name;
|
|
@@ -411,6 +499,20 @@ export class AgentInstance {
|
|
|
411
499
|
}
|
|
412
500
|
}
|
|
413
501
|
|
|
502
|
+
function extractScratchData(scratchNames: Set<string> | undefined, inst: Instance): any {
|
|
503
|
+
const data: any = {};
|
|
504
|
+
inst.attributes.forEach((v: any, k: string) => {
|
|
505
|
+
if (scratchNames) {
|
|
506
|
+
if (scratchNames.has(k)) {
|
|
507
|
+
data[k] = v;
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
data[k] = v;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
return data;
|
|
514
|
+
}
|
|
515
|
+
|
|
414
516
|
async function parseHelper(stmt: string, env: Environment): Promise<any> {
|
|
415
517
|
await parseAndEvaluateStatement(stmt, undefined, env);
|
|
416
518
|
return env.getLastResult();
|
|
@@ -332,6 +332,15 @@ workflow getUser {
|
|
|
332
332
|
workflow getUserByEmail {
|
|
333
333
|
await Auth.getUserInfoByEmail(getUserByEmail.email)
|
|
334
334
|
}
|
|
335
|
+
|
|
336
|
+
workflow inviteUser {
|
|
337
|
+
await Auth.inviteUser(inviteUser.email, inviteUser.firstName, inviteUser.lastName, inviteUser.userData)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
workflow acceptInvitation {
|
|
342
|
+
await Auth.acceptInvitationUser(acceptInvitation.email, acceptInvitation.tempPassword, acceptInvitation.newPassword)
|
|
343
|
+
}
|
|
335
344
|
`;
|
|
336
345
|
|
|
337
346
|
const evalEvent = makeEventEvaluator(CoreAuthModuleName);
|
|
@@ -1113,6 +1122,68 @@ export async function refreshUserToken(refreshToken: string, env: Environment):
|
|
|
1113
1122
|
}
|
|
1114
1123
|
}
|
|
1115
1124
|
|
|
1125
|
+
export async function inviteUser(
|
|
1126
|
+
email: string,
|
|
1127
|
+
firstName: string,
|
|
1128
|
+
lastName: string,
|
|
1129
|
+
userData: Map<string, any> | undefined,
|
|
1130
|
+
env: Environment
|
|
1131
|
+
): Promise<object> {
|
|
1132
|
+
const needCommit = env ? false : true;
|
|
1133
|
+
env = env ? env : new Environment();
|
|
1134
|
+
const f = async () => {
|
|
1135
|
+
try {
|
|
1136
|
+
let invitationInfo: any;
|
|
1137
|
+
await fetchAuthImpl().inviteUser(email, firstName, lastName, userData, env, (info: any) => {
|
|
1138
|
+
invitationInfo = info;
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
return {
|
|
1142
|
+
email: invitationInfo.email,
|
|
1143
|
+
firstName: invitationInfo.firstName,
|
|
1144
|
+
lastName: invitationInfo.lastName,
|
|
1145
|
+
invitationId: invitationInfo.invitationId,
|
|
1146
|
+
message: 'User invitation sent successfully',
|
|
1147
|
+
};
|
|
1148
|
+
} catch (err: any) {
|
|
1149
|
+
logger.error(`User invitation failed: ${err.message}`);
|
|
1150
|
+
throw err;
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
if (needCommit) {
|
|
1154
|
+
return await env.callInTransaction(f);
|
|
1155
|
+
} else {
|
|
1156
|
+
return await f();
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
export async function acceptInvitationUser(
|
|
1161
|
+
email: string,
|
|
1162
|
+
tempPassword: string,
|
|
1163
|
+
newPassword: string,
|
|
1164
|
+
env: Environment
|
|
1165
|
+
): Promise<object> {
|
|
1166
|
+
const needCommit = env ? false : true;
|
|
1167
|
+
env = env ? env : new Environment();
|
|
1168
|
+
const f = async () => {
|
|
1169
|
+
try {
|
|
1170
|
+
await fetchAuthImpl().acceptInvitation(email, tempPassword, newPassword, env);
|
|
1171
|
+
return {
|
|
1172
|
+
email: email,
|
|
1173
|
+
message: 'Invitation accepted successfully',
|
|
1174
|
+
};
|
|
1175
|
+
} catch (err: any) {
|
|
1176
|
+
logger.error(`Accept invitation failed: ${err.message}`);
|
|
1177
|
+
throw err;
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
if (needCommit) {
|
|
1181
|
+
return await env.callInTransaction(f);
|
|
1182
|
+
} else {
|
|
1183
|
+
return await f();
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1116
1187
|
export function requireAuth(moduleName: string, eventName: string): boolean {
|
|
1117
1188
|
if (isAuthEnabled()) {
|
|
1118
1189
|
const f =
|
|
@@ -1123,7 +1194,8 @@ export function requireAuth(moduleName: string, eventName: string): boolean {
|
|
|
1123
1194
|
eventName == 'resendConfirmationCode' ||
|
|
1124
1195
|
eventName == 'forgotPassword' ||
|
|
1125
1196
|
eventName == 'confirmForgotPassword' ||
|
|
1126
|
-
eventName == 'refreshToken'
|
|
1197
|
+
eventName == 'refreshToken' ||
|
|
1198
|
+
eventName == 'acceptInvitation');
|
|
1127
1199
|
return !f;
|
|
1128
1200
|
} else {
|
|
1129
1201
|
return false;
|