@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
package/src/parser.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { chunkContextForCaching } from './cache-chunking';
|
|
3
|
+
|
|
4
|
+
describe('chunkContextForCaching', () => {
|
|
5
|
+
it('returns empty chunks for empty context', () => {
|
|
6
|
+
const result = chunkContextForCaching('');
|
|
7
|
+
expect(result.chunks).toEqual([]);
|
|
8
|
+
expect(result.cacheBreakpointIndex).toBe(-1);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns empty chunks for whitespace-only context', () => {
|
|
12
|
+
const result = chunkContextForCaching(' \n\n ');
|
|
13
|
+
expect(result.chunks).toEqual([]);
|
|
14
|
+
expect(result.cacheBreakpointIndex).toBe(-1);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('creates single chunk for small context', () => {
|
|
18
|
+
const context = '- name: Alice\n- age: 30';
|
|
19
|
+
const result = chunkContextForCaching(context);
|
|
20
|
+
expect(result.chunks.length).toBe(1);
|
|
21
|
+
expect(result.chunks[0].content).toBe(context);
|
|
22
|
+
expect(result.cacheBreakpointIndex).toBe(-1); // No 2nd-to-last with single chunk
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('creates multiple chunks when context exceeds threshold', () => {
|
|
26
|
+
// Create context that exceeds 5000 tokens (~20000 chars)
|
|
27
|
+
const largeLine = '- data: ' + 'x'.repeat(10000); // ~2500 tokens
|
|
28
|
+
const context = [largeLine, largeLine, largeLine].join('\n'); // ~7500 tokens total
|
|
29
|
+
|
|
30
|
+
const result = chunkContextForCaching(context);
|
|
31
|
+
expect(result.chunks.length).toBeGreaterThan(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('sets cache breakpoint on 2nd-to-last chunk', () => {
|
|
35
|
+
// Create context that will produce exactly 2 chunks
|
|
36
|
+
const largeLine = '- data: ' + 'x'.repeat(10000);
|
|
37
|
+
const context = [largeLine, largeLine].join('\n');
|
|
38
|
+
|
|
39
|
+
const result = chunkContextForCaching(context);
|
|
40
|
+
expect(result.chunks.length).toBe(2);
|
|
41
|
+
expect(result.cacheBreakpointIndex).toBe(0); // 2nd-to-last of 2 = index 0
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('sets cache breakpoint correctly for 3 chunks', () => {
|
|
45
|
+
// Create context that will produce 3 chunks
|
|
46
|
+
const largeLine = '- data: ' + 'x'.repeat(10000);
|
|
47
|
+
const context = [largeLine, largeLine, largeLine].join('\n');
|
|
48
|
+
|
|
49
|
+
const result = chunkContextForCaching(context);
|
|
50
|
+
expect(result.chunks.length).toBe(3);
|
|
51
|
+
expect(result.cacheBreakpointIndex).toBe(1); // 2nd-to-last of 3 = index 1
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('preserves line content in chunks', () => {
|
|
55
|
+
const lines = ['- a: 1', '- b: 2', '- c: 3'];
|
|
56
|
+
const context = lines.join('\n');
|
|
57
|
+
|
|
58
|
+
const result = chunkContextForCaching(context);
|
|
59
|
+
expect(result.chunks.length).toBe(1);
|
|
60
|
+
expect(result.chunks[0].content).toBe(context);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('estimates token count approximately', () => {
|
|
64
|
+
// 400 chars should be ~100 tokens (4 chars/token)
|
|
65
|
+
const context = 'x'.repeat(400);
|
|
66
|
+
const result = chunkContextForCaching(context);
|
|
67
|
+
expect(result.chunks[0].tokenEstimate).toBe(100);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Anthropic prompt caching utilities
|
|
2
|
+
// Splits context into chunks for progressive caching
|
|
3
|
+
|
|
4
|
+
const TARGET_CHUNK_TOKENS = 5000;
|
|
5
|
+
const CHARS_PER_TOKEN = 4; // Rough estimate for token counting
|
|
6
|
+
|
|
7
|
+
/** A chunk of context content with estimated token count */
|
|
8
|
+
export interface CacheChunk {
|
|
9
|
+
content: string;
|
|
10
|
+
tokenEstimate: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Chunked context ready for Anthropic API with cache breakpoint info */
|
|
14
|
+
export interface ChunkedContext {
|
|
15
|
+
chunks: CacheChunk[];
|
|
16
|
+
cacheBreakpointIndex: number; // Which chunk gets cache_control (-1 if none)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Estimate token count for a string using character-based heuristic.
|
|
21
|
+
*/
|
|
22
|
+
function estimateTokens(text: string): number {
|
|
23
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Split formatted context text into chunks of ~5000 tokens each.
|
|
28
|
+
* Splits by line boundaries (each context entry is on its own line).
|
|
29
|
+
* Accumulates lines until hitting threshold, then starts new chunk.
|
|
30
|
+
* Cache breakpoint is placed on 2nd-to-last chunk to allow latest chunk to change.
|
|
31
|
+
*/
|
|
32
|
+
export function chunkContextForCaching(contextText: string): ChunkedContext {
|
|
33
|
+
const trimmed = contextText.trim();
|
|
34
|
+
if (!trimmed) {
|
|
35
|
+
return { chunks: [], cacheBreakpointIndex: -1 };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const lines = trimmed.split('\n');
|
|
39
|
+
const chunks: CacheChunk[] = [];
|
|
40
|
+
let currentLines: string[] = [];
|
|
41
|
+
let currentTokens = 0;
|
|
42
|
+
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
const lineTokens = estimateTokens(line);
|
|
45
|
+
|
|
46
|
+
// If adding this line exceeds threshold and we have content, start new chunk
|
|
47
|
+
if (currentTokens + lineTokens > TARGET_CHUNK_TOKENS && currentLines.length > 0) {
|
|
48
|
+
chunks.push({
|
|
49
|
+
content: currentLines.join('\n'),
|
|
50
|
+
tokenEstimate: currentTokens,
|
|
51
|
+
});
|
|
52
|
+
currentLines = [];
|
|
53
|
+
currentTokens = 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
currentLines.push(line);
|
|
57
|
+
currentTokens += lineTokens;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Push final chunk if we have content
|
|
61
|
+
if (currentLines.length > 0) {
|
|
62
|
+
chunks.push({
|
|
63
|
+
content: currentLines.join('\n'),
|
|
64
|
+
tokenEstimate: currentTokens,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Cache breakpoint on 2nd-to-last chunk (or -1 if < 2 chunks)
|
|
69
|
+
// This allows the last chunk to change without invalidating the cache
|
|
70
|
+
const cacheBreakpointIndex = chunks.length >= 2 ? chunks.length - 2 : -1;
|
|
71
|
+
|
|
72
|
+
return { chunks, cacheBreakpointIndex };
|
|
73
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// AI Client - Main orchestration for AI API calls
|
|
2
|
+
|
|
3
|
+
import type { AIProviderType, AIRequest, AIResponse, TargetType, ModelConfig, ProviderExecutor } from './types';
|
|
4
|
+
import type { VibeToolValue } from '../tools/types';
|
|
5
|
+
import { withRetry } from './retry';
|
|
6
|
+
import { executeOpenAI } from './providers/openai';
|
|
7
|
+
import { executeAnthropic } from './providers/anthropic';
|
|
8
|
+
import { executeGoogle } from './providers/google';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Detect provider from URL.
|
|
12
|
+
* Returns 'openai' as default for unknown/custom endpoints.
|
|
13
|
+
*/
|
|
14
|
+
export function detectProvider(url: string | null): AIProviderType {
|
|
15
|
+
if (!url) return 'openai';
|
|
16
|
+
|
|
17
|
+
const u = url.toLowerCase();
|
|
18
|
+
if (u.includes('anthropic')) return 'anthropic';
|
|
19
|
+
if (u.includes('google') || u.includes('generativelanguage')) return 'google';
|
|
20
|
+
return 'openai';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the provider executor function for a given provider type.
|
|
25
|
+
*/
|
|
26
|
+
export function getProviderExecutor(provider: AIProviderType): ProviderExecutor {
|
|
27
|
+
switch (provider) {
|
|
28
|
+
case 'anthropic':
|
|
29
|
+
return executeAnthropic;
|
|
30
|
+
case 'google':
|
|
31
|
+
return executeGoogle;
|
|
32
|
+
case 'openai':
|
|
33
|
+
default:
|
|
34
|
+
return executeOpenAI;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build an AI request from the given parameters.
|
|
40
|
+
*/
|
|
41
|
+
export function buildAIRequest(
|
|
42
|
+
model: ModelConfig,
|
|
43
|
+
prompt: string,
|
|
44
|
+
contextText: string,
|
|
45
|
+
operationType: 'do' | 'vibe',
|
|
46
|
+
targetType: TargetType
|
|
47
|
+
): AIRequest {
|
|
48
|
+
return {
|
|
49
|
+
operationType,
|
|
50
|
+
prompt,
|
|
51
|
+
contextText,
|
|
52
|
+
targetType,
|
|
53
|
+
model,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Model value as stored in runtime locals */
|
|
58
|
+
export interface VibeModelValue {
|
|
59
|
+
__vibeModel: true;
|
|
60
|
+
name: string | null;
|
|
61
|
+
apiKey: string | null;
|
|
62
|
+
url: string | null;
|
|
63
|
+
provider?: AIProviderType | null;
|
|
64
|
+
maxRetriesOnError?: number | null;
|
|
65
|
+
tools?: VibeToolValue[]; // Tools available to this model for AI calls
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Execute an AI request with the appropriate provider.
|
|
70
|
+
* Handles provider detection, request building, and retry logic.
|
|
71
|
+
*/
|
|
72
|
+
export async function executeAI(
|
|
73
|
+
modelValue: VibeModelValue,
|
|
74
|
+
prompt: string,
|
|
75
|
+
contextText: string,
|
|
76
|
+
operationType: 'do' | 'vibe',
|
|
77
|
+
targetType: TargetType
|
|
78
|
+
): Promise<AIResponse> {
|
|
79
|
+
// Validate model config
|
|
80
|
+
if (!modelValue.name) {
|
|
81
|
+
throw new Error('Model name is required');
|
|
82
|
+
}
|
|
83
|
+
if (!modelValue.apiKey) {
|
|
84
|
+
throw new Error('API key is required');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Determine provider (explicit or auto-detect)
|
|
88
|
+
const provider: AIProviderType =
|
|
89
|
+
(modelValue.provider as AIProviderType) ?? detectProvider(modelValue.url);
|
|
90
|
+
|
|
91
|
+
// Build model config
|
|
92
|
+
const model: ModelConfig = {
|
|
93
|
+
name: modelValue.name,
|
|
94
|
+
apiKey: modelValue.apiKey,
|
|
95
|
+
url: modelValue.url,
|
|
96
|
+
provider,
|
|
97
|
+
maxRetriesOnError: modelValue.maxRetriesOnError ?? undefined,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Build request
|
|
101
|
+
const request = buildAIRequest(model, prompt, contextText, operationType, targetType);
|
|
102
|
+
|
|
103
|
+
// Get provider executor
|
|
104
|
+
const execute = getProviderExecutor(provider);
|
|
105
|
+
|
|
106
|
+
// Execute with retry logic
|
|
107
|
+
const maxRetries = modelValue.maxRetriesOnError ?? 3;
|
|
108
|
+
return withRetry(() => execute(request), { maxRetries });
|
|
109
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Unified AI Context System
|
|
2
|
+
// Single source of truth for what gets sent to AI models and logged
|
|
3
|
+
//
|
|
4
|
+
// Architecture:
|
|
5
|
+
// buildAIContext(state) → AIContext (structured)
|
|
6
|
+
// ↓ ↓
|
|
7
|
+
// formatForProvider() formatForLog()
|
|
8
|
+
// ↓ ↓
|
|
9
|
+
// Provider API call Human-readable log
|
|
10
|
+
|
|
11
|
+
import type { RuntimeState, ContextEntry } from '../types';
|
|
12
|
+
import type { ToolSchema } from '../tools/types';
|
|
13
|
+
import type { TargetType, ModelConfig } from './types';
|
|
14
|
+
import { buildGlobalContext, formatContextForAI } from '../context';
|
|
15
|
+
// Import formatters for consistent message building
|
|
16
|
+
import {
|
|
17
|
+
buildSystemMessage as buildSystemMessageImpl,
|
|
18
|
+
buildToolSystemMessage as buildToolSystemMessageImpl,
|
|
19
|
+
buildPromptMessage as buildPromptMessageImpl,
|
|
20
|
+
} from './formatters';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Message in the AI conversation.
|
|
24
|
+
* Represents a single message sent to or received from the model.
|
|
25
|
+
*/
|
|
26
|
+
export interface AIMessage {
|
|
27
|
+
role: 'system' | 'user' | 'assistant';
|
|
28
|
+
content: string;
|
|
29
|
+
/** For assistant messages with tool calls */
|
|
30
|
+
toolCalls?: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
toolName: string;
|
|
33
|
+
args: Record<string, unknown>;
|
|
34
|
+
}>;
|
|
35
|
+
/** For user messages with tool results */
|
|
36
|
+
toolResults?: Array<{
|
|
37
|
+
toolCallId: string;
|
|
38
|
+
result?: unknown;
|
|
39
|
+
error?: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Complete AI context for a single interaction.
|
|
45
|
+
* This is the single source of truth for:
|
|
46
|
+
* - What gets sent to the AI model
|
|
47
|
+
* - What gets logged for debugging
|
|
48
|
+
*/
|
|
49
|
+
export interface AIContext {
|
|
50
|
+
/** Operation type */
|
|
51
|
+
operationType: 'do' | 'vibe';
|
|
52
|
+
|
|
53
|
+
/** Model being used */
|
|
54
|
+
model: ModelConfig;
|
|
55
|
+
|
|
56
|
+
/** Target type for response parsing */
|
|
57
|
+
targetType: TargetType;
|
|
58
|
+
|
|
59
|
+
/** Available tools (if any) */
|
|
60
|
+
tools?: ToolSchema[];
|
|
61
|
+
|
|
62
|
+
/** The complete message sequence to send to the model */
|
|
63
|
+
messages: AIMessage[];
|
|
64
|
+
|
|
65
|
+
/** Structured execution context (for detailed logging) */
|
|
66
|
+
executionContext: ContextEntry[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build the complete AI context from runtime state.
|
|
71
|
+
* This is the SINGLE SOURCE OF TRUTH for what gets sent to the model.
|
|
72
|
+
*/
|
|
73
|
+
export function buildAIContext(
|
|
74
|
+
state: RuntimeState,
|
|
75
|
+
model: ModelConfig,
|
|
76
|
+
prompt: string,
|
|
77
|
+
targetType: TargetType,
|
|
78
|
+
tools?: ToolSchema[],
|
|
79
|
+
previousToolCalls?: Array<{ id: string; toolName: string; args: Record<string, unknown> }>,
|
|
80
|
+
toolResults?: Array<{ toolCallId: string; result?: unknown; error?: string }>
|
|
81
|
+
): AIContext {
|
|
82
|
+
const operationType = state.pendingAI?.type ?? 'do';
|
|
83
|
+
|
|
84
|
+
// Build structured execution context from state
|
|
85
|
+
const executionContext = buildGlobalContext(state);
|
|
86
|
+
const formattedContext = formatContextForAI(executionContext);
|
|
87
|
+
|
|
88
|
+
// Build the message sequence
|
|
89
|
+
const messages: AIMessage[] = [];
|
|
90
|
+
|
|
91
|
+
// 1. System message
|
|
92
|
+
messages.push({
|
|
93
|
+
role: 'system',
|
|
94
|
+
content: buildSystemMessage(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 2. Tool system message (if tools available)
|
|
98
|
+
if (tools && tools.length > 0) {
|
|
99
|
+
messages.push({
|
|
100
|
+
role: 'system',
|
|
101
|
+
content: buildToolSystemMessage(tools),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 3. Context message (if there's execution context)
|
|
106
|
+
if (formattedContext.text) {
|
|
107
|
+
messages.push({
|
|
108
|
+
role: 'user',
|
|
109
|
+
content: `Here is the current program context:\n\n${formattedContext.text}`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 4. Current prompt
|
|
114
|
+
messages.push({
|
|
115
|
+
role: 'user',
|
|
116
|
+
content: buildPromptMessage(prompt, targetType),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// 5. Previous tool calls and results (for multi-turn)
|
|
120
|
+
if (previousToolCalls?.length && toolResults?.length) {
|
|
121
|
+
messages.push({
|
|
122
|
+
role: 'assistant',
|
|
123
|
+
content: '', // Content comes from tool calls
|
|
124
|
+
toolCalls: previousToolCalls,
|
|
125
|
+
});
|
|
126
|
+
messages.push({
|
|
127
|
+
role: 'user',
|
|
128
|
+
content: '', // Content comes from tool results
|
|
129
|
+
toolResults: toolResults,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
operationType,
|
|
135
|
+
model,
|
|
136
|
+
targetType,
|
|
137
|
+
tools,
|
|
138
|
+
messages,
|
|
139
|
+
executionContext,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build the base system message.
|
|
145
|
+
* Delegates to formatters.ts for consistent formatting.
|
|
146
|
+
*/
|
|
147
|
+
function buildSystemMessage(): string {
|
|
148
|
+
return buildSystemMessageImpl();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Build the tool system message describing available tools.
|
|
153
|
+
* Delegates to formatters.ts for consistent formatting.
|
|
154
|
+
*/
|
|
155
|
+
function buildToolSystemMessage(tools: ToolSchema[]): string {
|
|
156
|
+
return buildToolSystemMessageImpl(tools) ?? '';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Build the prompt message with type instructions if needed.
|
|
161
|
+
* Delegates to formatters.ts for consistent formatting.
|
|
162
|
+
*/
|
|
163
|
+
function buildPromptMessage(prompt: string, _targetType: TargetType): string {
|
|
164
|
+
return buildPromptMessageImpl(prompt);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Re-export formatters for backward compatibility
|
|
168
|
+
export { buildSystemMessage, buildToolSystemMessage, buildPromptMessage };
|