agentlang
Version:
The easiest way to build the most reliable AI agents - enterprise-grade teams of AI agents that collaborate with each other and humans
1,022 lines • 40.1 kB
JavaScript
import chalk from 'chalk';
import { createAgentlangServices } from '../language/agentlang-module.js';
import { isEntityDefinition, isEventDefinition, isRecordDefinition, isRelationshipDefinition, isWorkflowDefinition, isStandaloneStatement, isAgentDefinition, isResolverDefinition, isLiteral, isFlowDefinition, isDecisionDefinition, isScenarioDefinition, isDirectiveDefinition, isGlossaryEntryDefinition, isPublicWorkflowDefinition, isPublicAgentDefinition, isPublicEventDefinition, isRetryDefinition, } from '../language/generated/ast.js';
import { addEntity, addEvent, addModule, addRecord, addRelationship, addWorkflow, RbacSpecification, isModule, getUserModuleNames, removeModule, newInstanceAttributes, addAgent, fetchModule, Retry, } from './module.js';
import { asStringLiteralsMap, escapeSpecialChars, findRbacSchema, isFqName, makeFqName, maybeExtends, preprocessRawConfig, registerInitFunction, rootRef, } from './util.js';
import { getFileSystem, toFsPath, readFile, readdir, exists } from '../utils/fs-utils.js';
import { URI } from 'vscode-uri';
import { isNodeEnv, path } from '../utils/runtime.js';
import { CoreModules, registerCoreModules } from './modules/core.js';
import { canParse, introspectIf, maybeGetValidationErrors, maybeRaiseParserErrors, parse, parseModule, parseStatement, parseWorkflow, } from '../language/parser.js';
import { logger } from './logger.js';
import { Environment, evaluateStatements, GlobalEnvironment } from './interpreter.js';
import { createPermission, createRole } from './modules/auth.js';
import { AgentEntityName, CoreAIModuleName, LlmEntityName } from './modules/ai.js';
import { getDefaultLLMService } from './agents/registry.js';
import { GenericResolver } from './resolvers/interface.js';
import { registerResolver, setResolver } from './resolvers/registry.js';
import { ConfigSchema, setAppConfig } from './state.js';
import { getModuleFn, importModule } from './jsmodules.js';
import { SetSubscription } from './defs.js';
import z from 'zod';
import { registerAgentFlow, registerFlow } from './agents/flows.js';
import { addAgentDirective, addAgentGlossaryEntry, addAgentScenario, registerAgentDirectives, registerAgentGlossary, registerAgentResponseSchema, registerAgentScenarios, registerAgentScratchNames, } from './agents/common.js';
export async function extractDocument(fileName, services) {
const extensions = services.LanguageMetaData.fileExtensions;
if (isNodeEnv && typeof fileName === 'string') {
if (!extensions.includes(path.extname(fileName))) {
console.error(chalk.yellow(`Please choose a file with one of these extensions: ${extensions}.`));
process.exit(1);
}
const fullFilePath = path.resolve(fileName);
const fileExists = await exists(fullFilePath);
if (!fileExists) {
const errorMsg = `File ${fileName} does not exist.`;
throw new Error(errorMsg);
}
}
else if (!isNodeEnv && typeof fileName === 'string') {
const fullFilePath = path.resolve(fileName);
const fileExists = await exists(fullFilePath);
if (!fileExists) {
throw new Error(`File ${fileName} does not exist.`);
}
}
else {
throw new Error('Invalid input: expected file path (Node.js) or File object/content (browser)');
}
const document = await services.shared.workspace.LangiumDocuments.getOrCreateDocument(URI.file(path.resolve(fileName)));
// Build document
await services.shared.workspace.DocumentBuilder.build([document], {
validation: true,
});
// Handle validation errors
const errs = maybeGetValidationErrors(document);
if (errs) {
const errorMsg = `${errs.join('\n')}`;
throw new Error(errorMsg);
}
return document;
}
export async function extractAstNode(fileName, services) {
var _a;
return (_a = (await extractDocument(fileName, services)).parseResult) === null || _a === void 0 ? void 0 : _a.value;
}
export const DefaultAppSpec = {
name: 'agentlang-app',
version: '0.0.1',
};
let CurrentAppSpec = DefaultAppSpec;
export function getAppSpec() {
return CurrentAppSpec;
}
async function getAllModules(dir, fs, drill = true) {
let alFiles = new Array();
if (!(await fs.exists(dir))) {
return alFiles;
}
const directoryContents = await fs.readdir(dir);
for (let i = 0; i < directoryContents.length; ++i) {
const file = directoryContents[i];
if (path.extname(file).toLowerCase() == '.al') {
alFiles.push(dir + path.sep + file);
}
else if (drill) {
const fullPath = dir + path.sep + file;
const stat = await fs.stat(fullPath);
if (stat.isDirectory()) {
alFiles = alFiles.concat(await getAllModules(fullPath, fs));
}
}
}
return alFiles;
}
let dependenciesCallback = undefined;
export function setDependenciesCallback(cb) {
dependenciesCallback = cb;
}
async function loadApp(appDir, fsOptions, callback) {
// Initialize filesystem if not already done
const fs = await getFileSystem(fsOptions);
const appJsonFile = `${appDir}${path.sep}package.json`;
const s = await fs.readFile(appJsonFile);
const appSpec = JSON.parse(s);
CurrentAppSpec = appSpec;
if (dependenciesCallback !== undefined && appSpec.dependencies) {
const aldeps = new Array();
for (const [k, v] of Object.entries(appSpec.dependencies)) {
if (typeof v === 'string' && v.startsWith('git+http')) {
aldeps.push({
appName: k,
url: v,
});
}
}
if (aldeps.length > 0) {
await dependenciesCallback(aldeps);
}
}
let lastModuleLoaded = '';
async function cont2() {
const fls01 = await getAllModules(appDir, fs, false);
const fls02 = await getAllModules(appDir + path.sep + 'src', fs);
const alFiles0 = fls01.concat(fls02);
const configFile = `${appDir}${path.sep}config.al`;
const alFiles = alFiles0.filter((s) => {
return s != configFile;
});
for (let i = 0; i < alFiles.length; ++i) {
lastModuleLoaded = (await loadModule(alFiles[i], fsOptions)).name;
}
if (callback)
await callback(appSpec);
}
if (appSpec.dependencies !== undefined) {
for (const [depName, _] of Object.entries(appSpec.dependencies)) {
try {
// In browser (with virtual filesystem), use absolute path relative to appDir
// In Node.js, use relative path from current working directory
const isBrowser = fsOptions && fsOptions.name;
const depDirName = isBrowser
? `${appDir}${path.sep}node_modules${path.sep}${depName}`
: `./node_modules/${depName}`;
const fls01 = await fs.readdir(depDirName);
const srcDir = depDirName + path.sep + 'src';
const hasSrc = await fs.exists(srcDir);
const files = hasSrc ? fls01.concat(await fs.readdir(srcDir)) : fls01;
if (files.find(file => {
return path.extname(file).toLowerCase() == '.al';
})) {
await loadApp(depDirName, fsOptions);
}
}
catch (error) {
logger.error(`Error loading dependency ${depName}: ${error}`);
}
}
}
await cont2();
return appSpec.name || lastModuleLoaded;
}
/**
* Load a module from a file
* @param fileName Path to the file containing the module
* @param fsOptions Optional configuration for the filesystem
* @param callback Function to be called after loading the module
* @returns Promise that resolves when the module is loaded
*/
export async function load(fileName, fsOptions, callback) {
let result = '';
if (path.basename(fileName).endsWith('.al')) {
result = (await loadModule(fileName, fsOptions, callback)).name;
}
else {
result = await loadApp(fileName, fsOptions, callback);
}
return { name: result, version: '0.0.1' };
}
export function flushAllModules() {
getUserModuleNames().forEach((n) => {
removeModule(n);
});
}
/**
* Removes all existing user-modules and loads the specified module-file.
* @param fileName Path to the file containing the module
* @param fsOptions Optional configuration for the filesystem
* @param callback Function to be called after loading the module
* @returns Promise that resolves when the module is loaded
*/
export async function flushAllAndLoad(fileName, fsOptions, callback) {
flushAllModules();
return await load(fileName, fsOptions, callback);
}
export async function loadAppConfig(configDir) {
let cfgObj = undefined;
const fs = await getFileSystem();
const alCfgFile = `${configDir}${path.sep}config.al`;
if (await fs.exists(alCfgFile)) {
const cfgPats = await fs.readFile(alCfgFile);
if (canParse(cfgPats)) {
const cfgWf = `workflow createConfig{\n${cfgPats}}`;
const wf = await parseWorkflow(cfgWf);
const env = new Environment('config.env');
const cfgStmts = new Array();
const initInsts = new Array();
for (let i = 0; i < wf.statements.length; ++i) {
const stmt = wf.statements[i];
if (stmt.pattern.crudMap) {
initInsts.push(await makeDeleteAllConfigStatement(stmt.pattern.crudMap));
initInsts.push(stmt);
}
else {
cfgStmts.push(stmt);
}
}
if (initInsts.length > 0) {
registerInitFunction(async () => {
const env = new Environment('config.insts.env');
try {
await evaluateStatements(initInsts, env);
await env.commitAllTransactions();
}
catch (reason) {
await env.rollbackAllTransactions();
console.error(`Failed to initialize config instances: ${reason}`);
}
});
}
await evaluateStatements(cfgStmts, env);
cfgObj = env.getLastResult();
}
}
try {
const cfg = cfgObj
? configFromObject(cfgObj)
: await loadRawConfig(`${configDir}${path.sep}app.config.json`);
return setAppConfig(cfg);
}
catch (err) {
if (err instanceof z.ZodError) {
console.log(chalk.red('Config validation failed:'));
err.errors.forEach((error, index) => {
console.log(chalk.red(` ${index + 1}. ${error.path.join('.')}: ${error.message}`));
});
}
else {
console.log(`Config loading failed: ${err}`);
}
throw err;
}
}
async function makeDeleteAllConfigStatement(crudMap) {
const n = crudMap.name;
const p = `purge {${n}? {}}`;
return await parseStatement(p);
}
export async function loadCoreModules() {
if (CoreModules.length == 0) {
registerCoreModules();
}
for (let i = 0; i < CoreModules.length; ++i) {
await internModule(await parseModule(CoreModules[i]));
}
}
async function loadModule(fileName, fsOptions, callback) {
// Initialize filesystem if not already done
console.log(`loading ${fileName}`);
const fs = await getFileSystem(fsOptions);
const fsAdapter = getFsAdapter(fs);
// Create services with our custom filesystem adapter
const services = createAgentlangServices({
fileSystemProvider: _services => fsAdapter,
}).Agentlang;
// Extract the AST node
const module = await extractAstNode(fileName, services);
const result = await internModule(module, fileName);
console.log(chalk.green(`Module ${chalk.bold(result.name)} loaded`));
logger.info(`Module ${result.name} loaded`);
if (callback) {
await callback();
}
return result;
}
let cachedFsAdapter = null;
function getFsAdapter(fs) {
if (cachedFsAdapter === null) {
// Create an adapter to make our filesystem compatible with Langium
cachedFsAdapter = {
// Read file contents as text
readFile: async (uri) => {
return await readFile(uri);
},
// List directory contents with proper metadata
readDirectory: async (uri) => {
const result = await readdir(uri);
const dirPath = toFsPath(uri);
// Convert string[] to FileSystemNode[] as required by Langium
return Promise.all(result.map(async (name) => {
var _a, _b, _c, _d;
const filePath = dirPath.endsWith('/') ? `${dirPath}${name}` : `${dirPath}/${name}`;
const stats = await fs
.stat(filePath)
.catch(() => ({ isFile: () => true, isDirectory: () => false }));
return {
uri: URI.file(filePath),
isFile: (_b = (_a = stats.isFile) === null || _a === void 0 ? void 0 : _a.call(stats)) !== null && _b !== void 0 ? _b : true,
isDirectory: (_d = (_c = stats.isDirectory) === null || _c === void 0 ? void 0 : _c.call(stats)) !== null && _d !== void 0 ? _d : false,
};
}));
},
};
}
return cachedFsAdapter;
}
function setRbacForEntity(entity, rbacSpec) {
const rbac = rbacSpec.specEntries.map((rs) => {
return RbacSpecification.from(rs).setResource(makeFqName(entity.moduleName, entity.name));
});
if (rbac.length > 0) {
const f = async () => {
for (let i = 0; i < rbac.length; ++i) {
await createRolesAndPermissions(rbac[i]);
}
};
registerInitFunction(f);
entity.setRbacSpecifications(rbac);
}
}
async function createRolesAndPermissions(rbacSpec) {
const roles = [...rbacSpec.roles];
const env = new Environment();
async function f() {
for (let i = 0; i < roles.length; ++i) {
const r = roles[i];
await createRole(r, env);
if (rbacSpec.hasPermissions() && rbacSpec.hasResource()) {
await createPermission(`${r}_permission_${rbacSpec.resource}`, r, rbacSpec.resource, rbacSpec.hasCreatePermission(), rbacSpec.hasReadPermission(), rbacSpec.hasUpdatePermission(), rbacSpec.hasDeletePermission(), env);
}
}
}
await env.callInTransaction(f);
}
function addEntityFromDef(def, moduleName) {
const entity = addEntity(def.name, moduleName, def.schema, maybeExtends(def.extends));
const rbacSpec = findRbacSchema(def.schema);
if (rbacSpec) {
setRbacForEntity(entity, rbacSpec);
}
return entity;
}
function addSchemaFromDef(def, moduleName, ispub = false) {
let result;
if (isEntityDefinition(def)) {
result = addEntityFromDef(def, moduleName);
}
else if (isEventDefinition(def)) {
result = addEvent(def.name, moduleName, def.schema, maybeExtends(def.extends));
}
else if (isRecordDefinition(def)) {
result = addRecord(def.name, moduleName, def.schema, maybeExtends(def.extends));
}
else {
throw new Error(`Cannot add schema definition in module ${moduleName} for ${def}`);
}
if (ispub) {
result.setPublic(true);
}
return result;
}
export function addRelationshipFromDef(def, moduleName) {
return addRelationship(def.name, def.type, def.nodes, moduleName, def.schema, def.properties);
}
export function addWorkflowFromDef(def, moduleName, ispub = false) {
return addWorkflow(def.name || '', moduleName, def.statements, def.header, ispub);
}
const StandaloneStatements = new Map();
function addStandaloneStatement(stmt, moduleName, userDefined = true) {
let stmts = StandaloneStatements.get(moduleName);
if (stmts === undefined) {
stmts = new Array();
}
stmts.push(stmt);
if (!StandaloneStatements.has(moduleName)) {
StandaloneStatements.set(moduleName, stmts);
}
if (userDefined) {
const m = fetchModule(moduleName);
m.addStandaloneStatement(stmt);
}
}
export async function runStandaloneStatements() {
if (StandaloneStatements.size > 0) {
await GlobalEnvironment.callInTransaction(async () => {
const ks = [...StandaloneStatements.keys()];
for (let i = 0; i < ks.length; ++i) {
const moduleName = ks[i];
const stmts = StandaloneStatements.get(moduleName);
if (stmts) {
const oldModule = GlobalEnvironment.switchActiveModuleName(moduleName);
await evaluateStatements(stmts, GlobalEnvironment);
GlobalEnvironment.switchActiveModuleName(oldModule);
}
}
logger.debug(`Init eval result: ${GlobalEnvironment.getLastResult()}`);
});
StandaloneStatements.clear();
}
}
function processAgentDirectives(agentName, value) {
if (value.array) {
const conds = new Array();
value.array.vals.forEach((stmt) => {
const expr = stmt.pattern.expr;
if (expr && isLiteral(expr) && expr.map) {
let cond;
let then;
expr.map.entries.forEach((me) => {
const v = isLiteral(me.value) ? me.value.str : undefined;
if (v) {
if (me.key.str == 'if') {
cond = v;
}
else if (me.key.str == 'then') {
then = v;
}
}
});
if (cond && then) {
conds === null || conds === void 0 ? void 0 : conds.push({ if: cond, then, internal: true, ifPattern: undefined });
}
else {
throw new Error(`Invalid condition spec in agent ${agentName}`);
}
}
});
return conds;
}
return undefined;
}
function processAgentScenarios(agentName, value) {
if (value.array) {
const scenarios = new Array();
value.array.vals.forEach((stmt) => {
const expr = stmt.pattern.expr;
if (expr && isLiteral(expr) && expr.map) {
let user;
let ai;
expr.map.entries.forEach((me) => {
var _a;
let v = isLiteral(me.value) ? me.value.str : undefined;
if (v === undefined) {
v = (_a = me.value.$cstNode) === null || _a === void 0 ? void 0 : _a.text;
}
if (v) {
if (me.key.str == 'user') {
user = v;
}
else if (me.key.str == 'ai') {
ai = v;
}
}
});
if (user && ai) {
const internal = true;
scenarios.push({ user, ai, internal, ifPattern: undefined });
}
else {
throw new Error(`Invalid scenario spec in agent ${agentName}`);
}
}
});
return scenarios;
}
return undefined;
}
function processAgentGlossary(agentName, value) {
if (value.array) {
const gls = new Array();
value.array.vals.forEach((stmt) => {
const expr = stmt.pattern.expr;
if (expr && isLiteral(expr) && expr.map) {
let name;
let meaning;
let synonyms;
expr.map.entries.forEach((me) => {
const v = isLiteral(me.value) ? me.value.str : undefined;
if (v) {
if (me.key.str == 'name') {
name = v;
}
else if (me.key.str == 'meaning') {
meaning = v;
}
else if (me.key.str == 'synonyms') {
synonyms = v;
}
}
});
if (name && meaning) {
const internal = true;
gls.push({ name, meaning, synonyms, internal });
}
else {
throw new Error(`Invalid glossary spec in agent ${agentName}`);
}
}
});
return gls;
}
return undefined;
}
function processAgentScratchNames(agentName, value) {
if (value.array) {
const scratch = new Array();
value.array.vals.forEach((stmt) => {
const expr = stmt.pattern.expr;
if (expr && isLiteral(expr) && (expr.id || expr.str)) {
scratch.push(expr.id || expr.str || '');
}
});
return scratch;
}
return undefined;
}
async function addAgentDefinition(def, moduleName, ispub = false) {
var _a;
let llmName = undefined;
const name = def.name;
const attrsStrs = new Array();
attrsStrs.push(`name "${name}"`);
const attrs = newInstanceAttributes();
attrsStrs.push(`moduleName "${moduleName}"`);
attrs.set('moduleName', moduleName);
let conds = undefined;
let scenarios = undefined;
let glossary = undefined;
let responseSchema = undefined;
let scratchNames = undefined;
(_a = def.body) === null || _a === void 0 ? void 0 : _a.attributes.forEach((apdef) => {
if (apdef.name == 'flows') {
let fnames = undefined;
if (apdef.value.array) {
fnames = processAgentArray(apdef.value.array, name);
}
else {
fnames = apdef.value.id || apdef.value.str;
}
if (fnames) {
fnames.split(',').forEach((n) => {
n = n.trim();
const fqn = isFqName(n) ? n : `${moduleName}/${n}`;
registerAgentFlow(name, fqn);
});
attrsStrs.push(`type "flow-exec"`);
attrs.set('type', 'flow-exec');
attrsStrs.push(`flows "${fnames}"`);
attrs.set('flows', fnames);
}
else {
throw new Error(`Invalid flows list in agent ${name}`);
}
}
else if (apdef.name == 'directives') {
conds = processAgentDirectives(name, apdef.value);
}
else if (apdef.name == 'scenarios') {
scenarios = processAgentScenarios(name, apdef.value);
}
else if (apdef.name == 'glossary') {
glossary = processAgentGlossary(name, apdef.value);
}
else if (apdef.name == 'responseSchema') {
const s = apdef.value.id || apdef.value.ref || apdef.value.str;
if (s) {
if (isFqName(s)) {
responseSchema = s;
}
else {
responseSchema = makeFqName(moduleName, s);
}
}
else {
throw new Error(`responseSchema must be a valid name in agent ${name}`);
}
}
else if (apdef.name == 'scratch') {
scratchNames = processAgentScratchNames(name, apdef.value);
}
else {
let v = undefined;
if (apdef.value.array) {
v = processAgentArray(apdef.value.array, name);
}
else {
v = apdef.value.str || apdef.value.id || apdef.value.ref || apdef.value.num;
if (v === undefined) {
v = apdef.value.bool;
}
}
if (v === undefined) {
throw new Error(`Cannot initialize agent ${name}, only literals can be set for attributes`);
}
if (apdef.name == 'llm') {
llmName = v;
}
const ov = v;
if (apdef.value.id || apdef.value.ref || apdef.value.array) {
v = `"${v}"`;
}
else if (apdef.value.str) {
v = `"${escapeSpecialChars(v)}"`;
}
attrsStrs.push(`${apdef.name} ${v}`);
attrs.set(apdef.name, ov);
}
});
let createDefaultLLM = false;
if (!attrs.has('llm')) {
// Agent doesn't have an LLM specified, create a default one
llmName = `${name}_llm`;
createDefaultLLM = true;
}
// Create a copy of attrsStrs for the database operation
const dbAttrsStrs = [...attrsStrs];
// Only add llm to database attributes if we have one
if (llmName) {
dbAttrsStrs.push(`llm "${llmName}"`);
}
const createAgent = `{${CoreAIModuleName}/${AgentEntityName} {
${dbAttrsStrs.join(',')}
}, @upsert}`;
let wf = createAgent;
// Only create an LLM with default service if we're creating a default LLM
// If the user specified an LLM name, don't create/upsert it (it should already exist)
if (createDefaultLLM && llmName) {
const service = getDefaultLLMService();
wf = `{${CoreAIModuleName}/${LlmEntityName} {name "${llmName}", service "${service}"}, @upsert}; ${wf}`;
}
(await parseWorkflow(`workflow A {${wf}}`)).statements.forEach((stmt) => {
addStandaloneStatement(stmt, moduleName, false);
});
const agentFqName = makeFqName(moduleName, name);
if (conds) {
registerAgentDirectives(agentFqName, conds);
}
if (scenarios) {
registerAgentScenarios(agentFqName, scenarios);
}
if (glossary) {
registerAgentGlossary(agentFqName, glossary);
}
if (responseSchema) {
registerAgentResponseSchema(agentFqName, responseSchema);
}
if (scratchNames) {
registerAgentScratchNames(agentFqName, scratchNames);
}
// Don't add llm to module attrs if it wasn't originally specified
const agent = addAgent(def.name, attrs, moduleName);
if (ispub) {
agent.setPublic(true);
}
return agent;
}
function processAgentArray(array, attrName) {
return array.vals
.map((stmt) => {
const expr = stmt.pattern.expr;
return processAgentArrayValue(expr, attrName);
})
.join(',');
}
function processAgentArrayValue(expr, attrName) {
if (expr && isLiteral(expr)) {
const s = expr.str || expr.id || expr.ref || expr.bool;
if (s !== undefined) {
return s;
}
if (expr.array) {
return processAgentArray(expr.array, attrName);
}
else if (expr.map) {
const m = new Array();
expr.map.entries.forEach((me) => {
m.push(`${me.key.str || me.key.num || me.key.bool || ''}: ${processAgentArrayValue(me.value, attrName)}`);
});
return `{${m.join(',')}}`;
}
else {
throw new Error(`Type not supported in agent-arrays - ${attrName}`);
}
}
else {
throw new Error(`Invalid value in array passed to agent ${attrName}`);
}
}
function addFlowDefinition(def, moduleName) {
var _a;
const m = fetchModule(moduleName);
const sdef = (_a = def.$cstNode) === null || _a === void 0 ? void 0 : _a.text;
let f = '';
if (sdef) {
const idx = sdef.indexOf('{');
if (idx > 0) {
f = sdef.substring(idx + 1, sdef.lastIndexOf('}')).trim();
}
else {
f = sdef;
}
}
m.addFlow(def.name, f);
registerFlow(`${moduleName}/${def.name}`, f);
}
function addDecisionDefinition(def, moduleName) {
const m = fetchModule(moduleName);
const cases = def.body
? def.body.cases.map((ce) => {
var _a;
return (_a = ce.$cstNode) === null || _a === void 0 ? void 0 : _a.text;
})
: new Array();
m.addRawDecision(def.name, cases);
}
function agentXtraAttributesAsMap(xtras) {
const result = new Map();
xtras === null || xtras === void 0 ? void 0 : xtras.forEach((v) => {
result.set(v.name, v.value);
});
return result;
}
function scenarioConditionAsMap(cond) {
var _a, _b, _c;
const result = new Map();
if (cond) {
if (isLiteral(cond.cond)) {
const s = cond.cond.str;
if (s === undefined) {
throw new Error(`scenario condition must be a string - ${(_a = cond.cond.$cstNode) === null || _a === void 0 ? void 0 : _a.text}`);
}
const stmt = cond.statements[0];
const v = stmt ? (_b = stmt.pattern.$cstNode) === null || _b === void 0 ? void 0 : _b.text : '';
if (v === undefined) {
throw new Error(`scenario consequent must be a string or name - ${(_c = cond.cond.$cstNode) === null || _c === void 0 ? void 0 : _c.text}`);
}
result.set('user', s).set('ai', v).set('if', introspectIf(cond));
}
}
return result;
}
function addScenarioDefintion(def, moduleName) {
if (def.body || def.scn) {
let n = rootRef(def.name);
if (!isFqName(n)) {
n = makeFqName(moduleName, n);
}
const m = def.body ? asStringLiteralsMap(def.body) : scenarioConditionAsMap(def.scn);
const user = m.get('user');
const ai = m.get('ai');
const ifPattern = m.get('if');
if (user !== undefined && ai !== undefined) {
const scn = { user: user, ai: ai, internal: false, ifPattern };
addAgentScenario(n, scn);
fetchModule(moduleName).addScenario(def.name, scn);
}
else
throw new Error(`scenario ${def.name} requires both user and ai entries`);
}
}
function addDirectiveDefintion(def, moduleName) {
var _a;
if (def.body || def.dir) {
let n = rootRef(def.name);
if (!isFqName(n)) {
n = makeFqName(moduleName, n);
}
if (def.body) {
const m = asStringLiteralsMap(def.body);
const cond = m.get('if');
const then = m.get('then');
if (cond && then) {
const dir = { if: cond, then: then, internal: false, ifPattern: undefined };
addAgentDirective(n, dir);
fetchModule(moduleName).addDirective(def.name, dir);
}
else
throw new Error(`directive ${def.name} requires both if and then entries`);
}
else if (def.dir) {
const cond = (_a = def.dir.$cstNode) === null || _a === void 0 ? void 0 : _a.text;
if (cond) {
const ifPattern = introspectIf(def.dir);
const dir = { if: cond, then: '', internal: false, ifPattern };
addAgentDirective(n, dir);
fetchModule(moduleName).addDirective(def.name, dir);
}
else {
throw new Error(`directive ${def.name} requires a valid if expression`);
}
}
}
}
function addGlossaryEntryDefintion(def, moduleName) {
var _a;
if (def.body || def.glos) {
let n = rootRef(def.name);
if (!isFqName(n)) {
n = makeFqName(moduleName, n);
}
const m = def.body
? asStringLiteralsMap(def.body)
: agentXtraAttributesAsMap((_a = def.glos) === null || _a === void 0 ? void 0 : _a.attributes);
const name = m.get('name') || m.get('word');
const meaning = m.get('meaning');
const syn = m.get('synonyms');
if (name && meaning) {
const ge = {
name: name,
meaning: meaning,
synonyms: syn,
internal: false,
};
addAgentGlossaryEntry(n, ge);
fetchModule(moduleName).addGlossaryEntry(def.name, ge);
}
else
throw new Error(`glossaryEntry ${def.name} requires both name and meaning keys`);
}
}
function addRetryDefinition(def, moduleName) {
const retry = new Retry(def.name, moduleName, def.attempts !== undefined ? def.attempts : 0);
if (def.backoff) {
def.backoff.attributes.forEach((attr) => {
if (isLiteral(attr.value)) {
switch (attr.name) {
case 'strategy':
switch (attr.value.id || attr.value.str) {
case 'exponential':
retry.setExponentialBackoff();
break;
case 'linear':
retry.setLinearBackoff();
break;
case 'constant':
retry.setConstantBackoff();
break;
default:
throw new Error(`Invalid backoff strategy ${attr.value} specified for ${def.name}`);
}
break;
case 'delay':
if (attr.value.num) {
retry.setBackoffDelay(attr.value.num);
}
else {
throw new Error(`Backoff delay must be a numeric value for ${def.name}`);
}
break;
case 'magnitude':
switch (attr.value.id || attr.value.str) {
case 'milliseconds':
retry.setBackoffMagnitudeAsMilliseconds();
break;
case 'seconds':
retry.setBackoffMagnitudeAsSeconds();
break;
case 'minutes':
retry.setBackoffMagnitudeAsMinutes();
break;
default:
throw new Error(`Invalid backoff magnitude ${attr.value} set for ${def.name}`);
}
break;
case 'factor':
if (attr.value.num) {
retry.setBackoffFactor(attr.value.num);
}
else {
throw new Error(`Backoff factor must be a number for ${def.name}`);
}
break;
default:
throw new Error(`Invalid backoff option ${attr.name} specified for ${def.name}`);
}
}
else {
throw new Error(`strategy must be a string in ${def.name}`);
}
});
}
fetchModule(moduleName).addRetry(retry);
}
function addResolverDefinition(def, moduleName) {
const resolverName = `${moduleName}/${def.name}`;
const paths = def.paths;
if (paths.length == 0) {
logger.warn(`Resolver has no associated paths - ${resolverName}`);
return;
}
registerInitFunction(() => {
const methods = new Map();
let subsFn;
let subsEvent;
def.methods.forEach((spec) => {
const n = spec.key.name;
if (n == 'subscribe') {
subsFn = asResolverFn(spec.fn.name);
}
else if (n == 'onSubscription') {
subsEvent = spec.fn.name;
}
else {
methods.set(n, asResolverFn(spec.fn.name));
}
});
const methodsObj = Object.fromEntries(methods.entries());
const resolver = new GenericResolver(resolverName, methodsObj);
registerResolver(resolverName, () => {
return resolver;
});
paths.forEach((path) => {
setResolver(path, resolverName);
});
if (subsFn) {
resolver.subs = {
subscribe: subsFn,
};
if (subsEvent)
SetSubscription(subsEvent, resolverName);
resolver.subscribe();
}
});
}
function asResolverFn(fname) {
let fn = getModuleFn(fname);
if (fn)
return fn;
fn = eval(fname);
if (!(fn instanceof Function)) {
throw new Error(`${fname} is not a function`);
}
return fn;
}
export async function addFromDef(def, moduleName) {
if (isEntityDefinition(def))
addSchemaFromDef(def, moduleName);
else if (isEventDefinition(def))
addSchemaFromDef(def, moduleName);
else if (isPublicEventDefinition(def))
addSchemaFromDef(def.def, moduleName, true);
else if (isRecordDefinition(def))
addSchemaFromDef(def, moduleName);
else if (isRelationshipDefinition(def))
addRelationshipFromDef(def, moduleName);
else if (isWorkflowDefinition(def))
addWorkflowFromDef(def, moduleName);
else if (isPublicWorkflowDefinition(def))
addWorkflowFromDef(def.def, moduleName, true);
else if (isAgentDefinition(def))
await addAgentDefinition(def, moduleName);
else if (isPublicAgentDefinition(def))
await addAgentDefinition(def.def, moduleName, true);
else if (isStandaloneStatement(def))
addStandaloneStatement(def.stmt, moduleName);
else if (isResolverDefinition(def))
addResolverDefinition(def, moduleName);
else if (isFlowDefinition(def))
addFlowDefinition(def, moduleName);
else if (isDecisionDefinition(def))
addDecisionDefinition(def, moduleName);
else if (isScenarioDefinition(def))
addScenarioDefintion(def, moduleName);
else if (isDirectiveDefinition(def))
addDirectiveDefintion(def, moduleName);
else if (isGlossaryEntryDefinition(def))
addGlossaryEntryDefintion(def, moduleName);
else if (isRetryDefinition(def))
addRetryDefinition(def, moduleName);
}
export async function parseAndIntern(code, moduleName) {
if (moduleName && !isModule(moduleName)) {
throw new Error(`Module not found - ${moduleName}`);
}
const r = await parse(moduleName ? `module ${moduleName} ${code}` : code);
maybeRaiseParserErrors(r);
await internModule(r.parseResult.value);
}
export async function internModule(module, moduleFileName) {
const mn = module.name;
const r = addModule(mn);
module.imports.forEach(async (imp) => {
await importModule(imp.path, imp.name, moduleFileName);
});
for (let i = 0; i < module.defs.length; ++i) {
const def = module.defs[i];
await addFromDef(def, mn);
}
return r;
}
export async function loadRawConfig(configFileName, validate = true, fsOptions) {
const fs = await getFileSystem(fsOptions);
if (await fs.exists(configFileName)) {
let rawConfig = preprocessRawConfig(JSON.parse(await fs.readFile(configFileName)));
if (validate) {
rawConfig = ConfigSchema.parse(rawConfig);
}
return rawConfig;
}
else {
return { service: { port: 8080 } };
}
}
export function configFromObject(cfgObj, validate = true) {
const rawConfig = preprocessRawConfig(cfgObj);
if (validate) {
return ConfigSchema.parse(rawConfig);
}
return rawConfig;
}
export function generateRawConfig(configObj) {
return JSON.stringify(configObj);
}
//# sourceMappingURL=loader.js.map