dirac-lang 0.1.2
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/.env.example +8 -0
- package/COMMUNITY.md +465 -0
- package/LIBRARIES.md +172 -0
- package/NAMESPACES.md +366 -0
- package/PROMOTION.md +257 -0
- package/QUICKSTART-LIBRARY.md +93 -0
- package/README.md +257 -0
- package/config.yml +6 -0
- package/config.yml.openai +4 -0
- package/dirac-http/examples/demo.di +9 -0
- package/dirac-http/lib/index.di +12 -0
- package/dist/chunk-NDIRTD3D.js +217 -0
- package/dist/chunk-S625X7ME.js +1071 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +261 -0
- package/dist/index.d.ts +144 -0
- package/dist/index.js +22 -0
- package/dist/session-4QG7OERD.js +42 -0
- package/examples/add-demo.di +74 -0
- package/examples/add.bk +11 -0
- package/examples/advanced-math-demo.di +53 -0
- package/examples/calculator.di +32 -0
- package/examples/comprehensive.bk +29 -0
- package/examples/defvar-variable-demo.di +18 -0
- package/examples/direct-call.di +17 -0
- package/examples/disk-analysis.di +16 -0
- package/examples/executable-hello.di +7 -0
- package/examples/execute-demo.di +38 -0
- package/examples/file-manager.di +77 -0
- package/examples/file-stats.di +18 -0
- package/examples/hello.bk +1 -0
- package/examples/hello.di +5 -0
- package/examples/import-demo.di +31 -0
- package/examples/inline-test.bk +7 -0
- package/examples/lib/advanced-math.di +81 -0
- package/examples/lib/fileops.di +26 -0
- package/examples/lib/math.di +25 -0
- package/examples/lib/mongodb.di +96 -0
- package/examples/llm-agent.di +32 -0
- package/examples/llm-basic.di +12 -0
- package/examples/llm-command-no-exec.di +13 -0
- package/examples/llm-command.di +13 -0
- package/examples/llm-complex.di +141 -0
- package/examples/llm-recursive.di +31 -0
- package/examples/llm-reflection-test.di +19 -0
- package/examples/llm-subs.di +132 -0
- package/examples/llm-use-subs.di +6 -0
- package/examples/loop.di +12 -0
- package/examples/math-test.di +22 -0
- package/examples/mongodb-count-events.di +8 -0
- package/examples/mongodb-import-demo.di +25 -0
- package/examples/mongodb-simple-test.di +18 -0
- package/examples/nl-agent.di +47 -0
- package/examples/parameters-demo.di +68 -0
- package/examples/params-test.di +10 -0
- package/examples/recipe-chain.di +38 -0
- package/examples/recursive-llm.di +44 -0
- package/examples/sample-library/README.md +152 -0
- package/examples/sample-library/examples/demo.di +34 -0
- package/examples/sample-library/lib/index.di +65 -0
- package/examples/sample-library/package.json +31 -0
- package/examples/seamless.di +45 -0
- package/examples/shell-test.bk +10 -0
- package/examples/simple-import.di +13 -0
- package/examples/simple-recursive.di +26 -0
- package/examples/story-builder.di +45 -0
- package/examples/subroutine.di +23 -0
- package/examples/system-llm.di +21 -0
- package/examples/system-simple.di +3 -0
- package/examples/system-test.di +13 -0
- package/examples/task-assistant.di +27 -0
- package/examples/test-parameters.di +50 -0
- package/examples/two-styles.di +28 -0
- package/examples/var-debug.di +6 -0
- package/examples/var-inline.di +4 -0
- package/examples/var-test2.di +6 -0
- package/examples/variable-simple.di +16 -0
- package/examples/variable-test.di +22 -0
- package/filePath +1 -0
- package/greeting.txt +1 -0
- package/package.json +41 -0
- package/src/cli.ts +118 -0
- package/src/index.ts +33 -0
- package/src/llm/ollama.ts +58 -0
- package/src/runtime/braket-parser.ts +234 -0
- package/src/runtime/interpreter.ts +135 -0
- package/src/runtime/parser.ts +151 -0
- package/src/runtime/session.ts +228 -0
- package/src/tags/assign.ts +37 -0
- package/src/tags/call.ts +156 -0
- package/src/tags/defvar.ts +56 -0
- package/src/tags/eval.ts +68 -0
- package/src/tags/execute.ts +52 -0
- package/src/tags/expr.ts +128 -0
- package/src/tags/if.ts +58 -0
- package/src/tags/import.ts +66 -0
- package/src/tags/index.ts +37 -0
- package/src/tags/llm.ts +207 -0
- package/src/tags/loop.ts +43 -0
- package/src/tags/mongodb.ts +70 -0
- package/src/tags/output.ts +23 -0
- package/src/tags/parameters.ts +79 -0
- package/src/tags/require_module.ts +19 -0
- package/src/tags/subroutine.ts +52 -0
- package/src/tags/system.ts +70 -0
- package/src/tags/variable.ts +25 -0
- package/src/types/index.ts +101 -0
- package/src/utils/llm-adapter.ts +113 -0
- package/tools/create-library.sh +175 -0
- package/tsconfig.json +19 -0
package/src/tags/expr.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <expr> tag - arithmetic and logical operations
|
|
3
|
+
* Maps to mask_tag_expr in MASK
|
|
4
|
+
*
|
|
5
|
+
* Supports: plus, minus, times, divide, mod, lt, gt, eq, and, or, not
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
9
|
+
import { emit, substituteVariables } from '../runtime/session.js';
|
|
10
|
+
import { integrate } from '../runtime/interpreter.js';
|
|
11
|
+
|
|
12
|
+
export async function executeExpr(session: DiracSession, element: DiracElement): Promise<void> {
|
|
13
|
+
const op = element.attributes.eval || element.attributes.op;
|
|
14
|
+
|
|
15
|
+
if (!op) {
|
|
16
|
+
throw new Error('<expr> requires eval or op attribute');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Collect arguments from <arg> children
|
|
20
|
+
const args: number[] = [];
|
|
21
|
+
const stringArgs: string[] = [];
|
|
22
|
+
|
|
23
|
+
for (const child of element.children) {
|
|
24
|
+
if (child.tag === 'arg') {
|
|
25
|
+
let argValue = '';
|
|
26
|
+
|
|
27
|
+
// Check if arg has text content directly
|
|
28
|
+
if (child.text) {
|
|
29
|
+
argValue = substituteVariables(session, child.text);
|
|
30
|
+
} else {
|
|
31
|
+
// Capture output from arg children
|
|
32
|
+
const oldOutput = session.output;
|
|
33
|
+
session.output = [];
|
|
34
|
+
|
|
35
|
+
for (const argChild of child.children) {
|
|
36
|
+
await integrate(session, argChild);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
argValue = session.output.join('');
|
|
40
|
+
session.output = oldOutput;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
stringArgs.push(argValue);
|
|
44
|
+
const numValue = parseFloat(argValue);
|
|
45
|
+
args.push(isNaN(numValue) ? 0 : numValue);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let result: number | boolean = 0;
|
|
50
|
+
|
|
51
|
+
switch (op.toLowerCase()) {
|
|
52
|
+
case 'plus':
|
|
53
|
+
case 'add':
|
|
54
|
+
result = args.reduce((a, b) => a + b, 0);
|
|
55
|
+
break;
|
|
56
|
+
|
|
57
|
+
case 'minus':
|
|
58
|
+
case 'subtract':
|
|
59
|
+
result = args.length > 0 ? args[0] - args.slice(1).reduce((a, b) => a + b, 0) : 0;
|
|
60
|
+
break;
|
|
61
|
+
|
|
62
|
+
case 'times':
|
|
63
|
+
case 'multiply':
|
|
64
|
+
case 'mul':
|
|
65
|
+
result = args.reduce((a, b) => a * b, 1);
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
case 'divide':
|
|
69
|
+
case 'div':
|
|
70
|
+
if (args.length >= 2 && args[1] !== 0) {
|
|
71
|
+
result = args[0] / args[1];
|
|
72
|
+
} else {
|
|
73
|
+
result = 0;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'mod':
|
|
78
|
+
case 'modulo':
|
|
79
|
+
if (args.length >= 2 && args[1] !== 0) {
|
|
80
|
+
result = args[0] % args[1];
|
|
81
|
+
} else {
|
|
82
|
+
result = 0;
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
case 'lt':
|
|
87
|
+
case 'less':
|
|
88
|
+
result = args.length >= 2 ? args[0] < args[1] : false;
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case 'gt':
|
|
92
|
+
case 'greater':
|
|
93
|
+
result = args.length >= 2 ? args[0] > args[1] : false;
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case 'eq':
|
|
97
|
+
case 'equal':
|
|
98
|
+
result = args.length >= 2 ? args[0] === args[1] : false;
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case 'and':
|
|
102
|
+
result = args.every(a => a !== 0);
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case 'or':
|
|
106
|
+
result = args.some(a => a !== 0);
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'not':
|
|
110
|
+
result = args.length > 0 ? args[0] === 0 : true;
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
case 'same':
|
|
114
|
+
case 'strcmp':
|
|
115
|
+
result = stringArgs.length >= 2 ? stringArgs[0] === stringArgs[1] : false;
|
|
116
|
+
break;
|
|
117
|
+
|
|
118
|
+
default:
|
|
119
|
+
throw new Error(`<expr> unknown operation: ${op}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Emit result
|
|
123
|
+
if (typeof result === 'boolean') {
|
|
124
|
+
emit(session, result ? '1' : '0');
|
|
125
|
+
} else {
|
|
126
|
+
emit(session, String(result));
|
|
127
|
+
}
|
|
128
|
+
}
|
package/src/tags/if.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <if> tag - conditional execution
|
|
3
|
+
* Maps to mask_tag_if in MASK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { substituteVariables, getVariable } from '../runtime/session.js';
|
|
8
|
+
import { integrateChildren } from '../runtime/interpreter.js';
|
|
9
|
+
|
|
10
|
+
export async function executeIf(session: DiracSession, element: DiracElement): Promise<void> {
|
|
11
|
+
const test = element.attributes.test;
|
|
12
|
+
|
|
13
|
+
if (!test) {
|
|
14
|
+
throw new Error('<if> requires test attribute');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const condition = evaluateCondition(session, test);
|
|
18
|
+
|
|
19
|
+
if (condition) {
|
|
20
|
+
await integrateChildren(session, element);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function evaluateCondition(session: DiracSession, test: string): boolean {
|
|
25
|
+
// Substitute variables first
|
|
26
|
+
const substituted = substituteVariables(session, test);
|
|
27
|
+
|
|
28
|
+
// Simple condition evaluation (can be enhanced later)
|
|
29
|
+
// Supports: ==, !=, <, >, <=, >=
|
|
30
|
+
|
|
31
|
+
const operators = ['==', '!=', '<=', '>=', '<', '>'];
|
|
32
|
+
|
|
33
|
+
for (const op of operators) {
|
|
34
|
+
const parts = substituted.split(op);
|
|
35
|
+
if (parts.length === 2) {
|
|
36
|
+
const left = parts[0].trim();
|
|
37
|
+
const right = parts[1].trim();
|
|
38
|
+
|
|
39
|
+
switch (op) {
|
|
40
|
+
case '==':
|
|
41
|
+
return left === right;
|
|
42
|
+
case '!=':
|
|
43
|
+
return left !== right;
|
|
44
|
+
case '<':
|
|
45
|
+
return parseFloat(left) < parseFloat(right);
|
|
46
|
+
case '>':
|
|
47
|
+
return parseFloat(left) > parseFloat(right);
|
|
48
|
+
case '<=':
|
|
49
|
+
return parseFloat(left) <= parseFloat(right);
|
|
50
|
+
case '>=':
|
|
51
|
+
return parseFloat(left) >= parseFloat(right);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If no operator, treat as boolean (non-empty = true)
|
|
57
|
+
return substituted.trim() !== '' && substituted.trim() !== '0' && substituted.trim() !== 'false';
|
|
58
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <import> tag - Import subroutines from other Dirac files
|
|
3
|
+
* Similar to Node.js require/import
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { resolve, dirname } from 'path';
|
|
9
|
+
import { DiracParser } from '../runtime/parser.js';
|
|
10
|
+
import { integrate } from '../runtime/interpreter.js';
|
|
11
|
+
|
|
12
|
+
export async function executeImport(session: DiracSession, element: DiracElement): Promise<void> {
|
|
13
|
+
const src = element.attributes.src;
|
|
14
|
+
|
|
15
|
+
if (!src) {
|
|
16
|
+
throw new Error('<import> requires src attribute');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Get the current file's directory (if available in session)
|
|
20
|
+
const currentDir = session.currentFile ? dirname(session.currentFile) : process.cwd();
|
|
21
|
+
|
|
22
|
+
// Resolve the import path
|
|
23
|
+
const importPath = resolve(currentDir, src);
|
|
24
|
+
|
|
25
|
+
if (session.debug) {
|
|
26
|
+
console.error(`[IMPORT] Loading: ${importPath}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if already imported (prevent circular imports)
|
|
30
|
+
if (!session.importedFiles) {
|
|
31
|
+
session.importedFiles = new Set();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (session.importedFiles.has(importPath)) {
|
|
35
|
+
if (session.debug) {
|
|
36
|
+
console.error(`[IMPORT] Already imported: ${importPath}`);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
session.importedFiles.add(importPath);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Read and parse the imported file
|
|
45
|
+
const source = readFileSync(importPath, 'utf-8');
|
|
46
|
+
const parser = new DiracParser();
|
|
47
|
+
const ast = parser.parse(source);
|
|
48
|
+
|
|
49
|
+
// Save current file context and set new one
|
|
50
|
+
const previousFile = session.currentFile;
|
|
51
|
+
session.currentFile = importPath;
|
|
52
|
+
|
|
53
|
+
// Execute the imported file (this will register its subroutines)
|
|
54
|
+
await integrate(session, ast);
|
|
55
|
+
|
|
56
|
+
// Restore previous file context
|
|
57
|
+
session.currentFile = previousFile;
|
|
58
|
+
|
|
59
|
+
if (session.debug) {
|
|
60
|
+
console.error(`[IMPORT] Loaded: ${importPath}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new Error(`Import error: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as system from './system';
|
|
2
|
+
import * as execute from './execute';
|
|
3
|
+
import * as llm from './llm';
|
|
4
|
+
import * as variable from './variable';
|
|
5
|
+
import * as subroutine from './subroutine';
|
|
6
|
+
import * as assign from './assign';
|
|
7
|
+
import * as parameters from './parameters';
|
|
8
|
+
import * as loop from './loop';
|
|
9
|
+
import * as evalTag from './eval';
|
|
10
|
+
import * as call from './call';
|
|
11
|
+
import * as defvar from './defvar';
|
|
12
|
+
import * as output from './output';
|
|
13
|
+
import * as expr from './expr';
|
|
14
|
+
import * as ifTag from './if';
|
|
15
|
+
import * as importTag from './import';
|
|
16
|
+
import * as mongodb from './mongodb';
|
|
17
|
+
import * as requireModule from './require_module';
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
...system,
|
|
21
|
+
...execute,
|
|
22
|
+
...llm,
|
|
23
|
+
...variable,
|
|
24
|
+
...subroutine,
|
|
25
|
+
...assign,
|
|
26
|
+
...parameters,
|
|
27
|
+
...loop,
|
|
28
|
+
...evalTag,
|
|
29
|
+
...call,
|
|
30
|
+
...defvar,
|
|
31
|
+
...output,
|
|
32
|
+
...expr,
|
|
33
|
+
...ifTag,
|
|
34
|
+
...importTag,
|
|
35
|
+
...mongodb,
|
|
36
|
+
...requireModule,
|
|
37
|
+
};
|
package/src/tags/llm.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <LLM> tag - THE INNOVATION
|
|
3
|
+
* Execute LLM operation with recursive Dirac execution capability
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { setVariable, substituteVariables, emit, getVariable } from '../runtime/session.js';
|
|
8
|
+
import { integrate } from '../runtime/interpreter.js';
|
|
9
|
+
import { DiracParser } from '../runtime/parser.js';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export async function executeLLM(session: DiracSession, element: DiracElement): Promise<void> {
|
|
13
|
+
if (!session.llmClient) {
|
|
14
|
+
throw new Error('<LLM> requires API key (set OPENAI_API_KEY, ANTHROPIC_API_KEY, or LLM_PROVIDER=ollama in .env file)');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Check limits
|
|
18
|
+
if (session.limits.currentLLMCalls >= session.limits.maxLLMCalls) {
|
|
19
|
+
throw new Error('Maximum LLM calls exceeded');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
session.limits.currentLLMCalls++;
|
|
23
|
+
|
|
24
|
+
// Detect provider from client type
|
|
25
|
+
const providerName = session.llmClient.constructor.name;
|
|
26
|
+
const isOpenAI = providerName === 'OpenAI';
|
|
27
|
+
const isOllama = providerName === 'OllamaProvider';
|
|
28
|
+
const defaultModel = isOpenAI
|
|
29
|
+
? 'gpt-4.1-2025-04-14'
|
|
30
|
+
: isOllama
|
|
31
|
+
? 'llama2'
|
|
32
|
+
: 'claude-sonnet-4-20250514';
|
|
33
|
+
console.log('LLM Provider:', providerName);
|
|
34
|
+
|
|
35
|
+
const model = element.attributes.model || process.env.DEFAULT_MODEL || defaultModel;
|
|
36
|
+
console.log('LLM Model:', model);
|
|
37
|
+
const outputVar = element.attributes.output;
|
|
38
|
+
const contextVar = element.attributes.context;
|
|
39
|
+
const executeMode = element.attributes.execute === 'true'; // NEW: seamless execution mode
|
|
40
|
+
const temperature = parseFloat(element.attributes.temperature || '1.0');
|
|
41
|
+
const maxTokens = parseInt(element.attributes.maxTokens || '4096', 10);
|
|
42
|
+
|
|
43
|
+
// Build prompt from children or text
|
|
44
|
+
let userPrompt = '';
|
|
45
|
+
if (element.children.length > 0) {
|
|
46
|
+
// Execute children to build prompt
|
|
47
|
+
const beforeOutput = session.output.length;
|
|
48
|
+
for (const child of element.children) {
|
|
49
|
+
await integrate(session, child);
|
|
50
|
+
}
|
|
51
|
+
// Collect output from children
|
|
52
|
+
const childOutput = session.output.slice(beforeOutput);
|
|
53
|
+
userPrompt = childOutput.join('');
|
|
54
|
+
// Remove child output from main output
|
|
55
|
+
session.output = session.output.slice(0, beforeOutput);
|
|
56
|
+
} else if (element.text) {
|
|
57
|
+
userPrompt = substituteVariables(session, element.text);
|
|
58
|
+
} else {
|
|
59
|
+
throw new Error('<LLM> requires prompt content');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const noExtra = element.attributes.noextra === 'true';
|
|
63
|
+
let prompt: string;
|
|
64
|
+
if (noExtra) {
|
|
65
|
+
prompt = userPrompt;
|
|
66
|
+
if (session.debug || process.env.DIRAC_LOG_PROMPT === '1') {
|
|
67
|
+
console.error('[LLM] Full prompt sent to LLM (noextra):\n' + prompt + '\n');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
// Reflect subroutines for system prompt
|
|
71
|
+
const { getAvailableSubroutines } = await import('../runtime/session.js');
|
|
72
|
+
const subroutines = getAvailableSubroutines(session);
|
|
73
|
+
if (session.debug) {
|
|
74
|
+
console.error('[LLM] Subroutines available at prompt composition:',
|
|
75
|
+
subroutines.map(s => ({ name: s.name, description: s.description, parameters: s.parameters })));
|
|
76
|
+
}
|
|
77
|
+
let systemPrompt = `Dirac is a XML based language, you define the subroutine like
|
|
78
|
+
\`\`\`xml
|
|
79
|
+
<subroutine name=background >
|
|
80
|
+
<parameters select="@color" />
|
|
81
|
+
<paint_the_color_somewhere />
|
|
82
|
+
</subroutine>
|
|
83
|
+
\`\`\`
|
|
84
|
+
then you call it like
|
|
85
|
+
\`\`\`xml
|
|
86
|
+
<background color="blue" />
|
|
87
|
+
\`\`\`
|
|
88
|
+
`;
|
|
89
|
+
systemPrompt += 'Now, You are an expert Dirac XML code generator.\nAllowed Dirac XML tags (use ONLY these tags):';
|
|
90
|
+
for (const sub of subroutines) {
|
|
91
|
+
systemPrompt += `\n- <${sub.name} />: ${sub.description || ''}`;
|
|
92
|
+
if (sub.parameters && sub.parameters.length > 0) {
|
|
93
|
+
systemPrompt += ' Parameters: ' + sub.parameters.map(p => `${p.name} (${p.type || 'string'})`).join(', ');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
systemPrompt += '\nDo NOT invent or use any tags not listed above. For example, do NOT use <changeBackground> or <set-background>. Only use the allowed tags.\nInstructions: Output only valid Dirac XML tags from the list above. Do not include explanations or extra text.';
|
|
97
|
+
systemPrompt += '\nAfter generating your answer, check the command/tag list again and ensure every tag you use is in the list above. If any tag is not in the list, do not output it—regenerate your answer using only allowed tags.';
|
|
98
|
+
|
|
99
|
+
prompt = systemPrompt + '\nUser: ' + userPrompt + '\nOutput:';
|
|
100
|
+
if (session.debug || process.env.DIRAC_LOG_PROMPT === '1') {
|
|
101
|
+
console.error('[LLM] Full prompt sent to LLM:\n' + prompt + '\n');
|
|
102
|
+
}
|
|
103
|
+
// Add context if specified
|
|
104
|
+
if (contextVar) {
|
|
105
|
+
const contextValue = getVariable(session, contextVar);
|
|
106
|
+
if (contextValue) {
|
|
107
|
+
prompt = `Context: ${contextValue}\n\n${prompt}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (session.debug) {
|
|
113
|
+
console.error(`[LLM] Calling ${model} with prompt length: ${prompt.length}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
let result: string;
|
|
118
|
+
if (isOpenAI) {
|
|
119
|
+
// Call OpenAI API
|
|
120
|
+
const response = await session.llmClient.chat.completions.create({
|
|
121
|
+
model,
|
|
122
|
+
max_tokens: maxTokens,
|
|
123
|
+
temperature,
|
|
124
|
+
messages: [
|
|
125
|
+
{
|
|
126
|
+
role: 'user',
|
|
127
|
+
content: prompt,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
result = response.choices[0]?.message?.content || '';
|
|
132
|
+
} else if (isOllama) {
|
|
133
|
+
// Call OllamaProvider
|
|
134
|
+
result = await session.llmClient.complete(prompt, {
|
|
135
|
+
model,
|
|
136
|
+
temperature,
|
|
137
|
+
max_tokens: maxTokens,
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
// Call Anthropic API
|
|
141
|
+
const response = await session.llmClient.messages.create({
|
|
142
|
+
model,
|
|
143
|
+
max_tokens: maxTokens,
|
|
144
|
+
temperature,
|
|
145
|
+
messages: [
|
|
146
|
+
{
|
|
147
|
+
role: 'user',
|
|
148
|
+
content: prompt,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
});
|
|
152
|
+
const content = response.content[0];
|
|
153
|
+
result = content.type === 'text' ? content.text : '';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (session.debug) {
|
|
157
|
+
console.error(`[LLM] Response length: ${result.length}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Store in variable if requested
|
|
161
|
+
if (outputVar) {
|
|
162
|
+
setVariable(session, outputVar, result, false);
|
|
163
|
+
} else if (executeMode) {
|
|
164
|
+
// NEW: Execute mode - parse and interpret LLM response as Dirac code
|
|
165
|
+
if (session.debug) {
|
|
166
|
+
console.error(`[LLM] Executing response as Dirac code:\n${result}\n`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Only replace triple backtick code blocks if replace-tick="true" is set
|
|
170
|
+
const replaceTick = element.attributes['replace-tick'] === 'true';
|
|
171
|
+
let diracCode = result.trim();
|
|
172
|
+
if (replaceTick && diracCode.startsWith('```')) {
|
|
173
|
+
// Check for bash, xml, html, dirac, or no language
|
|
174
|
+
const match = diracCode.match(/^```(\w+)?\n?/m);
|
|
175
|
+
if (match && match[1] === 'bash') {
|
|
176
|
+
// Find closing triple backticks
|
|
177
|
+
const endIdx = diracCode.indexOf('```', 3);
|
|
178
|
+
let bashContent = diracCode.slice(match[0].length, endIdx).trim();
|
|
179
|
+
diracCode = `<system>${bashContent}</system>`;
|
|
180
|
+
} else {
|
|
181
|
+
// Remove opening and closing backticks for xml/html/dirac/none
|
|
182
|
+
diracCode = diracCode.replace(/^```(?:xml|html|dirac)?\n?/m, '').replace(/\n?```$/m, '').trim();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
// Parse and execute the LLM's output as Dirac code
|
|
187
|
+
const parser = new DiracParser();
|
|
188
|
+
const dynamicAST = parser.parse(diracCode);
|
|
189
|
+
await integrate(session, dynamicAST);
|
|
190
|
+
} catch (parseError) {
|
|
191
|
+
// If parsing fails, treat as plain text
|
|
192
|
+
if (session.debug) {
|
|
193
|
+
console.error(`[LLM] Failed to parse as Dirac, treating as text: ${parseError}`);
|
|
194
|
+
}
|
|
195
|
+
emit(session, result);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
// Otherwise emit to output as text
|
|
199
|
+
emit(session, result);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
} catch (error) {
|
|
203
|
+
throw new Error(`LLM error: ${error instanceof Error ? error.message : String(error)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
package/src/tags/loop.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <loop> tag - iteration
|
|
3
|
+
* Maps to mask_tag_loop in MASK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { setVariable, substituteVariables, getVariable } from '../runtime/session.js';
|
|
8
|
+
import { integrateChildren } from '../runtime/interpreter.js';
|
|
9
|
+
|
|
10
|
+
export async function executeLoop(session: DiracSession, element: DiracElement): Promise<void> {
|
|
11
|
+
const countAttr = element.attributes.count;
|
|
12
|
+
const varName = element.attributes.var || 'i';
|
|
13
|
+
|
|
14
|
+
if (!countAttr) {
|
|
15
|
+
throw new Error('<loop> requires count attribute');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const count = parseInt(substituteVariables(session, countAttr), 10);
|
|
19
|
+
|
|
20
|
+
if (isNaN(count) || count < 0) {
|
|
21
|
+
throw new Error(`Invalid loop count: ${countAttr}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const wasBreak = session.isBreak;
|
|
25
|
+
session.isBreak = false;
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < count; i++) {
|
|
28
|
+
setVariable(session, varName, i, false);
|
|
29
|
+
|
|
30
|
+
await integrateChildren(session, element);
|
|
31
|
+
|
|
32
|
+
if (session.isBreak) {
|
|
33
|
+
session.isBreak = false;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (session.isReturn) {
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
session.isBreak = wasBreak;
|
|
43
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { TagHandler, TagHandlerContext } from '../types';
|
|
2
|
+
|
|
3
|
+
console.log('[mongodb tag] mongodb.ts loaded');
|
|
4
|
+
import { MongoClient, Db, Collection } from 'mongodb';
|
|
5
|
+
|
|
6
|
+
// Utility to expand variables/child tags as Dirac does
|
|
7
|
+
async function expandContent(ctx: TagHandlerContext): Promise<any> {
|
|
8
|
+
if (ctx.body) {
|
|
9
|
+
// If body is present, expand it (could be JSON, array, or object)
|
|
10
|
+
const expanded = await ctx.evaluate(ctx.body);
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(expanded);
|
|
13
|
+
} catch {
|
|
14
|
+
return expanded;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const mongodb: TagHandler = async (ctx: TagHandlerContext) => {
|
|
21
|
+
const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017';
|
|
22
|
+
const database = ctx.getAttr('database');
|
|
23
|
+
const collection = ctx.getAttr('collection');
|
|
24
|
+
const action = ctx.getAttr('action');
|
|
25
|
+
const client = new MongoClient(uri);
|
|
26
|
+
let result;
|
|
27
|
+
console.log('[mongodb tag] Connecting to:', uri);
|
|
28
|
+
console.log('[mongodb tag] Database:', database, 'Collection:', collection, 'Action:', action);
|
|
29
|
+
try {
|
|
30
|
+
await client.connect();
|
|
31
|
+
let db: Db | undefined = database ? client.db(database) : undefined;
|
|
32
|
+
let col: Collection | undefined = db && collection ? db.collection(collection) : undefined;
|
|
33
|
+
const content = await expandContent(ctx);
|
|
34
|
+
console.log('[mongodb tag] Query content:', content);
|
|
35
|
+
switch (action) {
|
|
36
|
+
case 'find':
|
|
37
|
+
if (!col) throw new Error('Collection required for find');
|
|
38
|
+
result = await col.find(content || {}).toArray();
|
|
39
|
+
console.log('[mongodb tag] Find result:', result);
|
|
40
|
+
break;
|
|
41
|
+
case 'aggregate':
|
|
42
|
+
if (!col) throw new Error('Collection required for aggregate');
|
|
43
|
+
result = await col.aggregate(content).toArray();
|
|
44
|
+
console.log('[mongodb tag] Aggregate result:', result);
|
|
45
|
+
break;
|
|
46
|
+
case 'insert':
|
|
47
|
+
if (!col) throw new Error('Collection required for insert');
|
|
48
|
+
result = await col.insertOne(content);
|
|
49
|
+
console.log('[mongodb tag] Insert result:', result);
|
|
50
|
+
break;
|
|
51
|
+
case 'createDb':
|
|
52
|
+
if (!db) throw new Error('Database required for createDb');
|
|
53
|
+
result = { db: database, created: true };
|
|
54
|
+
console.log('[mongodb tag] CreateDb result:', result);
|
|
55
|
+
break;
|
|
56
|
+
case 'createCollection':
|
|
57
|
+
if (!db || !collection) throw new Error('Database and collection required');
|
|
58
|
+
result = await db.createCollection(collection);
|
|
59
|
+
console.log('[mongodb tag] CreateCollection result:', result);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
throw new Error('Unknown action for <mongodb>');
|
|
63
|
+
}
|
|
64
|
+
} finally {
|
|
65
|
+
await client.close();
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default mongodb;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <output> tag - emit content
|
|
3
|
+
* Maps to mask_tag_output in MASK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
7
|
+
import { emit, substituteVariables } from '../runtime/session.js';
|
|
8
|
+
import { integrateChildren } from '../runtime/interpreter.js';
|
|
9
|
+
|
|
10
|
+
export async function executeOutput(session: DiracSession, element: DiracElement): Promise<void> {
|
|
11
|
+
// If has children, process them (handles mixed content)
|
|
12
|
+
if (element.children && element.children.length > 0) {
|
|
13
|
+
await integrateChildren(session, element);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// If only text content, use it (with variable substitution)
|
|
18
|
+
if (element.text) {
|
|
19
|
+
const content = substituteVariables(session, element.text);
|
|
20
|
+
emit(session, content);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|