dirac-lang 0.1.24 → 0.1.26
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/cli.js +13 -1
- package/lib/index.di +9 -0
- package/{examples/lib → lib}/math.di +3 -3
- package/package.json +13 -1
- package/.env.example +0 -8
- package/COMMUNITY.md +0 -465
- package/CONDITIONAL-TAGS.md +0 -172
- package/EXCEPTION-HANDLING.md +0 -156
- package/LIBRARIES.md +0 -172
- package/LLM-VALIDATION.md +0 -128
- package/NAMESPACES.md +0 -366
- package/PROMOTION.md +0 -257
- package/QUICKSTART-LIBRARY.md +0 -93
- package/TEST-COVERAGE.md +0 -113
- package/TESTING.md +0 -162
- package/config.test.yml +0 -5
- package/dirac-http/examples/demo.di +0 -9
- package/dirac-http/lib/index.di +0 -12
- package/examples/add-demo.di +0 -74
- package/examples/add.bk +0 -11
- package/examples/advanced-math-demo.di +0 -53
- package/examples/calculator.di +0 -32
- package/examples/compact-test.di +0 -6
- package/examples/comprehensive.bk +0 -29
- package/examples/defvar-variable-demo.di +0 -18
- package/examples/direct-call.di +0 -17
- package/examples/disk-analysis.di +0 -16
- package/examples/exception-demo.di +0 -82
- package/examples/executable-hello.di +0 -7
- package/examples/execute-demo.di +0 -38
- package/examples/file-manager.di +0 -77
- package/examples/file-stats.di +0 -18
- package/examples/hello.bk +0 -1
- package/examples/hello.di +0 -5
- package/examples/if-comparison.di +0 -91
- package/examples/if-cstyle-test.di +0 -149
- package/examples/if-vs-testif.di +0 -56
- package/examples/import-demo.di +0 -31
- package/examples/inline-test.bk +0 -7
- package/examples/llm-agent.di +0 -32
- package/examples/llm-basic.di +0 -12
- package/examples/llm-command-more.di +0 -6
- package/examples/llm-command-no-exec.di +0 -13
- package/examples/llm-command.di +0 -6
- package/examples/llm-complex.di +0 -141
- package/examples/llm-feedback-debug.di +0 -30
- package/examples/llm-feedback-demo.di +0 -19
- package/examples/llm-feedback-math.di +0 -22
- package/examples/llm-feedback-simple.di +0 -16
- package/examples/llm-feedback-sub.di +0 -22
- package/examples/llm-no-feedback.di +0 -10
- package/examples/llm-recursive.di +0 -31
- package/examples/llm-reflection-test.di +0 -19
- package/examples/llm-simple-test.di +0 -12
- package/examples/llm-subs.di +0 -132
- package/examples/llm-use-subs.di +0 -6
- package/examples/llm-validate-test.di +0 -18
- package/examples/loop.di +0 -12
- package/examples/math-test.di +0 -22
- package/examples/minimal-test.di +0 -13
- package/examples/mongodb-context-test.di +0 -27
- package/examples/mongodb-count-events.di +0 -8
- package/examples/mongodb-import-demo.di +0 -25
- package/examples/mongodb-simple-test.di +0 -18
- package/examples/nl-agent.di +0 -47
- package/examples/parameters-demo.di +0 -68
- package/examples/params-meta-test.di +0 -17
- package/examples/params-test.di +0 -10
- package/examples/recipe-chain.di +0 -38
- package/examples/recursive-llm.di +0 -44
- package/examples/sample-library/README.md +0 -152
- package/examples/sample-library/examples/demo.di +0 -34
- package/examples/sample-library/lib/index.di +0 -65
- package/examples/sample-library/package.json +0 -31
- package/examples/scope-test-nested.di +0 -60
- package/examples/scope-test.di +0 -55
- package/examples/seamless.di +0 -45
- package/examples/shell-test.bk +0 -10
- package/examples/simple-import.di +0 -13
- package/examples/simple-recursive.di +0 -26
- package/examples/story-builder.di +0 -45
- package/examples/subroutine.di +0 -23
- package/examples/system-llm.di +0 -21
- package/examples/system-simple.di +0 -3
- package/examples/system-test.di +0 -8
- package/examples/tag-check-test.di +0 -139
- package/examples/task-assistant.di +0 -27
- package/examples/test-if-demo.di +0 -110
- package/examples/test-parameters.di +0 -50
- package/examples/try-catch-test.di +0 -118
- package/examples/two-styles.di +0 -28
- package/examples/var-debug.di +0 -6
- package/examples/var-inline.di +0 -4
- package/examples/var-test2.di +0 -6
- package/examples/variable-replace.di +0 -25
- package/examples/variable-simple.di +0 -16
- package/examples/variable-test.di +0 -22
- package/examples/whitespace-test.di +0 -24
- package/filePath +0 -1
- package/greeting.txt +0 -1
- package/src/cli.ts +0 -140
- package/src/index.ts +0 -33
- package/src/llm/ollama.ts +0 -58
- package/src/runtime/braket-parser.ts +0 -234
- package/src/runtime/interpreter.ts +0 -203
- package/src/runtime/parser.ts +0 -155
- package/src/runtime/session.ts +0 -325
- package/src/tags/assign.ts +0 -37
- package/src/tags/attr.ts +0 -64
- package/src/tags/available-subroutines.ts +0 -70
- package/src/tags/call.ts +0 -259
- package/src/tags/catch.ts +0 -24
- package/src/tags/defvar.ts +0 -115
- package/src/tags/environment.ts +0 -21
- package/src/tags/eval.ts +0 -71
- package/src/tags/exception.ts +0 -20
- package/src/tags/execute.ts +0 -52
- package/src/tags/expr.ts +0 -128
- package/src/tags/foreach.ts +0 -170
- package/src/tags/if.ts +0 -191
- package/src/tags/import.ts +0 -135
- package/src/tags/index.ts +0 -41
- package/src/tags/input.ts +0 -182
- package/src/tags/llm.ts +0 -415
- package/src/tags/loop.ts +0 -43
- package/src/tags/mongodb.ts +0 -70
- package/src/tags/output.ts +0 -53
- package/src/tags/parameters.ts +0 -81
- package/src/tags/require_module.ts +0 -19
- package/src/tags/subroutine.ts +0 -75
- package/src/tags/system.ts +0 -93
- package/src/tags/tag-check.ts +0 -176
- package/src/tags/test-if.ts +0 -112
- package/src/tags/throw.ts +0 -26
- package/src/tags/try.ts +0 -19
- package/src/tags/variable.ts +0 -25
- package/src/test-runner.ts +0 -300
- package/src/types/index.ts +0 -128
- package/src/utils/llm-adapter.ts +0 -113
- package/src/utils/tag-validator.ts +0 -231
- package/test-available-extends.di +0 -28
- package/test-available-simple.di +0 -12
- package/test-available-subroutines.di +0 -16
- package/test-call.di +0 -9
- package/test-extend-basic.di +0 -17
- package/test-extend-chain.di +0 -26
- package/test-extend-debug.di +0 -17
- package/test-extend-debug2.di +0 -15
- package/test-extend-person.di +0 -17
- package/test-extend-simple.di +0 -11
- package/test-extend-zhi.di +0 -23
- package/test-extend.di +0 -19
- package/test-extend2.di +0 -26
- package/test-extend3-fixed.di +0 -23
- package/test-extend3-trouble.di +0 -23
- package/test-extend3.di +0 -21
- package/test-factorial.di +0 -19
- package/test-input-debug.di +0 -19
- package/test-nested-debug.di +0 -7
- package/test-nested-debug2.di +0 -8
- package/test-no-extend.di +0 -16
- package/test-simple-call.di +0 -7
- package/test-simple.di +0 -6
- package/tests/README.md +0 -69
- package/tests/assign-basic.test.di +0 -7
- package/tests/assign-from-eval.test.di +0 -7
- package/tests/available-subroutines-foreach.test.di +0 -32
- package/tests/basic-output.test.di +0 -5
- package/tests/call-basic.test.di +0 -11
- package/tests/call-with-output.test.di +0 -10
- package/tests/comments-before-content.test.di +0 -6
- package/tests/defvar-multiple.test.di +0 -8
- package/tests/environment-basic.test.di +0 -22
- package/tests/environment-nonexistent.test.di +0 -5
- package/tests/environment-with-defvar.test.di +0 -15
- package/tests/eval-basic.test.di +0 -6
- package/tests/eval-with-variables.test.di +0 -8
- package/tests/exception-basic.test.di +0 -10
- package/tests/expr-basic.test.di +0 -11
- package/tests/extend-basic.test.di +0 -19
- package/tests/extend-inheritance-chain.test.di +0 -26
- package/tests/extend-multiple-nested.test.di +0 -24
- package/tests/extend-self.test.di +0 -19
- package/tests/extend-with-parameters.test.di +0 -14
- package/tests/generate-dirac.test.di +0 -22
- package/tests/if-conditional.test.di +0 -17
- package/tests/if-else.test.di +0 -11
- package/tests/if-with-variables.test.di +0 -8
- package/tests/import-basic.test.di +0 -6
- package/tests/input-file-all.test.di +0 -5
- package/tests/input-file-line.test.di +0 -15
- package/tests/input-file-loop.test.di +0 -12
- package/tests/input-stdin-all.test.di +0 -8
- package/tests/input-stdin-all.txt +0 -1
- package/tests/llm-validate.test.di +0 -17
- package/tests/loop-basic.test.di +0 -9
- package/tests/loop-nested.test.di +0 -10
- package/tests/loop-with-eval.test.di +0 -8
- package/tests/no-root-element.test.di +0 -6
- package/tests/output-file.test.di +0 -19
- package/tests/output-multiple.test.di +0 -9
- package/tests/parameters-basic.test.di +0 -6
- package/tests/require-module-basic.test.di +0 -7
- package/tests/subroutine-basic.test.di +0 -9
- package/tests/subroutine-multiple-params.test.di +0 -10
- package/tests/subroutine-nested-calls.test.di +0 -32
- package/tests/subroutine-sequential-calls.test.di +0 -36
- package/tests/system-background.test.di +0 -39
- package/tests/system-basic.test.di +0 -5
- package/tests/tag-check.test.di +0 -27
- package/tests/test-if-basic.test.di +0 -8
- package/tests/test-input.txt +0 -3
- package/tests/try-catch-basic.test.di +0 -10
- package/tests/try-catch-eval-error.test.di +0 -10
- package/tests/variable-basic.test.di +0 -6
- package/tests/variable-interpolation.test.di +0 -7
- package/tools/create-library.sh +0 -175
- package/tsconfig.json +0 -19
- /package/{examples/lib → lib}/advanced-math.di +0 -0
- /package/{examples/lib → lib}/fileops.di +0 -0
- /package/{examples/lib → lib}/mongodb.di +0 -0
package/src/tags/index.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
import * as environment from './environment';
|
|
19
|
-
import * as input from './input';
|
|
20
|
-
|
|
21
|
-
export default {
|
|
22
|
-
...system,
|
|
23
|
-
...execute,
|
|
24
|
-
...llm,
|
|
25
|
-
...variable,
|
|
26
|
-
...subroutine,
|
|
27
|
-
...assign,
|
|
28
|
-
...parameters,
|
|
29
|
-
...loop,
|
|
30
|
-
...evalTag,
|
|
31
|
-
...call,
|
|
32
|
-
...defvar,
|
|
33
|
-
...output,
|
|
34
|
-
...expr,
|
|
35
|
-
...ifTag,
|
|
36
|
-
...importTag,
|
|
37
|
-
...mongodb,
|
|
38
|
-
...requireModule,
|
|
39
|
-
...environment,
|
|
40
|
-
...input,
|
|
41
|
-
};
|
package/src/tags/input.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* <input> tag - read from stdin or file
|
|
3
|
-
* Usage:
|
|
4
|
-
* <input source="stdin" mode="all" />
|
|
5
|
-
* <input source="stdin" mode="line" />
|
|
6
|
-
* <input source="file" path="file.txt" mode="all" />
|
|
7
|
-
* <input source="file" path="file.txt" mode="line" />
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { DiracSession, DiracElement } from '../types/index.js';
|
|
11
|
-
import { emit, substituteAttribute } from '../runtime/session.js';
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import * as readline from 'readline';
|
|
14
|
-
|
|
15
|
-
// Store file readers for line-by-line reading
|
|
16
|
-
const fileReaders = new Map<string, readline.Interface>();
|
|
17
|
-
const fileIterators = new Map<string, AsyncIterator<string>>();
|
|
18
|
-
|
|
19
|
-
export async function executeInput(session: DiracSession, element: DiracElement): Promise<void> {
|
|
20
|
-
const source = element.attributes.source;
|
|
21
|
-
const mode = element.attributes.mode || 'all';
|
|
22
|
-
const pathAttr = element.attributes.path;
|
|
23
|
-
|
|
24
|
-
if (!source) {
|
|
25
|
-
throw new Error('<input> requires source attribute (stdin or file)');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (source === 'file' && !pathAttr) {
|
|
29
|
-
throw new Error('<input source="file"> requires path attribute');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let value = '';
|
|
33
|
-
|
|
34
|
-
if (source === 'stdin') {
|
|
35
|
-
if (mode === 'all') {
|
|
36
|
-
// Read all stdin at once
|
|
37
|
-
value = await readAllStdin();
|
|
38
|
-
} else if (mode === 'line') {
|
|
39
|
-
// Read one line from stdin
|
|
40
|
-
value = await readLineStdin();
|
|
41
|
-
} else {
|
|
42
|
-
throw new Error(`<input> invalid mode: ${mode}. Use 'all' or 'line'`);
|
|
43
|
-
}
|
|
44
|
-
} else if (source === 'file') {
|
|
45
|
-
const path = substituteAttribute(session, pathAttr);
|
|
46
|
-
|
|
47
|
-
if (mode === 'all') {
|
|
48
|
-
// Read entire file
|
|
49
|
-
value = fs.readFileSync(path, 'utf-8');
|
|
50
|
-
} else if (mode === 'line') {
|
|
51
|
-
// Read one line from file
|
|
52
|
-
value = await readLineFromFile(path);
|
|
53
|
-
} else {
|
|
54
|
-
throw new Error(`<input> invalid mode: ${mode}. Use 'all' or 'line'`);
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
throw new Error(`<input> invalid source: ${source}. Use 'stdin' or 'file'`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Emit the value to output
|
|
61
|
-
emit(session, value);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Read all stdin content at once
|
|
66
|
-
*/
|
|
67
|
-
async function readAllStdin(): Promise<string> {
|
|
68
|
-
// Remove any existing listeners to avoid conflicts
|
|
69
|
-
process.stdin.removeAllListeners('data');
|
|
70
|
-
process.stdin.removeAllListeners('end');
|
|
71
|
-
process.stdin.removeAllListeners('error');
|
|
72
|
-
|
|
73
|
-
return new Promise((resolve, reject) => {
|
|
74
|
-
const chunks: Buffer[] = [];
|
|
75
|
-
|
|
76
|
-
const onData = (chunk: Buffer) => {
|
|
77
|
-
chunks.push(chunk);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const onEnd = () => {
|
|
81
|
-
const result = Buffer.concat(chunks).toString('utf-8');
|
|
82
|
-
|
|
83
|
-
// Clean up listeners
|
|
84
|
-
process.stdin.removeListener('data', onData);
|
|
85
|
-
process.stdin.removeListener('end', onEnd);
|
|
86
|
-
process.stdin.removeListener('error', onError);
|
|
87
|
-
|
|
88
|
-
resolve(result);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const onError = (err: Error) => {
|
|
92
|
-
// Clean up listeners
|
|
93
|
-
process.stdin.removeListener('data', onData);
|
|
94
|
-
process.stdin.removeListener('end', onEnd);
|
|
95
|
-
process.stdin.removeListener('error', onError);
|
|
96
|
-
|
|
97
|
-
reject(err);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
process.stdin.on('data', onData);
|
|
101
|
-
process.stdin.on('end', onEnd);
|
|
102
|
-
process.stdin.on('error', onError);
|
|
103
|
-
|
|
104
|
-
process.stdin.resume();
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Read one line from stdin
|
|
110
|
-
*/
|
|
111
|
-
let stdinReader: readline.Interface | null = null;
|
|
112
|
-
let stdinIterator: AsyncIterator<string> | null = null;
|
|
113
|
-
|
|
114
|
-
async function readLineStdin(): Promise<string> {
|
|
115
|
-
if (!stdinReader) {
|
|
116
|
-
stdinReader = readline.createInterface({
|
|
117
|
-
input: process.stdin,
|
|
118
|
-
output: process.stdout,
|
|
119
|
-
terminal: false
|
|
120
|
-
});
|
|
121
|
-
stdinIterator = stdinReader[Symbol.asyncIterator]();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const result = await stdinIterator!.next();
|
|
125
|
-
|
|
126
|
-
if (result.done) {
|
|
127
|
-
// EOF reached, close reader
|
|
128
|
-
stdinReader.close();
|
|
129
|
-
stdinReader = null;
|
|
130
|
-
stdinIterator = null;
|
|
131
|
-
return '';
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return result.value;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Read one line from a file
|
|
139
|
-
*/
|
|
140
|
-
async function readLineFromFile(path: string): Promise<string> {
|
|
141
|
-
// Create reader if it doesn't exist for this file
|
|
142
|
-
if (!fileReaders.has(path)) {
|
|
143
|
-
const fileStream = fs.createReadStream(path);
|
|
144
|
-
const rl = readline.createInterface({
|
|
145
|
-
input: fileStream,
|
|
146
|
-
crlfDelay: Infinity
|
|
147
|
-
});
|
|
148
|
-
fileReaders.set(path, rl);
|
|
149
|
-
fileIterators.set(path, rl[Symbol.asyncIterator]());
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const iterator = fileIterators.get(path)!;
|
|
153
|
-
const result = await iterator.next();
|
|
154
|
-
|
|
155
|
-
if (result.done) {
|
|
156
|
-
// EOF reached, close and cleanup
|
|
157
|
-
const reader = fileReaders.get(path)!;
|
|
158
|
-
reader.close();
|
|
159
|
-
fileReaders.delete(path);
|
|
160
|
-
fileIterators.delete(path);
|
|
161
|
-
return '';
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return result.value;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Clean up all open file readers (can be called at end of session)
|
|
169
|
-
*/
|
|
170
|
-
export function cleanupInputReaders(): void {
|
|
171
|
-
if (stdinReader) {
|
|
172
|
-
stdinReader.close();
|
|
173
|
-
stdinReader = null;
|
|
174
|
-
stdinIterator = null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
for (const reader of fileReaders.values()) {
|
|
178
|
-
reader.close();
|
|
179
|
-
}
|
|
180
|
-
fileReaders.clear();
|
|
181
|
-
fileIterators.clear();
|
|
182
|
-
}
|
package/src/tags/llm.ts
DELETED
|
@@ -1,415 +0,0 @@
|
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
const model = element.attributes.model || process.env.DEFAULT_MODEL || defaultModel;
|
|
36
|
-
|
|
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('').trim();
|
|
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).trim();
|
|
58
|
-
} else {
|
|
59
|
-
throw new Error('<LLM> requires prompt content');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// FIFO dialog history support
|
|
63
|
-
let dialogHistory = [];
|
|
64
|
-
if (contextVar) {
|
|
65
|
-
const existing = getVariable(session, contextVar);
|
|
66
|
-
if (Array.isArray(existing)) {
|
|
67
|
-
dialogHistory = [...existing];
|
|
68
|
-
} else if (existing) {
|
|
69
|
-
// If context is a string, treat as system message
|
|
70
|
-
dialogHistory = [{ role: 'system', content: String(existing) }];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const noExtra = element.attributes.noextra === 'true';
|
|
75
|
-
let prompt: string;
|
|
76
|
-
let systemPrompt = '';
|
|
77
|
-
if (noExtra) {
|
|
78
|
-
prompt = userPrompt;
|
|
79
|
-
if (session.debug || process.env.DIRAC_LOG_PROMPT === '1') {
|
|
80
|
-
console.error('[LLM] Full prompt sent to LLM (noextra):\n' + prompt + '\n');
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
// Reflect subroutines for system prompt
|
|
84
|
-
const { getAvailableSubroutines } = await import('../runtime/session.js');
|
|
85
|
-
const subroutines = getAvailableSubroutines(session);
|
|
86
|
-
if (session.debug) {
|
|
87
|
-
console.error('[LLM] Subroutines available at prompt composition:',
|
|
88
|
-
subroutines.map(s => ({ name: s.name, description: s.description, parameters: s.parameters })));
|
|
89
|
-
}
|
|
90
|
-
systemPrompt = `Dirac is a XML based language, you define the subroutine like
|
|
91
|
-
\`\`\`xml
|
|
92
|
-
<subroutine name=background param-color="string">
|
|
93
|
-
<paint_the_color_somewhere />
|
|
94
|
-
</subroutine>
|
|
95
|
-
\`\`\`
|
|
96
|
-
then you call it like
|
|
97
|
-
\`\`\`xml
|
|
98
|
-
<background color="blue" />
|
|
99
|
-
\`\`\`
|
|
100
|
-
`;
|
|
101
|
-
systemPrompt += 'Now, You are an expert Dirac XML code generator.\nAllowed Dirac XML tags (use ONLY these tags):';
|
|
102
|
-
for (const sub of subroutines) {
|
|
103
|
-
systemPrompt += `\n- ${sub.name} : ${sub.description || ''}`;
|
|
104
|
-
systemPrompt += `\n\tEx: <${sub.name}`;
|
|
105
|
-
if (sub.parameters && sub.parameters.length > 0) {
|
|
106
|
-
for (const p of sub.parameters) {
|
|
107
|
-
systemPrompt += ` ${p.name}="${p.example || 'string'}"`;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
let example = sub.meta?.body?.example || '';
|
|
111
|
-
example = example.replace(/"/g, '"').replace(/:/g, ':');
|
|
112
|
-
systemPrompt += '>'+example+'</' + sub.name + '>';
|
|
113
|
-
}
|
|
114
|
-
systemPrompt += '\n\nIMPORTANT INSTRUCTIONS:';
|
|
115
|
-
systemPrompt += '\n1. Output ONLY valid XML tags from the list above';
|
|
116
|
-
systemPrompt += '\n2. Do NOT include any explanations, descriptions, or extra text';
|
|
117
|
-
systemPrompt += '\n3. Do NOT use bullet points or formatting - just pure XML';
|
|
118
|
-
systemPrompt += '\n4. Do NOT invent tags - only use tags from the list above';
|
|
119
|
-
systemPrompt += '\n5. Start your response directly with the XML tag (e.g., <add ...>)';
|
|
120
|
-
systemPrompt += '\n\nDouble-check: Does your response contain ONLY XML tags? If not, remove all non-XML text.';
|
|
121
|
-
|
|
122
|
-
prompt = systemPrompt + '\nUser: ' + userPrompt + '\nOutput:';
|
|
123
|
-
if (session.debug || process.env.DIRAC_LOG_PROMPT === '1') {
|
|
124
|
-
console.error('[LLM] Full prompt sent to LLM:\n' + prompt + '\n');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Add the full prompt as a user message to dialogHistory
|
|
129
|
-
dialogHistory.push({ role: 'user', content: noExtra ? userPrompt : prompt });
|
|
130
|
-
|
|
131
|
-
if (session.debug) {
|
|
132
|
-
console.error(`[LLM] Calling ${model} with prompt length: ${prompt.length}`);
|
|
133
|
-
console.error(`[LLM] Dialog history length: ${dialogHistory.length} messages`);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
let result: string;
|
|
138
|
-
if (isOpenAI) {
|
|
139
|
-
// Call OpenAI API with full dialog history
|
|
140
|
-
const response = await session.llmClient.chat.completions.create({
|
|
141
|
-
model,
|
|
142
|
-
max_tokens: maxTokens,
|
|
143
|
-
temperature,
|
|
144
|
-
messages: dialogHistory,
|
|
145
|
-
});
|
|
146
|
-
result = response.choices[0]?.message?.content || '';
|
|
147
|
-
} else if (isOllama) {
|
|
148
|
-
// Call OllamaProvider with dialog history as joined string
|
|
149
|
-
const ollamaPrompt = dialogHistory.map(m => `${m.role.charAt(0).toUpperCase() + m.role.slice(1)}: ${m.content}`).join('\n');
|
|
150
|
-
result = await session.llmClient.complete(ollamaPrompt, {
|
|
151
|
-
model,
|
|
152
|
-
temperature,
|
|
153
|
-
max_tokens: maxTokens,
|
|
154
|
-
});
|
|
155
|
-
} else {
|
|
156
|
-
// Call Anthropic API with full dialog history
|
|
157
|
-
const response = await session.llmClient.messages.create({
|
|
158
|
-
model,
|
|
159
|
-
max_tokens: maxTokens,
|
|
160
|
-
temperature,
|
|
161
|
-
messages: dialogHistory,
|
|
162
|
-
});
|
|
163
|
-
const content = response.content[0];
|
|
164
|
-
result = content.type === 'text' ? content.text : '';
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (session.debug) {
|
|
168
|
-
console.error(`[LLM] Response length: ${result.length}`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// After LLM call, append assistant response to dialogHistory and update context variable
|
|
172
|
-
if (contextVar) {
|
|
173
|
-
dialogHistory.push({ role: 'assistant', content: result });
|
|
174
|
-
setVariable(session, contextVar, dialogHistory, true);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Store in variable if requested
|
|
178
|
-
if (outputVar) {
|
|
179
|
-
setVariable(session, outputVar, result, false);
|
|
180
|
-
} else if (executeMode) {
|
|
181
|
-
// NEW: Execute mode - parse and interpret LLM response as Dirac code
|
|
182
|
-
const validateTags = element.attributes['validate'] === 'true';
|
|
183
|
-
const autocorrect = element.attributes['autocorrect'] === 'true';
|
|
184
|
-
const maxRetries = parseInt(element.attributes['max-retries'] || '0', 10);
|
|
185
|
-
const feedbackMode = element.attributes['feedback'] === 'true';
|
|
186
|
-
const maxIterations = parseInt(element.attributes['max-iterations'] || '3', 10);
|
|
187
|
-
const replaceTick = element.attributes['replace-tick'] === 'true';
|
|
188
|
-
|
|
189
|
-
if (session.debug) {
|
|
190
|
-
console.error(`[LLM] Executing response as Dirac code:\n${result}\n`);
|
|
191
|
-
if (validateTags) {
|
|
192
|
-
console.error(`[LLM] Tag validation enabled (autocorrect: ${autocorrect}, max-retries: ${maxRetries})`);
|
|
193
|
-
}
|
|
194
|
-
if (feedbackMode) {
|
|
195
|
-
console.error(`[LLM] Feedback mode enabled (max iterations: ${maxIterations})`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Feedback loop: execute, capture output, send back to LLM, repeat
|
|
200
|
-
let iteration = 0;
|
|
201
|
-
|
|
202
|
-
while (iteration < maxIterations && (iteration === 0 || feedbackMode)) {
|
|
203
|
-
iteration++;
|
|
204
|
-
if (session.debug && feedbackMode) {
|
|
205
|
-
console.error(`[LLM] Feedback iteration ${iteration}/${maxIterations}`);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Only replace triple backtick code blocks if replace-tick="true" is set
|
|
209
|
-
let diracCode = result.trim();
|
|
210
|
-
if (replaceTick && diracCode.startsWith('```')) {
|
|
211
|
-
// Check for bash, xml, html, dirac, or no language
|
|
212
|
-
const match = diracCode.match(/^```(\w+)?\n?/m);
|
|
213
|
-
if (match && match[1] === 'bash') {
|
|
214
|
-
// Find closing triple backticks
|
|
215
|
-
const endIdx = diracCode.indexOf('```', 3);
|
|
216
|
-
let bashContent = diracCode.slice(match[0].length, endIdx).trim();
|
|
217
|
-
diracCode = `<system>${bashContent}</system>`;
|
|
218
|
-
} else {
|
|
219
|
-
// Remove opening and closing backticks for xml/html/dirac/none
|
|
220
|
-
diracCode = diracCode.replace(/^```(?:xml|html|dirac)?\n?/m, '').replace(/\n?```$/m, '').trim();
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Capture output before execution (for feedback)
|
|
225
|
-
const outputBefore = feedbackMode ? session.output.slice() : [];
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
// Parse the LLM's output as Dirac code
|
|
229
|
-
const parser = new DiracParser();
|
|
230
|
-
let dynamicAST = parser.parse(diracCode);
|
|
231
|
-
|
|
232
|
-
// Validate tags if requested
|
|
233
|
-
if (validateTags) {
|
|
234
|
-
const { validateDiracCode, applyCorrectedTags } = await import('../utils/tag-validator.js');
|
|
235
|
-
let validation = await validateDiracCode(session, dynamicAST, { autocorrect });
|
|
236
|
-
let retryCount = 0;
|
|
237
|
-
|
|
238
|
-
while (!validation.valid && retryCount < maxRetries) {
|
|
239
|
-
retryCount++;
|
|
240
|
-
if (session.debug) {
|
|
241
|
-
console.error(`[LLM] Validation failed (attempt ${retryCount}/${maxRetries}):`, validation.errorMessages);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Build error feedback for LLM
|
|
245
|
-
const errorFeedback = validation.errorMessages.join('\n');
|
|
246
|
-
const retryPrompt = `Your previous response had the following errors:\n${errorFeedback}\n\nPlease fix these errors and generate valid Dirac XML again. Remember to only use the allowed tags.`;
|
|
247
|
-
|
|
248
|
-
// Add error feedback to dialog history
|
|
249
|
-
dialogHistory.push({ role: 'user', content: retryPrompt });
|
|
250
|
-
|
|
251
|
-
// Retry LLM call
|
|
252
|
-
if (isOpenAI) {
|
|
253
|
-
const response = await session.llmClient.chat.completions.create({
|
|
254
|
-
model,
|
|
255
|
-
max_tokens: maxTokens,
|
|
256
|
-
temperature,
|
|
257
|
-
messages: dialogHistory,
|
|
258
|
-
});
|
|
259
|
-
result = response.choices[0]?.message?.content || '';
|
|
260
|
-
} else if (isOllama) {
|
|
261
|
-
const ollamaPrompt = dialogHistory.map(m => `${m.role.charAt(0).toUpperCase() + m.role.slice(1)}: ${m.content}`).join('\n');
|
|
262
|
-
result = await session.llmClient.complete(ollamaPrompt, {
|
|
263
|
-
model,
|
|
264
|
-
temperature,
|
|
265
|
-
max_tokens: maxTokens,
|
|
266
|
-
});
|
|
267
|
-
} else {
|
|
268
|
-
const response = await session.llmClient.messages.create({
|
|
269
|
-
model,
|
|
270
|
-
max_tokens: maxTokens,
|
|
271
|
-
temperature,
|
|
272
|
-
messages: dialogHistory,
|
|
273
|
-
});
|
|
274
|
-
const content = response.content[0];
|
|
275
|
-
result = content.type === 'text' ? content.text : '';
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Add new response to dialog history
|
|
279
|
-
dialogHistory.push({ role: 'assistant', content: result });
|
|
280
|
-
|
|
281
|
-
// Update context variable if present
|
|
282
|
-
if (contextVar) {
|
|
283
|
-
setVariable(session, contextVar, dialogHistory, true);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (session.debug) {
|
|
287
|
-
console.error(`[LLM] Retry ${retryCount} response:\n${result}\n`);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Clean up and parse the new response
|
|
291
|
-
diracCode = result.trim();
|
|
292
|
-
if (replaceTick && diracCode.startsWith('```')) {
|
|
293
|
-
const match = diracCode.match(/^```(\w+)?\n?/m);
|
|
294
|
-
if (match && match[1] === 'bash') {
|
|
295
|
-
const endIdx = diracCode.indexOf('```', 3);
|
|
296
|
-
let bashContent = diracCode.slice(match[0].length, endIdx).trim();
|
|
297
|
-
diracCode = `<system>${bashContent}</system>`;
|
|
298
|
-
} else {
|
|
299
|
-
diracCode = diracCode.replace(/^```(?:xml|html|dirac)?\n?/m, '').replace(/\n?```$/m, '').trim();
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
dynamicAST = parser.parse(diracCode);
|
|
304
|
-
validation = await validateDiracCode(session, dynamicAST, { autocorrect });
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (!validation.valid) {
|
|
308
|
-
throw new Error(`Tag validation failed after ${maxRetries} retries:\n${validation.errorMessages.join('\n')}`);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Apply auto-corrections if enabled
|
|
312
|
-
if (autocorrect) {
|
|
313
|
-
dynamicAST = applyCorrectedTags(dynamicAST, validation.results);
|
|
314
|
-
if (session.debug) {
|
|
315
|
-
console.error('[LLM] Applied auto-corrections to tags');
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Execute the validated (and possibly corrected) code
|
|
321
|
-
await integrate(session, dynamicAST);
|
|
322
|
-
|
|
323
|
-
// If feedback mode, capture execution output and send back to LLM
|
|
324
|
-
if (feedbackMode) {
|
|
325
|
-
const outputAfter = session.output.slice();
|
|
326
|
-
const executionOutput = outputAfter.slice(outputBefore.length).join('');
|
|
327
|
-
|
|
328
|
-
if (session.debug) {
|
|
329
|
-
console.error(`[LLM] Execution output (${executionOutput.length} chars):\n${executionOutput}\n`);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Build feedback prompt
|
|
333
|
-
const feedbackPrompt = `The code executed successfully. Here is the output:\n\`\`\`\n${executionOutput}\n\`\`\`\n\nPlease review the output carefully. If the output is correct and complete, respond with ONLY the word "DONE" and nothing else. If the output is incorrect or incomplete, generate corrected Dirac XML code.`;
|
|
334
|
-
|
|
335
|
-
if (session.debug) {
|
|
336
|
-
console.error(`[LLM] Feedback prompt:\n${feedbackPrompt}\n`);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Add feedback to dialog history
|
|
340
|
-
dialogHistory.push({ role: 'user', content: feedbackPrompt });
|
|
341
|
-
|
|
342
|
-
// Get LLM's assessment
|
|
343
|
-
if (isOpenAI) {
|
|
344
|
-
const response = await session.llmClient.chat.completions.create({
|
|
345
|
-
model,
|
|
346
|
-
max_tokens: maxTokens,
|
|
347
|
-
temperature,
|
|
348
|
-
messages: dialogHistory,
|
|
349
|
-
});
|
|
350
|
-
result = response.choices[0]?.message?.content || '';
|
|
351
|
-
} else if (isOllama) {
|
|
352
|
-
const ollamaPrompt = dialogHistory.map(m => `${m.role.charAt(0).toUpperCase() + m.role.slice(1)}: ${m.content}`).join('\n');
|
|
353
|
-
result = await session.llmClient.complete(ollamaPrompt, {
|
|
354
|
-
model,
|
|
355
|
-
temperature,
|
|
356
|
-
max_tokens: maxTokens,
|
|
357
|
-
});
|
|
358
|
-
} else {
|
|
359
|
-
const response = await session.llmClient.messages.create({
|
|
360
|
-
model,
|
|
361
|
-
max_tokens: maxTokens,
|
|
362
|
-
temperature,
|
|
363
|
-
messages: dialogHistory,
|
|
364
|
-
});
|
|
365
|
-
const content = response.content[0];
|
|
366
|
-
result = content.type === 'text' ? content.text : '';
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// Add response to dialog history
|
|
370
|
-
dialogHistory.push({ role: 'assistant', content: result });
|
|
371
|
-
|
|
372
|
-
// Update context variable if present
|
|
373
|
-
if (contextVar) {
|
|
374
|
-
setVariable(session, contextVar, dialogHistory, true);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (session.debug) {
|
|
378
|
-
console.error(`[LLM] Feedback response:\n${result}\n`);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Check if LLM says we're done (check at start of response)
|
|
382
|
-
const responseStart = result.trim().substring(0, 100).toUpperCase();
|
|
383
|
-
if (responseStart.startsWith('DONE') || result.trim().toLowerCase().includes('looks correct') || result.trim().toLowerCase().includes('looks good')) {
|
|
384
|
-
if (session.debug) {
|
|
385
|
-
console.error(`[LLM] Feedback loop terminating - LLM indicated completion\n`);
|
|
386
|
-
}
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Otherwise, continue to next iteration with new LLM response
|
|
391
|
-
} else {
|
|
392
|
-
// No feedback mode, exit after first execution
|
|
393
|
-
break;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
} catch (parseError) {
|
|
397
|
-
// If parsing fails, treat as plain text
|
|
398
|
-
if (session.debug) {
|
|
399
|
-
console.error(`[LLM] Failed to parse as Dirac, treating as text: ${parseError}`);
|
|
400
|
-
}
|
|
401
|
-
emit(session, result);
|
|
402
|
-
break; // Exit feedback loop on parse error
|
|
403
|
-
}
|
|
404
|
-
} // end while loop
|
|
405
|
-
} else {
|
|
406
|
-
// Otherwise emit to output as text
|
|
407
|
-
emit(session, result);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
} catch (error) {
|
|
411
|
-
throw new Error(`LLM error: ${error instanceof Error ? error.message : String(error)}`);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
|
package/src/tags/loop.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
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
|
-
}
|