@uicontract/namer 0.1.0 → 0.2.1
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 +29 -2
- package/dist/ai-namer.d.ts +58 -16
- package/dist/ai-namer.d.ts.map +1 -1
- package/dist/ai-namer.js +274 -21
- package/dist/ai-namer.js.map +1 -1
- package/dist/deterministic-namer.js +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -40,9 +40,36 @@ Examples:
|
|
|
40
40
|
|
|
41
41
|
The namer is deterministic: the same element always receives the same ID across runs. It does not read source files or write anything to disk.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## AI-Assisted Naming
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
For elements lacking strong naming signals (no label or handler), enable AI naming to get context-aware IDs instead of fallback line-number names.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { assignNames } from '@uicontract/namer';
|
|
49
|
+
import type { RawElement } from '@uicontract/core';
|
|
50
|
+
|
|
51
|
+
const named = await assignNames(rawElements, {
|
|
52
|
+
projectRoot: '/path/to/my-app',
|
|
53
|
+
ai: true, // requires OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY in env
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### AiNamerOptions
|
|
58
|
+
|
|
59
|
+
| Option | Type | Default | Description |
|
|
60
|
+
|--------|------|---------|-------------|
|
|
61
|
+
| `timeout` | `number` | `10000` | Timeout in milliseconds per AI batch |
|
|
62
|
+
| `provider` | `'openai' \| 'anthropic' \| 'google'` | auto-detected | AI provider to use |
|
|
63
|
+
| `model` | `string` | provider-specific | Model override |
|
|
64
|
+
| `apiKey` | `string` | from env | API key override |
|
|
65
|
+
|
|
66
|
+
AI naming only targets "weak" elements (no label + no handler). All other elements use deterministic naming. If the AI call fails or times out, the element falls back to its deterministic ID.
|
|
67
|
+
|
|
68
|
+
See the [CLI docs](../../docs/CLI.md#ai-assisted-naming) for full setup and troubleshooting.
|
|
69
|
+
|
|
70
|
+
## Part of UI Contracts
|
|
71
|
+
|
|
72
|
+
This package is part of [UI Contracts](https://github.com/sherifkozman/uicontract) - making web app UIs machine-readable.
|
|
46
73
|
|
|
47
74
|
## License
|
|
48
75
|
|
package/dist/ai-namer.d.ts
CHANGED
|
@@ -1,30 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI-assisted naming engine
|
|
2
|
+
* AI-assisted naming engine.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* context-aware, human-friendly agent
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the
|
|
9
|
-
*
|
|
4
|
+
* Connects to an AI provider (OpenAI, Anthropic, or Google) to generate
|
|
5
|
+
* context-aware, human-friendly agent ID segments for elements where
|
|
6
|
+
* deterministic naming produces weak fallback IDs (no label and no handler).
|
|
7
|
+
*
|
|
8
|
+
* When the AI namer fails or times out, the deterministic namer is used
|
|
9
|
+
* as a fallback.
|
|
10
|
+
*
|
|
11
|
+
* TODO: Investigate how AI agent skills can detect and reuse the AI
|
|
12
|
+
* environment they run within (e.g., Claude Code's API context, Gemini CLI's
|
|
13
|
+
* credentials) instead of requiring separate API keys. This would enable
|
|
14
|
+
* zero-config AI naming when invoked as a skill from an AI agent.
|
|
10
15
|
*/
|
|
11
16
|
import type { RawElement } from '@uicontract/core';
|
|
17
|
+
/** Supported AI providers. */
|
|
18
|
+
export type AiProvider = 'openai' | 'anthropic' | 'google';
|
|
12
19
|
/** Options for the AI naming provider. */
|
|
13
20
|
export interface AiNamerOptions {
|
|
14
21
|
/** Timeout in milliseconds for the AI provider call. */
|
|
15
22
|
timeout?: number;
|
|
16
|
-
/** AI provider identifier
|
|
17
|
-
provider?:
|
|
23
|
+
/** AI provider identifier. */
|
|
24
|
+
provider?: AiProvider;
|
|
25
|
+
/** Model override (provider-specific). */
|
|
26
|
+
model?: string;
|
|
27
|
+
/** API key override (defaults to env variable). */
|
|
28
|
+
apiKey?: string;
|
|
18
29
|
}
|
|
19
30
|
/**
|
|
20
|
-
*
|
|
31
|
+
* Detect AI provider from environment variables.
|
|
32
|
+
* Returns null if no API key is found.
|
|
33
|
+
*/
|
|
34
|
+
export declare function detectProvider(): {
|
|
35
|
+
provider: AiProvider;
|
|
36
|
+
apiKey: string;
|
|
37
|
+
} | null;
|
|
38
|
+
/**
|
|
39
|
+
* Check whether an element would receive a weak deterministic name.
|
|
40
|
+
*
|
|
41
|
+
* A "weak" name falls to the deterministic namer's priority 5 fallback
|
|
42
|
+
* (component.type.line or unknown.type.line), which happens when the
|
|
43
|
+
* element has neither a label nor a handler.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isWeakName(element: RawElement): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Validate and sanitize an AI-suggested name.
|
|
48
|
+
* Returns null if the name is invalid or empty after sanitization.
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateAiName(suggestion: string): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Assign AI-generated agent ID segments to weak elements in a batch.
|
|
21
53
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
54
|
+
* Identifies elements that would receive weak deterministic names (no label,
|
|
55
|
+
* no handler), batches them, sends context to the configured AI provider,
|
|
56
|
+
* and returns validated name suggestions.
|
|
57
|
+
*
|
|
58
|
+
* @param elements - All raw elements to consider for AI naming.
|
|
59
|
+
* @param options - AI provider options.
|
|
60
|
+
* @returns Map from element index to AI-suggested name segment.
|
|
61
|
+
*/
|
|
62
|
+
export declare function assignAiNames(elements: ReadonlyArray<RawElement>, options?: AiNamerOptions): Promise<Map<number, string>>;
|
|
63
|
+
/**
|
|
64
|
+
* Attempt to assign an AI-generated agent ID to a single RawElement.
|
|
24
65
|
*
|
|
25
|
-
* @
|
|
26
|
-
* @param
|
|
27
|
-
* @
|
|
66
|
+
* @deprecated Use assignAiNames for batch processing.
|
|
67
|
+
* @param element - The raw element to name.
|
|
68
|
+
* @param options - AI provider options.
|
|
69
|
+
* @returns AI-suggested name segment, or null on failure.
|
|
28
70
|
*/
|
|
29
|
-
export declare function assignAiName(
|
|
71
|
+
export declare function assignAiName(element: RawElement, options?: AiNamerOptions): Promise<string | null>;
|
|
30
72
|
//# sourceMappingURL=ai-namer.d.ts.map
|
package/dist/ai-namer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-namer.d.ts","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ai-namer.d.ts","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,8BAA8B;AAC9B,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE3D,0CAA0C;AAC1C,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI;IAAE,QAAQ,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAWhF;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAEvD;AA6DD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhE;AA6HD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,EACnC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkE9B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,UAAU,EACnB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB"}
|
package/dist/ai-namer.js
CHANGED
|
@@ -1,29 +1,282 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI-assisted naming engine
|
|
2
|
+
* AI-assisted naming engine.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* context-aware, human-friendly agent
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the
|
|
9
|
-
*
|
|
4
|
+
* Connects to an AI provider (OpenAI, Anthropic, or Google) to generate
|
|
5
|
+
* context-aware, human-friendly agent ID segments for elements where
|
|
6
|
+
* deterministic naming produces weak fallback IDs (no label and no handler).
|
|
7
|
+
*
|
|
8
|
+
* When the AI namer fails or times out, the deterministic namer is used
|
|
9
|
+
* as a fallback.
|
|
10
|
+
*
|
|
11
|
+
* TODO: Investigate how AI agent skills can detect and reuse the AI
|
|
12
|
+
* environment they run within (e.g., Claude Code's API context, Gemini CLI's
|
|
13
|
+
* credentials) instead of requiring separate API keys. This would enable
|
|
14
|
+
* zero-config AI naming when invoked as a skill from an AI agent.
|
|
15
|
+
*/
|
|
16
|
+
import { sanitizeSegment } from './naming-rules.js';
|
|
17
|
+
/** Agent ID validation pattern. */
|
|
18
|
+
const AGENT_ID_PATTERN = /^[a-z][a-z0-9.-]*$/;
|
|
19
|
+
/** Default models per provider. */
|
|
20
|
+
const DEFAULT_MODELS = {
|
|
21
|
+
openai: 'gpt-4o-mini',
|
|
22
|
+
anthropic: 'claude-haiku-4-5-20251001',
|
|
23
|
+
google: 'gemini-2.0-flash',
|
|
24
|
+
};
|
|
25
|
+
/** Default timeout in milliseconds. */
|
|
26
|
+
const DEFAULT_TIMEOUT = 10_000;
|
|
27
|
+
/** Maximum elements per API batch. */
|
|
28
|
+
const BATCH_SIZE = 20;
|
|
29
|
+
/**
|
|
30
|
+
* Detect AI provider from environment variables.
|
|
31
|
+
* Returns null if no API key is found.
|
|
10
32
|
*/
|
|
33
|
+
export function detectProvider() {
|
|
34
|
+
const openaiKey = process.env['OPENAI_API_KEY'];
|
|
35
|
+
if (openaiKey)
|
|
36
|
+
return { provider: 'openai', apiKey: openaiKey };
|
|
37
|
+
const anthropicKey = process.env['ANTHROPIC_API_KEY'];
|
|
38
|
+
if (anthropicKey)
|
|
39
|
+
return { provider: 'anthropic', apiKey: anthropicKey };
|
|
40
|
+
const googleKey = process.env['GOOGLE_API_KEY'] ?? process.env['GEMINI_API_KEY'];
|
|
41
|
+
if (googleKey)
|
|
42
|
+
return { provider: 'google', apiKey: googleKey };
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
11
45
|
/**
|
|
12
|
-
*
|
|
46
|
+
* Check whether an element would receive a weak deterministic name.
|
|
13
47
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
48
|
+
* A "weak" name falls to the deterministic namer's priority 5 fallback
|
|
49
|
+
* (component.type.line or unknown.type.line), which happens when the
|
|
50
|
+
* element has neither a label nor a handler.
|
|
51
|
+
*/
|
|
52
|
+
export function isWeakName(element) {
|
|
53
|
+
return element.label === null && element.handler === null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a naming prompt for a batch of weak elements.
|
|
57
|
+
*/
|
|
58
|
+
function buildNamingPrompt(elements) {
|
|
59
|
+
const descriptions = elements
|
|
60
|
+
.map(({ element }, i) => {
|
|
61
|
+
const parts = [`${String(i + 1)}. Type: ${element.type}`];
|
|
62
|
+
if (element.componentName)
|
|
63
|
+
parts.push(`Component: ${element.componentName}`);
|
|
64
|
+
if (element.route)
|
|
65
|
+
parts.push(`Route: ${element.route}`);
|
|
66
|
+
if (element.filePath)
|
|
67
|
+
parts.push(`File: ${element.filePath}`);
|
|
68
|
+
parts.push(`Line: ${String(element.line)}`);
|
|
69
|
+
if (element.conditional)
|
|
70
|
+
parts.push('(conditionally rendered)');
|
|
71
|
+
if (element.dynamic)
|
|
72
|
+
parts.push('(dynamically rendered)');
|
|
73
|
+
const attrKeys = Object.keys(element.attributes);
|
|
74
|
+
if (attrKeys.length > 0)
|
|
75
|
+
parts.push(`Attributes: ${attrKeys.join(', ')}`);
|
|
76
|
+
return parts.join(', ');
|
|
77
|
+
})
|
|
78
|
+
.join('\n');
|
|
79
|
+
return [
|
|
80
|
+
'You are a naming assistant for UI elements in a web application.',
|
|
81
|
+
'Each element needs a short, descriptive kebab-case name (1-3 words) that describes its likely purpose.',
|
|
82
|
+
'',
|
|
83
|
+
'Rules:',
|
|
84
|
+
'- Use lowercase letters, digits, and hyphens only',
|
|
85
|
+
'- Must start with a letter',
|
|
86
|
+
'- Be concise: 1-3 words separated by hyphens',
|
|
87
|
+
'- Describe the element\'s purpose, not its type',
|
|
88
|
+
'',
|
|
89
|
+
'Elements:',
|
|
90
|
+
descriptions,
|
|
91
|
+
'',
|
|
92
|
+
'Respond with ONLY a JSON array of strings, one name per element, in the same order.',
|
|
93
|
+
'Example: ["search-query", "submit-form"]',
|
|
94
|
+
].join('\n');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Parse the AI response to extract name suggestions.
|
|
98
|
+
*/
|
|
99
|
+
function parseAiResponse(text, expectedCount) {
|
|
100
|
+
const match = text.match(/\[[\s\S]*?\]/);
|
|
101
|
+
if (!match)
|
|
102
|
+
return new Array(expectedCount).fill(null);
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(match[0]);
|
|
105
|
+
if (!Array.isArray(parsed))
|
|
106
|
+
return new Array(expectedCount).fill(null);
|
|
107
|
+
return parsed.map((item) => {
|
|
108
|
+
if (typeof item !== 'string')
|
|
109
|
+
return null;
|
|
110
|
+
return item;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return new Array(expectedCount).fill(null);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validate and sanitize an AI-suggested name.
|
|
119
|
+
* Returns null if the name is invalid or empty after sanitization.
|
|
120
|
+
*/
|
|
121
|
+
export function validateAiName(suggestion) {
|
|
122
|
+
const sanitized = sanitizeSegment(suggestion);
|
|
123
|
+
if (sanitized.length === 0)
|
|
124
|
+
return null;
|
|
125
|
+
if (sanitized.length > 40)
|
|
126
|
+
return null;
|
|
127
|
+
if (!AGENT_ID_PATTERN.test(sanitized))
|
|
128
|
+
return null;
|
|
129
|
+
return sanitized;
|
|
130
|
+
}
|
|
131
|
+
async function callOpenAI(prompt, apiKey, model, signal) {
|
|
132
|
+
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: {
|
|
135
|
+
'Content-Type': 'application/json',
|
|
136
|
+
Authorization: `Bearer ${apiKey}`,
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify({
|
|
139
|
+
model,
|
|
140
|
+
messages: [{ role: 'user', content: prompt }],
|
|
141
|
+
temperature: 0.3,
|
|
142
|
+
max_tokens: 512,
|
|
143
|
+
}),
|
|
144
|
+
signal,
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw new Error(`OpenAI API error: ${String(response.status)} ${response.statusText}`);
|
|
148
|
+
}
|
|
149
|
+
const data = (await response.json());
|
|
150
|
+
return data.choices?.[0]?.message?.content ?? '';
|
|
151
|
+
}
|
|
152
|
+
async function callAnthropic(prompt, apiKey, model, signal) {
|
|
153
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: {
|
|
156
|
+
'Content-Type': 'application/json',
|
|
157
|
+
'x-api-key': apiKey,
|
|
158
|
+
'anthropic-version': '2023-06-01',
|
|
159
|
+
},
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
model,
|
|
162
|
+
max_tokens: 512,
|
|
163
|
+
messages: [{ role: 'user', content: prompt }],
|
|
164
|
+
}),
|
|
165
|
+
signal,
|
|
166
|
+
});
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
throw new Error(`Anthropic API error: ${String(response.status)} ${response.statusText}`);
|
|
169
|
+
}
|
|
170
|
+
const data = (await response.json());
|
|
171
|
+
const textBlock = data.content?.find((b) => b.type === 'text');
|
|
172
|
+
return textBlock?.text ?? '';
|
|
173
|
+
}
|
|
174
|
+
async function callGoogle(prompt, apiKey, model, signal) {
|
|
175
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
|
|
176
|
+
const response = await fetch(url, {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: { 'Content-Type': 'application/json' },
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
contents: [{ parts: [{ text: prompt }] }],
|
|
181
|
+
}),
|
|
182
|
+
signal,
|
|
183
|
+
});
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
throw new Error(`Google API error: ${String(response.status)} ${response.statusText}`);
|
|
186
|
+
}
|
|
187
|
+
const data = (await response.json());
|
|
188
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text ?? '';
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Call the configured AI provider with the given prompt.
|
|
192
|
+
*/
|
|
193
|
+
async function callProvider(prompt, provider, apiKey, model, signal) {
|
|
194
|
+
switch (provider) {
|
|
195
|
+
case 'openai':
|
|
196
|
+
return callOpenAI(prompt, apiKey, model, signal);
|
|
197
|
+
case 'anthropic':
|
|
198
|
+
return callAnthropic(prompt, apiKey, model, signal);
|
|
199
|
+
case 'google':
|
|
200
|
+
return callGoogle(prompt, apiKey, model, signal);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Public API
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
/**
|
|
207
|
+
* Assign AI-generated agent ID segments to weak elements in a batch.
|
|
16
208
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
209
|
+
* Identifies elements that would receive weak deterministic names (no label,
|
|
210
|
+
* no handler), batches them, sends context to the configured AI provider,
|
|
211
|
+
* and returns validated name suggestions.
|
|
212
|
+
*
|
|
213
|
+
* @param elements - All raw elements to consider for AI naming.
|
|
214
|
+
* @param options - AI provider options.
|
|
215
|
+
* @returns Map from element index to AI-suggested name segment.
|
|
216
|
+
*/
|
|
217
|
+
export async function assignAiNames(elements, options) {
|
|
218
|
+
const result = new Map();
|
|
219
|
+
// Find weak elements that would benefit from AI naming
|
|
220
|
+
const weakElements = [];
|
|
221
|
+
for (let i = 0; i < elements.length; i++) {
|
|
222
|
+
const element = elements[i];
|
|
223
|
+
if (element && isWeakName(element)) {
|
|
224
|
+
weakElements.push({ index: i, element });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (weakElements.length === 0)
|
|
228
|
+
return result;
|
|
229
|
+
// Resolve provider config
|
|
230
|
+
const detected = detectProvider();
|
|
231
|
+
const provider = options?.provider ?? detected?.provider;
|
|
232
|
+
const apiKey = options?.apiKey ?? detected?.apiKey;
|
|
233
|
+
if (!provider || !apiKey) {
|
|
234
|
+
process.stderr.write('Warning: AI naming enabled but no API key found. ' +
|
|
235
|
+
'Set OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY. ' +
|
|
236
|
+
'Falling back to deterministic naming.\n');
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
const model = options?.model ?? DEFAULT_MODELS[provider];
|
|
240
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
|
|
241
|
+
// Process in batches to avoid overlong prompts
|
|
242
|
+
for (let start = 0; start < weakElements.length; start += BATCH_SIZE) {
|
|
243
|
+
const batch = weakElements.slice(start, start + BATCH_SIZE);
|
|
244
|
+
const prompt = buildNamingPrompt(batch);
|
|
245
|
+
const controller = new AbortController();
|
|
246
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
247
|
+
try {
|
|
248
|
+
const responseText = await callProvider(prompt, provider, apiKey, model, controller.signal);
|
|
249
|
+
const suggestions = parseAiResponse(responseText, batch.length);
|
|
250
|
+
for (let j = 0; j < batch.length; j++) {
|
|
251
|
+
const suggestion = suggestions[j];
|
|
252
|
+
if (suggestion) {
|
|
253
|
+
const validated = validateAiName(suggestion);
|
|
254
|
+
if (validated) {
|
|
255
|
+
result.set(batch[j].index, validated);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// On error (timeout, network, parse failure), skip this batch.
|
|
262
|
+
// Deterministic fallback will be used for these elements.
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
clearTimeout(timer);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Attempt to assign an AI-generated agent ID to a single RawElement.
|
|
272
|
+
*
|
|
273
|
+
* @deprecated Use assignAiNames for batch processing.
|
|
274
|
+
* @param element - The raw element to name.
|
|
275
|
+
* @param options - AI provider options.
|
|
276
|
+
* @returns AI-suggested name segment, or null on failure.
|
|
277
|
+
*/
|
|
278
|
+
export async function assignAiName(element, options) {
|
|
279
|
+
const names = await assignAiNames([element], options);
|
|
280
|
+
return names.get(0) ?? null;
|
|
28
281
|
}
|
|
29
282
|
//# sourceMappingURL=ai-namer.js.map
|
package/dist/ai-namer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-namer.js","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ai-namer.js","sourceRoot":"","sources":["../src/ai-namer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAiBpD,mCAAmC;AACnC,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAE9C,mCAAmC;AACnC,MAAM,cAAc,GAA+B;IACjD,MAAM,EAAE,aAAa;IACrB,SAAS,EAAE,2BAA2B;IACtC,MAAM,EAAE,kBAAkB;CAC3B,CAAC;AAEF,uCAAuC;AACvC,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B,sCAAsC;AACtC,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB;;;GAGG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAChD,IAAI,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAEhE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACtD,IAAI,YAAY;QAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAEzE,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACjF,IAAI,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAEhE,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,OAAmB;IAC5C,OAAO,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,QAA+D;IAE/D,MAAM,YAAY,GAAG,QAAQ;SAC1B,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;QACtB,MAAM,KAAK,GAAa,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,OAAO,CAAC,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,cAAc,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;QAC7E,IAAI,OAAO,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACzD,IAAI,OAAO,CAAC,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9D,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAChE,IAAI,OAAO,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1E,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,kEAAkE;QAClE,wGAAwG;QACxG,EAAE;QACF,QAAQ;QACR,mDAAmD;QACnD,4BAA4B;QAC5B,8CAA8C;QAC9C,iDAAiD;QACjD,EAAE;QACF,WAAW;QACX,YAAY;QACZ,EAAE;QACF,qFAAqF;QACrF,0CAA0C;KAC3C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,aAAqB;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,KAAK,CAAgB,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEtE,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,KAAK,CAAgB,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEtF,OAAQ,MAAoB,CAAC,GAAG,CAAC,CAAC,IAAa,EAAE,EAAE;YACjD,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAC1C,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,KAAK,CAAgB,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC9C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,SAAS,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,SAAS,CAAC;AACnB,CAAC;AAkBD,KAAK,UAAU,UAAU,CACvB,MAAc,EACd,MAAc,EACd,KAAa,EACb,MAAmB;IAEnB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,4CAA4C,EAAE;QACzE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK;YACL,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,GAAG;SAChB,CAAC;QACF,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;IACvD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,MAAc,EACd,MAAc,EACd,KAAa,EACb,MAAmB;IAEnB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,uCAAuC,EAAE;QACpE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,WAAW,EAAE,MAAM;YACnB,mBAAmB,EAAE,YAAY;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK;YACL,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;SAC9C,CAAC;QACF,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC/D,OAAO,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,MAAc,EACd,MAAc,EACd,KAAa,EACb,MAAmB;IAEnB,MAAM,GAAG,GAAG,2DAA2D,KAAK,wBAAwB,MAAM,EAAE,CAAC;IAC7G,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;SAC1C,CAAC;QACF,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;IACvD,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,YAAY,CACzB,MAAc,EACd,QAAoB,EACpB,MAAc,EACd,KAAa,EACb,MAAmB;IAEnB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACnD,KAAK,WAAW;YACd,OAAO,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QACtD,KAAK,QAAQ;YACX,OAAO,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAmC,EACnC,OAAwB;IAExB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,uDAAuD;IACvD,MAAM,YAAY,GAAkD,EAAE,CAAC;IACvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAE7C,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,QAAQ,EAAE,QAAQ,CAAC;IACzD,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,QAAQ,EAAE,MAAM,CAAC;IAEnD,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,mDAAmD;YACjD,4DAA4D;YAC5D,yCAAyC,CAC5C,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,eAAe,CAAC;IAEpD,+CAA+C;IAC/C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,IAAI,UAAU,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAC;QAE5D,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,MAAM,YAAY,CACrC,MAAM,EACN,QAAQ,EACR,MAAM,EACN,KAAK,EACL,UAAU,CAAC,MAAM,CAClB,CAAC;YACF,MAAM,WAAW,GAAG,eAAe,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAEhE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;gBAClC,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,SAAS,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;oBAC7C,IAAI,SAAS,EAAE,CAAC;wBACd,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;oBACzC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;YAC/D,0DAA0D;QAC5D,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAmB,EACnB,OAAwB;IAExB,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACtD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC9B,CAAC"}
|
|
@@ -70,7 +70,7 @@ export function assignDeterministicName(element) {
|
|
|
70
70
|
if (hasComponent && hasHandler) {
|
|
71
71
|
return ensureValid(buildId([componentSeg, handlerSeg], element.type));
|
|
72
72
|
}
|
|
73
|
-
// Priority 5: fallback
|
|
73
|
+
// Priority 5: fallback - component (or "unknown") + type + line
|
|
74
74
|
const base = hasComponent ? componentSeg : 'unknown';
|
|
75
75
|
return ensureValid(`${base}.${element.type}.${element.line}`);
|
|
76
76
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @uicontract/namer
|
|
2
|
+
* @uicontract/namer - naming engine for UIC elements.
|
|
3
3
|
*
|
|
4
4
|
* Takes RawElements discovered by a parser and assigns stable,
|
|
5
|
-
* hierarchical agentIds using deterministic rules
|
|
6
|
-
* AI-assisted mode for
|
|
5
|
+
* hierarchical agentIds using deterministic rules with an optional
|
|
6
|
+
* AI-assisted mode for elements that lack strong context.
|
|
7
7
|
*/
|
|
8
8
|
import type { RawElement, NamedElement } from '@uicontract/core';
|
|
9
9
|
/** Options for the naming engine. */
|
|
@@ -12,21 +12,26 @@ export interface NamerOptions {
|
|
|
12
12
|
ai?: boolean;
|
|
13
13
|
/** Timeout in milliseconds for AI naming calls. */
|
|
14
14
|
aiTimeout?: number;
|
|
15
|
+
/** AI provider identifier (e.g., "openai", "anthropic", "google"). */
|
|
16
|
+
aiProvider?: string;
|
|
17
|
+
/** Model override for the AI provider. */
|
|
18
|
+
aiModel?: string;
|
|
15
19
|
}
|
|
16
20
|
/**
|
|
17
21
|
* Name a list of raw elements, producing NamedElements with unique agentIds.
|
|
18
22
|
*
|
|
19
|
-
* 1.
|
|
20
|
-
* 2.
|
|
23
|
+
* 1. Optionally batch-processes weak elements via AI for better name segments.
|
|
24
|
+
* 2. Each element is named via the deterministic namer (with AI overrides).
|
|
25
|
+
* 3. Duplicate IDs are resolved with numeric suffixes.
|
|
21
26
|
*
|
|
22
27
|
* @param elements - Raw elements from a parser's discovery phase.
|
|
23
|
-
* @param options - Naming options
|
|
28
|
+
* @param options - Naming options.
|
|
24
29
|
* @returns Array of NamedElements with unique agentIds.
|
|
25
30
|
*/
|
|
26
|
-
export declare function nameElements(elements: ReadonlyArray<RawElement>, options?: NamerOptions): NamedElement[]
|
|
31
|
+
export declare function nameElements(elements: ReadonlyArray<RawElement>, options?: NamerOptions): Promise<NamedElement[]>;
|
|
27
32
|
export { sanitizeSegment, camelToKebab, routeToSegments, labelToSegment, handlerToSegment, componentToSegment, } from './naming-rules.js';
|
|
28
33
|
export { assignDeterministicName } from './deterministic-namer.js';
|
|
29
34
|
export { deduplicateNames } from './deduplicator.js';
|
|
30
|
-
export { assignAiName } from './ai-namer.js';
|
|
31
|
-
export type { AiNamerOptions } from './ai-namer.js';
|
|
35
|
+
export { assignAiName, assignAiNames } from './ai-namer.js';
|
|
36
|
+
export type { AiNamerOptions, AiProvider } from './ai-namer.js';
|
|
32
37
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAUjE,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,EACnC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAiDzB;AAGD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,48 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @uicontract/namer
|
|
2
|
+
* @uicontract/namer - naming engine for UIC elements.
|
|
3
3
|
*
|
|
4
4
|
* Takes RawElements discovered by a parser and assigns stable,
|
|
5
|
-
* hierarchical agentIds using deterministic rules
|
|
6
|
-
* AI-assisted mode for
|
|
5
|
+
* hierarchical agentIds using deterministic rules with an optional
|
|
6
|
+
* AI-assisted mode for elements that lack strong context.
|
|
7
7
|
*/
|
|
8
8
|
import { assignDeterministicName } from './deterministic-namer.js';
|
|
9
9
|
import { deduplicateNames } from './deduplicator.js';
|
|
10
|
-
import {
|
|
10
|
+
import { assignAiNames } from './ai-namer.js';
|
|
11
|
+
import { routeToSegments, componentToSegment, } from './naming-rules.js';
|
|
11
12
|
/**
|
|
12
13
|
* Name a list of raw elements, producing NamedElements with unique agentIds.
|
|
13
14
|
*
|
|
14
|
-
* 1.
|
|
15
|
-
* 2.
|
|
15
|
+
* 1. Optionally batch-processes weak elements via AI for better name segments.
|
|
16
|
+
* 2. Each element is named via the deterministic namer (with AI overrides).
|
|
17
|
+
* 3. Duplicate IDs are resolved with numeric suffixes.
|
|
16
18
|
*
|
|
17
19
|
* @param elements - Raw elements from a parser's discovery phase.
|
|
18
|
-
* @param options - Naming options
|
|
20
|
+
* @param options - Naming options.
|
|
19
21
|
* @returns Array of NamedElements with unique agentIds.
|
|
20
22
|
*/
|
|
21
|
-
export function nameElements(elements, options) {
|
|
23
|
+
export async function nameElements(elements, options) {
|
|
22
24
|
const useAi = options?.ai ?? false;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
// Phase 1: try AI naming for weak elements
|
|
26
|
+
let aiNames = new Map();
|
|
27
|
+
if (useAi) {
|
|
28
|
+
aiNames = await assignAiNames(elements, {
|
|
29
|
+
timeout: options?.aiTimeout,
|
|
30
|
+
provider: options?.aiProvider,
|
|
31
|
+
model: options?.aiModel,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// Phase 2: assign names (deterministic, with AI overrides for weak elements)
|
|
35
|
+
const named = elements.map((element, index) => {
|
|
36
|
+
const aiSuggestion = aiNames.get(index);
|
|
37
|
+
let agentId;
|
|
38
|
+
if (aiSuggestion) {
|
|
39
|
+
// AI provided a name segment — build the full ID using the same
|
|
40
|
+
// structure as the deterministic namer: prefix.suggestion.type
|
|
41
|
+
const routeSegs = element.route !== null ? routeToSegments(element.route) : [];
|
|
42
|
+
const componentSeg = element.componentName !== null ? componentToSegment(element.componentName) : '';
|
|
43
|
+
let prefix;
|
|
44
|
+
if (routeSegs.length > 0) {
|
|
45
|
+
prefix = routeSegs.join('.');
|
|
46
|
+
}
|
|
47
|
+
else if (componentSeg.length > 0) {
|
|
48
|
+
prefix = componentSeg;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
prefix = aiSuggestion;
|
|
52
|
+
}
|
|
53
|
+
// If prefix is the AI suggestion itself (no route/component), use suggestion.type
|
|
54
|
+
// Otherwise use prefix.suggestion.type
|
|
55
|
+
agentId =
|
|
56
|
+
prefix === aiSuggestion
|
|
57
|
+
? `${aiSuggestion}.${element.type}`
|
|
58
|
+
: `${prefix}.${aiSuggestion}.${element.type}`;
|
|
33
59
|
}
|
|
34
|
-
|
|
35
|
-
if (agentId === null) {
|
|
60
|
+
else {
|
|
36
61
|
agentId = assignDeterministicName(element);
|
|
37
62
|
}
|
|
38
63
|
return { ...element, agentId };
|
|
39
64
|
});
|
|
40
|
-
// Phase
|
|
65
|
+
// Phase 3: deduplicate
|
|
41
66
|
return deduplicateNames(named);
|
|
42
67
|
}
|
|
43
68
|
// Re-export individual modules for direct access and testing
|
|
44
69
|
export { sanitizeSegment, camelToKebab, routeToSegments, labelToSegment, handlerToSegment, componentToSegment, } from './naming-rules.js';
|
|
45
70
|
export { assignDeterministicName } from './deterministic-namer.js';
|
|
46
71
|
export { deduplicateNames } from './deduplicator.js';
|
|
47
|
-
export { assignAiName } from './ai-namer.js';
|
|
72
|
+
export { assignAiName, assignAiNames } from './ai-namer.js';
|
|
48
73
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EACL,eAAe,EACf,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAc3B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAmC,EACnC,OAAsB;IAEtB,MAAM,KAAK,GAAG,OAAO,EAAE,EAAE,IAAI,KAAK,CAAC;IAEnC,2CAA2C;IAC3C,IAAI,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE;YACtC,OAAO,EAAE,OAAO,EAAE,SAAS;YAC3B,QAAQ,EAAE,OAAO,EAAE,UAAoC;YACvD,KAAK,EAAE,OAAO,EAAE,OAAO;SACxB,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,MAAM,KAAK,GAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QAC5D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,OAAe,CAAC;QAEpB,IAAI,YAAY,EAAE,CAAC;YACjB,gEAAgE;YAChE,+DAA+D;YAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,YAAY,GAChB,OAAO,CAAC,aAAa,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAElF,IAAI,MAAc,CAAC;YACnB,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;iBAAM,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,MAAM,GAAG,YAAY,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,YAAY,CAAC;YACxB,CAAC;YAED,kFAAkF;YAClF,uCAAuC;YACvC,OAAO;gBACL,MAAM,KAAK,YAAY;oBACrB,CAAC,CAAC,GAAG,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE;oBACnC,CAAC,CAAC,GAAG,MAAM,IAAI,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,uBAAuB;IACvB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,6DAA6D;AAC7D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uicontract/namer",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Naming engine that assigns hierarchical agent IDs to UI elements",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"README.md"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@uicontract/core": "0.1
|
|
44
|
+
"@uicontract/core": "0.2.1"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -b",
|