@vibe-lang/runtime 0.2.5
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/package.json +46 -0
- package/src/ast/index.ts +375 -0
- package/src/ast.ts +2 -0
- package/src/debug/advanced-features.ts +482 -0
- package/src/debug/bun-inspector.ts +424 -0
- package/src/debug/handoff-manager.ts +283 -0
- package/src/debug/index.ts +150 -0
- package/src/debug/runner.ts +365 -0
- package/src/debug/server.ts +565 -0
- package/src/debug/stack-merger.ts +267 -0
- package/src/debug/state.ts +581 -0
- package/src/debug/test/advanced-features.test.ts +300 -0
- package/src/debug/test/e2e.test.ts +218 -0
- package/src/debug/test/handoff-manager.test.ts +256 -0
- package/src/debug/test/runner.test.ts +256 -0
- package/src/debug/test/stack-merger.test.ts +163 -0
- package/src/debug/test/state.test.ts +400 -0
- package/src/debug/test/ts-debug-integration.test.ts +374 -0
- package/src/debug/test/ts-import-tracker.test.ts +125 -0
- package/src/debug/test/ts-source-map.test.ts +169 -0
- package/src/debug/ts-import-tracker.ts +151 -0
- package/src/debug/ts-source-map.ts +171 -0
- package/src/errors/index.ts +124 -0
- package/src/index.ts +358 -0
- package/src/lexer/index.ts +348 -0
- package/src/lexer.ts +2 -0
- package/src/parser/index.ts +792 -0
- package/src/parser/parse.ts +45 -0
- package/src/parser/test/async.test.ts +248 -0
- package/src/parser/test/destructuring.test.ts +167 -0
- package/src/parser/test/do-expression.test.ts +486 -0
- package/src/parser/test/errors/do-expression.test.ts +95 -0
- package/src/parser/test/errors/error-locations.test.ts +230 -0
- package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
- package/src/parser/test/errors/missing-tokens.test.ts +126 -0
- package/src/parser/test/errors/model-declaration.test.ts +185 -0
- package/src/parser/test/errors/nested-blocks.test.ts +226 -0
- package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
- package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
- package/src/parser/test/import-export.test.ts +143 -0
- package/src/parser/test/literals.test.ts +404 -0
- package/src/parser/test/model-declaration.test.ts +161 -0
- package/src/parser/test/nested-blocks.test.ts +402 -0
- package/src/parser/test/parser.test.ts +743 -0
- package/src/parser/test/private.test.ts +136 -0
- package/src/parser/test/template-literal.test.ts +127 -0
- package/src/parser/test/tool-declaration.test.ts +302 -0
- package/src/parser/test/ts-block.test.ts +252 -0
- package/src/parser/test/type-annotations.test.ts +254 -0
- package/src/parser/visitor/helpers.ts +330 -0
- package/src/parser/visitor.ts +794 -0
- package/src/parser.ts +2 -0
- package/src/runtime/ai/cache-chunking.test.ts +69 -0
- package/src/runtime/ai/cache-chunking.ts +73 -0
- package/src/runtime/ai/client.ts +109 -0
- package/src/runtime/ai/context.ts +168 -0
- package/src/runtime/ai/formatters.ts +316 -0
- package/src/runtime/ai/index.ts +38 -0
- package/src/runtime/ai/language-ref.ts +38 -0
- package/src/runtime/ai/providers/anthropic.ts +253 -0
- package/src/runtime/ai/providers/google.ts +201 -0
- package/src/runtime/ai/providers/openai.ts +156 -0
- package/src/runtime/ai/retry.ts +100 -0
- package/src/runtime/ai/return-tools.ts +301 -0
- package/src/runtime/ai/test/client.test.ts +83 -0
- package/src/runtime/ai/test/formatters.test.ts +485 -0
- package/src/runtime/ai/test/retry.test.ts +137 -0
- package/src/runtime/ai/test/return-tools.test.ts +450 -0
- package/src/runtime/ai/test/tool-loop.test.ts +319 -0
- package/src/runtime/ai/test/tool-schema.test.ts +241 -0
- package/src/runtime/ai/tool-loop.ts +203 -0
- package/src/runtime/ai/tool-schema.ts +151 -0
- package/src/runtime/ai/types.ts +113 -0
- package/src/runtime/ai-logger.ts +255 -0
- package/src/runtime/ai-provider.ts +347 -0
- package/src/runtime/async/dependencies.ts +276 -0
- package/src/runtime/async/executor.ts +293 -0
- package/src/runtime/async/index.ts +43 -0
- package/src/runtime/async/scheduling.ts +163 -0
- package/src/runtime/async/test/dependencies.test.ts +284 -0
- package/src/runtime/async/test/executor.test.ts +388 -0
- package/src/runtime/context.ts +357 -0
- package/src/runtime/exec/ai.ts +139 -0
- package/src/runtime/exec/expressions.ts +475 -0
- package/src/runtime/exec/frames.ts +26 -0
- package/src/runtime/exec/functions.ts +305 -0
- package/src/runtime/exec/interpolation.ts +312 -0
- package/src/runtime/exec/statements.ts +604 -0
- package/src/runtime/exec/tools.ts +129 -0
- package/src/runtime/exec/typescript.ts +215 -0
- package/src/runtime/exec/variables.ts +279 -0
- package/src/runtime/index.ts +975 -0
- package/src/runtime/modules.ts +452 -0
- package/src/runtime/serialize.ts +103 -0
- package/src/runtime/state.ts +489 -0
- package/src/runtime/stdlib/core.ts +45 -0
- package/src/runtime/stdlib/directory.test.ts +156 -0
- package/src/runtime/stdlib/edit.test.ts +154 -0
- package/src/runtime/stdlib/fastEdit.test.ts +201 -0
- package/src/runtime/stdlib/glob.test.ts +106 -0
- package/src/runtime/stdlib/grep.test.ts +144 -0
- package/src/runtime/stdlib/index.ts +16 -0
- package/src/runtime/stdlib/readFile.test.ts +123 -0
- package/src/runtime/stdlib/tools/index.ts +707 -0
- package/src/runtime/stdlib/writeFile.test.ts +157 -0
- package/src/runtime/step.ts +969 -0
- package/src/runtime/test/ai-context.test.ts +1086 -0
- package/src/runtime/test/ai-result-object.test.ts +419 -0
- package/src/runtime/test/ai-tool-flow.test.ts +859 -0
- package/src/runtime/test/async-execution-order.test.ts +618 -0
- package/src/runtime/test/async-execution.test.ts +344 -0
- package/src/runtime/test/async-nested.test.ts +660 -0
- package/src/runtime/test/async-parallel-timing.test.ts +546 -0
- package/src/runtime/test/basic1.test.ts +154 -0
- package/src/runtime/test/binary-operators.test.ts +431 -0
- package/src/runtime/test/break-statement.test.ts +257 -0
- package/src/runtime/test/context-modes.test.ts +650 -0
- package/src/runtime/test/context.test.ts +466 -0
- package/src/runtime/test/core-functions.test.ts +228 -0
- package/src/runtime/test/e2e.test.ts +88 -0
- package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
- package/src/runtime/test/error-locations/main-error.vibe +4 -0
- package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
- package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
- package/src/runtime/test/for-in.test.ts +312 -0
- package/src/runtime/test/helpers.ts +69 -0
- package/src/runtime/test/imports.test.ts +334 -0
- package/src/runtime/test/json-expressions.test.ts +232 -0
- package/src/runtime/test/literals.test.ts +372 -0
- package/src/runtime/test/logical-indexing.test.ts +478 -0
- package/src/runtime/test/member-methods.test.ts +324 -0
- package/src/runtime/test/model-config.test.ts +338 -0
- package/src/runtime/test/null-handling.test.ts +342 -0
- package/src/runtime/test/private-visibility.test.ts +332 -0
- package/src/runtime/test/runtime-state.test.ts +514 -0
- package/src/runtime/test/scoping.test.ts +370 -0
- package/src/runtime/test/string-interpolation.test.ts +354 -0
- package/src/runtime/test/template-literal.test.ts +181 -0
- package/src/runtime/test/tool-execution.test.ts +467 -0
- package/src/runtime/test/tool-schema-generation.test.ts +477 -0
- package/src/runtime/test/tostring.test.ts +210 -0
- package/src/runtime/test/ts-block.test.ts +594 -0
- package/src/runtime/test/ts-error-location.test.ts +231 -0
- package/src/runtime/test/types.test.ts +732 -0
- package/src/runtime/test/verbose-logger.test.ts +710 -0
- package/src/runtime/test/vibe-expression.test.ts +54 -0
- package/src/runtime/test/vibe-value-errors.test.ts +541 -0
- package/src/runtime/test/while.test.ts +232 -0
- package/src/runtime/tools/builtin.ts +30 -0
- package/src/runtime/tools/directory-tools.ts +70 -0
- package/src/runtime/tools/file-tools.ts +228 -0
- package/src/runtime/tools/index.ts +5 -0
- package/src/runtime/tools/registry.ts +48 -0
- package/src/runtime/tools/search-tools.ts +134 -0
- package/src/runtime/tools/security.ts +36 -0
- package/src/runtime/tools/system-tools.ts +312 -0
- package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
- package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
- package/src/runtime/tools/test/registry.test.ts +713 -0
- package/src/runtime/tools/test/security.test.ts +86 -0
- package/src/runtime/tools/test/system-tools.test.ts +679 -0
- package/src/runtime/tools/test/ts-schema.test.ts +357 -0
- package/src/runtime/tools/ts-schema.ts +341 -0
- package/src/runtime/tools/types.ts +89 -0
- package/src/runtime/tools/utility-tools.ts +198 -0
- package/src/runtime/ts-eval.ts +126 -0
- package/src/runtime/types.ts +797 -0
- package/src/runtime/validation.ts +160 -0
- package/src/runtime/verbose-logger.ts +459 -0
- package/src/runtime.ts +2 -0
- package/src/semantic/analyzer-context.ts +62 -0
- package/src/semantic/analyzer-validators.ts +575 -0
- package/src/semantic/analyzer-visitors.ts +534 -0
- package/src/semantic/analyzer.ts +83 -0
- package/src/semantic/index.ts +11 -0
- package/src/semantic/symbol-table.ts +58 -0
- package/src/semantic/test/async-validation.test.ts +301 -0
- package/src/semantic/test/compress-validation.test.ts +179 -0
- package/src/semantic/test/const-reassignment.test.ts +111 -0
- package/src/semantic/test/control-flow.test.ts +346 -0
- package/src/semantic/test/destructuring.test.ts +185 -0
- package/src/semantic/test/duplicate-declarations.test.ts +168 -0
- package/src/semantic/test/export-validation.test.ts +111 -0
- package/src/semantic/test/fixtures/math.ts +31 -0
- package/src/semantic/test/imports.test.ts +148 -0
- package/src/semantic/test/json-type.test.ts +68 -0
- package/src/semantic/test/literals.test.ts +127 -0
- package/src/semantic/test/model-validation.test.ts +179 -0
- package/src/semantic/test/prompt-validation.test.ts +343 -0
- package/src/semantic/test/scoping.test.ts +312 -0
- package/src/semantic/test/tool-validation.test.ts +306 -0
- package/src/semantic/test/ts-type-checking.test.ts +563 -0
- package/src/semantic/test/type-constraints.test.ts +111 -0
- package/src/semantic/test/type-inference.test.ts +87 -0
- package/src/semantic/test/type-validation.test.ts +552 -0
- package/src/semantic/test/undefined-variables.test.ts +163 -0
- package/src/semantic/ts-block-checker.ts +204 -0
- package/src/semantic/ts-signatures.ts +194 -0
- package/src/semantic/ts-types.ts +170 -0
- package/src/semantic/types.ts +58 -0
- package/tests/fixtures/conditional-logic.vibe +14 -0
- package/tests/fixtures/function-call.vibe +16 -0
- package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
- package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
- package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
- package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
- package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
- package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
- package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
- package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
- package/tests/fixtures/imports/nested-import/main.vibe +3 -0
- package/tests/fixtures/imports/nested-import/utils.ts +3 -0
- package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
- package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
- package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
- package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
- package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
- package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
- package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
- package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
- package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
- package/tests/fixtures/imports/ts-import/main.vibe +4 -0
- package/tests/fixtures/imports/ts-import/math.ts +9 -0
- package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/data.ts +10 -0
- package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
- package/tests/fixtures/multiple-ai-calls.vibe +10 -0
- package/tests/fixtures/simple-greeting.vibe +6 -0
- package/tests/fixtures/template-literals.vibe +11 -0
- package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
- package/tests/integration/basic-ai/basic-ai.vibe +12 -0
- package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
- package/tests/integration/bug-fix/buggy-code.ts +22 -0
- package/tests/integration/bug-fix/fix-bug.vibe +21 -0
- package/tests/integration/compress/compress.integration.test.ts +206 -0
- package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
- package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
- package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
- package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
- package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
- package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
- package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
- package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Destructuring Integration Tests
|
|
2
|
+
// Tests destructuring declarations with const and let using Google Gemini 3
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect } from 'bun:test';
|
|
5
|
+
import { Runtime, formatAIInteractions } from '../../../src/runtime';
|
|
6
|
+
import { createRealAIProvider } from '../../../src/runtime/ai-provider';
|
|
7
|
+
import { parse } from '../../../src/parser/parse';
|
|
8
|
+
|
|
9
|
+
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
|
|
10
|
+
|
|
11
|
+
const modelConfig = `
|
|
12
|
+
model testModel = {
|
|
13
|
+
name: "gemini-3-flash-preview",
|
|
14
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
15
|
+
provider: "google"
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
async function runVibe(vibeCode: string, logAi = true): Promise<Runtime> {
|
|
20
|
+
const program = parse(modelConfig + vibeCode);
|
|
21
|
+
const runtime = new Runtime(
|
|
22
|
+
program,
|
|
23
|
+
createRealAIProvider(() => runtime.getState()),
|
|
24
|
+
{ logAiInteractions: logAi }
|
|
25
|
+
);
|
|
26
|
+
await runtime.run();
|
|
27
|
+
|
|
28
|
+
if (logAi) {
|
|
29
|
+
const interactions = runtime.getAIInteractions();
|
|
30
|
+
console.log('\n' + formatAIInteractions(interactions));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return runtime;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe.skipIf(!GOOGLE_API_KEY)('Google Gemini 3 - Destructuring Declarations', () => {
|
|
37
|
+
test('const destructuring with multiple fields', async () => {
|
|
38
|
+
const runtime = await runVibe(`
|
|
39
|
+
const {name: text, age: number} = do "Return a person named Alice who is 25 years old" testModel default
|
|
40
|
+
`);
|
|
41
|
+
expect(runtime.getValue('name')).toBe('Alice');
|
|
42
|
+
expect(runtime.getValue('age')).toBe(25);
|
|
43
|
+
}, 30000);
|
|
44
|
+
|
|
45
|
+
test('let destructuring with multiple fields', async () => {
|
|
46
|
+
const runtime = await runVibe(`
|
|
47
|
+
let {city: text, population: number} = do "Return a city named Tokyo with population 14000000" testModel default
|
|
48
|
+
`);
|
|
49
|
+
expect(runtime.getValue('city')).toBe('Tokyo');
|
|
50
|
+
expect(runtime.getValue('population')).toBe(14000000);
|
|
51
|
+
}, 30000);
|
|
52
|
+
|
|
53
|
+
test('const destructuring with boolean field', async () => {
|
|
54
|
+
const runtime = await runVibe(`
|
|
55
|
+
const {valid: boolean, message: text} = do "Return valid as true and message as OK" testModel default
|
|
56
|
+
`);
|
|
57
|
+
expect(runtime.getValue('valid')).toBe(true);
|
|
58
|
+
expect(runtime.getValue('message')).toBe('OK');
|
|
59
|
+
}, 30000);
|
|
60
|
+
|
|
61
|
+
test('let destructuring with array fields', async () => {
|
|
62
|
+
const runtime = await runVibe(`
|
|
63
|
+
let {colors: text[], counts: number[]} = do "Return colors as [red, green, blue] and counts as [1, 2, 3]" testModel default
|
|
64
|
+
`);
|
|
65
|
+
const colors = runtime.getValue('colors') as string[];
|
|
66
|
+
const counts = runtime.getValue('counts') as number[];
|
|
67
|
+
expect(Array.isArray(colors)).toBe(true);
|
|
68
|
+
expect(colors).toEqual(['red', 'green', 'blue']);
|
|
69
|
+
expect(Array.isArray(counts)).toBe(true);
|
|
70
|
+
expect(counts).toEqual([1, 2, 3]);
|
|
71
|
+
}, 30000);
|
|
72
|
+
|
|
73
|
+
test('const destructuring with json field', async () => {
|
|
74
|
+
const runtime = await runVibe(`
|
|
75
|
+
const {user: json, active: boolean} = do "Return user as an object with name Alice and role admin, and active as true" testModel default
|
|
76
|
+
`);
|
|
77
|
+
const user = runtime.getValue('user') as Record<string, unknown>;
|
|
78
|
+
expect(typeof user).toBe('object');
|
|
79
|
+
expect(user.name).toBe('Alice');
|
|
80
|
+
expect(user.role).toBe('admin');
|
|
81
|
+
expect(runtime.getValue('active')).toBe(true);
|
|
82
|
+
}, 30000);
|
|
83
|
+
|
|
84
|
+
test('destructuring with three fields', async () => {
|
|
85
|
+
const runtime = await runVibe(`
|
|
86
|
+
const {x: number, y: number, z: number} = do "Return x as 10, y as 20, z as 30" testModel default
|
|
87
|
+
`);
|
|
88
|
+
expect(runtime.getValue('x')).toBe(10);
|
|
89
|
+
expect(runtime.getValue('y')).toBe(20);
|
|
90
|
+
expect(runtime.getValue('z')).toBe(30);
|
|
91
|
+
}, 30000);
|
|
92
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Multi-language Hello World Translator Integration Test
|
|
2
|
+
// Tests optional model/context syntax and lastUsedModel feature
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect } from 'bun:test';
|
|
5
|
+
import { Runtime, formatAIInteractions } from '../../../src/runtime';
|
|
6
|
+
import { createRealAIProvider } from '../../../src/runtime/ai-provider';
|
|
7
|
+
import { parse } from '../../../src/parser/parse';
|
|
8
|
+
|
|
9
|
+
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
|
|
10
|
+
|
|
11
|
+
async function runVibe(code: string, logAi = true): Promise<Runtime> {
|
|
12
|
+
const program = parse(code);
|
|
13
|
+
const runtime = new Runtime(
|
|
14
|
+
program,
|
|
15
|
+
createRealAIProvider(() => runtime.getState()),
|
|
16
|
+
{ logAiInteractions: logAi }
|
|
17
|
+
);
|
|
18
|
+
await runtime.run();
|
|
19
|
+
|
|
20
|
+
if (logAi) {
|
|
21
|
+
const interactions = runtime.getAIInteractions();
|
|
22
|
+
console.log('\n' + formatAIInteractions(interactions));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return runtime;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe.skipIf(!GOOGLE_API_KEY)('Hello World Translator', () => {
|
|
29
|
+
|
|
30
|
+
test('translate Hello World to multiple languages', async () => {
|
|
31
|
+
// print() and env() are auto-imported core functions - no import needed
|
|
32
|
+
const code = `
|
|
33
|
+
model translator = {
|
|
34
|
+
name: "gemini-3-flash-preview",
|
|
35
|
+
provider: "google",
|
|
36
|
+
apiKey: env("GOOGLE_API_KEY")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// First call establishes lastUsedModel
|
|
40
|
+
let languages: text[] = do "List the major human languages(max 10) as a JSON array of strings."
|
|
41
|
+
|
|
42
|
+
// Loop uses lastUsedModel (translator) since no model specified
|
|
43
|
+
for language in languages {
|
|
44
|
+
let translated = do "Translate 'Hello World' into {language}. Return only the translation, nothing else."
|
|
45
|
+
print(translated)
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const runtime = await runVibe(code);
|
|
50
|
+
|
|
51
|
+
// Check that languages array was populated
|
|
52
|
+
const languages = runtime.getValue('languages') as string[];
|
|
53
|
+
expect(Array.isArray(languages)).toBe(true);
|
|
54
|
+
expect(languages.length).toBeGreaterThanOrEqual(1);
|
|
55
|
+
|
|
56
|
+
// The loop should have run and printed translations
|
|
57
|
+
expect(runtime.getState().status).toBe('completed');
|
|
58
|
+
}, 60000);
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
});
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Context Modes Integration Test
|
|
2
|
+
// Tests that forget mode properly cleans up inner loop context
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
5
|
+
import { Runtime, formatAIInteractions } from '../../../src/runtime';
|
|
6
|
+
import { createRealAIProvider } from '../../../src/runtime/ai-provider';
|
|
7
|
+
import { parse } from '../../../src/parser/parse';
|
|
8
|
+
import { buildLocalContext, formatContextForAI } from '../../../src/runtime/context';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
|
|
12
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
13
|
+
const shouldRun = !!ANTHROPIC_API_KEY;
|
|
14
|
+
|
|
15
|
+
// Test directory setup
|
|
16
|
+
const TEST_WORKSPACE = path.join(__dirname, '.test-workspace-context');
|
|
17
|
+
|
|
18
|
+
// Test file contents
|
|
19
|
+
const SAMPLE_FILES = {
|
|
20
|
+
'file1.txt': `Line one
|
|
21
|
+
Line two
|
|
22
|
+
Line three`,
|
|
23
|
+
|
|
24
|
+
'file2.txt': `Alpha
|
|
25
|
+
Beta
|
|
26
|
+
Gamma`,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Expected line lengths for verification
|
|
30
|
+
const EXPECTED_LENGTHS: Record<string, number[]> = {
|
|
31
|
+
'file1.txt': [8, 8, 10],
|
|
32
|
+
'file2.txt': [5, 4, 5],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Function to generate Vibe program with configurable context mode
|
|
36
|
+
function createVibeProgram(innerLoopMode: 'forget' | 'verbose' | ''): string {
|
|
37
|
+
const modeKeyword = innerLoopMode ? ` ${innerLoopMode}` : '';
|
|
38
|
+
|
|
39
|
+
return `
|
|
40
|
+
import { glob, readFile, writeFile } from "system/tools"
|
|
41
|
+
|
|
42
|
+
model analyzer = {
|
|
43
|
+
name: "claude-haiku-4-5",
|
|
44
|
+
apiKey: "${ANTHROPIC_API_KEY}",
|
|
45
|
+
url: "https://api.anthropic.com",
|
|
46
|
+
provider: "anthropic",
|
|
47
|
+
tools: [glob, readFile, writeFile]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Find all .txt files
|
|
51
|
+
let files: text[] = vibe "Find all .txt files in the current directory. Return just the filenames as an array." analyzer default
|
|
52
|
+
|
|
53
|
+
// Process each file
|
|
54
|
+
for file in files {
|
|
55
|
+
// Read the file
|
|
56
|
+
let content: text = vibe "Use the readFile tool to read '{file}'. Return the exact file contents with no formatting, no markdown, no code blocks - just the raw text." analyzer default
|
|
57
|
+
|
|
58
|
+
// Split content into lines using inline TS
|
|
59
|
+
let lines: text[] = ts(content) { return content.split('\\n'); }
|
|
60
|
+
|
|
61
|
+
// Process each line - TS calculates length
|
|
62
|
+
let annotatedLines: number[] = []
|
|
63
|
+
for line in lines {
|
|
64
|
+
let annotated: number = ts(line) { return (line ?? '').length }
|
|
65
|
+
annotatedLines.push(annotated)
|
|
66
|
+
}${modeKeyword}
|
|
67
|
+
|
|
68
|
+
// Write annotated file
|
|
69
|
+
vibe "update the file '{file}' with the annotated lines, so each line ends with its length in brackets like [5]" analyzer default
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
"done"
|
|
73
|
+
`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe.skipIf(!shouldRun)('Context Modes Integration', () => {
|
|
77
|
+
beforeAll(() => {
|
|
78
|
+
// Create test workspace and sample files
|
|
79
|
+
fs.mkdirSync(TEST_WORKSPACE, { recursive: true });
|
|
80
|
+
for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
|
|
81
|
+
fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
afterAll(() => {
|
|
86
|
+
// Clean up test workspace
|
|
87
|
+
if (fs.existsSync(TEST_WORKSPACE)) {
|
|
88
|
+
fs.rmSync(TEST_WORKSPACE, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test(
|
|
93
|
+
'forget mode cleans up inner loop iterations from context',
|
|
94
|
+
async () => {
|
|
95
|
+
// Run with forget mode on inner loop
|
|
96
|
+
const program = parse(createVibeProgram('forget'));
|
|
97
|
+
const runtime = new Runtime(program, createRealAIProvider(() => runtime.getState()), {
|
|
98
|
+
logAiInteractions: true,
|
|
99
|
+
rootDir: TEST_WORKSPACE,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await runtime.run();
|
|
103
|
+
|
|
104
|
+
const state = runtime.getState();
|
|
105
|
+
console.log('\n=== FORGET MODE ===');
|
|
106
|
+
console.log(formatAIInteractions(state.aiInteractions));
|
|
107
|
+
|
|
108
|
+
// Get the final context
|
|
109
|
+
const context = buildLocalContext(state);
|
|
110
|
+
const formatted = formatContextForAI(context);
|
|
111
|
+
console.log('\n=== FINAL CONTEXT ===');
|
|
112
|
+
console.log(formatted.text);
|
|
113
|
+
|
|
114
|
+
// Verify files were processed correctly
|
|
115
|
+
for (const [filename, expectedLengths] of Object.entries(EXPECTED_LENGTHS)) {
|
|
116
|
+
const filePath = path.join(TEST_WORKSPACE, filename);
|
|
117
|
+
const content = fs.readFileSync(filePath, 'utf-8').trim();
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
|
|
120
|
+
console.log(`\n--- ${filename} ---`);
|
|
121
|
+
console.log(content);
|
|
122
|
+
|
|
123
|
+
expect(lines.length).toBe(expectedLengths.length);
|
|
124
|
+
|
|
125
|
+
for (let i = 0; i < lines.length; i++) {
|
|
126
|
+
const match = lines[i].match(/\[(\d+)\]$/);
|
|
127
|
+
expect(match).not.toBeNull();
|
|
128
|
+
if (match) {
|
|
129
|
+
expect(parseInt(match[1], 10)).toBe(expectedLengths[i]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// KEY ASSERTION: With forget mode, the inner loop iterations should NOT be in context
|
|
135
|
+
// The context should NOT contain individual "line" variable assignments from the inner loop
|
|
136
|
+
const contextText = formatted.text;
|
|
137
|
+
|
|
138
|
+
// Count how many times "- line (text):" appears - should be minimal with forget
|
|
139
|
+
const lineVarMatches = contextText.match(/- line \(text\):/g) || [];
|
|
140
|
+
console.log(`\nLine variable occurrences in context: ${lineVarMatches.length}`);
|
|
141
|
+
|
|
142
|
+
// With forget, we should NOT see accumulated line iterations
|
|
143
|
+
// Each file has 3 lines, 2 files = 6 iterations total
|
|
144
|
+
// With forget, none of these should persist in final context
|
|
145
|
+
expect(lineVarMatches.length).toBe(0);
|
|
146
|
+
|
|
147
|
+
// Also verify the annotatedLines array IS present (the final result)
|
|
148
|
+
expect(contextText).toContain('annotatedLines');
|
|
149
|
+
},
|
|
150
|
+
300000
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
test(
|
|
154
|
+
'verbose mode (default) keeps all inner loop iterations in context',
|
|
155
|
+
async () => {
|
|
156
|
+
// Reset test files
|
|
157
|
+
for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
|
|
158
|
+
fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Run with verbose mode (explicit) on inner loop
|
|
162
|
+
const program = parse(createVibeProgram('verbose'));
|
|
163
|
+
const runtime = new Runtime(program, createRealAIProvider(() => runtime.getState()), {
|
|
164
|
+
logAiInteractions: true,
|
|
165
|
+
rootDir: TEST_WORKSPACE,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
await runtime.run();
|
|
169
|
+
|
|
170
|
+
const state = runtime.getState();
|
|
171
|
+
console.log('\n=== VERBOSE MODE ===');
|
|
172
|
+
console.log(formatAIInteractions(state.aiInteractions));
|
|
173
|
+
|
|
174
|
+
// Get the final context
|
|
175
|
+
const context = buildLocalContext(state);
|
|
176
|
+
const formatted = formatContextForAI(context);
|
|
177
|
+
console.log('\n=== FINAL CONTEXT ===');
|
|
178
|
+
console.log(formatted.text);
|
|
179
|
+
|
|
180
|
+
// Verify files were processed correctly
|
|
181
|
+
for (const [filename, expectedLengths] of Object.entries(EXPECTED_LENGTHS)) {
|
|
182
|
+
const filePath = path.join(TEST_WORKSPACE, filename);
|
|
183
|
+
const content = fs.readFileSync(filePath, 'utf-8').trim();
|
|
184
|
+
const lines = content.split('\n');
|
|
185
|
+
|
|
186
|
+
expect(lines.length).toBe(expectedLengths.length);
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < lines.length; i++) {
|
|
189
|
+
const match = lines[i].match(/\[(\d+)\]$/);
|
|
190
|
+
expect(match).not.toBeNull();
|
|
191
|
+
if (match) {
|
|
192
|
+
expect(parseInt(match[1], 10)).toBe(expectedLengths[i]);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// KEY ASSERTION: With verbose mode, the inner loop iterations SHOULD be in context
|
|
198
|
+
const contextText = formatted.text;
|
|
199
|
+
|
|
200
|
+
// Count how many times "- line (text):" appears
|
|
201
|
+
const lineVarMatches = contextText.match(/- line \(text\):/g) || [];
|
|
202
|
+
console.log(`\nLine variable occurrences in context: ${lineVarMatches.length}`);
|
|
203
|
+
|
|
204
|
+
// With verbose, we SHOULD see all line iterations
|
|
205
|
+
// Each file has 3 lines, 2 files = 6 iterations total
|
|
206
|
+
// But due to outer loop verbose behavior, we see all from both files
|
|
207
|
+
expect(lineVarMatches.length).toBeGreaterThan(0);
|
|
208
|
+
},
|
|
209
|
+
300000
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
test(
|
|
213
|
+
'compare token usage between forget and verbose modes',
|
|
214
|
+
async () => {
|
|
215
|
+
// Reset test files
|
|
216
|
+
for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
|
|
217
|
+
fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Run forget mode
|
|
221
|
+
const forgetProgram = parse(createVibeProgram('forget'));
|
|
222
|
+
const forgetRuntime = new Runtime(forgetProgram, createRealAIProvider(() => forgetRuntime.getState()), {
|
|
223
|
+
logAiInteractions: true,
|
|
224
|
+
rootDir: TEST_WORKSPACE,
|
|
225
|
+
});
|
|
226
|
+
await forgetRuntime.run();
|
|
227
|
+
const forgetState = forgetRuntime.getState();
|
|
228
|
+
|
|
229
|
+
// Calculate total tokens for forget mode
|
|
230
|
+
const forgetTokens = forgetState.aiInteractions?.reduce((sum, i) => sum + (i.usage?.inputTokens ?? 0), 0) ?? 0;
|
|
231
|
+
|
|
232
|
+
// Reset test files
|
|
233
|
+
for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
|
|
234
|
+
fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Run verbose mode
|
|
238
|
+
const verboseProgram = parse(createVibeProgram('verbose'));
|
|
239
|
+
const verboseRuntime = new Runtime(verboseProgram, createRealAIProvider(() => verboseRuntime.getState()), {
|
|
240
|
+
logAiInteractions: true,
|
|
241
|
+
rootDir: TEST_WORKSPACE,
|
|
242
|
+
});
|
|
243
|
+
await verboseRuntime.run();
|
|
244
|
+
const verboseState = verboseRuntime.getState();
|
|
245
|
+
|
|
246
|
+
// Calculate total tokens for verbose mode
|
|
247
|
+
const verboseTokens = verboseState.aiInteractions?.reduce((sum, i) => sum + (i.usage?.inputTokens ?? 0), 0) ?? 0;
|
|
248
|
+
|
|
249
|
+
console.log('\n=== TOKEN COMPARISON ===');
|
|
250
|
+
console.log(`Forget mode total input tokens: ${forgetTokens}`);
|
|
251
|
+
console.log(`Verbose mode total input tokens: ${verboseTokens}`);
|
|
252
|
+
console.log(`Difference: ${verboseTokens - forgetTokens} tokens`);
|
|
253
|
+
console.log(`Forget mode saves: ${((1 - forgetTokens / verboseTokens) * 100).toFixed(1)}%`);
|
|
254
|
+
|
|
255
|
+
// Forget mode should use fewer tokens (less context accumulation)
|
|
256
|
+
// Note: This might not always be true for small examples, but for larger ones it should be
|
|
257
|
+
console.log('\nBoth modes completed successfully!');
|
|
258
|
+
},
|
|
259
|
+
600000 // 10 minute timeout for both runs
|
|
260
|
+
);
|
|
261
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Line Annotator Integration Test
|
|
2
|
+
// Tests tool calling, Vibe loops, and TS functions working together
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
5
|
+
import { Runtime, formatAIInteractions } from '../../../src/runtime';
|
|
6
|
+
import { createRealAIProvider } from '../../../src/runtime/ai-provider';
|
|
7
|
+
import { parse } from '../../../src/parser/parse';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
12
|
+
const shouldRun = !!ANTHROPIC_API_KEY;
|
|
13
|
+
|
|
14
|
+
// Test directory setup
|
|
15
|
+
const TEST_WORKSPACE = path.join(__dirname, '.test-workspace');
|
|
16
|
+
|
|
17
|
+
// Test file contents (simplified for initial testing)
|
|
18
|
+
const SAMPLE_FILES = {
|
|
19
|
+
'sample1.txt': `Hello world
|
|
20
|
+
Short
|
|
21
|
+
End`,
|
|
22
|
+
|
|
23
|
+
'sample2.txt': `Apple
|
|
24
|
+
Banana
|
|
25
|
+
Cherry`,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Expected line lengths for verification
|
|
29
|
+
const EXPECTED_LENGTHS: Record<string, number[]> = {
|
|
30
|
+
'sample1.txt': [11, 5, 3],
|
|
31
|
+
'sample2.txt': [5, 6, 6],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Vibe program that processes files
|
|
35
|
+
const VIBE_PROGRAM = `
|
|
36
|
+
import { glob, readFile, writeFile } from "system/tools"
|
|
37
|
+
|
|
38
|
+
model analyzer = {
|
|
39
|
+
name: "claude-haiku-4-5",
|
|
40
|
+
apiKey: "${ANTHROPIC_API_KEY}",
|
|
41
|
+
url: "https://api.anthropic.com",
|
|
42
|
+
provider: "anthropic",
|
|
43
|
+
tools: [glob, readFile, writeFile]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Find all .txt files
|
|
47
|
+
let files: text[] = vibe "Find all .txt files in the current directory. Return just the filenames as an array." analyzer default
|
|
48
|
+
|
|
49
|
+
// Process each file
|
|
50
|
+
for file in files {
|
|
51
|
+
// Read the file
|
|
52
|
+
let content: text = vibe "Use the readFile tool to read '{file}'. Return the exact file contents with no formatting, no markdown, no code blocks - just the raw text." analyzer default
|
|
53
|
+
|
|
54
|
+
// Split content into lines using inline TS
|
|
55
|
+
let lines: text[] = ts(content) { return content.split('\\n'); }
|
|
56
|
+
|
|
57
|
+
// Process each line - TS calculates length and annotates
|
|
58
|
+
let annotatedLines: number[] = []
|
|
59
|
+
for line in lines {
|
|
60
|
+
let annotated: number = ts(line) {return (line ?? '').length }
|
|
61
|
+
annotatedLines.push(annotated)
|
|
62
|
+
}
|
|
63
|
+
vibe "update the file '{file}' with the annotated lines, so each line ends with its length in brackets like [5]" analyzer default
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
"done"
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
describe.skipIf(!shouldRun)('Line Annotator Integration', () => {
|
|
70
|
+
beforeAll(() => {
|
|
71
|
+
// Create test workspace and sample files
|
|
72
|
+
fs.mkdirSync(TEST_WORKSPACE, { recursive: true });
|
|
73
|
+
for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
|
|
74
|
+
fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterAll(() => {
|
|
79
|
+
// Clean up test workspace
|
|
80
|
+
if (fs.existsSync(TEST_WORKSPACE)) {
|
|
81
|
+
fs.rmSync(TEST_WORKSPACE, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test(
|
|
86
|
+
'AI annotates each line with its length using nested loops',
|
|
87
|
+
async () => {
|
|
88
|
+
// Verify initial files exist
|
|
89
|
+
for (const filename of Object.keys(SAMPLE_FILES)) {
|
|
90
|
+
expect(fs.existsSync(path.join(TEST_WORKSPACE, filename))).toBe(true);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Run the Vibe program
|
|
94
|
+
const program = parse(VIBE_PROGRAM);
|
|
95
|
+
const runtime = new Runtime(program, createRealAIProvider(() => runtime.getState()), {
|
|
96
|
+
logAiInteractions: true,
|
|
97
|
+
rootDir: TEST_WORKSPACE,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await runtime.run();
|
|
101
|
+
|
|
102
|
+
// Log AI interactions
|
|
103
|
+
const state = runtime.getState();
|
|
104
|
+
console.log('\n' + formatAIInteractions(state.aiInteractions));
|
|
105
|
+
|
|
106
|
+
// Verify tool calls were made
|
|
107
|
+
const hasToolCalls = state.callStack.some((frame) =>
|
|
108
|
+
frame.orderedEntries.some(
|
|
109
|
+
(entry) => entry.kind === 'prompt' && entry.toolCalls && entry.toolCalls.length > 0
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
console.log(`\nTool calls made: ${hasToolCalls}`);
|
|
113
|
+
expect(hasToolCalls).toBe(true);
|
|
114
|
+
|
|
115
|
+
// Verify each file was annotated correctly
|
|
116
|
+
for (const [filename, expectedLengths] of Object.entries(EXPECTED_LENGTHS)) {
|
|
117
|
+
const filePath = path.join(TEST_WORKSPACE, filename);
|
|
118
|
+
const content = fs.readFileSync(filePath, 'utf-8').trim();
|
|
119
|
+
const lines = content.split('\n');
|
|
120
|
+
|
|
121
|
+
console.log(`\n--- ${filename} ---`);
|
|
122
|
+
console.log(content);
|
|
123
|
+
|
|
124
|
+
// Check line count matches
|
|
125
|
+
expect(lines.length).toBe(expectedLengths.length);
|
|
126
|
+
|
|
127
|
+
// Check each line ends with correct length annotation
|
|
128
|
+
for (let i = 0; i < lines.length; i++) {
|
|
129
|
+
const line = lines[i];
|
|
130
|
+
const expectedLength = expectedLengths[i];
|
|
131
|
+
|
|
132
|
+
// Line should end with [N] where N is the expected length
|
|
133
|
+
const match = line.match(/\[(\d+)\]$/);
|
|
134
|
+
expect(match).not.toBeNull();
|
|
135
|
+
|
|
136
|
+
if (match) {
|
|
137
|
+
const actualLength = parseInt(match[1], 10);
|
|
138
|
+
expect(actualLength).toBe(expectedLength);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Program completed successfully
|
|
144
|
+
console.log('\nAll files annotated successfully!');
|
|
145
|
+
},
|
|
146
|
+
300000
|
|
147
|
+
); // 5 minute timeout for many AI calls
|
|
148
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Cumulative Sum Integration Test
|
|
2
|
+
// Simple test: AI adds numbers one at a time in a loop
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect } from 'bun:test';
|
|
5
|
+
import { Runtime, formatAIInteractions } from '../../../src/runtime';
|
|
6
|
+
import { createRealAIProvider } from '../../../src/runtime/ai-provider';
|
|
7
|
+
import { parse } from '../../../src/parser/parse';
|
|
8
|
+
|
|
9
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
10
|
+
const shouldRun = !!ANTHROPIC_API_KEY;
|
|
11
|
+
|
|
12
|
+
// Generate random integers for the test
|
|
13
|
+
const numbers = Array.from({ length: 5 }, () => Math.floor(Math.random() * 20) - 5);
|
|
14
|
+
const bonusNumber = Math.floor(Math.random() * 10) + 1;
|
|
15
|
+
const expectedSum = numbers.reduce((a, b) => a + b, 0) + bonusNumber;
|
|
16
|
+
|
|
17
|
+
const VIBE_PROGRAM = `
|
|
18
|
+
model calc = {
|
|
19
|
+
name: "claude-haiku-4-5",
|
|
20
|
+
apiKey: "${ANTHROPIC_API_KEY}",
|
|
21
|
+
url: "https://api.anthropic.com",
|
|
22
|
+
provider: "anthropic"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let numbers: number[] = [${numbers.join(', ')}]
|
|
26
|
+
let sum: number = 0
|
|
27
|
+
|
|
28
|
+
for n in numbers {
|
|
29
|
+
let result: number = vibe "Add n to sum. Return only the number." calc default
|
|
30
|
+
sum = result
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let bonus: number = ${bonusNumber}
|
|
34
|
+
let final: number = vibe "Add bonus to sum. Return only the number." calc default
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
async function runTest(logAi = true): Promise<Runtime> {
|
|
38
|
+
const program = parse(VIBE_PROGRAM);
|
|
39
|
+
const runtime = new Runtime(
|
|
40
|
+
program,
|
|
41
|
+
createRealAIProvider(() => runtime.getState()),
|
|
42
|
+
{ logAiInteractions: logAi }
|
|
43
|
+
);
|
|
44
|
+
await runtime.run();
|
|
45
|
+
|
|
46
|
+
if (logAi) {
|
|
47
|
+
console.log('\n' + formatAIInteractions(runtime.getAIInteractions()));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return runtime;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe.skipIf(!shouldRun)('Cumulative Sum Integration', () => {
|
|
54
|
+
test('AI adds numbers correctly in a loop', async () => {
|
|
55
|
+
console.log(`\nTest numbers: [${numbers.join(', ')}] + bonus ${bonusNumber}`);
|
|
56
|
+
console.log(`Expected sum: ${expectedSum}`);
|
|
57
|
+
|
|
58
|
+
const runtime = await runTest();
|
|
59
|
+
|
|
60
|
+
const finalSum = runtime.getValue('final') as number;
|
|
61
|
+
console.log(`AI computed sum: ${finalSum}`);
|
|
62
|
+
|
|
63
|
+
// AI should compute the correct sum
|
|
64
|
+
expect(finalSum).toBe(expectedSum);
|
|
65
|
+
|
|
66
|
+
// Verify we made the right number of AI calls (loop + bonus)
|
|
67
|
+
const interactions = runtime.getAIInteractions();
|
|
68
|
+
expect(interactions.length).toBe(numbers.length + 1);
|
|
69
|
+
|
|
70
|
+
// Each interaction should be a number response
|
|
71
|
+
for (const interaction of interactions) {
|
|
72
|
+
expect(interaction.targetType).toBe('number');
|
|
73
|
+
}
|
|
74
|
+
}, 120000);
|
|
75
|
+
});
|