@constela/ai 1.0.0 → 2.0.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/README.md +340 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# @constela/ai
|
|
2
|
+
|
|
3
|
+
AI-powered DSL generation for Constela. Generate UI components and views from natural language prompts.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @constela/ai
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
Generate a component from a natural language description:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createDslGenerator } from '@constela/ai';
|
|
17
|
+
|
|
18
|
+
const generator = createDslGenerator({
|
|
19
|
+
provider: 'anthropic', // or 'openai'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const result = await generator.generate({
|
|
23
|
+
prompt: 'Create a card component with title, description, and action button',
|
|
24
|
+
output: 'component',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log(result.dsl);
|
|
28
|
+
// {
|
|
29
|
+
// "kind": "element",
|
|
30
|
+
// "tag": "div",
|
|
31
|
+
// "props": { "class": "card" },
|
|
32
|
+
// "children": [...]
|
|
33
|
+
// }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Environment Variables
|
|
37
|
+
|
|
38
|
+
Set your API key:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# For Anthropic (Claude)
|
|
42
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
43
|
+
|
|
44
|
+
# For OpenAI
|
|
45
|
+
export OPENAI_API_KEY=sk-...
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
### AI Providers
|
|
51
|
+
|
|
52
|
+
Two providers are supported:
|
|
53
|
+
|
|
54
|
+
| Provider | Model | Environment Variable |
|
|
55
|
+
|----------|-------|---------------------|
|
|
56
|
+
| Anthropic | Claude Sonnet | `ANTHROPIC_API_KEY` |
|
|
57
|
+
| OpenAI | GPT-4o | `OPENAI_API_KEY` |
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// Use Anthropic
|
|
61
|
+
const generator = createDslGenerator({ provider: 'anthropic' });
|
|
62
|
+
|
|
63
|
+
// Use OpenAI
|
|
64
|
+
const generator = createDslGenerator({ provider: 'openai' });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Output Types
|
|
68
|
+
|
|
69
|
+
Generate different types of DSL:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Generate a reusable component
|
|
73
|
+
const component = await generator.generate({
|
|
74
|
+
prompt: 'A notification badge with count',
|
|
75
|
+
output: 'component',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Generate a full page/view
|
|
79
|
+
const view = await generator.generate({
|
|
80
|
+
prompt: 'A user profile page with avatar, bio, and recent posts',
|
|
81
|
+
output: 'view',
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Security Validation
|
|
86
|
+
|
|
87
|
+
All generated DSL is automatically validated:
|
|
88
|
+
|
|
89
|
+
**Forbidden Tags** (blocked by default):
|
|
90
|
+
- `script`, `iframe`, `object`, `embed`, `form`
|
|
91
|
+
|
|
92
|
+
**Forbidden Actions** (cannot be used even with whitelist):
|
|
93
|
+
- `import`, `call`, `dom`
|
|
94
|
+
|
|
95
|
+
**Restricted Actions** (require explicit whitelist):
|
|
96
|
+
- `fetch`
|
|
97
|
+
|
|
98
|
+
**URL Validation**:
|
|
99
|
+
- Blocks `javascript:`, `data:`, `vbscript:` schemes
|
|
100
|
+
- Supports domain whitelisting
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const generator = createDslGenerator({
|
|
104
|
+
provider: 'anthropic',
|
|
105
|
+
security: {
|
|
106
|
+
allowedTags: ['a', 'button', 'input'],
|
|
107
|
+
allowedActions: ['set', 'update', 'navigate'],
|
|
108
|
+
allowedUrlPatterns: ['https://api.example.com/*'],
|
|
109
|
+
maxNestingDepth: 32,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Generation Context
|
|
115
|
+
|
|
116
|
+
Provide context for more accurate generation:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const generator = createDslGenerator({
|
|
120
|
+
provider: 'anthropic',
|
|
121
|
+
context: {
|
|
122
|
+
existingComponents: ['Button', 'Card', 'Input'],
|
|
123
|
+
theme: { primaryColor: '#3b82f6' },
|
|
124
|
+
schema: { user: { name: 'string', email: 'string' } },
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## CLI: Suggest Command
|
|
130
|
+
|
|
131
|
+
Analyze DSL files for improvements:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Analyze for accessibility issues
|
|
135
|
+
constela suggest app.json --aspect accessibility
|
|
136
|
+
|
|
137
|
+
# Analyze for performance
|
|
138
|
+
constela suggest app.json --aspect performance
|
|
139
|
+
|
|
140
|
+
# Analyze for security
|
|
141
|
+
constela suggest app.json --aspect security
|
|
142
|
+
|
|
143
|
+
# Analyze for UX
|
|
144
|
+
constela suggest app.json --aspect ux
|
|
145
|
+
|
|
146
|
+
# Use OpenAI instead of Anthropic
|
|
147
|
+
constela suggest app.json --aspect accessibility --provider openai
|
|
148
|
+
|
|
149
|
+
# Output as JSON
|
|
150
|
+
constela suggest app.json --aspect ux --json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Example output:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
=== Suggestions for app.json (accessibility) ===
|
|
157
|
+
|
|
158
|
+
[HIGH] Missing aria-label on button
|
|
159
|
+
Recommendation: Add aria-label="Submit form" to the button element
|
|
160
|
+
Location: view.children[0].props
|
|
161
|
+
|
|
162
|
+
[MED] Low color contrast in text
|
|
163
|
+
Recommendation: Increase contrast ratio to meet WCAG AA standards
|
|
164
|
+
Location: view.children[2].props.style
|
|
165
|
+
|
|
166
|
+
Total: 2 suggestion(s)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## DSL Data Source
|
|
170
|
+
|
|
171
|
+
Use AI to generate content at build time:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"version": "1.0",
|
|
176
|
+
"data": {
|
|
177
|
+
"hero": {
|
|
178
|
+
"type": "ai",
|
|
179
|
+
"provider": "anthropic",
|
|
180
|
+
"prompt": "Create a hero section with gradient background and CTA",
|
|
181
|
+
"output": "component"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"view": {
|
|
185
|
+
"kind": "component",
|
|
186
|
+
"name": "hero",
|
|
187
|
+
"props": {}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Generate Action
|
|
193
|
+
|
|
194
|
+
Generate DSL at runtime in response to user actions:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"actions": [
|
|
199
|
+
{
|
|
200
|
+
"name": "generateCard",
|
|
201
|
+
"steps": [
|
|
202
|
+
{
|
|
203
|
+
"do": "generate",
|
|
204
|
+
"provider": "anthropic",
|
|
205
|
+
"prompt": { "expr": "state", "name": "userPrompt" },
|
|
206
|
+
"output": "component",
|
|
207
|
+
"result": "generatedCard",
|
|
208
|
+
"onSuccess": [
|
|
209
|
+
{ "do": "set", "target": "card", "value": { "expr": "var", "name": "generatedCard" } }
|
|
210
|
+
],
|
|
211
|
+
"onError": [
|
|
212
|
+
{ "do": "set", "target": "error", "value": { "expr": "lit", "value": "Generation failed" } }
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## API Reference
|
|
222
|
+
|
|
223
|
+
### createDslGenerator(options)
|
|
224
|
+
|
|
225
|
+
Creates a new DSL generator instance.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
interface DslGeneratorOptions {
|
|
229
|
+
provider: 'anthropic' | 'openai';
|
|
230
|
+
providerInstance?: AiProvider; // Custom provider instance
|
|
231
|
+
security?: SecurityOptions;
|
|
232
|
+
context?: GenerationContext;
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### generator.generate(options)
|
|
237
|
+
|
|
238
|
+
Generates DSL from a prompt.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
interface GenerateOptions {
|
|
242
|
+
prompt: string;
|
|
243
|
+
output: 'component' | 'view' | 'suggestion';
|
|
244
|
+
context?: GenerationContext;
|
|
245
|
+
security?: SecurityOptions;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
interface GenerateResult {
|
|
249
|
+
dsl: Record<string, unknown>;
|
|
250
|
+
raw: string;
|
|
251
|
+
validated: boolean;
|
|
252
|
+
errors?: string[];
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### generator.validate(dsl, security?)
|
|
257
|
+
|
|
258
|
+
Validates DSL against security rules.
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const result = generator.validate(dsl, {
|
|
262
|
+
allowedTags: ['div', 'span', 'button'],
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (!result.valid) {
|
|
266
|
+
console.error('Validation errors:', result.errors);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Direct Provider Access
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { getProvider, AnthropicProvider, OpenAIProvider } from '@constela/ai';
|
|
274
|
+
|
|
275
|
+
// Get provider by type
|
|
276
|
+
const provider = getProvider('anthropic');
|
|
277
|
+
|
|
278
|
+
// Or create directly
|
|
279
|
+
const anthropic = new AnthropicProvider({ model: 'claude-sonnet-4-20250514' });
|
|
280
|
+
const openai = new OpenAIProvider({ model: 'gpt-4o' });
|
|
281
|
+
|
|
282
|
+
const response = await provider.generate('Generate a button component', {
|
|
283
|
+
systemPrompt: 'You are a UI component generator...',
|
|
284
|
+
maxTokens: 4096,
|
|
285
|
+
temperature: 0.7,
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Security Utilities
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import {
|
|
293
|
+
isForbiddenTag,
|
|
294
|
+
isForbiddenAction,
|
|
295
|
+
isRestrictedAction,
|
|
296
|
+
validateUrl,
|
|
297
|
+
FORBIDDEN_TAGS,
|
|
298
|
+
FORBIDDEN_ACTIONS,
|
|
299
|
+
} from '@constela/ai';
|
|
300
|
+
|
|
301
|
+
// Check if tag is forbidden
|
|
302
|
+
isForbiddenTag('script'); // true
|
|
303
|
+
isForbiddenTag('div'); // false
|
|
304
|
+
|
|
305
|
+
// Validate URL
|
|
306
|
+
const result = validateUrl('https://example.com/api', {
|
|
307
|
+
allowedDomains: ['example.com'],
|
|
308
|
+
allowRelative: true,
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Error Handling
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { AiError, ValidationError, SecurityError } from '@constela/ai';
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const result = await generator.generate({ prompt, output: 'component' });
|
|
319
|
+
} catch (err) {
|
|
320
|
+
if (err instanceof SecurityError) {
|
|
321
|
+
console.error('Security violation:', err.violation);
|
|
322
|
+
} else if (err instanceof ValidationError) {
|
|
323
|
+
console.error('Validation failed:', err.violations);
|
|
324
|
+
} else if (err instanceof AiError) {
|
|
325
|
+
console.error('AI error:', err.code, err.message);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Error codes:
|
|
331
|
+
- `PROVIDER_NOT_CONFIGURED` - API key not set
|
|
332
|
+
- `PROVIDER_NOT_FOUND` - Unknown provider type
|
|
333
|
+
- `API_ERROR` - Provider API call failed
|
|
334
|
+
- `VALIDATION_ERROR` - Generated DSL validation failed
|
|
335
|
+
- `SECURITY_VIOLATION` - Security rule violated
|
|
336
|
+
- `RATE_LIMIT_EXCEEDED` - API rate limit hit
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/ai",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "AI provider abstraction layer for Constela DSL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"vitest": "^2.0.0"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@constela/core": "0.
|
|
28
|
+
"@constela/core": "0.17.0"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
31
|
"node": ">=20.0.0"
|