@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,166 @@
|
|
|
1
|
+
// Basic AI Integration Tests
|
|
2
|
+
// Tests real API calls with OpenAI, Anthropic, and Google for all return types
|
|
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
|
+
// API Keys from environment
|
|
10
|
+
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
11
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
12
|
+
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
|
|
13
|
+
|
|
14
|
+
// Provider configurations
|
|
15
|
+
const providers = [
|
|
16
|
+
{
|
|
17
|
+
name: 'OpenAI',
|
|
18
|
+
hasKey: !!OPENAI_API_KEY,
|
|
19
|
+
modelConfig: `
|
|
20
|
+
model testModel = {
|
|
21
|
+
name: "gpt-5-mini",
|
|
22
|
+
apiKey: "${OPENAI_API_KEY}",
|
|
23
|
+
url: "https://api.openai.com/v1",
|
|
24
|
+
provider: "openai"
|
|
25
|
+
}
|
|
26
|
+
`,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Anthropic',
|
|
30
|
+
hasKey: !!ANTHROPIC_API_KEY,
|
|
31
|
+
modelConfig: `
|
|
32
|
+
model testModel = {
|
|
33
|
+
name: "claude-haiku-4-5",
|
|
34
|
+
apiKey: "${ANTHROPIC_API_KEY}",
|
|
35
|
+
url: "https://api.anthropic.com",
|
|
36
|
+
provider: "anthropic"
|
|
37
|
+
}
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Google',
|
|
42
|
+
hasKey: !!GOOGLE_API_KEY,
|
|
43
|
+
modelConfig: `
|
|
44
|
+
model testModel = {
|
|
45
|
+
name: "gemini-3-flash-preview",
|
|
46
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
47
|
+
provider: "google"
|
|
48
|
+
}
|
|
49
|
+
`,
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Shared Vibe code for each test case
|
|
54
|
+
const testCases = [
|
|
55
|
+
{
|
|
56
|
+
name: 'returns text response',
|
|
57
|
+
vibeCode: `let result: text = vibe "Reply with exactly: PONG" testModel default`,
|
|
58
|
+
assert: (runtime: Runtime) => {
|
|
59
|
+
const result = runtime.getValue('result') as string;
|
|
60
|
+
expect(typeof result).toBe('string');
|
|
61
|
+
expect(result.toUpperCase()).toContain('PONG');
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'returns number response',
|
|
66
|
+
vibeCode: `let result: number = vibe "What is 2 + 2? Reply with just the number." testModel default`,
|
|
67
|
+
assert: (runtime: Runtime) => {
|
|
68
|
+
const result = runtime.getValue('result');
|
|
69
|
+
expect(typeof result).toBe('number');
|
|
70
|
+
expect(result).toBe(4);
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'returns boolean response',
|
|
75
|
+
vibeCode: `let result: boolean = vibe "Is 5 greater than 3? Reply with true or false only." testModel default`,
|
|
76
|
+
assert: (runtime: Runtime) => {
|
|
77
|
+
const result = runtime.getValue('result');
|
|
78
|
+
expect(typeof result).toBe('boolean');
|
|
79
|
+
expect(result).toBe(true);
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'returns json response',
|
|
84
|
+
vibeCode: `let result: json = vibe "Return a JSON object with name set to Alice and age set to 30" testModel default`,
|
|
85
|
+
assert: (runtime: Runtime) => {
|
|
86
|
+
const result = runtime.getValue('result') as Record<string, unknown>;
|
|
87
|
+
expect(typeof result).toBe('object');
|
|
88
|
+
expect(result).not.toBeNull();
|
|
89
|
+
expect(result.name).toBe('Alice');
|
|
90
|
+
expect(result.age).toBe(30);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'returns text[] response',
|
|
95
|
+
vibeCode: `let result: text[] = vibe "Return a JSON array of 3 colors: red, green, blue" testModel default`,
|
|
96
|
+
assert: (runtime: Runtime) => {
|
|
97
|
+
const result = runtime.getValue('result') as string[];
|
|
98
|
+
expect(Array.isArray(result)).toBe(true);
|
|
99
|
+
expect(result).toHaveLength(3);
|
|
100
|
+
expect(result).toContain('red');
|
|
101
|
+
expect(result).toContain('green');
|
|
102
|
+
expect(result).toContain('blue');
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'returns number[] response',
|
|
107
|
+
vibeCode: `let result: number[] = vibe "Return a JSON array of the first 5 prime numbers" testModel default`,
|
|
108
|
+
assert: (runtime: Runtime) => {
|
|
109
|
+
const result = runtime.getValue('result') as number[];
|
|
110
|
+
expect(Array.isArray(result)).toBe(true);
|
|
111
|
+
expect(result).toHaveLength(5);
|
|
112
|
+
expect(result).toEqual([2, 3, 5, 7, 11]);
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'returns boolean[] response',
|
|
117
|
+
vibeCode: `let result: boolean[] = vibe "Return a JSON array: [true, false, true]" testModel default`,
|
|
118
|
+
assert: (runtime: Runtime) => {
|
|
119
|
+
const result = runtime.getValue('result') as boolean[];
|
|
120
|
+
expect(Array.isArray(result)).toBe(true);
|
|
121
|
+
expect(result).toEqual([true, false, true]);
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'returns json[] response',
|
|
126
|
+
vibeCode: `let result: json[] = do "Return a JSON array with 2 objects: first with name Alice and age 30, second with name Bob and age 25" testModel default`,
|
|
127
|
+
assert: (runtime: Runtime) => {
|
|
128
|
+
const result = runtime.getValue('result') as Array<Record<string, unknown>>;
|
|
129
|
+
expect(Array.isArray(result)).toBe(true);
|
|
130
|
+
expect(result).toHaveLength(2);
|
|
131
|
+
expect(result[0].name).toBe('Alice');
|
|
132
|
+
expect(result[0].age).toBe(30);
|
|
133
|
+
expect(result[1].name).toBe('Bob');
|
|
134
|
+
expect(result[1].age).toBe(25);
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
async function runVibe(modelConfig: string, vibeCode: string, logAi = true): Promise<Runtime> {
|
|
140
|
+
const program = parse(modelConfig + vibeCode);
|
|
141
|
+
const runtime = new Runtime(
|
|
142
|
+
program,
|
|
143
|
+
createRealAIProvider(() => runtime.getState()),
|
|
144
|
+
{ logAiInteractions: logAi }
|
|
145
|
+
);
|
|
146
|
+
await runtime.run();
|
|
147
|
+
|
|
148
|
+
if (logAi) {
|
|
149
|
+
const interactions = runtime.getAIInteractions();
|
|
150
|
+
console.log('\n' + formatAIInteractions(interactions));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return runtime;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Generate tests for each provider
|
|
157
|
+
for (const provider of providers) {
|
|
158
|
+
describe.skipIf(!provider.hasKey)(`${provider.name} Integration - Return Types`, () => {
|
|
159
|
+
for (const testCase of testCases) {
|
|
160
|
+
test(testCase.name, async () => {
|
|
161
|
+
const runtime = await runVibe(provider.modelConfig, testCase.vibeCode);
|
|
162
|
+
testCase.assert(runtime);
|
|
163
|
+
}, 30000);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Basic AI Integration Test
|
|
2
|
+
// Tests the most fundamental AI operation: a simple prompt
|
|
3
|
+
|
|
4
|
+
model testModel = {
|
|
5
|
+
name: "gpt-5-mini",
|
|
6
|
+
apiKey: OPENAI_API_KEY,
|
|
7
|
+
url: "https://api.openai.com/v1",
|
|
8
|
+
provider: "openai"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Test: Basic text response - model should reply with exactly "PONG"
|
|
12
|
+
let greeting = vibe "Reply with exactly: PONG" testModel default
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Bug Fix Integration Test
|
|
2
|
+
// Tests that AI can use tools to find and fix bugs in real code
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect, beforeAll, afterAll, beforeEach } 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
|
+
// API Keys from environment
|
|
12
|
+
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
13
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
14
|
+
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
|
|
15
|
+
|
|
16
|
+
// Test directory setup
|
|
17
|
+
const BASE_WORKSPACE = path.join(__dirname, '.test-workspace');
|
|
18
|
+
|
|
19
|
+
// The buggy code - sumArray has an off-by-one error (starts at index 1 instead of 0)
|
|
20
|
+
const BUGGY_CODE = `// Array utilities module
|
|
21
|
+
|
|
22
|
+
export function sumArray(numbers: number[]): number {
|
|
23
|
+
// Bug: loop starts at index 1, skipping the first element
|
|
24
|
+
let total = 0;
|
|
25
|
+
for (let i = 1; i < numbers.length; i++) {
|
|
26
|
+
total += numbers[i];
|
|
27
|
+
}
|
|
28
|
+
return total;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function findMax(numbers: number[]): number {
|
|
32
|
+
if (numbers.length === 0) {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
// This function is correct - starts at 1 because max is initialized with numbers[0]
|
|
36
|
+
let max = numbers[0];
|
|
37
|
+
for (let j = 1; j < numbers.length; j++) {
|
|
38
|
+
if (numbers[j] > max) {
|
|
39
|
+
max = numbers[j];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return max;
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
// Generate Vibe program with provider-specific model config
|
|
47
|
+
function createVibeProgram(modelConfig: string): string {
|
|
48
|
+
return `
|
|
49
|
+
import { readFile, edit } from "system/tools"
|
|
50
|
+
|
|
51
|
+
${modelConfig}
|
|
52
|
+
|
|
53
|
+
let result: text = vibe "There's a file 'buggy-code.ts' with array utility functions.
|
|
54
|
+
|
|
55
|
+
A test is failing: sumArray([1, 2, 3, 4, 5]) returns 14 but should return 15.
|
|
56
|
+
|
|
57
|
+
Read the file, find the bug, and fix it. After fixing, respond with 'FIXED'." fixer default
|
|
58
|
+
|
|
59
|
+
result
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Core test function that runs the bug fix test for a given provider
|
|
64
|
+
async function runBugFixTest(
|
|
65
|
+
providerName: string,
|
|
66
|
+
modelConfig: string,
|
|
67
|
+
workspace: string
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
const buggyFile = path.join(workspace, 'buggy-code.ts');
|
|
70
|
+
|
|
71
|
+
// Ensure workspace exists and reset the buggy file
|
|
72
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
73
|
+
fs.writeFileSync(buggyFile, BUGGY_CODE);
|
|
74
|
+
|
|
75
|
+
// Verify the code is buggy
|
|
76
|
+
const buggyModule = await import(`${buggyFile}?t=${Date.now()}`);
|
|
77
|
+
const buggyResult = buggyModule.sumArray([1, 2, 3, 4, 5]);
|
|
78
|
+
console.log(`\n[${providerName}] Buggy sumArray([1,2,3,4,5]) = ${buggyResult} (expected 15)`);
|
|
79
|
+
expect(buggyResult).toBe(14);
|
|
80
|
+
|
|
81
|
+
// Run the Vibe program
|
|
82
|
+
const program = parse(createVibeProgram(modelConfig));
|
|
83
|
+
const runtime = new Runtime(
|
|
84
|
+
program,
|
|
85
|
+
createRealAIProvider(() => runtime.getState()),
|
|
86
|
+
{ logAiInteractions: true, rootDir: workspace }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
await runtime.run();
|
|
90
|
+
|
|
91
|
+
// Log AI interactions
|
|
92
|
+
const state = runtime.getState();
|
|
93
|
+
console.log('\n' + formatAIInteractions(state.aiInteractions));
|
|
94
|
+
|
|
95
|
+
// Verify tool calls were made
|
|
96
|
+
const hasToolCalls = state.callStack.some(frame =>
|
|
97
|
+
frame.orderedEntries.some(entry =>
|
|
98
|
+
entry.kind === 'prompt' && entry.toolCalls && entry.toolCalls.length > 0
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
console.log(`\n[${providerName}] Tool calls made: ${hasToolCalls}`);
|
|
102
|
+
expect(hasToolCalls).toBe(true);
|
|
103
|
+
|
|
104
|
+
// Verify the fix
|
|
105
|
+
const fixedCode = fs.readFileSync(buggyFile, 'utf-8');
|
|
106
|
+
console.log(`\n[${providerName}] Fixed code:\n`, fixedCode);
|
|
107
|
+
expect(fixedCode).toContain('i = 0');
|
|
108
|
+
|
|
109
|
+
// Test the fixed code
|
|
110
|
+
const fixedModule = await import(`${buggyFile}?t=${Date.now()}`);
|
|
111
|
+
const fixedResult = fixedModule.sumArray([1, 2, 3, 4, 5]);
|
|
112
|
+
console.log(`[${providerName}] Fixed sumArray([1,2,3,4,5]) = ${fixedResult}`);
|
|
113
|
+
expect(fixedResult).toBe(15);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Test findMax still works after fix
|
|
117
|
+
async function testFindMaxAfterFix(workspace: string): Promise<void> {
|
|
118
|
+
const buggyFile = path.join(workspace, 'buggy-code.ts');
|
|
119
|
+
const fixedModule = await import(`${buggyFile}?t=${Date.now()}`);
|
|
120
|
+
expect(fixedModule.findMax([3, 1, 4, 1, 5, 9, 2, 6])).toBe(9);
|
|
121
|
+
expect(fixedModule.findMax([1])).toBe(1);
|
|
122
|
+
expect(fixedModule.findMax([])).toBe(0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Cleanup workspace
|
|
126
|
+
function cleanupWorkspace(workspace: string): void {
|
|
127
|
+
if (fs.existsSync(workspace)) {
|
|
128
|
+
fs.rmSync(workspace, { recursive: true });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// OpenAI Tests
|
|
133
|
+
describe.skipIf(!OPENAI_API_KEY)('Bug Fix Integration - OpenAI', () => {
|
|
134
|
+
const workspace = path.join(BASE_WORKSPACE, 'openai');
|
|
135
|
+
const modelConfig = `
|
|
136
|
+
model fixer = {
|
|
137
|
+
name: "gpt-5-mini",
|
|
138
|
+
apiKey: "${OPENAI_API_KEY}",
|
|
139
|
+
url: "https://api.openai.com/v1",
|
|
140
|
+
provider: "openai",
|
|
141
|
+
tools: [readFile, edit]
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
afterAll(() => cleanupWorkspace(workspace));
|
|
146
|
+
|
|
147
|
+
test('AI diagnoses and fixes bug using tools', async () => {
|
|
148
|
+
await runBugFixTest('OpenAI', modelConfig, workspace);
|
|
149
|
+
}, 120000);
|
|
150
|
+
|
|
151
|
+
test('findMax function still works after fix', async () => {
|
|
152
|
+
await testFindMaxAfterFix(workspace);
|
|
153
|
+
}, 10000);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Anthropic Tests
|
|
157
|
+
describe.skipIf(!ANTHROPIC_API_KEY)('Bug Fix Integration - Anthropic', () => {
|
|
158
|
+
const workspace = path.join(BASE_WORKSPACE, 'anthropic');
|
|
159
|
+
const modelConfig = `
|
|
160
|
+
model fixer = {
|
|
161
|
+
name: "claude-haiku-4-5",
|
|
162
|
+
apiKey: "${ANTHROPIC_API_KEY}",
|
|
163
|
+
url: "https://api.anthropic.com",
|
|
164
|
+
provider: "anthropic",
|
|
165
|
+
tools: [readFile, edit]
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
afterAll(() => cleanupWorkspace(workspace));
|
|
170
|
+
|
|
171
|
+
test('AI diagnoses and fixes bug using tools', async () => {
|
|
172
|
+
await runBugFixTest('Anthropic', modelConfig, workspace);
|
|
173
|
+
}, 120000);
|
|
174
|
+
|
|
175
|
+
test('findMax function still works after fix', async () => {
|
|
176
|
+
await testFindMaxAfterFix(workspace);
|
|
177
|
+
}, 10000);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Google Tests
|
|
181
|
+
describe.skipIf(!GOOGLE_API_KEY)('Bug Fix Integration - Google', () => {
|
|
182
|
+
const workspace = path.join(BASE_WORKSPACE, 'google');
|
|
183
|
+
const modelConfig = `
|
|
184
|
+
model fixer = {
|
|
185
|
+
name: "gemini-3-flash-preview",
|
|
186
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
187
|
+
provider: "google",
|
|
188
|
+
tools: [readFile, edit]
|
|
189
|
+
}
|
|
190
|
+
`;
|
|
191
|
+
|
|
192
|
+
afterAll(() => cleanupWorkspace(workspace));
|
|
193
|
+
|
|
194
|
+
test('AI diagnoses and fixes bug using tools', async () => {
|
|
195
|
+
await runBugFixTest('Google', modelConfig, workspace);
|
|
196
|
+
}, 120000);
|
|
197
|
+
|
|
198
|
+
test('findMax function still works after fix', async () => {
|
|
199
|
+
await testFindMaxAfterFix(workspace);
|
|
200
|
+
}, 10000);
|
|
201
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Array utilities module
|
|
2
|
+
|
|
3
|
+
export function sumArray(numbers: number[]): number {
|
|
4
|
+
let total = 0;
|
|
5
|
+
for (let i = 1; i < numbers.length; i++) {
|
|
6
|
+
total += numbers[i];
|
|
7
|
+
}
|
|
8
|
+
return total;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function findMax(numbers: number[]): number {
|
|
12
|
+
if (numbers.length === 0) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
let max = numbers[0];
|
|
16
|
+
for (let i = 1; i < numbers.length; i++) {
|
|
17
|
+
if (numbers[i] > max) {
|
|
18
|
+
max = numbers[i];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return max;
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Bug Fix Script
|
|
2
|
+
// AI diagnoses and fixes bugs - figures out which tools to use on its own
|
|
3
|
+
|
|
4
|
+
model fixer = {
|
|
5
|
+
name: "claude-haiku-4-5",
|
|
6
|
+
apiKey: env("ANTHROPIC_API_KEY"),
|
|
7
|
+
url: "https://api.anthropic.com",
|
|
8
|
+
provider: "anthropic"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let code = readFile("buggy-code.ts")
|
|
12
|
+
|
|
13
|
+
let result: text = vibe "Here's a TypeScript file 'buggy-code.ts':
|
|
14
|
+
|
|
15
|
+
{code}
|
|
16
|
+
|
|
17
|
+
A test is failing: sumArray([1, 2, 3, 4, 5]) returns 14 but should return 15.
|
|
18
|
+
|
|
19
|
+
Fix the bug. After fixing, respond with 'FIXED'." fixer default
|
|
20
|
+
|
|
21
|
+
result
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Compress Context Mode Integration Test
|
|
2
|
+
// Tests that compress mode calls AI to summarize loop context
|
|
3
|
+
|
|
4
|
+
import { describe, test, expect } from 'bun:test';
|
|
5
|
+
import { Runtime } 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
|
+
|
|
10
|
+
// Use Google's Gemini Flash for cheap/fast summarization
|
|
11
|
+
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
|
|
12
|
+
const shouldRun = !!GOOGLE_API_KEY;
|
|
13
|
+
|
|
14
|
+
describe.skipIf(!shouldRun)('Compress Integration', () => {
|
|
15
|
+
test(
|
|
16
|
+
'compress summarizes loop iterations',
|
|
17
|
+
async () => {
|
|
18
|
+
const program = parse(`
|
|
19
|
+
model m = {
|
|
20
|
+
name: "gemini-3-flash-preview",
|
|
21
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
22
|
+
provider: "google"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Simple loop that compress will summarize
|
|
26
|
+
let results: number[] = []
|
|
27
|
+
for i in [1, 2, 3, 4, 5] {
|
|
28
|
+
let squared = ts(i) { return i * i }
|
|
29
|
+
results.push(squared)
|
|
30
|
+
} compress
|
|
31
|
+
|
|
32
|
+
results
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
const runtime = new Runtime(
|
|
36
|
+
program,
|
|
37
|
+
createRealAIProvider(() => runtime.getState()),
|
|
38
|
+
{ logAiInteractions: true }
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const result = await runtime.run();
|
|
42
|
+
|
|
43
|
+
// Verify the loop executed correctly
|
|
44
|
+
expect(result).toEqual([1, 4, 9, 16, 25]);
|
|
45
|
+
|
|
46
|
+
// Get final context
|
|
47
|
+
const state = runtime.getState();
|
|
48
|
+
const context = buildLocalContext(state);
|
|
49
|
+
const formatted = formatContextForAI(context);
|
|
50
|
+
|
|
51
|
+
console.log('\n=== FINAL CONTEXT ===');
|
|
52
|
+
console.log(formatted.text);
|
|
53
|
+
|
|
54
|
+
// Verify there's a summary in the context
|
|
55
|
+
const summaryEntry = context.find(e => e.kind === 'summary');
|
|
56
|
+
expect(summaryEntry).toBeDefined();
|
|
57
|
+
console.log('\n=== SUMMARY ===');
|
|
58
|
+
console.log((summaryEntry as { text: string }).text);
|
|
59
|
+
|
|
60
|
+
// The summary should exist and be non-empty
|
|
61
|
+
expect((summaryEntry as { text: string }).text.length).toBeGreaterThan(0);
|
|
62
|
+
},
|
|
63
|
+
60000
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
test(
|
|
67
|
+
'compress with custom prompt',
|
|
68
|
+
async () => {
|
|
69
|
+
const program = parse(`
|
|
70
|
+
model m = {
|
|
71
|
+
name: "gemini-3-flash-preview",
|
|
72
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
73
|
+
provider: "google"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let items: text[] = []
|
|
77
|
+
for word in ["apple", "banana", "cherry"] {
|
|
78
|
+
let upper: text = ts(word) { return word.toUpperCase() }
|
|
79
|
+
items.push(upper)
|
|
80
|
+
} compress("List the fruits that were processed")
|
|
81
|
+
|
|
82
|
+
items
|
|
83
|
+
`);
|
|
84
|
+
|
|
85
|
+
const runtime = new Runtime(
|
|
86
|
+
program,
|
|
87
|
+
createRealAIProvider(() => runtime.getState()),
|
|
88
|
+
{ logAiInteractions: true }
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const result = await runtime.run();
|
|
92
|
+
|
|
93
|
+
// Verify the loop executed correctly
|
|
94
|
+
expect(result).toEqual(['APPLE', 'BANANA', 'CHERRY']);
|
|
95
|
+
|
|
96
|
+
// Get final context and verify summary
|
|
97
|
+
const state = runtime.getState();
|
|
98
|
+
const context = buildLocalContext(state);
|
|
99
|
+
const summaryEntry = context.find(e => e.kind === 'summary');
|
|
100
|
+
|
|
101
|
+
expect(summaryEntry).toBeDefined();
|
|
102
|
+
const summaryText = (summaryEntry as { text: string }).text.toLowerCase();
|
|
103
|
+
console.log('\n=== SUMMARY ===');
|
|
104
|
+
console.log((summaryEntry as { text: string }).text);
|
|
105
|
+
|
|
106
|
+
// Summary should mention the fruits
|
|
107
|
+
expect(
|
|
108
|
+
summaryText.includes('apple') ||
|
|
109
|
+
summaryText.includes('banana') ||
|
|
110
|
+
summaryText.includes('cherry') ||
|
|
111
|
+
summaryText.includes('fruit')
|
|
112
|
+
).toBe(true);
|
|
113
|
+
},
|
|
114
|
+
60000
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
test(
|
|
118
|
+
'compress with explicit model',
|
|
119
|
+
async () => {
|
|
120
|
+
const program = parse(`
|
|
121
|
+
model main = {
|
|
122
|
+
name: "gemini-3-flash-preview",
|
|
123
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
124
|
+
provider: "google"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
model summarizer = {
|
|
128
|
+
name: "gemini-3-flash-preview",
|
|
129
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
130
|
+
provider: "google"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let total = 0
|
|
134
|
+
for n in [10, 20, 30] {
|
|
135
|
+
total = total + n
|
|
136
|
+
} compress(summarizer)
|
|
137
|
+
|
|
138
|
+
total
|
|
139
|
+
`);
|
|
140
|
+
|
|
141
|
+
const runtime = new Runtime(
|
|
142
|
+
program,
|
|
143
|
+
createRealAIProvider(() => runtime.getState()),
|
|
144
|
+
{ logAiInteractions: true }
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const result = await runtime.run();
|
|
148
|
+
|
|
149
|
+
// Verify the loop executed correctly
|
|
150
|
+
expect(result).toBe(60);
|
|
151
|
+
|
|
152
|
+
// Verify summary exists
|
|
153
|
+
const state = runtime.getState();
|
|
154
|
+
const context = buildLocalContext(state);
|
|
155
|
+
const summaryEntry = context.find(e => e.kind === 'summary');
|
|
156
|
+
|
|
157
|
+
expect(summaryEntry).toBeDefined();
|
|
158
|
+
console.log('\n=== SUMMARY ===');
|
|
159
|
+
console.log((summaryEntry as { text: string }).text);
|
|
160
|
+
},
|
|
161
|
+
60000
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
test(
|
|
165
|
+
'while loop with compress',
|
|
166
|
+
async () => {
|
|
167
|
+
const program = parse(`
|
|
168
|
+
model m = {
|
|
169
|
+
name: "gemini-3-flash-preview",
|
|
170
|
+
apiKey: "${GOOGLE_API_KEY}",
|
|
171
|
+
provider: "google"
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let count = 0
|
|
175
|
+
let sum = 0
|
|
176
|
+
while (count < 4) {
|
|
177
|
+
count = count + 1
|
|
178
|
+
sum = sum + count
|
|
179
|
+
} compress
|
|
180
|
+
|
|
181
|
+
sum
|
|
182
|
+
`);
|
|
183
|
+
|
|
184
|
+
const runtime = new Runtime(
|
|
185
|
+
program,
|
|
186
|
+
createRealAIProvider(() => runtime.getState()),
|
|
187
|
+
{ logAiInteractions: true }
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const result = await runtime.run();
|
|
191
|
+
|
|
192
|
+
// sum = 1 + 2 + 3 + 4 = 10
|
|
193
|
+
expect(result).toBe(10);
|
|
194
|
+
|
|
195
|
+
// Verify summary exists
|
|
196
|
+
const state = runtime.getState();
|
|
197
|
+
const context = buildLocalContext(state);
|
|
198
|
+
const summaryEntry = context.find(e => e.kind === 'summary');
|
|
199
|
+
|
|
200
|
+
expect(summaryEntry).toBeDefined();
|
|
201
|
+
console.log('\n=== SUMMARY ===');
|
|
202
|
+
console.log((summaryEntry as { text: string }).text);
|
|
203
|
+
},
|
|
204
|
+
60000
|
|
205
|
+
);
|
|
206
|
+
});
|