@orcalang/orca-lang 0.1.18 → 0.1.21
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/dist/compiler/dt-compiler.d.ts +26 -0
- package/dist/compiler/dt-compiler.d.ts.map +1 -0
- package/dist/compiler/dt-compiler.js +387 -0
- package/dist/compiler/dt-compiler.js.map +1 -0
- package/dist/health-check.d.ts +3 -0
- package/dist/health-check.d.ts.map +1 -0
- package/dist/health-check.js +235 -0
- package/dist/health-check.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/ast-to-markdown.d.ts.map +1 -1
- package/dist/parser/ast-to-markdown.js +3 -1
- package/dist/parser/ast-to-markdown.js.map +1 -1
- package/dist/parser/ast.d.ts +3 -0
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/dt-ast.d.ts +43 -0
- package/dist/parser/dt-ast.d.ts.map +1 -0
- package/dist/parser/dt-ast.js +3 -0
- package/dist/parser/dt-ast.js.map +1 -0
- package/dist/parser/dt-parser.d.ts +40 -0
- package/dist/parser/dt-parser.d.ts.map +1 -0
- package/dist/parser/dt-parser.js +240 -0
- package/dist/parser/dt-parser.js.map +1 -0
- package/dist/parser/markdown-parser.d.ts.map +1 -1
- package/dist/parser/markdown-parser.js +43 -8
- package/dist/parser/markdown-parser.js.map +1 -1
- package/dist/skills.d.ts +50 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +508 -21
- package/dist/skills.js.map +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +49 -0
- package/dist/tools.js.map +1 -1
- package/dist/verifier/dt-verifier.d.ts +32 -0
- package/dist/verifier/dt-verifier.d.ts.map +1 -0
- package/dist/verifier/dt-verifier.js +830 -0
- package/dist/verifier/dt-verifier.js.map +1 -0
- package/dist/verifier/properties.d.ts +4 -0
- package/dist/verifier/properties.d.ts.map +1 -1
- package/dist/verifier/properties.js +56 -20
- package/dist/verifier/properties.js.map +1 -1
- package/dist/verifier/structural.d.ts.map +1 -1
- package/dist/verifier/structural.js +6 -1
- package/dist/verifier/structural.js.map +1 -1
- package/dist/verifier/types.d.ts +4 -0
- package/dist/verifier/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/compiler/dt-compiler.ts +454 -0
- package/src/health-check.ts +273 -0
- package/src/index.ts +5 -1
- package/src/parser/ast-to-markdown.ts +2 -1
- package/src/parser/ast.ts +4 -0
- package/src/parser/dt-ast.ts +40 -0
- package/src/parser/dt-parser.ts +289 -0
- package/src/parser/markdown-parser.ts +43 -8
- package/src/skills.ts +591 -22
- package/src/tools.ts +53 -0
- package/src/verifier/dt-verifier.ts +928 -0
- package/src/verifier/properties.ts +78 -23
- package/src/verifier/structural.ts +5 -1
- package/src/verifier/types.ts +4 -0
package/dist/skills.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
|
package/dist/skills.js
CHANGED
|
@@ -6,6 +6,8 @@ import { checkDeterminism } from './verifier/determinism.js';
|
|
|
6
6
|
import { checkProperties } from './verifier/properties.js';
|
|
7
7
|
import { compileToXState } from './compiler/xstate.js';
|
|
8
8
|
import { compileToMermaid } from './compiler/mermaid.js';
|
|
9
|
+
import { verifyDecisionTables, checkFileContextAlignment, checkDTMachineIntegration, computeAlignedDTOutputDomain } from './verifier/dt-verifier.js';
|
|
10
|
+
import { compileDecisionTableToTypeScript, compileDecisionTableToPython, compileDecisionTableToGo, compileDecisionTableToJSON, toSnakeCase, } from './compiler/dt-compiler.js';
|
|
9
11
|
import { loadConfig } from './config/index.js';
|
|
10
12
|
import { createProvider } from './llm/index.js';
|
|
11
13
|
import { getCodeGenerator } from './generators/index.js';
|
|
@@ -134,8 +136,15 @@ export async function verifySkill(input) {
|
|
|
134
136
|
const source = resolveSource(input);
|
|
135
137
|
const label = resolveLabel(input);
|
|
136
138
|
let machine;
|
|
139
|
+
let fileDecisionTables = [];
|
|
137
140
|
try {
|
|
138
|
-
|
|
141
|
+
const { file } = parseMarkdown(source);
|
|
142
|
+
if (file.machines.length === 0)
|
|
143
|
+
throw new Error(`${label} contains no machine definition.`);
|
|
144
|
+
if (file.machines.length > 1)
|
|
145
|
+
throw new Error(`${label} contains multiple machines.`);
|
|
146
|
+
machine = file.machines[0];
|
|
147
|
+
fileDecisionTables = file.decisionTables;
|
|
139
148
|
}
|
|
140
149
|
catch (err) {
|
|
141
150
|
// Parse error - return as verification error
|
|
@@ -157,7 +166,16 @@ export async function verifySkill(input) {
|
|
|
157
166
|
const structural = checkStructural(machine);
|
|
158
167
|
const completeness = checkCompleteness(machine);
|
|
159
168
|
const determinism = checkDeterminism(machine);
|
|
160
|
-
|
|
169
|
+
// Check co-located decision table alignment and machine integration (single-machine files only)
|
|
170
|
+
const orcaFile = { machines: [machine], decisionTables: fileDecisionTables };
|
|
171
|
+
const dtOutputDomain = fileDecisionTables.length > 0 ? computeAlignedDTOutputDomain(orcaFile) : undefined;
|
|
172
|
+
const properties = checkProperties(machine, { dtOutputDomain });
|
|
173
|
+
const dtAlignment = fileDecisionTables.length > 0
|
|
174
|
+
? checkFileContextAlignment(orcaFile)
|
|
175
|
+
: [];
|
|
176
|
+
const dtIntegration = fileDecisionTables.length > 0
|
|
177
|
+
? checkDTMachineIntegration(orcaFile)
|
|
178
|
+
: [];
|
|
161
179
|
const mapError = (e) => ({
|
|
162
180
|
code: e.code,
|
|
163
181
|
message: e.message,
|
|
@@ -165,6 +183,9 @@ export async function verifySkill(input) {
|
|
|
165
183
|
location: e.location ? {
|
|
166
184
|
state: e.location.state,
|
|
167
185
|
event: e.location.event,
|
|
186
|
+
decisionTable: e.location.decisionTable,
|
|
187
|
+
condition: e.location.condition,
|
|
188
|
+
action: e.location.action,
|
|
168
189
|
} : undefined,
|
|
169
190
|
suggestion: e.suggestion,
|
|
170
191
|
});
|
|
@@ -173,6 +194,8 @@ export async function verifySkill(input) {
|
|
|
173
194
|
...completeness.errors.map(mapError),
|
|
174
195
|
...determinism.errors.map(mapError),
|
|
175
196
|
...properties.errors.map(mapError),
|
|
197
|
+
...dtAlignment.map(mapError),
|
|
198
|
+
...dtIntegration.map(mapError),
|
|
176
199
|
];
|
|
177
200
|
return {
|
|
178
201
|
status: allErrors.some(e => e.severity === 'error') ? 'invalid' : 'valid',
|
|
@@ -219,7 +242,17 @@ export async function compileSkill(input, target) {
|
|
|
219
242
|
}
|
|
220
243
|
export async function generateActionsSkill(input, language = 'typescript', useLLM = false, configPath, generateTests = false) {
|
|
221
244
|
const source = resolveSource(input);
|
|
222
|
-
|
|
245
|
+
// Parse the full file to get both the machine and any co-located decision tables
|
|
246
|
+
const { file } = parseMarkdown(source);
|
|
247
|
+
const label = resolveLabel(input);
|
|
248
|
+
if (file.machines.length === 0) {
|
|
249
|
+
throw new Error(`${label} contains no machine definition.`);
|
|
250
|
+
}
|
|
251
|
+
if (file.machines.length > 1) {
|
|
252
|
+
throw new Error(`${label} contains multiple machines. Use a single-machine file for action generation.`);
|
|
253
|
+
}
|
|
254
|
+
const machine = file.machines[0];
|
|
255
|
+
const decisionTables = file.decisionTables;
|
|
223
256
|
const actions = machine.actions.map(action => ({
|
|
224
257
|
name: action.name,
|
|
225
258
|
signature: `${action.name}(${action.parameters.join(', ')}) -> ${action.returnType}${action.hasEffect ? ` + Effect<${action.effectType}>` : ''}`,
|
|
@@ -229,6 +262,19 @@ export async function generateActionsSkill(input, language = 'typescript', useLL
|
|
|
229
262
|
effectType: action.effectType,
|
|
230
263
|
context_used: extractContextFields(machine, action.name),
|
|
231
264
|
}));
|
|
265
|
+
// Compile decision table evaluator code for each DT in the file
|
|
266
|
+
const decisionTableCode = {};
|
|
267
|
+
for (const dt of decisionTables) {
|
|
268
|
+
if (language === 'python') {
|
|
269
|
+
decisionTableCode[dt.name] = compileDecisionTableToPython(dt);
|
|
270
|
+
}
|
|
271
|
+
else if (language === 'go') {
|
|
272
|
+
decisionTableCode[dt.name] = compileDecisionTableToGo(dt);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
decisionTableCode[dt.name] = compileDecisionTableToTypeScript(dt);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
232
278
|
let scaffolds = {};
|
|
233
279
|
let tests = {};
|
|
234
280
|
if (useLLM) {
|
|
@@ -241,7 +287,7 @@ export async function generateActionsSkill(input, language = 'typescript', useLL
|
|
|
241
287
|
max_tokens: config.max_tokens,
|
|
242
288
|
temperature: config.temperature,
|
|
243
289
|
});
|
|
244
|
-
scaffolds = await generateWithLLM(provider, actions, machine, language);
|
|
290
|
+
scaffolds = await generateWithLLM(provider, actions, machine, language, decisionTables);
|
|
245
291
|
if (generateTests) {
|
|
246
292
|
tests = await generateUnitTests(provider, actions, machine, language);
|
|
247
293
|
}
|
|
@@ -249,7 +295,14 @@ export async function generateActionsSkill(input, language = 'typescript', useLL
|
|
|
249
295
|
else {
|
|
250
296
|
// Use template-based scaffold generation
|
|
251
297
|
for (const action of machine.actions) {
|
|
252
|
-
|
|
298
|
+
const matchedDT = findMatchingDT(action.name, decisionTables);
|
|
299
|
+
if (matchedDT && !action.hasEffect && isDTFullyAligned(matchedDT, machine)) {
|
|
300
|
+
// All DT conditions and outputs exist in context — generate fully wired code
|
|
301
|
+
scaffolds[action.name] = generateFullyWiredActionScaffold(action, machine, language, matchedDT);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
scaffolds[action.name] = generateActionScaffold(action, machine, language, matchedDT ?? undefined);
|
|
305
|
+
}
|
|
253
306
|
}
|
|
254
307
|
if (generateTests) {
|
|
255
308
|
tests = generateTemplateTests(actions, machine, language);
|
|
@@ -260,22 +313,31 @@ export async function generateActionsSkill(input, language = 'typescript', useLL
|
|
|
260
313
|
machine: machine.name,
|
|
261
314
|
actions,
|
|
262
315
|
scaffolds,
|
|
316
|
+
decisionTableCode: Object.keys(decisionTableCode).length > 0 ? decisionTableCode : undefined,
|
|
263
317
|
tests: Object.keys(tests).length > 0 ? tests : undefined,
|
|
264
318
|
};
|
|
265
319
|
}
|
|
266
|
-
async function generateWithLLM(provider, actions, machine, language) {
|
|
320
|
+
async function generateWithLLM(provider, actions, machine, language, decisionTables = []) {
|
|
267
321
|
const generator = getCodeGenerator(language);
|
|
268
322
|
const scaffolds = {};
|
|
323
|
+
const dtContext = decisionTables.length > 0
|
|
324
|
+
? `\nDecision tables available:\n${decisionTables.map(dt => `- ${dt.name}: conditions=[${dt.conditions.map(c => c.name).join(', ')}] outputs=[${dt.actions.map(a => a.name).join(', ')}]`).join('\n')}`
|
|
325
|
+
: '';
|
|
269
326
|
const systemPrompt = `You are an expert ${language} developer specializing in state machine action implementations.
|
|
270
327
|
Given a machine definition and action signatures, generate complete action implementations.
|
|
271
328
|
Follow the type signatures exactly. Use the provided context fields.
|
|
272
|
-
If an action has an effect, return [newContext, effect] tuple
|
|
329
|
+
If an action has an effect, return [newContext, effect] tuple.
|
|
330
|
+
If decision tables are listed, use their evaluator functions (e.g. evaluate${language === 'go' ? 'DtName' : 'DtName'}) when appropriate.`;
|
|
273
331
|
for (const action of actions) {
|
|
332
|
+
const matchedDT = findMatchingDT(action.name, decisionTables);
|
|
333
|
+
const dtHint = matchedDT
|
|
334
|
+
? `\nThis action should use the ${matchedDT.name} decision table evaluator.`
|
|
335
|
+
: '';
|
|
274
336
|
const userPrompt = `Machine: ${machine.name}
|
|
275
|
-
Context fields: ${machine.context.map(f => `${f.name}: ${f.type || 'unknown'}`).join(', ')}
|
|
337
|
+
Context fields: ${machine.context.map(f => `${f.name}: ${f.type || 'unknown'}`).join(', ')}${dtContext}
|
|
276
338
|
|
|
277
339
|
Action: ${action.signature}
|
|
278
|
-
Description: ${action.name}${action.hasEffect ? ` (effect type: ${action.effectType})` : ''}
|
|
340
|
+
Description: ${action.name}${action.hasEffect ? ` (effect type: ${action.effectType})` : ''}${dtHint}
|
|
279
341
|
|
|
280
342
|
Generate the implementation:`;
|
|
281
343
|
try {
|
|
@@ -293,7 +355,7 @@ Generate the implementation:`;
|
|
|
293
355
|
catch (err) {
|
|
294
356
|
console.error(`LLM error for action ${action.name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
295
357
|
// Fall back to scaffold on error
|
|
296
|
-
scaffolds[action.name] = generateActionScaffold(action, machine, language);
|
|
358
|
+
scaffolds[action.name] = generateActionScaffold(action, machine, language, matchedDT ?? undefined);
|
|
297
359
|
}
|
|
298
360
|
}
|
|
299
361
|
return scaffolds;
|
|
@@ -600,7 +662,199 @@ function extractContextFields(machine, actionName) {
|
|
|
600
662
|
function toPascalCase(snake) {
|
|
601
663
|
return snake.split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('');
|
|
602
664
|
}
|
|
603
|
-
|
|
665
|
+
/**
|
|
666
|
+
* Find a decision table whose name tokens overlap with the action name tokens.
|
|
667
|
+
* E.g. action "apply_routing_decision" matches DT "PaymentRouting" via "routing".
|
|
668
|
+
*/
|
|
669
|
+
function findMatchingDT(actionName, dts) {
|
|
670
|
+
if (dts.length === 0)
|
|
671
|
+
return null;
|
|
672
|
+
const actionTokens = new Set(actionName.toLowerCase().split('_').filter(t => t.length > 2));
|
|
673
|
+
for (const dt of dts) {
|
|
674
|
+
const dtTokens = dt.name
|
|
675
|
+
.replace(/([A-Z])/g, ' $1')
|
|
676
|
+
.trim()
|
|
677
|
+
.toLowerCase()
|
|
678
|
+
.split(/\s+/)
|
|
679
|
+
.filter(t => t.length > 2);
|
|
680
|
+
if (dtTokens.some(t => actionTokens.has(t)))
|
|
681
|
+
return dt;
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Generate a commented example DT call block to include in an action stub.
|
|
687
|
+
*/
|
|
688
|
+
function generateDTCallComment(dt, language) {
|
|
689
|
+
const dtName = dt.name;
|
|
690
|
+
if (language === 'python') {
|
|
691
|
+
const fnName = `evaluate_${toSnakeCase(dtName)}`;
|
|
692
|
+
const inputClass = `${toPascalCase(dtName)}Input`;
|
|
693
|
+
const inputArgs = dt.conditions.map(c => {
|
|
694
|
+
const hint = c.type === 'enum' && c.values.length > 0
|
|
695
|
+
? `str (${c.values.join(', ')})`
|
|
696
|
+
: c.type === 'bool' ? 'bool' : 'str';
|
|
697
|
+
return ` # ${c.name}=..., # ${hint} — map from ctx`;
|
|
698
|
+
}).join('\n');
|
|
699
|
+
const outputFields = dt.actions.map(a => ` # # dt_result.${a.name} → ctx['${a.name}']`).join('\n');
|
|
700
|
+
return [
|
|
701
|
+
` # Call ${fnName} to evaluate ${dtName} rules:`,
|
|
702
|
+
` # dt_result = ${fnName}(${inputClass}(`,
|
|
703
|
+
inputArgs,
|
|
704
|
+
` # ))`,
|
|
705
|
+
` # if dt_result is not None:`,
|
|
706
|
+
outputFields,
|
|
707
|
+
].join('\n');
|
|
708
|
+
}
|
|
709
|
+
if (language === 'go') {
|
|
710
|
+
const fnName = `Evaluate${toPascalCase(dtName)}`;
|
|
711
|
+
const inputStruct = `${toPascalCase(dtName)}Input`;
|
|
712
|
+
const inputArgs = dt.conditions.map(c => {
|
|
713
|
+
const goField = toPascalCase(c.name);
|
|
714
|
+
const hint = c.type === 'enum' && c.values.length > 0
|
|
715
|
+
? `string (${c.values.join(', ')})`
|
|
716
|
+
: c.type === 'bool' ? 'bool' : 'string';
|
|
717
|
+
return `\t// \t${goField}: ..., // ${hint} — map from ctx`;
|
|
718
|
+
}).join('\n');
|
|
719
|
+
const outputFields = dt.actions.map(a => `\t// \tresult["${a.name}"] = dtResult.${toPascalCase(a.name)}`).join('\n');
|
|
720
|
+
return [
|
|
721
|
+
`\t// Call ${fnName} to evaluate ${dtName} rules:`,
|
|
722
|
+
`\t// dtResult := ${fnName}(${inputStruct}{`,
|
|
723
|
+
inputArgs,
|
|
724
|
+
`\t// })`,
|
|
725
|
+
`\t// if dtResult != nil {`,
|
|
726
|
+
outputFields,
|
|
727
|
+
`\t// }`,
|
|
728
|
+
].join('\n');
|
|
729
|
+
}
|
|
730
|
+
// TypeScript (default)
|
|
731
|
+
const fnName = `evaluate${toPascalCase(dtName)}`;
|
|
732
|
+
const inputType = `${toPascalCase(dtName)}Input`;
|
|
733
|
+
const inputArgs = dt.conditions.map(c => {
|
|
734
|
+
const hint = c.type === 'enum' && c.values.length > 0
|
|
735
|
+
? `enum: ${c.values.join(', ')}`
|
|
736
|
+
: c.type;
|
|
737
|
+
return ` // ${c.name}: /* ctx.? */ as ${inputType}['${c.name}'], // ${hint} — map from ctx`;
|
|
738
|
+
}).join('\n');
|
|
739
|
+
const outputFields = dt.actions.map(a => ` // ${a.name}: dtResult.${a.name},`).join('\n');
|
|
740
|
+
return [
|
|
741
|
+
` // Call ${fnName} to evaluate ${dtName} rules:`,
|
|
742
|
+
` // const dtResult = ${fnName}({`,
|
|
743
|
+
inputArgs,
|
|
744
|
+
` // });`,
|
|
745
|
+
` // if (dtResult !== null) {`,
|
|
746
|
+
` // return { ...ctx,`,
|
|
747
|
+
outputFields,
|
|
748
|
+
` // };`,
|
|
749
|
+
` // }`,
|
|
750
|
+
].join('\n');
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Returns true when every DT condition name and output name exists as a
|
|
754
|
+
* context field in the machine — the full co-location contract is satisfied.
|
|
755
|
+
*/
|
|
756
|
+
function isDTFullyAligned(dt, machine) {
|
|
757
|
+
const contextNames = new Set(machine.context.map(f => f.name));
|
|
758
|
+
return dt.conditions.every(c => contextNames.has(c.name)) &&
|
|
759
|
+
dt.actions.every(a => contextNames.has(a.name));
|
|
760
|
+
}
|
|
761
|
+
/** Generate a Go type assertion for reading a context value. */
|
|
762
|
+
function goCtxRead(fieldName, condType) {
|
|
763
|
+
if (condType === 'bool')
|
|
764
|
+
return `ctx["${fieldName}"].(bool)`;
|
|
765
|
+
if (condType === 'int_range')
|
|
766
|
+
return `ctx["${fieldName}"].(int)`;
|
|
767
|
+
return `ctx["${fieldName}"].(string)`;
|
|
768
|
+
}
|
|
769
|
+
function generateFullyWiredActionScaffold(action, machine, language, dt) {
|
|
770
|
+
if (language === 'python') {
|
|
771
|
+
const dtFnName = `evaluate_${toSnakeCase(dt.name)}`;
|
|
772
|
+
const inputClass = `${toPascalCase(dt.name)}Input`;
|
|
773
|
+
const inputArgs = dt.conditions
|
|
774
|
+
.map(c => ` ${c.name}=ctx['${c.name}'],`)
|
|
775
|
+
.join('\n');
|
|
776
|
+
const outputAssigns = dt.actions
|
|
777
|
+
.map(a => ` '${a.name}': dt_result.${a.name},`)
|
|
778
|
+
.join('\n');
|
|
779
|
+
return `# Action: ${action.name}
|
|
780
|
+
# Decision table: ${dt.name}
|
|
781
|
+
# Register via: machine.register_action("${action.name}", ${action.name})
|
|
782
|
+
|
|
783
|
+
from typing import Any
|
|
784
|
+
|
|
785
|
+
async def ${action.name}(ctx: dict[str, Any], event: Any = None) -> dict[str, Any]:
|
|
786
|
+
dt_result = ${dtFnName}(${inputClass}(
|
|
787
|
+
${inputArgs}
|
|
788
|
+
))
|
|
789
|
+
if dt_result is not None:
|
|
790
|
+
return {**ctx,
|
|
791
|
+
${outputAssigns}
|
|
792
|
+
}
|
|
793
|
+
return dict(ctx)
|
|
794
|
+
`;
|
|
795
|
+
}
|
|
796
|
+
if (language === 'go') {
|
|
797
|
+
const fnName = toPascalCase(action.name);
|
|
798
|
+
const dtFnName = `Evaluate${toPascalCase(dt.name)}`;
|
|
799
|
+
const inputStruct = `${toPascalCase(dt.name)}Input`;
|
|
800
|
+
const ctxReads = dt.conditions.map(c => {
|
|
801
|
+
const varName = toPascalCase(c.name).charAt(0).toLowerCase() + toPascalCase(c.name).slice(1);
|
|
802
|
+
return `\t${varName}, _ := ${goCtxRead(c.name, c.type)}`;
|
|
803
|
+
}).join('\n');
|
|
804
|
+
const inputFields = dt.conditions.map(c => {
|
|
805
|
+
const goField = toPascalCase(c.name);
|
|
806
|
+
const varName = goField.charAt(0).toLowerCase() + goField.slice(1);
|
|
807
|
+
return `\t\t${goField}: ${varName},`;
|
|
808
|
+
}).join('\n');
|
|
809
|
+
const outputAssigns = dt.actions
|
|
810
|
+
.map(a => `\t\tresult["${a.name}"] = dtResult.${toPascalCase(a.name)}`)
|
|
811
|
+
.join('\n');
|
|
812
|
+
return `// Action: ${action.name}
|
|
813
|
+
// Decision table: ${dt.name}
|
|
814
|
+
// Register via: machine.RegisterAction("${action.name}", ${fnName})
|
|
815
|
+
|
|
816
|
+
func ${fnName}(ctx orca.Context, event map[string]any) map[string]any {
|
|
817
|
+
${ctxReads}
|
|
818
|
+
\tdtResult := ${dtFnName}(${inputStruct}{
|
|
819
|
+
${inputFields}
|
|
820
|
+
\t})
|
|
821
|
+
\tresult := make(orca.Context)
|
|
822
|
+
\tfor k, v := range ctx {
|
|
823
|
+
\t\tresult[k] = v
|
|
824
|
+
\t}
|
|
825
|
+
\tif dtResult != nil {
|
|
826
|
+
${outputAssigns}
|
|
827
|
+
\t}
|
|
828
|
+
\treturn result
|
|
829
|
+
}
|
|
830
|
+
`;
|
|
831
|
+
}
|
|
832
|
+
// TypeScript (default)
|
|
833
|
+
const dtFnName = `evaluate${toPascalCase(dt.name)}`;
|
|
834
|
+
const inputArgs = dt.conditions
|
|
835
|
+
.map(c => ` ${c.name}: ctx.${c.name},`)
|
|
836
|
+
.join('\n');
|
|
837
|
+
const outputAssigns = dt.actions
|
|
838
|
+
.map(a => ` ${a.name}: dtResult.${a.name},`)
|
|
839
|
+
.join('\n');
|
|
840
|
+
return `// Action: ${action.name}
|
|
841
|
+
// Decision table: ${dt.name}
|
|
842
|
+
|
|
843
|
+
export function ${action.name}(ctx: Context): Context {
|
|
844
|
+
const dtResult = ${dtFnName}({
|
|
845
|
+
${inputArgs}
|
|
846
|
+
});
|
|
847
|
+
if (dtResult !== null) {
|
|
848
|
+
return {
|
|
849
|
+
...ctx,
|
|
850
|
+
${outputAssigns}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
return { ...ctx };
|
|
854
|
+
}
|
|
855
|
+
`;
|
|
856
|
+
}
|
|
857
|
+
function generateActionScaffold(action, machine, language, matchedDT) {
|
|
604
858
|
if (language === 'python') {
|
|
605
859
|
if (action.hasEffect) {
|
|
606
860
|
return `# Action: ${action.name}
|
|
@@ -617,13 +871,17 @@ async def ${action.name}(effect: Effect) -> EffectResult:
|
|
|
617
871
|
return EffectResult(status=EffectStatus.SUCCESS, data={})
|
|
618
872
|
`;
|
|
619
873
|
}
|
|
620
|
-
|
|
874
|
+
const pyDTComment = matchedDT ? '\n' + generateDTCallComment(matchedDT, 'python') + '\n' : '';
|
|
875
|
+
const pyTodo = matchedDT
|
|
876
|
+
? ` # TODO: Implement action using ${matchedDT.name} decision table`
|
|
877
|
+
: ' # TODO: Implement action';
|
|
878
|
+
return `# Action: ${action.name}${matchedDT ? `\n# Decision table: ${matchedDT.name}` : ''}
|
|
621
879
|
# Register via: machine.register_action("${action.name}", ${action.name})
|
|
622
880
|
|
|
623
881
|
from typing import Any
|
|
624
882
|
|
|
625
|
-
async def ${action.name}(ctx: dict[str, Any], event: Any = None) -> dict[str, Any]
|
|
626
|
-
|
|
883
|
+
async def ${action.name}(ctx: dict[str, Any], event: Any = None) -> dict[str, Any]:${pyDTComment}
|
|
884
|
+
${pyTodo}
|
|
627
885
|
return dict(ctx)
|
|
628
886
|
`;
|
|
629
887
|
}
|
|
@@ -643,11 +901,15 @@ func ${fnName}(effect orca.Effect) orca.EffectResult {
|
|
|
643
901
|
}
|
|
644
902
|
`;
|
|
645
903
|
}
|
|
646
|
-
|
|
904
|
+
const goDTComment = matchedDT ? '\n' + generateDTCallComment(matchedDT, 'go') + '\n' : '';
|
|
905
|
+
const goTodo = matchedDT
|
|
906
|
+
? `\t// TODO: Implement action using ${matchedDT.name} decision table`
|
|
907
|
+
: '\t// TODO: Implement action';
|
|
908
|
+
return `// Action: ${action.name}${matchedDT ? `\n// Decision table: ${matchedDT.name}` : ''}
|
|
647
909
|
// Register via: machine.RegisterAction("${action.name}", ${fnName})
|
|
648
910
|
|
|
649
|
-
func ${fnName}(ctx orca.Context, event map[string]any) map[string]any {
|
|
650
|
-
|
|
911
|
+
func ${fnName}(ctx orca.Context, event map[string]any) map[string]any {${goDTComment}
|
|
912
|
+
${goTodo}
|
|
651
913
|
\tresult := make(orca.Context)
|
|
652
914
|
\tfor k, v := range ctx {
|
|
653
915
|
\t\tresult[k] = v
|
|
@@ -679,10 +941,14 @@ export function ${action.name}(${params}): [Context, Effect<${action.effectType}
|
|
|
679
941
|
// }
|
|
680
942
|
`;
|
|
681
943
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
// TODO: Implement action
|
|
944
|
+
const tsDTComment = matchedDT ? '\n' + generateDTCallComment(matchedDT, 'typescript') + '\n' : '';
|
|
945
|
+
const tsTodo = matchedDT
|
|
946
|
+
? ` // TODO: Implement action using ${matchedDT.name} decision table`
|
|
947
|
+
: ' // TODO: Implement action';
|
|
948
|
+
return `// Action: ${action.name}${matchedDT ? `\n// Decision table: ${matchedDT.name}` : ''}
|
|
949
|
+
|
|
950
|
+
export function ${action.name}(ctx: Context): Context {${tsDTComment}
|
|
951
|
+
${tsTodo}
|
|
686
952
|
return { ...ctx };
|
|
687
953
|
}
|
|
688
954
|
`;
|
|
@@ -1386,4 +1652,225 @@ function extractMachineNameFromSource(orca) {
|
|
|
1386
1652
|
const match = orca.match(/^(?:#\s+)?machine\s+(\w+)/m);
|
|
1387
1653
|
return match ? match[1] : 'Unknown';
|
|
1388
1654
|
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Parse a decision table from source.
|
|
1657
|
+
*/
|
|
1658
|
+
export function parseDTSkill(input) {
|
|
1659
|
+
try {
|
|
1660
|
+
const source = resolveSource(input);
|
|
1661
|
+
const { file } = parseMarkdown(source);
|
|
1662
|
+
if (file.decisionTables.length === 0) {
|
|
1663
|
+
return { status: 'error', error: 'No decision table found in source' };
|
|
1664
|
+
}
|
|
1665
|
+
// Return all decision tables as plain objects
|
|
1666
|
+
const tables = file.decisionTables.map(dt => ({
|
|
1667
|
+
name: dt.name,
|
|
1668
|
+
description: dt.description,
|
|
1669
|
+
conditions: dt.conditions.map(c => ({
|
|
1670
|
+
name: c.name,
|
|
1671
|
+
type: c.type,
|
|
1672
|
+
values: c.values,
|
|
1673
|
+
range: c.range,
|
|
1674
|
+
})),
|
|
1675
|
+
actions: dt.actions.map(a => ({
|
|
1676
|
+
name: a.name,
|
|
1677
|
+
type: a.type,
|
|
1678
|
+
description: a.description,
|
|
1679
|
+
values: a.values,
|
|
1680
|
+
})),
|
|
1681
|
+
rules: dt.rules.map(r => ({
|
|
1682
|
+
number: r.number,
|
|
1683
|
+
conditions: Object.fromEntries(r.conditions),
|
|
1684
|
+
actions: Object.fromEntries(r.actions),
|
|
1685
|
+
})),
|
|
1686
|
+
policy: dt.policy,
|
|
1687
|
+
}));
|
|
1688
|
+
return { status: 'success', decisionTables: tables };
|
|
1689
|
+
}
|
|
1690
|
+
catch (err) {
|
|
1691
|
+
return { status: 'error', error: err instanceof Error ? err.message : String(err) };
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Verify a decision table.
|
|
1696
|
+
*/
|
|
1697
|
+
export function verifyDTSkill(input) {
|
|
1698
|
+
try {
|
|
1699
|
+
const source = resolveSource(input);
|
|
1700
|
+
const { file } = parseMarkdown(source);
|
|
1701
|
+
if (file.decisionTables.length === 0) {
|
|
1702
|
+
return {
|
|
1703
|
+
status: 'invalid',
|
|
1704
|
+
decisionTable: '',
|
|
1705
|
+
conditions: 0,
|
|
1706
|
+
actions: 0,
|
|
1707
|
+
rules: 0,
|
|
1708
|
+
errors: [{ code: 'DT_NOT_FOUND', message: 'No decision table found in source', severity: 'error' }],
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
// Verify all decision tables
|
|
1712
|
+
const verification = verifyDecisionTables(file.decisionTables);
|
|
1713
|
+
// Return result for the first decision table
|
|
1714
|
+
const dt = file.decisionTables[0];
|
|
1715
|
+
const errors = [];
|
|
1716
|
+
for (const e of verification.errors) {
|
|
1717
|
+
const loc = e.location;
|
|
1718
|
+
errors.push({
|
|
1719
|
+
code: e.code,
|
|
1720
|
+
message: e.message,
|
|
1721
|
+
severity: e.severity,
|
|
1722
|
+
location: loc ? {
|
|
1723
|
+
state: loc.state,
|
|
1724
|
+
event: loc.event,
|
|
1725
|
+
rule: loc.rule,
|
|
1726
|
+
condition: loc.condition,
|
|
1727
|
+
action: loc.action,
|
|
1728
|
+
decisionTable: loc.decisionTable,
|
|
1729
|
+
} : undefined,
|
|
1730
|
+
suggestion: e.suggestion,
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
return {
|
|
1734
|
+
status: verification.valid ? 'valid' : 'invalid',
|
|
1735
|
+
decisionTable: dt.name,
|
|
1736
|
+
conditions: dt.conditions.length,
|
|
1737
|
+
actions: dt.actions.length,
|
|
1738
|
+
rules: dt.rules.length,
|
|
1739
|
+
errors,
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
catch (err) {
|
|
1743
|
+
return {
|
|
1744
|
+
status: 'invalid',
|
|
1745
|
+
decisionTable: '',
|
|
1746
|
+
conditions: 0,
|
|
1747
|
+
actions: 0,
|
|
1748
|
+
rules: 0,
|
|
1749
|
+
errors: [{ code: 'PARSE_ERROR', message: err instanceof Error ? err.message : String(err), severity: 'error' }],
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Compile a decision table to TypeScript or JSON.
|
|
1755
|
+
*/
|
|
1756
|
+
export function compileDTSkill(input, target = 'typescript') {
|
|
1757
|
+
try {
|
|
1758
|
+
const source = resolveSource(input);
|
|
1759
|
+
const { file } = parseMarkdown(source);
|
|
1760
|
+
if (file.decisionTables.length === 0) {
|
|
1761
|
+
return {
|
|
1762
|
+
status: 'error',
|
|
1763
|
+
target,
|
|
1764
|
+
output: '',
|
|
1765
|
+
warnings: [{ code: 'DT_NOT_FOUND', message: 'No decision table found in source', severity: 'error' }],
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
const dt = file.decisionTables[0];
|
|
1769
|
+
const output = target === 'json'
|
|
1770
|
+
? compileDecisionTableToJSON(dt)
|
|
1771
|
+
: compileDecisionTableToTypeScript(dt);
|
|
1772
|
+
return { status: 'success', target, output, warnings: [] };
|
|
1773
|
+
}
|
|
1774
|
+
catch (err) {
|
|
1775
|
+
return {
|
|
1776
|
+
status: 'error',
|
|
1777
|
+
target,
|
|
1778
|
+
output: '',
|
|
1779
|
+
warnings: [{ code: 'COMPILE_ERROR', message: err instanceof Error ? err.message : String(err), severity: 'error' }],
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
// Decision table syntax reference for LLM generation
|
|
1784
|
+
const DT_SYNTAX_REFERENCE = `
|
|
1785
|
+
# decision_table Name
|
|
1786
|
+
|
|
1787
|
+
## conditions
|
|
1788
|
+
|
|
1789
|
+
| Name | Type | Values |
|
|
1790
|
+
|------|------|--------|
|
|
1791
|
+
| field_name | enum | value1, value2 |
|
|
1792
|
+
| is_active | bool | |
|
|
1793
|
+
| count | int_range | 1..100 |
|
|
1794
|
+
|
|
1795
|
+
## actions
|
|
1796
|
+
|
|
1797
|
+
| Name | Type | Description |
|
|
1798
|
+
|------|------|-------------|
|
|
1799
|
+
| result | enum | Result description |
|
|
1800
|
+
| flag | bool | Whether something |
|
|
1801
|
+
|
|
1802
|
+
## rules
|
|
1803
|
+
|
|
1804
|
+
| condition1 | condition2 | → action1 | → action2 |
|
|
1805
|
+
|------------|-----------|----------|-----------|
|
|
1806
|
+
| value1 | - | result1 | true |
|
|
1807
|
+
| value2 | !value3 | result2 | false |
|
|
1808
|
+
|
|
1809
|
+
Notes:
|
|
1810
|
+
- "-" in a condition cell means "any" (wildcard)
|
|
1811
|
+
- "!value" negates a value
|
|
1812
|
+
- "a,b" matches any of the values (OR semantics)
|
|
1813
|
+
- Rules are evaluated top-to-bottom; first match wins
|
|
1814
|
+
`;
|
|
1815
|
+
/**
|
|
1816
|
+
* Generate a decision table from natural language spec.
|
|
1817
|
+
*/
|
|
1818
|
+
export async function generateDTSkill(spec, configPath) {
|
|
1819
|
+
const config = loadConfig(configPath);
|
|
1820
|
+
const provider = createProvider(config.provider, {
|
|
1821
|
+
api_key: config.api_key,
|
|
1822
|
+
base_url: config.base_url,
|
|
1823
|
+
model: config.model,
|
|
1824
|
+
max_tokens: config.max_tokens,
|
|
1825
|
+
temperature: config.temperature,
|
|
1826
|
+
});
|
|
1827
|
+
if (!provider) {
|
|
1828
|
+
return { status: 'error', error: 'No LLM provider configured' };
|
|
1829
|
+
}
|
|
1830
|
+
const prompt = `Generate a decision table in .orca.md format based on the following specification.
|
|
1831
|
+
|
|
1832
|
+
${DT_SYNTAX_REFERENCE}
|
|
1833
|
+
|
|
1834
|
+
Specification:
|
|
1835
|
+
${spec}
|
|
1836
|
+
|
|
1837
|
+
Respond with the decision table only, no explanation.`;
|
|
1838
|
+
try {
|
|
1839
|
+
const response = await provider.complete({
|
|
1840
|
+
messages: [{ role: 'user', content: prompt }],
|
|
1841
|
+
model: '',
|
|
1842
|
+
max_tokens: config.max_tokens || 2048,
|
|
1843
|
+
temperature: 0.7,
|
|
1844
|
+
});
|
|
1845
|
+
const orca = stripCodeFence(response.content);
|
|
1846
|
+
// Verify the generated decision table
|
|
1847
|
+
const { file } = parseMarkdown(orca);
|
|
1848
|
+
if (file.decisionTables.length === 0) {
|
|
1849
|
+
return { status: 'error', error: 'Generated content does not contain a valid decision table', orca };
|
|
1850
|
+
}
|
|
1851
|
+
const verification = verifyDecisionTables(file.decisionTables);
|
|
1852
|
+
return {
|
|
1853
|
+
status: verification.valid ? 'success' : 'requires_refinement',
|
|
1854
|
+
decisionTable: file.decisionTables[0].name,
|
|
1855
|
+
orca,
|
|
1856
|
+
verification: verification.valid ? undefined : {
|
|
1857
|
+
status: 'invalid',
|
|
1858
|
+
decisionTable: file.decisionTables[0].name,
|
|
1859
|
+
conditions: file.decisionTables[0].conditions.length,
|
|
1860
|
+
actions: file.decisionTables[0].actions.length,
|
|
1861
|
+
rules: file.decisionTables[0].rules.length,
|
|
1862
|
+
errors: verification.errors.map(e => ({
|
|
1863
|
+
code: e.code,
|
|
1864
|
+
message: e.message,
|
|
1865
|
+
severity: e.severity,
|
|
1866
|
+
location: e.location,
|
|
1867
|
+
suggestion: e.suggestion,
|
|
1868
|
+
})),
|
|
1869
|
+
},
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
catch (err) {
|
|
1873
|
+
return { status: 'error', error: err instanceof Error ? err.message : String(err) };
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1389
1876
|
//# sourceMappingURL=skills.js.map
|