UNPKG

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
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