@orcalang/orca-lang 0.1.0
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/LICENSE +176 -0
- package/README.md +128 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/lock.d.ts +2 -0
- package/dist/auth/lock.d.ts.map +1 -0
- package/dist/auth/lock.js +59 -0
- package/dist/auth/lock.js.map +1 -0
- package/dist/auth/providers/anthropic.d.ts +14 -0
- package/dist/auth/providers/anthropic.d.ts.map +1 -0
- package/dist/auth/providers/anthropic.js +145 -0
- package/dist/auth/providers/anthropic.js.map +1 -0
- package/dist/auth/providers/index.d.ts +3 -0
- package/dist/auth/providers/index.d.ts.map +1 -0
- package/dist/auth/providers/index.js +3 -0
- package/dist/auth/providers/index.js.map +1 -0
- package/dist/auth/providers/minimax.d.ts +6 -0
- package/dist/auth/providers/minimax.d.ts.map +1 -0
- package/dist/auth/providers/minimax.js +65 -0
- package/dist/auth/providers/minimax.js.map +1 -0
- package/dist/auth/refresh.d.ts +8 -0
- package/dist/auth/refresh.d.ts.map +1 -0
- package/dist/auth/refresh.js +104 -0
- package/dist/auth/refresh.js.map +1 -0
- package/dist/auth/store.d.ts +11 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +63 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/auth/types.d.ts +51 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/compiler/mermaid.d.ts +3 -0
- package/dist/compiler/mermaid.d.ts.map +1 -0
- package/dist/compiler/mermaid.js +86 -0
- package/dist/compiler/mermaid.js.map +1 -0
- package/dist/compiler/xstate.d.ts +15 -0
- package/dist/compiler/xstate.d.ts.map +1 -0
- package/dist/compiler/xstate.js +542 -0
- package/dist/compiler/xstate.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +109 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +13 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +8 -0
- package/dist/config/types.js.map +1 -0
- package/dist/generators/index.d.ts +5 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +5 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/registry.d.ts +12 -0
- package/dist/generators/registry.d.ts.map +1 -0
- package/dist/generators/registry.js +15 -0
- package/dist/generators/registry.js.map +1 -0
- package/dist/generators/typescript.d.ts +9 -0
- package/dist/generators/typescript.d.ts.map +1 -0
- package/dist/generators/typescript.js +55 -0
- package/dist/generators/typescript.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +630 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic.d.ts +14 -0
- package/dist/llm/anthropic.d.ts.map +1 -0
- package/dist/llm/anthropic.js +87 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/grok.d.ts +13 -0
- package/dist/llm/grok.d.ts.map +1 -0
- package/dist/llm/grok.js +60 -0
- package/dist/llm/grok.js.map +1 -0
- package/dist/llm/index.d.ts +11 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +23 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/ollama.d.ts +11 -0
- package/dist/llm/ollama.d.ts.map +1 -0
- package/dist/llm/ollama.js +51 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +13 -0
- package/dist/llm/openai.d.ts.map +1 -0
- package/dist/llm/openai.js +61 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/provider.d.ts +32 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +2 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/parser/ast-to-markdown.d.ts +3 -0
- package/dist/parser/ast-to-markdown.d.ts.map +1 -0
- package/dist/parser/ast-to-markdown.js +209 -0
- package/dist/parser/ast-to-markdown.js.map +1 -0
- package/dist/parser/ast.d.ts +183 -0
- package/dist/parser/ast.d.ts.map +1 -0
- package/dist/parser/ast.js +3 -0
- package/dist/parser/ast.js.map +1 -0
- package/dist/parser/markdown-parser.d.ts +8 -0
- package/dist/parser/markdown-parser.d.ts.map +1 -0
- package/dist/parser/markdown-parser.js +838 -0
- package/dist/parser/markdown-parser.js.map +1 -0
- package/dist/runtime/effects.d.ts +17 -0
- package/dist/runtime/effects.d.ts.map +1 -0
- package/dist/runtime/effects.js +28 -0
- package/dist/runtime/effects.js.map +1 -0
- package/dist/runtime/machine.d.ts +8 -0
- package/dist/runtime/machine.d.ts.map +1 -0
- package/dist/runtime/machine.js +158 -0
- package/dist/runtime/machine.js.map +1 -0
- package/dist/runtime/types.d.ts +37 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +3 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/skills.d.ts +114 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +1103 -0
- package/dist/skills.js.map +1 -0
- package/dist/tools.d.ts +18 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +124 -0
- package/dist/tools.js.map +1 -0
- package/dist/verifier/completeness.d.ts +4 -0
- package/dist/verifier/completeness.d.ts.map +1 -0
- package/dist/verifier/completeness.js +82 -0
- package/dist/verifier/completeness.js.map +1 -0
- package/dist/verifier/determinism.d.ts +17 -0
- package/dist/verifier/determinism.d.ts.map +1 -0
- package/dist/verifier/determinism.js +301 -0
- package/dist/verifier/determinism.js.map +1 -0
- package/dist/verifier/properties.d.ts +6 -0
- package/dist/verifier/properties.d.ts.map +1 -0
- package/dist/verifier/properties.js +404 -0
- package/dist/verifier/properties.js.map +1 -0
- package/dist/verifier/structural.d.ts +50 -0
- package/dist/verifier/structural.d.ts.map +1 -0
- package/dist/verifier/structural.js +692 -0
- package/dist/verifier/structural.js.map +1 -0
- package/dist/verifier/types.d.ts +40 -0
- package/dist/verifier/types.d.ts.map +1 -0
- package/dist/verifier/types.js +2 -0
- package/dist/verifier/types.js.map +1 -0
- package/package.json +49 -0
- package/src/auth/index.ts +5 -0
- package/src/auth/lock.ts +71 -0
- package/src/auth/providers/anthropic.ts +192 -0
- package/src/auth/providers/index.ts +17 -0
- package/src/auth/providers/minimax.ts +100 -0
- package/src/auth/refresh.ts +138 -0
- package/src/auth/store.ts +75 -0
- package/src/auth/types.ts +62 -0
- package/src/compiler/mermaid.ts +109 -0
- package/src/compiler/xstate.ts +615 -0
- package/src/config/index.ts +2 -0
- package/src/config/loader.ts +122 -0
- package/src/config/types.ts +21 -0
- package/src/generators/index.ts +6 -0
- package/src/generators/registry.ts +27 -0
- package/src/generators/typescript.ts +67 -0
- package/src/index.ts +671 -0
- package/src/llm/anthropic.ts +102 -0
- package/src/llm/grok.ts +73 -0
- package/src/llm/index.ts +29 -0
- package/src/llm/ollama.ts +62 -0
- package/src/llm/openai.ts +74 -0
- package/src/llm/provider.ts +35 -0
- package/src/parser/ast-to-markdown.ts +220 -0
- package/src/parser/ast.ts +236 -0
- package/src/parser/markdown-parser.ts +844 -0
- package/src/runtime/effects.ts +48 -0
- package/src/runtime/machine.ts +201 -0
- package/src/runtime/types.ts +44 -0
- package/src/skills.ts +1339 -0
- package/src/tools.ts +144 -0
- package/src/verifier/completeness.ts +89 -0
- package/src/verifier/determinism.ts +328 -0
- package/src/verifier/properties.ts +507 -0
- package/src/verifier/structural.ts +803 -0
- package/src/verifier/types.ts +45 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { LLMProvider, LLMRequest, LLMResponse, LLMProviderConfig } from './provider.js';
|
|
2
|
+
|
|
3
|
+
export class AnthropicProvider implements LLMProvider {
|
|
4
|
+
private apiKey: string;
|
|
5
|
+
private baseUrl: string;
|
|
6
|
+
private model: string;
|
|
7
|
+
private maxTokens: number;
|
|
8
|
+
private temperature: number;
|
|
9
|
+
private authType: 'x-api-key' | 'bearer';
|
|
10
|
+
|
|
11
|
+
constructor(config: LLMProviderConfig) {
|
|
12
|
+
this.baseUrl = config.base_url || 'https://api.anthropic.com';
|
|
13
|
+
this.model = config.model || 'claude-sonnet-4-6';
|
|
14
|
+
this.maxTokens = config.max_tokens || 4096;
|
|
15
|
+
this.temperature = config.temperature ?? 0.7;
|
|
16
|
+
|
|
17
|
+
// MiniMax uses Bearer token auth, Anthropic uses x-api-key
|
|
18
|
+
if (this.baseUrl.includes('minimax.io')) {
|
|
19
|
+
this.apiKey = config.api_key || process.env.MINIMAX_API_KEY || '';
|
|
20
|
+
this.authType = 'bearer';
|
|
21
|
+
} else {
|
|
22
|
+
this.apiKey = config.api_key || process.env.ANTHROPIC_API_KEY || '';
|
|
23
|
+
this.authType = 'x-api-key';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!this.apiKey) {
|
|
27
|
+
throw new Error(`API key is required for ${this.baseUrl.includes('minimax.io') ? 'MINIMAX_API_KEY' : 'ANTHROPIC_API_KEY'}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
name(): string {
|
|
32
|
+
return 'anthropic';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async complete(request: LLMRequest): Promise<LLMResponse> {
|
|
36
|
+
// Separate system message from user/assistant messages
|
|
37
|
+
const systemMessage = request.messages.find(m => m.role === 'system');
|
|
38
|
+
const otherMessages = request.messages.filter(m => m.role !== 'system');
|
|
39
|
+
|
|
40
|
+
const headers: Record<string, string> = {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
'anthropic-version': '2023-06-01',
|
|
43
|
+
'anthropic-dangerous-direct-browser-access': 'true',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (this.authType === 'bearer') {
|
|
47
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
48
|
+
} else {
|
|
49
|
+
headers['x-api-key'] = this.apiKey;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const response = await fetch(`${this.baseUrl}/v1/messages`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers,
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
model: request.model || this.model,
|
|
57
|
+
system: systemMessage?.content,
|
|
58
|
+
messages: otherMessages.map(m => ({
|
|
59
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
60
|
+
content: m.content,
|
|
61
|
+
})),
|
|
62
|
+
max_tokens: request.max_tokens || this.maxTokens,
|
|
63
|
+
temperature: request.temperature ?? this.temperature,
|
|
64
|
+
stop_sequences: request.stop_sequences,
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const error = await response.text();
|
|
70
|
+
throw new Error(`Anthropic API error: ${response.status} ${error}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const data = await response.json() as {
|
|
74
|
+
content: Array<{ type: string; text: string }>;
|
|
75
|
+
model: string;
|
|
76
|
+
usage: { input_tokens: number; output_tokens: number };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const textContent = data.content.find(c => c.type === 'text');
|
|
80
|
+
return {
|
|
81
|
+
content: textContent?.text || '',
|
|
82
|
+
model: data.model,
|
|
83
|
+
usage: {
|
|
84
|
+
input_tokens: data.usage.input_tokens,
|
|
85
|
+
output_tokens: data.usage.output_tokens,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async completeWithPrefix(request: LLMRequest, prefix: string): Promise<LLMResponse> {
|
|
91
|
+
// Anthropic doesn't support continuation prefixes directly
|
|
92
|
+
// We need to include the prefix in the system prompt and ask for completion
|
|
93
|
+
const modifiedRequest: LLMRequest = {
|
|
94
|
+
...request,
|
|
95
|
+
messages: [
|
|
96
|
+
...request.messages,
|
|
97
|
+
{ role: 'assistant' as const, content: prefix },
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
return this.complete(modifiedRequest);
|
|
101
|
+
}
|
|
102
|
+
}
|
package/src/llm/grok.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { LLMProvider, LLMRequest, LLMResponse, LLMProviderConfig } from './provider.js';
|
|
2
|
+
|
|
3
|
+
export class GrokProvider implements LLMProvider {
|
|
4
|
+
private apiKey: string;
|
|
5
|
+
private baseUrl: string;
|
|
6
|
+
private model: string;
|
|
7
|
+
private maxTokens: number;
|
|
8
|
+
private temperature: number;
|
|
9
|
+
|
|
10
|
+
constructor(config: LLMProviderConfig) {
|
|
11
|
+
this.apiKey = config.api_key || process.env.XAI_API_KEY || process.env.GROK_API_KEY || '';
|
|
12
|
+
this.baseUrl = config.base_url || 'https://api.x.ai/v1';
|
|
13
|
+
this.model = config.model || 'grok-3';
|
|
14
|
+
this.maxTokens = config.max_tokens || 4096;
|
|
15
|
+
this.temperature = config.temperature ?? 0.7;
|
|
16
|
+
|
|
17
|
+
if (!this.apiKey) {
|
|
18
|
+
throw new Error('XAI_API_KEY or GROK_API_KEY is required for Grok provider');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
name(): string {
|
|
23
|
+
return 'grok';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async complete(request: LLMRequest): Promise<LLMResponse> {
|
|
27
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
model: request.model || this.model,
|
|
35
|
+
messages: request.messages,
|
|
36
|
+
max_tokens: request.max_tokens || this.maxTokens,
|
|
37
|
+
temperature: request.temperature ?? this.temperature,
|
|
38
|
+
stop: request.stop_sequences,
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const error = await response.text();
|
|
44
|
+
throw new Error(`Grok API error: ${response.status} ${error}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = await response.json() as {
|
|
48
|
+
choices: Array<{ message: { content: string } }>;
|
|
49
|
+
model: string;
|
|
50
|
+
usage: { prompt_tokens: number; completion_tokens: number };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
content: data.choices[0]?.message?.content || '',
|
|
55
|
+
model: data.model,
|
|
56
|
+
usage: {
|
|
57
|
+
input_tokens: data.usage.prompt_tokens,
|
|
58
|
+
output_tokens: data.usage.completion_tokens,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async completeWithPrefix(request: LLMRequest, prefix: string): Promise<LLMResponse> {
|
|
64
|
+
const modifiedRequest: LLMRequest = {
|
|
65
|
+
...request,
|
|
66
|
+
messages: [
|
|
67
|
+
...request.messages,
|
|
68
|
+
{ role: 'assistant' as const, content: prefix },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
return this.complete(modifiedRequest);
|
|
72
|
+
}
|
|
73
|
+
}
|
package/src/llm/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LLMProvider, LLMProviderConfig } from './provider.js';
|
|
2
|
+
import type { LLMMessage, LLMRequest, LLMResponse } from './provider.js';
|
|
3
|
+
import { AnthropicProvider } from './anthropic.js';
|
|
4
|
+
import { OpenAIProvider } from './openai.js';
|
|
5
|
+
import { OllamaProvider } from './ollama.js';
|
|
6
|
+
import { GrokProvider } from './grok.js';
|
|
7
|
+
import { LLMProviderType } from '../config/types.js';
|
|
8
|
+
|
|
9
|
+
export type { LLMProvider };
|
|
10
|
+
export type { LLMMessage, LLMRequest, LLMResponse };
|
|
11
|
+
export { AnthropicProvider } from './anthropic.js';
|
|
12
|
+
export { OpenAIProvider } from './openai.js';
|
|
13
|
+
export { OllamaProvider } from './ollama.js';
|
|
14
|
+
export { GrokProvider } from './grok.js';
|
|
15
|
+
|
|
16
|
+
export function createProvider(type: LLMProviderType, config: LLMProviderConfig): LLMProvider {
|
|
17
|
+
switch (type) {
|
|
18
|
+
case 'anthropic':
|
|
19
|
+
return new AnthropicProvider(config);
|
|
20
|
+
case 'openai':
|
|
21
|
+
return new OpenAIProvider(config);
|
|
22
|
+
case 'ollama':
|
|
23
|
+
return new OllamaProvider(config);
|
|
24
|
+
case 'grok':
|
|
25
|
+
return new GrokProvider(config);
|
|
26
|
+
default:
|
|
27
|
+
throw new Error(`Unknown LLM provider type: ${type}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { LLMProvider, LLMRequest, LLMResponse, LLMProviderConfig } from './provider.js';
|
|
2
|
+
|
|
3
|
+
export class OllamaProvider implements LLMProvider {
|
|
4
|
+
private baseUrl: string;
|
|
5
|
+
private model: string;
|
|
6
|
+
private temperature: number;
|
|
7
|
+
|
|
8
|
+
constructor(config: LLMProviderConfig) {
|
|
9
|
+
this.baseUrl = config.base_url || 'http://localhost:11434';
|
|
10
|
+
this.model = config.model || 'llama3';
|
|
11
|
+
this.temperature = config.temperature ?? 0.7;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
name(): string {
|
|
15
|
+
return 'ollama';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async complete(request: LLMRequest): Promise<LLMResponse> {
|
|
19
|
+
const response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
},
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
model: request.model || this.model,
|
|
26
|
+
messages: request.messages,
|
|
27
|
+
options: {
|
|
28
|
+
temperature: request.temperature ?? this.temperature,
|
|
29
|
+
num_predict: request.max_tokens || 4096,
|
|
30
|
+
},
|
|
31
|
+
stream: false,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const error = await response.text();
|
|
37
|
+
throw new Error(`Ollama API error: ${response.status} ${error}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const data = await response.json() as {
|
|
41
|
+
message: { content: string };
|
|
42
|
+
model: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
content: data.message.content,
|
|
47
|
+
model: data.model,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async completeWithPrefix(request: LLMRequest, prefix: string): Promise<LLMResponse> {
|
|
52
|
+
// Ollama doesn't support continuation prefixes directly
|
|
53
|
+
const modifiedRequest: LLMRequest = {
|
|
54
|
+
...request,
|
|
55
|
+
messages: [
|
|
56
|
+
...request.messages,
|
|
57
|
+
{ role: 'assistant' as const, content: prefix },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
return this.complete(modifiedRequest);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { LLMProvider, LLMRequest, LLMResponse, LLMProviderConfig } from './provider.js';
|
|
2
|
+
|
|
3
|
+
export class OpenAIProvider implements LLMProvider {
|
|
4
|
+
private apiKey: string;
|
|
5
|
+
private baseUrl: string;
|
|
6
|
+
private model: string;
|
|
7
|
+
private maxTokens: number;
|
|
8
|
+
private temperature: number;
|
|
9
|
+
|
|
10
|
+
constructor(config: LLMProviderConfig) {
|
|
11
|
+
this.apiKey = config.api_key || process.env.OPENAI_API_KEY || '';
|
|
12
|
+
this.baseUrl = config.base_url || 'https://api.openai.com/v1';
|
|
13
|
+
this.model = config.model || 'gpt-4o';
|
|
14
|
+
this.maxTokens = config.max_tokens || 4096;
|
|
15
|
+
this.temperature = config.temperature ?? 0.7;
|
|
16
|
+
|
|
17
|
+
if (!this.apiKey) {
|
|
18
|
+
throw new Error('OPENAI_API_KEY is required for OpenAI provider');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
name(): string {
|
|
23
|
+
return 'openai';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async complete(request: LLMRequest): Promise<LLMResponse> {
|
|
27
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
model: request.model || this.model,
|
|
35
|
+
messages: request.messages,
|
|
36
|
+
max_tokens: request.max_tokens || this.maxTokens,
|
|
37
|
+
temperature: request.temperature ?? this.temperature,
|
|
38
|
+
stop: request.stop_sequences,
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const error = await response.text();
|
|
44
|
+
throw new Error(`OpenAI API error: ${response.status} ${error}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = await response.json() as {
|
|
48
|
+
choices: Array<{ message: { content: string } }>;
|
|
49
|
+
model: string;
|
|
50
|
+
usage: { prompt_tokens: number; completion_tokens: number };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
content: data.choices[0]?.message?.content || '',
|
|
55
|
+
model: data.model,
|
|
56
|
+
usage: {
|
|
57
|
+
input_tokens: data.usage.prompt_tokens,
|
|
58
|
+
output_tokens: data.usage.completion_tokens,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async completeWithPrefix(request: LLMRequest, prefix: string): Promise<LLMResponse> {
|
|
64
|
+
// OpenAI doesn't support continuation prefixes directly
|
|
65
|
+
const modifiedRequest: LLMRequest = {
|
|
66
|
+
...request,
|
|
67
|
+
messages: [
|
|
68
|
+
...request.messages,
|
|
69
|
+
{ role: 'assistant' as const, content: prefix },
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
return this.complete(modifiedRequest);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface LLMMessage {
|
|
2
|
+
role: 'system' | 'user' | 'assistant';
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface LLMRequest {
|
|
7
|
+
messages: LLMMessage[];
|
|
8
|
+
model: string;
|
|
9
|
+
max_tokens?: number;
|
|
10
|
+
temperature?: number;
|
|
11
|
+
stop_sequences?: string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface LLMResponse {
|
|
15
|
+
content: string;
|
|
16
|
+
model: string;
|
|
17
|
+
usage?: {
|
|
18
|
+
input_tokens: number;
|
|
19
|
+
output_tokens: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface LLMProvider {
|
|
24
|
+
name(): string;
|
|
25
|
+
complete(request: LLMRequest): Promise<LLMResponse>;
|
|
26
|
+
completeWithPrefix?(request: LLMRequest, prefix: string): Promise<LLMResponse>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface LLMProviderConfig {
|
|
30
|
+
api_key?: string;
|
|
31
|
+
base_url?: string;
|
|
32
|
+
model: string;
|
|
33
|
+
max_tokens?: number;
|
|
34
|
+
temperature?: number;
|
|
35
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// AST to Markdown converter for Orca
|
|
2
|
+
// Converts MachineDef AST to .orca.md format
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
MachineDef, ContextField, StateDef, Transition,
|
|
6
|
+
GuardDef, GuardExpression, ActionSignature, Property,
|
|
7
|
+
Type, ValueRef, ParallelDef, EffectDef,
|
|
8
|
+
} from './ast.js';
|
|
9
|
+
|
|
10
|
+
function typeToString(type: Type): string {
|
|
11
|
+
switch (type.kind) {
|
|
12
|
+
case 'string': return 'string';
|
|
13
|
+
case 'int': return 'int';
|
|
14
|
+
case 'decimal': return 'decimal';
|
|
15
|
+
case 'bool': return 'bool';
|
|
16
|
+
case 'array': return `${type.elementType}[]`;
|
|
17
|
+
case 'map': return `map<${type.keyType}, ${type.valueType}>`;
|
|
18
|
+
case 'optional': return `${type.innerType}?`;
|
|
19
|
+
case 'custom': return type.name;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function guardExpressionToString(expr: GuardExpression): string {
|
|
24
|
+
switch (expr.kind) {
|
|
25
|
+
case 'true': return 'true';
|
|
26
|
+
case 'false': return 'false';
|
|
27
|
+
case 'not': {
|
|
28
|
+
const inner = guardExpressionToString(expr.expr);
|
|
29
|
+
// Add parens for clarity if inner is a compound expression
|
|
30
|
+
if (expr.expr.kind === 'and' || expr.expr.kind === 'or') return `not (${inner})`;
|
|
31
|
+
return `not ${inner}`;
|
|
32
|
+
}
|
|
33
|
+
case 'and': {
|
|
34
|
+
const left = guardExpressionToString(expr.left);
|
|
35
|
+
const right = guardExpressionToString(expr.right);
|
|
36
|
+
const wrapLeft = expr.left.kind === 'or' ? `(${left})` : left;
|
|
37
|
+
const wrapRight = expr.right.kind === 'or' ? `(${right})` : right;
|
|
38
|
+
return `${wrapLeft} and ${wrapRight}`;
|
|
39
|
+
}
|
|
40
|
+
case 'or': return `${guardExpressionToString(expr.left)} or ${guardExpressionToString(expr.right)}`;
|
|
41
|
+
case 'compare': {
|
|
42
|
+
const opStr = { eq: '==', ne: '!=', lt: '<', gt: '>', le: '<=', ge: '>=' }[expr.op];
|
|
43
|
+
return `${expr.left.path.join('.')} ${opStr} ${valueToString(expr.right)}`;
|
|
44
|
+
}
|
|
45
|
+
case 'nullcheck': return expr.expr.path.join('.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function valueToString(val: ValueRef): string {
|
|
50
|
+
if (val.type === 'null') return 'null';
|
|
51
|
+
if (val.type === 'string') return `"${val.value}"`;
|
|
52
|
+
if (val.type === 'boolean') return String(val.value);
|
|
53
|
+
return String(val.value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function actionSignatureToString(action: ActionSignature): string {
|
|
57
|
+
const params = action.parameters.join(', ');
|
|
58
|
+
let sig = `(${params}) -> ${action.returnType}`;
|
|
59
|
+
if (action.hasEffect && action.effectType) sig += ` + Effect<${action.effectType}>`;
|
|
60
|
+
return sig;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function padColumn(rows: string[][], colIdx: number): void {
|
|
64
|
+
const maxLen = Math.max(...rows.map(r => (r[colIdx] || '').length));
|
|
65
|
+
for (const row of rows) {
|
|
66
|
+
row[colIdx] = (row[colIdx] || '').padEnd(maxLen);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function formatTable(headers: string[], rows: string[][]): string[] {
|
|
71
|
+
const allRows = [headers, ...rows];
|
|
72
|
+
for (let c = 0; c < headers.length; c++) padColumn(allRows, c);
|
|
73
|
+
|
|
74
|
+
const lines: string[] = [];
|
|
75
|
+
lines.push('| ' + allRows[0].join(' | ') + ' |');
|
|
76
|
+
lines.push('|' + allRows[0].map(h => '-'.repeat(h.length + 2)).join('|') + '|');
|
|
77
|
+
for (let r = 1; r < allRows.length; r++) {
|
|
78
|
+
lines.push('| ' + allRows[r].join(' | ') + ' |');
|
|
79
|
+
}
|
|
80
|
+
return lines;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function emitStates(states: StateDef[], level: number): string[] {
|
|
84
|
+
const lines: string[] = [];
|
|
85
|
+
for (const state of states) {
|
|
86
|
+
const prefix = '#'.repeat(level);
|
|
87
|
+
const annotations: string[] = [];
|
|
88
|
+
if (state.isInitial) annotations.push('initial');
|
|
89
|
+
if (state.isFinal) annotations.push('final');
|
|
90
|
+
if (state.parallel) {
|
|
91
|
+
annotations.push('parallel');
|
|
92
|
+
if (state.parallel.sync) annotations.push(`sync: ${state.parallel.sync}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let heading = `${prefix} state ${state.name}`;
|
|
96
|
+
if (annotations.length > 0) heading += ` [${annotations.join(', ')}]`;
|
|
97
|
+
lines.push(heading);
|
|
98
|
+
|
|
99
|
+
if (state.description) lines.push(`> ${state.description}`);
|
|
100
|
+
|
|
101
|
+
if (state.onEntry) lines.push(`- on_entry: ${state.onEntry}`);
|
|
102
|
+
if (state.onExit) lines.push(`- on_exit: ${state.onExit}`);
|
|
103
|
+
if (state.timeout) lines.push(`- timeout: ${state.timeout.duration} -> ${state.timeout.target}`);
|
|
104
|
+
if (state.ignoredEvents?.length) lines.push(`- ignore: ${state.ignoredEvents.join(', ')}`);
|
|
105
|
+
if (state.onDone) lines.push(`- on_done: -> ${state.onDone}`);
|
|
106
|
+
|
|
107
|
+
if (state.parallel) {
|
|
108
|
+
for (const region of state.parallel.regions) {
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push(`${'#'.repeat(level + 1)} region ${region.name}`);
|
|
111
|
+
lines.push('');
|
|
112
|
+
lines.push(...emitStates(region.states, level + 2));
|
|
113
|
+
}
|
|
114
|
+
} else if (state.contains?.length) {
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push(...emitStates(state.contains, level + 1));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
lines.push('');
|
|
120
|
+
}
|
|
121
|
+
return lines;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function emitProperties(properties: Property[]): string[] {
|
|
125
|
+
return properties.map(prop => {
|
|
126
|
+
switch (prop.kind) {
|
|
127
|
+
case 'reachable':
|
|
128
|
+
case 'unreachable':
|
|
129
|
+
return `- ${prop.kind}: ${prop.to} from ${prop.from}`;
|
|
130
|
+
case 'passes_through':
|
|
131
|
+
return `- passes_through: ${prop.through} for ${prop.from} -> ${prop.to}`;
|
|
132
|
+
case 'live':
|
|
133
|
+
return '- live';
|
|
134
|
+
case 'responds':
|
|
135
|
+
return `- responds: ${prop.to} from ${prop.from} within ${prop.within}`;
|
|
136
|
+
case 'invariant': {
|
|
137
|
+
const expr = guardExpressionToString(prop.expression);
|
|
138
|
+
const inState = prop.inState ? ` in ${prop.inState}` : '';
|
|
139
|
+
return `- invariant: \`${expr}\`${inState}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function machineToMarkdown(machine: MachineDef): string {
|
|
146
|
+
const lines: string[] = [];
|
|
147
|
+
|
|
148
|
+
lines.push(`# machine ${machine.name}`);
|
|
149
|
+
lines.push('');
|
|
150
|
+
|
|
151
|
+
// Context
|
|
152
|
+
if (machine.context.length > 0) {
|
|
153
|
+
lines.push('## context');
|
|
154
|
+
lines.push('');
|
|
155
|
+
const rows = machine.context.map(f => [f.name, typeToString(f.type), f.defaultValue || '']);
|
|
156
|
+
lines.push(...formatTable(['Field', 'Type', 'Default'], rows));
|
|
157
|
+
lines.push('');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Events
|
|
161
|
+
if (machine.events.length > 0) {
|
|
162
|
+
lines.push('## events');
|
|
163
|
+
lines.push('');
|
|
164
|
+
for (const e of machine.events) lines.push(`- ${e.name}`);
|
|
165
|
+
lines.push('');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// States
|
|
169
|
+
lines.push(...emitStates(machine.states, 2));
|
|
170
|
+
|
|
171
|
+
// Transitions
|
|
172
|
+
if (machine.transitions.length > 0) {
|
|
173
|
+
lines.push('## transitions');
|
|
174
|
+
lines.push('');
|
|
175
|
+
const rows = machine.transitions.map(t => {
|
|
176
|
+
let guard = '';
|
|
177
|
+
if (t.guard) guard = t.guard.negated ? `!${t.guard.name}` : t.guard.name;
|
|
178
|
+
return [t.source, t.event, guard, t.target, t.action || ''];
|
|
179
|
+
});
|
|
180
|
+
lines.push(...formatTable(['Source', 'Event', 'Guard', 'Target', 'Action'], rows));
|
|
181
|
+
lines.push('');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Guards
|
|
185
|
+
if (machine.guards.length > 0) {
|
|
186
|
+
lines.push('## guards');
|
|
187
|
+
lines.push('');
|
|
188
|
+
const rows = machine.guards.map(g => [g.name, `\`${guardExpressionToString(g.expression)}\``]);
|
|
189
|
+
lines.push(...formatTable(['Name', 'Expression'], rows));
|
|
190
|
+
lines.push('');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Actions
|
|
194
|
+
if (machine.actions.length > 0) {
|
|
195
|
+
lines.push('## actions');
|
|
196
|
+
lines.push('');
|
|
197
|
+
const rows = machine.actions.map(a => [a.name, `\`${actionSignatureToString(a)}\``]);
|
|
198
|
+
lines.push(...formatTable(['Name', 'Signature'], rows));
|
|
199
|
+
lines.push('');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Effects
|
|
203
|
+
if (machine.effects && machine.effects.length > 0) {
|
|
204
|
+
lines.push('## effects');
|
|
205
|
+
lines.push('');
|
|
206
|
+
const rows = machine.effects.map(e => [e.name, e.input, e.output]);
|
|
207
|
+
lines.push(...formatTable(['Name', 'Input', 'Output'], rows));
|
|
208
|
+
lines.push('');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Properties
|
|
212
|
+
if (machine.properties?.length) {
|
|
213
|
+
lines.push('## properties');
|
|
214
|
+
lines.push('');
|
|
215
|
+
lines.push(...emitProperties(machine.properties));
|
|
216
|
+
lines.push('');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return lines.join('\n');
|
|
220
|
+
}
|