openagentflow 0.1.0

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.
@@ -0,0 +1,134 @@
1
+ /**
2
+ * OpenAgentFlow Compiler — Unified Pipeline
3
+ *
4
+ * Orchestrates the full compilation pipeline:
5
+ * 1. Lexical Analysis (Lexer)
6
+ * 2. Parsing (Parser → AST)
7
+ * 3. Semantic Validation (Validator)
8
+ * 4. IR Generation (IRGenerator)
9
+ *
10
+ * Each stage can be invoked independently or the full pipeline can be run
11
+ * via the `compile()` method.
12
+ */
13
+
14
+ import { Lexer } from '../parser/lexer.js';
15
+ import { Parser } from '../parser/parser.js';
16
+ import { SemanticValidator } from './validator.js';
17
+ import { IRGenerator } from './ir-generator.js';
18
+
19
+ // ─── Compilation Result ────────────────────────────────────────────────────────
20
+
21
+ export class CompilationResult {
22
+ constructor() {
23
+ /** @type {import('../parser/lexer.js').Token[]|null} */
24
+ this.tokens = null;
25
+
26
+ /** @type {import('../parser/ast.js').Program|null} */
27
+ this.ast = null;
28
+
29
+ /** @type {import('./validator.js').ValidationResult|null} */
30
+ this.validation = null;
31
+
32
+ /** @type {object|null} */
33
+ this.ir = null;
34
+
35
+ /** @type {Error|null} */
36
+ this.error = null;
37
+
38
+ /** @type {'success'|'lexer_error'|'parse_error'|'validation_error'|'error'} */
39
+ this.status = 'success';
40
+ }
41
+ }
42
+
43
+ // ─── Compiler ──────────────────────────────────────────────────────────────────
44
+
45
+ export class Compiler {
46
+ /**
47
+ * @param {string} source - The .oaf source text
48
+ * @param {string} [filename='<input>'] - Filename for diagnostics
49
+ */
50
+ constructor(source, filename = '<input>') {
51
+ this.source = source;
52
+ this.filename = filename;
53
+ }
54
+
55
+ /**
56
+ * Run the full compilation pipeline.
57
+ * @returns {CompilationResult}
58
+ */
59
+ compile() {
60
+ const result = new CompilationResult();
61
+
62
+ try {
63
+ // Stage 1: Lexical Analysis
64
+ result.tokens = this.lex();
65
+
66
+ // Stage 2: Parsing
67
+ result.ast = this.parse(result.tokens);
68
+
69
+ // Stage 3: Semantic Validation
70
+ result.validation = this.validate(result.ast);
71
+
72
+ if (!result.validation.isValid) {
73
+ result.status = 'validation_error';
74
+ return result;
75
+ }
76
+
77
+ // Stage 4: IR Generation
78
+ result.ir = this.generateIR(result.ast);
79
+
80
+ result.status = 'success';
81
+ } catch (err) {
82
+ result.error = err;
83
+
84
+ if (err.name === 'LexerError') {
85
+ result.status = 'lexer_error';
86
+ } else if (err.name === 'ParseError') {
87
+ result.status = 'parse_error';
88
+ } else {
89
+ result.status = 'error';
90
+ }
91
+ }
92
+
93
+ return result;
94
+ }
95
+
96
+ /**
97
+ * Stage 1: Tokenize the source.
98
+ * @returns {import('../parser/lexer.js').Token[]}
99
+ */
100
+ lex() {
101
+ const lexer = new Lexer(this.source, this.filename);
102
+ return lexer.tokenize();
103
+ }
104
+
105
+ /**
106
+ * Stage 2: Parse tokens into an AST.
107
+ * @param {import('../parser/lexer.js').Token[]} tokens
108
+ * @returns {import('../parser/ast.js').Program}
109
+ */
110
+ parse(tokens) {
111
+ const parser = new Parser(tokens);
112
+ return parser.parse();
113
+ }
114
+
115
+ /**
116
+ * Stage 3: Validate the AST semantically.
117
+ * @param {import('../parser/ast.js').Program} ast
118
+ * @returns {import('./validator.js').ValidationResult}
119
+ */
120
+ validate(ast) {
121
+ const validator = new SemanticValidator(ast, this.filename);
122
+ return validator.validate();
123
+ }
124
+
125
+ /**
126
+ * Stage 4: Generate the IR from a validated AST.
127
+ * @param {import('../parser/ast.js').Program} ast
128
+ * @returns {object}
129
+ */
130
+ generateIR(ast) {
131
+ const generator = new IRGenerator(ast);
132
+ return generator.generate();
133
+ }
134
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OpenAgentFlow Compiler — Public API
3
+ */
4
+
5
+ export { Compiler, CompilationResult } from './compiler.js';
6
+ export { SemanticValidator, Diagnostic, ValidationResult } from './validator.js';
7
+ export { IRGenerator } from './ir-generator.js';
@@ -0,0 +1,142 @@
1
+ /**
2
+ * OpenAgentFlow IR Generator
3
+ *
4
+ * Transforms a validated AST into the Intermediate Representation (IR).
5
+ * The IR is a plain JSON-serializable object conforming to the IR spec.
6
+ *
7
+ * Pipeline: Validated AST → [IR Generator] → IR (JSON)
8
+ */
9
+
10
+ const IR_VERSION = '0.1.0';
11
+
12
+ export class IRGenerator {
13
+ /**
14
+ * @param {import('../parser/ast.js').Program} ast - The validated AST
15
+ */
16
+ constructor(ast) {
17
+ this.ast = ast;
18
+ }
19
+
20
+ /**
21
+ * Generate the IR from the AST.
22
+ * @returns {object} IR document
23
+ */
24
+ generate() {
25
+ const workflow = this.ast.workflow;
26
+
27
+ return {
28
+ version: IR_VERSION,
29
+ workflow: this.buildWorkflowMeta(workflow),
30
+ state: this.buildState(workflow.state),
31
+ agents: this.buildAgents(workflow.agents),
32
+ graph: this.buildGraph(workflow.flow),
33
+ };
34
+ }
35
+
36
+ // ── Workflow Metadata ───────────────────────────────────────────────────────
37
+
38
+ buildWorkflowMeta(workflow) {
39
+ const config = {};
40
+ if (workflow.config) {
41
+ for (const entry of workflow.config.entries) {
42
+ config[entry.key] = entry.value;
43
+ }
44
+ }
45
+ return {
46
+ name: workflow.name,
47
+ config,
48
+ };
49
+ }
50
+
51
+ // ── State ───────────────────────────────────────────────────────────────────
52
+
53
+ buildState(stateBlock) {
54
+ if (!stateBlock) {
55
+ return { variables: [] };
56
+ }
57
+
58
+ return {
59
+ variables: stateBlock.fields.map(field => ({
60
+ name: field.name,
61
+ type: this.serializeType(field.typeExpr),
62
+ options: (field.options ?? []).map(opt => ({
63
+ name: opt.name,
64
+ args: opt.args ?? [],
65
+ })),
66
+ })),
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Convert a TypeExpr AST node into an IR type descriptor string.
72
+ * @param {import('../parser/ast.js').TypeExpr} typeExpr
73
+ * @returns {string}
74
+ */
75
+ serializeType(typeExpr) {
76
+ switch (typeExpr.kind) {
77
+ case 'primitive':
78
+ return typeExpr.name;
79
+ case 'list':
80
+ return `list<${this.serializeType(typeExpr.elementType)}>`;
81
+ case 'map':
82
+ return `map<${this.serializeType(typeExpr.keyType)},${this.serializeType(typeExpr.valueType)}>`;
83
+ default:
84
+ return 'unknown';
85
+ }
86
+ }
87
+
88
+ // ── Agents ──────────────────────────────────────────────────────────────────
89
+
90
+ buildAgents(agents) {
91
+ return agents.map(agent => {
92
+ let provider = agent.provider ?? null;
93
+ if (!provider && agent.model) {
94
+ if (agent.model.startsWith('claude-')) provider = 'anthropic';
95
+ else if (agent.model.startsWith('gpt-') || agent.model.startsWith('o1') || agent.model.startsWith('o3')) provider = 'openai';
96
+ else if (agent.model.startsWith('gemini-') || agent.model.startsWith('gemma-')) provider = 'gemini';
97
+ }
98
+ return {
99
+ id: agent.id,
100
+ instructions: agent.instructions,
101
+ model: agent.model,
102
+ provider,
103
+ temperature: agent.temperature,
104
+ tools: agent.tools,
105
+ inputs: agent.inputs,
106
+ outputs: agent.outputs,
107
+ };
108
+ });
109
+ }
110
+
111
+ // ── Graph ───────────────────────────────────────────────────────────────────
112
+
113
+ buildGraph(flowBlock) {
114
+ if (!flowBlock) {
115
+ return { edges: [], entrypoint: null, terminals: [] };
116
+ }
117
+
118
+ // Find entrypoint: the target of the start edge
119
+ let entrypoint = null;
120
+ const terminals = [];
121
+ const agentEdges = [];
122
+
123
+ for (const edge of flowBlock.edges) {
124
+ if (edge.source === 'start') {
125
+ entrypoint = edge.target;
126
+ } else if (edge.target === 'end') {
127
+ terminals.push(edge.source);
128
+ } else {
129
+ agentEdges.push({
130
+ source: edge.source,
131
+ target: edge.target,
132
+ });
133
+ }
134
+ }
135
+
136
+ return {
137
+ edges: agentEdges,
138
+ entrypoint,
139
+ terminals,
140
+ };
141
+ }
142
+ }